From 8238207894295d89edca87768b9096f46258aeae Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Fri, 13 Sep 2024 22:41:31 +0200 Subject: [PATCH] [orx-mesh] Split MeshData types from orx-obj-loader --- orx-mesh/build.gradle.kts | 26 +++++ .../src/commonMain/kotlin/CompoundMeshData.kt | 30 +++++ .../kotlin/CompoundMeshDataExtensions.kt | 24 ++++ .../src/commonMain}/kotlin/IndexedPolygon.kt | 96 ++++++++++++---- orx-mesh/src/commonMain/kotlin/MeshData.kt | 41 +++++++ .../commonMain/kotlin/MeshDataExtensions.kt | 50 +++++++++ orx-mesh/src/commonMain/kotlin/Polygon.kt | 103 ++++++++++++++++++ orx-mesh/src/commonMain/kotlin/VertexData.kt | 59 ++++++++++ orx-mesh/src/commonMain/kotlin/Wireframe.kt | 13 +++ orx-obj-loader/build.gradle.kts | 1 + orx-obj-loader/src/main/kotlin/MeshData.kt | 18 --- .../src/main/kotlin/MeshDataExtensions.kt | 50 --------- orx-obj-loader/src/main/kotlin/OBJLoader.kt | 54 +++++++-- orx-obj-loader/src/main/kotlin/Polygon.kt | 56 ---------- orx-obj-loader/src/main/kotlin/VertexData.kt | 10 -- orx-obj-loader/src/main/kotlin/Wireframe.kt | 9 -- 16 files changed, 467 insertions(+), 173 deletions(-) create mode 100644 orx-mesh/build.gradle.kts create mode 100644 orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt create mode 100644 orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt rename {orx-obj-loader/src/main => orx-mesh/src/commonMain}/kotlin/IndexedPolygon.kt (56%) create mode 100644 orx-mesh/src/commonMain/kotlin/MeshData.kt create mode 100644 orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt create mode 100644 orx-mesh/src/commonMain/kotlin/Polygon.kt create mode 100644 orx-mesh/src/commonMain/kotlin/VertexData.kt create mode 100644 orx-mesh/src/commonMain/kotlin/Wireframe.kt delete mode 100644 orx-obj-loader/src/main/kotlin/MeshData.kt delete mode 100644 orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt delete mode 100644 orx-obj-loader/src/main/kotlin/Polygon.kt delete mode 100644 orx-obj-loader/src/main/kotlin/VertexData.kt delete mode 100644 orx-obj-loader/src/main/kotlin/Wireframe.kt diff --git a/orx-mesh/build.gradle.kts b/orx-mesh/build.gradle.kts new file mode 100644 index 000000000..99247e9f4 --- /dev/null +++ b/orx-mesh/build.gradle.kts @@ -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")) + } + } + } +} diff --git a/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt b/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt new file mode 100644 index 000000000..b521c260a --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/CompoundMeshData.kt @@ -0,0 +1,30 @@ +package org.openrndr.extra.objloader + +interface ICompoundMeshData { + val vertexData: IVertexData + val compounds: Map + + fun triangulate(): ICompoundMeshData +} + +class CompoundMeshData( + override val vertexData: VertexData, + override val compounds: Map +) : ICompoundMeshData { + + override fun triangulate(): CompoundMeshData { + return CompoundMeshData(vertexData, compounds.mapValues { + it.value.triangulate() + }) + } +} + +class MutableCompoundMeshData( + override val vertexData: MutableVertexData, + override val compounds: MutableMap +) : ICompoundMeshData { + + override fun triangulate(): MutableCompoundMeshData { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt new file mode 100644 index 000000000..22087b9f1 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt @@ -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> { + return compounds.mapValues { it.value.flattenPolygons() } +} \ No newline at end of file diff --git a/orx-obj-loader/src/main/kotlin/IndexedPolygon.kt b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt similarity index 56% rename from orx-obj-loader/src/main/kotlin/IndexedPolygon.kt rename to orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt index 3a6d6d07a..d49ce0a8e 100644 --- a/orx-obj-loader/src/main/kotlin/IndexedPolygon.kt +++ b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt @@ -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 + val textureCoords: List + val normals: List + val colors: List + val tangents: List + val bitangents: List + + 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) @@ -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)]] @@ -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 { @@ -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) { @@ -95,37 +99,69 @@ data class IndexedPolygon( return abs(round(angleSum / (2 * PI))) == 1.0 } - fun tessellate(vertexData: VertexData): List { + fun toPolygon(vertexData: IVertexData): IPolygon +} + + +data class IndexedPolygon( + override val positions: List, + override val textureCoords: List, + override val normals: List, + override val colors: List, + override val tangents: List, + override val bitangents: List + + ) : IIndexedPolygon { + + fun tessellate(vertexData: IVertexData): List { 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 { + fun triangulate(vertexData: IVertexData): List { 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) + ), ) } } @@ -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, + override val textureCoords: MutableList, + override val normals: MutableList, + override val colors: MutableList, + override val tangents: MutableList, + override val bitangents: MutableList +) : 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() ) } } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/MeshData.kt b/orx-mesh/src/commonMain/kotlin/MeshData.kt new file mode 100644 index 000000000..6b46e7cee --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/MeshData.kt @@ -0,0 +1,41 @@ +package org.openrndr.extra.objloader + +import kotlin.jvm.JvmRecord + +interface IMeshData { + val vertexData: IVertexData + val polygons: List + fun triangulate(): IMeshData + fun flattenPolygons(): List +} + +@JvmRecord +data class MeshData( + override val vertexData: VertexData, + override val polygons: List, +) : IMeshData { + override fun triangulate(): MeshData { + return copy(polygons = polygons.flatMap { polygon -> polygon.triangulate(vertexData) }) + } + + override fun flattenPolygons(): List { + return polygons.map { ip -> + ip.toPolygon(vertexData) + } + } +} + + +data class MutableMeshData( + override val vertexData: MutableVertexData, + override val polygons: MutableList +) : IMeshData { + override fun triangulate(): MutableMeshData { + return copy(polygons = polygons.flatMap { it.triangulate(vertexData) }.toMutableList()) + } + + override fun flattenPolygons(): List { + return polygons.map { it.toPolygon(vertexData) } + + } +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt new file mode 100644 index 000000000..5397ef3b7 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt @@ -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 +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/Polygon.kt b/orx-mesh/src/commonMain/kotlin/Polygon.kt new file mode 100644 index 000000000..8d2635f96 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/Polygon.kt @@ -0,0 +1,103 @@ +package org.openrndr.extra.objloader + +import org.openrndr.color.ColorRGBa +import org.openrndr.math.Matrix44 +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.shape.Box +import kotlin.math.max +import kotlin.math.min + +interface IPolygon { + val positions: List + val normals: List + val textureCoords: List + val colors: List + val tangents: List + val bitangents: List + + fun transform(t: Matrix44): IPolygon +} + +/** + * A 3D Polygon + * + * @property positions Vertex 3D positions + * @property normals Vertex 3D normals + * @property textureCoords Vertex 2D texture coordinates + * @constructor Create empty 3D Polygon + */ +class Polygon( + override val positions: List = emptyList(), + override val normals: List = emptyList(), + override val textureCoords: List = emptyList(), + override val colors: List = emptyList(), + override val tangents: List = emptyList(), + override val bitangents: List = emptyList(), +) : IPolygon { + override fun transform(t: Matrix44): Polygon { + return Polygon(positions.map { (t * it.xyz1).div }, normals, textureCoords, colors, tangents, bitangents) + } + + /** + * Create a [MutablePolygon] by copying + */ + fun toMutablePolygon(): MutablePolygon { + return MutablePolygon( + positions.toMutableList(), + normals.toMutableList(), + textureCoords.toMutableList(), + colors.toMutableList(), + tangents.toMutableList(), + bitangents.toMutableList() + ) + } +} + +class MutablePolygon( + override val positions: MutableList = mutableListOf(), + override val normals: MutableList = mutableListOf(), + override val textureCoords: MutableList = mutableListOf(), + override val colors: MutableList = mutableListOf(), + override val tangents: MutableList = mutableListOf(), + override val bitangents: MutableList = mutableListOf() + +) : IPolygon { + override fun transform(t: Matrix44): MutablePolygon { + return MutablePolygon( + positions.map { (t * it.xyz1).div }.toMutableList(), + ArrayList(normals), + ArrayList(textureCoords), + ArrayList(colors), + ArrayList(tangents), + ArrayList(bitangents) + ) + } +} + + +/** + * Calculates the 3D bounding box of a list of [IPolygon]. + */ +fun bounds(polygons: List): Box { + var minX = Double.POSITIVE_INFINITY + var minY = Double.POSITIVE_INFINITY + var minZ = Double.POSITIVE_INFINITY + + var maxX = Double.NEGATIVE_INFINITY + var maxY = Double.NEGATIVE_INFINITY + var maxZ = Double.NEGATIVE_INFINITY + + polygons.forEach { + it.positions.forEach { pos -> + minX = min(minX, pos.x) + minY = min(minY, pos.y) + minZ = min(minZ, pos.z) + + maxX = max(maxX, pos.x) + maxY = max(maxY, pos.y) + maxZ = max(maxZ, pos.z) + } + } + return Box(Vector3(minX, minY, minZ), maxX - minX, maxY - minY, maxZ - minZ) +} diff --git a/orx-mesh/src/commonMain/kotlin/VertexData.kt b/orx-mesh/src/commonMain/kotlin/VertexData.kt new file mode 100644 index 000000000..682b04402 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/VertexData.kt @@ -0,0 +1,59 @@ +package org.openrndr.extra.objloader + +import org.openrndr.color.ColorRGBa +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 + + +/** + * Vertex data interface + */ +interface IVertexData { + /** + * Vertex positions + */ + val positions: List + + /** + * Vertex normals + */ + val normals: List + + /** + * Vertex texture coordinates + */ + val textureCoords: List + + /** + * Vertex colors + */ + val colors: List + + /** + * Vertex tangents + */ + val tangents: List + + /** + * Vertex bitangents + */ + val bitangents: List +} + +class VertexData( + override val positions: List = emptyList(), + override val normals: List = emptyList(), + override val textureCoords: List = emptyList(), + override val colors: List = emptyList(), + override val tangents: List = emptyList(), + override val bitangents: List = emptyList() +) : IVertexData + +class MutableVertexData( + override val positions: MutableList = mutableListOf(), + override val normals: MutableList = mutableListOf(), + override val textureCoords: MutableList = mutableListOf(), + override val colors: MutableList = mutableListOf(), + override val tangents: MutableList = mutableListOf(), + override val bitangents: MutableList = mutableListOf() +) : IVertexData \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/Wireframe.kt b/orx-mesh/src/commonMain/kotlin/Wireframe.kt new file mode 100644 index 000000000..f9c776d7e --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/Wireframe.kt @@ -0,0 +1,13 @@ +package org.openrndr.extra.objloader + +import org.openrndr.math.Vector3 + +fun IMeshData.wireframe(): List> { + return polygons.map { ip -> ip.toPolygon(this.vertexData).positions.toList() } +} + +fun ICompoundMeshData.wireframe(): List> { + return compounds.values.flatMap { + it.wireframe() + } +} \ No newline at end of file diff --git a/orx-obj-loader/build.gradle.kts b/orx-obj-loader/build.gradle.kts index e233a9a6b..8c340b2f0 100644 --- a/orx-obj-loader/build.gradle.kts +++ b/orx-obj-loader/build.gradle.kts @@ -6,6 +6,7 @@ dependencies { implementation(libs.openrndr.application) implementation(libs.openrndr.math) implementation(libs.openrndr.ffmpeg) + api(project(":orx-mesh")) demoImplementation(project(":orx-camera")) demoImplementation(project(":orx-mesh-generators")) } diff --git a/orx-obj-loader/src/main/kotlin/MeshData.kt b/orx-obj-loader/src/main/kotlin/MeshData.kt deleted file mode 100644 index 2170f5815..000000000 --- a/orx-obj-loader/src/main/kotlin/MeshData.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.openrndr.extra.objloader - -@JvmRecord -data class MeshData(val vertexData: VertexData, val polygonGroups: Map>) { - fun triangulate(): MeshData { - return copy(polygonGroups = polygonGroups.mapValues { - it.value.flatMap { polygon -> polygon.triangulate(vertexData) } - }) - } - - fun flattenPolygons(): Map> { - return polygonGroups.mapValues { - it.value.map { ip -> - ip.toPolygon(vertexData) - } - } - } -} \ No newline at end of file diff --git a/orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt b/orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt deleted file mode 100644 index da254721f..000000000 --- a/orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt +++ /dev/null @@ -1,50 +0,0 @@ -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. - */ -private val objVertexFormat = vertexFormat { - position(3) - normal(3) - textureCoordinate(2) -} - -/** - * Converts a [MeshData] instance into a [VertexBuffer] - */ -fun MeshData.toVertexBuffer() : VertexBuffer { - val objects = triangulate().flattenPolygons() - val triangleCount = objects.values.sumOf { it.size } - val vertexBuffer = vertexBuffer(objVertexFormat, triangleCount * 3) - - vertexBuffer.put { - objects.entries.forEach { - it.value.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 -} \ No newline at end of file diff --git a/orx-obj-loader/src/main/kotlin/OBJLoader.kt b/orx-obj-loader/src/main/kotlin/OBJLoader.kt index c6307b5de..8d1af4ca8 100644 --- a/orx-obj-loader/src/main/kotlin/OBJLoader.kt +++ b/orx-obj-loader/src/main/kotlin/OBJLoader.kt @@ -1,5 +1,6 @@ package org.openrndr.extra.objloader +import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer import org.openrndr.math.* import java.io.File @@ -10,7 +11,7 @@ import java.net.URL * Loads an OBJ file as a Map of names to lists of [Polygon]. * Use this method to access the loaded OBJ data from the CPU. */ -fun loadOBJ(fileOrUrl: String): Map> { +fun loadOBJ(fileOrUrl: String): Map> { return try { val url = URL(fileOrUrl) loadOBJ(url) @@ -43,15 +44,16 @@ fun loadOBJEx(file: File) = loadOBJMeshData(file.readLines()) fun loadOBJ(url: URL) = loadOBJ(url.readText().split("\n")) fun loadOBJEx(url: URL) = loadOBJMeshData(url.readText().split("\n")) -fun loadOBJ(lines: List): Map> = loadOBJMeshData(lines).triangulate().flattenPolygons() +fun loadOBJ(lines: List): Map> = loadOBJMeshData(lines).triangulate().flattenPolygons() fun loadOBJMeshData(file: File) = loadOBJMeshData(file.readLines()) -fun loadOBJMeshData(lines: List): MeshData { +fun loadOBJMeshData(lines: List): CompoundMeshData { val meshes = mutableMapOf>() val positions = mutableListOf() val normals = mutableListOf() val textureCoords = mutableListOf() + val colors = mutableListOf() var activeMesh = mutableListOf() lines.forEach { line -> @@ -60,7 +62,32 @@ fun loadOBJMeshData(lines: List): MeshData { if (tokens.isNotEmpty()) { when (tokens[0]) { - "v" -> positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble()) + "v" -> { + when (tokens.size) { + 3, 4 -> { + positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble()) + colors += ColorRGBa.WHITE + } + + 6 -> { + positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble()) + colors += ColorRGBa(tokens[4].toDouble(), tokens[5].toDouble(), tokens[6].toDouble()) + } + + 7 -> { + positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble()) + colors += ColorRGBa( + tokens[4].toDouble(), + tokens[5].toDouble(), + tokens[6].toDouble(), + tokens[7].toDouble() + ) + } + + else -> error("vertex has ${tokens.size - 1} components, loader only supports 3/4/6/7 components") + } + } + "vn" -> normals += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble()) "vt" -> textureCoords += Vector2(tokens[1].toDouble(), tokens[2].toDouble()) "g" -> { @@ -75,12 +102,16 @@ fun loadOBJMeshData(lines: List): MeshData { val hasPosition = (indices[0].getOrNull(0) ?: 0) != 0 val hasUV = (indices[0].getOrNull(1) ?: 0) != 0 val hasNormal = (indices[0].getOrNull(2) ?: 0) != 0 + val hasColor = hasPosition activeMesh.add( IndexedPolygon( - if (hasPosition) indices.map { it[0] - 1 }.toIntArray() else intArrayOf(), - if (hasUV) indices.map { it[1] - 1 }.toIntArray() else intArrayOf(), - if (hasNormal) indices.map { it[2] - 1 }.toIntArray() else intArrayOf() + if (hasPosition) indices.map { it[0] - 1 } else listOf(), + if (hasUV) indices.map { it[1] - 1 } else listOf(), + if (hasNormal) indices.map { it[2] - 1 } else listOf(), + if (hasColor) indices.map { it[0] - 1 } else listOf(), + emptyList(), + emptyList() ) ) @@ -93,8 +124,11 @@ fun loadOBJMeshData(lines: List): MeshData { } } - return MeshData( - VertexData(positions.toTypedArray(), normals.toTypedArray(), textureCoords.toTypedArray()), - meshes + val vertexData = VertexData(positions, normals, textureCoords, colors) + return CompoundMeshData( + vertexData, + meshes.mapValues { + MeshData(vertexData, it.value) + } ) } diff --git a/orx-obj-loader/src/main/kotlin/Polygon.kt b/orx-obj-loader/src/main/kotlin/Polygon.kt deleted file mode 100644 index 70af932db..000000000 --- a/orx-obj-loader/src/main/kotlin/Polygon.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.openrndr.extra.objloader - -import org.openrndr.math.Matrix44 -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 -import kotlin.math.max -import kotlin.math.min - -/** - * A 3D Polygon - * - * @property positions Vertex 3D positions - * @property normals Vertex 3D normals - * @property textureCoords Vertex 2D texture coordinates - * @constructor Create empty 3D Polygon - */ -class Polygon( - val positions: Array = emptyArray(), - val normals: Array = emptyArray(), - val textureCoords: Array = emptyArray() -) { - fun transform(t: Matrix44): Polygon { - return Polygon(positions.map { (t * it.xyz1).div }.toTypedArray(), normals, textureCoords) - } -} - -/** - * A 3D Box defined by an anchor point ([corner]), [width], [height] and [depth]. - */ -class Box(val corner: Vector3, val width: Double, val height: Double, val depth: Double) - -/** - * Calculates the 3D bounding box of a list of [Polygon]. - */ -fun bounds(polygons: List): Box { - var minX = Double.POSITIVE_INFINITY - var minY = Double.POSITIVE_INFINITY - var minZ = Double.POSITIVE_INFINITY - - var maxX = Double.NEGATIVE_INFINITY - var maxY = Double.NEGATIVE_INFINITY - var maxZ = Double.NEGATIVE_INFINITY - - polygons.forEach { - it.positions.forEach { pos -> - minX = min(minX, pos.x) - minY = min(minY, pos.y) - minZ = min(minZ, pos.z) - - maxX = max(maxX, pos.x) - maxY = max(maxY, pos.y) - maxZ = max(maxZ, pos.z) - } - } - return Box(Vector3(minX, minY, minZ), maxX - minX, maxY - minY, maxZ - minZ) -} diff --git a/orx-obj-loader/src/main/kotlin/VertexData.kt b/orx-obj-loader/src/main/kotlin/VertexData.kt deleted file mode 100644 index 8baf0cf00..000000000 --- a/orx-obj-loader/src/main/kotlin/VertexData.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.openrndr.extra.objloader - -import org.openrndr.math.Vector2 -import org.openrndr.math.Vector3 - -class VertexData( - val positions: Array = emptyArray(), - val normals: Array = emptyArray(), - val textureCoords: Array = emptyArray() -) \ No newline at end of file diff --git a/orx-obj-loader/src/main/kotlin/Wireframe.kt b/orx-obj-loader/src/main/kotlin/Wireframe.kt deleted file mode 100644 index 81b7c28a5..000000000 --- a/orx-obj-loader/src/main/kotlin/Wireframe.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.openrndr.extra.objloader - -import org.openrndr.math.Vector3 - -fun MeshData.wireframe() : List> { - return polygonGroups.values.flatMap { - it.map { ip -> ip.toPolygon(this.vertexData).positions.toList() } - } -} \ No newline at end of file