[orx-mesh, orx-obj-loader] Separate into commonMain and jvmMain
This commit is contained in:
@@ -25,21 +25,21 @@ vertexBuffer.saveOBJ("my/path/exported.obj")
|
||||
<!-- __demos__ -->
|
||||
## Demos
|
||||
### DemoObjLoader01
|
||||
[source code](src/demo/kotlin/DemoObjLoader01.kt)
|
||||
[source code](src/jvmDemo/kotlin/DemoObjLoader01.kt)
|
||||
|
||||

|
||||
|
||||
### DemoObjSaver01
|
||||
[source code](src/demo/kotlin/DemoObjSaver01.kt)
|
||||
[source code](src/jvmDemo/kotlin/DemoObjSaver01.kt)
|
||||
|
||||

|
||||
|
||||
### DemoObjSaver02
|
||||
[source code](src/demo/kotlin/DemoObjSaver02.kt)
|
||||
[source code](src/jvmDemo/kotlin/DemoObjSaver02.kt)
|
||||
|
||||

|
||||
|
||||
### DemoWireframe01
|
||||
[source code](src/demo/kotlin/DemoWireframe01.kt)
|
||||
[source code](src/jvmDemo/kotlin/DemoWireframe01.kt)
|
||||
|
||||

|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
plugins {
|
||||
org.openrndr.extra.convention.`kotlin-jvm`
|
||||
org.openrndr.extra.convention.`kotlin-multiplatform`
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(libs.openrndr.application)
|
||||
implementation(libs.openrndr.math)
|
||||
api(project(":orx-mesh"))
|
||||
}
|
||||
}
|
||||
|
||||
val jvmDemo by getting {
|
||||
dependencies {
|
||||
implementation(project(":orx-camera"))
|
||||
implementation(project(":orx-mesh-generators"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,56 +2,15 @@ package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.VertexBuffer
|
||||
import org.openrndr.math.*
|
||||
import java.io.File
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
|
||||
/**
|
||||
* 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<String, List<IPolygon>> {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
loadOBJ(url)
|
||||
} catch (e: MalformedURLException) {
|
||||
loadOBJ(File(fileOrUrl))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an OBJ file as a [VertexBuffer].
|
||||
* Use this method to render / process the loaded OBJ data using the GPU.
|
||||
*/
|
||||
fun loadOBJasVertexBuffer(fileOrUrl: String): VertexBuffer {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
loadOBJasVertexBuffer(url)
|
||||
} catch (e: MalformedURLException) {
|
||||
loadOBJasVertexBuffer(File(fileOrUrl))
|
||||
}
|
||||
}
|
||||
|
||||
fun loadOBJasVertexBuffer(url: URL): VertexBuffer = loadOBJasVertexBuffer(url.readText().split("\n"))
|
||||
fun loadOBJasVertexBuffer(file: File): VertexBuffer = loadOBJasVertexBuffer(file.readLines())
|
||||
fun loadOBJasVertexBuffer(lines: List<String>): VertexBuffer {
|
||||
return loadOBJMeshData(lines).toVertexBuffer()
|
||||
}
|
||||
|
||||
fun loadOBJ(file: File) = loadOBJ(file.readLines())
|
||||
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<String>): Map<String, List<IPolygon>> = loadOBJMeshData(lines).triangulate().flattenPolygons()
|
||||
|
||||
|
||||
fun loadOBJMeshData(file: File) = loadOBJMeshData(file.readLines())
|
||||
fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
fun readObjMeshData(lines: Iterable<String>): CompoundMeshData {
|
||||
val meshes = mutableMapOf<String, List<IndexedPolygon>>()
|
||||
val positions = mutableListOf<Vector3>()
|
||||
val normals = mutableListOf<Vector3>()
|
||||
val tangents = mutableListOf<Vector3>()
|
||||
val bitangents = mutableListOf<Vector3>()
|
||||
val textureCoords = mutableListOf<Vector2>()
|
||||
val colors = mutableListOf<ColorRGBa>()
|
||||
var activeMesh = mutableListOf<IndexedPolygon>()
|
||||
@@ -66,7 +25,6 @@ fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
when (tokens.size) {
|
||||
3, 4 -> {
|
||||
positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
colors += ColorRGBa.WHITE
|
||||
}
|
||||
|
||||
6 -> {
|
||||
@@ -88,7 +46,18 @@ fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
}
|
||||
}
|
||||
|
||||
"vn" -> normals += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
"vn" -> {
|
||||
when (tokens.size) {
|
||||
3, 4 -> normals += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
9 -> {
|
||||
normals += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
tangents += Vector3(tokens[4].toDouble(), tokens[5].toDouble(), tokens[6].toDouble())
|
||||
bitangents += Vector3(tokens[7].toDouble(), tokens[8].toDouble(), tokens[9].toDouble())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
"vt" -> textureCoords += Vector2(tokens[1].toDouble(), tokens[2].toDouble())
|
||||
"g" -> {
|
||||
activeMesh = mutableListOf()
|
||||
@@ -102,7 +71,9 @@ fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
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
|
||||
val hasColor = colors.isNotEmpty()
|
||||
val hasTangents = tangents.isNotEmpty()
|
||||
val hasBitangents = bitangents.isNotEmpty()
|
||||
|
||||
activeMesh.add(
|
||||
IndexedPolygon(
|
||||
@@ -110,8 +81,8 @@ fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
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()
|
||||
if (hasTangents) indices.map { it[2] - 1 } else listOf(),
|
||||
if (hasBitangents) indices.map { it[2] - 1 } else listOf()
|
||||
)
|
||||
)
|
||||
|
||||
@@ -132,3 +103,10 @@ fun loadOBJMeshData(lines: List<String>): CompoundMeshData {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun loadOBJasVertexBuffer(lines: List<String>): VertexBuffer {
|
||||
return readObjMeshData(lines).toVertexBuffer()
|
||||
}
|
||||
|
||||
fun loadOBJ(lines: List<String>): Map<String, List<IPolygon>> = readObjMeshData(lines).triangulate().toPolygons()
|
||||
|
||||
75
orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt
Normal file
75
orx-obj-loader/src/commonMain/kotlin/ObjWriter.kt
Normal file
@@ -0,0 +1,75 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
/**
|
||||
* Convert mesh data to Wavefront OBJ representation
|
||||
* @param allowNonStandardBehavior if true non-standard encoding of color and tangent data is allowed
|
||||
*/
|
||||
fun ICompoundMeshData.toObj(allowNonStandardBehavior: Boolean = true): String {
|
||||
val sb = StringBuilder()
|
||||
|
||||
require(compounds.values.all { it.vertexData == vertexData }) {
|
||||
"compounds do not share vertex data"
|
||||
}
|
||||
|
||||
/*
|
||||
Output positions
|
||||
*/
|
||||
if (vertexData.colors.isEmpty()) {
|
||||
for (p in vertexData.positions) {
|
||||
sb.appendLine("v ${p.x} ${p.y} ${p.z}")
|
||||
}
|
||||
} else {
|
||||
require(vertexData.positions.size == vertexData.colors.size) {
|
||||
"position and color data do not align"
|
||||
}
|
||||
for (pc in vertexData.positions.zip(vertexData.colors)) {
|
||||
sb.appendLine("v ${pc.first.x} ${pc.first.y} ${pc.first.z} ${pc.second.r} ${pc.second.g} ${pc.second.b} ${pc.second.alpha}")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Output normals. Non-standard behavior where normal-tangent-bitangent is emitted as `vn`
|
||||
*/
|
||||
if (!allowNonStandardBehavior || vertexData.tangents.isEmpty() || vertexData.bitangents.isEmpty()) {
|
||||
for (n in vertexData.normals) {
|
||||
sb.appendLine("vn ${n.x} ${n.y} ${n.z}")
|
||||
}
|
||||
} else {
|
||||
for (i in vertexData.normals.indices) {
|
||||
val n = vertexData.normals[i]
|
||||
val t = vertexData.tangents[i]
|
||||
val b = vertexData.bitangents[i]
|
||||
sb.appendLine("vn ${n.x} ${n.y} ${n.z} ${t.x} ${t.y} ${t.z} ${b.x} ${b.y} ${b.z}")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Output texture coordinates
|
||||
*/
|
||||
for (t in vertexData.textureCoords) {
|
||||
sb.appendLine("vt ${t.x} ${t.y}")
|
||||
}
|
||||
|
||||
/*
|
||||
Output compounds
|
||||
*/
|
||||
for (g in compounds) {
|
||||
sb.appendLine("g ${g.key}")
|
||||
|
||||
/*
|
||||
Output polygons
|
||||
*/
|
||||
for (p in g.value.polygons) {
|
||||
sb.appendLine("f ${
|
||||
(0 until p.positions.size).joinToString(" ") { i ->
|
||||
listOf(
|
||||
p.positions.getOrNull(i)?.plus(1)?.toString() ?: "",
|
||||
p.textureCoords.getOrNull(i)?.plus(1)?.toString() ?: "",
|
||||
p.normals.getOrNull(i)?.plus(1)?.toString() ?: ""
|
||||
).joinToString("/")
|
||||
}
|
||||
}")
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
15
orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt
Normal file
15
orx-obj-loader/src/jvmDemo/kotlin/DemoObjCompoundRW01.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.extra.objloader.loadOBJMeshData
|
||||
import org.openrndr.extra.objloader.toObj
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
program {
|
||||
val path = "demo-data/obj-models"
|
||||
val cm = loadOBJMeshData(File("$path/suzanne/Suzanne.obj"))
|
||||
|
||||
println(cm.toObj())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ 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.loadOBJMeshData
|
||||
import org.openrndr.extra.objloader.readObjMeshData
|
||||
import org.openrndr.extra.objloader.loadOBJasVertexBuffer
|
||||
import org.openrndr.extra.objloader.wireframe
|
||||
import org.openrndr.math.Vector3
|
||||
@@ -25,7 +25,7 @@ fun main() {
|
||||
}
|
||||
program {
|
||||
val vb = loadOBJasVertexBuffer("orx-obj-loader/test-data/non-planar.obj")
|
||||
val md = loadOBJMeshData(File("orx-obj-loader/test-data/non-planar.obj").readLines())
|
||||
val md = readObjMeshData(File("orx-obj-loader/test-data/non-planar.obj").readLines())
|
||||
|
||||
val paths = md.wireframe().map {
|
||||
Path3D.fromPoints(it, true)
|
||||
44
orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt
Normal file
44
orx-obj-loader/src/jvmMain/kotlin/OBJLoader.kt
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.draw.VertexBuffer
|
||||
import java.io.File
|
||||
import java.net.MalformedURLException
|
||||
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<String, List<IPolygon>> {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
loadOBJ(url)
|
||||
} catch (e: MalformedURLException) {
|
||||
loadOBJ(File(fileOrUrl))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an OBJ file as a [VertexBuffer].
|
||||
* Use this method to render / process the loaded OBJ data using the GPU.
|
||||
*/
|
||||
fun loadOBJasVertexBuffer(fileOrUrl: String): VertexBuffer {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
loadOBJasVertexBuffer(url)
|
||||
} catch (e: MalformedURLException) {
|
||||
loadOBJasVertexBuffer(File(fileOrUrl))
|
||||
}
|
||||
}
|
||||
|
||||
fun loadOBJasVertexBuffer(url: URL): VertexBuffer = loadOBJasVertexBuffer(url.readText().split("\n"))
|
||||
fun loadOBJasVertexBuffer(file: File): VertexBuffer = loadOBJasVertexBuffer(file.readLines())
|
||||
|
||||
fun loadOBJ(file: File) = loadOBJ(file.readLines())
|
||||
fun loadOBJEx(file: File) = readObjMeshData(file.readLines())
|
||||
fun loadOBJ(url: URL) = loadOBJ(url.readText().split("\n"))
|
||||
fun loadOBJEx(url: URL) = readObjMeshData(url.readText().split("\n"))
|
||||
|
||||
|
||||
|
||||
fun loadOBJMeshData(file: File) = readObjMeshData(file.readLines())
|
||||
@@ -47,6 +47,7 @@ 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",
|
||||
Reference in New Issue
Block a user