diff --git a/orx-mesh-generators/build.gradle.kts b/orx-mesh-generators/build.gradle.kts index 452baa3fc..cea3718db 100644 --- a/orx-mesh-generators/build.gradle.kts +++ b/orx-mesh-generators/build.gradle.kts @@ -10,6 +10,7 @@ kotlin { api(libs.openrndr.application) api(libs.openrndr.math) implementation(project(":orx-shapes")) + api(project(":orx-mesh")) } } @@ -20,6 +21,7 @@ kotlin { implementation(project(":orx-mesh-generators")) implementation(project(":orx-camera")) implementation(project(":orx-noise")) + implementation(project(":orx-obj-loader")) } } } diff --git a/orx-mesh-generators/src/commonMain/kotlin/decal/Decal.kt b/orx-mesh-generators/src/commonMain/kotlin/decal/Decal.kt new file mode 100644 index 000000000..249deab60 --- /dev/null +++ b/orx-mesh-generators/src/commonMain/kotlin/decal/Decal.kt @@ -0,0 +1,201 @@ +package org.openrndr.extra.meshgenerators.decal + +import org.openrndr.extra.mesh.* +import org.openrndr.math.Matrix44 +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import kotlin.math.abs + +/** + * Create a decal mesh + * @param projectorMatrix + * @param size + */ +fun IMeshData.decal( + projectorMatrix: Matrix44, + size: Vector3 +): IVertexData { + require(isTriangular()) + + val projectorMatrixInverse = projectorMatrix.inversed + + val positions = vertexData.positions.slice(polygons.flatMap { it.positions }).map { + (projectorMatrixInverse * (it.xyz1)).div + } + val normals = vertexData.normals.slice(polygons.flatMap { it.normals }) + val textureCoords = vertexData.textureCoords.slice(polygons.flatMap { it.textureCoords }) + val colors = vertexData.colors.slice(polygons.flatMap { it.colors }) + val tangents = vertexData.tangents.slice(polygons.flatMap { it.tangents }) + val bitangents = vertexData.bitangents.slice(polygons.flatMap { it.bitangents }) + + var decalVertices: IVertexData = VertexData(positions, textureCoords, colors, normals, tangents, bitangents) + + decalVertices = decalVertices.clipToPlane(size, Vector3(1.0, 0.0, 0.0)) + decalVertices = decalVertices.clipToPlane(size, Vector3(-1.0, 0.0, 0.0)) + decalVertices = decalVertices.clipToPlane(size, Vector3(0.0, 1.0, 0.0)) + decalVertices = decalVertices.clipToPlane(size, Vector3(0.0, -1.0, 0.0)) + decalVertices = decalVertices.clipToPlane(size, Vector3(0.0, 0.0, 1.0)) + decalVertices = decalVertices.clipToPlane(size, Vector3(0.0, 0.0, -1.0)) + + + val decalMesh = MutableVertexData() + for (i in decalVertices.positions.indices) { + val v = decalVertices[i] + + val w = v.copy( + position = (projectorMatrix * v.position.xyz1).div, + textureCoord = v.position.xy / size.xy + Vector2(0.5) + ) + decalMesh.add(w) + } + return decalMesh +} + +fun IVertexData.clipToPlane( + size: Vector3, + plane: Vector3 +): IVertexData { + val outVertices = MutableVertexData() + val s = 0.5 * abs(size.dot(plane)) + + fun clip( + v0: Point, + v1: Point, p: Vector3, s: Double + ): Point { + + val d0 = v0.position.dot(p) - s; + val d1 = v1.position.dot(p) - s; + + val s0 = d0 / (d0 - d1) + + val v = Point( + v0.position + (v1.position - v0.position) * s0, + if (v0.textureCoord != null) { + v0.textureCoord!! + (v1.textureCoord!! - v0.textureCoord!!) * s0 + } else { + null + }, + if (v0.color != null) { + v0.color!! + (v1.color!! - v0.color!!) * s0 + } else { + null + }, + if (v0.normal != null) { + v0.normal!! + (v1.normal!! - v0.normal!!) * s0 + } else { + null + }, + if (v0.tangent != null) { + v0.tangent!! + (v1.tangent!! - v0.tangent!!) * s0 + } else { + null + }, + if (v0.bitangent != null) { + v0.bitangent!! + (v1.bitangent!! - v0.bitangent!!) * s0 + } else { + null + } + ) + return v + } + + for (i in positions.indices step 3) { + + val d1 = positions[i + 0].dot(plane) - s + val d2 = positions[i + 1].dot(plane) - s + val d3 = positions[i + 2].dot(plane) - s + + val v1Out = d1 > 0 + val v2Out = d2 > 0 + val v3Out = d3 > 0 + + val total = (if (v1Out) 1 else 0) + (if (v2Out) 1 else 0) + (if (v3Out) 1 else 0) + + when (total) { + 0 -> { + outVertices.add(this[i]) + outVertices.add(this[i + 1]) + outVertices.add(this[i + 2]) + } + + 1 -> { + if (v1Out) { + val nV1 = this[i + 1] + val nV2 = this[i + 2] + val nV3 = clip(this[i], nV1, plane, s) + val nV4 = clip(this[i], nV2, plane, s) + + outVertices.add(nV1) + outVertices.add(nV2) + outVertices.add(nV3) + + outVertices.add(nV4) + outVertices.add(nV3) + outVertices.add(nV2) + } + + if (v2Out) { + val nV1 = this[i]; + val nV2 = this[i + 2]; + val nV3 = clip(this[i + 1], nV1, plane, s) + val nV4 = clip(this[i + 1], nV2, plane, s) + + outVertices.add(nV3) + outVertices.add(nV2) + outVertices.add(nV1) + + outVertices.add(nV2) + outVertices.add(nV3) + outVertices.add(nV4) + } + + if (v3Out) { + val nV1 = this[i] + val nV2 = this[i + 1] + val nV3 = clip(this[i + 2], nV1, plane, s) + val nV4 = clip(this[i + 2], nV2, plane, s) + + outVertices.add(nV1) + outVertices.add(nV2) + outVertices.add(nV3) + + outVertices.add(nV4) + outVertices.add(nV3) + outVertices.add(nV2) + } + } + + 2 -> { + if (!v1Out) { + val nV1 = this[i] + val nV2 = clip(nV1, this[i + 1], plane, s) + val nV3 = clip(nV1, this[i + 2], plane, s) + outVertices.add(nV1) + outVertices.add(nV2) + outVertices.add(nV3) + } + + if (!v2Out) { + val nV1 = this[i + 1] + val nV2 = clip(nV1, this[i + 2], plane, s) + val nV3 = clip(nV1, this[i], plane, s) + outVertices.add(nV1) + outVertices.add(nV2) + outVertices.add(nV3) + } + + if (!v3Out) { + val nV1 = this[i + 2] + val nV2 = clip(nV1, this[i], plane, s) + val nV3 = clip(nV1, this[i + 1], plane, s) + outVertices.add(nV1) + outVertices.add(nV2) + outVertices.add(nV3) + } + } + else -> { + } + } + } + return outVertices +} \ No newline at end of file diff --git a/orx-mesh-generators/src/commonMain/kotlin/normals/MeshDataNormals.kt b/orx-mesh-generators/src/commonMain/kotlin/normals/MeshDataNormals.kt new file mode 100644 index 000000000..a9f34ec57 --- /dev/null +++ b/orx-mesh-generators/src/commonMain/kotlin/normals/MeshDataNormals.kt @@ -0,0 +1,63 @@ +package org.openrndr.extra.meshgenerators.normals + +import org.openrndr.extra.mesh.IMeshData +import org.openrndr.extra.mesh.IndexedPolygon +import org.openrndr.extra.mesh.MeshData +import org.openrndr.extra.mesh.VertexData +import org.openrndr.math.Vector3 + +/** + * Estimate per-vertex normals + */ +fun IMeshData.estimateNormals(): MeshData { + val normals = MutableList(vertexData.positions.size) { Vector3.ZERO } + + for (polygon in polygons) { + for (p in polygon.positions) { + normals[p] += polygon.normal(vertexData) + } + } + + for (i in normals.indices) { + normals[i] = normals[i].normalized + } + + return MeshData( + VertexData( + vertexData.positions, + vertexData.textureCoords, + vertexData.colors, + normals, + vertexData.tangents, + vertexData.bitangents + ), + polygons.map { + IndexedPolygon(it.positions, it.textureCoords, it.colors, it.positions, it.tangents, it.bitangents) + }) +} + +/** + * Assign vertex normals based on face normals + */ +fun IMeshData.assignFaceNormals(): MeshData { + val normals = MutableList(polygons.size) { Vector3.ZERO } + + for (i in polygons.indices) { + normals[i] = polygons[i].normal(vertexData) + } + + return MeshData( + VertexData( + vertexData.positions, + vertexData.textureCoords, + vertexData.colors, + normals, + vertexData.tangents, + vertexData.bitangents + ), + polygons.mapIndexed { index, it -> + IndexedPolygon(it.positions, it.textureCoords, it.colors, it.positions.map { + index + }, it.tangents, it.bitangents) + }) +} \ No newline at end of file diff --git a/orx-mesh-generators/src/commonMain/kotlin/tangents/MeshDataTangents.kt b/orx-mesh-generators/src/commonMain/kotlin/tangents/MeshDataTangents.kt new file mode 100644 index 000000000..77cdf4559 --- /dev/null +++ b/orx-mesh-generators/src/commonMain/kotlin/tangents/MeshDataTangents.kt @@ -0,0 +1,94 @@ +package org.openrndr.extra.meshgenerators.tangents + +import org.openrndr.extra.mesh.* +import org.openrndr.math.Vector3 + +/** + * Estimate tangents from normals and texture coordinates + * https://terathon.com/blog/tangent-space.html + */ +fun IMeshData.estimateTangents(): MeshData { + require(vertexData.textureCoords.isNotEmpty()) { + "need texture coordinates to estimate tangents" + } + require(isTriangular()) { + + } + + val normals = MutableList(vertexData.positions.size) { Vector3.ZERO } + + val tan1 = MutableList(vertexData.positions.size) { Vector3.ZERO } + val tan2 = MutableList(vertexData.positions.size) { Vector3.ZERO } + for (polygon in polygons) { + val v1 = vertexData.positions[polygon.positions[0]] + val v2 = vertexData.positions[polygon.positions[1]] + val v3 = vertexData.positions[polygon.positions[2]] + + val w1 = vertexData.textureCoords[polygon.textureCoords[0]] + val w2 = vertexData.textureCoords[polygon.textureCoords[1]] + val w3 = vertexData.textureCoords[polygon.textureCoords[2]] + + val x1 = (v2.x - v1.x) + val x2 = (v3.x - v1.x) + val y1 = (v2.y - v1.y) + val y2 = (v3.y - v1.y) + val z1 = (v2.z - v1.z) + val z2 = (v3.z - v1.z) + + val s1 = (w2.x - w1.x) + val s2 = (w3.x - w1.x) + val t1 = (w2.y - w1.y) + val t2 = (w3.y - w1.y) + + var det = s1 * t2 - s2 * t1 + if (det == 0.0) det = 1.0 + val r = 1.0/ (det) + val sdir = Vector3( + (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r + ).normalized + val tdir = Vector3( + (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r + ).normalized + + tan1[polygon.positions[0]] += sdir + tan1[polygon.positions[1]] += sdir + tan1[polygon.positions[2]] += sdir + + tan2[polygon.positions[0]] += tdir + tan2[polygon.positions[1]] += tdir + tan2[polygon.positions[2]] += tdir + + normals[polygon.positions[0]] += vertexData.normals[polygon.normals[0]] + normals[polygon.positions[1]] += vertexData.normals[polygon.normals[1]] + normals[polygon.positions[2]] += vertexData.normals[polygon.normals[2]] + + } + + for (a in 0 until vertexData.positions.size) { + normals[a] = normals[a].normalized + tan1[a] = tan1[a].normalized + tan2[a] = tan2[a].normalized + + val t = tan1[a] + val n = normals[a] + + tan1[a] = (t - n * n.dot(t)).normalized + val w = if ((n.cross(t)).dot(tan2[a]) < 0.0f) -1.0 else 1.0 + tan2[a] = n.cross((t)).normalized * w + } + + return MeshData(VertexData(vertexData.positions, vertexData.textureCoords, vertexData.colors, normals, tan1, tan2), + polygons = polygons.map { + IndexedPolygon( + it.positions, + it.textureCoords, + it.colors, + normals = it.positions, + tangents = it.positions, + bitangents = it.positions + ) + } + ) +} \ No newline at end of file diff --git a/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal01.kt b/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal01.kt new file mode 100644 index 000000000..11f105278 --- /dev/null +++ b/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal01.kt @@ -0,0 +1,55 @@ +package decal + +import org.openrndr.application +import org.openrndr.draw.DrawPrimitive +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.camera.Orbital +import org.openrndr.extra.mesh.* +import org.openrndr.extra.objloader.loadOBJMeshData +import org.openrndr.extra.meshgenerators.decal.decal +import org.openrndr.math.Vector3 +import org.openrndr.math.transforms.buildTransform +import java.io.File + +/** + * Demonstrate decal generator as an object slicer + */ +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + val obj = loadOBJMeshData(File("demo-data/obj-models/suzanne/Suzanne.obj")).toMeshData().triangulate() + + val slices = 25 + val sliceStep = 0.1 + val sliceWidth = 0.14 + + val sliceVBs = (0 until slices).map { + val projector = buildTransform { + translate(0.0, 0.0, -1.0 + it * sliceStep) + } + val decal = obj.decal(projector, Vector3(4.0, 4.0, sliceWidth)) + val vb = decal.toVertexBuffer() + vb + } + + extend(Orbital()) { + eye = Vector3(0.0, 0.0, 2.0) + } + extend { + drawer.shadeStyle = shadeStyle { + fragmentTransform = """x_fill.rgb = v_viewNormal.rgb * 0.5 + 0.5; """ + } + + drawer.translate(0.0, 0.0, slices * 0.5 * 0.5) + for (i in 0 until sliceVBs.size) { + drawer.vertexBuffer(sliceVBs[i], DrawPrimitive.TRIANGLES) + drawer.translate(0.0, 0.0, -0.5) + } + } + } + } +} \ No newline at end of file diff --git a/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal02.kt b/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal02.kt new file mode 100644 index 000000000..32c73a4a4 --- /dev/null +++ b/orx-mesh-generators/src/jvmDemo/kotlin/decal/DemoDecal02.kt @@ -0,0 +1,86 @@ +package decal + +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.objloader.loadOBJMeshData +import org.openrndr.extra.mesh.toVertexBuffer +import org.openrndr.extra.meshgenerators.decal.decal +import org.openrndr.math.Vector3 +import org.openrndr.math.transforms.buildTransform +import java.io.File +import kotlin.math.PI + +/** + * Demonstrate decal generation and rendering + */ +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + /** base object */ + val obj = loadOBJMeshData(File("demo-data/obj-models/suzanne/Suzanne.obj")) + .toMeshData() // convert from CompoundMeshData to MeshData + .triangulate() // convert to triangles, we need this for the decal generation steps + + /** object [VertexBuffer] */ + val objVB = obj.toVertexBuffer() + + + /** positions for the decal projectors */ + val decalPositions = listOf( + Vector3(0.35, 0.245, 0.8), + Vector3(-0.35, 0.245, 0.8) + ) + + /** decal vertex buffers */ + val decalVBs = decalPositions.map { + val projector = buildTransform { + translate(it) + } + val decal = obj.decal(projector, Vector3(2.0, 2.0, 0.5)) + val vb = decal.toVertexBuffer() + vb + } + + extend(Orbital()) { + eye = Vector3(0.0, 0.0, 2.0) + } + extend { + /* draw the base mesh */ + drawer.isolated { + drawer.shadeStyle = shadeStyle { + fragmentTransform = """x_fill.rgb = vec3(v_viewNormal * 0.5 + 0.5); """ + } + drawer.vertexBuffer(objVB, DrawPrimitive.TRIANGLES) + } + + /* draw the decals */ + drawer.isolated { + for ((index, decal) in decalVBs.withIndex()) { + /* offset the projection transform to avoid z-fighting */ + drawer.projection = buildTransform { + translate(0.0, 0.0, -1e-4) + } * drawer.projection + + /* draw effects on the decal geometry */ + drawer.shadeStyle = shadeStyle { + fragmentTransform = """ + float d = length(va_texCoord0.xy - vec2(0.5)); + float sd = smoothstep(-0.01, 0.01, cos(p_time + d * 3.1415 * 2.0 * 10.0)); + float l = max(0.0, va_normal.z); + x_fill = vec4(0.0, 0.0, 0.0, l * sd * 0.5); """ + parameter("time", seconds * PI * 2 + index * PI) + } + drawer.vertexBuffer(decal, DrawPrimitive.TRIANGLES) + } + } + } + } + } +} \ 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 index a2d7e497a..5aab85220 100644 --- a/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt +++ b/orx-mesh-noise/src/jvmDemo/kotlin/DemoMeshNoise01.kt @@ -3,7 +3,7 @@ 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.objloader.loadOBJMeshData import org.openrndr.extra.mesh.noise.uniform import org.openrndr.extra.meshgenerators.sphereMesh import org.openrndr.math.Vector3 diff --git a/orx-mesh/build.gradle.kts b/orx-mesh/build.gradle.kts index 99247e9f4..03ebfeac9 100644 --- a/orx-mesh/build.gradle.kts +++ b/orx-mesh/build.gradle.kts @@ -18,6 +18,7 @@ kotlin { api(libs.openrndr.shape) implementation(project(":orx-shapes")) implementation(project(":orx-mesh-generators")) + implementation(project(":orx-obj-loader")) implementation(project(":orx-camera")) implementation(project(":orx-noise")) } diff --git a/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt index 0df55f699..add6d7021 100644 --- a/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt +++ b/orx-mesh/src/commonMain/kotlin/CompoundMeshDataExtensions.kt @@ -3,6 +3,9 @@ package org.openrndr.extra.mesh import org.openrndr.draw.VertexBuffer import org.openrndr.draw.vertexBuffer +/** + * Write compound mesh data to [VertexBuffer] + */ fun ICompoundMeshData.toVertexBuffer(): VertexBuffer { val triangulated = this.triangulate() @@ -19,6 +22,9 @@ fun ICompoundMeshData.toVertexBuffer(): VertexBuffer { return vertexBuffer } +/** + * Convert compound mesh data to [IPolygon] compounds + */ fun ICompoundMeshData.toPolygons(): Map> { return compounds.mapValues { it.value.toPolygons() } } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt index da7222e36..8e1833bf6 100644 --- a/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt +++ b/orx-mesh/src/commonMain/kotlin/IndexedPolygon.kt @@ -4,10 +4,7 @@ import org.openrndr.math.Matrix44 import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 import org.openrndr.math.Vector4 -import kotlin.math.PI -import kotlin.math.abs -import kotlin.math.atan2 -import kotlin.math.round +import kotlin.math.* /** * Indexed polygon interface @@ -133,6 +130,15 @@ interface IIndexedPolygon { return abs(round(angleSum / (2 * PI))) == 1.0 } + /** + * Evaluate polygon normal + */ + fun normal(vertexData: IVertexData) : Vector3 { + val u = vertexData.positions[positions[1]] - vertexData.positions[positions[0]] + val v = vertexData.positions[positions[2]] - vertexData.positions[positions[0]] + return u.cross(v).normalized + } + /** * Convert to [IPolygon] * @param vertexData the vertex data required to build the [IPolygon] @@ -146,12 +152,12 @@ interface IIndexedPolygon { data class IndexedPolygon( override val positions: List, override val textureCoords: List, - override val normals: List, override val colors: List, + override val normals: List, override val tangents: List, override val bitangents: List - ) : IIndexedPolygon { +) : IIndexedPolygon { private fun tessellate(vertexData: IVertexData): List { val points = vertexData.positions.slice(positions.toList()) @@ -161,8 +167,8 @@ data class IndexedPolygon( IndexedPolygon( 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 (normals.isNotEmpty()) normals.slice(it) else listOf(), if (tangents.isNotEmpty()) tangents.slice(it) else listOf(), if (bitangents.isNotEmpty()) bitangents.slice(it) else listOf() ) @@ -187,16 +193,16 @@ data class IndexedPolygon( textureCoords.getOrNull(it), textureCoords.getOrNull(it + 1) ), - listOfNotNull( - normals.getOrNull(0), - normals.getOrNull(it), - normals.getOrNull(it + 1) - ), listOfNotNull( colors.getOrNull(0), colors.getOrNull(it + 1), colors.getOrNull(it + 2) ), + listOfNotNull( + normals.getOrNull(0), + normals.getOrNull(it), + normals.getOrNull(it + 1) + ), listOfNotNull( tangents.getOrNull(0), tangents.getOrNull(it + 1), @@ -218,9 +224,39 @@ data class IndexedPolygon( override fun toPolygon(vertexData: IVertexData): Polygon { return Polygon( vertexData.positions.slice(positions), - vertexData.normals.slice(normals), vertexData.textureCoords.slice(textureCoords), - vertexData.colors.slice(colors) + vertexData.colors.slice(colors), + vertexData.normals.slice(normals), + vertexData.tangents.slice(tangents), + vertexData.bitangents.slice(bitangents) + ) + } + + /** + * Shift indices + * @param positions position index shift + * @param textureCoords texture coordinate index shift + * @param colors color index shift + * @param normals normal index shift + * @param tangents tangent index shift + * @param bitangents bitangent index shift + * + */ + fun shiftIndices( + positions: Int = 0, + textureCoords: Int = 0, + colors: Int = 0, + normals: Int = 0, + tangents: Int = 0, + bitangents: Int = 0 + ): IndexedPolygon { + return IndexedPolygon( + positions = this.positions.map { it + positions }, + textureCoords = this.textureCoords.map { it + textureCoords }, + colors = this.colors.map { it + colors }, + normals = this.normals.map { it + normals }, + tangents = this.tangents.map { it + tangents }, + bitangents = this.bitangents.map { it + bitangents } ) } } @@ -240,9 +276,9 @@ data class MutableIndexedPolygon( 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() + vertexData.colors.slice(colors).toMutableList(), + vertexData.normals.slice(normals).toMutableList() ) } } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/IndexedPolygonExtensions.kt b/orx-mesh/src/commonMain/kotlin/IndexedPolygonExtensions.kt new file mode 100644 index 000000000..cd8461a17 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/IndexedPolygonExtensions.kt @@ -0,0 +1,33 @@ +package org.openrndr.extra.mesh + +import org.openrndr.math.LinearType +import org.openrndr.math.Vector3 + +internal fun > bc(barycentric: Vector3, items: List): T { + return (items[0] * barycentric.x) + (items[1] * barycentric.y) + (items[2] * barycentric.z) +} + +/** + * Evaluate a point in triangle + * @param vertexData the vertex data to use + * @param barycentric the barycentric coordinates of the point to evaluate + */ +fun IIndexedPolygon.point(vertexData: VertexData, barycentric: Vector3): Point { + require(positions.size == 3) + + val positions = vertexData.positions.slice(positions) + val colors = vertexData.colors.slice(colors) + val normals = vertexData.normals.slice(normals) + val tangents = vertexData.tangents.slice(tangents) + val bitangents = vertexData.bitangents.slice(bitangents) + val textureCoords = vertexData.textureCoords.slice(textureCoords) + + return Point( + (if (positions.isNotEmpty()) bc(barycentric, positions) else null)!!, + if (textureCoords.isNotEmpty()) bc(barycentric, textureCoords) else null, + if (colors.isNotEmpty()) bc(barycentric, colors) else null, + if (normals.isNotEmpty()) bc(barycentric, normals) else null, + if (tangents.isNotEmpty()) bc(barycentric, tangents) else null, + if (bitangents.isNotEmpty()) bc(barycentric, bitangents) else null + ) +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/MeshData.kt b/orx-mesh/src/commonMain/kotlin/MeshData.kt index d5446e08d..c013c15e8 100644 --- a/orx-mesh/src/commonMain/kotlin/MeshData.kt +++ b/orx-mesh/src/commonMain/kotlin/MeshData.kt @@ -8,8 +8,26 @@ import kotlin.jvm.JvmRecord interface IMeshData { val vertexData: IVertexData val polygons: List + + /** + * Convert mesh data to triangular mesh data + */ fun triangulate(): IMeshData + + /** + * Convert mesh data to a list of [IPolygon] + */ fun toPolygons(): List + + /** + * Join mesh data with [other] mesh data + */ + fun join(other: IMeshData): IMeshData + + + fun toMeshData(): MeshData + + fun toMutableMeshData() : MutableMeshData } /** @@ -32,6 +50,94 @@ data class MeshData( ip.toPolygon(vertexData) } } + + override fun join(other: IMeshData): IMeshData { + if (vertexData === other.vertexData) { + @Suppress("UNCHECKED_CAST") + return MeshData(vertexData, polygons + (other.polygons as List)) + } else { + val positionsShift: Int + val positions = if (vertexData.positions === other.vertexData.positions) { + positionsShift = 0 + vertexData.positions + } else { + positionsShift = vertexData.positions.size + vertexData.positions + other.vertexData.positions + } + + val textureCoordsShift: Int + val textureCoords = if (vertexData.textureCoords === other.vertexData.textureCoords) { + textureCoordsShift = 0 + vertexData.textureCoords + } else { + textureCoordsShift = vertexData.textureCoords.size + vertexData.textureCoords + other.vertexData.textureCoords + } + + val colorsShift: Int + val colors = if (vertexData.colors === other.vertexData.colors) { + colorsShift = 0 + vertexData.colors + } else { + colorsShift = vertexData.colors.size + vertexData.colors + other.vertexData.colors + } + + val normalsShift: Int + val normals = if (vertexData.normals === other.vertexData.normals) { + normalsShift = 0 + vertexData.normals + } else { + normalsShift = vertexData.normals.size + vertexData.normals + other.vertexData.normals + } + + val tangentsShift: Int + val tangents = if (vertexData.tangents === other.vertexData.tangents) { + tangentsShift = 0 + vertexData.tangents + } else { + tangentsShift = vertexData.tangents.size + vertexData.tangents + other.vertexData.tangents + } + + val bitangentsShift: Int + val bitangents = if (vertexData.bitangents === other.vertexData.bitangents) { + bitangentsShift = 0 + vertexData.bitangents + } else { + bitangentsShift = vertexData.bitangents.size + vertexData.bitangents + other.vertexData.bitangents + } + + return MeshData( + VertexData( + positions = positions, + textureCoords = textureCoords, + colors = colors, + normals = normals, + tangents = tangents, + bitangents = bitangents + ), + polygons + other.polygons.map { + (it as IndexedPolygon).shiftIndices( + positionsShift, + textureCoordsShift, + colorsShift, + normalsShift, + tangentsShift, + bitangentsShift + ) + } + ) + } + } + + override fun toMeshData(): MeshData = this + + override fun toMutableMeshData(): MutableMeshData { + TODO("Not yet implemented") + } } @@ -53,4 +159,16 @@ data class MutableMeshData( override fun toPolygons(): List { return polygons.map { it.toPolygon(vertexData) } } + + override fun join(other: IMeshData): IMeshData { + TODO("Not yet implemented") + } + + override fun toMutableMeshData(): MutableMeshData { +return this + } + + override fun toMeshData(): MeshData { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt index 089a07409..b93f9585e 100644 --- a/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt +++ b/orx-mesh/src/commonMain/kotlin/MeshDataExtensions.kt @@ -1,11 +1,8 @@ package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa -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 +import org.openrndr.draw.* +import org.openrndr.math.* /** * The [VertexFormat] for a [VertexBuffer] with positions, normals and texture coordinates. @@ -17,6 +14,16 @@ internal val objVertexFormat = vertexFormat { color(4) } +internal val objVertexFormatTangents = vertexFormat { + position(3) + normal(3) + textureCoordinate(2) + color(4) + attribute("tangent", VertexElementType.VECTOR3_FLOAT32) + attribute("bitangent", VertexElementType.VECTOR3_FLOAT32) +} + + /** * Determine if [IMeshData] is triangular by checking if each polygon has exactly 3 vertices */ @@ -30,11 +37,15 @@ fun IMeshData.isTriangular(): Boolean { fun IMeshData.toVertexBuffer(elementOffset: Int = 0, vertexBuffer: VertexBuffer? = null): VertexBuffer { val objects = triangulate().toPolygons() val triangleCount = objects.size - val vertexBuffer = vertexBuffer ?: vertexBuffer(objVertexFormat, triangleCount * 3) + + val format = if (vertexData.tangents.isNotEmpty() && vertexData.bitangents.isNotEmpty()) { + objVertexFormatTangents + } else objVertexFormat + + val vertexBuffer = vertexBuffer ?: vertexBuffer(format, triangleCount * 3) vertexBuffer.put(elementOffset) { objects.forEach { - for (i in it.positions.indices) { write(it.positions[i]) if (it.normals.isNotEmpty()) { @@ -54,9 +65,164 @@ fun IMeshData.toVertexBuffer(elementOffset: Int = 0, vertexBuffer: VertexBuffer? } else { write(ColorRGBa.WHITE) } + if (format == objVertexFormatTangents) { + write(it.tangents[i]) + write(it.bitangents[i]) + } } } } vertexBuffer.shadow.destroy() return vertexBuffer +} + +/** + * Weld vertices + * @param positionFractBits number of bits to use for fractional representation, negative amount to skip welding + */ +fun IMeshData.weld( + positionFractBits: Int, + textureCoordFractBits: Int = -1, + colorFractBits: Int = -1, + normalFractBits: Int = -1, + tangentFractBits: Int = -1, + bitangentFractBits: Int = -1 +): MeshData { + + fun MutableMap.quantize(v: Vector3, bits: Int): Int = + getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size } + + fun MutableMap.quantize(v: Vector2, bits: Int): Int = + getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size } + + fun MutableMap.quantize(v: Vector4, bits: Int): Int = + getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size } + + val positionMap = mutableMapOf() + val textureCoordMap = mutableMapOf() + val colorMap = mutableMapOf() + val normalMap = mutableMapOf() + val tangentMap = mutableMapOf() + val bitangentMap = mutableMapOf() + + if (positionFractBits >= 0) { + for (p in vertexData.positions) { + positionMap.quantize(p, positionFractBits) + } + } + + if (textureCoordFractBits >= 0) { + for (p in vertexData.textureCoords) { + textureCoordMap.quantize(p, textureCoordFractBits) + } + } + + if (colorFractBits >= 0) { + for (p in vertexData.colors) { + colorMap.quantize(p.toVector4(), colorFractBits) + } + } + + if (normalFractBits >= 0) { + for (p in vertexData.normals) { + normalMap.quantize(p, normalFractBits) + } + } + + if (tangentFractBits >= 0) { + for (p in vertexData.tangents) { + tangentMap.quantize(p, tangentFractBits) + } + } + + if (bitangentFractBits >= 0) { + for (p in vertexData.bitangents) { + bitangentMap.quantize(p, bitangentFractBits) + } + } + + val reindexedPolygons = mutableListOf() + + for (polygon in polygons) { + val positions = if (positionFractBits >= 0) { + vertexData.positions.slice(polygon.positions).map { positionMap.quantize(it, positionFractBits) } + } else { + polygon.positions + } + + val textureCoords = if (textureCoordFractBits >= 0) { + vertexData.textureCoords.slice(polygon.textureCoords) + .map { textureCoordMap.quantize(it, textureCoordFractBits) } + } else { + polygon.textureCoords + } + + val colors = if (colorFractBits >= 0) { + vertexData.colors.slice(polygon.colors).map { colorMap.quantize(it.toVector4(), colorFractBits) } + } else { + polygon.colors + } + + val normals = if (normalFractBits >= 0) { + vertexData.normals.slice(polygon.normals).map { normalMap.quantize(it, normalFractBits) } + } else { + polygon.normals + } + + val tangents = if (tangentFractBits >= 0) { + vertexData.tangents.slice(polygon.tangents).map { tangentMap.quantize(it, tangentFractBits) } + } else { + polygon.tangents + } + + val bitangents = if (bitangentFractBits >= 0) { + vertexData.bitangents.slice(polygon.bitangents).map { bitangentMap.quantize(it, bitangentFractBits) } + } else { + polygon.bitangents + } + + reindexedPolygons.add(IndexedPolygon(positions, textureCoords, colors, normals, tangents, bitangents)) + } + + val positionByIndex = vertexData.positions.associateBy { positionMap.quantize(it, positionFractBits) } + val textureCoordByIndex = + vertexData.textureCoords.associateBy { textureCoordMap.quantize(it, textureCoordFractBits) } + val colorByIndex = vertexData.colors.associateBy { colorMap.quantize(it.toVector4(), colorFractBits) } + val normalByIndex = vertexData.normals.associateBy { normalMap.quantize(it, normalFractBits) } + val tangentByIndex = vertexData.tangents.associateBy { tangentMap.quantize(it, tangentFractBits) } + val bitangentByIndex = vertexData.bitangents.associateBy { bitangentMap.quantize(it, bitangentFractBits) } + + val reindexedVertexData = VertexData( + if (positionFractBits >= 0) { + (0 until positionByIndex.size).map { positionByIndex.getValue(it) } + } else { + vertexData.positions + }, + if (textureCoordFractBits >= 0) { + (0 until textureCoordByIndex.size).map { textureCoordByIndex.getValue(it) } + } else { + vertexData.textureCoords + }, + if (colorFractBits >= 0) { + (0 until colorByIndex.size).map { colorByIndex.getValue(it) } + } else { + vertexData.colors + }, + if (normalFractBits >= 0) { + (0 until normalByIndex.size).map { normalByIndex.getValue(it) } + } else { + vertexData.normals + }, + if (tangentFractBits >= 0) { + (0 until tangentByIndex.size).map { tangentByIndex.getValue(it) } + } else { + vertexData.tangents + }, + if (bitangentFractBits >= 0) { + (0 until bitangentByIndex.size).map { bitangentByIndex.getValue(it) } + } else { + vertexData.bitangents + } + ) + return MeshData(reindexedVertexData, reindexedPolygons) } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/Point.kt b/orx-mesh/src/commonMain/kotlin/Point.kt new file mode 100644 index 000000000..4a8b53968 --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/Point.kt @@ -0,0 +1,23 @@ +package org.openrndr.extra.mesh + +import org.openrndr.color.ColorRGBa +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 + +/** + * Point with optional attributes + * @param position position attribute + * @param textureCoord optional texture coordinate attribute + * @param color optional color attribute + * @param normal optional normal attribute + * @param tangent optional tangent attribute + * @param bitangent optional bitangent attribute + */ +data class Point( + val position: Vector3, + val textureCoord: Vector2? = null, + val color: ColorRGBa? = null, + val normal: Vector3? = null, + val tangent: Vector3? = null, + val bitangent: Vector3? =null +) \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/Polygon.kt b/orx-mesh/src/commonMain/kotlin/Polygon.kt index dd42f2b91..c494a0a63 100644 --- a/orx-mesh/src/commonMain/kotlin/Polygon.kt +++ b/orx-mesh/src/commonMain/kotlin/Polygon.kt @@ -14,9 +14,9 @@ import kotlin.math.min */ interface IPolygon { val positions: List - val normals: List val textureCoords: List val colors: List + val normals: List val tangents: List val bitangents: List @@ -33,14 +33,14 @@ interface IPolygon { */ class Polygon( override val positions: List = emptyList(), - override val normals: List = emptyList(), override val textureCoords: List = emptyList(), override val colors: List = emptyList(), + override val normals: 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) + return Polygon(positions.map { (t * it.xyz1).div }, textureCoords, colors, normals, tangents, bitangents) } /** @@ -49,9 +49,9 @@ class Polygon( fun toMutablePolygon(): MutablePolygon { return MutablePolygon( positions.toMutableList(), - normals.toMutableList(), textureCoords.toMutableList(), colors.toMutableList(), + normals.toMutableList(), tangents.toMutableList(), bitangents.toMutableList() ) @@ -63,9 +63,9 @@ class Polygon( */ class MutablePolygon( override val positions: MutableList = mutableListOf(), - override val normals: MutableList = mutableListOf(), override val textureCoords: MutableList = mutableListOf(), override val colors: MutableList = mutableListOf(), + override val normals: MutableList = mutableListOf(), override val tangents: MutableList = mutableListOf(), override val bitangents: MutableList = mutableListOf() @@ -73,9 +73,9 @@ class MutablePolygon( override fun transform(t: Matrix44): MutablePolygon { return MutablePolygon( positions.map { (t * it.xyz1).div }.toMutableList(), - ArrayList(normals), ArrayList(textureCoords), ArrayList(colors), + ArrayList(normals), ArrayList(tangents), ArrayList(bitangents) ) @@ -136,8 +136,8 @@ fun List.toMeshData(): MeshData { IndexedPolygon( positions = indices, textureCoords = if (p.textureCoords.isNotEmpty()) indices else emptyList(), - normals = if (p.normals.isNotEmpty()) indices else emptyList(), colors = if (p.colors.isNotEmpty()) indices else emptyList(), + normals = if (p.normals.isNotEmpty()) indices else emptyList(), tangents = if (p.tangents.isNotEmpty()) indices else emptyList(), bitangents = if (p.bitangents.isNotEmpty()) indices else emptyList() ) diff --git a/orx-mesh/src/commonMain/kotlin/VertexData.kt b/orx-mesh/src/commonMain/kotlin/VertexData.kt index 961900b11..dee29ff13 100644 --- a/orx-mesh/src/commonMain/kotlin/VertexData.kt +++ b/orx-mesh/src/commonMain/kotlin/VertexData.kt @@ -13,11 +13,6 @@ interface IVertexData { */ val positions: List - /** - * Vertex normals - */ - val normals: List - /** * Vertex texture coordinates */ @@ -28,6 +23,11 @@ interface IVertexData { */ val colors: List + /** + * Vertex normals + */ + val normals: List + /** * Vertex tangents */ @@ -37,6 +37,16 @@ interface IVertexData { * Vertex bitangents */ val bitangents: List + + /** + * Convert to [VertexData] + */ + fun toVertexData() : VertexData + + /** + * Convert to [MutableVertexData] + */ + fun toMutableVertexData() : MutableVertexData } /** @@ -44,26 +54,23 @@ interface IVertexData { */ class VertexData( override val positions: List = emptyList(), - override val normals: List = emptyList(), override val textureCoords: List = emptyList(), override val colors: List = emptyList(), + override val normals: List = emptyList(), override val tangents: List = emptyList(), override val bitangents: List = emptyList() ) : IVertexData { - /** - * Convert to [MutableVertexData] - */ - fun toMutableVertexData(): MutableVertexData { - return MutableVertexData( - positions.toMutableList(), - normals.toMutableList(), - textureCoords.toMutableList(), - colors.toMutableList(), - tangents.toMutableList(), - bitangents.toMutableList() - ) - } + override fun toVertexData(): VertexData = this + + override fun toMutableVertexData(): MutableVertexData = MutableVertexData( + positions.toMutableList(), + textureCoords.toMutableList(), + colors.toMutableList(), + normals.toMutableList(), + tangents.toMutableList(), + bitangents.toMutableList() + ) } @@ -72,24 +79,54 @@ class VertexData( */ class MutableVertexData( override val positions: MutableList = mutableListOf(), - override val normals: MutableList = mutableListOf(), override val textureCoords: MutableList = mutableListOf(), override val colors: MutableList = mutableListOf(), + override val normals: MutableList = mutableListOf(), override val tangents: MutableList = mutableListOf(), override val bitangents: MutableList = mutableListOf() ) : IVertexData { - /** - * Convert to [VertexData] - */ - fun toVertexData(): VertexData { - return VertexData( - positions.toList(), - normals.toList(), - textureCoords.toList(), - colors.toList(), - tangents.toList(), - bitangents.toList() - ) - } + override fun toVertexData(): VertexData = VertexData( + positions.toList(), + textureCoords.toList(), + colors.toList(), + normals.toList(), + tangents.toList(), + bitangents.toList() + ) + + override fun toMutableVertexData(): MutableVertexData = this +} + +/** + * Add [point] to vertex data + */ +fun MutableVertexData.add(point: Point) { + positions.add(point.position) + point.color?.let { colors.add(it) } + point.textureCoord?.let { textureCoords.add(it) } + point.normal?.let { normals.add(it) } + point.tangent?.let { tangents.add(it) } + point.bitangent?.let { bitangents.add(it) } +} + +/** + * Retrieve [Point] from vertex data + */ +operator fun IVertexData.get( + index: Int, + textureCoordsIndex: Int = index, + colorsIndex: Int = index, + normalsIndex: Int = index, + tangentsIndex: Int = index, + bitangentsIndex: Int = index +): Point { + return Point( + positions[index], + textureCoords.getOrNull(textureCoordsIndex), + colors.getOrNull(colorsIndex), + normals.getOrNull(normalsIndex), + tangents.getOrNull(tangentsIndex), + bitangents.getOrNull(bitangentsIndex) + ) } \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/VertexDataExtensions.kt b/orx-mesh/src/commonMain/kotlin/VertexDataExtensions.kt new file mode 100644 index 000000000..6aab82a5b --- /dev/null +++ b/orx-mesh/src/commonMain/kotlin/VertexDataExtensions.kt @@ -0,0 +1,68 @@ +package org.openrndr.extra.mesh + +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.VertexBuffer +import org.openrndr.draw.vertexBuffer +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 + +/** + * Convert vertex data to [VertexBuffer]. Assumes every 3 consecutive vertices encode a triangle. + */ +fun IVertexData.toVertexBuffer(elementOffset: Int = 0, vertexBuffer: VertexBuffer? = null): VertexBuffer { + + val triangleCount = positions.size / 3 + val vertexBuffer = vertexBuffer ?: vertexBuffer(objVertexFormat, triangleCount * 3) + + vertexBuffer.put(elementOffset) { + var offset = 0 + for (triangle in 0 until triangleCount) { + for (i in 0 until 3) { + write(positions[offset]) + if (normals.isNotEmpty()) { + write(normals[offset]) + } else { + write(Vector3.ZERO) + } + if (textureCoords.isNotEmpty()) { + write(textureCoords[offset]) + } else { + write(Vector2.ZERO) + } + if (colors.isNotEmpty()) { + write(colors[offset]) + } else { + write(ColorRGBa.WHITE) + } + offset++ + } + } + } + vertexBuffer.shadow.destroy() + return vertexBuffer +} + +/** + * Convert vertex data to [MeshData]. Assumes every 3 consecutive vertices encode a triangle. + */ + +fun VertexData.toMeshData(): MeshData { + val polygons = mutableListOf() + + val triangleCount = positions.size / 3 + + for (t in 0 until triangleCount) { + val indices = listOf(t * 3, t * 3 + 1, t * 3 + 2) + polygons.add( + IndexedPolygon( + indices, + if (textureCoords.isNotEmpty()) indices else emptyList(), + if (colors.isNotEmpty()) indices else emptyList(), + if (normals.isNotEmpty()) indices else emptyList(), + if (tangents.isNotEmpty()) indices else emptyList(), + if (bitangents.isNotEmpty()) indices else emptyList() + ) + ) + } + return MeshData(this, polygons) +} \ No newline at end of file diff --git a/orx-mesh/src/commonMain/kotlin/Wireframe.kt b/orx-mesh/src/commonMain/kotlin/Wireframe.kt index 4209b6cdc..f8711d9ab 100644 --- a/orx-mesh/src/commonMain/kotlin/Wireframe.kt +++ b/orx-mesh/src/commonMain/kotlin/Wireframe.kt @@ -9,7 +9,6 @@ fun IMeshData.wireframe(): List> { return polygons.map { ip -> ip.toPolygon(this.vertexData).positions.toList() } } - /** * Extract wireframe from compound mesh data */ diff --git a/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt b/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt index f6ff329d7..cf6e87a3c 100644 --- a/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt +++ b/orx-mesh/src/jvmMain/kotlin/VertexBufferExtensions.kt @@ -2,8 +2,6 @@ package org.openrndr.extra.mesh import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer -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 @@ -53,7 +51,7 @@ fun VertexBuffer.toPolygons(vertexCount: Int = this.vertexCount): List textureCoordinates.add(buffer.getVector2()) colors.add(buffer.getColorRGBa()) } - polygons.add(Polygon(positions, normals, textureCoordinates, colors)) + polygons.add(Polygon(positions, textureCoordinates, colors, normals)) } return polygons } \ No newline at end of file diff --git a/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt b/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt index 1057670fb..98420fa4b 100644 --- a/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt +++ b/orx-obj-loader/src/commonMain/kotlin/ObjReader.kt @@ -1,7 +1,8 @@ -package org.openrndr.extra.mesh +package org.openrndr.extra.objloader import org.openrndr.color.ColorRGBa import org.openrndr.draw.VertexBuffer +import org.openrndr.extra.mesh.* import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 @@ -79,8 +80,8 @@ fun readObjMeshData(lines: Iterable): CompoundMeshData { IndexedPolygon( 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(), + if (hasNormal) indices.map { it[2] - 1 } else listOf(), if (hasTangents) indices.map { it[2] - 1 } else listOf(), if (hasBitangents) indices.map { it[2] - 1 } else listOf() ) @@ -95,7 +96,7 @@ fun readObjMeshData(lines: Iterable): CompoundMeshData { } } - val vertexData = VertexData(positions, normals, textureCoords, colors) + val vertexData = VertexData(positions, textureCoords, colors, normals) return CompoundMeshData( vertexData, meshes.mapValues { diff --git a/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt b/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt index 892ff8bb9..1448a43f5 100644 --- a/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt +++ b/orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt @@ -1,4 +1,6 @@ -package org.openrndr.extra.mesh +package org.openrndr.extra.objloader + +import org.openrndr.extra.mesh.ICompoundMeshData /** * 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 0e048e9f2..79701f22b 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.mesh.loadOBJMeshData -import org.openrndr.extra.mesh.toObj +import org.openrndr.extra.objloader.loadOBJMeshData +import org.openrndr.extra.objloader.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 dbf42ef2e..470f52459 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.mesh.loadOBJasVertexBuffer +import org.openrndr.extra.objloader.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 5ae0145b8..68bef7b1a 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.mesh.loadOBJasVertexBuffer -import org.openrndr.extra.mesh.saveOBJ +import org.openrndr.extra.objloader.loadOBJasVertexBuffer +import org.openrndr.extra.objloader.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 8361a35b9..6277e4c5f 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.mesh.saveOBJ +import org.openrndr.extra.objloader.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 190bea5df..85c82524e 100644 --- a/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt +++ b/orx-obj-loader/src/jvmDemo/kotlin/DemoWireframe01.kt @@ -8,8 +8,8 @@ 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.mesh.readObjMeshData -import org.openrndr.extra.mesh.loadOBJasVertexBuffer +import org.openrndr.extra.objloader.readObjMeshData +import org.openrndr.extra.objloader.loadOBJasVertexBuffer import org.openrndr.extra.mesh.wireframe import org.openrndr.math.Vector3 import org.openrndr.shape.Path3D diff --git a/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt b/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt index f8479d163..4588c1db5 100644 --- a/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt +++ b/orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt @@ -1,6 +1,7 @@ -package org.openrndr.extra.mesh +package org.openrndr.extra.objloader import org.openrndr.draw.VertexBuffer +import org.openrndr.extra.mesh.IPolygon import java.io.File import java.net.MalformedURLException import java.net.URL diff --git a/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt b/orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt index 66c21aed6..4597b6429 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.mesh +package org.openrndr.extra.objloader import org.openrndr.draw.VertexBuffer import java.io.File