VOXELFORMAT: added kv6 support
parent
6e4720dde4
commit
686caa9461
|
@ -1,4 +1,4 @@
|
|||
[Thumbnailer Entry]
|
||||
TryExec=@CMAKE_PROJECT_NAME@-@NAME@
|
||||
Exec=@CMAKE_PROJECT_NAME@-@NAME@ -s %s %i %o
|
||||
MimeType=application/x-build-engine;application/x-cubeworld;application/x-binvox;application/x-magicavoxel;application/x-qubicle-binary;application/x-qubicle-tree;application/x-sandbox-voxedit
|
||||
MimeType=application/x-slab6;application/x-build-engine;application/x-cubeworld;application/x-binvox;application/x-magicavoxel;application/x-qubicle-binary;application/x-qubicle-tree;application/x-sandbox-voxedit
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
.SH DESCRIPTION
|
||||
\fB@COMMANDLINE@\fP is a command line application that can convert several voxel
|
||||
volume formats into others. Supported formats are e.g. cub (CubeWorld), qb/qbt
|
||||
(Qubicle), vox (MagicaVoxel), vmx (VoxEdit Sandbox), kvx (Build engine), binvox
|
||||
and maybe others.
|
||||
(Qubicle), vox (MagicaVoxel), vmx (VoxEdit Sandbox), kvx (Build engine), kv6 (SLAB6),
|
||||
binvox and maybe others.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\--trace|--debug\fR
|
||||
|
|
|
@ -10,4 +10,4 @@ Type=Application
|
|||
Keywords=3d;modeling;game;voxel;engine;pixel;art
|
||||
Categories=Graphics
|
||||
StartupNotify=true
|
||||
MimeType=application/x-build-engine;application/x-cubeworld;application/x-binvox;application/x-magicavoxel;application/x-qubicle-binary;application/x-qubicle-tree;application/x-sandbox-voxedit
|
||||
MimeType=application/x-slab6;application/x-build-engine;application/x-cubeworld;application/x-binvox;application/x-magicavoxel;application/x-qubicle-binary;application/x-qubicle-tree;application/x-sandbox-voxedit
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
</mime-type>
|
||||
<mime-type type="application/x-magicavoxel">
|
||||
<comment>Voxel model MagicaVoxel</comment>
|
||||
<magic priority="50">
|
||||
<match type="string" offset="0" value="VOX "/>
|
||||
</magic>
|
||||
<glob pattern="*.vox"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-qubicle-binary">
|
||||
|
@ -18,6 +21,9 @@
|
|||
</mime-type>
|
||||
<mime-type type="application/x-qubicle-tree">
|
||||
<comment>Voxel model Qubicle tree</comment>
|
||||
<magic priority="50">
|
||||
<match type="string" offset="0" value="QB 2"/>
|
||||
</magic>
|
||||
<glob pattern="*.qbt"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-sandbox-voxedit">
|
||||
|
@ -28,4 +34,11 @@
|
|||
<comment>Voxel model used by the Build engine</comment>
|
||||
<glob pattern="*.kvx"/>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-slab6">
|
||||
<comment>Voxel model used by the SLAB6 editor</comment>
|
||||
<magic priority="50">
|
||||
<match type="string" offset="0" value="Kvxl"/>
|
||||
</magic>
|
||||
<glob pattern="*.kv6"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
|
Binary file not shown.
|
@ -2,6 +2,7 @@ set(LIB voxelformat)
|
|||
set(SRCS
|
||||
BinVoxFormat.h BinVoxFormat.cpp
|
||||
KVXFormat.h KVXFormat.cpp
|
||||
KV6Format.h KV6Format.cpp
|
||||
VoxFileFormat.h VoxFileFormat.cpp
|
||||
VoxFormat.h VoxFormat.cpp
|
||||
QBTFormat.h QBTFormat.cpp
|
||||
|
@ -23,6 +24,7 @@ set(TEST_SRCS
|
|||
tests/QBFormatTest.cpp
|
||||
tests/CubFormatTest.cpp
|
||||
tests/KVXFormatTest.cpp
|
||||
tests/KV6FormatTest.cpp
|
||||
tests/VXMFormatTest.cpp
|
||||
)
|
||||
set(TEST_FILES
|
||||
|
@ -30,6 +32,7 @@ set(TEST_FILES
|
|||
tests/qubicle.qbt
|
||||
tests/test.binvox
|
||||
tests/test.kvx
|
||||
tests/test.kv6
|
||||
tests/magicavoxel.vox
|
||||
tests/test.vxm
|
||||
tests/cw.cub
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#include "KV6Format.h"
|
||||
#include "voxel/MaterialColor.h"
|
||||
#include "core/io/FileStream.h"
|
||||
#include "core/StringUtil.h"
|
||||
#include "core/Log.h"
|
||||
#include "core/Color.h"
|
||||
#include "core/FourCC.h"
|
||||
#include <glm/common.hpp>
|
||||
|
||||
namespace voxel {
|
||||
|
||||
#define wrap(read) \
|
||||
if (read != 0) { \
|
||||
Log::error("Could not load kv6 file: Not enough data in stream " CORE_STRINGIFY(read) " - still %i bytes left", (int)stream.remaining()); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool KV6Format::loadGroups(const io::FilePtr& file, VoxelVolumes& volumes) {
|
||||
if (!(bool)file || !file->exists()) {
|
||||
Log::error("Could not load kv6 file: File doesn't exist");
|
||||
return false;
|
||||
}
|
||||
io::FileStream stream(file.get());
|
||||
|
||||
uint32_t magic;
|
||||
wrap(stream.readInt(magic))
|
||||
if (magic != FourCC('K','v','x','l')) {
|
||||
Log::error("Invalid magic");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dimensions of voxel. (our depth is kvx height)
|
||||
uint32_t xsiz, ysiz, zsiz;
|
||||
wrap(stream.readInt(xsiz))
|
||||
wrap(stream.readInt(ysiz))
|
||||
wrap(stream.readInt(zsiz))
|
||||
|
||||
if (xsiz > 256 || ysiz > 256 || zsiz > 255) {
|
||||
Log::error("Dimensions exceeded: w: %i, h: %i, d: %i", xsiz, zsiz, ysiz);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centroid of voxel. For extra precision, this location has been shifted up by 8 bits.
|
||||
*/
|
||||
glm::vec3 pivot;
|
||||
wrap(stream.readFloat(pivot.x))
|
||||
wrap(stream.readFloat(pivot.y))
|
||||
wrap(stream.readFloat(pivot.z))
|
||||
|
||||
if (xsiz > MaxRegionSize || ysiz > MaxRegionSize || zsiz > MaxRegionSize) {
|
||||
Log::error("Volume exceeds the max allowed size: %i:%i:%i", xsiz, zsiz, ysiz);
|
||||
return false;
|
||||
}
|
||||
const voxel::Region region(0, 0, 0, xsiz - 1, zsiz - 1, ysiz - 1);
|
||||
if (!region.isValid()) {
|
||||
Log::error("Invalid region: %i:%i:%i", xsiz, zsiz, ysiz);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t numvoxs;
|
||||
wrap(stream.readInt(numvoxs))
|
||||
Log::debug("numvoxs: %u", numvoxs);
|
||||
constexpr uint32_t MAXVOXS = 1048576;
|
||||
if (numvoxs > MAXVOXS) {
|
||||
Log::error("Max allowed voxels exceeded: %u (max is %u)", numvoxs, MAXVOXS);
|
||||
return false;
|
||||
}
|
||||
|
||||
core_assert(stream.pos() == 32);
|
||||
if (stream.seek(32 + numvoxs * 8 + (xsiz << 2) + ((xsiz * ysiz) << 1)) != -1) {
|
||||
if (stream.remaining() != 0) {
|
||||
uint32_t palMagic;
|
||||
wrap(stream.readInt(palMagic))
|
||||
if (palMagic == FourCC('S','P','a','l')) {
|
||||
_paletteSize = 256;
|
||||
_palette.resize(_paletteSize);
|
||||
const MaterialColorArray& materialColors = getMaterialColors();
|
||||
for (size_t i = 0; i < _paletteSize; ++i) {
|
||||
uint8_t r, g, b;
|
||||
wrap(stream.readByte(b))
|
||||
wrap(stream.readByte(g))
|
||||
wrap(stream.readByte(r))
|
||||
|
||||
const uint8_t nr = glm::clamp((uint32_t)glm::round((r * 255) / 63.0f), 0u, 255u);
|
||||
const uint8_t ng = glm::clamp((uint32_t)glm::round((g * 255) / 63.0f), 0u, 255u);
|
||||
const uint8_t nb = glm::clamp((uint32_t)glm::round((b * 255) / 63.0f), 0u, 255u);
|
||||
|
||||
const glm::vec4& color = core::Color::fromRGBA(nr, ng, nb, 255);
|
||||
const int index = core::Color::getClosestMatch(color, materialColors);
|
||||
_palette[i] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.seek(32);
|
||||
|
||||
RawVolume *volume = new RawVolume(region);
|
||||
volumes.push_back(VoxelVolume{volume, file->fileName(), true});
|
||||
|
||||
typedef struct {
|
||||
uint8_t z, col, vis, dir;
|
||||
} voxtype;
|
||||
|
||||
voxtype voxdata[MAXVOXS];
|
||||
for (uint32_t c = 0u; c < numvoxs; ++c) {
|
||||
uint8_t palr, palg, palb, pala;
|
||||
wrap(stream.readByte(palb))
|
||||
wrap(stream.readByte(palg))
|
||||
wrap(stream.readByte(palr))
|
||||
wrap(stream.readByte(pala))
|
||||
const glm::vec4& color = core::Color::fromRGBA(palr, palg, palb, pala);
|
||||
voxdata[c].col = findClosestIndex(color);
|
||||
uint16_t zpos;
|
||||
wrap(stream.readShort(zpos))
|
||||
voxdata[c].z = zpos;
|
||||
wrap(stream.readByte(voxdata[c].vis))
|
||||
wrap(stream.readByte(voxdata[c].dir))
|
||||
}
|
||||
stream.skip(xsiz * sizeof(uint32_t));
|
||||
|
||||
uint16_t xyoffset[256][256];
|
||||
for (uint32_t x = 0u; x < xsiz; ++x) {
|
||||
for (uint32_t y = 0u; y < ysiz; ++y) {
|
||||
wrap(stream.readShort(xyoffset[x][y]))
|
||||
}
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for (uint32_t x = 0; x < xsiz; ++x) {
|
||||
for (uint32_t y = 0; y < ysiz; ++y) {
|
||||
for (int end = idx + xyoffset[x][y]; idx < end; ++idx) {
|
||||
const voxtype& vox = voxdata[idx];
|
||||
const voxel::Voxel col = voxel::createVoxel(voxel::VoxelType::Generic, vox.col);
|
||||
volume->setVoxel(x, (zsiz - 1) - vox.z, y, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t lastZ = 256;
|
||||
voxel::Voxel lastCol;
|
||||
idx = 0;
|
||||
for (uint32_t x = 0; x < xsiz; ++x) {
|
||||
for (uint32_t y = 0; y < ysiz; ++y) {
|
||||
for (int end = idx + xyoffset[x][y]; idx < end; ++idx) {
|
||||
const voxtype& vox = voxdata[idx];
|
||||
if (vox.vis & (1 << 4)) {
|
||||
lastZ = vox.z;
|
||||
lastCol = voxel::createVoxel(voxel::VoxelType::Generic, vox.col);
|
||||
}
|
||||
if (vox.vis & (1 << 5)) {
|
||||
for (; lastZ < vox.z; ++lastZ) {
|
||||
volume->setVoxel(x, (zsiz - 1) - lastZ, y, lastCol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef wrap
|
||||
|
||||
bool KV6Format::saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VoxFileFormat.h"
|
||||
#include "core/io/File.h"
|
||||
#include "core/String.h"
|
||||
|
||||
namespace voxel {
|
||||
/**
|
||||
* @brief Voxel sprite format used by the Build engine
|
||||
*/
|
||||
class KV6Format : public VoxFileFormat {
|
||||
public:
|
||||
bool loadGroups(const io::FilePtr& file, VoxelVolumes& volumes) override;
|
||||
bool saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "KVXFormat.h"
|
||||
#include "voxel/MaterialColor.h"
|
||||
#include "core/io/FileStream.h"
|
||||
#include "core/StringUtil.h"
|
||||
#include "core/Log.h"
|
||||
#include "core/Color.h"
|
||||
|
@ -87,7 +88,6 @@ bool KVXFormat::loadGroups(const io::FilePtr& file, VoxelVolumes& volumes) {
|
|||
_paletteSize = 256;
|
||||
stream.seek(stream.size() - 3 * _paletteSize);
|
||||
_palette.resize(_paletteSize);
|
||||
const MaterialColorArray& materialColors = getMaterialColors();
|
||||
|
||||
/**
|
||||
* The last 768 bytes of the KVX file is a standard 256-color VGA palette.
|
||||
|
@ -105,8 +105,7 @@ bool KVXFormat::loadGroups(const io::FilePtr& file, VoxelVolumes& volumes) {
|
|||
const uint8_t nb = glm::clamp((uint32_t)glm::round((b * 255) / 63.0f), 0u, 255u);
|
||||
|
||||
const glm::vec4& color = core::Color::fromRGBA(nr, ng, nb, 255);
|
||||
const int index = core::Color::getClosestMatch(color, materialColors);
|
||||
_palette[i] = index;
|
||||
_palette[i] = findClosestIndex(color);
|
||||
}
|
||||
stream.seek(currentPos);
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "VoxFileFormat.h"
|
||||
#include "core/io/FileStream.h"
|
||||
#include "core/io/File.h"
|
||||
#include "core/String.h"
|
||||
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
#include "CubFormat.h"
|
||||
#include "BinVoxFormat.h"
|
||||
#include "KVXFormat.h"
|
||||
#include "KV6Format.h"
|
||||
|
||||
namespace voxelformat {
|
||||
|
||||
const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox,cub,kvx";
|
||||
const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox,cub,kvx,kv6";
|
||||
const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb,binvox,cub";
|
||||
|
||||
bool loadVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& newVolumes) {
|
||||
|
@ -43,6 +44,11 @@ bool loadVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& newVolume
|
|||
if (!f.loadGroups(filePtr, newVolumes)) {
|
||||
voxelformat::clearVolumes(newVolumes);
|
||||
}
|
||||
} else if (ext == "kv6") {
|
||||
voxel::KV6Format f;
|
||||
if (!f.loadGroups(filePtr, newVolumes)) {
|
||||
voxelformat::clearVolumes(newVolumes);
|
||||
}
|
||||
} else if (ext == "cub") {
|
||||
voxel::CubFormat f;
|
||||
if (!f.loadGroups(filePtr, newVolumes)) {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @file
|
||||
*/
|
||||
|
||||
#include "AbstractVoxFormatTest.h"
|
||||
#include "voxelformat/KV6Format.h"
|
||||
|
||||
namespace voxel {
|
||||
|
||||
class KV6FormatTest: public AbstractVoxFormatTest {
|
||||
};
|
||||
|
||||
TEST_F(KV6FormatTest, testLoad) {
|
||||
KV6Format f;
|
||||
std::unique_ptr<RawVolume> volume(load("test.kv6", f));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue