[orx-mesh, orx-mesh-generator, orx-obj-loader] Add decal and tangent tools
This commit is contained in:
@@ -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"))
|
||||
}
|
||||
|
||||
@@ -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<String, List<IPolygon>> {
|
||||
return compounds.mapValues { it.value.toPolygons() }
|
||||
}
|
||||
@@ -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<Int>,
|
||||
override val textureCoords: List<Int>,
|
||||
override val normals: List<Int>,
|
||||
override val colors: List<Int>,
|
||||
override val normals: List<Int>,
|
||||
override val tangents: List<Int>,
|
||||
override val bitangents: List<Int>
|
||||
|
||||
) : IIndexedPolygon {
|
||||
) : IIndexedPolygon {
|
||||
|
||||
private fun tessellate(vertexData: IVertexData): List<IndexedPolygon> {
|
||||
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()
|
||||
)
|
||||
}
|
||||
}
|
||||
33
orx-mesh/src/commonMain/kotlin/IndexedPolygonExtensions.kt
Normal file
33
orx-mesh/src/commonMain/kotlin/IndexedPolygonExtensions.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.openrndr.extra.mesh
|
||||
|
||||
import org.openrndr.math.LinearType
|
||||
import org.openrndr.math.Vector3
|
||||
|
||||
internal fun <T : LinearType<T>> bc(barycentric: Vector3, items: List<T>): 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
|
||||
)
|
||||
}
|
||||
@@ -8,8 +8,26 @@ import kotlin.jvm.JvmRecord
|
||||
interface IMeshData {
|
||||
val vertexData: IVertexData
|
||||
val polygons: List<IIndexedPolygon>
|
||||
|
||||
/**
|
||||
* Convert mesh data to triangular mesh data
|
||||
*/
|
||||
fun triangulate(): IMeshData
|
||||
|
||||
/**
|
||||
* Convert mesh data to a list of [IPolygon]
|
||||
*/
|
||||
fun toPolygons(): List<IPolygon>
|
||||
|
||||
/**
|
||||
* 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<IndexedPolygon>))
|
||||
} 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<Polygon> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -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<IntVector3, Int>.quantize(v: Vector3, bits: Int): Int =
|
||||
getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size }
|
||||
|
||||
fun MutableMap<IntVector2, Int>.quantize(v: Vector2, bits: Int): Int =
|
||||
getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size }
|
||||
|
||||
fun MutableMap<IntVector4, Int>.quantize(v: Vector4, bits: Int): Int =
|
||||
getOrPut((v * (1 shl bits).toDouble()).toInt()) { this.size }
|
||||
|
||||
val positionMap = mutableMapOf<IntVector3, Int>()
|
||||
val textureCoordMap = mutableMapOf<IntVector2, Int>()
|
||||
val colorMap = mutableMapOf<IntVector4, Int>()
|
||||
val normalMap = mutableMapOf<IntVector3, Int>()
|
||||
val tangentMap = mutableMapOf<IntVector3, Int>()
|
||||
val bitangentMap = mutableMapOf<IntVector3, Int>()
|
||||
|
||||
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<IndexedPolygon>()
|
||||
|
||||
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)
|
||||
}
|
||||
23
orx-mesh/src/commonMain/kotlin/Point.kt
Normal file
23
orx-mesh/src/commonMain/kotlin/Point.kt
Normal file
@@ -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
|
||||
)
|
||||
@@ -14,9 +14,9 @@ import kotlin.math.min
|
||||
*/
|
||||
interface IPolygon {
|
||||
val positions: List<Vector3>
|
||||
val normals: List<Vector3>
|
||||
val textureCoords: List<Vector2>
|
||||
val colors: List<ColorRGBa>
|
||||
val normals: List<Vector3>
|
||||
val tangents: List<Vector3>
|
||||
val bitangents: List<Vector3>
|
||||
|
||||
@@ -33,14 +33,14 @@ interface IPolygon {
|
||||
*/
|
||||
class Polygon(
|
||||
override val positions: List<Vector3> = emptyList(),
|
||||
override val normals: List<Vector3> = emptyList(),
|
||||
override val textureCoords: List<Vector2> = emptyList(),
|
||||
override val colors: List<ColorRGBa> = emptyList(),
|
||||
override val normals: List<Vector3> = emptyList(),
|
||||
override val tangents: List<Vector3> = emptyList(),
|
||||
override val bitangents: List<Vector3> = 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<Vector3> = mutableListOf(),
|
||||
override val normals: MutableList<Vector3> = mutableListOf(),
|
||||
override val textureCoords: MutableList<Vector2> = mutableListOf(),
|
||||
override val colors: MutableList<ColorRGBa> = mutableListOf(),
|
||||
override val normals: MutableList<Vector3> = mutableListOf(),
|
||||
override val tangents: MutableList<Vector3> = mutableListOf(),
|
||||
override val bitangents: MutableList<Vector3> = 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<IPolygon>.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()
|
||||
)
|
||||
|
||||
@@ -13,11 +13,6 @@ interface IVertexData {
|
||||
*/
|
||||
val positions: List<Vector3>
|
||||
|
||||
/**
|
||||
* Vertex normals
|
||||
*/
|
||||
val normals: List<Vector3>
|
||||
|
||||
/**
|
||||
* Vertex texture coordinates
|
||||
*/
|
||||
@@ -28,6 +23,11 @@ interface IVertexData {
|
||||
*/
|
||||
val colors: List<ColorRGBa>
|
||||
|
||||
/**
|
||||
* Vertex normals
|
||||
*/
|
||||
val normals: List<Vector3>
|
||||
|
||||
/**
|
||||
* Vertex tangents
|
||||
*/
|
||||
@@ -37,6 +37,16 @@ interface IVertexData {
|
||||
* Vertex bitangents
|
||||
*/
|
||||
val bitangents: List<Vector3>
|
||||
|
||||
/**
|
||||
* Convert to [VertexData]
|
||||
*/
|
||||
fun toVertexData() : VertexData
|
||||
|
||||
/**
|
||||
* Convert to [MutableVertexData]
|
||||
*/
|
||||
fun toMutableVertexData() : MutableVertexData
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,26 +54,23 @@ interface IVertexData {
|
||||
*/
|
||||
class VertexData(
|
||||
override val positions: List<Vector3> = emptyList(),
|
||||
override val normals: List<Vector3> = emptyList(),
|
||||
override val textureCoords: List<Vector2> = emptyList(),
|
||||
override val colors: List<ColorRGBa> = emptyList(),
|
||||
override val normals: List<Vector3> = emptyList(),
|
||||
override val tangents: List<Vector3> = emptyList(),
|
||||
override val bitangents: List<Vector3> = 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<Vector3> = mutableListOf(),
|
||||
override val normals: MutableList<Vector3> = mutableListOf(),
|
||||
override val textureCoords: MutableList<Vector2> = mutableListOf(),
|
||||
override val colors: MutableList<ColorRGBa> = mutableListOf(),
|
||||
override val normals: MutableList<Vector3> = mutableListOf(),
|
||||
override val tangents: MutableList<Vector3> = mutableListOf(),
|
||||
override val bitangents: MutableList<Vector3> = 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)
|
||||
)
|
||||
}
|
||||
68
orx-mesh/src/commonMain/kotlin/VertexDataExtensions.kt
Normal file
68
orx-mesh/src/commonMain/kotlin/VertexDataExtensions.kt
Normal file
@@ -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<IndexedPolygon>()
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -9,7 +9,6 @@ fun IMeshData.wireframe(): List<List<Vector3>> {
|
||||
return polygons.map { ip -> ip.toPolygon(this.vertexData).positions.toList() }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract wireframe from compound mesh data
|
||||
*/
|
||||
|
||||
@@ -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<Polygon>
|
||||
textureCoordinates.add(buffer.getVector2())
|
||||
colors.add(buffer.getColorRGBa())
|
||||
}
|
||||
polygons.add(Polygon(positions, normals, textureCoordinates, colors))
|
||||
polygons.add(Polygon(positions, textureCoordinates, colors, normals))
|
||||
}
|
||||
return polygons
|
||||
}
|
||||
Reference in New Issue
Block a user