Instances are now serialized with little-endian

This commit is contained in:
Marc Gilleron 2022-02-26 23:05:55 +00:00
parent d9c2d227ac
commit a75de1eee2
3 changed files with 86 additions and 7 deletions

View File

@ -1,6 +1,9 @@
Instance block format
=======================
!!! warn
This document is about an old version of the format. You may check the most recent version.
This page describes the binary format used by the module to save instances to files or databases.
Specification

View File

@ -0,0 +1,68 @@
Instance block format
=======================
This page describes the binary format used by the module to save instances to files or databases.
Changes from version 0
-------------------------
- Data is now little-endian instead of big-endian.
- No other changes were made, so version 0 is easily convertible.
Specification
---------------
### Compressed container
A block is usually serialized as compressed data.
See [Compressed container format](#compressed-container) for specification.
### Binary data
This data is little-endian.
In pseudo-code:
```cpp
// Root structure
struct InstanceBlockData {
// Version tag in case more stuff is added in the future
uint8_t version = 1;
// There can be up to 256 different layers in one block
uint8_t layer_count;
// To compress positions we need to know their range.
// It's local to the block so we know it starts from zero.
float position_range;
LayerData layers[layer_count];
// Magic number to signal the end of the data block
uint32_t control_end = 0x900df00d;
};
struct LayerData {
uint16_t id; // Identifies the type of instances (rocks, grass, pebbles, bushes etc)
uint16_t count;
// To be able to compress scale we must know its range
float scale_min;
float scale_max;
// This tells which format instances of this layer use. For now I always use the same format,
// But maybe some types of instances will need more, or less data?
uint8_t format = 0;
InstanceData data[count];
};
struct InstanceData {
// Position is lossy-compressed based on the size of the block
uint16_t x;
uint16_t y;
uint16_t z;
// Scale is uniform and is lossy-compressed to 256 values
uint8_t scale;
// Rotation is a compressed quaternion
uint8_t x;
uint8_t y;
uint8_t z;
uint8_t w;
};
```

View File

@ -8,7 +8,12 @@ namespace zylann::voxel {
namespace {
const uint32_t TRAILING_MAGIC = 0x900df00d;
}
enum FormatVersion {
INSTANCE_BLOCK_FORMAT_VERSION_0 = 0,
// Now using little-endian.
INSTANCE_BLOCK_FORMAT_VERSION_1 = 1
};
} //namespace
const float InstanceBlockData::POSITION_RANGE_MINIMUM = 0.01f;
@ -50,18 +55,17 @@ struct CompressedQuaternion4b {
};
bool serialize_instance_block_data(const InstanceBlockData &src, std::vector<uint8_t> &dst) {
const uint8_t version = 0;
const uint8_t instance_format = InstanceBlockData::FORMAT_SIMPLE_11B_V1;
// TODO Apparently big-endian is dead
// I chose it originally to match "network byte order",
// but as I read comments about it there seem to be no reason to continue using it. Needs a version increment.
zylann::MemoryWriter w(dst, zylann::ENDIANESS_BIG_ENDIAN);
zylann::MemoryWriter w(dst, zylann::ENDIANESS_LITTLE_ENDIAN);
ERR_FAIL_COND_V(src.position_range < 0.f, false);
const float position_range = math::max(src.position_range, InstanceBlockData::POSITION_RANGE_MINIMUM);
w.store_8(version);
w.store_8(INSTANCE_BLOCK_FORMAT_VERSION_1);
w.store_8(src.layers.size());
w.store_float(position_range);
@ -114,13 +118,17 @@ bool serialize_instance_block_data(const InstanceBlockData &src, std::vector<uin
}
bool deserialize_instance_block_data(InstanceBlockData &dst, Span<const uint8_t> src) {
const uint8_t expected_version = 0;
const uint8_t expected_version = INSTANCE_BLOCK_FORMAT_VERSION_1;
const uint8_t expected_instance_format = InstanceBlockData::FORMAT_SIMPLE_11B_V1;
zylann::MemoryReader r(src, zylann::ENDIANESS_BIG_ENDIAN);
zylann::MemoryReader r(src, zylann::ENDIANESS_LITTLE_ENDIAN);
const uint8_t version = r.get_8();
ERR_FAIL_COND_V(version != expected_version, false);
if (version == INSTANCE_BLOCK_FORMAT_VERSION_0) {
r.endianess = zylann::ENDIANESS_BIG_ENDIAN;
} else {
ERR_FAIL_COND_V(version != expected_version, false);
}
const uint8_t layers_count = r.get_8();
dst.layers.resize(layers_count);