diff --git a/orx-mesh-noise/build.gradle.kts b/orx-mesh-noise/build.gradle.kts new file mode 100644 index 000000000..33f235401 --- /dev/null +++ b/orx-mesh-noise/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + org.openrndr.extra.convention.`kotlin-multiplatform` +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + api(libs.openrndr.application) + api(libs.openrndr.math) + api(libs.openrndr.shape) + api(project(":orx-mesh")) + } + } + + val jvmDemo by getting { + dependencies { + api(libs.openrndr.shape) + implementation(project(":orx-shapes")) + implementation(project(":orx-mesh")) + implementation(project(":orx-mesh-generators")) + implementation(project(":orx-obj-loader")) + implementation(project(":orx-noise")) + implementation(project(":orx-camera")) + } + } + } +} diff --git a/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt b/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt new file mode 100644 index 000000000..7c87c261e --- /dev/null +++ b/orx-mesh-noise/src/commonMain/kotlin/MeshNoise.kt @@ -0,0 +1,65 @@ +package org.openrndr.extra.mesh.noise + +import org.openrndr.extra.mesh.IIndexedPolygon +import org.openrndr.extra.mesh.IMeshData +import org.openrndr.extra.mesh.IVertexData +import org.openrndr.math.Vector3 +import kotlin.math.sqrt +import kotlin.random.Random + +/** + * Generate a uniformly distributed barycentric coordinate + * @param random a random number generator + */ +fun uniformBarycentric(random: Random = Random.Default): Vector3 { + val u = random.nextDouble() + val v = random.nextDouble() + val su0 = sqrt(u) + val b0 = 1.0 - su0 + val b1 = v * su0 + return Vector3(b0, b1, 1.0 - b0 - b1) +} + +/** + * Generate a uniformly distributed point that lies inside this [IIndexedPolygon] + * @param vertexData vertex data used to resolve positions + * @param random a random number generator + */ +fun IIndexedPolygon.uniform(vertexData: IVertexData, random: Random = Random.Default): Vector3 { + require(positions.size == 3) { "polygon must be a triangle"} + + val x = vertexData.positions.slice(positions) + val b = uniformBarycentric(random) + return x[0] * b.x + x[1] * b.y + x[2] * b.z +} + +internal fun IIndexedPolygon.area(vertexData: IVertexData): Double { + require(positions.size == 3) { "polygon must be a triangle"} + val x = vertexData.positions.slice(positions) + val u = x[1] - x[0] + val v = x[2] - x[0] + return u.areaBetween(v) / 2.0 +} + +/** + * Generate points on the surface described by the mesh data + */ +fun IMeshData.uniform(count: Int, random: Random = Random.Default): List { + val triangulated = triangulate() + val result = mutableListOf() + val totalArea = triangulated.polygons.sumOf { it.area(vertexData) } + val randoms = (0 until count).map { + random.nextDouble(totalArea) + }.sorted() + + var idx = 0 + var sum = 0.0 + for (t in triangulated.polygons) { + sum += t.area(vertexData) + while (idx <= randoms.lastIndex && sum > randoms[idx]) { + result.add(t.uniform(vertexData, random)) + idx++ + } + } + return result +} \ No newline at end of file diff --git a/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt b/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt new file mode 100644 index 000000000..a2d7e497a --- /dev/null +++ b/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt @@ -0,0 +1,43 @@ +import org.openrndr.application +import org.openrndr.draw.DrawPrimitive +import org.openrndr.draw.isolated +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.camera.Orbital +import org.openrndr.extra.mesh.loadOBJMeshData +import org.openrndr.extra.mesh.noise.uniform +import org.openrndr.extra.meshgenerators.sphereMesh +import org.openrndr.math.Vector3 +import java.io.File +import kotlin.random.Random + +/** + * Demonstrate uniform point on mesh generation + */ +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val mesh = loadOBJMeshData(File("demo-data/obj-models/suzanne/Suzanne.obj")).toMeshData() + val points = mesh.uniform(1000, Random(0)) + + val sphere = sphereMesh(radius = 0.1) + extend(Orbital()) { + eye = Vector3(0.0, 0.0, 2.0) + } + extend { + drawer.shadeStyle = shadeStyle { + fragmentTransform = "x_fill = vec4(v_viewNormal*0.5+0.5, 1.0);" + } + for (point in points) { + drawer.isolated { + drawer.translate(point) + drawer.vertexBuffer(sphere, DrawPrimitive.TRIANGLES) + } + } + } + } + } +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt b/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt index c92d3563c..9d0dfaeb2 100644 --- a/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt +++ b/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh /** * Compound mesh data interface @@ -8,6 +8,8 @@ interface ICompoundMeshData { val compounds: Map fun triangulate(): ICompoundMeshData + + fun toMeshData(): IMeshData } class CompoundMeshData( @@ -15,15 +17,15 @@ class CompoundMeshData( override val compounds: Map ) : ICompoundMeshData { - init { - - } - override fun triangulate(): CompoundMeshData { return CompoundMeshData(vertexData, compounds.mapValues { it.value.triangulate() }) } + + override fun toMeshData(): MeshData { + return MeshData(vertexData, compounds.values.flatMap { it.polygons }) + } } class MutableCompoundMeshData( @@ -36,6 +38,11 @@ class MutableCompoundMeshData( vertexData, compounds.mapValues { it.value.triangulate() - }.toMutableMap()) + }.toMutableMap() + ) + } + + override fun toMeshData(): IMeshData { + return MutableMeshData(vertexData, compounds.values.flatMap { it.polygons }.toMutableList()) } } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt index 6307a06c7..0df55f699 100644 --- a/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt +++ b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.draw.VertexBuffer import org.openrndr.draw.vertexBuffer diff --git a/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt index aaa83f374..da7222e36 100644 --- a/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt +++ b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.math.Matrix44 import org.openrndr.math.Vector2 diff --git a/orx-mesh/src/commonMain/kotlin/MeshData.kt b/orx-mesh/src/commonMain/kotlin/MeshData.kt index ea164ec5c..d5446e08d 100644 --- a/orx-mesh/src/commonMain/kotlin/MeshData.kt +++ b/orx-mesh/src/commonMain/kotlin/MeshData.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import kotlin.jvm.JvmRecord @@ -21,7 +21,10 @@ data class MeshData( override val polygons: List, ) : IMeshData { override fun triangulate(): MeshData { - return copy(polygons = polygons.flatMap { polygon -> polygon.triangulate(vertexData) }) + return if (isTriangular()) { + this + } else + copy(polygons = polygons.flatMap { polygon -> polygon.triangulate(vertexData) }) } override fun toPolygons(): List { @@ -40,7 +43,11 @@ data class MutableMeshData( override val polygons: MutableList ) : IMeshData { override fun triangulate(): MutableMeshData { - return copy(polygons = polygons.flatMap { it.triangulate(vertexData) }.toMutableList()) + return if (isTriangular()) { + this + } else { + copy(polygons = polygons.flatMap { it.triangulate(vertexData) }.toMutableList()) + } } override fun toPolygons(): List { diff --git a/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt index 319852d55..089a07409 100644 --- a/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt +++ b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer @@ -18,7 +18,14 @@ internal val objVertexFormat = vertexFormat { } /** - * Converts a [MeshData] instance into a [VertexBuffer] + * Determine if [IMeshData] is triangular by checking if each polygon has exactly 3 vertices + */ +fun IMeshData.isTriangular(): Boolean { + return polygons.all { it.positions.size == 3 } +} + +/** + * Convert a [MeshData] instance into a [VertexBuffer] */ fun IMeshData.toVertexBuffer(elementOffset: Int = 0, vertexBuffer: VertexBuffer? = null): VertexBuffer { val objects = triangulate().toPolygons() diff --git a/orx-mesh/src/commonMain/kotlin/Polygon.kt b/orx-mesh/src/commonMain/kotlin/Polygon.kt index 44fdb34c4..dd42f2b91 100644 --- a/orx-mesh/src/commonMain/kotlin/Polygon.kt +++ b/orx-mesh/src/commonMain/kotlin/Polygon.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa import org.openrndr.math.Matrix44 diff --git a/orx-mesh/src/commonMain/kotlin/VertexData.kt b/orx-mesh/src/commonMain/kotlin/VertexData.kt index c3754da46..961900b11 100644 --- a/orx-mesh/src/commonMain/kotlin/VertexData.kt +++ b/orx-mesh/src/commonMain/kotlin/VertexData.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa import org.openrndr.math.Vector2 diff --git a/orx-mesh/src/commonMain/kotlin/Wireframe.kt b/orx-mesh/src/commonMain/kotlin/Wireframe.kt index 01f30910f..4209b6cdc 100644 --- a/orx-mesh/src/commonMain/kotlin/Wireframe.kt +++ b/orx-mesh/src/commonMain/kotlin/Wireframe.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.math.Vector3 diff --git a/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt b/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt index 249c0bba3..f6ff329d7 100644 --- a/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt +++ b/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt @@ -1,7 +1,9 @@ +package org.openrndr.extra.mesh + import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer -import org.openrndr.extra.objloader.Polygon -import org.openrndr.extra.objloader.objVertexFormat +import org.openrndr.extra.mesh.Polygon +import org.openrndr.extra.mesh.objVertexFormat import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 import java.nio.ByteBuffer diff --git a/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt b/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt index fddb1cf66..1057670fb 100644 --- a/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt +++ b/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer diff --git a/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt b/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt index 2f9181fea..892ff8bb9 100644 --- a/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt +++ b/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh /** * Convert mesh data to Wavefront OBJ representation diff --git a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt index 79701f22b..0e048e9f2 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt @@ -1,6 +1,6 @@ import org.openrndr.application -import org.openrndr.extra.objloader.loadOBJMeshData -import org.openrndr.extra.objloader.toObj +import org.openrndr.extra.mesh.loadOBJMeshData +import org.openrndr.extra.mesh.toObj import java.io.File fun main() { diff --git a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjLoader01.kt b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjLoader01.kt index 470f52459..dbf42ef2e 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjLoader01.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjLoader01.kt @@ -3,7 +3,7 @@ import org.openrndr.color.ColorRGBa import org.openrndr.draw.DepthTestPass import org.openrndr.draw.DrawPrimitive import org.openrndr.draw.shadeStyle -import org.openrndr.extra.objloader.loadOBJasVertexBuffer +import org.openrndr.extra.mesh.loadOBJasVertexBuffer import org.openrndr.math.Vector3 fun main() = application { diff --git a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver01.kt b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver01.kt index 68bef7b1a..5ae0145b8 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver01.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver01.kt @@ -1,7 +1,7 @@ import org.openrndr.application import org.openrndr.draw.loadFont -import org.openrndr.extra.objloader.loadOBJasVertexBuffer -import org.openrndr.extra.objloader.saveOBJ +import org.openrndr.extra.mesh.loadOBJasVertexBuffer +import org.openrndr.extra.mesh.saveOBJ fun main() = application { configure { diff --git a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver02.kt b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver02.kt index 6277e4c5f..8361a35b9 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver02.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoObjSaver02.kt @@ -2,7 +2,7 @@ import org.openrndr.application import org.openrndr.draw.loadFont import org.openrndr.extra.meshgenerators.buildTriangleMesh import org.openrndr.extra.meshgenerators.sphere -import org.openrndr.extra.objloader.saveOBJ +import org.openrndr.extra.mesh.saveOBJ fun main() = application { configure { diff --git a/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt b/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt index 5f0d5e8e7..190bea5df 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt @@ -8,9 +8,9 @@ import org.openrndr.draw.DrawPrimitive import org.openrndr.draw.TransformTarget import org.openrndr.draw.shadeStyle import org.openrndr.extra.camera.Orbital -import org.openrndr.extra.objloader.readObjMeshData -import org.openrndr.extra.objloader.loadOBJasVertexBuffer -import org.openrndr.extra.objloader.wireframe +import org.openrndr.extra.mesh.readObjMeshData +import org.openrndr.extra.mesh.loadOBJasVertexBuffer +import org.openrndr.extra.mesh.wireframe import org.openrndr.math.Vector3 import org.openrndr.shape.Path3D import java.io.File diff --git a/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt b/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt index f0acc8af0..f8479d163 100644 --- a/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt +++ b/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.draw.VertexBuffer import java.io.File diff --git a/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt b/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt index 4597b6429..66c21aed6 100644 --- a/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt +++ b/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt @@ -1,4 +1,4 @@ -package org.openrndr.extra.objloader +package org.openrndr.extra.mesh import org.openrndr.draw.VertexBuffer import java.io.File diff --git a/settings.gradle.kts b/settings.gradle.kts index 431a9848d..44838cc04 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,6 +48,7 @@ include( "orx-jvm:orx-keyframer", "orx-mesh", "orx-mesh-generators", + "orx-mesh-noise", "orx-jvm:orx-minim", "orx-jvm:orx-kotlin-parser", "orx-jvm:orx-midi",