This is admittedly a very nitpicky change. For most of the changes, I went through the various Markdown files and added language names to the source blocks for better syntax highlighting on GitHub. It also makes it easier to copy and paste commands without copying the leading `$`. I avoided changing anything in `third_party`. Additionally, I added some instructions for compiling the Android samples on the command line and fixed some typos.
269 lines
9.2 KiB
Markdown
269 lines
9.2 KiB
Markdown
# Filamesh
|
|
|
|
`filamesh` converts any mesh file supported by `assimp` (as configured in this source tree) into a
|
|
custom binary file format. The goal of this binary file format is to allow test applications to
|
|
easily and quickly load meshes.
|
|
|
|
The source mesh must have at least one set of UV coordinates.
|
|
|
|
The destination mesh will contain vertex positions, one set of UV coordinates and per-vertex
|
|
tangents, bitangents and normals.
|
|
|
|
The destination mesh is made of a single vertex buffer and a single index buffer. Mesh parts are
|
|
identified by an offset and count in the index buffer. Each part can have its own material.
|
|
|
|
## Usage
|
|
|
|
```shell
|
|
filamesh source_mesh destination_mesh
|
|
```
|
|
|
|
## Format
|
|
|
|
Note: the UV1 attribute cannot be used in interleaved mode
|
|
|
|
> **Note**
|
|
> If you use the hex editor for macOS called [Hex Fiend](https://hexfiend.com/), you can use the
|
|
> template found in `ide/hexfiend/Templates` to inspect filamesh files.
|
|
|
|
### Header
|
|
|
|
char[8] : magic identifier "FILAMESH"
|
|
uint32 : version number
|
|
uint32 : number of parts (sub-meshes or draw calls)
|
|
float3 : center of the total bounding box (AABB)
|
|
float3 : half extent of the total bounding box (AABB)
|
|
uint32 : flags (see below)
|
|
uint32 : offset of the position attribute
|
|
uint32 : stride of the position attribute
|
|
uint32 : offset of the tangents attribute
|
|
uint32 : stride of the tangents attribute
|
|
uint32 : offset of the color attribute
|
|
uint32 : stride of the color attribute
|
|
uint32 : offset of the UV0 attribute
|
|
uint32 : stride of the UV0 attribute
|
|
uint32 : offset of the UV1 attribute (0xffffffff if UV1 is not present)
|
|
uint32 : stride of the UV1 attribute (0xffffffff if UV1 is not present)
|
|
uint32 : total number of vertices
|
|
uint32 : size in bytes occupied by the (compressed) vertices
|
|
uint32 : 0 if indices are stored as uint32, 1 if stored as uint16
|
|
uint32 : total number of indices
|
|
uint32 : size in bytes occupied by the (compressed) indices
|
|
|
|
The `flags` field contains the following bits:
|
|
|
|
- Bit 0: Specifies that vertex attributes are interleaved.
|
|
- Bit 1: UV's are 16-bit integers normalized into [-1, +1] rather than half-floats.
|
|
- Bit 2: Vertex and index data are compressed using zeux/meshoptimizer.
|
|
|
|
### Vertex data
|
|
|
|
char* : non-interleaved:
|
|
with n = number of vertices
|
|
n * half4: XYZ positions, W set to 1.0
|
|
n * short4: tangent, bitangent and normal as a quaternion (snorm unsigned short)
|
|
n * ubyte4: color
|
|
n * half2: UV texture coordinates
|
|
n * half2: UV texture coordinates (if UV1 offset and stride != 0xffffffff)
|
|
interleaved:
|
|
for each vertex:
|
|
half4: XYZ position, W set to 1.0
|
|
short4: tangent, bitangent and normal as a quaternion (snorm unsigned short)
|
|
ubyte4: color
|
|
half2: UV texture coordinates
|
|
|
|
### Index data
|
|
|
|
char* : each index is a uint32 or uint16 (see header)
|
|
|
|
### Parts
|
|
|
|
for each part:
|
|
uint32: offset of the first index in the index buffer
|
|
uint32: number of indices that compose this part
|
|
uint32: min index referenced by this part (glDrawRangeElements)
|
|
uint32: max index referenced by this part (glDrawRangeElements)
|
|
uint32: material ID (index in list of materials)
|
|
float3: center of the part's bounding box (AABB)
|
|
float3: half extent of the part's bounding box (AABB)
|
|
|
|
### Materials
|
|
|
|
uint32 : number of materials
|
|
for each material:
|
|
uint32: length in bytes of the material name's string (not counting terminating \0)
|
|
char* : name of the material (null terminated)
|
|
|
|
## Example
|
|
|
|
```c++
|
|
struct Mesh {
|
|
utils::Entity renderable;
|
|
VertexBuffer* vertexBuffer = nullptr;
|
|
IndexBuffer* indexBuffer = nullptr;
|
|
};
|
|
|
|
struct Header {
|
|
uint32_t version;
|
|
uint32_t parts;
|
|
Box aabb;
|
|
uint32_t flags;
|
|
uint32_t offsetPosition;
|
|
uint32_t stridePosition;
|
|
uint32_t offsetTangents;
|
|
uint32_t strideTangents;
|
|
uint32_t offsetColor;
|
|
uint32_t strideColor;
|
|
uint32_t offsetUV0;
|
|
uint32_t strideUV0;
|
|
uint32_t offsetUV1;
|
|
uint32_t strideUV1;
|
|
uint32_t vertexCount;
|
|
uint32_t vertexSize;
|
|
uint32_t indexType;
|
|
uint32_t indexCount;
|
|
uint32_t indexSize;
|
|
};
|
|
|
|
struct Vertex {
|
|
half4 position;
|
|
short4 tangents;
|
|
ubyte4 color;
|
|
short2 uv0; // either half-float or snorm int16
|
|
};
|
|
|
|
struct Part {
|
|
uint32_t offset;
|
|
uint32_t indexCount;
|
|
uint32_t minIndex;
|
|
uint32_t maxIndex;
|
|
uint32_t materialID;
|
|
Box aabb;
|
|
};
|
|
|
|
static size_t fileSize(int fd) {
|
|
size_t filesize;
|
|
filesize = (size_t) lseek(fd, 0, SEEK_END);
|
|
lseek(fd, 0, SEEK_SET);
|
|
return filesize;
|
|
}
|
|
|
|
Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
|
|
const std::map<std::string, filament::MaterialInstance*>& materials) {
|
|
|
|
Mesh mesh;
|
|
|
|
int fd = open(path.c_str(), O_RDONLY);
|
|
|
|
size_t size = fileSize(fd);
|
|
char* data = (char*) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
|
|
if (data) {
|
|
char *p = data;
|
|
|
|
char magic[9];
|
|
memcpy(magic, (const char*) p, sizeof(char) * 8);
|
|
magic[8] = '\0';
|
|
p += sizeof(char) * 8;
|
|
|
|
if (!strcmp("FILAMESH", magic)) {
|
|
Header* header = (Header*) p;
|
|
p += sizeof(Header);
|
|
|
|
char* vertexData = p;
|
|
p += header->vertexSize;
|
|
|
|
char* indices = p;
|
|
p += header->indexSize;
|
|
|
|
Part* parts = (Part*) p;
|
|
p += header->parts * sizeof(Part);
|
|
|
|
uint32_t materialCount = (uint32_t) *p;
|
|
p += sizeof(uint32_t);
|
|
|
|
std::vector<std::string> partsMaterial;
|
|
partsMaterial.resize(materialCount);
|
|
|
|
for (size_t i = 0; i < materialCount; i++) {
|
|
uint32_t nameLength = (uint32_t) *p;
|
|
p += sizeof(uint32_t);
|
|
|
|
partsMaterial[i] = p;
|
|
p += nameLength + 1; // null terminated
|
|
}
|
|
|
|
mesh.indexBuffer = IndexBuffer::Builder()
|
|
.indexCount(header->indexCount)
|
|
.bufferType(header->indexType ? IndexBuffer::IndexType::USHORT
|
|
: IndexBuffer::IndexType::UINT)
|
|
.build(*engine);
|
|
|
|
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)
|
|
.normalized(VertexAttribute::TANGENTS)
|
|
.normalized(VertexAttribute::COLOR)
|
|
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::HALF4,
|
|
header->offsetPosition, uint8_t(header->stridePosition))
|
|
.attribute(VertexAttribute::TANGENTS, 0, VertexBuffer::AttributeType::SHORT4,
|
|
header->offsetTangents, uint8_t(header->strideTangents))
|
|
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4,
|
|
header->offsetColor, uint8_t(header->strideColor))
|
|
.attribute(VertexAttribute::UV0, 0, uvType,
|
|
header->offsetUV0, uint8_t(header->strideUV0))
|
|
.normalized(VertexAttribute::UV0, uvNormalized);
|
|
}
|
|
|
|
if (header->offsetUV1 != std::numeric_limits<uint32_t>::max() &&
|
|
header->strideUV1 != std::numeric_limits<uint32_t>::max()) {
|
|
vbb
|
|
.attribute(VertexAttribute::UV1, 0, uvType,
|
|
header->offsetUV1, uint8_t(header->strideUV1))
|
|
.normalized(VertexAttribute::UV1, uvNormalized);
|
|
}
|
|
|
|
mesh.vertexBuffer = vbb.build(*engine);
|
|
|
|
VertexBuffer::BufferDescriptor buffer(vertexData, header->vertexSize);
|
|
mesh.vertexBuffer->setBufferAt(*engine, 0, std::move(buffer));
|
|
|
|
RenderableManager::Builder builder(header->parts);
|
|
builder.boundingBox(header->aabb);
|
|
|
|
for (size_t i = 0; i < header->parts; i++) {
|
|
builder.geometry(i, RenderableManager::PrimitiveType::TRIANGLES,
|
|
mesh.vertexBuffer, mesh.indexBuffer, parts[i].offset,
|
|
parts[i].minIndex, parts[i].maxIndex, parts[i].indexCount);
|
|
auto m = materials.find(partsMaterial[i]);
|
|
if (m != materials.end()) {
|
|
builder.material(i, m->second);
|
|
} else {
|
|
builder.material(i, materials.at("DefaultMaterial"));
|
|
}
|
|
}
|
|
|
|
mesh.renderable = utils::EntityManager::get().create();
|
|
builder.build(*engine, mesh.renderable);
|
|
}
|
|
|
|
Fence::waitAndDestroy(engine->createFence());
|
|
munmap(data, size);
|
|
}
|
|
close(fd);
|
|
|
|
return mesh;
|
|
}
|
|
```
|