Fix the Android filamesh file loader (#709)

* Fix the Android filamesh file loader

The loader was not updated to support the SNORM16 format now sometimes
used to encode UV sets in filamesh files.

Fixes #708

* Update android/samples/image-based-lighting/app/src/main/java/com/google/android/filament/ibl/MeshLoader.kt
This commit is contained in:
Romain Guy
2019-01-15 16:11:34 -08:00
committed by GitHub
parent b3cd6a7f59
commit cebc83e8fa
3 changed files with 74 additions and 15 deletions

View File

@@ -69,12 +69,16 @@ fun loadMesh(assets: AssetManager, name: String,
private const val FILAMESH_FILE_IDENTIFIER = "FILAMESH"
private const val MAX_UINT32 = 4294967295
private const val HEADER_FLAG_INTERLEAVED = 0x1L
private const val HEADER_FLAG_SNORM16_UV = 0x2L
private const val HEADER_FLAG_COMPRESSED = 0x4L
private class Header {
var valid = false
var versionNumber = 0L
var parts = 0L
var aabb = Box()
var interleaved = 0L
var flags = 0L
var posOffset = 0L
var positionStride = 0L
var tangentOffset = 0L
@@ -121,7 +125,7 @@ private fun readHeader(input: InputStream): Header {
header.aabb = Box(
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input),
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input))
header.interleaved = readUIntLE(input)
header.flags = readUIntLE(input)
header.posOffset = readUIntLE(input)
header.positionStride = readUIntLE(input)
header.tangentOffset = readUIntLE(input)
@@ -142,7 +146,6 @@ private fun readHeader(input: InputStream): Header {
return header
}
private fun readSizedData(channel: ReadableByteChannel, sizeInBytes: Long): ByteBuffer {
val buffer = ByteBuffer.allocateDirect(sizeInBytes.toInt())
buffer.order(ByteOrder.LITTLE_ENDIAN)
@@ -190,7 +193,15 @@ private fun createIndexBuffer(engine: Engine, header: Header, data: ByteBuffer):
.apply { setBuffer(engine, data) }
}
private fun uvNormalized(header: Header) = header.flags and HEADER_FLAG_SNORM16_UV != 0L
private fun createVertexBuffer(engine: Engine, header: Header, data: ByteBuffer): VertexBuffer {
val uvType = if (!uvNormalized(header)) {
HALF2
} else {
SHORT2
}
val vertexBufferBuilder = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(header.totalVertices.toInt())
@@ -203,11 +214,23 @@ private fun createVertexBuffer(engine: Engine, header: Header, data: ByteBuffer)
.attribute(POSITION, 0, HALF4, header.posOffset.toInt(), header.positionStride.toInt())
.attribute(TANGENTS, 0, SHORT4, header.tangentOffset.toInt(), header.tangentStride.toInt())
.attribute(COLOR, 0, UBYTE4, header.colorOffset.toInt(), header.colorStride.toInt())
.attribute(UV0, 0, HALF2, header.uv0Offset.toInt(), header.uv0Stride.toInt())
// UV coordinates are stored as normalized 16-bit integers or half-floats depending on
// the range they span. When stored as half-float, there is only enough precision for
// sub-pixel addressing in textures that are <= 1024x1024
.attribute(UV0, 0, uvType, header.uv0Offset.toInt(), header.uv0Stride.toInt())
// When UV coordinates are stored as 16-bit integers we must normalize them (we want values
// in the range -1..1)
if (uvNormalized(header)) {
vertexBufferBuilder.normalized(UV0)
}
if (header.uv1Offset != MAX_UINT32 && header.uv1Stride != MAX_UINT32) {
vertexBufferBuilder
.attribute(UV1, 0, HALF2, header.uv1Offset.toInt(), header.uv1Stride.toInt())
.attribute(UV1, 0, uvType, header.uv1Offset.toInt(), header.uv1Stride.toInt())
if (uvNormalized(header)) {
vertexBufferBuilder.normalized(UV1)
}
}
return vertexBufferBuilder.build(engine).apply { setBufferAt(engine, 0, data) }
@@ -224,7 +247,7 @@ private fun createRenderable(
val builder = RenderableManager.Builder(header.parts.toInt()).boundingBox(header.aabb)
(0 until header.parts.toInt()).forEach { i ->
repeat(header.parts.toInt()) { i ->
builder.geometry(i,
RenderableManager.PrimitiveType.TRIANGLES,
vertexBuffer,

View File

@@ -69,12 +69,16 @@ fun loadMesh(assets: AssetManager, name: String,
private const val FILAMESH_FILE_IDENTIFIER = "FILAMESH"
private const val MAX_UINT32 = 4294967295
private const val HEADER_FLAG_INTERLEAVED = 0x1L
private const val HEADER_FLAG_SNORM16_UV = 0x2L
private const val HEADER_FLAG_COMPRESSED = 0x4L
private class Header {
var valid = false
var versionNumber = 0L
var parts = 0L
var aabb = Box()
var interleaved = 0L
var flags = 0L
var posOffset = 0L
var positionStride = 0L
var tangentOffset = 0L
@@ -121,7 +125,7 @@ private fun readHeader(input: InputStream): Header {
header.aabb = Box(
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input),
readFloat32LE(input), readFloat32LE(input), readFloat32LE(input))
header.interleaved = readUIntLE(input)
header.flags = readUIntLE(input)
header.posOffset = readUIntLE(input)
header.positionStride = readUIntLE(input)
header.tangentOffset = readUIntLE(input)
@@ -142,7 +146,6 @@ private fun readHeader(input: InputStream): Header {
return header
}
private fun readSizedData(channel: ReadableByteChannel, sizeInBytes: Long): ByteBuffer {
val buffer = ByteBuffer.allocateDirect(sizeInBytes.toInt())
buffer.order(ByteOrder.LITTLE_ENDIAN)
@@ -190,7 +193,15 @@ private fun createIndexBuffer(engine: Engine, header: Header, data: ByteBuffer):
.apply { setBuffer(engine, data) }
}
private fun uvNormalized(header: Header) = header.flags and HEADER_FLAG_SNORM16_UV != 0L
private fun createVertexBuffer(engine: Engine, header: Header, data: ByteBuffer): VertexBuffer {
val uvType = if (!uvNormalized(header)) {
HALF2
} else {
SHORT2
}
val vertexBufferBuilder = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(header.totalVertices.toInt())
@@ -203,13 +214,23 @@ private fun createVertexBuffer(engine: Engine, header: Header, data: ByteBuffer)
.attribute(POSITION, 0, HALF4, header.posOffset.toInt(), header.positionStride.toInt())
.attribute(TANGENTS, 0, SHORT4, header.tangentOffset.toInt(), header.tangentStride.toInt())
.attribute(COLOR, 0, UBYTE4, header.colorOffset.toInt(), header.colorStride.toInt())
// UV coordinates are stored in fp16, which gives sub-pixel precision only
// for textures up to 1024x1024
.attribute(UV0, 0, HALF2, header.uv0Offset.toInt(), header.uv0Stride.toInt())
// UV coordinates are stored as normalized 16-bit integers or half-floats depending on
// the range they span. When stored as half-float, there is only enough precision for
// sub-pixel addressing in textures that are <= 1024x1024
.attribute(UV0, 0, uvType, header.uv0Offset.toInt(), header.uv0Stride.toInt())
// When UV coordinates are stored as 16-bit integeres we must normalize them (we want values
// in the range -1..1)
if (uvNormalized(header)) {
vertexBufferBuilder.normalized(UV0)
}
if (header.uv1Offset != MAX_UINT32 && header.uv1Stride != MAX_UINT32) {
vertexBufferBuilder
.attribute(UV1, 0, HALF2, header.uv1Offset.toInt(), header.uv1Stride.toInt())
.attribute(UV1, 0, uvType, header.uv1Offset.toInt(), header.uv1Stride.toInt())
if (uvNormalized(header)) {
vertexBufferBuilder.normalized(UV1)
}
}
return vertexBufferBuilder.build(engine).apply { setBufferAt(engine, 0, data) }

View File

@@ -198,6 +198,14 @@ Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
mesh.indexBuffer->setBuffer(*engine,
IndexBuffer::BufferDescriptor(indices, header->indexSize));
const uint32_t FLAG_SNORM16_UV = 0x2;
VertexBuffer::AttributeType::HALF2 uvType = VertexBuffer::AttributeType::HALF2;
if (header->flags & FLAG_SNORM16_UV) {
uvType = VertexBuffer::AttributeType::SHORT2;
}
bool uvNormalized = header->flags & FLAG_SNORM16_UV;
VertexBuffer::Builder vbb;
vbb.vertexCount(header->vertexCount)
.bufferCount(1)
@@ -209,13 +217,20 @@ Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
header->offsetTangents, uint8_t(header->strideTangents))
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4,
header->offsetColor, uint8_t(header->strideColor))
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::HALF2,
.attribute(VertexAttribute::UV0, 0, uvType,
header->offsetUV0, uint8_t(header->strideUV0));
if (uvNormalized) {
vbb.normalized(VertexAttribute::UV0);
}
if (header->offsetUV1 != std::numeric_limits<uint32_t>::max() &&
header->strideUV1 != std::numeric_limits<uint32_t>::max()) {
vbb.attribute(VertexAttribute::UV1, 0, VertexBuffer::AttributeType::HALF2,
vbb.attribute(VertexAttribute::UV1, 0, uvType,
header->offsetUV1, uint8_t(header->strideUV1));
if (uvNormalized) {
vbb.normalized(VertexAttribute::UV1);
}
}
mesh.vertexBuffer = vbb.build(*engine);