libgdx中只有obj加载器和自定义格式的g3d加载器,如果想使用别的模型,就只能先转成fbv,再转成g3d,非常的不方便(
查看libgdx的ObjLoader和G3dModelLoader,只要继承**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
}