Skip to content

Commit

Permalink
[orx-mesh] Split MeshData types from orx-obj-loader
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinRNDR committed Sep 13, 2024
1 parent 3588aec commit 8238207
Show file tree
Hide file tree
Showing 16 changed files with 467 additions and 173 deletions.
26 changes: 26 additions & 0 deletions orx-mesh/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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)
implementation(project(":orx-shapes"))
}
}

val jvmDemo by getting {
dependencies {
api(libs.openrndr.shape)
implementation(project(":orx-shapes"))
implementation(project(":orx-mesh-generators"))
implementation(project(":orx-camera"))
implementation(project(":orx-noise"))
}
}
}
}
30 changes: 30 additions & 0 deletions orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.openrndr.extra.objloader

interface ICompoundMeshData {
val vertexData: IVertexData
val compounds: Map<String, IMeshData>

fun triangulate(): ICompoundMeshData
}

class CompoundMeshData(
override val vertexData: VertexData,
override val compounds: Map<String, MeshData>
) : ICompoundMeshData {

override fun triangulate(): CompoundMeshData {
return CompoundMeshData(vertexData, compounds.mapValues {
it.value.triangulate()
})
}
}

class MutableCompoundMeshData(
override val vertexData: MutableVertexData,
override val compounds: MutableMap<String, MutableMeshData>
) : ICompoundMeshData {

override fun triangulate(): MutableCompoundMeshData {
TODO("Not yet implemented")
}
}
24 changes: 24 additions & 0 deletions orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.openrndr.extra.objloader

import org.openrndr.draw.VertexBuffer
import org.openrndr.draw.vertexBuffer

fun ICompoundMeshData.toVertexBuffer(): VertexBuffer {
val triangulated = this.triangulate()

val triangleCount = triangulated.compounds.values.sumOf { it.polygons.size }

val vertexBuffer = vertexBuffer(objVertexFormat, triangleCount * 3)

var elementOffset = 0
for (compound in compounds) {
compound.value.toVertexBuffer(elementOffset, vertexBuffer)
elementOffset += compound.value.polygons.size * 3
}

return vertexBuffer
}

fun ICompoundMeshData.flattenPolygons(): Map<String, List<IPolygon>> {
return compounds.mapValues { it.value.flattenPolygons() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.round

data class IndexedPolygon(
val positions: IntArray, val textureCoords: IntArray, val normals: IntArray
) {

fun base(vertexData: VertexData): Matrix44 {
interface IIndexedPolygon {
val positions: List<Int>
val textureCoords: List<Int>
val normals: List<Int>
val colors: List<Int>
val tangents: List<Int>
val bitangents: List<Int>

fun base(vertexData: IVertexData): Matrix44 {
val u = (vertexData.positions[positions[1]] - vertexData.positions[positions[0]])
val v = (vertexData.positions[positions[positions.size - 1]] - vertexData.positions[positions[0]])
val normal = u.cross(v)
Expand All @@ -26,7 +30,7 @@ data class IndexedPolygon(
)
}

fun isPlanar(vertexData: VertexData, eps: Double = 1E-2): Boolean {
fun isPlanar(vertexData: IVertexData, eps: Double = 1E-2): Boolean {
fun normal(i: Int): Vector3 {
val p0 = vertexData.positions[positions[(i - 1).mod(positions.size)]]
val p1 = vertexData.positions[positions[(i).mod(positions.size)]]
Expand All @@ -44,7 +48,7 @@ data class IndexedPolygon(
}
}

fun isConvex(vertexData: VertexData): Boolean {
fun isConvex(vertexData: IVertexData): Boolean {
val planar = base(vertexData).inversed

fun p(v: Vector3): Vector2 {
Expand All @@ -71,7 +75,7 @@ data class IndexedPolygon(
return false
}
var angle = newDirection - oldDirection
if (angle <= -Math.PI)
if (angle <= -PI)
angle += PI * 2.0

if (angle > PI) {
Expand All @@ -95,37 +99,69 @@ data class IndexedPolygon(
return abs(round(angleSum / (2 * PI))) == 1.0
}

fun tessellate(vertexData: VertexData): List<IndexedPolygon> {
fun toPolygon(vertexData: IVertexData): IPolygon
}


data class IndexedPolygon(
override val positions: List<Int>,
override val textureCoords: List<Int>,
override val normals: List<Int>,
override val colors: List<Int>,
override val tangents: List<Int>,
override val bitangents: List<Int>

) : IIndexedPolygon {

fun tessellate(vertexData: IVertexData): List<IndexedPolygon> {
val points = vertexData.positions.slice(positions.toList())
val triangles = org.openrndr.shape.triangulate(listOf(points))

return triangles.windowed(3, 3).map {
IndexedPolygon(
positions.sliceArray(it),
if (textureCoords.isNotEmpty()) textureCoords.sliceArray(it) else intArrayOf(),
if (normals.isNotEmpty()) normals.sliceArray(it) else intArrayOf()
positions.slice(it),
if (textureCoords.isNotEmpty()) textureCoords.slice(it) else listOf(),
if (normals.isNotEmpty()) normals.slice(it) else listOf(),
if (colors.isNotEmpty()) colors.slice(it) else listOf(),
if (tangents.isNotEmpty()) tangents.slice(it) else listOf(),
if (bitangents.isNotEmpty()) bitangents.slice(it) else listOf()
)
}
}

fun triangulate(vertexData: VertexData): List<IndexedPolygon> {
fun triangulate(vertexData: IVertexData): List<IndexedPolygon> {
return when {
positions.size == 3 -> listOf(this)
isPlanar(vertexData) && isConvex(vertexData) -> {
val triangleCount = positions.size - 2
(0 until triangleCount).map {
IndexedPolygon(
intArrayOf(positions[0], positions[it + 1], positions[it + 2]),
listOf(positions[0], positions[it + 1], positions[it + 2]),
listOfNotNull(
textureCoords.getOrNull(0),
textureCoords.getOrNull(it),
textureCoords.getOrNull(it + 1)
).toIntArray(),
),
listOfNotNull(
normals.getOrNull(0),
normals.getOrNull(it),
normals.getOrNull(it + 1)
).toIntArray(),
),
listOfNotNull(
colors.getOrNull(0),
colors.getOrNull(it + 1),
colors.getOrNull(it + 2)
),
listOfNotNull(
tangents.getOrNull(0),
tangents.getOrNull(it + 1),
tangents.getOrNull(it + 2)
),
listOfNotNull(
bitangents.getOrNull(0),
bitangents.getOrNull(it + 1),
bitangents.getOrNull(it + 2)
),
)
}
}
Expand All @@ -134,11 +170,31 @@ data class IndexedPolygon(
}
}

fun toPolygon(vertexData: VertexData): Polygon {
override fun toPolygon(vertexData: IVertexData): Polygon {
return Polygon(
vertexData.positions.slice(positions.toList()).toTypedArray(),
vertexData.normals.slice(normals.toList()).toTypedArray(),
vertexData.textureCoords.slice(textureCoords.toList()).toTypedArray()
vertexData.positions.slice(positions),
vertexData.normals.slice(normals),
vertexData.textureCoords.slice(textureCoords),
vertexData.colors.slice(colors)
)
}
}

data class MutableIndexedPolygon(
override val positions: MutableList<Int>,
override val textureCoords: MutableList<Int>,
override val normals: MutableList<Int>,
override val colors: MutableList<Int>,
override val tangents: MutableList<Int>,
override val bitangents: MutableList<Int>
) : IIndexedPolygon {

override fun toPolygon(vertexData: IVertexData): MutablePolygon {
return MutablePolygon(
vertexData.positions.slice(positions).toMutableList(),
vertexData.normals.slice(normals).toMutableList(),
vertexData.textureCoords.slice(textureCoords).toMutableList(),
vertexData.colors.slice(colors).toMutableList()
)
}
}
41 changes: 41 additions & 0 deletions orx-mesh/src/commonMain/kotlin/MeshData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.openrndr.extra.objloader

import kotlin.jvm.JvmRecord

interface IMeshData {
val vertexData: IVertexData
val polygons: List<IIndexedPolygon>
fun triangulate(): IMeshData
fun flattenPolygons(): List<IPolygon>
}

@JvmRecord
data class MeshData(
override val vertexData: VertexData,
override val polygons: List<IndexedPolygon>,
) : IMeshData {
override fun triangulate(): MeshData {
return copy(polygons = polygons.flatMap { polygon -> polygon.triangulate(vertexData) })
}

override fun flattenPolygons(): List<Polygon> {
return polygons.map { ip ->
ip.toPolygon(vertexData)
}
}
}


data class MutableMeshData(
override val vertexData: MutableVertexData,
override val polygons: MutableList<IndexedPolygon>
) : IMeshData {
override fun triangulate(): MutableMeshData {
return copy(polygons = polygons.flatMap { it.triangulate(vertexData) }.toMutableList())
}

override fun flattenPolygons(): List<Polygon> {
return polygons.map { it.toPolygon(vertexData) }

}
}
50 changes: 50 additions & 0 deletions orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.openrndr.extra.objloader

import org.openrndr.draw.VertexBuffer
import org.openrndr.draw.VertexFormat
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Vector2

/**
* The [VertexFormat] for a [VertexBuffer] with positions, normals and texture coordinates.
*/
internal val objVertexFormat = vertexFormat {
position(3)
normal(3)
textureCoordinate(2)
}

/**
* Converts a [MeshData] instance into a [VertexBuffer]
*/
fun IMeshData.toVertexBuffer(elementOffset: Int = 0, vertexBuffer: VertexBuffer? = null): VertexBuffer {
val objects = triangulate().flattenPolygons()
val triangleCount = objects.size
val vertexBuffer = vertexBuffer ?: vertexBuffer(objVertexFormat, triangleCount * 3)

vertexBuffer.put(elementOffset) {
objects.forEach {

for (i in it.positions.indices) {
write(it.positions[i])
if (it.normals.isNotEmpty()) {
write(it.normals[i])
} else {
val d0 = it.positions[2] - it.positions[0]
val d1 = it.positions[1] - it.positions[0]
write(d0.normalized.cross(d1.normalized).normalized)
}
if (it.textureCoords.isNotEmpty()) {
write(it.textureCoords[i])
} else {
write(Vector2.ZERO)
}
}
}
}


vertexBuffer.shadow.destroy()
return vertexBuffer
}
Loading

0 comments on commit 8238207

Please sign in to comment.