VOXELFORMAT: added binvox support

master
Martin Gerhardy 2019-08-29 09:56:30 +02:00
parent e89adc0a13
commit 839f84ee37
9 changed files with 209 additions and 3 deletions

View File

@ -6,5 +6,6 @@
<glob pattern="*.qb"/>
<glob pattern="*.qbt"/>
<glob pattern="*.vxm"/>
<glob pattern="*.binvox"/>
</mime-type>
</mime-info>

BIN
data/tests/test.binvox Normal file

Binary file not shown.

View File

@ -0,0 +1,140 @@
/**
* @file
*/
#include "BinVoxFormat.h"
#include "voxel/MaterialColor.h"
#include "core/String.h"
#include <SDL.h>
#include <string.h>
#include <memory>
namespace voxel {
bool BinVoxFormat::readHeader(const std::string& header) {
const char *delimiters = "\n";
// Skip delimiters at beginning.
std::string::size_type lastPos = header.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
std::string::size_type pos = header.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
// Found a token, add it to the vector.
const std::string_view line = header.substr(lastPos, pos - lastPos);
if (line == "data") {
return true;
}
if (line.size() > 127) {
Log::error("Line width exceeded max allowed value");
return false;
}
char buf[128];
strncpy(buf, line.data(), line.size());
buf[line.size()] = '\0';
if (core::string::startsWith(line, "#binvox")) {
SDL_sscanf(buf, "#binvox %u", &_version);
} else if (core::string::startsWith(buf, "dim ")) {
SDL_sscanf(buf, "dim %u %u %u", &_w, &_h, &_d);
} else if (core::string::startsWith(buf, "translate ")) {
SDL_sscanf(buf, "translate %f %f %f", &_tx, &_ty, &_tz);
} else if (core::string::startsWith(buf, "scale ")) {
SDL_sscanf(buf, "scale %f", &_scale);
}
// Skip delimiters. Note the "not_of"
lastPos = header.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = header.find_first_of(delimiters, lastPos);
}
if (_version != 1) {
Log::error("Failed to parse the header data");
return false;
}
_size = _w * _h * _d;
return true;
}
bool BinVoxFormat::readData(const io::FilePtr& file, const size_t offset, VoxelVolumes& volumes) {
uint8_t *buf;
const int fileSize = file->read((void**) &buf);
std::unique_ptr<uint8_t[]> pFile(buf);
RawVolume *volume = new RawVolume(voxel::Region(0, 0, 0, _d - 1, _h - 1, _w - 1));
volumes.push_back(VoxelVolume{volume, file->fileName(), true});
const int dataSize = fileSize - offset;
const int bufSize = _w * _h * _d;
uint8_t* voxelBuf = new uint8_t[bufSize]();
std::unique_ptr<uint8_t[]> pVoxelBuf(voxelBuf);
int index = 0;
int n = 0;
if ((dataSize % 2) != 0) {
Log::warn("Expected data segment size %i", dataSize);
}
for (int i = offset; i < fileSize; i += 2) {
const uint8_t value = buf[i + 0];
if (value > 1) {
Log::error("Invalid value at offset %i (currently at index: %i)", i, index);
}
const uint8_t count = buf[i + 1];
n += count;
const int endIndex = index + count;
if (endIndex > bufSize) {
Log::error("The end index %i is bigger than the data size %i", endIndex, dataSize);
break;
}
if (value != 0) {
for (int v = index; v < endIndex; ++v) {
voxelBuf[v] = 1;
}
}
index = endIndex;
}
if (n != bufSize) {
Log::warn("Unexpected voxel amount: %i, expected: %i (w: %i, h: %i, d: %i), fileSize %i, offset %i",
dataSize, bufSize, (int)_w, (int)_h, (int)_d, fileSize, (int)offset);
}
const voxel::Voxel &voxel = voxel::createRandomColorVoxel(voxel::VoxelType::Generic);
int idx = 0;
for (int x = 0; x < _d; ++x) {
for (int y = 0; y < _w; ++y) {
for (int z = 0; z < _h; ++z) {
if (voxelBuf[idx] == 1) {
volume->setVoxel(z, y, x, voxel);
}
++idx;
}
}
}
return true;
}
VoxelVolumes BinVoxFormat::loadGroups(const io::FilePtr& file) {
std::string str = file->load();
const size_t dataOffset = str.find("data");
if (dataOffset == std::string::npos) {
Log::error("Could not find end of header in %s", file->name().c_str());
return VoxelVolumes();
}
const std::string& header = str.substr(0, dataOffset);
if (!readHeader(header)) {
Log::error("Could not read header of %s", file->name().c_str());
return VoxelVolumes();
}
VoxelVolumes v;
if (!readData(file, dataOffset + 5, v)) {
Log::warn("Could not load the whole data from %s", file->name().c_str());
}
return v;
}
bool BinVoxFormat::saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) {
return false;
}
}

