[orx-mesh, orx-obj-loader] Separate into commonMain and jvmMain
This commit is contained in:
95
orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt
Normal file
95
orx-obj-loader/src/jvmMain/kotlin/OBJSaver.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.draw.VertexBuffer
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
/**
|
||||
* An Ordered Map
|
||||
* containing Vec2 / Vec3 String representations.
|
||||
* Ensures no strings are duplicates by using a Map
|
||||
* and maintains their order by using a List.
|
||||
*/
|
||||
private class UniqueCoords {
|
||||
// Map pointing unique strings to their index.
|
||||
// We could just use `coords.indexOf(floats)` but using a map is faster.
|
||||
private val indices = mutableMapOf<String, Int>()
|
||||
|
||||
// List containing unique Strings
|
||||
private val coords = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* Adds strings only if they are not found yet in `coords`.
|
||||
* Returns the index of the received argument inside `coords`.
|
||||
*/
|
||||
fun add(floats: String): Int {
|
||||
val index = indices[floats]
|
||||
return if (index == null) {
|
||||
coords.add(floats)
|
||||
val newIndex = coords.size
|
||||
indices[floats] = newIndex
|
||||
newIndex
|
||||
} else index
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a valid .obj block representing `coords`.
|
||||
*/
|
||||
fun toObjBlock(token: String) = coords.joinToString("\n$token ", "$token ", "\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a VertexBuffer to a Wavefront OBJ file.
|
||||
* Faces use indices. Vertices, normals and texture coordinates are deduplicated.
|
||||
*/
|
||||
fun VertexBuffer.saveOBJ(filePath: String) {
|
||||
val bb = ByteBuffer.allocateDirect(vertexCount * vertexFormat.size)
|
||||
bb.order(ByteOrder.nativeOrder())
|
||||
read(bb)
|
||||
bb.rewind()
|
||||
|
||||
val tokens = mapOf(
|
||||
"position" to "v",
|
||||
"normal" to "vn",
|
||||
"texCoord0" to "vt"
|
||||
)
|
||||
|
||||
val indexMap = tokens.values.associateWith { UniqueCoords() }
|
||||
val lastIndices = tokens.values.associateWith { 0 }.toMutableMap()
|
||||
val vertexIndices = mutableListOf<String>()
|
||||
|
||||
// Process the ByteBuffer and populate data structures
|
||||
while (bb.position() < bb.capacity()) {
|
||||
vertexFormat.items.forEach { vertexElement ->
|
||||
val floats = List(vertexElement.type.componentCount) {
|
||||
bb.getFloat()
|
||||
}.joinToString(" ")
|
||||
val token = tokens[vertexElement.attribute]
|
||||
if (token != null) {
|
||||
lastIndices[token] = indexMap[token]!!.add(floats)
|
||||
}
|
||||
}
|
||||
vertexIndices.add("${lastIndices["v"]}/${lastIndices["vt"]}/${lastIndices["vn"]}")
|
||||
}
|
||||
|
||||
val f = File(filePath)
|
||||
f.bufferedWriter().use { writer ->
|
||||
writer.run {
|
||||
// Write header
|
||||
appendLine("# OPENRNDR")
|
||||
appendLine("# www.openrndr.org")
|
||||
appendLine("o " + f.name)
|
||||
|
||||
// Write v, vt, vn blocks
|
||||
indexMap.forEach { (token, verts) ->
|
||||
appendLine(verts.toObjBlock(token))
|
||||
|
||||
}
|
||||
// Write faces processing three vertices at a time
|
||||
vertexIndices.chunked(3) {
|
||||
appendLine("f ${it[0]} ${it[1]} ${it[2]}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user