Skip to content

kotlin voxel解析 四

Published:

上一章中,我们发现转换后顶点数据中,有很多重复的点

image-20240925172117083

如上,可以合并成2个面,但实际上占用了8个面

因为每个面都是四边形,可以建一个类来进行后续操作

data class Quad(
    val a: Int,
    val b: Int,
    val c: Int,
    val d: Int
)

首先是判断四边形是否可以合并以及得到合并后的四边形

这里可以穷举每种可能的情况

    fun canMerge(oth: Quad): Int {
        return if (a == oth.c && b == oth.d) 1
        else if (b == oth.a && d == oth.c) 2
        else if (c == oth.a && d == oth.b) 3
        else if (a == oth.b && c == oth.d) 4
        else if (d == oth.b && c == oth.a) 5
        else 0
    }
    fun merge(oth: Quad, method: Int): Quad {
        return when(method) {
            1 -> Quad(oth.a, oth.b, c, d)
            2 -> Quad(a, oth.b, c, oth.d)
            3 -> Quad(a, b, oth.c, oth.d)
            4 -> Quad(oth.a, b, oth.c, d)
            5 -> Quad(a, b, oth.c, oth.d)
            else -> this
        }
    }

索引数组中,每6个点构成一个四边形,同时在转换顶点数据中,是按照特定的顺序生成的各个顶点

将索引数组转成Quad数组

fun buildQuad(indices: IntArray, offset: Int): Quad {
    return Quad(indices[offset], indices[offset+1], indices[offset+2], indices[offset+4])
}
fun buildQuads(indices: IntArray): MutableList<Quad> {
    val quads = ArrayList<Quad>(indices.size / 6)
    for(i in indices.indices step 6) {
        quads.add(buildQuad(indices, i))
    }
    return quads
}

遍历每个四边形进行合并

fun merge(quads: MutableList<Quad>) {
    var merged = true
    while(merged) {
        merged = false
        var i = 0
        while(i<quads.size) {
            var qa = quads[i]
            var j = i + 1
            while(j<quads.size){
                val qb = quads[j]
                if(qa.canMerge(qb)>0){
                    merged = true
                    quads[i] = qa.merge(qb, qa.canMerge(qb))
                    quads.removeAt(j)
                    qa = quads[i]
                }else{
                    j++
                }
            }
            i++
        }
    }
}

剔除合并后,未使用的顶点

data class Vertex(
    val x: Float,
    val y: Float,
    val z: Float,
    val nx: Float,
    val ny: Float,
    val nz: Float,
    val r: Float,
    val g: Float,
    val b: Float,
    val a: Float
)
fun buildVertices(vertices: FloatArray): MutableList<Vertex> {
    val ret = ArrayList<Vertex>(vertices.size / 10)
    for(i in vertices.indices step 10) {
        ret.add(Vertex(
            vertices[i],vertices[i+1],vertices[i+2],
            vertices[i+3],vertices[i+4],vertices[i+5],
            vertices[i+6],vertices[i+7],vertices[i+8],vertices[i+9]
        ))
    }
    return ret
}
fun toVertices(verts: List<Vertex>): FloatArray {
    val arr = FloatArray(verts.size * 10)
    for(i in verts.indices) {
        arr[10*i] = verts[i].x
        arr[10*i+1] = verts[i].y
        arr[10*i+2] = verts[i].z
        arr[10*i+3] = verts[i].nx
        arr[10*i+4] = verts[i].ny
        arr[10*i+5] = verts[i].nz
        arr[10*i+6] = verts[i].r
        arr[10*i+7] = verts[i].g
        arr[10*i+8] = verts[i].b
        arr[10*i+9] = verts[i].a
    }
    return arr
}
fun compressVertices(vertices: FloatArray, indices: IntArray): FloatArray {
    val verts = buildVertices(vertices)
    val min = indices.min()
    var max = indices.max()
    var i = min + 1
    while(i < max) {
        if(!indices.contains(i)){
            verts.removeAt(i)
            for(a in indices.indices) {
                if(indices[a]>i) indices[a] = indices[a]-1
            }
            max--
        }else {
            i++
        }
    }
    return toVertices(verts)
}

再来看下效果吧

val vc = VoxelLoader.load(Path("scene_aliens.vox").readBytes())
val (v,i) = c.optimization().buildArray()
val quads = buildQuads(i)
merge(quads)
val indices = toIndices(quads)
val vertices = compressVertices(v, indices)
convertPly(vertices,indices)

image-20240925205057446

面数缩减到了5730,文件大小缩减到了360KB

当然这个压缩方法不是最好的,且非常耗时,压缩这个vox用了20秒

完整代码在github


Previous Post
libgdx自定义模型加载器
Next Post
kotlin voxel解析 三