[orx-mesh, orx-mesh-generator, orx-obj-loader] Add decal and tangent tools

This commit is contained in:
Edwin Jakobs
2024-09-25 09:51:46 +02:00
parent fb3bb6f7a6
commit e016891b1d
28 changed files with 1072 additions and 82 deletions

View File

@@ -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"))
}

View File

@@ -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() }
}

View File

@@ -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()
)
}
}

View 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
)
}

View File

@@ -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")
}
}

View File

@@ -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)
}

View 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
)

View File

@@ -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()
)

View File

@@ -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)
)
}

View 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)
}

View File

@@ -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
*/

View File

@@ -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
}