VOXELFORMAT: added new meshformat test

master
Martin Gerhardy 2022-05-21 19:56:48 +02:00
parent 2a6042adc4
commit aba673fdf8
5 changed files with 136 additions and 82 deletions

View File

@ -8,6 +8,7 @@ set(SRCS
private/MinecraftPaletteMap.h private/MinecraftPaletteMap.cpp
private/NamedBinaryTag.h private/NamedBinaryTag.cpp
private/PaletteLookup.h
private/Tri.h private/Tri.cpp
Format.h Format.cpp
AoSVXLFormat.h AoSVXLFormat.cpp
@ -58,6 +59,7 @@ set(TEST_SRCS
tests/GoxFormatTest.cpp
tests/KVXFormatTest.cpp
tests/KV6FormatTest.cpp
tests/MeshFormatTest.cpp
tests/MCRFormatTest.cpp
tests/OBJFormatTest.cpp
tests/QBTFormatTest.cpp

View File

@ -5,6 +5,7 @@
#pragma once
#include "Format.h"
#include "private/Tri.h"
#include <glm/geometric.hpp>
namespace voxelformat {
@ -14,88 +15,6 @@ namespace voxelformat {
*/
class MeshFormat : public Format {
protected:
struct Tri {
glm::vec3 vertices[3];
glm::vec2 uv[3];
image::ImagePtr texture;
uint32_t color = 0xFFFFFFFF;
constexpr glm::vec2 centerUV() const {
return (uv[0] + uv[1] + uv[2]) / 3.0f;
}
constexpr glm::vec3 center() const {
return (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
}
float area() const {
return glm::length(glm::cross(vertices[1] - vertices[0], vertices[2] - vertices[0])) / 2;
}
glm::vec3 mins() const {
glm::vec3 v;
for (int i = 0; i < 3; ++i) {
v[i] = core_min(vertices[0][i], core_min(vertices[1][i], vertices[2][i]));
}
return v;
}
glm::vec3 maxs() const {
glm::vec3 v;
for (int i = 0; i < 3; ++i) {
v[i] = core_max(vertices[0][i], core_max(vertices[1][i], vertices[2][i]));
}
return v;
}
core::RGBA colorAt(const glm::vec2 &uv) const {
if (texture) {
const float w = (float)texture->width();
const float h = (float)texture->height();
float x = uv.x * w;
float y = uv.y * h;
while (x < 0.0f)
x += w;
while (x > w)
x -= w;
while (y < 0.0f)
y += h;
while (y > h)
y -= h;
const int xint = (int)glm::round(x - 0.5f);
const int yint = texture->height() - (int)glm::round(y - 0.5f) - 1;
const uint8_t *ptr = texture->at(xint, yint);
return *(const core::RGBA *)ptr;
}
return color;
}
// Sierpinski gasket with keeping the middle
void subdivide(Tri out[4]) const {
const glm::vec3 midv[]{glm::mix(vertices[0], vertices[1], 0.5f), glm::mix(vertices[1], vertices[2], 0.5f),
glm::mix(vertices[2], vertices[0], 0.5f)};
const glm::vec2 miduv[]{glm::mix(uv[0], uv[1], 0.5f), glm::mix(uv[1], uv[2], 0.5f),
glm::mix(uv[2], uv[0], 0.5f)};
// the subdivided new three triangles
out[0] = Tri{{vertices[0], midv[0], midv[2]}, {uv[0], miduv[0], miduv[2]}, texture, color};
out[1] = Tri{{vertices[1], midv[1], midv[0]}, {uv[1], miduv[1], miduv[0]}, texture, color};
out[2] = Tri{{vertices[2], midv[2], midv[1]}, {uv[2], miduv[2], miduv[1]}, texture, color};
// keep the middle
out[3] = Tri{{midv[0], midv[1], midv[2]}, {miduv[0], miduv[1], miduv[2]}, texture, color};
}
};
using TriCollection = core::DynamicArray<Tri, 512>;
/**
* Subdivide until we brought the triangles down to the size of 1 or smaller
*/
static void subdivideTri(const Tri &tri, TriCollection &tinyTris);
static void voxelizeTris(voxelformat::SceneGraphNode &node, const TriCollection &tinyTris);
struct MeshExt {
MeshExt(voxel::Mesh *mesh, const SceneGraphNode &node, bool applyTransform);
voxel::Mesh *mesh;
@ -117,6 +36,14 @@ protected:
static bool flipWinding(const glm::vec3 &scale);
public:
using TriCollection = core::DynamicArray<Tri, 512>;
/**
* Subdivide until we brought the triangles down to the size of 1 or smaller
*/
static void subdivideTri(const Tri &tri, TriCollection &tinyTris);
static void voxelizeTris(voxelformat::SceneGraphNode &node, const TriCollection &tinyTris);
bool loadGroups(const core::String &filename, io::SeekableReadStream &file, SceneGraph &sceneGraph) override;
bool saveGroups(const SceneGraph &sceneGraph, const core::String &filename,
io::SeekableWriteStream &stream) override;

View File

@ -0,0 +1,67 @@
/**
* @file
*/
#include "Tri.h"
#include <glm/geometric.hpp>
namespace voxelformat {
float Tri::area() const {
return glm::length(glm::cross(vertices[1] - vertices[0], vertices[2] - vertices[0])) / 2;
}
glm::vec3 Tri::mins() const {
glm::vec3 v;
for (int i = 0; i < 3; ++i) {
v[i] = core_min(vertices[0][i], core_min(vertices[1][i], vertices[2][i]));
}
return v;
}
glm::vec3 Tri::maxs() const {
glm::vec3 v;
for (int i = 0; i < 3; ++i) {
v[i] = core_max(vertices[0][i], core_max(vertices[1][i], vertices[2][i]));
}
return v;
}
core::RGBA Tri::colorAt(const glm::vec2 &uv) const {
if (texture) {
const float w = (float)texture->width();
const float h = (float)texture->height();
float x = uv.x * w;
float y = uv.y * h;
while (x < 0.0f)
x += w;
while (x > w)
x -= w;
while (y < 0.0f)
y += h;
while (y > h)
y -= h;
const int xint = (int)glm::round(x - 0.5f);
const int yint = texture->height() - (int)glm::round(y - 0.5f) - 1;
const uint8_t *ptr = texture->at(xint, yint);
return *(const core::RGBA *)ptr;
}
return color;
}
// Sierpinski gasket with keeping the middle
void Tri::subdivide(Tri out[4]) const {
const glm::vec3 midv[]{glm::mix(vertices[0], vertices[1], 0.5f), glm::mix(vertices[1], vertices[2], 0.5f),
glm::mix(vertices[2], vertices[0], 0.5f)};
const glm::vec2 miduv[]{glm::mix(uv[0], uv[1], 0.5f), glm::mix(uv[1], uv[2], 0.5f), glm::mix(uv[2], uv[0], 0.5f)};
// the subdivided new three triangles
out[0] = Tri{{vertices[0], midv[0], midv[2]}, {uv[0], miduv[0], miduv[2]}, texture, color};
out[1] = Tri{{vertices[1], midv[1], midv[0]}, {uv[1], miduv[1], miduv[0]}, texture, color};
out[2] = Tri{{vertices[2], midv[2], midv[1]}, {uv[2], miduv[2], miduv[1]}, texture, color};
// keep the middle
out[3] = Tri{{midv[0], midv[1], midv[2]}, {miduv[0], miduv[1], miduv[2]}, texture, color};
}
} // namespace voxelformat

View File

@ -0,0 +1,36 @@
/**
* @file
*/
#pragma once
#include "core/RGBA.h"
#include "image/Image.h"
#include "math/Math.h"
namespace voxelformat {
struct Tri {
glm::vec3 vertices[3];
glm::vec2 uv[3];
image::ImagePtr texture;
uint32_t color = 0xFFFFFFFF;
constexpr glm::vec2 centerUV() const {
return (uv[0] + uv[1] + uv[2]) / 3.0f;
}
constexpr glm::vec3 center() const {
return (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
}
float area() const;
glm::vec3 mins() const;
glm::vec3 maxs() const;
core::RGBA colorAt(const glm::vec2 &uv) const;
// Sierpinski gasket with keeping the middle
void subdivide(Tri out[4]) const;
};
} // namespace voxelformat

View File

@ -0,0 +1,22 @@
/**
* @file
*/
#include "voxelformat/MeshFormat.h"
#include "app/tests/AbstractTest.h"
namespace voxelformat {
class MeshFormatTest : public app::AbstractTest {};
TEST_F(MeshFormatTest, testSubdivide) {
MeshFormat::TriCollection tinyTris;
Tri tri;
tri.vertices[0] = glm::vec3(-8.77272797, -11.43335, -0.154544264);
tri.vertices[1] = glm::vec3(-8.77272701, 11.1000004, -0.154543981);
tri.vertices[2] = glm::vec3(8.77272701, 11.1000004, -0.154543981);
MeshFormat::subdivideTri(tri, tinyTris);
EXPECT_EQ(4u, tinyTris.size());
}
} // namespace voxelformat