[orx-shapes] Add Drawer.bezierPatches

This commit is contained in:
Edwin Jakobs
2021-03-20 10:10:26 +01:00
parent a664907e0f
commit 9acd221f49
7 changed files with 620 additions and 26 deletions

View File

@@ -9,6 +9,8 @@ sourceSets {
} }
dependencies { dependencies {
implementation(project(":orx-color"))
implementation(project(":orx-shader-phrases"))
demoImplementation("org.openrndr:openrndr-core:$openrndrVersion") demoImplementation("org.openrndr:openrndr-core:$openrndrVersion")
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion") demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion") demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")

View File

@@ -0,0 +1,42 @@
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.extra.shapes.bezierPatch
import org.openrndr.extra.shapes.drawers.bezierPatch
import org.openrndr.shape.Circle
fun main() {
application {
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
extend {
drawer.clear(ColorRGBa.PINK)
val bp = bezierPatch(
Circle(width/2.0, height/2.0, 200.0).contour
).withColors(
listOf(
listOf(ColorRGBa.PINK, ColorRGBa.RED, ColorRGBa.BLACK, ColorRGBa.BLUE),
listOf(ColorRGBa.RED, ColorRGBa.BLACK, ColorRGBa.BLUE, ColorRGBa.GREEN),
listOf(ColorRGBa.PINK, ColorRGBa.RED, ColorRGBa.WHITE, ColorRGBa.GREEN),
listOf(ColorRGBa.BLACK, ColorRGBa.WHITE, ColorRGBa.BLACK, ColorRGBa.BLUE),
)
)
drawer.bezierPatch(bp)
drawer.fill = null
drawer.contour(bp.contour)
for (i in 0 until 10) {
drawer.contour(bp.horizontal(i/9.0))
}
for (i in 0 until 10) {
drawer.contour(bp.vertical(i/9.0))
}
}
}
}
}

View File

@@ -0,0 +1,54 @@
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.loadFont
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.extra.shapes.bezierPatch
import org.openrndr.extra.shapes.drawers.bezierPatch
import org.openrndr.extras.color.spaces.toOKLABa
import org.openrndr.shape.Circle
fun main() {
application {
configure {
width = 720
height = 720
}
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
extend {
drawer.clear(ColorRGBa.BLACK)
val bp2 = bezierPatch(
Circle(width/2.0 - 180.0, height/2.0, 170.0).contour
).withColors(
listOf(
listOf(ColorRGBa.PINK, ColorRGBa.PINK, ColorRGBa.PINK, ColorRGBa.PINK),
listOf(ColorRGBa.RED, ColorRGBa.RED, ColorRGBa.RED, ColorRGBa.RED),
listOf(ColorRGBa.BLUE, ColorRGBa.BLUE, ColorRGBa.BLUE, ColorRGBa.BLUE),
listOf(ColorRGBa.WHITE, ColorRGBa.WHITE, ColorRGBa.WHITE, ColorRGBa.WHITE),
)
)
drawer.bezierPatch(bp2)
val bp3 = bezierPatch(
Circle(width/2.0 + 180.0, height/2.0, 170.0).contour
).withColors(
listOf(
listOf(ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa()),
listOf(ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa()),
listOf(ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa()),
listOf(ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa()),
)
)
drawer.bezierPatch(bp3)
drawer.fill = ColorRGBa.WHITE
drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0)
drawer.text("RGB", width/2.0 - 180.0, height/2.0 + 200.0)
drawer.text("OKLab", width/2.0 + 180.0, height/2.0 + 200.0)
}
}
}
}

View File

