Skip to content

Commit

Permalink
VOXELFORMAT: add normal support for importing mesh formats #514
Browse files Browse the repository at this point in the history
this is for importing only - mesh exports don't yet include the normals
  • Loading branch information
mgerhardy committed Oct 29, 2024
1 parent e1d8b8e commit 312724f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 26 deletions.
52 changes: 34 additions & 18 deletions src/modules/voxelformat/private/mesh/MeshFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,39 @@ void MeshFormat::subdivideTri(const voxelformat::TexturedTri &tri, TriCollection
tinyTris.push_back(tri);
}

uint8_t MeshFormat::PosSampling::getNormal() const {
uint8_t normal = 0;
uint32_t area = 0;
for (const PosSamplingEntry &pe : entries) {
if (pe.area > area) {
area = pe.area;
normal = pe.normal;
}
}
return normal;
}

core::RGBA MeshFormat::PosSampling::getColor(uint8_t flattenFactor, bool weightedAverage) const {
if (entries.size() == 1) {
return core::Color::flattenRGB(entries[0].color.r, entries[0].color.g, entries[0].color.b, entries[0].color.a,
flattenFactor);
}
if (weightedAverage) {
float sumArea = 0.0f;
uint32_t sumArea = 0;
for (const PosSamplingEntry &pe : entries) {
sumArea += pe.area;
}
core::RGBA color(0, 0, 0, 255);
if (sumArea <= 0.0f) {
if (sumArea == 0) {
return color;
}
for (const PosSamplingEntry &pe : entries) {
color = core::RGBA::mix(color, pe.color, pe.area / sumArea);
color = core::RGBA::mix(color, pe.color, (float)pe.area / (float)sumArea);
}
return core::Color::flattenRGB(color.r, color.g, color.b, color.a, flattenFactor);
}
core::RGBA color(0, 0, 0, AlphaThreshold);
float area = 0.0f;
uint32_t area = 0;
for (const PosSamplingEntry &pe : entries) {
if (pe.area > area) {
area = pe.area;
Expand Down Expand Up @@ -142,34 +154,37 @@ glm::vec2 MeshFormat::paletteUV(int colorIndex) {
return {u, v};
}

void MeshFormat::transformTris(const voxel::Region &region, const TriCollection &tris, PosMap &posMap) {
void MeshFormat::transformTris(const voxel::Region &region, const TriCollection &tris, PosMap &posMap,
const palette::NormalPalette &normalPalette) {
Log::debug("subdivided into %i triangles", (int)tris.size());
for (const voxelformat::TexturedTri &tri : tris) {
if (stopExecution()) {
return;
}
const float area = tri.area();
const core::RGBA rgba = tri.centerColor();
if (rgba.a <= AlphaThreshold) {
continue;
}
const uint32_t area = (uint32_t)(tri.area() * 1000.0f);
glm::vec3 c = tri.center();
convertToVoxelGrid(c);

const uint8_t normalIdx = normalPalette.getClosestMatch(tri.normal());

const glm::ivec3 p(c);
core_assert_msg(region.containsPoint(p), "Failed to transform tri %i:%i:%i (region: %s)", p.x, p.y, p.z,
region.toString().c_str());
auto iter = posMap.find(p);
if (iter == posMap.end()) {
posMap.emplace(p, {area, rgba});
posMap.emplace(p, {area, rgba, normalIdx});
} else if (iter->value.entries.size() < MaxTriangleColorContributions && iter->value.entries[0].color != rgba) {
PosSampling &pos = iter->value;
pos.entries.emplace_back(area, rgba);
pos.entries.emplace_back(area, rgba, normalIdx);
}
}
}

void MeshFormat::transformTrisAxisAligned(const voxel::Region &region, const TriCollection &tris, PosMap &posMap) {
void MeshFormat::transformTrisAxisAligned(const voxel::Region &region, const TriCollection &tris, PosMap &posMap, const palette::NormalPalette &normalPalette) {
Log::debug("axis aligned %i triangles", (int)tris.size());
for (const voxelformat::TexturedTri &tri : tris) {
if (stopExecution()) {
Expand All @@ -179,7 +194,7 @@ void MeshFormat::transformTrisAxisAligned(const voxel::Region &region, const Tri
if (rgba.a <= AlphaThreshold) {
continue;
}
const float area = tri.area();
const uint32_t area = (uint32_t)(tri.area() * 1000.0f);
const glm::vec3 &normal = glm::normalize(tri.normal());
const glm::ivec3 sideDelta(normal.x <= 0 ? 0 : -1, normal.y <= 0 ? 0 : -1, normal.z <= 0 ? 0 : -1);
const glm::ivec3 mins = tri.roundedMins();
Expand All @@ -188,7 +203,7 @@ void MeshFormat::transformTrisAxisAligned(const voxel::Region &region, const Tri
Log::debug("maxs: %i:%i:%i", maxs.x, maxs.y, maxs.z);
Log::debug("normal: %f:%f:%f", normal.x, normal.y, normal.z);
Log::debug("sideDelta: %i:%i:%i", sideDelta.x, sideDelta.y, sideDelta.z);

const uint8_t normalIdx = normalPalette.getClosestMatch(normal);
for (int x = mins.x; x < maxs.x; x++) {
for (int y = mins.y; y < maxs.y; y++) {
for (int z = mins.z; z < maxs.z; z++) {
Expand All @@ -200,10 +215,10 @@ void MeshFormat::transformTrisAxisAligned(const voxel::Region &region, const Tri
}
auto iter = posMap.find(p);
if (iter == posMap.end()) {
posMap.emplace(p, {area, rgba});
posMap.emplace(p, {area, rgba, normalIdx});
} else if (iter->value.entries.size() < MaxTriangleColorContributions && iter->value.entries[0].color != rgba) {
PosSampling &pos = iter->value;
pos.entries.emplace_back(area, rgba);
pos.entries.emplace_back(area, rgba, normalIdx);
}
}
}
Expand Down Expand Up @@ -299,20 +314,21 @@ int MeshFormat::voxelizeNode(const core::String &uuid, const core::String &name,
scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model, uuid);
node.setVolume(new voxel::RawVolume(region), true);
node.setName(name);
palette::NormalPalette normalPalette;
normalPalette.redAlert2();
node.setNormalPalette(normalPalette);

const int voxelizeMode = core::Var::getSafe(cfg::VoxformatVoxelizeMode)->intVal();
const bool fillHollow = core::Var::getSafe(cfg::VoxformatFillHollow)->boolVal();
if (axisAligned) {
const int maxVoxels = vdim.x * vdim.y * vdim.z;
Log::debug("max voxels: %i (%i:%i:%i)", maxVoxels, vdim.x, vdim.y, vdim.z);
PosMap posMap(maxVoxels);
transformTrisAxisAligned(region, tris, posMap);
transformTrisAxisAligned(region, tris, posMap, normalPalette);
voxelizeTris(node, posMap, fillHollow);
} else if (voxelizeMode == VoxelizeMode::Fast) {
voxel::RawVolumeWrapper wrapper(node.volume());
palette::Palette palette;
palette::NormalPalette normalPalette;
normalPalette.redAlert2();

const bool shouldCreatePalette = core::Var::getSafe(cfg::VoxelCreatePalette)->boolVal();
if (shouldCreatePalette) {
Expand Down Expand Up @@ -382,7 +398,7 @@ int MeshFormat::voxelizeNode(const core::String &uuid, const core::String &name,
}

PosMap posMap((int)subdivided.size() * 3);
transformTris(region, subdivided, posMap);
transformTris(region, subdivided, posMap, normalPalette);
voxelizeTris(node, posMap, fillHollow);
}

Expand Down Expand Up @@ -447,7 +463,7 @@ void MeshFormat::voxelizeTris(scenegraph::SceneGraphNode &node, const PosMap &po
if (rgba.a <= AlphaThreshold) {
continue;
}
const voxel::Voxel voxel = voxel::createVoxel(palette, palette.getClosestMatch(rgba));
const voxel::Voxel voxel = voxel::createVoxel(palette, palette.getClosestMatch(rgba), pos.getNormal());
wrapper.setVoxel(entry->first, voxel);
}
if (palette.colorCount() == 1) {
Expand Down
17 changes: 11 additions & 6 deletions src/modules/voxelformat/private/mesh/MeshFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "core/collection/DynamicArray.h"
#include "core/collection/Map.h"
#include "io/Archive.h"
#include "palette/NormalPalette.h"
#include "voxel/ChunkMesh.h"
#include "voxelformat/Format.h"

Expand Down Expand Up @@ -99,9 +100,10 @@ class MeshFormat : public Format {
* during voxelization
*/
struct PosSamplingEntry {
inline PosSamplingEntry(float _area, core::RGBA _color) : area(_area), color(_color) {
inline PosSamplingEntry(uint32_t _area, core::RGBA _color, uint8_t _normal) : area(_area), normal(_normal), color(_color) {
}
float area;
uint32_t area : 24;
uint8_t normal;
core::RGBA color;
};

Expand All @@ -111,10 +113,11 @@ class MeshFormat : public Format {
*/
struct PosSampling {
core::DynamicArray<PosSamplingEntry> entries;
inline PosSampling(float area, core::RGBA color) {
entries.emplace_back(area, color);
inline PosSampling(uint32_t area, core::RGBA color, uint8_t normal) {
entries.emplace_back(area, color, normal);
}
core::RGBA getColor(uint8_t flattenFactor, bool weightedAverage) const;
uint8_t getNormal() const;
};

/**
Expand All @@ -130,7 +133,8 @@ class MeshFormat : public Format {
* @sa transformTrisAxisAligned()
* @sa voxelizeTris()
*/
static void transformTris(const voxel::Region &region, const TriCollection &tris, PosMap &posMap);
static void transformTris(const voxel::Region &region, const TriCollection &tris, PosMap &posMap,
const palette::NormalPalette &normalPalette);
/**
* @brief Convert the given input triangles into a list of positions to place the voxels at. This version is for
* aligned aligned triangles. This is usually the case for meshes that were exported from voxels.
Expand All @@ -140,7 +144,8 @@ class MeshFormat : public Format {
* @sa transformTris()
* @sa voxelizeTris()
*/
static void transformTrisAxisAligned(const voxel::Region &region, const TriCollection &tris, PosMap &posMap);
static void transformTrisAxisAligned(const voxel::Region &region, const TriCollection &tris, PosMap &posMap,
const palette::NormalPalette &normalPalette);
/**
* @brief Convert the given @c PosMap into a volume
*
Expand Down
7 changes: 5 additions & 2 deletions src/tools/voxedit/modules/voxedit-ui/tests/TestUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#include "scenegraph/SceneGraphNode.h"
#include "ui/IMGUIApp.h"
#include "voxedit-ui/Viewport.h"
#include "voxelutil/VolumeVisitor.h"
#include "voxedit-util/SceneManager.h"
#include "voxel/RawVolume.h"
#include "voxel/Region.h"
#include "voxelutil/VolumeVisitor.h"

namespace voxedit {

Expand Down Expand Up @@ -40,7 +42,8 @@ void executeViewportClick() {
command::executeCommands("+actionexecute 1 1;-actionexecute 1 1");
}

bool executeViewportClickArea(ImGuiTestContext *ctx, const SceneManagerPtr &sceneMgr, int viewportId, const ImVec2 &offset) {
bool executeViewportClickArea(ImGuiTestContext *ctx, const SceneManagerPtr &sceneMgr, int viewportId,
const ImVec2 &offset) {
IM_CHECK_RETV(centerOnViewport(ctx, sceneMgr, viewportId, {0.0f, 0.0f}), false);
command::executeCommands("+actionexecute 1 1");
IM_CHECK_RETV(centerOnViewport(ctx, sceneMgr, viewportId, offset), false);
Expand Down

0 comments on commit 312724f

Please sign in to comment.