diff --git a/orx-mesh-generators/build.gradle b/orx-mesh-generators/build.gradle index 685856adc..7fa4be0e2 100644 --- a/orx-mesh-generators/build.gradle +++ b/orx-mesh-generators/build.gradle @@ -10,6 +10,7 @@ sourceSets { dependencies { demoImplementation(project(":orx-camera")) + demoImplementation(project(":orx-shapes")) demoImplementation(libs.openrndr.application) demoImplementation(libs.openrndr.extensions) demoRuntimeOnly(libs.openrndr.gl3.core) diff --git a/orx-mesh-generators/src/demo/kotlin/DemoAll.kt b/orx-mesh-generators/src/demo/kotlin/DemoAll.kt new file mode 100644 index 000000000..bfe7f13e1 --- /dev/null +++ b/orx-mesh-generators/src/demo/kotlin/DemoAll.kt @@ -0,0 +1,65 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.* +import org.openrndr.extra.meshgenerators.* +import org.openrndr.extra.shapes.grid +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.shape.Rectangle + +fun main() { + application { + program { + val meshes = listOf( + boxMesh(1.0, 1.0, 1.0), + sphereMesh(radius = 0.5), + dodecahedronMesh(0.5), + cylinderMesh(radius = 0.5, length = 1.0), + planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Y), + capMesh( + 15, 0.5, + listOf(Vector2.ZERO, Vector2(0.5, 0.2), Vector2.UNIT_X) + ), + revolveMesh(5, 0.5) + ) + + val texture = colorBuffer(256, 256) + val s = texture.shadow + for (y in 0 until 256) { + for (x in 0 until 256) { + s[x, y] = ColorRGBa(x / 256.0, y / 256.0, 0.0, 1.0) + } + } + s.upload() + + val positions = Rectangle.fromCenter(Vector2.ZERO, width * 0.01, height * 0.01) + .grid(4, 2).flatten().map { + it.center.vector3(z = -5.0) + } + + extend { + drawer.clear(ColorRGBa.PINK) + drawer.perspective(60.0, width * 1.0 / height, 0.01, 1000.0) + drawer.depthWrite = true + drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL + drawer.shadeStyle = shadeStyle { + fragmentTransform = """ + float light = dot(v_worldNormal, p_light) * 0.5 + 0.5; + x_fill = texture(p_texture, va_texCoord0.xy); + x_fill.rgb *= light; + """.trimIndent() + parameter("texture", texture) + parameter("light", Vector3(1.0).normalized) + } + meshes.forEachIndexed { i, mesh -> + drawer.isolated { + translate(positions[i]) + rotate(Vector3.UNIT_Y, seconds * 12) + rotate(Vector3.UNIT_X, seconds * 25) + vertexBuffer(mesh, DrawPrimitive.TRIANGLES) + } + } + } + } + } +} \ No newline at end of file diff --git a/orx-mesh-generators/src/demo/kotlin/DemoBox.kt b/orx-mesh-generators/src/demo/kotlin/DemoBox.kt index f21fcdf1a..857e3f943 100644 --- a/orx-mesh-generators/src/demo/kotlin/DemoBox.kt +++ b/orx-mesh-generators/src/demo/kotlin/DemoBox.kt @@ -12,6 +12,7 @@ fun main() { application { program { val box = boxMesh(1.0, 1.0, 1.0) + val texture = colorBuffer(256, 256) val s = texture.shadow for (y in 0 until 256) { @@ -20,6 +21,7 @@ fun main() { } } s.upload() + if (System.getProperty("takeScreenshot") == "true") { extend(SingleScreenshot()) { this.outputFile = System.getProperty("screenshotPath") diff --git a/orx-mesh-generators/src/main/kotlin/Cap.kt b/orx-mesh-generators/src/main/kotlin/Cap.kt index dee48f2f5..18de22f7a 100644 --- a/orx-mesh-generators/src/main/kotlin/Cap.kt +++ b/orx-mesh-generators/src/main/kotlin/Cap.kt @@ -1,12 +1,38 @@ package org.openrndr.extra.meshgenerators -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import org.openrndr.math.YPolarity +import org.openrndr.draw.VertexBuffer +import org.openrndr.math.* import org.openrndr.math.transforms.rotateY -fun generateCap(sides: Int, radius: Double, enveloppe: List = listOf(Vector2(0.0, 0.0), Vector2(1.0, 0.0)), writer: VertexWriter) { +/** + * A shape created by rotating an envelope around a vertical axis. + * The default envelope is a horizontal line which produces a flat round disk. + * By providing a more complex envelop one can create curved shapes like a bowl. + */ +fun capMesh( + sides: Int, + radius: Double, + enveloppe: List = listOf( + Vector2(0.0, 0.0), + Vector2(1.0, 0.0) + ) +): VertexBuffer { + val vb = meshVertexBuffer(6 * sides * (enveloppe.size - 1)) + vb.put { + generateCap(sides, radius, enveloppe, bufferWriter(this)) + } + return vb +} + +fun generateCap( + sides: Int, + radius: Double, + enveloppe: List = listOf( + Vector2(0.0, 0.0), + Vector2(1.0, 0.0) + ), + writer: VertexWriter +) { val maxX = enveloppe.maxByOrNull { it.x } ?: Vector2(1.0, 0.0) val a = maxX.x @@ -50,7 +76,34 @@ fun generateCap(sides: Int, radius: Double, enveloppe: List = listOf(Ve } } -fun generateRevolve(sides: Int, length: Double, enveloppe: List = listOf(Vector2(1.0, 0.0), Vector2(1.0, 1.0)), writer: VertexWriter) { +/** + * A shape created by rotating an envelope around a vertical axis. + * The default envelope is a vertical line which produces a hollow cylinder. + */ +fun revolveMesh( + sides: Int, + length: Double, + enveloppe: List = listOf( + Vector2(1.0, 0.0), + Vector2(1.0, 1.0) + ) +): VertexBuffer { + val vb = meshVertexBuffer(6 * sides * (enveloppe.size - 1)) + vb.put { + generateRevolve(sides, length, enveloppe, bufferWriter(this)) + } + return vb +} + +fun generateRevolve( + sides: Int, + length: Double, + enveloppe: List = listOf( + Vector2(1.0, 0.0), + Vector2(1.0, 1.0) + ), + writer: VertexWriter +) { val maxY = enveloppe.maxByOrNull { it.y } ?: Vector2(0.0, 1.0) val a = maxY.y @@ -59,7 +112,6 @@ fun generateRevolve(sides: Int, length: Double, enveloppe: List = listO val normals2D = enveloppe.zipWithNext().map { val d = it.second - it.first d.normalized.perpendicular() * Vector2(1.0, -1.0) - } val basePositions = cleanEnveloppe.map { Vector3(it.x, it.y, 0.0) } diff --git a/orx-mesh-generators/src/main/kotlin/GeneratorBuffer.kt b/orx-mesh-generators/src/main/kotlin/GeneratorBuffer.kt index b213e8d82..71ee89cd9 100644 --- a/orx-mesh-generators/src/main/kotlin/GeneratorBuffer.kt +++ b/orx-mesh-generators/src/main/kotlin/GeneratorBuffer.kt @@ -6,7 +6,6 @@ import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 import org.openrndr.math.transforms.normalMatrix import org.openrndr.math.transforms.rotate -import org.openrndr.math.transforms.transform import org.openrndr.shape.Shape import java.nio.ByteBuffer import java.nio.ByteOrder @@ -121,6 +120,10 @@ fun GeneratorBuffer.cylinder(sides: Int, segments: Int, radius: Double, length: generateCylinder(sides, segments, radius, length, invert, this::write) } +fun GeneratorBuffer.dodecahedron(radius: Double) { + generateDodecahedron(radius, this::write) +} + fun GeneratorBuffer.taperedCylinder(sides: Int, segments: Int, startRadius: Double, endRadius: Double, length: Double, invert: Boolean = false) { generateTaperedCylinder(sides, segments, startRadius, endRadius, length, invert, this::write) }