@@ -0,0 +1,68 @@
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.isolated
import org.openrndr.draw.loadFont
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.extra.shapes.bezierPatch
import org.openrndr.extra.shapes.drawers.bezierPatch
import org.openrndr.extra.shapes.grid
import org.openrndr.extras.color.spaces.toOKLABa
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.min
import org.openrndr.math.transforms.buildTransform
import org.openrndr.shape.Circle
import org.openrndr.shape.Rectangle
import kotlin.math.min
fun main() {
application {
configure {
width = 720
height = 720
}
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
extend {
drawer.clear(ColorRGBa.BLACK)
val colors = listOf(
listOf(ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa(), ColorRGBa.PINK.toOKLABa()),
listOf(ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa(), ColorRGBa.RED.toOKLABa()),
listOf(ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa(), ColorRGBa.BLUE.toOKLABa()),
listOf(ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa(), ColorRGBa.WHITE.toOKLABa()),
)
val grid = drawer.bounds.grid(4,4, marginX = 20.0, marginY = 20.0, gutterX = 10.0, gutterY = 10.0)
val cellWidth = grid[0][0].width
val cellHeight = grid[0][0].height
val a = bezierPatch(Rectangle.fromCenter(Vector2(0.0, 0.0), cellWidth, cellHeight).contour)
.withColors(colors)
val b = bezierPatch(
Circle(0.0, 0.0, min(cellWidth, cellHeight) / 2.0).contour.transform(
buildTransform {
rotate(Vector3.UNIT_Z, 45.0)
}
)
).withColors(colors)
for (y in grid.indices) {
for (x in grid[y].indices) {
val f = (y * grid[y].size + x).toDouble() / (grid.size * grid[y].size - 1.0)
val blend = a * (1.0 - f) + b * f
drawer.isolated {
drawer.translate(grid[y][x].center)
drawer.bezierPatch(blend)
}
}
}
}
}
}
}

View File

