
info.laht.threekt.geometries.CylinderGeometry.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Port of the three.js 3D javascript library for Kotlin/JVM
The newest version!
package info.laht.threekt.geometries
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.FloatBufferAttribute
import info.laht.threekt.core.IntBufferAttribute
import info.laht.threekt.math.TWO_PI
import info.laht.threekt.math.Vector2
import info.laht.threekt.math.Vector3
import kotlin.math.cos
import kotlin.math.sin
open class CylinderBufferGeometry(
radiusTop: Float? = null,
radiusBottom: Float? = null,
height: Float? = null,
radialSegments: Int? = null,
heightSegments: Int? = null,
openEnded: Boolean? = null,
thetaStart: Float? = null,
thetaLength: Float? = null,
) : BufferGeometry() {
val radiusTop: Float = radiusTop ?: 1f
val radiusBottom: Float = radiusBottom ?: 1f
val height: Float = height ?: 1f
val radialSegments: Int = radialSegments ?: 32
val heightSegments: Int = heightSegments ?: 8
val openEnded: Boolean = openEnded ?: false
val thetaStart: Float = thetaStart ?: 0f
val thetaLength: Float = thetaLength ?: TWO_PI
constructor(radius: Float, height: Float) : this(radius, radius, height)
init {
val helper = CylinderBufferGeometryHelper()
setIndex(helper.indices)
addAttribute("position", helper.vertices)
addAttribute("normal", helper.normals)
addAttribute("uv", helper.uvs)
}
private inner class CylinderBufferGeometryHelper {
val indices: IntBufferAttribute
val vertices: FloatBufferAttribute
val normals: FloatBufferAttribute
val uvs: FloatBufferAttribute
var index = 0
var indexOffset = 0
var indexArray: MutableList> = mutableListOf()
val halfHeight = height / 2
var groupStart = 0
init {
var nbCap = 0
if (!openEnded) {
if (radiusTop > 0) {
nbCap++
}
if (radiusBottom > 0) {
nbCap++
}
}
val vertexCount = calculateVertexCount(nbCap)
val indexCount = calculateIndexCount(nbCap)
indices = IntBufferAttribute(IntArray(indexCount), 1)
vertices = FloatBufferAttribute(FloatArray(vertexCount * 3), 3)
normals = FloatBufferAttribute(FloatArray(vertexCount * 3), 3)
uvs = FloatBufferAttribute(FloatArray(vertexCount * 2), 2)
generateTorso()
if (!openEnded) {
if (radiusTop > 0) {
generateCap(true)
}
if (radiusBottom > 0) {
generateCap(false)
}
}
}
private fun calculateVertexCount(nbCap: Int): Int {
var count = (radialSegments + 1) * (heightSegments + 1)
if (!openEnded) {
count += (radialSegments + 1) * nbCap + radialSegments * nbCap
}
return count
}
private fun calculateIndexCount(nbCap: Int): Int {
var count = radialSegments * heightSegments * 2 * 3
if (!openEnded) {
count += radialSegments * nbCap * 3
}
return count
}
private fun generateTorso() {
val normal = Vector3()
val vertex = Vector3()
var groupCount = 0
// this will be used to calculate the normal
val slope = (radiusBottom - radiusTop) / height
// generate vertices, normals and uvs
for (y in 0..heightSegments) {
val indexRow = mutableListOf()
val v = y.toFloat() / heightSegments
// calculate the radius of the current row
val radius = v * (radiusBottom - radiusTop) + radiusTop
for (x in 0..radialSegments) {
val u = x.toFloat() / radialSegments
val theta = u * thetaLength + thetaStart
val sinTheta = sin(theta)
val cosTheta = cos(theta)
// vertex
vertex.x = radius * sinTheta
vertex.y = -v * height + halfHeight
vertex.z = radius * cosTheta
vertices.setXYZ(index, vertex.x, vertex.y, vertex.z)
// normal
normal.set(sinTheta, slope, cosTheta).normalize()
normals.setXYZ(index, normal.x, normal.y, normal.z)
// uv
uvs.setXY(index, u, 1 - v)
// save index of vertex in respective row
indexRow.add(index++)
}
// now save vertices of the row in our index array
indexArray.add(indexRow)
}
// generate indices
for (x in 0 until radialSegments) {
for (y in 0 until heightSegments) {
// we use the index array to access the correct indices
val i1 = indexArray[y][x]
val i2 = indexArray[y + 1][x]
val i3 = indexArray[y + 1][x + 1]
val i4 = indexArray[y][x + 1]
// face one
indices.setX(indexOffset, i1)
indexOffset++
indices.setX(indexOffset, i2)
indexOffset++
indices.setX(indexOffset, i4)
indexOffset++
// face two
indices.setX(indexOffset, i2)
indexOffset++
indices.setX(indexOffset, i3)
indexOffset++
indices.setX(indexOffset, i4)
indexOffset++
// update counters
groupCount += 6
}
}
// add a group to the geometry. this will ensure multi material support
addGroup(groupStart, groupCount, 0)
// calculate new start value for groups
groupStart += groupCount
}
private fun generateCap(top: Boolean) {
val uv = Vector2()
val vertex = Vector3()
var groupCount = 0
val radius = if (top) radiusTop else radiusBottom
val sign = if (top) 1 else -1
// save the index of the first center vertex
val centerIndexStart = index
// first we generate the center vertex data of the cap.
// because the geometry needs one set of uvs per face,
// we must generate a center vertex per face/segment
for (x in 1..radialSegments) {
// vertex
vertices.setXYZ(index, 0f, halfHeight * sign, 0f)
// normal
normals.setXYZ(index, 0f, sign.toFloat(), 0f)
// uv
uv.x = 0.5f
uv.y = 0.5f
uvs.setXY(index, uv.x, uv.y)
// increase index
index++
}
// save the index of the last center vertex
val centerIndexEnd = index
// now we generate the surrounding vertices, normals and uvs
for (x in 0..radialSegments) {
val u = x.toFloat() / radialSegments
val theta = u * thetaLength + thetaStart
val cosTheta = cos(theta)
val sinTheta = sin(theta)
// vertex
vertex.x = radius * sinTheta
vertex.y = halfHeight * sign
vertex.z = radius * cosTheta
vertices.setXYZ(index, vertex.x, vertex.y, vertex.z)
// normal
normals.setXYZ(index, 0f, sign.toFloat(), 0f)
// uv
uv.x = cosTheta * 0.5f + 0.5f
uv.y = sinTheta * 0.5f * sign.toFloat() + 0.5f
uvs.setXY(index, uv.x, uv.y)
// increase index
index++
}
// generate indices
for (x in 0 until radialSegments) {
val c = centerIndexStart + x
val i = centerIndexEnd + x
if (top) {
// face top
indices.setX(indexOffset, i)
indexOffset++
indices.setX(indexOffset, i + 1)
indexOffset++
indices.setX(indexOffset, c)
indexOffset++
} else {
// face bottom
indices.setX(indexOffset, i + 1)
indexOffset++
indices.setX(indexOffset, i)
indexOffset++
indices.setX(indexOffset, c)
indexOffset++
}
// update counters
groupCount += 3
}
// add a group to the geometry. this will ensure multi material support
addGroup(groupStart, groupCount, if (top) 1 else 2)
// calculate new start value for groups
groupStart += groupCount
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy