VOXCONVERT: use the new volume import feature
and also allow to import both sidesmaster
parent
53c02d08dd
commit
c18e4e89c7
|
@ -49,6 +49,23 @@ Import input images as heightmaps (default).
|
|||
\fB\--image-as-plane\fR
|
||||
Import input images as planes.
|
||||
|
||||
.TP
|
||||
\fB\--image-as-volume\fR
|
||||
Import given input image as volume. Uses a depth map to make a volume out of the
|
||||
image. The depth map R channel is using values from 0 (black) to white (255)
|
||||
resulting in voxel heights from 1 to max-height (see --image-as-volume-max-depth).
|
||||
|
||||
The \fB\--input\fR with e.g. \fBsomeimage.png\fR will pick the depth map next to
|
||||
the image path called \fBsomeimage-dm.png\fR as depth map.
|
||||
|
||||
.TP
|
||||
\fB\--image-as-volume-max-depth\fR
|
||||
Default is 8 - see --image-as-volume.
|
||||
|
||||
.TP
|
||||
\fB\--image-as-volume-both-sides\fR
|
||||
Importing image as volume and use the depth map for both sides.
|
||||
|
||||
.TP
|
||||
\fB\--input|-i <file>\fR
|
||||
Allow to specify input files.
|
||||
|
|
|
@ -8,6 +8,16 @@ Generate a lod scaled by 50% from the input model.
|
|||
|
||||
`./vengi-voxconvert -s --input infile.vox --output output.vox`
|
||||
|
||||
## Import 2d image as volume
|
||||
|
||||
Imports a 2d image and applies depth to it.
|
||||
|
||||
`./vengi-voxconvert --image-as-volume --image-as-volume-max-depth 8 --image-as-volume-both-sides true --input infile.png --output output.vox`
|
||||
|
||||
Import given input image as volume. Uses a depth map to make a volume out of the image. The depth map R channel is using values from 0 (black) to white (255) resulting in voxel heights from 1 to max-height (see `--image-as-volume-max-depth`).
|
||||
|
||||
The `--input` with e.g. `infile.png` will pick the depth map next to the image path called `infile-dm.png` as depth map.
|
||||
|
||||
## Merge several models
|
||||
|
||||
Merge several models into one:
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
* `--filter <filter>`: will filter out layers not mentioned in the expression. E.g. `1-2,4` will handle layer 1, 2 and 4. It is the same as `1,2,4`. The first layer is `0`. See the layers note below.
|
||||
* `--force`: overwrite existing files
|
||||
* `--image-as-heightmap`: import input images as heightmap (default)
|
||||
* `--image-as-volume`: import given input image as volume. Uses a depth map to make a volume out of the image.
|
||||
* `--image-as-volume-max-depth`: importing image as volume max depth
|
||||
* `--image-as-volume-both-sides`: importing image as volume and use the depth map for both sides
|
||||
* `--image-as-plane`: import input images as planes
|
||||
* `--input <file>`: allows to specify input files. You can specify more than one file
|
||||
* `--merge`: will merge a multi layer volume (like `vox`, `qb` or `qbt`) into a single volume of the target file
|
||||
|
|
|
@ -57,9 +57,15 @@ void importHeightmap(voxel::RawVolumeWrapper& volume, const image::ImagePtr& ima
|
|||
|
||||
voxel::RawVolume* importAsPlane(const image::ImagePtr& image, uint8_t thickness) {
|
||||
if (thickness <= 0) {
|
||||
Log::error("Thickness can't be 0");
|
||||
return nullptr;
|
||||
}
|
||||
if (!image || !image->isLoaded()) {
|
||||
Log::error("No color image given");
|
||||
return nullptr;
|
||||
}
|
||||
if (image->depth() != 4) {
|
||||
Log::error("Expected to get an rgba image");
|
||||
return nullptr;
|
||||
}
|
||||
const int imageWidth = image->width();
|
||||
|
@ -71,8 +77,6 @@ voxel::RawVolume* importAsPlane(const image::ImagePtr& image, uint8_t thickness)
|
|||
Log::info("Import image as plane: w(%i), h(%i), d(%i)", imageWidth, imageHeight, thickness);
|
||||
const voxel::Region region(0, 0, 0, imageWidth - 1, imageHeight - 1, thickness - 1);
|
||||
const voxel::Palette &palette = voxel::getPalette();
|
||||
core::DynamicArray<glm::vec4> materialColors;
|
||||
palette.toVec4f(materialColors);
|
||||
voxel::RawVolume* volume = new voxel::RawVolume(region);
|
||||
for (int x = 0; x < imageWidth; ++x) {
|
||||
for (int y = 0; y < imageHeight; ++y) {
|
||||
|
@ -81,7 +85,7 @@ voxel::RawVolume* importAsPlane(const image::ImagePtr& image, uint8_t thickness)
|
|||
if (data[3] == 0) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t index = core::Color::getClosestMatch(color, materialColors);
|
||||
const uint8_t index = palette.getClosestMatch(color);
|
||||
const voxel::Voxel voxel = voxel::createVoxel(voxel::VoxelType::Generic, index);
|
||||
for (int z = 0; z < thickness; ++z) {
|
||||
volume->setVoxel(x, (imageHeight - 1) - y, z, voxel);
|
||||
|
@ -91,8 +95,8 @@ voxel::RawVolume* importAsPlane(const image::ImagePtr& image, uint8_t thickness)
|
|||
return volume;
|
||||
}
|
||||
|
||||
voxel::RawVolume* importAsVolume(const image::ImagePtr& image, const image::ImagePtr& heightmap, uint8_t maxHeight) {
|
||||
if (maxHeight <= 0) {
|
||||
voxel::RawVolume* importAsVolume(const image::ImagePtr& image, const image::ImagePtr& heightmap, uint8_t maxDepth, bool bothSides) {
|
||||
if (maxDepth <= 0) {
|
||||
Log::error("Max height can't be 0");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -110,32 +114,45 @@ voxel::RawVolume* importAsVolume(const image::ImagePtr& image, const image::Imag
|
|||
}
|
||||
const int imageWidth = image->width();
|
||||
const int imageHeight = image->height();
|
||||
if (imageWidth * imageHeight * maxHeight > 1024 * 1024 * 4) {
|
||||
Log::warn("Did not import plane - max volume size of 1024x1024 (thickness 4) exceeded (%i:%i:%i)", imageWidth, imageHeight, maxHeight);
|
||||
int volumeDepth = bothSides ? maxDepth * 2 : maxDepth;
|
||||
if (volumeDepth % 2 == 0) {
|
||||
Log::warn("Make max volume depth uneven");
|
||||
volumeDepth++;
|
||||
}
|
||||
if (imageWidth * imageHeight * volumeDepth > 1024 * 1024 * 4) {
|
||||
Log::warn("Did not import plane - max volume size of 1024x1024 (depth 4) exceeded (%i:%i:%i)", imageWidth, imageHeight, volumeDepth);
|
||||
return nullptr;
|
||||
}
|
||||
Log::info("Import image as volume: w(%i), h(%i), d(%i)", imageWidth, imageHeight, maxHeight);
|
||||
const voxel::Region region(0, 0, 0, imageWidth - 1, imageHeight - 1, maxHeight - 1);
|
||||
Log::info("Import image as volume: w(%i), h(%i), d(%i)", imageWidth, imageHeight, volumeDepth);
|
||||
const voxel::Region region(0, 0, 0, imageWidth - 1, imageHeight - 1, volumeDepth - 1);
|
||||
const voxel::Palette &palette = voxel::getPalette();
|
||||
core::DynamicArray<glm::vec4> materialColors;
|
||||
palette.toVec4f(materialColors);
|
||||
voxel::RawVolume* volume = new voxel::RawVolume(region);
|
||||
for (int x = 0; x < imageWidth; ++x) {
|
||||
for (int y = 0; y < imageHeight; ++y) {
|
||||
const uint8_t* data = image->at(x, y);
|
||||
const glm::vec4& color = core::Color::fromRGBA(data[0], data[1], data[2], data[3]);
|
||||
if (data[3] == 0) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t index = core::Color::getClosestMatch(color, materialColors);
|
||||
const glm::vec4& color = core::Color::fromRGBA(data[0], data[1], data[2], data[3]);
|
||||
const uint8_t index = palette.getClosestMatch(color);
|
||||
const voxel::Voxel voxel = voxel::createVoxel(voxel::VoxelType::Generic, index);
|
||||
const uint8_t* heightdata = heightmap->at(x, y);
|
||||
const float thickness = (float)*heightdata;
|
||||
const float maxthickness = maxHeight;
|
||||
const float maxthickness = maxDepth;
|
||||
const float height = thickness * maxthickness / 255.0f;
|
||||
for (int z = 0; z < (int)glm::ceil(height); ++z) {
|
||||
if (bothSides) {
|
||||
const int heighti = (int)glm::ceil(height/ 2.0f);
|
||||
const int minZ = maxDepth - heighti;
|
||||
const int maxZ = maxDepth + heighti;
|
||||
for (int z = minZ; z <= maxZ; ++z) {
|
||||
volume->setVoxel(x, (imageHeight - 1) - y, z, voxel);
|
||||
}
|
||||
} else {
|
||||
const int heighti = (int)glm::ceil(height);
|
||||
for (int z = 0; z < heighti; ++z) {
|
||||
volume->setVoxel(x, (imageHeight - 1) - y, z, voxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return volume;
|
||||
|
|
|
@ -11,6 +11,6 @@ namespace voxelutil {
|
|||
|
||||
extern void importHeightmap(voxel::RawVolumeWrapper& volume, const image::ImagePtr& image, const voxel::Voxel &underground, const voxel::Voxel &surface);
|
||||
extern voxel::RawVolume* importAsPlane(const image::ImagePtr& image, uint8_t thickness = 1);
|
||||
extern voxel::RawVolume* importAsVolume(const image::ImagePtr& image, const image::ImagePtr& heightmap, uint8_t maxHeight);
|
||||
extern voxel::RawVolume* importAsVolume(const image::ImagePtr& image, const image::ImagePtr& heightmap, uint8_t maxDepth, bool bothSides = true);
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,9 @@ app::AppState VoxConvert::onConstruct() {
|
|||
registerArg("--filter").setDescription("Layer filter. For example '1-4,6'");
|
||||
registerArg("--force").setShort("-f").setDescription("Overwrite existing files");
|
||||
registerArg("--image-as-plane").setDescription("Import given input images as planes");
|
||||
registerArg("--image-as-volume").setDescription("Import given input image as volume");
|
||||
registerArg("--image-as-volume-max-depth").setDefaultValue("8").setDescription("Importing image as volume max depth");
|
||||
registerArg("--image-as-volume-both-sides").setDefaultValue("true").setDescription("Importing image as volume for both sides");
|
||||
registerArg("--image-as-heightmap").setDescription("Import given input images as heightmaps");
|
||||
registerArg("--input").setShort("-i").setDescription("Allow to specify input files");
|
||||
registerArg("--merge").setShort("-m").setDescription("Merge layers into one volume");
|
||||
|
@ -384,8 +387,9 @@ bool VoxConvert::handleInputFile(const core::String &infile, voxelformat::SceneG
|
|||
return false;
|
||||
}
|
||||
const bool importAsPlane = hasArg("--image-as-plane");
|
||||
const bool importAsVolume = hasArg("--image-as-volume");
|
||||
const bool importAsHeightmap = hasArg("--image-as-heightmap");
|
||||
if (importAsHeightmap || !importAsPlane) {
|
||||
if (importAsHeightmap || (!importAsPlane && !importAsVolume)) {
|
||||
Log::info("Generate from heightmap (%i:%i)", image->width(), image->height());
|
||||
if (image->width() > MaxHeightmapWidth || image->height() >= MaxHeightmapHeight) {
|
||||
Log::warn("Skip creating heightmap - image dimensions exceeds the max allowed boundaries");
|
||||
|
@ -402,6 +406,23 @@ bool VoxConvert::handleInputFile(const core::String &infile, voxelformat::SceneG
|
|||
node.setName(infile);
|
||||
sceneGraph.emplace(core::move(node));
|
||||
}
|
||||
if (importAsVolume) {
|
||||
const core::String &extinfile = core::string::extractExtension(infile);
|
||||
core::String baseinfile = core::string::stripExtension(infile);
|
||||
baseinfile.append("-dm.");
|
||||
baseinfile.append(extinfile);
|
||||
const image::ImagePtr& heightmap = image::loadImage(baseinfile, false);
|
||||
if (!heightmap || !heightmap->isLoaded()) {
|
||||
Log::error("Couldn't load heightmap %s", baseinfile.c_str());
|
||||
return false;
|
||||
}
|
||||
voxelformat::SceneGraphNode node;
|
||||
const int maxDepth = core::string::toInt(getArgVal("--image-as-volume-max-depth"));
|
||||
const bool bothSides = core::string::toBool(getArgVal("--image-as-volume-both-sides"));
|
||||
node.setVolume(voxelutil::importAsVolume(image, heightmap, maxDepth, bothSides), true);
|
||||
node.setName(infile);
|
||||
sceneGraph.emplace(core::move(node));
|
||||
}
|
||||
if (importAsPlane) {
|
||||
voxelformat::SceneGraphNode node;
|
||||
node.setVolume(voxelutil::importAsPlane(image), true);
|
||||
|
|
Loading…
Reference in New Issue