Skip to content

libgdx自定义模型加载器

Published:

libgdx中只有obj加载器和自定义格式的g3d加载器,如果想使用别的模型,就只能先转成fbv,再转成g3d,非常的不方便(

查看libgdx的ObjLoaderG3dModelLoader,只要继承**ModelLoader<ModelLoader.ModelParameters>**这个类就可以创建自己的模型加载器了

class PlyLoader(r: FileHandleResolver): ModelLoader<ModelLoader.ModelParameters>(r) {
    override fun loadModelData(p0: FileHandle, p1: ModelParameters?): ModelData {
        TODO("")
    }
}

继承后需要实现loadModelData方法,方法返回ModelData

查看ModelData,有这几个成员

public class ModelData {
    public String id;
    public final short[] version = new short[2];
    public final Array<ModelMesh> meshes = new Array();
    public final Array<ModelMaterial> materials = new Array();
    public final Array<ModelNode> nodes = new Array();
    public final Array<ModelAnimation> animations = new Array();
}

先解析之前保存的ply模型

class PlyHeader(
    val format: Int,
    val vertex: Int,
    val face: Int,
    val types: IntArray
) {
    companion object {
        fun parse(header: List<String>): PlyHeader {
            var step = 0
            var format = -1
            var vertex = 0
            var face = 0
            val types = mutableListOf<Int>()
            header.forEach { line->
                val params = line.split(" ")
                if(params.isNotEmpty()) {
                    when(params[0]) {
                        "format" -> {
                            format = when(params[1]) {
                                "ascii" -> 0
                                "binary_little_endian" -> 1
                                "binary_big_endian" -> 2
                                else -> -1
                            }
                        }
                        "property" -> {
                            if(step==1){
                                when(params[1]) {
                                    "float" -> types.add(1)
                                    "int" -> types.add(2)
                                    "uchar" -> types.add(3)
                                }
                            }
                        }
                        "element" -> {
                            when(params[1]) {
                                "vertex" -> {
                                    step = 1
                                    vertex = params[2].toInt()
                                }
                                "face" -> {
                                    step = 2
                                    face = params[2].toInt()
                                }
                            }
                        }
                    }
                }
            }
            return PlyHeader(format, vertex, face, types.toIntArray())
        }
    }
}
interface LineParser {
    fun parseVertices(array: FloatArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int
    fun parseIndices(array: ShortArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int
}
class AsciiParser(
    val v_type: IntArray,
): LineParser {
    override fun parseVertices(array: FloatArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int {
        val en = buffer.find(0x0A, bufferOffset)
        val str = buffer.string(bufferOffset, en-bufferOffset).split(" ")
        var i = 0
        v_type.forEach { type ->
            when(type) {
                1,2 -> { //float
                    array[offset + i] = str[i].toFloat()
                }
                3 -> { //byte
                    array[offset + i] = str[i].toInt() / 255f
                }
            }
            i++
        }
        return en+1
    }

    override fun parseIndices(array: ShortArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int {
        val en = buffer.find(0x0A, bufferOffset)
        val str = buffer.string(bufferOffset, en-bufferOffset).split(" ")
        for(i in 1 until 4) {
            array[offset+i-1] = str[i].toShort()
        }
        return en+1
    }

}
class BinaryParser(
    val v_type: IntArray,
    val littleEndian: Boolean = true
): LineParser {
    override fun parseVertices(array: FloatArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int {
        var i = bufferOffset
        var j = offset
        v_type.forEach { type ->
            when(type) {
                1 -> { //float
                    array[j++] = buffer.float32(i, littleEndian)
                    i += 4
                }
                2 -> { //int
                    array[j++] = buffer.int32(i, littleEndian).toFloat()
                    i += 4
                }
                3 -> { //byte
                    array[j++] = (buffer[i].toInt() and 0xFF) / 255f
                    i++
                }
            }
        }
        return i
    }
    override fun parseIndices(array: ShortArray, offset: Int, buffer: ByteArray, bufferOffset: Int): Int {
        array[offset] = buffer.uint32(bufferOffset+1, littleEndian).toShort()
        array[offset+1] = buffer.uint32(bufferOffset+5, littleEndian).toShort()
        array[offset+2] = buffer.uint32(bufferOffset+9, littleEndian).toShort()
        return bufferOffset+13
    }

}
    fun loadMeshData(buffer: ByteArray): Pair<FloatArray, ShortArray> {
        val header = buffer.string(0,3)
        if(header!="ply") throw RuntimeException("not ply format")
        val headers = mutableListOf<String>()
        var i = buffer.find(0x0A, 0)+1
        while(true) {
            val j = buffer.find(0x0A, i)
            val str = buffer.string(i, j-i)
            i = j+1
            headers.add(str)
            if(str.startsWith("end_header")) break
        }
        val ph = PlyHeader.parse(headers)
        val parser: LineParser? = when(ph.format) {
            0 -> AsciiParser(ph.types)
            1 -> BinaryParser(ph.types, true)
            2 -> BinaryParser(ph.types, false)
            else -> null
        }
        if(parser==null) throw RuntimeException("unknown format")
        val vertices = FloatArray(ph.vertex * ph.types.size)
        val indices = ShortArray(ph.face * 3)
        for(j in 0 until ph.vertex) {
            i = parser.parseVertices(vertices, j * ph.types.size, buffer, i)
        }
        for(j in 0 until ph.face) {
            i = parser.parseIndices(indices, j * 3, buffer, i)
        }
        return vertices to indices
    }

然后保存到ModelData里面

    override fun loadModelData(p0: FileHandle, p1: ModelParameters?): ModelData {
        val buffer = p0.readBytes()
        val (v,i) = loadMeshData(buffer)
        val model = ModelData()
        // id随便命名
        model.id = "plymodel"

        val mesh = ModelMesh().apply {
            id = "mesh"
            vertices = v
            attributes = arrayOf(VertexAttribute.Position(), VertexAttribute.Normal(), VertexAttribute.ColorUnpacked())
            parts = arrayOf(
                ModelMeshPart().apply {
                    id = "part"
                    indices = i
                    primitiveType = GL20.GL_TRIANGLES
                }
            )
        }
        model.meshes.add(mesh)

        val material = ModelMaterial().apply {
            id = "material"
        }
        model.materials.add(material)

        val node = ModelNode().apply {
            id = "node"
            translation = Vector3()
            rotation = Quaternion()
            scale = Vector3(1f, 1f, 1f)
            meshId = "mesh"
            parts = arrayOf(
                ModelNodePart().apply {
                    meshPartId = "part"
                    materialId = "material"
                }
            )
            children = arrayOf()
        }

        model.nodes.add(node)

        return model
    }

测试

		// show
		val assets = AssetManager()
        assets.setLoader(Model::class.java, ".ply", PlyLoader(LocalFileHandleResolver()))
        assets.load("assets/ply/monu1.ply", Model::class.java)
	
		// render
		if(!loadend&&assets.update()){
			var mi = ModelInstance(assets.get("assets/ply/monu1.ply", Model::class.java))
            mi.transform.setToScaling(0.2f,0.2f,0.2f)
            instances.add(mi)
            loadend = true
        }

image-20241012163923040


Previous Post
astro-paper主题
Next Post
kotlin voxel解析 四