[orx-obj-loader] Add support for wireframes and non-planar and concave faces
This commit is contained in:
@@ -5,4 +5,6 @@ plugins {
|
||||
dependencies {
|
||||
implementation(libs.openrndr.application)
|
||||
implementation(libs.openrndr.math)
|
||||
implementation(libs.openrndr.ffmpeg)
|
||||
demoImplementation(project(":orx-camera"))
|
||||
}
|
||||
63
orx-obj-loader/src/demo/kotlin/DemoWireframe01.kt
Normal file
63
orx-obj-loader/src/demo/kotlin/DemoWireframe01.kt
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
Display wireframe and non-planar faces
|
||||
*/
|
||||
import org.openrndr.WindowMultisample
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
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.loadOBJasVertexBuffer
|
||||
import org.openrndr.extra.objloader.wireframe
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.shape.Path3D
|
||||
import java.io.File
|
||||
import kotlin.math.cos
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
multisample = WindowMultisample.SampleCount(4)
|
||||
}
|
||||
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 paths = md.wireframe().map {
|
||||
Path3D.fromPoints(it, true)
|
||||
}
|
||||
|
||||
extend(Orbital())
|
||||
extend {
|
||||
drawer.rotate(Vector3.Companion.UNIT_Y, seconds * 45.0 + 45.0, TransformTarget.MODEL)
|
||||
drawer.translate(0.0, 0.0, 9.0, TransformTarget.VIEW)
|
||||
drawer.shadeStyle = shadeStyle {
|
||||
fragmentTransform = """
|
||||
x_fill.rgb = normalize(v_viewNormal) * 0.5 + vec3(0.5);
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLES)
|
||||
drawer.stroke = ColorRGBa.WHITE
|
||||
drawer.strokeWeight = 1.0
|
||||
|
||||
drawer.shadeStyle = shadeStyle {
|
||||
vertexTransform = """
|
||||
x_projectionMatrix[3][2] -= 0.001;
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
drawer.strokeWeight = 1.0
|
||||
drawer.paths(paths.mapIndexed { index, it ->
|
||||
it.sub(
|
||||
0.0, cos(seconds * 0.5 + index * 0.5) * 0.5 + 0.5
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
orx-obj-loader/src/main/kotlin/IndexedPolygon.kt
Normal file
144
orx-obj-loader/src/main/kotlin/IndexedPolygon.kt
Normal file
@@ -0,0 +1,144 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
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
|
||||
|
||||
data class IndexedPolygon(
|
||||
val positions: IntArray, val textureCoords: IntArray, val normals: IntArray
|
||||
) {
|
||||
|
||||
fun base(vertexData: VertexData): 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)
|
||||
val bitangent = normal.cross(u)
|
||||
return Matrix44.fromColumnVectors(
|
||||
u.xyz0.normalized,
|
||||
bitangent.xyz0.normalized,
|
||||
normal.xyz0.normalized,
|
||||
Vector4.UNIT_W
|
||||
)
|
||||
}
|
||||
|
||||
fun isPlanar(vertexData: VertexData, 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)]]
|
||||
val p2 = vertexData.positions[positions[(i + 1).mod(positions.size)]]
|
||||
|
||||
val u = (p0 - p1).normalized
|
||||
val v = (p2 - p1).normalized
|
||||
return u.cross(v).normalized
|
||||
}
|
||||
return if (positions.size <= 3) {
|
||||
true
|
||||
} else {
|
||||
val n0 = normal(0)
|
||||
(1 until positions.size - 2).all { n0.dot(normal(it)) >= 1.0 - eps }
|
||||
}
|
||||
}
|
||||
|
||||
fun isConvex(vertexData: VertexData): Boolean {
|
||||
val planar = base(vertexData).inversed
|
||||
|
||||
fun p(v: Vector3): Vector2 {
|
||||
return (planar * v.xyz1).xy
|
||||
}
|
||||
|
||||
if (positions.size < 3) {
|
||||
return false
|
||||
}
|
||||
var old = p(vertexData.positions[positions[positions.size - 2]])
|
||||
var new = p(vertexData.positions[positions[positions.size - 1]])
|
||||
var newDirection = atan2(new.y - old.y, new.x - old.x)
|
||||
var angleSum = 0.0
|
||||
var oldDirection: Double
|
||||
var orientation = Double.POSITIVE_INFINITY
|
||||
for ((ndx, newPointIndex) in positions.withIndex()) {
|
||||
old = new
|
||||
oldDirection = newDirection
|
||||
val newPoint = p(vertexData.positions[newPointIndex])
|
||||
new = newPoint
|
||||
newDirection = atan2(new.y - old.y, new.x - old.x)
|
||||
|
||||
if (old == new) {
|
||||
return false
|
||||
}
|
||||
var angle = newDirection - oldDirection
|
||||
if (angle <= -Math.PI)
|
||||
angle += PI * 2.0
|
||||
|
||||
if (angle > PI) {
|
||||
angle -= PI * 2.0
|
||||
}
|
||||
|
||||
if (ndx == 0) {
|
||||
if (angle == 0.0) {
|
||||
return false
|
||||
}
|
||||
orientation = if (angle > 0.0) 1.0 else -1.0
|
||||
|
||||
} else {
|
||||
if (orientation * angle <= 0.0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
angleSum += angle
|
||||
|
||||
}
|
||||
return abs(round(angleSum / (2 * PI))) == 1.0
|
||||
}
|
||||
|
||||
fun tessellate(vertexData: VertexData): List<IndexedPolygon> {
|
||||
val points = vertexData.positions.slice(positions.toList())
|
||||
val triangles = org.openrndr.shape.triangulate(listOf(points))
|
||||
|
||||
return triangles.windowed(3, 3).map {
|
||||
IndexedPolygon(
|
||||
positions.sliceArray(it),
|
||||
if (textureCoords.isNotEmpty()) textureCoords.sliceArray(it) else intArrayOf(),
|
||||
if (normals.isNotEmpty()) normals.sliceArray(it) else intArrayOf()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun triangulate(vertexData: VertexData): List<IndexedPolygon> {
|
||||
return when {
|
||||
positions.size == 3 -> listOf(this)
|
||||
isPlanar(vertexData) && isConvex(vertexData) -> {
|
||||
val triangleCount = positions.size - 2
|
||||
(0 until triangleCount).map {
|
||||
IndexedPolygon(
|
||||
intArrayOf(positions[0], positions[it + 1], positions[it + 2]),
|
||||
listOfNotNull(
|
||||
textureCoords.getOrNull(0),
|
||||
textureCoords.getOrNull(it),
|
||||
textureCoords.getOrNull(it + 1)
|
||||
).toIntArray(),
|
||||
listOfNotNull(
|
||||
normals.getOrNull(0),
|
||||
normals.getOrNull(it),
|
||||
normals.getOrNull(it + 1)
|
||||
).toIntArray(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> tessellate(vertexData)
|
||||
}
|
||||
}
|
||||
|
||||
fun toPolygon(vertexData: VertexData): Polygon {
|
||||
return Polygon(
|
||||
vertexData.positions.slice(positions.toList()).toTypedArray(),
|
||||
vertexData.normals.slice(normals.toList()).toTypedArray(),
|
||||
vertexData.textureCoords.slice(textureCoords.toList()).toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
18
orx-obj-loader/src/main/kotlin/MeshData.kt
Normal file
18
orx-obj-loader/src/main/kotlin/MeshData.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
@JvmRecord
|
||||
data class MeshData(val vertexData: VertexData, val polygonGroups: Map<String, List<IndexedPolygon>>) {
|
||||
fun triangulate(): MeshData {
|
||||
return copy(polygonGroups = polygonGroups.mapValues {
|
||||
it.value.flatMap { polygon -> polygon.triangulate(vertexData) }
|
||||
})
|
||||
}
|
||||
|
||||
fun flattenPolygons(): Map<String, List<Polygon>> {
|
||||
return polygonGroups.mapValues {
|
||||
it.value.map { ip ->
|
||||
ip.toPolygon(vertexData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt
Normal file
43
orx-obj-loader/src/main/kotlin/MeshDataExtensions.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.draw.VertexBuffer
|
||||
import org.openrndr.draw.vertexBuffer
|
||||
import org.openrndr.draw.vertexFormat
|
||||
import org.openrndr.math.Vector2
|
||||
|
||||
private val objVertexFormat = vertexFormat {
|
||||
position(3)
|
||||
normal(3)
|
||||
textureCoordinate(2)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,66 +1,12 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.draw.VertexBuffer
|
||||
import org.openrndr.draw.vertexBuffer
|
||||
import org.openrndr.draw.vertexFormat
|
||||
import org.openrndr.math.Matrix44
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.*
|
||||
import java.io.File
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class Triangle(val positions: Array<Vector3> = emptyArray(),
|
||||
val normals: Array<Vector3> = emptyArray(),
|
||||
val textureCoords: Array<Vector2> = emptyArray()) {
|
||||
fun transform(t: Matrix44): Triangle {
|
||||
return Triangle(positions.map { (t * it.xyz1).div }.toTypedArray(), normals, textureCoords)
|
||||
}
|
||||
}
|
||||
|
||||
class Box(val corner: Vector3, val width: Double, val height: Double, val depth: Double)
|
||||
|
||||
fun bounds(triangles: List<Triangle>): 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
|
||||
|
||||
triangles.forEach {
|
||||
it.positions.forEach {
|
||||
minX = min(minX, it.x)
|
||||
minY = min(minY, it.y)
|
||||
minZ = min(minZ, it.z)
|
||||
|
||||
maxX = max(maxX, it.x)
|
||||
maxY = max(maxY, it.y)
|
||||
maxZ = max(maxZ, it.z)
|
||||
}
|
||||
}
|
||||
return Box(Vector3(minX, minY, minZ), maxX - minX, maxY - minY, maxZ - minZ)
|
||||
}
|
||||
|
||||
fun List<Triangle>.vertexBuffer(): VertexBuffer {
|
||||
val vertexBuffer = vertexBuffer(objVertexFormat, size * 3)
|
||||
vertexBuffer.put {
|
||||
this@vertexBuffer.forEach {
|
||||
for (i in it.positions.indices) {
|
||||
write(it.positions[i])
|
||||
write(it.normals[i])
|
||||
write(Vector2.ZERO)
|
||||
}
|
||||
}
|
||||
}
|
||||
vertexBuffer.shadow.destroy()
|
||||
return vertexBuffer
|
||||
}
|
||||
|
||||
fun loadOBJ(fileOrUrl: String): Map<String, List<Triangle>> {
|
||||
fun loadOBJ(fileOrUrl: String): Map<String, List<Polygon>> {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
loadOBJ(url)
|
||||
@@ -69,12 +15,6 @@ fun loadOBJ(fileOrUrl: String): Map<String, List<Triangle>> {
|
||||
}
|
||||
}
|
||||
|
||||
private val objVertexFormat = vertexFormat {
|
||||
position(3)
|
||||
normal(3)
|
||||
textureCoordinate(2)
|
||||
}
|
||||
|
||||
fun loadOBJasVertexBuffer(fileOrUrl: String): VertexBuffer {
|
||||
return try {
|
||||
val url = URL(fileOrUrl)
|
||||
@@ -87,51 +27,24 @@ fun loadOBJasVertexBuffer(fileOrUrl: String): VertexBuffer {
|
||||
fun loadOBJasVertexBuffer(url: URL): VertexBuffer = loadOBJasVertexBuffer(url.readText().split("\n"))
|
||||
fun loadOBJasVertexBuffer(file: File): VertexBuffer = loadOBJasVertexBuffer(file.readLines())
|
||||
fun loadOBJasVertexBuffer(lines: List<String>): VertexBuffer {
|
||||
val objects = loadOBJ(lines)
|
||||
val triangleCount = objects.values.sumBy { 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
|
||||
return loadOBJMeshData(lines).toVertexBuffer()
|
||||
}
|
||||
|
||||
fun loadOBJ(file: File) = loadOBJ(file.readLines())
|
||||
fun loadOBJEx(file: File) = loadOBJEx(file.readLines())
|
||||
fun loadOBJEx(file: File) = loadOBJMeshData(file.readLines())
|
||||
fun loadOBJ(url: URL) = loadOBJ(url.readText().split("\n"))
|
||||
fun loadOBJEx(url: URL) = loadOBJEx(url.readText().split("\n"))
|
||||
fun loadOBJEx(url: URL) = loadOBJMeshData(url.readText().split("\n"))
|
||||
|
||||
class OBJData(val positions: List<Vector3>, val normals: List<Vector3>, val textureCoords: List<Vector2>)
|
||||
fun loadOBJ(lines: List<String>): Map<String, List<Polygon>> = loadOBJMeshData(lines).triangulate().flattenPolygons()
|
||||
|
||||
fun loadOBJ(lines: List<String>): Map<String, List<Triangle>> = loadOBJEx(lines).second
|
||||
|
||||
fun loadOBJEx(lines: List<String>): Pair<OBJData, Map<String, List<Triangle>>> {
|
||||
val meshes = mutableMapOf<String, List<Triangle>>()
|
||||
fun loadOBJMeshData(file: File) = loadOBJMeshData(file.readLines())
|
||||
fun loadOBJMeshData(lines: List<String>): MeshData {
|
||||
val meshes = mutableMapOf<String, List<IndexedPolygon>>()
|
||||
val positions = mutableListOf<Vector3>()
|
||||
val normals = mutableListOf<Vector3>()
|
||||
val textureCoords = mutableListOf<Vector2>()
|
||||
var activeMesh = mutableListOf<Triangle>()
|
||||
var activeMesh = mutableListOf<IndexedPolygon>()
|
||||
|
||||
lines.forEach { line ->
|
||||
if (line.isNotEmpty()) {
|
||||
@@ -142,46 +55,30 @@ fun loadOBJEx(lines: List<String>): Pair<OBJData, Map<String, List<Triangle>>> {
|
||||
"v" -> {
|
||||
positions += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
}
|
||||
|
||||
"vn" -> normals += Vector3(tokens[1].toDouble(), tokens[2].toDouble(), tokens[3].toDouble())
|
||||
"vt" -> textureCoords += Vector2(tokens[1].toDouble(), tokens[2].toDouble())
|
||||
"g" -> {
|
||||
activeMesh = mutableListOf()
|
||||
meshes[tokens.getOrNull(1) ?: "no-name-${meshes.size}"] = activeMesh
|
||||
}
|
||||
|
||||
"f" -> {
|
||||
val indices = tokens.subList(1, tokens.size).map { it.split("/") }.map {
|
||||
it.map { it.toIntOrNull() }
|
||||
it.map { it.toIntOrNull() ?: 0 }
|
||||
}
|
||||
val hasPosition = (indices[0].getOrNull(0) ?: 0) != 0
|
||||
val hasUV = (indices[0].getOrNull(1) ?: 0) != 0
|
||||
val hasNormal = (indices[0].getOrNull(2) ?: 0) != 0
|
||||
|
||||
for (i in 0 until indices.size - 2) {
|
||||
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()
|
||||
)
|
||||
)
|
||||
|
||||
val attributes = indices[0].size
|
||||
val o = i * 2
|
||||
val s = indices.size
|
||||
|
||||
val ps = if (attributes >= 1) arrayOf(
|
||||
indices[(0 + o) % s][0]?.let { positions[it - 1] } ?: Vector3.ZERO,
|
||||
indices[(1 + o) % s][0]?.let { positions[it - 1] } ?: Vector3.ZERO,
|
||||
indices[(2 + o) % s][0]?.let { positions[it - 1] } ?: Vector3.ZERO)
|
||||
else
|
||||
emptyArray()
|
||||
|
||||
val tcs = if (attributes >= 2) arrayOf(
|
||||
indices[(0 + o) % s][1]?.let { textureCoords[it - 1] } ?: Vector2.ZERO,
|
||||
indices[(1 + o) % s][1]?.let { textureCoords[it - 1] } ?: Vector2.ZERO,
|
||||
indices[(2 + o) % s][1]?.let { textureCoords[it - 1] } ?: Vector2.ZERO)
|
||||
else
|
||||
emptyArray()
|
||||
|
||||
|
||||
val ns = if (attributes >= 3) arrayOf(
|
||||
indices[(0 + o) % s][2]?.let { normals[it - 1] } ?: Vector3.ZERO,
|
||||
indices[(1 + o) % s][2]?.let { normals[it - 1] } ?: Vector3.ZERO,
|
||||
indices[(2 + o) % s][2]?.let { normals[it - 1] } ?: Vector3.ZERO)
|
||||
else
|
||||
emptyArray()
|
||||
|
||||
activeMesh.add(Triangle(ps, ns, tcs))
|
||||
if (meshes.isEmpty()) {
|
||||
meshes["no-name"] = activeMesh
|
||||
}
|
||||
@@ -190,6 +87,9 @@ fun loadOBJEx(lines: List<String>): Pair<OBJData, Map<String, List<Triangle>>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair(OBJData(positions, normals, textureCoords), meshes)
|
||||
|
||||
return MeshData(
|
||||
VertexData(positions.toTypedArray(), normals.toTypedArray(), textureCoords.toTypedArray()),
|
||||
meshes
|
||||
)
|
||||
}
|
||||
42
orx-obj-loader/src/main/kotlin/Polygon.kt
Normal file
42
orx-obj-loader/src/main/kotlin/Polygon.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
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
|
||||
|
||||
class Polygon(
|
||||
val positions: Array<Vector3> = emptyArray(),
|
||||
val normals: Array<Vector3> = emptyArray(),
|
||||
val textureCoords: Array<Vector2> = emptyArray()
|
||||
) {
|
||||
fun transform(t: Matrix44): Polygon {
|
||||
return Polygon(positions.map { (t * it.xyz1).div }.toTypedArray(), normals, textureCoords)
|
||||
}
|
||||
}
|
||||
|
||||
class Box(val corner: Vector3, val width: Double, val height: Double, val depth: Double)
|
||||
|
||||
fun bounds(polygons: List<Polygon>): 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 {
|
||||
minX = min(minX, it.x)
|
||||
minY = min(minY, it.y)
|
||||
minZ = min(minZ, it.z)
|
||||
|
||||
maxX = max(maxX, it.x)
|
||||
maxY = max(maxY, it.y)
|
||||
maxZ = max(maxZ, it.z)
|
||||
}
|
||||
}
|
||||
return Box(Vector3(minX, minY, minZ), maxX - minX, maxY - minY, maxZ - minZ)
|
||||
}
|
||||
10
orx-obj-loader/src/main/kotlin/VertexData.kt
Normal file
10
orx-obj-loader/src/main/kotlin/VertexData.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
|
||||
class VertexData(
|
||||
val positions: Array<Vector3> = emptyArray(),
|
||||
val normals: Array<Vector3> = emptyArray(),
|
||||
val textureCoords: Array<Vector2> = emptyArray()
|
||||
)
|
||||
9
orx-obj-loader/src/main/kotlin/Wireframe.kt
Normal file
9
orx-obj-loader/src/main/kotlin/Wireframe.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package org.openrndr.extra.objloader
|
||||
|
||||
import org.openrndr.math.Vector3
|
||||
|
||||
fun MeshData.wireframe() : List<List<Vector3>> {
|
||||
return polygonGroups.values.flatMap {
|
||||
it.map { ip -> ip.toPolygon(this.vertexData).positions.toList() }
|
||||
}
|
||||
}
|
||||
240
orx-obj-loader/test-data/non-planar.obj
Normal file
240
orx-obj-loader/test-data/non-planar.obj
Normal file
@@ -0,0 +1,240 @@
|
||||
# Blender 4.1.1
|
||||
# www.blender.org
|
||||
o DualMesh.001
|
||||
v 0.021984 -0.277658 -0.430998
|
||||
v -0.204619 -0.556645 0.115528
|
||||
v -0.403490 -0.542851 0.064243
|
||||
v -0.544140 -0.114765 0.332156
|
||||
v 0.015883 -0.555982 -0.063713
|
||||
v 0.236454 -0.045176 -0.339770
|
||||
v 0.249796 0.218757 0.111759
|
||||
v -0.112775 0.341972 0.321373
|
||||
v -0.193385 0.522715 0.053406
|
||||
v -0.678204 -0.027070 -0.001874
|
||||
v -0.032705 0.104827 0.393468
|
||||
v 0.123686 -0.395786 0.113667
|
||||
v -0.116826 -0.470232 -0.270761
|
||||
v 0.291240 -0.022586 -0.001464
|
||||
v 0.165593 -0.405526 -0.158528
|
||||
v -0.495750 -0.031019 -0.449963
|
||||
v -0.646896 0.109705 -0.278212
|
||||
v -0.386238 0.480781 -0.124313
|
||||
v 0.027355 -0.081802 -0.498989
|
||||
v -0.666021 -0.167266 -0.157766
|
||||
v -0.577613 -0.415008 0.123184
|
||||
v -0.215203 0.409463 -0.316206
|
||||
v -0.364278 0.377487 -0.344023
|
||||
v -0.087502 0.207641 -0.445280
|
||||
v 0.109200 0.448525 0.017939
|
||||
v 0.186167 0.055347 0.318411
|
||||
v -0.213099 -0.176466 -0.501705
|
||||
v 0.279213 0.116671 -0.158070
|
||||
v -0.381582 -0.407444 0.269440
|
||||
v -0.282921 -0.090902 0.426808
|
||||
v -0.423395 0.342130 0.178287
|
||||
v -0.608862 0.331507 -0.161547
|
||||
v 0.113853 -0.195037 -0.432046
|
||||
v 0.042877 -0.381805 -0.356640
|
||||
v -0.255488 -0.587709 0.008464
|
||||
v -0.080638 -0.546490 0.111235
|
||||
v -0.489237 -0.493150 0.005557
|
||||
v -0.546990 -0.387226 -0.201046
|
||||
v -0.448887 -0.472520 -0.139017
|
||||
v -0.600003 -0.008096 0.266411
|
||||
v -0.562672 -0.232964 0.301183
|
||||
v 0.010728 -0.542196 0.032509
|
||||
v 0.268279 -0.222690 -0.245134
|
||||
v 0.223645 -0.168571 -0.332393
|
||||
v 0.235145 0.313794 0.024661
|
||||
v 0.176527 0.279206 0.225200
|
||||
v 0.172309 0.186422 0.304716
|
||||
v 0.074674 0.421514 0.204670
|
||||
v -0.053842 0.424907 0.242095
|
||||
v -0.309003 0.490575 0.089716
|
||||
v -0.104763 0.501903 0.121662
|
||||
v -0.666211 -0.072755 0.134157
|
||||
v -0.662518 0.128767 0.007951
|
||||
v -0.680730 0.168731 -0.122329
|
||||
v 0.071148 -0.005290 0.391771
|
||||
v 0.021967 0.245545 0.361024
|
||||
v -0.194470 0.072487 0.412014
|
||||
v 0.219431 -0.316766 0.075989
|
||||
v 0.097967 -0.110582 0.344100
|
||||
v -0.018233 -0.202148 0.342412
|
||||
v -0.037383 -0.327600 0.279150
|
||||
v 0.031331 -0.400143 0.193220
|
||||
v -0.068883 -0.535387 -0.174527
|
||||
v -0.121589 -0.588925 -0.088666
|
||||
v -0.277981 -0.460584 -0.267848
|
||||
v -0.284602 -0.373603 -0.388582
|
||||
v 0.279098 0.024117 0.132942
|
||||
v 0.277341 -0.177928 0.007750
|
||||
v 0.292635 -0.221079 -0.119253
|
||||
v 0.237104 -0.352246 -0.052307
|
||||
v 0.069419 -0.501412 -0.145866
|
||||
v 0.181855 -0.351574 -0.273267
|
||||
v -0.411624 0.035548 -0.498635
|
||||
v -0.417254 -0.162936 -0.451488
|
||||
v -0.445481 -0.356561 -0.332897
|
||||
v -0.513819 -0.243523 -0.358687
|
||||
v -0.593192 0.305409 -0.265858
|
||||
v -0.582652 0.210948 -0.331099
|
||||
v -0.624821 -0.003842 -0.337998
|
||||
v -0.480599 0.384079 -0.291137
|
||||
v -0.495007 0.410860 -0.188673
|
||||
v -0.408269 0.478779 -0.006055
|
||||
v 0.075385 0.111976 -0.432794
|
||||
v 0.167417 0.186984 -0.342917
|
||||
v 0.112700 -0.016790 -0.448287
|
||||
v -0.639972 -0.138190 -0.279984
|
||||
v -0.641318 -0.304137 -0.107135
|
||||
v -0.644548 -0.219084 0.163250
|
||||
v -0.633932 -0.312571 0.078065
|
||||
v -0.224367 0.484840 -0.201995
|
||||
v -0.131621 0.510707 -0.117921
|
||||
v -0.375824 0.250861 -0.422573
|
||||
v -0.475814 0.167171 -0.436026
|
||||
v -0.253863 0.017689 -0.532891
|
||||
v -0.234268 0.155056 -0.483175
|
||||
v -0.081703 0.333228 -0.359859
|
||||
v 0.071827 0.308456 -0.315256
|
||||
v 0.039466 0.441940 -0.117374
|
||||
v 0.133434 0.345092 -0.198605
|
||||
v 0.024499 0.498264 0.086863
|
||||
v 0.216450 -0.044082 0.265688
|
||||
v -0.132096 -0.064058 -0.534789
|
||||
v -0.147608 -0.275368 -0.464692
|
||||
v 0.255269 0.086844 -0.279571
|
||||
v 0.252511 0.256621 -0.109472
|
||||
v -0.489316 -0.463854 0.183910
|
||||
v -0.234418 -0.454549 0.250139
|
||||
v -0.409915 -0.298034 0.358924
|
||||
v -0.426552 -0.050014 0.407378
|
||||
v -0.246355 -0.221582 0.405114
|
||||
v -0.537048 0.376931 0.018764
|
||||
v -0.561709 0.281796 0.100541
|
||||
v -0.481989 0.060101 0.345142
|
||||
v -0.370654 0.149064 0.341946
|
||||
v -0.346059 0.275707 0.280567
|
||||
v -0.632405 0.298653 -0.070722
|
||||
vn 0.5962 -0.4245 -0.6814
|
||||
vn 0.1490 -0.9719 0.1823
|
||||
vn 0.2875 -0.8778 -0.3831
|
||||
vn 0.6849 -0.7152 0.1393
|
||||
vn 0.7374 0.6163 0.2765
|
||||
vn 0.3738 0.5589 0.7402
|
||||
vn -0.2462 0.7253 0.6429
|
||||
vn -0.9922 -0.0138 -0.1240
|
||||
vn -0.1357 0.3569 0.9242
|
||||
vn 0.6095 -0.2055 0.7657
|
||||
vn -0.2404 -0.8899 -0.3876
|
||||
vn 0.9861 0.1600 0.0456
|
||||
vn -0.9885 -0.1456 0.0408
|
||||
vn -0.2413 -0.2056 -0.9484
|
||||
vn 0.0490 -0.7017 -0.7108
|
||||
vn -0.5956 0.8033 0.0064
|
||||
vn 0.6324 -0.0808 -0.7704
|
||||
vn 0.3071 0.8029 -0.5108
|
||||
vn 0.0265 0.5458 -0.8375
|
||||
vn -0.4196 0.4947 -0.7611
|
||||
vn 0.6624 0.7063 -0.2498
|
||||
vn -0.5921 0.7559 -0.2794
|
||||
vn -0.6543 0.1375 -0.7437
|
||||
vn -0.6345 -0.4955 0.5932
|
||||
vn 0.1770 -0.3235 -0.9295
|
||||
vn 0.3698 -0.7898 -0.4893
|
||||
vn 0.2423 -0.6590 0.7121
|
||||
vn -0.6002 0.1551 0.7846
|
||||
vn -0.7620 -0.4317 -0.4826
|
||||
vn -0.3221 -0.8003 -0.5057
|
||||
vn -0.7351 -0.6660 -0.1272
|
||||
vn -0.8572 -0.0512 0.5124
|
||||
vn -0.1477 -0.8389 0.5238
|
||||
vn 0.8961 0.1645 0.4123
|
||||
vn 0.9345 -0.2878 0.2093
|
||||
vn -0.8672 0.3667 0.3369
|
||||
vn 0.1343 -0.5441 0.8282
|
||||
vn 0.3659 0.1700 0.9150
|
||||
vn 0.1665 -0.1369 0.9765
|
||||
vn 0.4960 -0.6812 0.5385
|
||||
vn 0.7382 -0.3787 0.5583
|
||||
vn 0.9876 0.0011 -0.1571
|
||||
vn 0.8288 -0.5252 -0.1932
|
||||
vn -0.6230 -0.2593 -0.7380
|
||||
vn -0.2853 -0.4366 -0.8532
|
||||
vn -0.9072 0.3101 -0.2841
|
||||
vn 0.1597 0.8589 0.4865
|
||||
vn -0.1991 0.8752 -0.4409
|
||||
vn 0.3470 0.4953 -0.7964
|
||||
vn 0.7676 0.4843 -0.4198
|
||||
vn 0.6576 0.2105 -0.7234
|
||||
vn 0.2437 0.9639 -0.1069
|
||||
vn -0.1615 0.3536 -0.9214
|
||||
vn 0.2191 0.2732 -0.9367
|
||||
vn -0.3048 -0.1984 0.9315
|
||||
vn -0.5246 0.6877 0.5019
|
||||
vn -0.1506 0.9862 -0.0690
|
||||
vn -0.1870 0.2957 0.9368
|
||||
vn -0.7408 0.4011 0.5388
|
||||
vn -0.5565 -0.8199 0.1344
|
||||
s 0
|
||||
f 33//1 44//1 43//1 72//1 34//1 1//1
|
||||
f 35//2 64//2 5//2 42//2 36//2 2//2
|
||||
f 5//3 64//3 63//3 71//3
|
||||
f 12//4 42//4 5//4 71//4 15//4 70//4 58//4
|
||||
f 7//5 45//5 25//5 100//5 48//5 46//5
|
||||
f 8//6 56//6 47//6 46//6 48//6 49//6
|
||||
f 9//7 50//7 31//7 115//7 8//7 49//7 51//7
|
||||
f 20//8 10//8 53//8 54//8 17//8 79//8 86//8
|
||||
f 11//9 56//9 8//9 115//9 114//9 57//9
|
||||
f 26//10 55//10 59//10 101//10
|
||||
f 13//11 63//11 64//11 35//11 3//11 37//11 39//11 65//11
|
||||
f 7//12 67//12 14//12 28//12 105//12 45//12
|
||||
f 88//13 52//13 10//13 20//13 87//13 89//13
|
||||
f 73//14 94//14 102//14 27//14 74//14 16//14
|
||||
f 66//15 103//15 1//15 34//15 13//15 65//15
|
||||
f 18//16 81//16 32//16 116//16 111//16 82//16
|
||||
f 6//17 44//17 33//17 19//17 85//17
|
||||
f 97//18 96//18 22//18 90//18 91//18 98//18 99//18
|
||||
f 24//19 95//19 92//19 23//19 22//19 96//19
|
||||
f 77//20 80//20 23//20 92//20 93//20 78//20
|
||||
f 45//21 105//21 99//21 98//21 25//21
|
||||
f 32//22 81//22 80//22 77//22
|
||||
f 78//23 93//23 73//23 16//23 79//23 17//23
|
||||
f 29//24 108//24 41//24 88//24 89//24 21//24 106//24
|
||||
f 27//25 102//25 19//25 33//25 1//25 103//25
|
||||
f 34//26 72//26 15//26 71//26 63//26 13//26
|
||||
f 36//27 62//27 61//27 107//27 2//27
|
||||
f 4//28 109//28 113//28 40//28
|
||||
f 38//29 87//29 20//29 86//29 76//29 75//29
|
||||
f 38//30 75//30 66//30 65//30 39//30
|
||||
f 37//31 21//31 89//31 87//31 38//31 39//31
|
||||
f 52//32 88//32 41//32 4//32 40//32
|
||||
f 29//33 106//33 3//33 35//33 2//33 107//33
|
||||
f 67//34 7//34 46//34 47//34 26//34 101//34
|
||||
f 58//35 70//35 69//35 68//35
|
||||
f 53//36 112//36 111//36 116//36 54//36
|
||||
f 60//37 110//37 108//37 29//37 107//37 61//37
|
||||
f 11//38 55//38 26//38 47//38 56//38
|
||||
f 59//39 55//39 11//39 57//39 30//39 110//39 60//39
|
||||
f 12//40 62//40 36//40 42//40
|
||||
f 67//41 101//41 59//41 60//41 61//41 62//41 12//41 58//41 68//41 14//41
|
||||
f 28//42 14//42 68//42 69//42 43//42 44//42 6//42 104//42
|
||||
f 15//43 72//43 43//43 69//43 70//43
|
||||
f 76//44 86//44 79//44 16//44 74//44
|
||||
f 74//45 27//45 103//45 66//45 75//45 76//45
|
||||
f 32//46 77//46 78//46 17//46 54//46 116//46
|
||||
f 48//47 100//47 51//47 49//47
|
||||
f 23//48 80//48 81//48 18//48 90//48 22//48
|
||||
f 96//49 97//49 84//49 83//49 24//49
|
||||
f 99//50 105//50 28//50 104//50 84//50 97//50
|
||||
f 84//51 104//51 6//51 85//51 83//51
|
||||
f 51//52 100//52 25//52 98//52 91//52 9//52
|
||||
f 94//53 73//53 93//53 92//53 95//53
|
||||
f 24//54 83//54 85//54 19//54 102//54 94//54 95//54
|
||||
f 30//55 109//55 4//55 41//55 108//55 110//55
|
||||
f 82//56 111//56 112//56 31//56 50//56
|
||||
f 9//57 91//57 90//57 18//57 82//57 50//57
|
||||
f 30//58 57//58 114//58 113//58 109//58
|
||||
f 10//59 52//59 40//59 113//59 114//59 115//59 31//59 112//59 53//59
|
||||
f 3//60 106//60 21//60 37//60
|
||||
Reference in New Issue
Block a user