@@ -1,5 +1,8 @@
package org.openrndr.extra.shapes package org.openrndr.extra.shapes
import org.openrndr.color.AlgebraicColor
import org.openrndr.color.ColorRGBa
import org.openrndr.color.ConvertibleToColorRGBa
import org.openrndr.math.Matrix44 import org.openrndr.math.Matrix44
import org.openrndr.math.Vector2 import org.openrndr.math.Vector2
import org.openrndr.shape.Rectangle import org.openrndr.shape.Rectangle
@@ -7,27 +10,40 @@ import org.openrndr.shape.Segment
import org.openrndr.shape.ShapeContour import org.openrndr.shape.ShapeContour
import kotlin.random.Random import kotlin.random.Random
class BezierPatch(val points: List<List<Vector2>>) { open class BezierPatchBase<C>(
val points: List<List<Vector2>>,
val colors: List<List<C>> = emptyList()
)
where C : AlgebraicColor<C>, C : ConvertibleToColorRGBa {
init { init {
require(points.size == 4 && points.all { it.size == 4 }) require(points.size == 4 && points.all { it.size == 4 })
require(colors.isEmpty() || colors.size == 4 && colors.all { it.size == 4 })
} }
/** /**
* Return a transposed version of the bezier path by transposing the [points] matrix * Return a transposed version of the bezier path by transposing the [points] matrix
*/ */
val transposed val transposed
get() = BezierPatch( get() = BezierPatchBase(
listOf(
listOf(points[0][0], points[1][0], points[2][0], points[3][0]),
listOf(points[0][1], points[1][1], points[2][1], points[3][1]),
listOf(points[0][2], points[1][2], points[2][2], points[3][2]),
listOf(points[0][3], points[1][3], points[2][3], points[3][3]),
),
if (colors.isEmpty()) emptyList() else {
listOf( listOf(
listOf(points[0][0], points[1][0], points[2][0], points[3][0]), listOf(colors[0][0], colors[1][0], colors[2][0], colors[3][0]),
listOf(points[0][1], points[1][1], points[2][1], points[3][1]), listOf(colors[0][1], colors[1][1], colors[2][1], colors[3][1]),
listOf(points[0][2], points[1][2], points[2][2], points[3][2]), listOf(colors[0][2], colors[1][2], colors[2][2], colors[3][2]),
listOf(points[0][3], points[1][3], points[2][3], points[3][3]), listOf(colors[0][3], colors[1][3], colors[2][3], colors[3][3]),
) )
}
) )
fun transform(transform: Matrix44) = BezierPatch(points.map { r -> fun transform(transform: Matrix44) = BezierPatchBase(points.map { r ->
r.map { (transform * it.xy01).div.xy } r.map { (transform * it.xy01).div.xy }
}) }, colors)
private fun coeffs2(t: Double): DoubleArray { private fun coeffs2(t: Double): DoubleArray {
val it = 1.0 - t val it = 1.0 - t
@@ -124,7 +140,7 @@ class BezierPatch(val points: List<List<Vector2>>) {
/** /**
* Extract a sub-patch based on uv parameterization * Extract a sub-patch based on uv parameterization
*/ */
fun sub(u0: Double, v0: Double, u1: Double, v1: Double): BezierPatch { fun sub(u0: Double, v0: Double, u1: Double, v1: Double): BezierPatchBase<C> {
val c0 = Segment(points[0][0], points[0][1], points[0][2], points[0][3]).sub(u0, u1) val c0 = Segment(points[0][0], points[0][1], points[0][2], points[0][3]).sub(u0, u1)
val c1 = Segment(points[1][0], points[1][1], points[1][2], points[1][3]).sub(u0, u1) val c1 = Segment(points[1][0], points[1][1], points[1][2], points[1][3]).sub(u0, u1)
val c2 = Segment(points[2][0], points[2][1], points[2][2], points[2][3]).sub(u0, u1) val c2 = Segment(points[2][0], points[2][1], points[2][2], points[2][3]).sub(u0, u1)
@@ -136,27 +152,69 @@ class BezierPatch(val points: List<List<Vector2>>) {
val d2 = Segment(sub0.points[0][2], sub0.points[1][2], sub0.points[2][2], sub0.points[3][2]).sub(v0, v1) val d2 = Segment(sub0.points[0][2], sub0.points[1][2], sub0.points[2][2], sub0.points[3][2]).sub(v0, v1)
val d3 = Segment(sub0.points[0][3], sub0.points[1][3], sub0.points[2][3], sub0.points[3][3]).sub(v0, v1) val d3 = Segment(sub0.points[0][3], sub0.points[1][3], sub0.points[2][3], sub0.points[3][3]).sub(v0, v1)
return bezierPatch(d0, d1, d2, d3).transposed return fromSegments<C>(d0, d1, d2, d3).transposed
} }
val contour: ShapeContour = ShapeContour( val contour: ShapeContour = ShapeContour(
listOf( listOf(
Segment(points[0][0], points[0][1], points[0][2], points[0][3]), Segment(points[0][0], points[0][1], points[0][2], points[0][3]),
Segment(points[0][3], points[1][3], points[2][3], points[3][3]), Segment(points[0][3], points[1][3], points[2][3], points[3][3]),
Segment(points[3][3], points[3][2], points[3][1], points[3][0]), Segment(points[3][3], points[3][2], points[3][1], points[3][0]),
Segment(points[3][0], points[2][0], points[1][0], points[0][0]), Segment(points[3][0], points[2][0], points[1][0], points[0][0]),
), true) ), true
)
operator fun times(scale: Double) = BezierPatch(points.map { j -> j.map { i -> i * scale } }) operator fun times(scale: Double) =
operator fun div(scale: Double) = BezierPatch(points.map { j -> j.map { i -> i / scale } }) BezierPatchBase(
operator fun plus(right: BezierPatch) = points.map { j -> j.map { i -> i * scale } },
BezierPatch(List(4) { j -> List(4) { i -> points[j][i] + right.points[j][i] } }) if (colors.isEmpty()) colors else colors.map { j -> j.map { i -> i * scale } }
)
operator fun minus(right: BezierPatch) = operator fun div(scale: Double) =
BezierPatch(List(4) { j -> List(4) { i -> points[j][i] - right.points[j][i] } }) BezierPatchBase(points.map { j -> j.map { i -> i / scale } },
if (colors.isEmpty()) colors else colors.map { j -> j.map { i -> i / scale } }
)
operator fun plus(right: BezierPatchBase<C>) =
BezierPatchBase(List(4) { j -> List(4) { i -> points[j][i] + right.points[j][i] } },
if (colors.isEmpty() && right.colors.isEmpty()) { colors }
else if (colors.isEmpty() && right.colors.isNotEmpty()) { right.colors }
else if (colors.isNotEmpty() && right.colors.isEmpty()) { colors }
else { List(4) { j -> List(4) { i -> colors[j][i] + right.colors[j][i] } } }
)
operator fun minus(right: BezierPatchBase<C>) =
BezierPatchBase(List(4) { j -> List(4) { i -> points[j][i] - right.points[j][i] } },
if (colors.isEmpty() && right.colors.isEmpty()) { colors }
else if (colors.isEmpty() && right.colors.isNotEmpty()) { right.colors }
else if (colors.isNotEmpty() && right.colors.isEmpty()) { colors }
else { List(4) { j -> List(4) { i -> colors[j][i] - right.colors[j][i] } } }
)
fun <K> withColors(colors: List<List<K>>): BezierPatchBase<K>
where K : AlgebraicColor<K>, K : ConvertibleToColorRGBa {
return BezierPatchBase(points, colors)
}
companion object {
fun <C> fromSegments(c0: Segment, c1: Segment, c2: Segment, c3: Segment): BezierPatchBase<C>
where C : AlgebraicColor<C>, C : ConvertibleToColorRGBa {
val c0c = c0.cubic
val c1c = c1.cubic
val c2c = c2.cubic
val c3c = c3.cubic
val c0l = listOf(c0c.start, c0c.control[0], c0c.control[1], c0c.end)
val c1l = listOf(c1c.start, c1c.control[0], c1c.control[1], c1c.end)
val c2l = listOf(c2c.start, c2c.control[0], c2c.control[1], c2c.end)
val c3l = listOf(c3c.start, c3c.control[0], c3c.control[1], c3c.end)
return BezierPatchBase(listOf(c0l, c1l, c2l, c3l))
}
}
} }
class BezierPatch(points: List<List<Vector2>>, colors: List<List<ColorRGBa>> = emptyList()) :
BezierPatchBase<ColorRGBa>(points, colors)
/** /**
* Create a cubic bezier patch from 4 segments. The control points of the segments are used in row-wise fashion * Create a cubic bezier patch from 4 segments. The control points of the segments are used in row-wise fashion
@@ -196,10 +254,10 @@ fun bezierPatch(shapeContour: ShapeContour, alpha: Double = 1.0 / 3.0): BezierPa
val x10 = (c0.control[0] * fb + c2.control[1] * fa + c3.control[0] * fa + c1.control[1] * fb) / 2.0 val x10 = (c0.control[0] * fb + c2.control[1] * fa + c3.control[0] * fa + c1.control[1] * fb) / 2.0
val x11 = (c0.control[1] * fb + c2.control[0] * fa + c3.control[0] * fb + c1.control[1] * fa) / 2.0 val x11 = (c0.control[1] * fb + c2.control[0] * fa + c3.control[0] * fb + c1.control[1] * fa) / 2.0
val cps = listOf( val cps = listOf(
listOf(c0.start, c0.control[0], c0.control[1], c0.end), listOf(c0.start, c0.control[0], c0.control[1], c0.end),
listOf(c3.control[1], x00, x01, c1.control[0]), listOf(c3.control[1], x00, x01, c1.control[0]),
listOf(c3.control[0], x10, x11, c1.control[1]), listOf(c3.control[0], x10, x11, c1.control[1]),
listOf(c2.end, c2.control[1], c2.control[0], c2.start), listOf(c2.end, c2.control[1], c2.control[0], c2.start),
) )
return BezierPatch(cps) return BezierPatch(cps)
} }

View File

@@ -0,0 +1,282 @@
package org.openrndr.extra.shapes.drawers
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extra.shapes.BezierPatchBase
import org.openrndr.internal.Driver
import org.openrndr.math.Vector2
import org.openrndr.draw.ShadeStyleGLSL.Companion.drawerUniforms
import org.openrndr.draw.ShadeStyleGLSL.Companion.fragmentMainConstants
import org.openrndr.draw.ShadeStyleGLSL.Companion.vertexMainConstants
import org.openrndr.extra.shaderphrases.preprocess
import org.openrndr.extra.shapes.phrases.BezierPhraseBook
import org.openrndr.extras.color.phrases.ColorPhraseBook
import org.openrndr.extras.color.spaces.ColorOKLABa
import org.openrndr.math.Vector4
private val glslVersion = "410 core"
class BezierPatchDrawer {
private fun vsGenerator(structure: ShadeStructure): String {
return """
|// BezierPatchDrawer.kt / vsGenerator
|#version $glslVersion
|${drawerUniforms()}
|${structure.attributes.orEmpty()}
|${structure.varyingOut.orEmpty()}
|void main() {
| ${vertexMainConstants()}
| vec3 x_normal = vec3(0.0, 0.0, 1.0);
| vec3 x_position = a_position;
| ${structure.varyingBridge}
|}""".trimMargin()
}
private fun fsGenerator(structure: ShadeStructure): String {
return ("""
|// BezierPatchDrawer.kt / fsGenerator
|#version $glslVersion
|${drawerUniforms()}
|${structure.varyingIn.orEmpty()}
|out vec4 o_color;
|void main() {
| vec4 x_fill = u_fill * va_color;
| vec4 x_stroke = u_stroke;
| {
| ${structure.fragmentTransform.orEmpty()}
| }
| o_color = x_fill;
| o_color.rgb *= o_color.a;
}""".trimMargin())
}
private fun fsGeneratorOKLab(structure: ShadeStructure): String {
return ("""
|// BezierPatchDrawer.kt / fsGeneratorOKLab
|#version $glslVersion
|${drawerUniforms()}
|${ColorPhraseBook.oklabToLinearRgb.phrase}
|${ColorPhraseBook.linearRgbToSRgb.phrase}
|${structure.varyingIn.orEmpty()}
|out vec4 o_color;
|void main() {
| ${fragmentMainConstants(instance = "0")}
| vec4 x_fill = u_fill * va_color;
| vec4 x_stroke = u_stroke;
| {
| ${structure.fragmentTransform.orEmpty()}
| }
| o_color = linear_rgb_to_srgb(oklab_to_linear_rgb(x_fill));
| o_color.rgb *= o_color.a;
|}""".trimMargin())
}
private fun tseGenerator(structure: ShadeStructure): String {
BezierPhraseBook.register()
return """
|#version $glslVersion
|
|#pragma import beziers.bezier_patch42
|#pragma import beziers.bezier_patch43
|#pragma import beziers.bezier_patch44
|
|${drawerUniforms()}
|layout(quads, equal_spacing, ccw) in;
|
|in vec3 cva_position[gl_MaxPatchVertices];
|in vec4 cva_color[gl_MaxPatchVertices];
|in vec2 cva_texCoord0[gl_MaxPatchVertices];
|
|${structure.varyingOut.orEmpty()}
|
|void main() {
| va_position = bezier_patch43(cva_position, gl_TessCoord.xy);
| va_color = bezier_patch44(cva_color, gl_TessCoord.xy);
| va_texCoord0 = bezier_patch42(cva_texCoord0, gl_TessCoord.xy);
| gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * vec4(va_position,1.0);
}""".trimMargin().preprocess()
}
private fun tscGenerator(structure: ShadeStructure): String {
return """
|#version $glslVersion
|uniform int u_subdivisions;
|${drawerUniforms()}
|layout(vertices = 16) out; // 16 points per patch
|
|in vec3 va_position[];
|in vec4 va_color[];
|in vec2 va_texCoord0[];
|
|out vec3 cva_position[];
|out vec4 cva_color[];
|out vec2 cva_texCoord0[];
|
|void main() {
| cva_position[gl_InvocationID] = va_position[gl_InvocationID];
| cva_color[gl_InvocationID] = va_color[gl_InvocationID];
| cva_texCoord0[gl_InvocationID] = va_texCoord0[gl_InvocationID];
|
| if (gl_InvocationID == 0) {
| gl_TessLevelOuter[0] = u_subdivisions;
| gl_TessLevelOuter[1] = u_subdivisions;
| gl_TessLevelOuter[2] = u_subdivisions;
| gl_TessLevelOuter[3] = u_subdivisions;
| gl_TessLevelInner[0] = u_subdivisions;
| gl_TessLevelInner[1] = u_subdivisions;
| }
|}""".trimMargin()
}
val shadeStyleManager by lazy {
ShadeStyleManager.fromGenerators(
name = "bezier-patches",
vsGenerator = ::vsGenerator,
tscGenerator = ::tscGenerator,
tseGenerator = ::tseGenerator,
fsGenerator = ::fsGenerator
)
}
val shadeStyleManagerOKLab by lazy {
ShadeStyleManager.fromGenerators(
name = "bezier-patches-oklab",
vsGenerator = ::vsGenerator,
tscGenerator = ::tscGenerator,
tseGenerator = ::tseGenerator,
fsGenerator = ::fsGeneratorOKLab
)
}
var vertices =
vertexBuffer(
vertexFormat {
position(3)
color(4)
textureCoordinate(2)
}, 16, session = Session.root)
internal fun ensureVertexCount(count: Int) {
if (vertices.vertexCount < count) {
vertices.destroy()
vertexBuffer(
vertexFormat {
position(3)
color(4)
textureCoordinate(2)
}, count, session = Session.root)
}
}
fun drawBezierPatches(
context: DrawContext,
drawStyle: DrawStyle,
bezierPatches: List<BezierPatchBase<ColorRGBa>>,
subdivisions: Int = 32
) {
ensureVertexCount(bezierPatches.size * 16)
val shader = shadeStyleManager.shader(
drawStyle.shadeStyle,
listOf(vertices.vertexFormat),
emptyList()
)
vertices.put {
for (bezierPatch in bezierPatches) {
for (j in 0 until 4) {
for (i in 0 until 4) {
write(bezierPatch.points[j][i].xy0)
if (bezierPatch.colors.isEmpty()) {
write(ColorRGBa.WHITE)
} else {
write(bezierPatch.colors[j][i])
}
write(Vector2(i / 3.0, j / 3.0))
}
}
}
}
shader.begin()
shader.uniform("u_subdivisions", subdivisions)
context.applyToShader(shader)
drawStyle.applyToShader(shader)
Driver.instance.setState(drawStyle)
Driver.instance.drawVertexBuffer(
shader,
listOf(vertices),
DrawPrimitive.PATCHES,
0,
16 * bezierPatches.size,
16
)
shader.end()
}
@JvmName("drawBezierPatchOKLab")
fun drawBezierPatches(
context: DrawContext,
drawStyle: DrawStyle,
bezierPatches: List<BezierPatchBase<ColorOKLABa>>,
subdivisions: Int = 32
) {
ensureVertexCount(bezierPatches.size * 16)
val shader = shadeStyleManagerOKLab.shader(
drawStyle.shadeStyle,
listOf(vertices.vertexFormat),
emptyList()
)
vertices.put {
for(bezierPatch in bezierPatches) {
for (j in 0 until 4) {
for (i in 0 until 4) {
write(bezierPatch.points[j][i].xy0)
if (bezierPatch.colors.isEmpty()) {
write(ColorRGBa.WHITE)
} else {
write(bezierPatch.colors[j][i].let {
Vector4(it.l, it.a, it.b, it.alpha)
})
}
write(Vector2(i / 3.0, j / 3.0))
}
}
}
}
shader.begin()
shader.uniform("u_subdivisions", subdivisions)
context.applyToShader(shader)
drawStyle.applyToShader(shader)
Driver.instance.setState(drawStyle)
Driver.instance.drawVertexBuffer(
shader,
listOf(vertices),
DrawPrimitive.PATCHES,
0,
16 * bezierPatches.size,
16
)
shader.end()
}
}
private val Drawer.bezierPatchDrawer: BezierPatchDrawer by lazy { BezierPatchDrawer() }
@JvmName("bezierPatchRGBa")
fun Drawer.bezierPatch(bezierPatch: BezierPatchBase<ColorRGBa>, subdivisions: Int = 32) {
bezierPatchDrawer.drawBezierPatches(context, drawStyle, listOf(bezierPatch), subdivisions)
}
@JvmName("bezierPatchesRGBa")
fun Drawer.bezierPatches(bezierPatch: List<BezierPatchBase<ColorRGBa>>, subdivisions: Int = 32) {
bezierPatchDrawer.drawBezierPatches(context, drawStyle, bezierPatch, subdivisions)
}
@JvmName("bezierPatchOKLAB")
fun Drawer.bezierPatch(bezierPatch: BezierPatchBase<ColorOKLABa>, subdivisions: Int = 32) {
bezierPatchDrawer.drawBezierPatches(context, drawStyle, listOf(bezierPatch), subdivisions)
}
@JvmName("bezierPatchesOKLAB")
fun Drawer.bezierPatches(bezierPatch: List<BezierPatchBase<ColorOKLABa>>, subdivisions: Int = 32) {
bezierPatchDrawer.drawBezierPatches(context, drawStyle, bezierPatch, subdivisions)
}

View File

@@ -0,0 +1,88 @@
package org.openrndr.extra.shapes.phrases
import org.openrndr.extra.shaderphrases.ShaderPhrase
import org.openrndr.extra.shaderphrases.ShaderPhraseBook
object BezierPhraseBook: ShaderPhraseBook("beziers") {
val bezier22 = ShaderPhrase("""
|vec2 bezier22(vec2 a, vec2 b, float t) {
| return mix(a, b, t);
|}""".trimMargin())
val bezier32 = ShaderPhrase("""
|#pragma import $bookId.bezier22
|vec2 bezier32(vec2 a, vec2 b, vec2 c, float t) {
| return mix(bezier22(a, b, t), bezier22(b, c, t), t);
|}""".trimMargin())
val bezier42 = ShaderPhrase("""
|#pragma import $bookId.bezier32
|vec2 bezier42(vec2 a, vec2 b, vec2 c, vec2 d, float t) {
| return mix(bezier32(a, b, c, t), bezier32(b, c, d, t), t);
|}""".trimMargin())
val bezier23 = ShaderPhrase("""
|vec3 bezier23(vec3 a, vec3 b, float t) {
| return mix(a, b, t);
|}""".trimMargin())
val bezier33 = ShaderPhrase("""
|#pragma import $bookId.bezier23
|vec3 bezier33(vec3 a, vec3 b, vec3 c, float t) {
| return mix(bezier23(a, b, t), bezier23(b, c, t), t);
|}""".trimMargin())
val bezier43 = ShaderPhrase("""
|#pragma import $bookId.bezier33
|vec3 bezier43(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
| return mix(bezier33(a, b, c, t), bezier33(b, c, d, t), t);
|}""".trimMargin())
val bezier24 = ShaderPhrase("""
|vec4 bezier24(vec4 a, vec4 b, float t) {
| return mix(a, b, t);
|}""".trimMargin())
val bezier34 = ShaderPhrase("""
|#pragma import $bookId.bezier24
|vec4 bezier34(vec4 a, vec4 b, vec4 c, float t) {
| return mix(bezier24(a, b, t), bezier24(b, c, t), t);
|}""".trimMargin())
val bezier44 = ShaderPhrase("""
|#pragma import $bookId.bezier34
|vec4 bezier44(vec4 a, vec4 b, vec4 c, vec4 d, float t) {
| return mix(bezier34(a, b, c, t), bezier34(b, c, d, t), t);
|}""".trimMargin())
val bezierPatch42 = ShaderPhrase("""
|#pragma import $bookId.bezier42
|vec2 bezier_patch42(in vec2[gl_MaxPatchVertices] cps, vec2 uv) {
| vec2 p0 = bezier42(cps[0], cps[1], cps[2], cps[3], uv.x);
| vec2 p1 = bezier42(cps[4], cps[5], cps[6], cps[7], uv.x);
| vec2 p2 = bezier42(cps[8], cps[9], cps[10], cps[11], uv.x);
| vec2 p3 = bezier42(cps[12], cps[13], cps[14], cps[15], uv.x);
| return bezier42(p0, p1, p2, p3, uv.y);
|}""".trimMargin())
val bezierPatch43 = ShaderPhrase("""
|#pragma import $bookId.bezier43
|vec3 bezier_patch43(in vec3[gl_MaxPatchVertices] cps, vec2 uv) {
| vec3 p0 = bezier43(cps[0], cps[1], cps[2], cps[3], uv.x);
| vec3 p1 = bezier43(cps[4], cps[5], cps[6], cps[7], uv.x);
| vec3 p2 = bezier43(cps[8], cps[9], cps[10], cps[11], uv.x);
| vec3 p3 = bezier43(cps[12], cps[13], cps[14], cps[15], uv.x);
| return bezier43(p0, p1, p2, p3, uv.y);
|}""".trimMargin())
val bezierPatch44 = ShaderPhrase("""
|#pragma import $bookId.bezier44
|vec4 bezier_patch44(in vec4[gl_MaxPatchVertices] cps, vec2 uv) {
| vec4 p0 = bezier44(cps[0], cps[1], cps[2], cps[3], uv.x);
| vec4 p1 = bezier44(cps[4], cps[5], cps[6], cps[7], uv.x);
| vec4 p2 = bezier44(cps[8], cps[9], cps[10], cps[11], uv.x);
| vec4 p3 = bezier44(cps[12], cps[13], cps[14], cps[15], uv.x);
| return bezier44(p0, p1, p2, p3, uv.y);
|}""".trimMargin())
}