View File

@ -0,0 +1,38 @@
/**
* @file
*/
#pragma once
#include "VoxFileFormat.h"
#include "io/FileStream.h"
#include "io/File.h"
#include <string>
namespace voxel {
/**
* @brief BinVox (binvox) format.
*
* https://www.patrickmin.com/binvox/binvox.html
*/
class BinVoxFormat : public VoxFileFormat {
private:
uint32_t _version = 0u;
uint32_t _w = 0u;
uint32_t _h = 0u;
uint32_t _d = 0u;
uint32_t _size = 0u;
float _tx = 0.0f;
float _ty = 0.0f;
float _tz = 0.0f;
float _scale = 0.0f;
bool readData(const io::FilePtr& file, const size_t offset, VoxelVolumes& volumes);
bool readHeader(const std::string& header);
public:
VoxelVolumes loadGroups(const io::FilePtr& file) override;
bool saveGroups(const VoxelVolumes& volumes, const io::FilePtr& file) override;
};
}

View File

@ -1,5 +1,6 @@
set(LIB voxelformat)
set(SRCS
BinVoxFormat.h BinVoxFormat.cpp
VoxFileFormat.h VoxFileFormat.cpp
VoxFormat.h VoxFormat.cpp
QBTFormat.h QBTFormat.cpp
@ -12,6 +13,7 @@ engine_add_module(TARGET ${LIB} SRCS ${SRCS} DEPENDENCIES voxel assimp)
set(TEST_SRCS
tests/AbstractVoxFormatTest.h tests/AbstractVoxFormatTest.cpp
tests/BinVoxFormatTest.cpp
tests/VoxFormatTest.cpp
tests/QBTFormatTest.cpp
tests/QBFormatTest.cpp
@ -21,6 +23,7 @@ set(TEST_SRCS
set(TEST_FILES
tests/qubicle.qb
tests/qubicle.qbt
tests/test.binvox
tests/magicavoxel.vox
tests/test.vxm
)

View File

@ -8,6 +8,7 @@
#include "QBTFormat.h"
#include "QBFormat.h"
#include "VXMFormat.h"
#include "BinVoxFormat.h"
namespace voxelformat {
@ -25,8 +26,12 @@ bool loadVolumeFormat(const io::FilePtr& filePtr, voxel::VoxelVolumes& newVolume
} else if (ext == "vxm") {
voxel::VXMFormat f;
newVolumes = f.loadGroups(filePtr);
} else if (ext == "binvox") {
voxel::BinVoxFormat f;
newVolumes = f.loadGroups(filePtr);
} else {
Log::error("Failed to load model file %s - unsupported file format", filePtr->name().c_str());
Log::error("Failed to load model file %s - unsupported file format for extension '%s'",
filePtr->name().c_str(), ext.c_str());
return false;
}
if (newVolumes.empty()) {

View File

@ -0,0 +1,19 @@
/**
* @file
*/
#include "AbstractVoxFormatTest.h"
#include "voxelformat/BinVoxFormat.h"
namespace voxel {
class BinVoxFormatTest: public AbstractVoxFormatTest {
};
TEST_F(BinVoxFormatTest, testLoad) {
BinVoxFormat f;
std::unique_ptr<RawVolume> volume(load("test.binvox", f));
ASSERT_NE(nullptr, volume) << "Could not load binvox file";
}
}

View File

@ -200,7 +200,7 @@ core::AppState VoxEdit::onInit() {
core::setBindingContext(voxedit::BindingContext::UI);
if (_argc >= 1) {
if (_argc >= 2) {
const char *file = _argv[_argc - 1];
const io::FilePtr& filePtr = filesystem()->open(file);
if (filePtr->exists()) {

View File

@ -25,7 +25,7 @@
namespace voxedit {
static const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm";
static const char *SUPPORTED_VOXEL_FORMATS_LOAD = "vox,qbt,qb,vxm,binvox";
static const char *SUPPORTED_VOXEL_FORMATS_SAVE = "vox,qbt,qb";
static const struct {