[orx-shapes] Add Drawer.bezierPatches
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
42
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer01.kt
Normal file
42
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer01.kt
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer02.kt
Normal file
54
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer02.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer03.kt
Normal file
68
orx-shapes/src/demo/kotlin/DemoBezierPatchDrawer03.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(
|
||||||
listOf(points[0][0], points[1][0], points[2][0], points[3][0]),
|
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][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][2], points[1][2], points[2][2], points[3][2]),
|
||||||
listOf(points[0][3], points[1][3], points[2][3], points[3][3]),
|
listOf(points[0][3], points[1][3], points[2][3], points[3][3]),
|
||||||
|
),
|
||||||
|
if (colors.isEmpty()) emptyList() else {
|
||||||
|
listOf(
|
||||||
|
listOf(colors[0][0], colors[1][0], colors[2][0], colors[3][0]),
|
||||||
|
listOf(colors[0][1], colors[1][1], colors[2][1], colors[3][1]),
|
||||||
|
listOf(colors[0][2], colors[1][2], colors[2][2], colors[3][2]),
|
||||||
|
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,7 +152,7 @@ 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(
|
||||||
@@ -145,18 +161,60 @@ class BezierPatch(val points: List<List<Vector2>>) {
|
|||||||
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
|
||||||
|
|||||||
282
orx-shapes/src/main/kotlin/drawers/BezierPatchDrawer.kt
Normal file
282
orx-shapes/src/main/kotlin/drawers/BezierPatchDrawer.kt
Normal 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)
|
||||||
|
}
|
||||||
88
orx-shapes/src/main/kotlin/phrases/BezierPhraseBook.kt
Normal file
88
orx-shapes/src/main/kotlin/phrases/BezierPhraseBook.kt
Normal 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())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user