278 lines
7.5 KiB
Kotlin
278 lines
7.5 KiB
Kotlin
package org.openrndr.extra.meshgenerators
|
|
|
|
import org.openrndr.draw.*
|
|
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(BufferAlignment.STD430) {
|
|
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(BufferAlignment.STD430) {
|
|
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<Vector2>,
|
|
contours: List<List<Vector2>>,
|
|
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<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
|
|
) {
|
|
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
|
|
}
|
|
}
|