VOXELFORMAT: added binvox support
parent
e89adc0a13
commit
839f84ee37
|
@ -6,5 +6,6 @@
|
|||
<glob pattern="*.qb"/>
|
||||
<glob pattern="*.qbt"/>
|
||||
<glob pattern="*.vxm"/>
|
||||
<glob pattern="*.binvox"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
|
|
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -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()) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue