1442 lines
53 KiB
C++
1442 lines
53 KiB
C++
#include "wiVoxelGrid.h"
|
|
#include "shaders/ShaderInterop.h"
|
|
#include "wiEventHandler.h"
|
|
#include "wiRenderer.h"
|
|
#include "wiHelper.h"
|
|
|
|
#include "Utility/meshoptimizer/meshoptimizer.h"
|
|
|
|
using namespace wi::graphics;
|
|
using namespace wi::primitive;
|
|
|
|
namespace wi
|
|
{
|
|
void VoxelGrid::init(uint32_t dimX, uint32_t dimY, uint32_t dimZ)
|
|
{
|
|
resolution.x = std::max(4u, dimX);
|
|
resolution.y = std::max(4u, dimY);
|
|
resolution.z = std::max(4u, dimZ);
|
|
resolution_div4.x = (resolution.x + 3u) / 4u;
|
|
resolution_div4.y = (resolution.y + 3u) / 4u;
|
|
resolution_div4.z = (resolution.z + 3u) / 4u;
|
|
resolution_rcp.x = 1.0f / resolution.x;
|
|
resolution_rcp.y = 1.0f / resolution.y;
|
|
resolution_rcp.z = 1.0f / resolution.z;
|
|
voxels.clear();
|
|
voxels.resize(resolution_div4.x * resolution_div4.y * resolution_div4.z);
|
|
}
|
|
void VoxelGrid::cleardata()
|
|
{
|
|
std::fill(voxels.begin(), voxels.end(), 0ull);
|
|
}
|
|
|
|
// 3D array index to flattened 1D array index
|
|
inline uint flatten3D(uint3 coord, uint3 dim)
|
|
{
|
|
return (coord.z * dim.x * dim.y) + (coord.y * dim.x) + coord.x;
|
|
}
|
|
// flattened array index to 3D array index
|
|
inline uint3 unflatten3D(uint idx, uint3 dim)
|
|
{
|
|
const uint z = idx / (dim.x * dim.y);
|
|
idx -= (z * dim.x * dim.y);
|
|
const uint y = idx / dim.x;
|
|
const uint x = idx % dim.x;
|
|
return uint3(x, y, z);
|
|
}
|
|
|
|
void VoxelGrid::inject_triangle(XMVECTOR A, XMVECTOR B, XMVECTOR C, bool subtract)
|
|
{
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
// world -> uvw space:
|
|
A = world_to_uvw(A, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
B = world_to_uvw(B, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
C = world_to_uvw(C, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
|
|
// pixel space:
|
|
A *= RESOLUTION;
|
|
B *= RESOLUTION;
|
|
C *= RESOLUTION;
|
|
|
|
// Degenerate triangle check:
|
|
XMVECTOR Normal = XMVector3Cross(XMVectorSubtract(B, A), XMVectorSubtract(C, A));
|
|
if (XMVector3Equal(Normal, XMVectorZero()))
|
|
return;
|
|
|
|
XMVECTOR MIN = XMVectorMin(A, XMVectorMin(B, C));
|
|
XMVECTOR MAX = XMVectorMax(A, XMVectorMax(B, C));
|
|
|
|
MIN = XMVectorFloor(MIN);
|
|
MAX = XMVectorCeiling(MAX + XMVectorSet(0.0001f, 0.0001f, 0.0001f, 0));
|
|
|
|
MIN = XMVectorMax(MIN, XMVectorZero());
|
|
MAX = XMVectorMin(MAX, RESOLUTION);
|
|
|
|
XMUINT3 mini, maxi;
|
|
XMStoreUInt3(&mini, MIN);
|
|
XMStoreUInt3(&maxi, MAX);
|
|
|
|
volatile long long* data = (volatile long long*)voxels.data();
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
const DirectX::BoundingBox voxel_aabb(XMFLOAT3(x + 0.5f, y + 0.5f, z + 0.5f), XMFLOAT3(0.5f, 0.5f, 0.5f));
|
|
if (voxel_aabb.Intersects(A, B, C))
|
|
{
|
|
const uint3 macro_coord = uint3(x / 4u, y / 4u, z / 4u);
|
|
const uint3 sub_coord = uint3(x % 4u, y % 4u, z % 4u);
|
|
const uint32_t idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint32_t bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
if (subtract)
|
|
{
|
|
AtomicAnd(data + idx, ~mask);
|
|
}
|
|
else
|
|
{
|
|
AtomicOr(data + idx, mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void VoxelGrid::inject_aabb(const wi::primitive::AABB& aabb, bool subtract)
|
|
{
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
XMVECTOR _MIN = XMLoadFloat3(&aabb._min);
|
|
XMVECTOR _MAX = XMLoadFloat3(&aabb._max);
|
|
|
|
// world -> uvw space:
|
|
_MIN = world_to_uvw(_MIN, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
_MAX = world_to_uvw(_MAX, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
|
|
// pixel space:
|
|
_MIN *= RESOLUTION;
|
|
_MAX *= RESOLUTION;
|
|
|
|
// After changing spaces, need to minmax again:
|
|
XMVECTOR MIN = XMVectorMin(_MIN, _MAX);
|
|
XMVECTOR MAX = XMVectorMax(_MIN, _MAX);
|
|
|
|
MIN = XMVectorFloor(MIN);
|
|
MAX = XMVectorCeiling(MAX + XMVectorSet(0.0001f, 0.0001f, 0.0001f, 0));
|
|
|
|
MIN = XMVectorMax(MIN, XMVectorZero());
|
|
MAX = XMVectorMin(MAX, RESOLUTION);
|
|
|
|
XMUINT3 mini, maxi;
|
|
XMStoreUInt3(&mini, MIN);
|
|
XMStoreUInt3(&maxi, MAX);
|
|
|
|
wi::primitive::AABB aabb_src;
|
|
XMStoreFloat3(&aabb_src._min, MIN);
|
|
XMStoreFloat3(&aabb_src._max, MAX);
|
|
|
|
volatile long long* data = (volatile long long*)voxels.data();
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
const uint3 macro_coord = uint3(x / 4u, y / 4u, z / 4u);
|
|
const uint3 sub_coord = uint3(x % 4u, y % 4u, z % 4u);
|
|
const uint32_t idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint32_t bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
if (subtract)
|
|
{
|
|
AtomicAnd(data + idx, ~mask);
|
|
}
|
|
else
|
|
{
|
|
AtomicOr(data + idx, mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void VoxelGrid::inject_sphere(const wi::primitive::Sphere& sphere, bool subtract)
|
|
{
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
AABB aabb;
|
|
aabb.createFromHalfWidth(sphere.center, XMFLOAT3(sphere.radius, sphere.radius, sphere.radius));
|
|
|
|
XMVECTOR _MIN = XMLoadFloat3(&aabb._min);
|
|
XMVECTOR _MAX = XMLoadFloat3(&aabb._max);
|
|
|
|
// world -> uvw space:
|
|
_MIN = world_to_uvw(_MIN, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
_MAX = world_to_uvw(_MAX, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
|
|
// pixel space:
|
|
_MIN *= RESOLUTION;
|
|
_MAX *= RESOLUTION;
|
|
|
|
// After changing spaces, need to minmax again:
|
|
XMVECTOR MIN = XMVectorMin(_MIN, _MAX);
|
|
XMVECTOR MAX = XMVectorMax(_MIN, _MAX);
|
|
|
|
MIN = XMVectorFloor(MIN);
|
|
MAX = XMVectorCeiling(MAX + XMVectorSet(0.0001f, 0.0001f, 0.0001f, 0));
|
|
|
|
MIN = XMVectorMax(MIN, XMVectorZero());
|
|
MAX = XMVectorMin(MAX, RESOLUTION);
|
|
|
|
XMUINT3 mini, maxi;
|
|
XMStoreUInt3(&mini, MIN);
|
|
XMStoreUInt3(&maxi, MAX);
|
|
|
|
volatile long long* data = (volatile long long*)voxels.data();
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
wi::primitive::AABB voxel_aabb;
|
|
XMUINT3 voxel_center_coord = XMUINT3(x, y, z);
|
|
XMFLOAT3 voxel_center_world = coord_to_world(voxel_center_coord);
|
|
voxel_aabb.createFromHalfWidth(voxel_center_world, voxelSize);
|
|
if (voxel_aabb.intersects(sphere))
|
|
{
|
|
const uint3 macro_coord = uint3(x / 4u, y / 4u, z / 4u);
|
|
const uint3 sub_coord = uint3(x % 4u, y % 4u, z % 4u);
|
|
const uint32_t idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint32_t bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
if (subtract)
|
|
{
|
|
AtomicAnd(data + idx, ~mask);
|
|
}
|
|
else
|
|
{
|
|
AtomicOr(data + idx, mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void VoxelGrid::inject_capsule(const wi::primitive::Capsule& capsule, bool subtract)
|
|
{
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
AABB aabb = capsule.getAABB();
|
|
|
|
XMVECTOR _MIN = XMLoadFloat3(&aabb._min);
|
|
XMVECTOR _MAX = XMLoadFloat3(&aabb._max);
|
|
|
|
// world -> uvw space:
|
|
_MIN = world_to_uvw(_MIN, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
_MAX = world_to_uvw(_MAX, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
|
|
// pixel space:
|
|
_MIN *= RESOLUTION;
|
|
_MAX *= RESOLUTION;
|
|
|
|
// After changing spaces, need to minmax again:
|
|
XMVECTOR MIN = XMVectorMin(_MIN, _MAX);
|
|
XMVECTOR MAX = XMVectorMax(_MIN, _MAX);
|
|
|
|
MIN = XMVectorFloor(MIN);
|
|
MAX = XMVectorCeiling(MAX + XMVectorSet(0.0001f, 0.0001f, 0.0001f, 0));
|
|
|
|
MIN = XMVectorMax(MIN, XMVectorZero());
|
|
MAX = XMVectorMin(MAX, RESOLUTION);
|
|
|
|
XMUINT3 mini, maxi;
|
|
XMStoreUInt3(&mini, MIN);
|
|
XMStoreUInt3(&maxi, MAX);
|
|
|
|
volatile long long* data = (volatile long long*)voxels.data();
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
wi::primitive::AABB voxel_aabb;
|
|
XMUINT3 voxel_center_coord = XMUINT3(x, y, z);
|
|
XMFLOAT3 voxel_center_world = coord_to_world(voxel_center_coord);
|
|
voxel_aabb.createFromHalfWidth(voxel_center_world, voxelSize);
|
|
// This capsule-box test can fail if capsule doesn't contain any of the corners or center,
|
|
// but it intersects with the cube. But for now this simple method is used.
|
|
bool intersects = capsule.intersects(voxel_aabb.getCenter());
|
|
if (!intersects)
|
|
{
|
|
for (int c = 0; c < 8; ++c)
|
|
{
|
|
if (capsule.intersects(voxel_aabb.corner(c)))
|
|
{
|
|
intersects = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (intersects)
|
|
{
|
|
const uint3 macro_coord = uint3(x / 4u, y / 4u, z / 4u);
|
|
const uint3 sub_coord = uint3(x % 4u, y % 4u, z % 4u);
|
|
const uint32_t idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint32_t bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
if (subtract)
|
|
{
|
|
AtomicAnd(data + idx, ~mask);
|
|
}
|
|
else
|
|
{
|
|
AtomicOr(data + idx, mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
XMUINT3 VoxelGrid::world_to_coord(const XMFLOAT3& worldpos) const
|
|
{
|
|
XMUINT3 coord;
|
|
XMStoreUInt3(&coord, world_to_uvw(XMLoadFloat3(&worldpos), XMLoadFloat3(¢er), XMLoadFloat3(&resolution_rcp), XMLoadFloat3(&voxelSize_rcp)) * XMLoadUInt3(&resolution));
|
|
return coord;
|
|
}
|
|
XMINT3 VoxelGrid::world_to_coord_signed(const XMFLOAT3& worldpos) const
|
|
{
|
|
XMFLOAT3 coord;
|
|
XMStoreFloat3(&coord, world_to_uvw(XMLoadFloat3(&worldpos), XMLoadFloat3(¢er), XMLoadFloat3(&resolution_rcp), XMLoadFloat3(&voxelSize_rcp)) * XMLoadUInt3(&resolution));
|
|
return XMINT3((int)coord.x, (int)coord.y, (int)coord.z);
|
|
}
|
|
XMFLOAT3 VoxelGrid::coord_to_world(const XMUINT3& coord) const
|
|
{
|
|
XMFLOAT3 worldpos;
|
|
XMStoreFloat3(&worldpos, uvw_to_world((XMLoadUInt3(&coord) + XMVectorReplicate(0.5f)) * XMLoadFloat3(&resolution_rcp), XMLoadFloat3(¢er), XMLoadUInt3(&resolution), XMLoadFloat3(&voxelSize)));
|
|
return worldpos;
|
|
}
|
|
XMFLOAT3 VoxelGrid::coord_to_world(const XMINT3& coord) const
|
|
{
|
|
XMFLOAT3 worldpos;
|
|
XMStoreFloat3(&worldpos, uvw_to_world((XMVectorSet((float)coord.x, (float)coord.y, (float)coord.z, 0) + XMVectorReplicate(0.5f)) * XMLoadFloat3(&resolution_rcp), XMLoadFloat3(¢er), XMLoadUInt3(&resolution), XMLoadFloat3(&voxelSize)));
|
|
return worldpos;
|
|
}
|
|
bool VoxelGrid::check_voxel(const XMINT3& coord) const
|
|
{
|
|
return check_voxel(XMUINT3((uint32_t)coord.x, (uint32_t)coord.y, (uint32_t)coord.z));
|
|
}
|
|
bool VoxelGrid::check_voxel(const XMUINT3& coord) const
|
|
{
|
|
if (!is_coord_valid(coord))
|
|
return false; // early exit when coord is not valid (outside of resolution)
|
|
const uint3 macro_coord = uint3(coord.x / 4u, coord.y / 4u, coord.z / 4u);
|
|
const uint idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint64_t voxels_4x4_block = voxels[idx];
|
|
if (voxels_4x4_block == 0)
|
|
return false; // early exit when whole block is empty
|
|
uint3 sub_coord;
|
|
sub_coord.x = coord.x % 4u;
|
|
sub_coord.y = coord.y % 4u;
|
|
sub_coord.z = coord.z % 4u;
|
|
const uint bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
return (voxels_4x4_block & mask) != 0ull;
|
|
}
|
|
bool VoxelGrid::check_voxel(const XMFLOAT3& worldpos) const
|
|
{
|
|
return check_voxel(world_to_coord(worldpos));
|
|
}
|
|
bool VoxelGrid::is_coord_valid(const XMINT3& coord) const
|
|
{
|
|
return is_coord_valid(XMUINT3((uint32_t)coord.x, (uint32_t)coord.y, (uint32_t)coord.z));
|
|
}
|
|
bool VoxelGrid::is_coord_valid(const XMUINT3& coord) const
|
|
{
|
|
return coord.x < resolution.x && coord.y < resolution.y && coord.z < resolution.z;
|
|
}
|
|
void VoxelGrid::set_voxel(const XMINT3& coord, bool value)
|
|
{
|
|
set_voxel(XMUINT3((uint32_t)coord.x, (uint32_t)coord.y, (uint32_t)coord.z), value);
|
|
}
|
|
void VoxelGrid::set_voxel(const XMUINT3& coord, bool value)
|
|
{
|
|
if (!is_coord_valid(coord))
|
|
return; // early exit when coord is not valid (outside of resolution)
|
|
const uint3 macro_coord = uint3(coord.x / 4u, coord.y / 4u, coord.z / 4u);
|
|
const uint3 sub_coord = uint3(coord.x % 4u, coord.y % 4u, coord.z % 4u);
|
|
const uint idx = flatten3D(macro_coord, resolution_div4);
|
|
const uint bit = flatten3D(sub_coord, uint3(4, 4, 4));
|
|
const uint64_t mask = 1ull << bit;
|
|
if (value)
|
|
{
|
|
voxels[idx] |= mask;
|
|
}
|
|
else
|
|
{
|
|
voxels[idx] &= ~mask;
|
|
}
|
|
}
|
|
void VoxelGrid::set_voxel(const XMFLOAT3& worldpos, bool value)
|
|
{
|
|
set_voxel(world_to_coord(worldpos), value);
|
|
}
|
|
size_t VoxelGrid::get_memory_size() const
|
|
{
|
|
return voxels.size() * sizeof(uint64_t);
|
|
}
|
|
|
|
void VoxelGrid::set_voxelsize(float size)
|
|
{
|
|
set_voxelsize(XMFLOAT3(size, size, size));
|
|
}
|
|
void VoxelGrid::set_voxelsize(const XMFLOAT3& size)
|
|
{
|
|
voxelSize = size;
|
|
voxelSize_rcp.x = 1.0f / voxelSize.x;
|
|
voxelSize_rcp.y = 1.0f / voxelSize.y;
|
|
voxelSize_rcp.z = 1.0f / voxelSize.z;
|
|
}
|
|
|
|
wi::primitive::AABB VoxelGrid::get_aabb() const
|
|
{
|
|
AABB aabb;
|
|
aabb.createFromHalfWidth(center, XMFLOAT3(resolution.x * voxelSize.x, resolution.y * voxelSize.y, resolution.z * voxelSize.z));
|
|
return aabb;
|
|
}
|
|
void VoxelGrid::from_aabb(const wi::primitive::AABB& aabb)
|
|
{
|
|
center = aabb.getCenter();
|
|
XMFLOAT3 halfwidth = aabb.getHalfWidth();
|
|
set_voxelsize(XMFLOAT3(halfwidth.x / resolution.x, halfwidth.y / resolution.y, halfwidth.z / resolution.z));
|
|
}
|
|
|
|
bool VoxelGrid::is_visible(const XMUINT3& start, const XMUINT3& goal) const
|
|
{
|
|
const int dx = int(goal.x) - int(start.x);
|
|
const int dy = int(goal.y) - int(start.y);
|
|
const int dz = int(goal.z) - int(start.z);
|
|
|
|
const int step = std::max(std::abs(dx), std::max(std::abs(dy), std::abs(dz)));
|
|
|
|
const float x_incr = float(dx) / step;
|
|
const float y_incr = float(dy) / step;
|
|
const float z_incr = float(dz) / step;
|
|
|
|
float x = float(start.x);
|
|
float y = float(start.y);
|
|
float z = float(start.z);
|
|
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
debug_subject_coords.push_back(goal);
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
|
|
for (int i = 0; i < step; i++)
|
|
{
|
|
XMUINT3 coord = XMUINT3(uint32_t(std::round(x)), uint32_t(std::round(y)), uint32_t(std::round(z)));
|
|
if (coord.x == goal.x && coord.y == goal.y && coord.z == goal.z)
|
|
return true;
|
|
if (check_voxel(coord))
|
|
{
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
debug_occluded_coords.push_back(coord);
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
return false;
|
|
}
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
debug_visible_coords.push_back(coord);
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
x += x_incr;
|
|
y += y_incr;
|
|
z += z_incr;
|
|
}
|
|
return true;
|
|
}
|
|
bool VoxelGrid::is_visible(const XMFLOAT3& observer, const XMFLOAT3& subject) const
|
|
{
|
|
XMUINT3 start = world_to_coord(observer);
|
|
XMUINT3 goal = world_to_coord(subject);
|
|
return is_visible(start, goal);
|
|
}
|
|
bool VoxelGrid::is_visible(const XMFLOAT3& observer, const AABB& subject) const
|
|
{
|
|
XMUINT3 start = world_to_coord(observer);
|
|
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
XMVECTOR _MIN = XMLoadFloat3(&subject._min);
|
|
XMVECTOR _MAX = XMLoadFloat3(&subject._max);
|
|
|
|
// world -> uvw space:
|
|
_MIN = world_to_uvw(_MIN, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
_MAX = world_to_uvw(_MAX, CENTER, RESOLUTION_RCP, VOXELSIZE_RCP);
|
|
|
|
// pixel space:
|
|
_MIN *= RESOLUTION;
|
|
_MAX *= RESOLUTION;
|
|
|
|
// After changing spaces, need to minmax again:
|
|
XMVECTOR MIN = XMVectorMin(_MIN, _MAX);
|
|
XMVECTOR MAX = XMVectorMax(_MIN, _MAX);
|
|
|
|
MIN = XMVectorFloor(MIN);
|
|
MAX = XMVectorCeiling(MAX + XMVectorSet(0.0001f, 0.0001f, 0.0001f, 0));
|
|
|
|
MIN = XMVectorMax(MIN, XMVectorZero());
|
|
MAX = XMVectorMin(MAX, RESOLUTION);
|
|
|
|
XMUINT3 mini, maxi;
|
|
XMStoreUInt3(&mini, MIN);
|
|
XMStoreUInt3(&maxi, MAX);
|
|
|
|
wi::primitive::AABB aabb_src;
|
|
XMStoreFloat3(&aabb_src._min, MIN);
|
|
XMStoreFloat3(&aabb_src._max, MAX);
|
|
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
// In debug mode we visualize all tests and don't return early
|
|
bool result = false;
|
|
debug_subject_coords.clear();
|
|
debug_occluded_coords.clear();
|
|
debug_visible_coords.clear();
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
XMUINT3 goal = XMUINT3(x, y, z);
|
|
if (is_visible(start, goal))
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
#else
|
|
for (uint32_t x = mini.x; x < maxi.x; ++x)
|
|
{
|
|
for (uint32_t y = mini.y; y < maxi.y; ++y)
|
|
{
|
|
for (uint32_t z = mini.z; z < maxi.z; ++z)
|
|
{
|
|
XMUINT3 goal = XMUINT3(x, y, z);
|
|
if (is_visible(start, goal))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
}
|
|
|
|
void VoxelGrid::add(const VoxelGrid& other)
|
|
{
|
|
if (voxels.size() != other.voxels.size())
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < voxels.size(); ++i)
|
|
{
|
|
voxels[i] |= other.voxels[i];
|
|
}
|
|
}
|
|
void VoxelGrid::subtract(const VoxelGrid& other)
|
|
{
|
|
if (voxels.size() != other.voxels.size())
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < voxels.size(); ++i)
|
|
{
|
|
voxels[i] &= ~other.voxels[i];
|
|
}
|
|
}
|
|
void VoxelGrid::flood_fill()
|
|
{
|
|
VoxelGrid traversed;
|
|
traversed.init(resolution.x, resolution.y, resolution.z);
|
|
wi::vector<int3> stack;
|
|
|
|
for(size_t i=0;i<voxels.size();++i)
|
|
{
|
|
if (voxels[i] == ~0ull)
|
|
continue; // whole block is filled already
|
|
|
|
const uint3 coord = unflatten3D(uint(i), resolution_div4);
|
|
for (uint32_t bit = 0; bit < 64; ++bit)
|
|
{
|
|
const uint3 sub_coord = unflatten3D(bit, uint3(4, 4, 4));
|
|
const int3 origin = int3(int(coord.x * 4 + sub_coord.x), int(coord.y * 4 + sub_coord.y), int(coord.z * 4 + sub_coord.z));
|
|
if (check_voxel(origin))
|
|
continue; // voxel is filled, abort
|
|
|
|
traversed.cleardata();
|
|
stack.clear();
|
|
|
|
stack.push_back(origin);
|
|
bool exit = false;
|
|
|
|
do
|
|
{
|
|
int3 center = stack.back();
|
|
stack.pop_back();
|
|
traversed.set_voxel(center, true);
|
|
|
|
int3 neighbors[] = {
|
|
// left-right:
|
|
int3(center.x - 1, center.y, center.z),
|
|
int3(center.x + 1, center.y, center.z),
|
|
|
|
// up-down:
|
|
int3(center.x, center.y - 1, center.z),
|
|
int3(center.x, center.y + 1, center.z),
|
|
|
|
// forward-back:
|
|
int3(center.x, center.y, center.z - 1),
|
|
int3(center.x, center.y, center.z + 1),
|
|
};
|
|
for (int3 neighbor : neighbors)
|
|
{
|
|
if (!is_coord_valid(neighbor))
|
|
{
|
|
// got out of the voxel grid, the origin cannot be filled
|
|
exit = true;
|
|
break;
|
|
}
|
|
if (traversed.check_voxel(neighbor))
|
|
continue; // don't go to a previously traversed voxel again
|
|
if (!check_voxel(neighbor))
|
|
{
|
|
stack.push_back(neighbor); // add empty neighbor to continue traversing
|
|
}
|
|
}
|
|
} while (!stack.empty() && !exit);
|
|
|
|
if (!exit)
|
|
{
|
|
// No exit was found, mark voxel as solid
|
|
set_voxel(origin, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VoxelGrid::Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri)
|
|
{
|
|
if (archive.IsReadMode())
|
|
{
|
|
archive >> _flags;
|
|
archive >> voxels;
|
|
archive >> resolution;
|
|
archive >> voxelSize;
|
|
archive >> center;
|
|
archive >> debug_color;
|
|
archive >> debug_color_extent;
|
|
|
|
resolution_div4.x = (resolution.x + 3u) / 4u;
|
|
resolution_div4.y = (resolution.y + 3u) / 4u;
|
|
resolution_div4.z = (resolution.z + 3u) / 4u;
|
|
resolution_rcp.x = 1.0f / resolution.x;
|
|
resolution_rcp.y = 1.0f / resolution.y;
|
|
resolution_rcp.z = 1.0f / resolution.z;
|
|
set_voxelsize(voxelSize);
|
|
}
|
|
else
|
|
{
|
|
archive << _flags;
|
|
archive << voxels;
|
|
archive << resolution;
|
|
archive << voxelSize;
|
|
archive << center;
|
|
archive << debug_color;
|
|
archive << debug_color_extent;
|
|
}
|
|
}
|
|
|
|
namespace VoxelGrid_internal
|
|
{
|
|
PipelineState pso;
|
|
static void LoadShaders()
|
|
{
|
|
PipelineStateDesc desc;
|
|
desc.vs = wi::renderer::GetShader(wi::enums::VSTYPE_VERTEXCOLOR);
|
|
desc.ps = wi::renderer::GetShader(wi::enums::PSTYPE_VERTEXCOLOR);
|
|
desc.il = wi::renderer::GetInputLayout(wi::enums::ILTYPE_VERTEXCOLOR);
|
|
desc.dss = wi::renderer::GetDepthStencilState(wi::enums::DSSTYPE_DEPTHREAD);
|
|
desc.rs = wi::renderer::GetRasterizerState(wi::enums::RSTYPE_FRONT);
|
|
desc.bs = wi::renderer::GetBlendState(wi::enums::BSTYPE_ADDITIVE);
|
|
desc.pt = PrimitiveTopology::TRIANGLELIST;
|
|
|
|
GraphicsDevice* device = GetDevice();
|
|
device->CreatePipelineState(&desc, &pso);
|
|
}
|
|
}
|
|
using namespace VoxelGrid_internal;
|
|
|
|
void VoxelGrid::debugdraw(const XMFLOAT4X4& ViewProjection, CommandList cmd) const
|
|
{
|
|
// add box renderer for whole volume:
|
|
XMFLOAT4X4 boxmat;
|
|
XMStoreFloat4x4(&boxmat, get_aabb().getAsBoxMatrix());
|
|
wi::renderer::DrawBox(boxmat, debug_color_extent);
|
|
|
|
// Add a cube for every filled voxel below:
|
|
uint32_t numVoxels = 0;
|
|
for (auto& x : voxels)
|
|
{
|
|
if (x != 0)
|
|
numVoxels += (uint32_t)countbits(x);
|
|
}
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
numVoxels += uint32_t(debug_subject_coords.size() + debug_visible_coords.size() + debug_occluded_coords.size());
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
if (numVoxels == 0)
|
|
return;
|
|
|
|
static bool shaders_loaded = false;
|
|
if (!shaders_loaded)
|
|
{
|
|
shaders_loaded = true;
|
|
static wi::eventhandler::Handle handle = wi::eventhandler::Subscribe(wi::eventhandler::EVENT_RELOAD_SHADERS, [](uint64_t userdata) { LoadShaders(); });
|
|
LoadShaders();
|
|
}
|
|
|
|
struct Vertex
|
|
{
|
|
XMFLOAT4 position;
|
|
XMFLOAT4 color;
|
|
};
|
|
static constexpr Vertex cubeVerts[] = {
|
|
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
|
|
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
|
|
};
|
|
|
|
GraphicsDevice* device = GetDevice();
|
|
|
|
auto mem = device->AllocateGPU(sizeof(cubeVerts) * numVoxels, cmd);
|
|
|
|
const XMVECTOR CENTER = XMLoadFloat3(¢er);
|
|
const XMVECTOR RESOLUTION = XMLoadUInt3(&resolution);
|
|
const XMVECTOR RESOLUTION_RCP = XMLoadFloat3(&resolution_rcp);
|
|
const XMVECTOR VOXELSIZE = XMLoadFloat3(&voxelSize);
|
|
const XMVECTOR VOXELSIZE_RCP = XMLoadFloat3(&voxelSize_rcp);
|
|
|
|
size_t dst_offset = 0;
|
|
for (size_t i = 0; i < voxels.size(); ++i)
|
|
{
|
|
uint64_t voxel_bits = voxels[i];
|
|
if (voxel_bits == 0)
|
|
continue;
|
|
const uint3 coord = unflatten3D(uint(i), resolution_div4);
|
|
while (voxel_bits != 0)
|
|
{
|
|
unsigned long bit_index = firstbitlow(voxel_bits);
|
|
voxel_bits ^= 1ull << bit_index; // remove current bit
|
|
const uint3 sub_coord = unflatten3D(bit_index, uint3(4, 4, 4));
|
|
XMVECTOR uvw = XMVectorSet(coord.x * 4 + sub_coord.x + 0.5f, coord.y * 4 + sub_coord.y + 0.5f, coord.z * 4 + sub_coord.z + 0.5f, 1) * RESOLUTION_RCP;
|
|
XMVECTOR P = uvw_to_world(uvw, CENTER, RESOLUTION, VOXELSIZE);
|
|
Vertex verts[arraysize(cubeVerts)];
|
|
std::memcpy(verts, cubeVerts, sizeof(cubeVerts));
|
|
for (auto& v : verts)
|
|
{
|
|
XMVECTOR C = XMLoadFloat4(&v.position);
|
|
C *= VOXELSIZE;
|
|
C += P;
|
|
C = XMVectorSetW(C, 1);
|
|
XMStoreFloat4(&v.position, C);
|
|
v.color = debug_color;
|
|
}
|
|
std::memcpy((uint8_t*)mem.data + dst_offset, verts, sizeof(verts));
|
|
dst_offset += sizeof(verts);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_VOXEL_OCCLUSION
|
|
auto dbg_voxel = [&](const XMUINT3& coord, const XMFLOAT4& color) {
|
|
XMFLOAT3 pos = coord_to_world(coord);
|
|
XMVECTOR P = XMLoadFloat3(&pos);
|
|
Vertex verts[arraysize(cubeVerts)];
|
|
std::memcpy(verts, cubeVerts, sizeof(cubeVerts));
|
|
for (auto& v : verts)
|
|
{
|
|
XMVECTOR C = XMLoadFloat4(&v.position);
|
|
C *= VOXELSIZE;
|
|
C += P;
|
|
C = XMVectorSetW(C, 1);
|
|
XMStoreFloat4(&v.position, C);
|
|
v.color = color;
|
|
}
|
|
std::memcpy((uint8_t*)mem.data + dst_offset, verts, sizeof(verts));
|
|
dst_offset += sizeof(verts);
|
|
};
|
|
for (auto& coord : debug_subject_coords)
|
|
{
|
|
dbg_voxel(coord, XMFLOAT4(1, 1, 1, 0.25f));
|
|
}
|
|
debug_subject_coords.clear();
|
|
for (auto& coord : debug_visible_coords)
|
|
{
|
|
dbg_voxel(coord, XMFLOAT4(1, 1, 0, 0.125f));
|
|
}
|
|
debug_visible_coords.clear();
|
|
for (auto& coord : debug_occluded_coords)
|
|
{
|
|
dbg_voxel(coord, XMFLOAT4(1, 0, 0, 0.5f));
|
|
}
|
|
debug_occluded_coords.clear();
|
|
#endif // DEBUG_VOXEL_OCCLUSION
|
|
|
|
device->EventBegin("VoxelGrid::debugdraw", cmd);
|
|
device->BindPipelineState(&pso, cmd);
|
|
|
|
const GPUBuffer* vbs[] = {
|
|
&mem.buffer,
|
|
};
|
|
const uint32_t strides[] = {
|
|
sizeof(Vertex),
|
|
};
|
|
const uint64_t offsets[] = {
|
|
mem.offset,
|
|
};
|
|
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
|
|
|
|
MiscCB sb;
|
|
sb.g_xTransform = ViewProjection;
|
|
sb.g_xColor = XMFLOAT4(1, 1, 1, 1);
|
|
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
|
|
|
|
device->Draw(arraysize(cubeVerts) * numVoxels, 0, cmd);
|
|
|
|
device->EventEnd(cmd);
|
|
}
|
|
|
|
|
|
using XYZ = XMFLOAT3;
|
|
|
|
// Source: http://www.paulbourke.net/geometry/polygonise/
|
|
|
|
/*
|
|
int edgeTable[256]. It corresponds to the 2^8 possible combinations of
|
|
of the eight (n) vertices either existing inside or outside (2^n) of the
|
|
surface. A vertex is inside of a surface if the value at that vertex is
|
|
less than that of the surface you are scanning for. The table index is
|
|
constructed bitwise with bit 0 corresponding to vertex 0, bit 1 to vert
|
|
1.. bit 7 to vert 7. The value in the table tells you which edges of
|
|
the table are intersected by the surface. Once again bit 0 corresponds
|
|
to edge 0 and so on, up to edge 12.
|
|
Constructing the table simply consisted of having a program run thru
|
|
the 256 cases and setting the edge bit if the vertices at either end of
|
|
the edge had different values (one is inside while the other is out).
|
|
The purpose of the table is to speed up the scanning process. Only the
|
|
edges whose bit's are set contain vertices of the surface.
|
|
Vertex 0 is on the bottom face, back edge, left side.
|
|
The progression of vertices is clockwise around the bottom face
|
|
and then clockwise around the top face of the cube. Edge 0 goes from
|
|
vertex 0 to vertex 1, Edge 1 is from 2->3 and so on around clockwise to
|
|
vertex 0 again. Then Edge 4 to 7 make up the top face, 4->5, 5->6, 6->7
|
|
and 7->4. Edge 8 thru 11 are the vertical edges from vert 0->4, 1->5,
|
|
2->6, and 3->7.
|
|
4--------5 *---4----*
|
|
/| /| /| /|
|
|
/ | / | 7 | 5 |
|
|
/ | / | / 8 / 9
|
|
7--------6 | *----6---* |
|
|
| | | | | | | |
|
|
| 0----|---1 | *---0|---*
|
|
| / | / 11 / 10 /
|
|
| / | / | 3 | 1
|
|
|/ |/ |/ |/
|
|
3--------2 *---2----*
|
|
*/
|
|
static const int edgeTable[256] = {
|
|
0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
|
|
0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
|
|
0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
|
|
0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
|
|
0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
|
|
0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
|
|
0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
|
|
0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
|
|
0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
|
|
0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
|
|
0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
|
|
0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
|
|
0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
|
|
0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
|
|
0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
|
|
0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
|
|
0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
|
|
0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
|
|
0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
|
|
0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
|
|
0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
|
|
0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
|
|
0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
|
|
0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
|
|
0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
|
|
0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
|
|
0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
|
|
0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
|
|
0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
|
|
0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
|
|
0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
|
|
0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0
|
|
};
|
|
/*
|
|
int triTable[256][16] also corresponds to the 256 possible combinations
|
|
of vertices.
|
|
The [16] dimension of the table is again the list of edges of the cube
|
|
which are intersected by the surface. This time however, the edges are
|
|
enumerated in the order of the vertices making up the triangle mesh of
|
|
the surface. Each edge contains one vertex that is on the surface.
|
|
Each triple of edges listed in the table contains the vertices of one
|
|
triangle on the mesh. The are 16 entries because it has been shown that
|
|
there are at most 5 triangles in a cube and each "edge triple" list is
|
|
terminated with the value -1.
|
|
For example triTable[3] contains
|
|
{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
|
|
This corresponds to the case of a cube whose vertex 0 and 1 are inside
|
|
of the surface and the rest of the verts are outside (00000001 bitwise
|
|
OR'ed with 00000010 makes 00000011 == 3). Therefore, this cube is
|
|
intersected by the surface roughly in the form of a plane which cuts
|
|
edges 8,9,1 and 3. This quadrilateral can be constructed from two
|
|
triangles: one which is made of the intersection vertices found on edges
|
|
1,8, and 3; the other is formed from the vertices on edges 9,8, and 1.
|
|
Remember, each intersected edge contains only one surface vertex. The
|
|
vertex triples are listed in counter clockwise order for proper facing.
|
|
*/
|
|
static const int triTable[256][16] =
|
|
{
|
|
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
|
{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
|
|
{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
|
|
{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
|
{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
|
|
{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
|
|
{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
|
|
{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
|
|
{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
|
|
{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
|
|
{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
|
{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
|
|
{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
|
|
{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
|
|
{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
|
|
{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
|
|
{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
|
|
{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
|
{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
|
|
{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
|
|
{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
|
|
{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
|
|
{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
|
|
{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
|
|
{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
|
|
{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
|
|
{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
|
|
{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
|
|
{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
|
|
{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
|
|
{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
|
{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
|
|
{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
|
|
{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
|
|
{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
|
|
{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
|
|
{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
|
|
{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
|
|
{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
|
|
{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
|
{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
|
|
{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
|
|
{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
|
|
{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
|
|
{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
|
|
{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
|
|
{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
|
|
{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
|
|
{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
|
|
{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
|
|
{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
|
|
{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
|
|
{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
|
|
{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
|
|
{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
|
|
{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
|
|
{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
|
|
{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
|
|
{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
|
|
{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
|
|
{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
|
|
{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
|
|
{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
|
|
{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
|
|
{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
|
|
{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
|
|
{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
|
|
{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
|
|
{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
|
|
{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
|
|
{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
|
|
{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
|
|
{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
|
|
{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
|
|
{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
|
|
{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
|
|
{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
|
|
{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
|
|
{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
|
|
{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
|
|
{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
|
|
{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
|
|
{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
|
|
{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
|
|
{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
|
|
{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
|
|
{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
|
|
{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
|
|
{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
|
|
{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
|
|
{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
|
|
{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
|
|
{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
|
|
{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
|
|
{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
|
|
{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
|
|
{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
|
|
{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
|
|
{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
|
|
{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
|
|
{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
|
|
{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
|
|
{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
|
|
{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
|
|
{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
|
|
{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
|
|
{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
|
|
{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
|
|
{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
|
|
{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
|
|
{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
|
|
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
|
|
};
|
|
|
|
typedef struct {
|
|
XYZ p[3];
|
|
} TRIANGLE;
|
|
|
|
typedef struct {
|
|
XYZ p[8];
|
|
float val[8];
|
|
} GRIDCELL;
|
|
|
|
/*
|
|
Linearly interpolate the position where an isosurface cuts
|
|
an edge between two vertices, each with their own scalar value
|
|
*/
|
|
XYZ VertexInterp(float isolevel, XYZ p1, XYZ p2, float valp1, float valp2)
|
|
{
|
|
float mu;
|
|
XYZ p;
|
|
|
|
if (std::abs(isolevel - valp1) < 0.00001f)
|
|
return(p1);
|
|
if (std::abs(isolevel - valp2) < 0.00001f)
|
|
return(p2);
|
|
if (std::abs(valp1 - valp2) < 0.00001f)
|
|
return(p1);
|
|
mu = (isolevel - valp1) / (valp2 - valp1);
|
|
p.x = p1.x + mu * (p2.x - p1.x);
|
|
p.y = p1.y + mu * (p2.y - p1.y);
|
|
p.z = p1.z + mu * (p2.z - p1.z);
|
|
|
|
return(p);
|
|
}
|
|
|
|
/*
|
|
Given a grid cell and an isolevel, calculate the triangular
|
|
facets required to represent the isosurface through the cell.
|
|
Return the number of triangular facets, the array "triangles"
|
|
will be loaded up with the vertices at most 5 triangular facets.
|
|
0 will be returned if the grid cell is either totally above
|
|
of totally below the isolevel.
|
|
*/
|
|
int Polygonise(GRIDCELL grid, float isolevel, TRIANGLE* triangles)
|
|
{
|
|
int i, ntriang;
|
|
int cubeindex;
|
|
XYZ vertlist[12];
|
|
|
|
|
|
/*
|
|
Determine the index into the edge table which
|
|
tells us which vertices are inside of the surface
|
|
*/
|
|
cubeindex = 0;
|
|
if (grid.val[0] < isolevel) cubeindex |= 1;
|
|
if (grid.val[1] < isolevel) cubeindex |= 2;
|
|
if (grid.val[2] < isolevel) cubeindex |= 4;
|
|
if (grid.val[3] < isolevel) cubeindex |= 8;
|
|
if (grid.val[4] < isolevel) cubeindex |= 16;
|
|
if (grid.val[5] < isolevel) cubeindex |= 32;
|
|
if (grid.val[6] < isolevel) cubeindex |= 64;
|
|
if (grid.val[7] < isolevel) cubeindex |= 128;
|
|
|
|
/* Cube is entirely in/out of the surface */
|
|
if (edgeTable[cubeindex] == 0)
|
|
return(0);
|
|
|
|
/* Find the vertices where the surface intersects the cube */
|
|
if (edgeTable[cubeindex] & 1)
|
|
vertlist[0] =
|
|
VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]);
|
|
if (edgeTable[cubeindex] & 2)
|
|
vertlist[1] =
|
|
VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]);
|
|
if (edgeTable[cubeindex] & 4)
|
|
vertlist[2] =
|
|
VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]);
|
|
if (edgeTable[cubeindex] & 8)
|
|
vertlist[3] =
|
|
VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]);
|
|
if (edgeTable[cubeindex] & 16)
|
|
vertlist[4] =
|
|
VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]);
|
|
if (edgeTable[cubeindex] & 32)
|
|
vertlist[5] =
|
|
VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]);
|
|
if (edgeTable[cubeindex] & 64)
|
|
vertlist[6] =
|
|
VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]);
|
|
if (edgeTable[cubeindex] & 128)
|
|
vertlist[7] =
|
|
VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]);
|
|
if (edgeTable[cubeindex] & 256)
|
|
vertlist[8] =
|
|
VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]);
|
|
if (edgeTable[cubeindex] & 512)
|
|
vertlist[9] =
|
|
VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]);
|
|
if (edgeTable[cubeindex] & 1024)
|
|
vertlist[10] =
|
|
VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]);
|
|
if (edgeTable[cubeindex] & 2048)
|
|
vertlist[11] =
|
|
VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]);
|
|
|
|
/* Create the triangle */
|
|
ntriang = 0;
|
|
for (i = 0; triTable[cubeindex][i] != -1; i += 3) {
|
|
triangles[ntriang].p[0] = vertlist[triTable[cubeindex][i]];
|
|
triangles[ntriang].p[1] = vertlist[triTable[cubeindex][i + 1]];
|
|
triangles[ntriang].p[2] = vertlist[triTable[cubeindex][i + 2]];
|
|
ntriang++;
|
|
}
|
|
|
|
return(ntriang);
|
|
}
|
|
|
|
void VoxelGrid::create_mesh(
|
|
wi::vector<uint32_t>& indices,
|
|
wi::vector<XMFLOAT3>& vertices,
|
|
bool simplify
|
|
)
|
|
{
|
|
TRIANGLE triangles[10];
|
|
wi::vector<XMFLOAT3> unindexed_vertices;
|
|
// Note: step over the voxel grid's perimeter as well, without that the polygonize can leave holes on the sides
|
|
for (int x = -1; x < (int)resolution.x; ++x)
|
|
{
|
|
for (int y = -1; y < (int)resolution.y; ++y)
|
|
{
|
|
for (int z = -1; z < (int)resolution.z; ++z)
|
|
{
|
|
int3 coords[8] = {
|
|
int3(x + 0, y + 0, z + 0),
|
|
int3(x + 1, y + 0, z + 0),
|
|
int3(x + 1, y + 1, z + 0),
|
|
int3(x + 0, y + 1, z + 0),
|
|
int3(x + 0, y + 0, z + 1),
|
|
int3(x + 1, y + 0, z + 1),
|
|
int3(x + 1, y + 1, z + 1),
|
|
int3(x + 0, y + 1, z + 1),
|
|
};
|
|
|
|
GRIDCELL grid = {};
|
|
for (int c = 0; c < arraysize(coords); ++c)
|
|
{
|
|
grid.p[c] = coord_to_world(coords[c]);
|
|
grid.val[c] = check_voxel(coords[c]) ? -1.0f : 1.0f;
|
|
}
|
|
|
|
int num = Polygonise(grid, 0, triangles);
|
|
for (int t = 0; t < num; ++t)
|
|
{
|
|
unindexed_vertices.push_back(triangles[t].p[0]);
|
|
unindexed_vertices.push_back(triangles[t].p[2]);
|
|
unindexed_vertices.push_back(triangles[t].p[1]);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if (unindexed_vertices.empty())
|
|
return;
|
|
|
|
size_t index_count = unindexed_vertices.size();
|
|
size_t unindexed_vertex_count = unindexed_vertices.size();
|
|
std::vector<unsigned int> remap(index_count); // allocate temporary memory for the remap table
|
|
size_t vertex_count = meshopt_generateVertexRemap(&remap[0], NULL, index_count, &unindexed_vertices[0], unindexed_vertex_count, sizeof(XMFLOAT3));
|
|
|
|
indices.resize(index_count);
|
|
vertices.resize(vertex_count);
|
|
|
|
meshopt_remapIndexBuffer(indices.data(), nullptr, index_count, &remap[0]);
|
|
meshopt_remapVertexBuffer(vertices.data(), &unindexed_vertices[0], unindexed_vertex_count, sizeof(XMFLOAT3), &remap[0]);
|
|
|
|
if (simplify)
|
|
{
|
|
static float threshold = 0.2f;
|
|
size_t target_index_count = size_t(index_count * threshold);
|
|
float target_error = 1e-2f;
|
|
unsigned int options = 0; // meshopt_SimplifyX flags, 0 is a safe default
|
|
|
|
std::vector<unsigned int> lod(index_count);
|
|
float lod_error = 0.f;
|
|
lod.resize(meshopt_simplify(
|
|
&lod[0],
|
|
indices.data(),
|
|
index_count,
|
|
&vertices[0].x,
|
|
vertex_count,
|
|
sizeof(XMFLOAT3),
|
|
target_index_count,
|
|
target_error,
|
|
options,
|
|
&lod_error)
|
|
);
|
|
|
|
index_count = lod.size();
|
|
unindexed_vertex_count = vertices.size();
|
|
size_t vertex_count = meshopt_generateVertexRemap(&remap[0], lod.data(), index_count, &vertices[0], unindexed_vertex_count, sizeof(XMFLOAT3));
|
|
|
|
wi::vector<uint32_t> simplified_indices(index_count);
|
|
wi::vector<XMFLOAT3> simplified_vertices(vertex_count);
|
|
|
|
meshopt_remapIndexBuffer(simplified_indices.data(), lod.data(), index_count, &remap[0]);
|
|
meshopt_remapVertexBuffer(simplified_vertices.data(), &vertices[0], unindexed_vertex_count, sizeof(XMFLOAT3), &remap[0]);
|
|
|
|
std::swap(indices, simplified_indices);
|
|
std::swap(vertices, simplified_vertices);
|
|
}
|
|
|
|
meshopt_optimizeVertexCache(indices.data(), indices.data(), index_count, vertex_count);
|
|
meshopt_optimizeOverdraw(indices.data(), indices.data(), index_count, &vertices[0].x, vertex_count, sizeof(XMFLOAT3), 1.05f);
|
|
meshopt_optimizeVertexFetch(vertices.data(), indices.data(), index_count, vertices.data(), vertex_count, sizeof(XMFLOAT3));
|
|
}
|
|
|
|
}
|