sped up sphere creation, added some chunk stuff. removed serialization that didnt work.
This commit is contained in:
@@ -69,6 +69,7 @@ void setup(defaults config, VoxelGrid& grid) {
|
|||||||
uint8_t threshold = 0.1 * 255;
|
uint8_t threshold = 0.1 * 255;
|
||||||
grid.resize(config.gridWidth, config.gridHeight, config.gridDepth);
|
grid.resize(config.gridWidth, config.gridHeight, config.gridDepth);
|
||||||
std::cout << "Generating grid of size " << config.gridWidth << "x" << config.gridHeight << "x" << config.gridDepth << std::endl;
|
std::cout << "Generating grid of size " << config.gridWidth << "x" << config.gridHeight << "x" << config.gridDepth << std::endl;
|
||||||
|
|
||||||
size_t rValw = config.gridWidth / 64;
|
size_t rValw = config.gridWidth / 64;
|
||||||
size_t rValh = config.gridHeight / 64;
|
size_t rValh = config.gridHeight / 64;
|
||||||
size_t rVald = config.gridDepth / 64;
|
size_t rVald = config.gridDepth / 64;
|
||||||
@@ -81,6 +82,11 @@ void setup(defaults config, VoxelGrid& grid) {
|
|||||||
size_t aValw = config.gridWidth / 8;
|
size_t aValw = config.gridWidth / 8;
|
||||||
size_t aValh = config.gridHeight / 8;
|
size_t aValh = config.gridHeight / 8;
|
||||||
size_t aVald = config.gridDepth / 8;
|
size_t aVald = config.gridDepth / 8;
|
||||||
|
|
||||||
|
// Collect all positions to set
|
||||||
|
std::vector<Vec3i> positions;
|
||||||
|
positions.reserve(config.gridWidth * config.gridHeight * config.gridDepth / 10); // Estimate 10% will be active
|
||||||
|
|
||||||
for (int z = 0; z < config.gridDepth; ++z) {
|
for (int z = 0; z < config.gridDepth; ++z) {
|
||||||
if (z % 64 == 0) {
|
if (z % 64 == 0) {
|
||||||
std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl;
|
std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl;
|
||||||
@@ -93,13 +99,24 @@ void setup(defaults config, VoxelGrid& grid) {
|
|||||||
uint8_t b = config.noise.permute(Vec3f(static_cast<float>(x) * bValw, static_cast<float>(y) * bValh, static_cast<float>(z) * bVald)) * 255;
|
uint8_t b = config.noise.permute(Vec3f(static_cast<float>(x) * bValw, static_cast<float>(y) * bValh, static_cast<float>(z) * bVald)) * 255;
|
||||||
uint8_t a = config.noise.permute(Vec3f(static_cast<float>(x) * aValw, static_cast<float>(y) * aValh, static_cast<float>(z) * aVald)) * 255;
|
uint8_t a = config.noise.permute(Vec3f(static_cast<float>(x) * aValw, static_cast<float>(y) * aValh, static_cast<float>(z) * aVald)) * 255;
|
||||||
if (a > threshold) {
|
if (a > threshold) {
|
||||||
grid.set(Vec3i(x, y, z), true, Vec3ui8(r,g,b));
|
positions.emplace_back(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process in batches every few layers to manage memory
|
||||||
|
if (z % 8 == 0 && !positions.empty()) {
|
||||||
|
grid.setBatch(positions, true, Vec3ui8(255, 255, 255), 1.0f);
|
||||||
|
positions.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process any remaining positions
|
||||||
|
if (!positions.empty()) {
|
||||||
|
grid.setBatch(positions, true, Vec3ui8(255, 255, 255), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "Noise grid generation complete!" << std::endl;
|
std::cout << "Noise grid generation complete!" << std::endl;
|
||||||
grid.serializeToFile("output/gridsave.ygg3");
|
|
||||||
grid.printStats();
|
grid.printStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +132,10 @@ void createGreenSphere(defaults config, VoxelGrid& grid) {
|
|||||||
|
|
||||||
int progressStep = std::max(1, config.gridDepth / 10);
|
int progressStep = std::max(1, config.gridDepth / 10);
|
||||||
|
|
||||||
|
// Collect all positions to set
|
||||||
|
std::vector<Vec3i> positions;
|
||||||
|
positions.reserve(static_cast<size_t>(4.0/3.0 * M_PI * sphereConfig.radius * sphereConfig.radius * sphereConfig.radius));
|
||||||
|
|
||||||
for (int z = 0; z < config.gridDepth; ++z) {
|
for (int z = 0; z < config.gridDepth; ++z) {
|
||||||
if (z % progressStep == 0) {
|
if (z % progressStep == 0) {
|
||||||
std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl;
|
std::cout << "Processing layer " << z << " of " << config.gridDepth << std::endl;
|
||||||
@@ -148,10 +169,21 @@ void createGreenSphere(defaults config, VoxelGrid& grid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldSet) {
|
if (shouldSet) {
|
||||||
grid.set(Vec3i(x, y, z), true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25);
|
positions.emplace_back(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process in batches to manage memory
|
||||||
|
if (z % 4 == 0 && !positions.empty()) {
|
||||||
|
grid.setBatch(positions, true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25f);
|
||||||
|
positions.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process any remaining positions
|
||||||
|
if (!positions.empty()) {
|
||||||
|
grid.setBatch(positions, true, Vec3ui8(sphereConfig.r, sphereConfig.g, sphereConfig.b), 0.25f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Green sphere generation complete!" << std::endl;
|
std::cout << "Green sphere generation complete!" << std::endl;
|
||||||
@@ -159,7 +191,6 @@ void createGreenSphere(defaults config, VoxelGrid& grid) {
|
|||||||
<< sphereConfig.centerY << ", " << sphereConfig.centerZ << ")" << std::endl;
|
<< sphereConfig.centerY << ", " << sphereConfig.centerZ << ")" << std::endl;
|
||||||
std::cout << "Sphere radius: " << sphereConfig.radius << std::endl;
|
std::cout << "Sphere radius: " << sphereConfig.radius << std::endl;
|
||||||
|
|
||||||
grid.serializeToFile("output/sphere_grid.ygg3");
|
|
||||||
grid.printStats();
|
grid.printStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,14 +349,14 @@ int main() {
|
|||||||
defaults config;
|
defaults config;
|
||||||
VoxelGrid grid;
|
VoxelGrid grid;
|
||||||
bool gridInitialized = false;
|
bool gridInitialized = false;
|
||||||
auto supposedGrid = VoxelGrid::deserializeFromFile("output/gridsave.ygg3");
|
//auto supposedGrid = VoxelGrid::deserializeFromFile("output/gridsave.ygg3");
|
||||||
if (supposedGrid) {
|
// if (supposedGrid) {
|
||||||
grid = std::move(*supposedGrid);
|
// grid = std::move(*supposedGrid);
|
||||||
gridInitialized = true;
|
// gridInitialized = true;
|
||||||
config.gridDepth = grid.getDepth();
|
// config.gridDepth = grid.getDepth();
|
||||||
config.gridHeight = grid.getHeight();
|
// config.gridHeight = grid.getHeight();
|
||||||
config.gridWidth = grid.getWidth();
|
// config.gridWidth = grid.getWidth();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,93 +7,93 @@
|
|||||||
|
|
||||||
constexpr char magic[4] = {'Y', 'G', 'G', '3'};
|
constexpr char magic[4] = {'Y', 'G', 'G', '3'};
|
||||||
|
|
||||||
inline bool VoxelGrid::serializeToFile(const std::string& filename) {
|
// inline bool VoxelGrid::serializeToFile(const std::string& filename) {
|
||||||
std::ofstream file(filename, std::ios::binary);
|
// std::ofstream file(filename, std::ios::binary);
|
||||||
if (!file.is_open()) {
|
// if (!file.is_open()) {
|
||||||
std::cerr << "failed to open file (serializeToFile): " << filename << std::endl;
|
// std::cerr << "failed to open file (serializeToFile): " << filename << std::endl;
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
file.write(magic, 4);
|
// file.write(magic, 4);
|
||||||
int dims[3] = {gridSize.x, gridSize.y, gridSize.z};
|
// int dims[3] = {gridSize.x, gridSize.y, gridSize.z};
|
||||||
file.write(reinterpret_cast<const char*>(dims), sizeof(dims));
|
// file.write(reinterpret_cast<const char*>(dims), sizeof(dims));
|
||||||
size_t voxelCount = voxels.size();
|
// size_t voxelCount = voxels.size();
|
||||||
file.write(reinterpret_cast<const char*>(&voxelCount), sizeof(voxelCount));
|
// file.write(reinterpret_cast<const char*>(&voxelCount), sizeof(voxelCount));
|
||||||
for (const Voxel& voxel : voxels) {
|
// for (const Voxel& voxel : voxels) {
|
||||||
auto write_member = [&file](const auto& member) {
|
// auto write_member = [&file](const auto& member) {
|
||||||
file.write(reinterpret_cast<const char*>(&member), sizeof(member));
|
// file.write(reinterpret_cast<const char*>(&member), sizeof(member));
|
||||||
};
|
// };
|
||||||
|
|
||||||
std::apply([&write_member](const auto&... members) {
|
// std::apply([&write_member](const auto&... members) {
|
||||||
(write_member(members), ...);
|
// (write_member(members), ...);
|
||||||
}, voxel.members());
|
// }, voxel.members());
|
||||||
}
|
// }
|
||||||
|
|
||||||
file.close();
|
// file.close();
|
||||||
return !file.fail();
|
// return !file.fail();
|
||||||
}
|
// }
|
||||||
|
|
||||||
std::unique_ptr<VoxelGrid> VoxelGrid::deserializeFromFile(const std::string& filename) {
|
// std::unique_ptr<VoxelGrid> VoxelGrid::deserializeFromFile(const std::string& filename) {
|
||||||
std::ifstream file(filename, std::ios::binary);
|
// std::ifstream file(filename, std::ios::binary);
|
||||||
if (!file.is_open()) {
|
// if (!file.is_open()) {
|
||||||
std::cerr << "failed to open file (deserializeFromFile): " << filename << std::endl;
|
// std::cerr << "failed to open file (deserializeFromFile): " << filename << std::endl;
|
||||||
return nullptr;
|
// return nullptr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Read and verify magic number
|
// // Read and verify magic number
|
||||||
char filemagic[4];
|
// char filemagic[4];
|
||||||
file.read(filemagic, 4);
|
// file.read(filemagic, 4);
|
||||||
if (std::strncmp(filemagic, magic, 4) != 0) {
|
// if (std::strncmp(filemagic, magic, 4) != 0) {
|
||||||
std::cerr << "Error: Invalid file format or corrupted file (expected "
|
// std::cerr << "Error: Invalid file format or corrupted file (expected "
|
||||||
<< magic[0] << magic[1] << magic[2] << magic[3]
|
// << magic[0] << magic[1] << magic[2] << magic[3]
|
||||||
<< ", got " << filemagic[0] << filemagic[1] << filemagic[2] << filemagic[3]
|
// << ", got " << filemagic[0] << filemagic[1] << filemagic[2] << filemagic[3]
|
||||||
<< ")" << std::endl;
|
// << ")" << std::endl;
|
||||||
return nullptr;
|
// return nullptr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create output grid
|
// // Create output grid
|
||||||
auto outgrid = std::make_unique<VoxelGrid>();
|
// auto outgrid = std::make_unique<VoxelGrid>();
|
||||||
|
|
||||||
// Read grid dimensions
|
// // Read grid dimensions
|
||||||
int dims[3];
|
// int dims[3];
|
||||||
file.read(reinterpret_cast<char*>(dims), sizeof(dims));
|
// file.read(reinterpret_cast<char*>(dims), sizeof(dims));
|
||||||
|
|
||||||
// Resize grid
|
// // Resize grid
|
||||||
outgrid->gridSize = Vec3i(dims[0], dims[1], dims[2]);
|
// outgrid->gridSize = Vec3i(dims[0], dims[1], dims[2]);
|
||||||
outgrid->voxels.resize(dims[0] * dims[1] * dims[2]);
|
// outgrid->voxels.resize(dims[0] * dims[1] * dims[2]);
|
||||||
|
|
||||||
// Read voxel count
|
// // Read voxel count
|
||||||
size_t voxelCount;
|
// size_t voxelCount;
|
||||||
file.read(reinterpret_cast<char*>(&voxelCount), sizeof(voxelCount));
|
// file.read(reinterpret_cast<char*>(&voxelCount), sizeof(voxelCount));
|
||||||
|
|
||||||
// Verify voxel count matches grid dimensions
|
// // Verify voxel count matches grid dimensions
|
||||||
size_t expectedCount = static_cast<size_t>(dims[0]) * dims[1] * dims[2];
|
// size_t expectedCount = static_cast<size_t>(dims[0]) * dims[1] * dims[2];
|
||||||
if (voxelCount != expectedCount) {
|
// if (voxelCount != expectedCount) {
|
||||||
std::cerr << "Error: Voxel count mismatch. Expected " << expectedCount
|
// std::cerr << "Error: Voxel count mismatch. Expected " << expectedCount
|
||||||
<< ", found " << voxelCount << std::endl;
|
// << ", found " << voxelCount << std::endl;
|
||||||
return nullptr;
|
// return nullptr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Read all voxels
|
// // Read all voxels
|
||||||
for (size_t i = 0; i < voxelCount; ++i) {
|
// for (size_t i = 0; i < voxelCount; ++i) {
|
||||||
auto members = outgrid->voxels[i].members();
|
// auto members = outgrid->voxels[i].members();
|
||||||
|
|
||||||
std::apply([&file](auto&... member) {
|
// std::apply([&file](auto&... member) {
|
||||||
((file.read(reinterpret_cast<char*>(&member), sizeof(member))), ...);
|
// ((file.read(reinterpret_cast<char*>(&member), sizeof(member))), ...);
|
||||||
}, members);
|
// }, members);
|
||||||
}
|
// }
|
||||||
|
|
||||||
file.close();
|
// file.close();
|
||||||
|
|
||||||
if (file.fail() && !file.eof()) {
|
// if (file.fail() && !file.eof()) {
|
||||||
std::cerr << "Error: Failed to read from file: " << filename << std::endl;
|
// std::cerr << "Error: Failed to read from file: " << filename << std::endl;
|
||||||
return nullptr;
|
// return nullptr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
std::cout << "Successfully loaded grid: " << dims[0] << " x "
|
// std::cout << "Successfully loaded grid: " << dims[0] << " x "
|
||||||
<< dims[1] << " x " << dims[2] << std::endl;
|
// << dims[1] << " x " << dims[2] << std::endl;
|
||||||
|
|
||||||
return outgrid;
|
// return outgrid;
|
||||||
}
|
// }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -113,12 +113,276 @@ struct Camera {
|
|||||||
struct Chunk {
|
struct Chunk {
|
||||||
Voxel reprVoxel; //average of all voxels in chunk for LOD rendering
|
Voxel reprVoxel; //average of all voxels in chunk for LOD rendering
|
||||||
std::vector<bool> activeVoxels; //use this to specify active voxels in this chunk.
|
std::vector<bool> activeVoxels; //use this to specify active voxels in this chunk.
|
||||||
//std::vector<Voxel> voxels; //list of all voxels in chunk.
|
std::vector<Voxel> voxels; //list of all voxels in chunk.
|
||||||
std::vector<Chunk> children; //list of all chunks in chunk
|
//std::vector<Chunk> children; //list of all chunks in chunk. for future use.
|
||||||
bool active; //active if any child chunk or child voxel is active. used to efficiently find active voxels by only going down when an active chunk is found.
|
bool active; //active if any child chunk or child voxel is active. used to efficiently find active voxels by only going down when an active chunk is found.
|
||||||
int chunkSize; //should be (CHUNK_THRESHOLD/2) * 2 ^ depth I think. (ie: 1 depth will be (16/2)*(2^1) or 16, second will be (16/2)*(2^2) or 8*4=32)
|
int chunkSize; //should be (CHUNK_THRESHOLD/2) * 2 ^ depth I think. (ie: 1 depth will be (16/2)*(2^1) or 16, second will be (16/2)*(2^2) or 8*4=32)
|
||||||
Vec3i minCorner; //position of chunk in world space.
|
Vec3i minCorner; //position of chunk in world space.
|
||||||
|
Vec3i maxCorner;
|
||||||
int depth; //number of parent/child traversals to get here.
|
int depth; //number of parent/child traversals to get here.
|
||||||
|
|
||||||
|
Chunk() : active(false), chunkSize(0), depth(0) {}
|
||||||
|
|
||||||
|
Chunk(const Vec3i& minCorner, int chunkSize, int depth = 0) : minCorner(minCorner), chunkSize(chunkSize),
|
||||||
|
depth(depth), maxCorner(minCorner + chunkSize), active(false) {
|
||||||
|
int voxelCount = chunkSize * chunkSize * chunkSize;
|
||||||
|
activeVoxels.resize(voxelCount, false);
|
||||||
|
voxels.resize(voxelCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert world position to local chunk index
|
||||||
|
Vec3i worldToLocal(const Vec3i& worldPos) const {
|
||||||
|
return worldPos - minCorner;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3i localToWorld(const Vec3i& localPos) const {
|
||||||
|
return localPos + minCorner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert local chunk position to index
|
||||||
|
size_t mortonIndex(const Vec3i& localPos) const {
|
||||||
|
uint8_t x = static_cast<uint8_t>(localPos.x) & 0x0F;
|
||||||
|
uint8_t y = static_cast<uint8_t>(localPos.y) & 0x0F;
|
||||||
|
uint8_t z = static_cast<uint8_t>(localPos.z) & 0x0F;
|
||||||
|
|
||||||
|
// Spread 4 bits using lookup tables or bit operations
|
||||||
|
// For 4 bits: x = abcd -> a000b000c000d
|
||||||
|
uint16_t xx = x;
|
||||||
|
xx = (xx | (xx << 4)) & 0x0F0F; // 0000abcd -> 0000abcd0000abcd
|
||||||
|
xx = (xx | (xx << 2)) & 0x3333; // -> 00ab00cd00ab00cd
|
||||||
|
xx = (xx | (xx << 1)) & 0x5555; // -> 0a0b0c0d0a0b0c0d
|
||||||
|
|
||||||
|
uint16_t yy = y;
|
||||||
|
yy = (yy | (yy << 4)) & 0x0F0F;
|
||||||
|
yy = (yy | (yy << 2)) & 0x3333;
|
||||||
|
yy = (yy | (yy << 1)) & 0x5555;
|
||||||
|
|
||||||
|
uint16_t zz = z;
|
||||||
|
zz = (zz | (zz << 4)) & 0x0F0F;
|
||||||
|
zz = (zz | (zz << 2)) & 0x3333;
|
||||||
|
zz = (zz | (zz << 1)) & 0x5555;
|
||||||
|
|
||||||
|
// Combine: x in bit 0, y in bit 1, z in bit 2
|
||||||
|
return xx | (yy << 1) | (zz << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get voxel at world position
|
||||||
|
Voxel& getWVoxel(const Vec3i& worldPos) {
|
||||||
|
Vec3i local = worldToLocal(worldPos);
|
||||||
|
return voxels[mortonIndex(local)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Voxel& getWVoxel(const Vec3i& worldPos) const {
|
||||||
|
Vec3i local = worldToLocal(worldPos);
|
||||||
|
return voxels[mortonIndex(local)];
|
||||||
|
}
|
||||||
|
|
||||||
|
Voxel& getLVoxel(const Vec3i& localPos) {
|
||||||
|
return voxels[mortonIndex(localPos)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Voxel& getLVoxel(const Vec3i& localPos) const {
|
||||||
|
return voxels[mortonIndex(localPos)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set voxel at world position
|
||||||
|
void setVoxel(const Vec3i& worldPos, const Voxel& voxel) {
|
||||||
|
Vec3i local = worldToLocal(worldPos);
|
||||||
|
size_t idx = mortonIndex(local);
|
||||||
|
voxels[idx] = voxel;
|
||||||
|
activeVoxels[idx] = voxel.active;
|
||||||
|
|
||||||
|
// Update chunk active status
|
||||||
|
if (voxel.active && !active) {
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a world position is inside this chunk
|
||||||
|
bool contains(const Vec3i& worldPos) const {
|
||||||
|
return worldPos.AllGTE(minCorner) && worldPos.AllLT(maxCorner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a point is inside this chunk
|
||||||
|
bool contains(const Vec3f& worldPos) const {
|
||||||
|
return worldPos.AllGTE(minCorner.toFloat()) && worldPos.AllLT(maxCorner.toFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ray bypass - calculate where ray exits this chunk
|
||||||
|
bool rayBypass(const Vec3f& rayOrigin, const Vec3f& rayDir, float& tExit) const {
|
||||||
|
Vec3f invDir = rayDir.safeInverse();
|
||||||
|
Vec3f t1 = (minCorner.toFloat() - rayOrigin) * invDir;
|
||||||
|
Vec3f t2 = (maxCorner.toFloat() - rayOrigin) * invDir;
|
||||||
|
|
||||||
|
Vec3f tMin = t1.min(t2);
|
||||||
|
Vec3f tMax = t1.max(t2);
|
||||||
|
|
||||||
|
float tNear = tMin.maxComp();
|
||||||
|
tExit = tMax.minComp();
|
||||||
|
|
||||||
|
return tMax >= tMin && tMax >= 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ray traverse within this chunk
|
||||||
|
bool rayTraverse(const Vec3f& entryPoint, const Vec3f& exitPoint,
|
||||||
|
Voxel& outVoxel, std::vector<size_t>& hitIndices) const {
|
||||||
|
Vec3f ray = exitPoint - entryPoint;
|
||||||
|
|
||||||
|
// Initialize DDA algorithm
|
||||||
|
Vec3i cv = entryPoint.floorToI();
|
||||||
|
Vec3i lv = exitPoint.floorToI();
|
||||||
|
|
||||||
|
// Clamp to chunk bounds
|
||||||
|
cv = cv.max(minCorner).min(maxCorner - Vec3i(1, 1, 1));
|
||||||
|
lv = lv.max(minCorner).min(maxCorner - Vec3i(1, 1, 1));
|
||||||
|
|
||||||
|
Vec3<int8_t> step = Vec3<int8_t>(
|
||||||
|
ray.x >= 0 ? 1 : -1,
|
||||||
|
ray.y >= 0 ? 1 : -1,
|
||||||
|
ray.z >= 0 ? 1 : -1
|
||||||
|
);
|
||||||
|
|
||||||
|
Vec3f tDelta = Vec3f(
|
||||||
|
ray.x != 0 ? std::abs(1.0f / ray.x) : INF,
|
||||||
|
ray.y != 0 ? std::abs(1.0f / ray.y) : INF,
|
||||||
|
ray.z != 0 ? std::abs(1.0f / ray.z) : INF
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate initial tMax values
|
||||||
|
Vec3f tMax;
|
||||||
|
if (ray.x > 0) {
|
||||||
|
tMax.x = (std::floor(entryPoint.x) + 1.0f - entryPoint.x) / ray.x;
|
||||||
|
} else if (ray.x < 0) {
|
||||||
|
tMax.x = (entryPoint.x - std::floor(entryPoint.x)) / -ray.x;
|
||||||
|
} else {
|
||||||
|
tMax.x = INF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ray.y > 0) {
|
||||||
|
tMax.y = (std::floor(entryPoint.y) + 1.0f - entryPoint.y) / ray.y;
|
||||||
|
} else if (ray.y < 0) {
|
||||||
|
tMax.y = (entryPoint.y - std::floor(entryPoint.y)) / -ray.y;
|
||||||
|
} else {
|
||||||
|
tMax.y = INF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ray.z > 0) {
|
||||||
|
tMax.z = (std::floor(entryPoint.z) + 1.0f - entryPoint.z) / ray.z;
|
||||||
|
} else if (ray.z < 0) {
|
||||||
|
tMax.z = (entryPoint.z - std::floor(entryPoint.z)) / -ray.z;
|
||||||
|
} else {
|
||||||
|
tMax.z = INF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear hit indices
|
||||||
|
hitIndices.clear();
|
||||||
|
|
||||||
|
// DDA traversal within chunk
|
||||||
|
while (cv != lv && contains(cv)) {
|
||||||
|
Vec3i local = worldToLocal(cv);
|
||||||
|
size_t idx = mortonIndex(local);
|
||||||
|
|
||||||
|
if (activeVoxels[idx]) {
|
||||||
|
hitIndices.push_back(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find next voxel boundary
|
||||||
|
int axis = (tMax.x < tMax.y) ?
|
||||||
|
((tMax.x < tMax.z) ? 0 : 2) :
|
||||||
|
((tMax.y < tMax.z) ? 1 : 2);
|
||||||
|
|
||||||
|
switch(axis) {
|
||||||
|
case 0:
|
||||||
|
tMax.x += tDelta.x;
|
||||||
|
cv.x += step.x;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tMax.y += tDelta.y;
|
||||||
|
cv.y += step.y;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tMax.z += tDelta.z;
|
||||||
|
cv.z += step.z;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the last voxel
|
||||||
|
if (contains(cv)) {
|
||||||
|
Vec3i local = worldToLocal(cv);
|
||||||
|
size_t idx = mortonIndex(local);
|
||||||
|
if (activeVoxels[idx]) {
|
||||||
|
hitIndices.push_back(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process hits if any
|
||||||
|
if (!hitIndices.empty()) {
|
||||||
|
outVoxel.alpha = 0.0f;
|
||||||
|
outVoxel.active = true;
|
||||||
|
|
||||||
|
for (size_t idx : hitIndices) {
|
||||||
|
if (outVoxel.alpha >= 1.0f) break;
|
||||||
|
|
||||||
|
const Voxel& curVoxel = voxels[idx];
|
||||||
|
float remainingOpacity = 1.0f - outVoxel.alpha;
|
||||||
|
float contribution = curVoxel.alpha * remainingOpacity;
|
||||||
|
|
||||||
|
if (outVoxel.alpha < EPSILON) {
|
||||||
|
outVoxel.color = curVoxel.color;
|
||||||
|
} else {
|
||||||
|
// Blend colors
|
||||||
|
outVoxel.color = Vec3ui8(
|
||||||
|
static_cast<uint8_t>(outVoxel.color.x + (curVoxel.color.x * remainingOpacity)),
|
||||||
|
static_cast<uint8_t>(outVoxel.color.y + (curVoxel.color.y * remainingOpacity)),
|
||||||
|
static_cast<uint8_t>(outVoxel.color.z + (curVoxel.color.z * remainingOpacity))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
outVoxel.alpha += contribution;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build representation voxel (average of all active voxels)
|
||||||
|
void buildReprVoxel() {
|
||||||
|
if (!active) {
|
||||||
|
reprVoxel = Voxel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int activeCount = 0;
|
||||||
|
Vec3f accumColor(0, 0, 0);
|
||||||
|
float accumAlpha = 0.0f;
|
||||||
|
float accumWeight = 0.0f;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < voxels.size(); ++i) {
|
||||||
|
if (activeVoxels[i]) {
|
||||||
|
const Voxel& v = voxels[i];
|
||||||
|
accumColor.x += v.color.x;
|
||||||
|
accumColor.y += v.color.y;
|
||||||
|
accumColor.z += v.color.z;
|
||||||
|
accumAlpha += v.alpha;
|
||||||
|
accumWeight += v.weight;
|
||||||
|
activeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCount > 0) {
|
||||||
|
reprVoxel.color = Vec3ui8(
|
||||||
|
static_cast<uint8_t>(accumColor.x / activeCount),
|
||||||
|
static_cast<uint8_t>(accumColor.y / activeCount),
|
||||||
|
static_cast<uint8_t>(accumColor.z / activeCount)
|
||||||
|
);
|
||||||
|
reprVoxel.alpha = accumAlpha / activeCount;
|
||||||
|
reprVoxel.weight = accumWeight / activeCount;
|
||||||
|
reprVoxel.active = true;
|
||||||
|
} else {
|
||||||
|
reprVoxel = Voxel();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class VoxelGrid {
|
class VoxelGrid {
|
||||||
@@ -175,30 +439,17 @@ private:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slab method for AABB intersection
|
|
||||||
bool intersectRayAABB(const Vec3f& origin, const Vec3f& dir, const Vec3f& boxMin, const Vec3f& boxMax, float& tNear, float& tFar) const {
|
bool intersectRayAABB(const Vec3f& origin, const Vec3f& dir, const Vec3f& boxMin, const Vec3f& boxMax, float& tNear, float& tFar) const {
|
||||||
Vec3f invDir(1.0f / dir.x, 1.0f / dir.y, 1.0f / dir.z);
|
Vec3f invDir = dir.safeInverse();
|
||||||
|
|
||||||
float t1 = (boxMin.x - origin.x) * invDir.x;
|
Vec3f t1 = (boxMin - origin) * invDir;
|
||||||
float t2 = (boxMax.x - origin.x) * invDir.x;
|
Vec3f t2 = (boxMax - origin) * invDir;
|
||||||
|
|
||||||
float tMin = std::min(t1, t2);
|
Vec3f tMin = t1.min(t2);
|
||||||
float tMax = std::max(t1, t2);
|
Vec3f tMax = t1.max(t2);
|
||||||
|
|
||||||
t1 = (boxMin.y - origin.y) * invDir.y;
|
tNear = tMin.maxComp();
|
||||||
t2 = (boxMax.y - origin.y) * invDir.y;
|
tFar = tMax.minComp();
|
||||||
|
|
||||||
tMin = std::max(tMin, std::min(t1, t2));
|
|
||||||
tMax = std::min(tMax, std::max(t1, t2));
|
|
||||||
|
|
||||||
t1 = (boxMin.z - origin.z) * invDir.z;
|
|
||||||
t2 = (boxMax.z - origin.z) * invDir.z;
|
|
||||||
|
|
||||||
tMin = std::max(tMin, std::min(t1, t2));
|
|
||||||
tMax = std::min(tMax, std::max(t1, t2));
|
|
||||||
|
|
||||||
tNear = tMin;
|
|
||||||
tFar = tMax;
|
|
||||||
|
|
||||||
return tMax >= tMin && tMax >= 0.0f;
|
return tMax >= tMin && tMax >= 0.0f;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user