VOXELFORMAT: started to add vxl write support

master
Martin Gerhardy 2020-06-14 22:47:19 +02:00
parent 4c5f587da9
commit efb2e9def5
6 changed files with 148 additions and 4 deletions

View File

@ -12,7 +12,7 @@ A more detailed changelog can be found at: https://github.com/mgerhardy/engine/c
- Improved scene graph support for Magicavoxel vox files
- Fixed invisible voxels for qb and qbt (Qubicle) volume format
- Support automatic loading different volume formats for assets
- Support loading Command&Conquer vxl files
- Support Command&Conquer vxl files
#### 0.0.4 (2020-06-07)

2
debian/changelog vendored
View File

@ -10,7 +10,7 @@ vengi (0.0.5.0-1) UNRELEASED; urgency=low
* Improved scene graph support for Magicavoxel vox files
* Fixed invisible voxels for qb and qbt (Qubicle) volume format
* Support automatic loading different volume formats for assets
* Support loading Command&Conquer vxl files
* Support Command&Conquer vxl files
-- Martin Gerhardy <martin.gerhardy@gmail.com> Sun, 7 Jun 2020 10:32:10 +0200

View File

@ -18,7 +18,7 @@ namespace voxelformat {
const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox,cub,kvx,kv6,vxl";
const char *SUPPORTED_VOXEL_FORMATS_LOAD_LIST[] = { "qb", "vox", nullptr };
const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb,binvox,cub";
const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb,binvox,cub,vxl";
bool loadVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& newVolumes) {
if (!filePtr->exists()) {
@ -103,6 +103,9 @@ bool saveVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& volumes)
} else if (ext == "cub") {
voxel::CubFormat f;
return f.saveGroups(volumes, filePtr);
} else if (ext == "vxl") {
voxel::VXLFormat f;
return f.saveGroups(volumes, filePtr);
} else if (ext == "binvox") {
voxel::BinVoxFormat f;
return f.saveGroups(volumes, filePtr);

View File

@ -26,10 +26,122 @@ namespace voxel {
return false; \
}
bool VXLFormat::saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) {
bool VXLFormat::writeLimb(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const {
const VoxelVolume& v = volumes[limbIdx];
const voxel::Region& region = v.volume->region();
const glm::ivec3& size = region.getDimensionsInVoxels();
const uint32_t baseSize = size.x * size.z;
for (uint32_t i = 0; i < baseSize; i++) {
wrapBool(stream.addInt(0)) // TODO: spanStart[i] and EmptyColumn handling
}
for (uint32_t i = 0; i < baseSize; i++) {
wrapBool(stream.addInt(0)) // TODO: spanEnd[i] and EmptyColumn handling
}
for (uint32_t i = 0u; i < baseSize; ++i) {
//if (spanStart[i] == EmptyColumn) {
// continue;
//}
// TODO: there might be multiple of this for regions that exceed the byte boundary
uint32_t firstNonEmptyY = 0;
uint32_t voxelAmountY = 0u;
const uint8_t x = (uint8_t)(i % size.x);
const uint8_t z = (uint8_t)(i / size.x);
wrapBool(stream.addByte(firstNonEmptyY))
wrapBool(stream.addByte(voxelAmountY))
for (uint8_t y = 0; y < voxelAmountY; ++y) {
const voxel::Voxel& voxel = v.volume->voxel(x, y, z);
wrapBool(stream.addByte(voxel.getColor()))
wrapBool(stream.addByte(0)) // TODO: normal
}
}
// TODO: once finished, activate it
return false;
}
bool VXLFormat::writeLimbHeader(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const {
const VoxelVolume& v = volumes[limbIdx];
wrapBool(stream.append((const uint8_t*)v.name.c_str(), 15))
wrapBool(stream.addByte('\0'))
wrapBool(stream.addInt(limbIdx))
wrapBool(stream.addInt(1))
wrapBool(stream.addInt(0))
return true;
}
bool VXLFormat::writeLimbFooter(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const {
const VoxelVolume& v = volumes[limbIdx];
const uint32_t spanStartOff = stream.pos();
wrapBool(stream.addInt(spanStartOff))
wrapBool(stream.addInt(0)) // TODO: spanEndOff
wrapBool(stream.addInt(0)) // TODO: spanDataOff
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
wrapBool(stream.addFloat(0.0f)) // TODO: region.getLowerCorner
}
}
for (int i = 0; i < 3; ++i) {
wrapBool(stream.addFloat(1.0f))
}
const voxel::Region& region = v.volume->region();
const glm::ivec3& size = region.getDimensionsInVoxels();
wrapBool(stream.addByte(size.x))
wrapBool(stream.addByte(size.z))
wrapBool(stream.addByte(size.y))
wrapBool(stream.addByte(2))
return true;
}
bool VXLFormat::writeHeader(io::FileStream& stream, const VoxelVolumes& volumes) {
wrapBool(stream.addString("Voxel Animation"))
wrapBool(stream.addInt(1))
wrapBool(stream.addInt(volumes.size()))
wrapBool(stream.addInt(volumes.size()))
wrapBool(stream.addInt(0)) // bodysize is filled later
wrapBool(stream.addShort(0x1f10U))
const MaterialColorArray& materialColors = getMaterialColors();
const uint32_t paletteSize = materialColors.size();
for (uint32_t i = 0; i < paletteSize; ++i) {
const glm::u8vec4& rgba = core::Color::getRGBAVec(materialColors[i]);
wrapBool(stream.addByte(rgba[0]))
wrapBool(stream.addByte(rgba[1]))
wrapBool(stream.addByte(rgba[2]))
}
core_assert(stream.pos() == HeaderSize);
return true;
}
bool VXLFormat::saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) {
if (!(bool)file) {
Log::error("Could not save vxl file: No file given");
return false;
}
io::FileStream stream(file.get());
wrap(writeHeader(stream, volumes))
for (uint32_t i = 0; i < volumes.size(); ++i) {
wrapBool(writeLimbHeader(stream, volumes, i))
}
const uint64_t afterHeaderPos = stream.pos();
for (uint32_t i = 0; i < volumes.size(); ++i) {
wrapBool(writeLimb(stream, volumes, i))
}
const uint64_t afterBodyPos = stream.pos();
const uint64_t bodySize = afterBodyPos - afterHeaderPos;
wrap(stream.seek(HeaderBodySizeOffset));
wrapBool(stream.addInt(bodySize))
wrap(stream.seek(afterBodyPos));
for (uint32_t i = 0; i < volumes.size(); ++i) {
wrapBool(writeLimbFooter(stream, volumes, i))
}
return true;
}
bool VXLFormat::readLimb(io::FileStream& stream, vxl_mdl& mdl, uint32_t limbIdx, VoxelVolumes& volumes) const {
const vxl_limb_tailer &footer = mdl.limb_tailers[limbIdx];
const vxl_limb_header &header = mdl.limb_headers[limbIdx];

View File

@ -63,8 +63,18 @@ private:
static constexpr size_t HeaderSize = 802;
// 28 is the unpadded size of vxl_limb_header
static constexpr size_t LimbHeaderSize = 28;
// 92 is the unpadded size of vxl_limb_tailer
static constexpr size_t LimbTailerSize = 92;
static constexpr size_t HeaderBodySizeOffset = 28;
static constexpr int EmptyColumn = -1;
// writing
bool writeLimb(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const;
bool writeLimbHeader(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const;
bool writeLimbFooter(io::FileStream& stream, const VoxelVolumes& volumes, uint32_t limbIdx) const;
bool writeHeader(io::FileStream& stream, const VoxelVolumes& volumes);
// reading
bool readLimbHeader(io::FileStream& stream, vxl_mdl& mdl, uint32_t limbIdx) const;
bool readLimbFooter(io::FileStream& stream, vxl_mdl& mdl, uint32_t limbIdx) const;
bool readLimb(io::FileStream& stream, vxl_mdl& mdl, uint32_t limbIdx, VoxelVolumes& volumes) const;

View File

@ -19,4 +19,23 @@ TEST_F(VXLFormatTest, testLoad) {
delete volume;
}
TEST_F(VXLFormatTest, DISABLED_testSave) {
VXLFormat f;
const io::FilePtr& file = _testApp->filesystem()->open("cc.vxl");
ASSERT_TRUE((bool)file) << "Could not open vxl file";
RawVolume* loadedVolume = f.load(file);
ASSERT_NE(nullptr, loadedVolume) << "Could not load vxl file";
const io::FilePtr& fileSave = _testApp->filesystem()->open("cc-save.vxl", io::FileMode::Write);
EXPECT_TRUE(f.save(loadedVolume, fileSave));
const io::FilePtr& fileLoadAfterSave = _testApp->filesystem()->open("cc-save.vxl");
RawVolume *savedVolume = f.load(fileLoadAfterSave);
EXPECT_NE(nullptr, savedVolume) << "Could not load saved vxl file";
if (savedVolume) {
EXPECT_EQ(*savedVolume, *loadedVolume);
delete savedVolume;
}
delete loadedVolume;
}
}