commonMain.MeshGenerators.kt Maven / Gradle / Ivy
The newest version!
package org.openrndr.extra.meshgenerators
import org.openrndr.draw.BufferWriter
import org.openrndr.draw.VertexBuffer
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.mod
import org.openrndr.shape.Shape
import org.openrndr.shape.triangulate
/**
* Vertex writer function interface
*/
typealias VertexWriter = (position: Vector3, normal: Vector3, texCoord: Vector2) -> Unit
/**
* create a [VertexWriter] that writes into a [java.nio.ByteBuffer] through [BufferWriter]
*/
fun bufferWriter(bw: BufferWriter): VertexWriter {
return { p, n, t ->
bw.write(p)
bw.write(n)
bw.write(t)
}
}
/**
* Creates a [VertexBuffer] that is suited for holding meshes.
* Each vertex contains:
* - `position` (vec3)
* - `normal` (vec3)
* - `textureCoordinate` (vec2)
*/
fun meshVertexBuffer(size: Int): VertexBuffer {
return vertexBuffer(vertexFormat {
position(3)
normal(3)
textureCoordinate(2)
}, size)
}
/**
* Creates a [VertexBuffer] that is suited for holding meshes.
* Each vertex contains:
* - `position` (vec3)
* - `normal` (vec3)
* - `textureCoordinate` (vec2)
* - `color` (vec4)
*/
fun meshVertexBufferWithColor(size: Int): VertexBuffer {
return vertexBuffer(vertexFormat {
position(3)
normal(3)
textureCoordinate(2)
color(4)
}, size)
}
@Deprecated("binary compatibility only")
fun extrudeShape(
shape: Shape,
front: Double,
back: Double,
distanceTolerance: Double = 0.5,
writer: VertexWriter
) {
extrudeShape(
shape,
front,
back,
distanceTolerance = distanceTolerance,
flipNormals = false,
writer = writer
)
}
/**
* Extrudes a [Shape] from its triangulations
*
* @param baseTriangles triangle vertices for the caps
* @param contours contour vertices for the sides
* @param front the `z` position of the front
* @param back the `z` position of the back
* @param frontScale scale factor for the front cap
* @param backScale scale factor for the back cap
* @param frontCap add a front cap if true
* @param backCap add a back cap if true
* @param sides add the sides if true
* @param flipNormals generates inside-out geometry if true
* @param writer the vertex writer function
*/
fun extrudeShape(
baseTriangles: List,
contours: List>,
front: Double,
back: Double,
frontScale: Double = 1.0,
backScale: Double = 1.0,
frontCap: Boolean = true,
backCap: Boolean = true,
sides: Boolean = true,
flipNormals: Boolean = false,
writer: VertexWriter
) {
val depth = back - front
val flip = if (flipNormals) 1.0 else -1.0
run {
val normal = Vector3(0.0, 0.0, depth).normalized * flip
val negativeNormal = normal * -1.0
if (frontCap) {
baseTriangles.reversed().forEach {
writer(
(it * frontScale).vector3(z = front),
normal,
Vector2.ZERO
)
}
}
if (backCap) {
baseTriangles.forEach {
writer(
(it * backScale).vector3(z = back),
negativeNormal,
Vector2.ZERO
)
}
}
}
if (sides) {
contours.forEach {
val points = it
val normals = (points.indices).map { index ->
val a = mod(index + 1, points.size)
val b = mod(index - 1, points.size)
(points[a] - points[b]).safeNormalized * -flip
}
val forward = Vector3(0.0, 0.0, depth)
val base = Vector3(0.0, 0.0, front)
var offset = 0.0
(points zip normals).zipWithNext().forEach { (left, right) ->
val width = right.first.distanceTo(left.first)
val frontRight = (right.first * frontScale).xy0 + base
val frontLeft = (left.first * frontScale).xy0 + base
val backRight = (right.first * backScale).xy0 + base + forward
val backLeft = (left.first * backScale).xy0 + base + forward
val height = frontRight.distanceTo(backRight)
val backRightUV = Vector2(offset + width, 0.0)
val backLeftUV = Vector2(offset, 0.0)
val frontLeftUV = Vector2(offset, height)
val frontRightUV = Vector2(offset + width, height)
val lnormal =
(frontLeft - backLeft).normalized.cross(left.second.xy0)
val rnormal =
(frontRight - backRight).normalized.cross(right.second.xy0)
writer(frontLeft, lnormal, frontLeftUV)
writer(frontRight, rnormal, frontRightUV)
writer(backRight, rnormal, backRightUV)
writer(backRight, rnormal, backRightUV)
writer(backLeft, lnormal, backLeftUV)
writer(frontLeft, lnormal, frontLeftUV)
offset += width
}
}
}
}
/**
* Extrudes a [shape] by triangulating it and creating side- and cap geometry.
* @param front the `z` position of the front
* @param back the `z` position of the back
* @param frontScale scale factor for the front cap
* @param backScale scale factor for the back cap
* @param frontCap add a front cap if true
* @param backCap add a back cap if true
* @param sides add the sides if true
* @param distanceTolerance
* @param flipNormals generates inside-out geometry if true
* @param writer the vertex writer function
*/
fun extrudeShape(
shape: Shape,
front: Double,
back: Double,
frontScale: Double = 1.0,
backScale: Double = 1.0,
frontCap: Boolean = true,
backCap: Boolean = true,
sides: Boolean = true,
distanceTolerance: Double = 0.5,
flipNormals: Boolean = false,
writer: VertexWriter
) {
val baseTriangles = triangulate(shape, distanceTolerance)
val points = shape.contours.map { it.adaptivePositions(distanceTolerance) }
extrudeShape(
baseTriangles = baseTriangles,
contours = points,
front = front,
back = back,
frontScale = frontScale,
backScale = backScale,
frontCap = frontCap,
backCap = backCap,
sides = sides,
flipNormals = flipNormals,
writer = writer
)
}
/**
* Extrudes all [shapes]. Uses [writer] to write the resulting
* 3D meshes. The arguments are passed unmodified to [extrudeShape].
*/
fun extrudeShapes(
shapes: List,
front: Double,
back: Double,
frontScale: Double = 1.0,
backScale: Double = 1.0,
frontCap: Boolean = true,
backCap: Boolean = true,
sides: Boolean = true,
distanceTolerance: Double = 0.5,
flipNormals: Boolean = false,
writer: VertexWriter
) {
shapes.forEach {
extrudeShape(
shape = it,
front = front,
back = back,
frontScale = frontScale,
backScale = backScale,
frontCap = frontCap,
backCap = backCap,
sides = sides,
distanceTolerance = distanceTolerance,
flipNormals = flipNormals,
writer = writer
)
}
}
/**
* Return a normalized [Vector2], or [Vector2.ZERO] if the vector is too small
*/
private val Vector2.safeNormalized: Vector2
get() {
return if (length > 0.0001) {
normalized
} else {
Vector2.ZERO
}
}