VOXELFORMAT: added cube world load and save support
the cub file format is quite easy. The first three uint32_t values are the width, depth and height (little endian) values of the following volume data. The volume data itself is stored in 3 bytes for RGBmaster
parent
aeb4ed22de
commit
9bba5c91ac
|
@ -7,5 +7,6 @@
|
|||
<glob pattern="*.qbt"/>
|
||||
<glob pattern="*.vxm"/>
|
||||
<glob pattern="*.binvox"/>
|
||||
<glob pattern="*.cub"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
|
Binary file not shown.
|
@ -204,7 +204,7 @@ unsigned int Color::getRGBA(const glm::vec4& color) {
|
|||
}
|
||||
|
||||
glm::u8vec4 Color::getRGBAVec(const glm::vec4& color) {
|
||||
return glm::u8vec4(static_cast<int>(color.a * magnitude), static_cast<int>(color.b * magnitude), static_cast<int>(color.g * magnitude), static_cast<int>(color.r * magnitude));
|
||||
return glm::u8vec4(static_cast<int>(color.r * magnitude), static_cast<int>(color.g * magnitude), static_cast<int>(color.b * magnitude), static_cast<int>(color.a * magnitude));
|
||||
}
|
||||
|
||||
unsigned int Color::getBGRA(const glm::vec4& color) {
|
||||
|
|
|
@ -8,6 +8,7 @@ set(SRCS
|
|||
VXMFormat.h VXMFormat.cpp
|
||||
MeshCache.h MeshCache.cpp
|
||||
MeshExporter.h MeshExporter.cpp
|
||||
CubFormat.h CubFormat.cpp
|
||||
Loader.h Loader.cpp
|
||||
)
|
||||
engine_add_module(TARGET ${LIB} SRCS ${SRCS} DEPENDENCIES voxel assimp)
|
||||
|
@ -18,6 +19,7 @@ set(TEST_SRCS
|
|||
tests/VoxFormatTest.cpp
|
||||
tests/QBTFormatTest.cpp
|
||||
tests/QBFormatTest.cpp
|
||||
tests/CubFormatTest.cpp
|
||||
tests/VXMFormatTest.cpp
|
||||
tests/MeshExporterTest.cpp
|
||||
)
|
||||
|
@ -27,6 +29,7 @@ set(TEST_FILES
|
|||
tests/test.binvox
|
||||
tests/magicavoxel.vox
|
||||
tests/test.vxm
|
||||
tests/cw.cub
|
||||
)
|
||||
|
||||
gtest_suite_files(tests ${TEST_FILES})
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#include "CubFormat.h"
|
||||
#include "voxel/MaterialColor.h"
|
||||
#include "core/String.h"
|
||||
#include "core/Log.h"
|
||||
#include "core/Color.h"
|
||||
|
||||
namespace voxel {
|
||||
|
||||
#define wrap(read) \
|
||||
if (read != 0) { \
|
||||
Log::error("Could not load cub file: Not enough data in stream " CORE_STRINGIFY(read) " - still %i bytes left", (int)stream.remaining()); \
|
||||
return VoxelVolumes(); \
|
||||
}
|
||||
|
||||
VoxelVolumes CubFormat::loadGroups(const io::FilePtr& file) {
|
||||
if (!(bool)file || !file->exists()) {
|
||||
Log::error("Could not load cub file: File doesn't exist");
|
||||
return VoxelVolumes();
|
||||
}
|
||||
io::FileStream stream(file.get());
|
||||
|
||||
uint32_t width, depth, height;
|
||||
wrap(stream.readInt(width))
|
||||
wrap(stream.readInt(depth))
|
||||
wrap(stream.readInt(height))
|
||||
|
||||
VoxelVolumes volumes;
|
||||
|
||||
RawVolume *volume = new RawVolume(voxel::Region(0, 0, 0, width - 1, height - 1, depth - 1));
|
||||
volumes.push_back(VoxelVolume{volume, file->fileName(), true});
|
||||
|
||||
// TODO: support loading own palette
|
||||
|
||||
const MaterialColorArray& materialColors = getMaterialColors();
|
||||
|
||||
for (uint32_t h = 0u; h < height; ++h) {
|
||||
for (uint32_t d = 0u; d < depth; ++d) {
|
||||
for (uint32_t w = 0u; w < width; ++w) {
|
||||
uint8_t r, g, b;
|
||||
wrap(stream.readByte(r))
|
||||
wrap(stream.readByte(g))
|
||||
wrap(stream.readByte(b))
|
||||
if (r == 0u && g == 0u && b == 0u) {
|
||||
// empty voxel
|
||||
continue;
|
||||
}
|
||||
const glm::vec4& color = core::Color::fromRGBA(r, g, b, 255);
|
||||
int index = core::Color::getClosestMatch(color, materialColors);
|
||||
voxel::VoxelType voxelType = voxel::VoxelType::Generic;
|
||||
if (index == 0) {
|
||||
voxelType = voxel::VoxelType::Air;
|
||||
}
|
||||
const voxel::Voxel& voxel = voxel::createVoxel(voxelType, index);
|
||||
// we have to flip depth with height for our own coordinate system
|
||||
volume->setVoxel(w, h, d, voxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volumes;
|
||||
}
|
||||
|
||||
#undef wrap
|
||||
|
||||
bool CubFormat::saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) {
|
||||
io::FileStream stream(file.get());
|
||||
|
||||
RawVolume* mergedVolume = merge(volumes);
|
||||
|
||||
const voxel::Region& region = mergedVolume->region();
|
||||
RawVolume::Sampler sampler(mergedVolume);
|
||||
const glm::ivec3& lower = region.getLowerCorner();
|
||||
|
||||
const MaterialColorArray& materialColors = getMaterialColors();
|
||||
|
||||
const uint32_t width = region.getWidthInVoxels();
|
||||
const uint32_t height = region.getHeightInVoxels();
|
||||
const uint32_t depth = region.getDepthInVoxels();
|
||||
|
||||
// we have to flip depth with height for our own coordinate system
|
||||
stream.addInt(width);
|
||||
stream.addInt(depth);
|
||||
stream.addInt(height);
|
||||
|
||||
for (uint32_t y = 0u; y < height; ++y) {
|
||||
for (uint32_t z = 0u; z < depth; ++z) {
|
||||
for (uint32_t x = 0u; x < width; ++x) {
|
||||
sampler.setPosition(lower.x + x, lower.y + y, lower.z + z);
|
||||
const voxel::Voxel& voxel = sampler.voxel();
|
||||
if (voxel.getMaterial() == VoxelType::Air) {
|
||||
stream.addByte(0);
|
||||
stream.addByte(0);
|
||||
stream.addByte(0);
|
||||
continue;
|
||||
}
|
||||
const glm::vec4& color = materialColors[voxel.getColor()];
|
||||
const glm::u8vec4& rgba = core::Color::getRGBAVec(color);
|
||||
stream.addByte(rgba.r);
|
||||
stream.addByte(rgba.g);
|
||||
stream.addByte(rgba.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete mergedVolume;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VoxFileFormat.h"
|
||||
#include "io/FileStream.h"
|
||||
#include "io/File.h"
|
||||
#include <string>
|
||||
|
||||
namespace voxel {
|
||||
|
||||
/**
|
||||
* @brief CubeWorld cub format
|
||||
*
|
||||
* The first 12 bytes of the file are the width, depth and height of the volume (uint32_t little endian).
|
||||
* The remaining parts are the RGB values (3 bytes)
|
||||
*/
|
||||
class CubFormat : public VoxFileFormat {
|
||||
public:
|
||||
VoxelVolumes loadGroups(const io::FilePtr& file) override;
|
||||
bool saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include "QBTFormat.h"
|
||||
#include "QBFormat.h"
|
||||
#include "VXMFormat.h"
|
||||
#include "CubFormat.h"
|
||||
#include "BinVoxFormat.h"
|
||||
|
||||
namespace voxelformat {
|
||||
|
@ -27,6 +28,9 @@ bool loadVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& newVolume
|
|||
} else if (ext == "qb") {
|
||||
voxel::QBFormat f;
|
||||
newVolumes = f.loadGroups(filePtr);
|
||||
} else if (ext == "cub") {
|
||||
voxel::CubFormat f;
|
||||
newVolumes = f.loadGroups(filePtr);
|
||||
} else if (ext == "vxm") {
|
||||
voxel::VXMFormat f;
|
||||
newVolumes = f.loadGroups(filePtr);
|
||||
|
|
|
@ -40,6 +40,9 @@ RawVolume* VoxFileFormat::merge(const VoxelVolumes& volumes) const {
|
|||
if (volumes.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (volumes.size() == 1) {
|
||||
return new RawVolume(volumes[0].volume);
|
||||
}
|
||||
std::vector<const RawVolume*> rawVolumes;
|
||||
rawVolumes.reserve(volumes.size());
|
||||
for (const auto& v : volumes) {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#include "AbstractVoxFormatTest.h"
|
||||
#include "voxelformat/CubFormat.h"
|
||||
|
||||
namespace voxel {
|
||||
|
||||
class CubFormatTest: public AbstractVoxFormatTest {
|
||||
};
|
||||
|
||||
TEST_F(CubFormatTest, testLoad) {
|
||||
CubFormat f;
|
||||
std::unique_ptr<RawVolume> volume(load("cw.cub", f));
|
||||
}
|
||||
|
||||
}
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
namespace voxedit {
|
||||
|
||||
static const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox";
|
||||
static const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb";
|
||||
static const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox,cub";
|
||||
static const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb,cub";
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "voxelformat/Loader.h"
|
||||
#include "voxelformat/VoxFormat.h"
|
||||
#include "voxelformat/QBTFormat.h"
|
||||
#include "voxelformat/CubFormat.h"
|
||||
#include "voxelformat/QBFormat.h"
|
||||
#include "voxelformat/VXMFormat.h"
|
||||
#include "video/ScopedPolygonMode.h"
|
||||
|
@ -327,6 +328,9 @@ bool SceneManager::save(const std::string& file, bool autosave) {
|
|||
} else if (ext == "qb") {
|
||||
voxel::QBFormat f;
|
||||
saved = f.saveGroups(volumes, filePtr);
|
||||
} else if (ext == "cub") {
|
||||
voxel::CubFormat f;
|
||||
saved = f.saveGroups(volumes, filePtr);
|
||||
} else {
|
||||
Log::warn("Failed to save file with unknown type: %s - saving as vox instead", ext.c_str());
|
||||
voxel::VoxFormat f;
|
||||
|
|
Loading…
Reference in New Issue