Add new extrusion types, refactor code. (#323)
This commit is contained in:
@@ -10,34 +10,6 @@ import org.openrndr.shape.Shape
|
|||||||
import org.openrndr.shape.ShapeContour
|
import org.openrndr.shape.ShapeContour
|
||||||
import org.openrndr.shape.Triangle
|
import org.openrndr.shape.Triangle
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes two triangles to [writer] representing
|
|
||||||
* the quad formed by four vertices.
|
|
||||||
*
|
|
||||||
* @param v00 vertex (0, 0)
|
|
||||||
* @param v01 vertex (0, 1)
|
|
||||||
* @param v10 vertex (1, 0)
|
|
||||||
* @param v11 vertex (1, 1)
|
|
||||||
* @param faceNormal the face normal
|
|
||||||
* @param writer the vertex writer function
|
|
||||||
*/
|
|
||||||
fun quadToTris(
|
|
||||||
v00: Vector3,
|
|
||||||
v01: Vector3,
|
|
||||||
v10: Vector3,
|
|
||||||
v11: Vector3,
|
|
||||||
faceNormal: Vector3,
|
|
||||||
writer: VertexWriter
|
|
||||||
) {
|
|
||||||
writer(v11, faceNormal, Vector2.ZERO)
|
|
||||||
writer(v01, faceNormal, Vector2.ZERO)
|
|
||||||
writer(v00, faceNormal, Vector2.ZERO)
|
|
||||||
|
|
||||||
writer(v00, faceNormal, Vector2.ZERO)
|
|
||||||
writer(v10, faceNormal, Vector2.ZERO)
|
|
||||||
writer(v11, faceNormal, Vector2.ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes quads to [writer] creating a surface that connects two
|
* Writes quads to [writer] creating a surface that connects two
|
||||||
* displaced instances of [linearContour]. The positions and orientations
|
* displaced instances of [linearContour]. The positions and orientations
|
||||||
@@ -49,7 +21,7 @@ fun quadToTris(
|
|||||||
* @param writer the vertex writer function
|
* @param writer the vertex writer function
|
||||||
*/
|
*/
|
||||||
fun contourSegment(
|
fun contourSegment(
|
||||||
linearContour: List<Vector3>,
|
linearContour: List<Vector2>,
|
||||||
frame0: Matrix44,
|
frame0: Matrix44,
|
||||||
frame1: Matrix44,
|
frame1: Matrix44,
|
||||||
writer: VertexWriter
|
writer: VertexWriter
|
||||||
@@ -58,42 +30,16 @@ fun contourSegment(
|
|||||||
val v0 = linearContour[i]
|
val v0 = linearContour[i]
|
||||||
val v1 = linearContour[(i + 1).mod(linearContour.size)]
|
val v1 = linearContour[(i + 1).mod(linearContour.size)]
|
||||||
|
|
||||||
val v00 = (frame0 * v0.xyz1).xyz
|
val v00 = (frame0 * v0.xy01).xyz
|
||||||
val v01 = (frame0 * v1.xyz1).xyz
|
val v01 = (frame0 * v1.xy01).xyz
|
||||||
|
|
||||||
val v10 = (frame1 * v0.xyz1).xyz
|
val v10 = (frame1 * v0.xy01).xyz
|
||||||
val v11 = (frame1 * v1.xyz1).xyz
|
val v11 = (frame1 * v1.xy01).xyz
|
||||||
val faceNormal = ((v10 - v00).normalized cross (v01 - v00).normalized).normalized
|
val faceNormal = ((v10 - v00).normalized cross (v01 - v00).normalized).normalized
|
||||||
quadToTris(v00, v01, v10, v11, faceNormal, writer)
|
quadToTris(v00, v01, v10, v11, faceNormal, writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a list of triangles transformed by the [frame]
|
|
||||||
* transformation matrix into [writer].
|
|
||||||
*
|
|
||||||
* @param triangulation the list of triangles to write
|
|
||||||
* @param frame a transformation matrix to apply to each triangle
|
|
||||||
* @param flipNormals generates inside-out geometry if true
|
|
||||||
* @param writer the vertex writer function
|
|
||||||
*/
|
|
||||||
fun triangulationWithFrame(
|
|
||||||
triangulation: List<Triangle>,
|
|
||||||
frame: Matrix44,
|
|
||||||
flipNormals: Boolean = true,
|
|
||||||
writer: VertexWriter
|
|
||||||
) {
|
|
||||||
val normalFrame = normalMatrix(frame)
|
|
||||||
val normalScale = if (!flipNormals) -1.0 else 1.0
|
|
||||||
val normal = ((normalFrame * Vector4(0.0, 0.0, normalScale, 0.0)).xyz)
|
|
||||||
for (triangle in triangulation) {
|
|
||||||
val t = if (!flipNormals) triangle else Triangle(triangle.x3, triangle.x2, triangle.x1)
|
|
||||||
writer((frame * t.x1.xy01).xyz, normal, Vector2.ZERO)
|
|
||||||
writer((frame * t.x2.xy01).xyz, normal, Vector2.ZERO)
|
|
||||||
writer((frame * t.x3.xy01).xyz, normal, Vector2.ZERO)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extrude a [contour] along a [path] specifying the number of steps.
|
* Extrude a [contour] along a [path] specifying the number of steps.
|
||||||
*
|
*
|
||||||
@@ -129,7 +75,7 @@ fun extrudeContourSteps(
|
|||||||
writer: VertexWriter
|
writer: VertexWriter
|
||||||
) {
|
) {
|
||||||
val linearContour = contour.sampleLinear(contourDistanceTolerance)
|
val linearContour = contour.sampleLinear(contourDistanceTolerance)
|
||||||
val linearContourPoints = linearContour.adaptivePositions().map { it.xy0 }
|
val linearContourPoints = linearContour.adaptivePositions()
|
||||||
val finalFrames = if (path.closed) frames + frames.first() else frames
|
val finalFrames = if (path.closed) frames + frames.first() else frames
|
||||||
|
|
||||||
// First add caps
|
// First add caps
|
||||||
@@ -141,36 +87,6 @@ fun extrudeContourSteps(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds caps to an extruded shape
|
|
||||||
*
|
|
||||||
* @param linearShape the cross-section of the mesh
|
|
||||||
* @param path the 3D path
|
|
||||||
* @param startCap adds a start cap if set to true
|
|
||||||
* @param endCap adds an end cap if set to true
|
|
||||||
* @param frames a list of matrices holding the transformation matrices along
|
|
||||||
* the path
|
|
||||||
* @param writer the vertex writer function
|
|
||||||
*/
|
|
||||||
private fun extrudeCaps(
|
|
||||||
linearShape: Shape,
|
|
||||||
path: Path3D,
|
|
||||||
startCap: Boolean,
|
|
||||||
endCap: Boolean,
|
|
||||||
frames: List<Matrix44>,
|
|
||||||
writer: VertexWriter
|
|
||||||
) {
|
|
||||||
if ((startCap || endCap) && !path.closed) {
|
|
||||||
val capTriangles = linearShape.triangulation
|
|
||||||
if (startCap) {
|
|
||||||
triangulationWithFrame(capTriangles, frames.first(), false, writer)
|
|
||||||
}
|
|
||||||
if (endCap) {
|
|
||||||
triangulationWithFrame(capTriangles, frames.last(), true, writer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extrude a [contour] along a [path]. The number of resulting steps
|
* Extrude a [contour] along a [path]. The number of resulting steps
|
||||||
* along the path depends on the tolerance values.
|
* along the path depends on the tolerance values.
|
||||||
@@ -202,7 +118,7 @@ fun extrudeContourAdaptive(
|
|||||||
writer: VertexWriter
|
writer: VertexWriter
|
||||||
) {
|
) {
|
||||||
val linearContour = contour.sampleLinear(contourDistanceTolerance)
|
val linearContour = contour.sampleLinear(contourDistanceTolerance)
|
||||||
val linearContourPoints = linearContour.adaptivePositions().map { it.xy0 }
|
val linearContourPoints = linearContour.adaptivePositions()
|
||||||
val finalFrames = if (path.closed) frames + frames.first() else frames
|
val finalFrames = if (path.closed) frames + frames.first() else frames
|
||||||
|
|
||||||
// First add caps
|
// First add caps
|
||||||
@@ -442,4 +358,4 @@ fun TriangleMeshBuilder.extrudeContourAdaptive(
|
|||||||
contourDistanceTolerance,
|
contourDistanceTolerance,
|
||||||
pathDistanceTolerance,
|
pathDistanceTolerance,
|
||||||
writer = this::write
|
writer = this::write
|
||||||
)
|
)
|
||||||
|
|||||||
111
orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt
Normal file
111
orx-mesh-generators/src/commonMain/kotlin/ExtrusionMorphed.kt
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package org.openrndr.extra.meshgenerators
|
||||||
|
|
||||||
|
import org.openrndr.math.Matrix44
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
import org.openrndr.shape.Path3D
|
||||||
|
import org.openrndr.shape.ShapeContour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrude a [contour] along a [path] specifying the number of steps.
|
||||||
|
*
|
||||||
|
* @param contour the cross-section of the mesh
|
||||||
|
* @param path the 3D path
|
||||||
|
* @param stepCount the number of steps along the [path]
|
||||||
|
* @param up0 the initial up-vector
|
||||||
|
* @param contourDistanceTolerance precision for calculating steps along
|
||||||
|
* [contour]. Lower tolerance results in higher precision.
|
||||||
|
* @param pathDistanceTolerance precision for calculating steps along
|
||||||
|
* [path]. Lower tolerance results in higher precision.
|
||||||
|
* @param steps the resulting positions in the path
|
||||||
|
* @param frames a list of matrices holding the transformation matrices along
|
||||||
|
* the path
|
||||||
|
* @param startCap adds a start cap if set to true
|
||||||
|
* @param endCap adds an end cap if set to true
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun extrudeContourStepsMorphed(
|
||||||
|
contour: (Double) -> ShapeContour,
|
||||||
|
path: Path3D,
|
||||||
|
stepCount: Int,
|
||||||
|
up0: Vector3,
|
||||||
|
contourDistanceTolerance: Double = 0.5,
|
||||||
|
pathDistanceTolerance: Double = 0.5,
|
||||||
|
steps: List<Vector3> = path.equidistantPositions(
|
||||||
|
stepCount,
|
||||||
|
pathDistanceTolerance
|
||||||
|
),
|
||||||
|
frames: List<Matrix44> = steps.frames(up0),
|
||||||
|
startCap: Boolean = true,
|
||||||
|
endCap: Boolean = true,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
val finalFrames = if (path.closed) frames + frames.first() else frames
|
||||||
|
|
||||||
|
val crossSections = List(finalFrames.size) {
|
||||||
|
val t = it / (finalFrames.size - 1.0)
|
||||||
|
val linearContour = contour(t).sampleLinear(contourDistanceTolerance)
|
||||||
|
linearContour.adaptivePositions()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add caps
|
||||||
|
if (!path.closed) {
|
||||||
|
if (startCap) {
|
||||||
|
triangulationWithFrame(
|
||||||
|
ShapeContour.fromPoints(crossSections.first(), true).shape.triangulation,
|
||||||
|
finalFrames.first(), false, writer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (endCap) {
|
||||||
|
triangulationWithFrame(
|
||||||
|
ShapeContour.fromPoints(crossSections.last(), true).shape.triangulation,
|
||||||
|
finalFrames.last(), false, writer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sides
|
||||||
|
var i = 0
|
||||||
|
(finalFrames zip crossSections).windowed(2, 1).forEach {
|
||||||
|
contourSegment(
|
||||||
|
it[0].second, it[1].second,
|
||||||
|
it[0].first, it[1].first,
|
||||||
|
i.toDouble() / crossSections.size,
|
||||||
|
(++i).toDouble() / crossSections.size,
|
||||||
|
writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrude a [contour] along a [path] specifying the number of steps.
|
||||||
|
*
|
||||||
|
* @param contour the cross-section of the mesh
|
||||||
|
* @param path the 3D path
|
||||||
|
* @param stepCount the number of steps along the [path]
|
||||||
|
* @param up0 the initial up-vector
|
||||||
|
* @param contourDistanceTolerance precision for calculating steps along
|
||||||
|
* [contour]. Lower tolerance results in higher precision.
|
||||||
|
* @param pathDistanceTolerance precision for calculating steps along
|
||||||
|
* [path]. Lower tolerance results in higher precision.
|
||||||
|
*/
|
||||||
|
fun TriangleMeshBuilder.extrudeContourStepsMorphed(
|
||||||
|
contour: (Double) -> ShapeContour,
|
||||||
|
path: Path3D,
|
||||||
|
stepCount: Int,
|
||||||
|
up0: Vector3,
|
||||||
|
startCap: Boolean = true,
|
||||||
|
endCap: Boolean = true,
|
||||||
|
contourDistanceTolerance: Double = 0.5,
|
||||||
|
pathDistanceTolerance: Double = 0.5
|
||||||
|
) = extrudeContourStepsMorphed(
|
||||||
|
contour,
|
||||||
|
path,
|
||||||
|
stepCount,
|
||||||
|
up0,
|
||||||
|
startCap = startCap,
|
||||||
|
endCap = endCap,
|
||||||
|
contourDistanceTolerance = contourDistanceTolerance,
|
||||||
|
pathDistanceTolerance = pathDistanceTolerance,
|
||||||
|
writer = this::write
|
||||||
|
)
|
||||||
|
|
||||||
116
orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt
Normal file
116
orx-mesh-generators/src/commonMain/kotlin/ExtrusionScaled.kt
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package org.openrndr.extra.meshgenerators
|
||||||
|
|
||||||
|
import org.openrndr.math.Matrix44
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
import org.openrndr.shape.Path3D
|
||||||
|
import org.openrndr.shape.ShapeContour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrude a [contour] along a [path] specifying the number of steps.
|
||||||
|
* The [scale] argument can be used to make variable width shapes.
|
||||||
|
* For example `scale = { t: Double -> 0.5 - 0.5 * cos(t * 2 * PI) }`
|
||||||
|
* produces an extruded shape that begins and ends with hairline thickness.
|
||||||
|
*
|
||||||
|
* @param contour the cross-section of the mesh
|
||||||
|
* @param path the 3D path
|
||||||
|
* @param stepCount the number of steps along the [path]
|
||||||
|
* @param up0 the initial up-vector
|
||||||
|
* @param contourDistanceTolerance precision for calculating steps along
|
||||||
|
* [contour]. Lower tolerance results in higher precision.
|
||||||
|
* @param pathDistanceTolerance precision for calculating steps along
|
||||||
|
* [path]. Lower tolerance results in higher precision.
|
||||||
|
* @param steps the resulting positions in the path
|
||||||
|
* @param frames a list of matrices holding the transformation matrices along
|
||||||
|
* the path
|
||||||
|
* @param startCap adds a start cap if set to true
|
||||||
|
* @param endCap adds an end cap if set to true
|
||||||
|
* @param scale A function that takes a curve `t` value and returns
|
||||||
|
* a scaling factor for [contour] at that point.
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun extrudeContourStepsScaled(
|
||||||
|
contour: ShapeContour,
|
||||||
|
path: Path3D,
|
||||||
|
stepCount: Int,
|
||||||
|
up0: Vector3,
|
||||||
|
contourDistanceTolerance: Double = 0.5,
|
||||||
|
pathDistanceTolerance: Double = 0.5,
|
||||||
|
steps: List<Vector3> = path.equidistantPositions(
|
||||||
|
stepCount,
|
||||||
|
pathDistanceTolerance
|
||||||
|
),
|
||||||
|
frames: List<Matrix44> = steps.frames(up0),
|
||||||
|
startCap: Boolean = true,
|
||||||
|
endCap: Boolean = true,
|
||||||
|
scale: (Double) -> Double = { _ -> 1.0 },
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
val finalFrames = if (path.closed) frames + frames.first() else frames
|
||||||
|
|
||||||
|
val linearContour = contour.sampleLinear(contourDistanceTolerance)
|
||||||
|
val linearContourPoints2D = linearContour.adaptivePositions()
|
||||||
|
|
||||||
|
val crossSections = List(finalFrames.size) {
|
||||||
|
val t = it / (finalFrames.size - 1.0)
|
||||||
|
linearContourPoints2D.map { p -> p * scale(t) }
|
||||||
|
}
|
||||||
|
if (!path.closed) {
|
||||||
|
if (startCap) {
|
||||||
|
triangulationWithFrame(
|
||||||
|
ShapeContour.fromPoints(crossSections.first(), true).shape.triangulation,
|
||||||
|
finalFrames.first(), false, writer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (endCap) {
|
||||||
|
triangulationWithFrame(
|
||||||
|
ShapeContour.fromPoints(crossSections.last(), true).shape.triangulation,
|
||||||
|
finalFrames.last(), false, writer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add sides
|
||||||
|
(finalFrames zip crossSections).windowed(2, 1).forEach {
|
||||||
|
contourSegment(it[0].second, it[1].second, it[0].first, it[1].first, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrude a [contour] along a [path] specifying the number of steps.
|
||||||
|
* The [scale] argument can be used to make variable width shapes.
|
||||||
|
* For example `scale = { t: Double -> 0.5 - 0.5 * cos(t * 2 * PI) }`
|
||||||
|
* produces an extruded shape that begins and ends with hairline thickness.
|
||||||
|
*
|
||||||
|
* @param contour the cross-section of the mesh
|
||||||
|
* @param path the 3D path
|
||||||
|
* @param stepCount the number of steps along the [path]
|
||||||
|
* @param up0 the initial up-vector
|
||||||
|
* @param contourDistanceTolerance precision for calculating steps along
|
||||||
|
* [contour]. Lower tolerance results in higher precision.
|
||||||
|
* @param pathDistanceTolerance precision for calculating steps along
|
||||||
|
* @param scale A function that converts `t` into a radius
|
||||||
|
* [path]. Lower tolerance results in higher precision.
|
||||||
|
*/
|
||||||
|
fun TriangleMeshBuilder.extrudeContourStepsScaled(
|
||||||
|
contour: ShapeContour,
|
||||||
|
path: Path3D,
|
||||||
|
stepCount: Int,
|
||||||
|
up0: Vector3,
|
||||||
|
contourDistanceTolerance: Double = 0.5,
|
||||||
|
pathDistanceTolerance: Double = 0.5,
|
||||||
|
startCap: Boolean = true,
|
||||||
|
endCap: Boolean = true,
|
||||||
|
scale: (Double) -> Double = { _ -> 1.0 }
|
||||||
|
) = extrudeContourStepsScaled(
|
||||||
|
contour,
|
||||||
|
path,
|
||||||
|
stepCount,
|
||||||
|
up0,
|
||||||
|
contourDistanceTolerance,
|
||||||
|
pathDistanceTolerance,
|
||||||
|
startCap = startCap,
|
||||||
|
endCap = endCap,
|
||||||
|
scale = scale,
|
||||||
|
writer = this::write
|
||||||
|
)
|
||||||
|
|
||||||
112
orx-mesh-generators/src/commonMain/kotlin/Helpers.kt
Normal file
112
orx-mesh-generators/src/commonMain/kotlin/Helpers.kt
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package org.openrndr.extra.meshgenerators
|
||||||
|
|
||||||
|
import org.openrndr.math.Matrix44
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
import org.openrndr.math.Vector4
|
||||||
|
import org.openrndr.math.transforms.normalMatrix
|
||||||
|
import org.openrndr.shape.Path3D
|
||||||
|
import org.openrndr.shape.Shape
|
||||||
|
import org.openrndr.shape.Triangle
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes two triangles to [writer] representing
|
||||||
|
* the quad formed by four vertices.
|
||||||
|
*
|
||||||
|
* @param v00 vertex (0, 0)
|
||||||
|
* @param v01 vertex (0, 1)
|
||||||
|
* @param v10 vertex (1, 0)
|
||||||
|
* @param v11 vertex (1, 1)
|
||||||
|
* @param faceNormal the face normal
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun quadToTris(
|
||||||
|
v00: Vector3,
|
||||||
|
v01: Vector3,
|
||||||
|
v10: Vector3,
|
||||||
|
v11: Vector3,
|
||||||
|
faceNormal: Vector3,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
writeTri(v11, v01, v00, Vector2.ZERO, Vector2.ZERO, Vector2.ZERO, faceNormal, writer)
|
||||||
|
writeTri(v00, v10, v11, Vector2.ZERO, Vector2.ZERO, Vector2.ZERO, faceNormal, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a triangle to [writer].
|
||||||
|
*
|
||||||
|
* @param v0 vertex 0
|
||||||
|
* @param v1 vertex 1
|
||||||
|
* @param v2 vertex 2
|
||||||
|
* @param tc0 texture coordinate 0
|
||||||
|
* @param tc1 texture coordinate 1
|
||||||
|
* @param tc2 texture coordinate 2
|
||||||
|
* @param faceNormal the face normal
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun writeTri(
|
||||||
|
v0: Vector3, v1: Vector3, v2: Vector3,
|
||||||
|
tc0: Vector2, tc1: Vector2, tc2: Vector2,
|
||||||
|
faceNormal: Vector3,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
writer(v0, faceNormal, tc0)
|
||||||
|
writer(v1, faceNormal, tc1)
|
||||||
|
writer(v2, faceNormal, tc2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a list of triangles transformed by the [frame]
|
||||||
|
* transformation matrix into [writer].
|
||||||
|
*
|
||||||
|
* @param triangulation the list of triangles to write
|
||||||
|
* @param frame a transformation matrix to apply to each triangle
|
||||||
|
* @param flipNormals generates inside-out geometry if true
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun triangulationWithFrame(
|
||||||
|
triangulation: List<Triangle>,
|
||||||
|
frame: Matrix44,
|
||||||
|
flipNormals: Boolean = true,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
val normalFrame = normalMatrix(frame)
|
||||||
|
val normalScale = if (!flipNormals) -1.0 else 1.0
|
||||||
|
val normal = ((normalFrame * Vector4(0.0, 0.0, normalScale, 0.0)).xyz)
|
||||||
|
for (triangle in triangulation) {
|
||||||
|
val t = if (!flipNormals) triangle else Triangle(triangle.x3, triangle.x2, triangle.x1)
|
||||||
|
writer((frame * t.x1.xy01).xyz, normal, Vector2.ZERO)
|
||||||
|
writer((frame * t.x2.xy01).xyz, normal, Vector2.ZERO)
|
||||||
|
writer((frame * t.x3.xy01).xyz, normal, Vector2.ZERO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds caps to an extruded shape
|
||||||
|
*
|
||||||
|
* @param linearShape the cross-section of the mesh
|
||||||
|
* @param path the 3D path
|
||||||
|
* @param startCap adds a start cap if set to true
|
||||||
|
* @param endCap adds an end cap if set to true
|
||||||
|
* @param frames a list of matrices holding the transformation matrices along
|
||||||
|
* the path
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun extrudeCaps(
|
||||||
|
linearShape: Shape,
|
||||||
|
path: Path3D,
|
||||||
|
startCap: Boolean,
|
||||||
|
endCap: Boolean,
|
||||||
|
frames: List<Matrix44>,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
if ((startCap || endCap) && !path.closed) {
|
||||||
|
val capTriangles = linearShape.triangulation
|
||||||
|
if (startCap) {
|
||||||
|
triangulationWithFrame(capTriangles, frames.first(), false, writer)
|
||||||
|
}
|
||||||
|
if (endCap) {
|
||||||
|
triangulationWithFrame(capTriangles, frames.last(), true, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
orx-mesh-generators/src/commonMain/kotlin/Segment.kt
Normal file
108
orx-mesh-generators/src/commonMain/kotlin/Segment.kt
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package org.openrndr.extra.meshgenerators
|
||||||
|
|
||||||
|
import org.openrndr.math.Matrix44
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes quads to [writer] creating a surface that connects
|
||||||
|
* [linearContour0] with [linearContour1].
|
||||||
|
* The positions and orientations of the two contours
|
||||||
|
* are defined by the [frame0] and [frame1] matrices.
|
||||||
|
*
|
||||||
|
* @param linearContour0 the first cross-section
|
||||||
|
* @param linearContour1 the second cross-section
|
||||||
|
* @param frame0 a transformation matrix with the pose of [linearContour0]
|
||||||
|
* @param frame1 a transformation matrix with the pose of [linearContour1]
|
||||||
|
* @param writer the vertex writer function
|
||||||
|
*/
|
||||||
|
fun contourSegment(
|
||||||
|
linearContour0: List<Vector2>,
|
||||||
|
linearContour1: List<Vector2>,
|
||||||
|
frame0: Matrix44,
|
||||||
|
frame1: Matrix44,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
for (i in linearContour0.indices) {
|
||||||
|
val v0a = linearContour0[i]
|
||||||
|
val v1a = linearContour0[(i + 1).mod(linearContour0.size)]
|
||||||
|
|
||||||
|
val v0b = linearContour1[i]
|
||||||
|
val v1b = linearContour1[(i + 1).mod(linearContour1.size)]
|
||||||
|
|
||||||
|
val v00a = (frame0 * v0a.xy01).xyz
|
||||||
|
val v01a = (frame0 * v1a.xy01).xyz
|
||||||
|
|
||||||
|
val v10b = (frame1 * v0b.xy01).xyz
|
||||||
|
val v11b = (frame1 * v1b.xy01).xyz
|
||||||
|
val faceNormal = ((v10b - v00a).normalized cross (v01a - v00a).normalized).normalized
|
||||||
|
quadToTris(v00a, v01a, v10b, v11b, faceNormal, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contour segment
|
||||||
|
*
|
||||||
|
* @param linearContour0
|
||||||
|
* @param linearContour1
|
||||||
|
* @param frame0
|
||||||
|
* @param frame1
|
||||||
|
* @param v0
|
||||||
|
* @param v1
|
||||||
|
* @param writer
|
||||||
|
*/
|
||||||
|
fun contourSegment(
|
||||||
|
linearContour0: List<Vector2>,
|
||||||
|
linearContour1: List<Vector2>,
|
||||||
|
frame0: Matrix44,
|
||||||
|
frame1: Matrix44,
|
||||||
|
v0: Double,
|
||||||
|
v1: Double,
|
||||||
|
writer: VertexWriter
|
||||||
|
) {
|
||||||
|
var i0 = 0
|
||||||
|
var i1 = 0
|
||||||
|
do {
|
||||||
|
val flipNormal = false
|
||||||
|
|
||||||
|
val vCurr0 = (frame0 * linearContour0[i0 % linearContour0.size].xy01).xyz
|
||||||
|
val vCurr1 = (frame1 * linearContour1[i1 % linearContour1.size].xy01).xyz
|
||||||
|
val vNext0 = (frame0 * linearContour0[(i0 + 1) % linearContour0.size].xy01).xyz
|
||||||
|
val vNext1 = (frame1 * linearContour1[(i1 + 1) % linearContour1.size].xy01).xyz
|
||||||
|
|
||||||
|
val tCurr0 = i0.toDouble() / linearContour0.size
|
||||||
|
val tCurr1 = i1.toDouble() / linearContour1.size
|
||||||
|
val tNext0 = (i0 + 1.0) / linearContour0.size
|
||||||
|
val tNext1 = (i1 + 1.0) / linearContour1.size
|
||||||
|
|
||||||
|
val uvCurr0 = Vector2(tCurr0, v0)
|
||||||
|
val uvCurr1 = Vector2(tCurr1, v1)
|
||||||
|
|
||||||
|
val vNext: Vector3
|
||||||
|
val uvNext: Vector2
|
||||||
|
|
||||||
|
if (tNext0 < tNext1 || (tNext0 == tNext1 && tCurr0 < tCurr1)) {
|
||||||
|
++i0
|
||||||
|
vNext = vNext0
|
||||||
|
uvNext = Vector2(i0.toDouble() / linearContour0.size, v0)
|
||||||
|
} else {
|
||||||
|
++i1
|
||||||
|
vNext = vNext1
|
||||||
|
uvNext = Vector2(i1.toDouble() / linearContour1.size, v1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val faceNormal = if (flipNormal)
|
||||||
|
(vNext - vCurr1).cross(vCurr0 - vCurr1)
|
||||||
|
else
|
||||||
|
(vNext - vCurr0).cross(vCurr1 - vCurr0)
|
||||||
|
|
||||||
|
writeTri(
|
||||||
|
vCurr0, vNext, vCurr1,
|
||||||
|
uvCurr0, uvNext, uvCurr1,
|
||||||
|
faceNormal.normalized,
|
||||||
|
writer
|
||||||
|
)
|
||||||
|
|
||||||
|
} while (i0 < linearContour0.size || i1 < linearContour1.size)
|
||||||
|
}
|
||||||
|
|
||||||
89
orx-mesh-generators/src/jvmDemo/kotlin/DemoExtrude05.kt
Normal file
89
orx-mesh-generators/src/jvmDemo/kotlin/DemoExtrude05.kt
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import org.openrndr.WindowMultisample
|
||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.draw.DrawPrimitive
|
||||||
|
import org.openrndr.draw.shadeStyle
|
||||||
|
import org.openrndr.extra.camera.Orbital
|
||||||
|
import org.openrndr.extra.meshgenerators.buildTriangleMesh
|
||||||
|
import org.openrndr.extra.meshgenerators.extrudeContourStepsScaled
|
||||||
|
import org.openrndr.extra.noise.Random
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
import org.openrndr.shape.Circle
|
||||||
|
import org.openrndr.shape.Path3D
|
||||||
|
import org.openrndr.shape.Segment3D
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extruded Bézier tubes grown on a morphing Bézier surface.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
configure {
|
||||||
|
width = 800
|
||||||
|
height = 800
|
||||||
|
multisample = WindowMultisample.SampleCount(8)
|
||||||
|
}
|
||||||
|
program {
|
||||||
|
val crossSection = Circle(0.0, 0.0, 0.2).contour
|
||||||
|
|
||||||
|
extend(Orbital()) {
|
||||||
|
this.eye = Vector3(0.0, 3.0, 7.0)
|
||||||
|
this.lookAt = Vector3(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
extend {
|
||||||
|
drawer.shadeStyle = shadeStyle {
|
||||||
|
fragmentTransform = """
|
||||||
|
x_fill = va_color;
|
||||||
|
x_fill.rgb *= v_viewNormal.z;
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
val m = buildTriangleMesh {
|
||||||
|
val beziers = List(4) { curveId ->
|
||||||
|
val n = List(12) {
|
||||||
|
Random.simplex(it * 7.387, curveId * 5.531 + seconds * 0.05) * 10.0
|
||||||
|
}
|
||||||
|
Segment3D(
|
||||||
|
Vector3(n[0], n[1], n[2]),
|
||||||
|
Vector3(n[3], n[4], n[5]),
|
||||||
|
Vector3(n[6], n[7], n[8]),
|
||||||
|
Vector3(n[9], n[10], n[11])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until 20) {
|
||||||
|
val t = i / (20.0 - 1.0)
|
||||||
|
val path = Path3D(
|
||||||
|
listOf(
|
||||||
|
Segment3D(
|
||||||
|
beziers[0].position(t),
|
||||||
|
beziers[1].position(t),
|
||||||
|
beziers[2].position(t),
|
||||||
|
beziers[3].position(t)
|
||||||
|
)
|
||||||
|
), false
|
||||||
|
)
|
||||||
|
color = if(i % 2 == 0) ColorRGBa.PINK else ColorRGBa.WHITE.shade(0.1)
|
||||||
|
extrudeContourStepsScaled(
|
||||||
|
crossSection,
|
||||||
|
path,
|
||||||
|
120,
|
||||||
|
Vector3.UNIT_Y,
|
||||||
|
contourDistanceTolerance = 0.05,
|
||||||
|
pathDistanceTolerance = 0.05,
|
||||||
|
scale = { tt: Double -> 0.5 - 0.5 * cos(tt * 2 * PI) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawer.vertexBuffer(m, DrawPrimitive.TRIANGLES)
|
||||||
|
|
||||||
|
// Remember to free the memory! Otherwise, the computer will quickly run out of RAM.
|
||||||
|
m.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
orx-mesh-generators/src/jvmDemo/kotlin/DemoExtrude06.kt
Normal file
130
orx-mesh-generators/src/jvmDemo/kotlin/DemoExtrude06.kt
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import org.openrndr.WindowMultisample
|
||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.draw.*
|
||||||
|
import org.openrndr.extra.camera.Orbital
|
||||||
|
import org.openrndr.extra.meshgenerators.buildTriangleMesh
|
||||||
|
import org.openrndr.extra.meshgenerators.extrudeContourStepsMorphed
|
||||||
|
import org.openrndr.extra.noise.Random
|
||||||
|
import org.openrndr.extra.noise.simplex
|
||||||
|
import org.openrndr.math.Polar
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.math.Vector3
|
||||||
|
import org.openrndr.math.transforms.transform
|
||||||
|
import org.openrndr.shape.Circle
|
||||||
|
import org.openrndr.shape.Path3D
|
||||||
|
import org.openrndr.shape.Segment3D
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo extrudeContourStepsMorphed which allows to create a mesh with a morphing cross-section
|
||||||
|
* based on the t value along a Path3D. In other words, a tube in which the cross-section does not need
|
||||||
|
* to be constant, but can be scaled, rotated and displaced along its curvy axis.
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
configure {
|
||||||
|
width = 800
|
||||||
|
height = 800
|
||||||
|
multisample = WindowMultisample.SampleCount(8)
|
||||||
|
}
|
||||||
|
program {
|
||||||
|
Random.seed = System.currentTimeMillis().toString()
|
||||||
|
|
||||||
|
val texture = loadImage("demo-data/images/peopleCity01.jpg").also {
|
||||||
|
it.wrapU = WrapMode.REPEAT
|
||||||
|
it.wrapV = WrapMode.REPEAT
|
||||||
|
it.filterMag = MagnifyingFilter.LINEAR
|
||||||
|
it.filterMin = MinifyingFilter.LINEAR
|
||||||
|
}
|
||||||
|
|
||||||
|
val shader = shadeStyle {
|
||||||
|
fragmentTransform = """
|
||||||
|
// A. Passed color
|
||||||
|
x_fill = va_color;
|
||||||
|
|
||||||
|
// B. Sample texture
|
||||||
|
//x_fill = texture(p_img, va_texCoord0.yx * vec2(20.0, 1.0));
|
||||||
|
|
||||||
|
// Show (add) UV coords
|
||||||
|
x_fill.rb += va_texCoord0.yx;
|
||||||
|
|
||||||
|
// Vertical lighting
|
||||||
|
x_fill.rgb *= dot(vec3(0.0, 1.0, 0.0), v_viewNormal.xyz) * 0.3 + 0.7;
|
||||||
|
|
||||||
|
// Black fog (darken far away shapes)
|
||||||
|
x_fill.rgb += v_viewPosition.z * 0.05;
|
||||||
|
""".trimIndent()
|
||||||
|
parameter("img", texture)
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(Orbital()) {
|
||||||
|
eye = Vector3(0.0, 3.0, 7.0)
|
||||||
|
lookAt = Vector3(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
extend {
|
||||||
|
drawer.stroke = null
|
||||||
|
|
||||||
|
val path = get3DPath(10.0, seconds * 0.05, 400)
|
||||||
|
val tubes = makeTubes(path, seconds * 0.2)
|
||||||
|
|
||||||
|
shader.parameter("seconds", seconds * 0.1)
|
||||||
|
drawer.fill = ColorRGBa.WHITE
|
||||||
|
drawer.shadeStyle = shader
|
||||||
|
tubes.forEachIndexed { i, vb ->
|
||||||
|
shader.parameter("offset", i * 0.3 + 0.2)
|
||||||
|
|
||||||
|
// Mirror the mesh 5 times
|
||||||
|
repeat(5) {
|
||||||
|
drawer.isolated {
|
||||||
|
rotate(Vector3.UNIT_Z, it * 72.0)
|
||||||
|
vertexBuffer(vb, DrawPrimitive.TRIANGLES)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember to free the memory! Otherwise, the computer will quickly run out of RAM.
|
||||||
|
vb.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val crossSection = Circle(Vector2.ZERO, 0.1).contour.transform(
|
||||||
|
transform { scale(5.0, 1.0, 1.0) }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create simplex-based 3D path
|
||||||
|
fun get3DPath(scale: Double, time: Double, steps: Int): Path3D {
|
||||||
|
val mult = 0.005
|
||||||
|
val points = List(steps) { Vector3.simplex(337, time + it * mult) * scale }
|
||||||
|
return Path3D(points.windowed(2).map { Segment3D(it[0], it[1]) }, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create 3 spinning tubes around path
|
||||||
|
fun makeTubes(path: Path3D, seconds: Double) = List(3) { i ->
|
||||||
|
buildTriangleMesh {
|
||||||
|
val degrees = seconds * 60
|
||||||
|
val crossSection = crossSection
|
||||||
|
|
||||||
|
color = listOf(ColorRGBa.RED, ColorRGBa.GREEN, ColorRGBa.BLUE)[i]
|
||||||
|
extrudeContourStepsMorphed(
|
||||||
|
{ t: Double ->
|
||||||
|
val turns = t * 360 * 10
|
||||||
|
val cosEnv = 0.5 - 0.49 * cos(t * 2 * PI)
|
||||||
|
crossSection.transform(transform {
|
||||||
|
val theta = i * 120.0
|
||||||
|
translate(Polar(turns - degrees + theta, 0.6 * cosEnv).cartesian)
|
||||||
|
rotate(-turns - degrees)
|
||||||
|
scale(cosEnv)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
path,
|
||||||
|
path.segments.size,
|
||||||
|
Vector3.UNIT_Y,
|
||||||
|
contourDistanceTolerance = 0.01
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user