Files
CDAG/Research/core/Voxelizer/PVMVoxelizer.cpp

227 lines
8.9 KiB
C++

#include "PVMVoxelizer.h"
#include <algorithm>
#include <numeric>
#include <fstream>
#include "../../PropertyLoader.h"
#include "../OctreeBuilder/SettingsParser.h"
#include "../OctreeBuilder/BaseOctreeBuilder.h"
#include "../../inc/pvm/ddsbase.h"
#include "../../inc/glm/common.hpp"
#include "../ColorHelper.h"
#include "../MathHelper.h"
#include "../StringHelper.h"
#include "../Serializer.h"
#include"../CollectionHelper.h"
struct ColorInterpolator
{
glm::u8vec3 operator()(const glm::u8vec3& color1, glm::u8vec3& color2, float t)
{
//glm::u8vec3 hsv1 = ColorHelper::RGBtoHSV(color1);
//glm::u8vec3 hsv2 = ColorHelper::RGBtoHSV(color2);
//glm::u8vec3 hsvRes;
//hsvRes.r = (unsigned8)MathHelper::lerp(t, (float)hsv1.r, (float)hsv2.r);
//hsvRes.g = (unsigned8)MathHelper::lerp(t, (float)hsv1.g, (float)hsv2.g);
//hsvRes.b = (unsigned8)MathHelper::lerp(t, (float)hsv1.b, (float)hsv2.b);
//return ColorHelper::HSVtoRGB(hsvRes);
return glm::u8vec3(
unsigned8(MathHelper::lerp(t, float(color1.r), float(color2.r))),
unsigned8(MathHelper::lerp(t, float(color1.g), float(color2.g))),
unsigned8(MathHelper::lerp(t, float(color1.b), float(color2.b)))
);
}
};
PVMVoxelizer::PVMVoxelizer() :
mWidth(0), mHeight(0), mDepth(0), mComponents(0), mScale(0), mVolume(std::vector<unsigned8>()), mMinOpacity(0.3f)
{
mTransferFunction = TransferFunction<glm::u8vec3>();
//==============================================
// Bonsai tree TF:
//mTransferFunction.AddNode(0, 0, glm::u8vec3(0));
//mTransferFunction.AddNode(21, 0.f, glm::u8vec3(0, 78, 0));
//mTransferFunction.AddNode(23, 0.7f, glm::u8vec3(0, 78, 0));
//mTransferFunction.AddNode(29, 0.7f, glm::u8vec3(118, 97, 71));
//mTransferFunction.AddNode(255, 1.f, glm::u8vec3(160, 160, 160));
//===============================================
// Baby skull TF:
//mTransferFunction.AddNode(0, 0, glm::u8vec3(0));
//mTransferFunction.AddNode(50, 0.f, glm::u8vec3(255, 0, 0));
//mTransferFunction.AddNode(255, 1.f, glm::u8vec3(160, 160, 160));
//===============================================
// Christmas tree (lossless):
// Weird transfer function to make sure that each value has a unique color
//for (unsigned32 i = 0; i < 256; i++)
//{
// mTransferFunction.AddNode(i * 256, 1.f, glm::u8vec3((i % 2) * 255, (i * 4) % 255, i));
//}
//mTransferFunction.AddNode(0, 0.f, glm::u8vec3(0, 0, 0));
//mTransferFunction.AddNode(4096 * 16, 1.f, glm::u8vec3(255, 255, 255));
// Christmas tree (beautiful)
mTransferFunction.AddNode(0, 0, glm::u8vec3(0));
//mTransferFunction.AddNode(255 * 256, 1.f, glm::u8vec3(255, 255, 255));
mTransferFunction.AddNode(60 * 256, 0.f, glm::u8vec3(0, 50, 0));
mTransferFunction.AddNode(90 * 256, 1.f, glm::u8vec3(0, 50, 0));
mTransferFunction.AddNode(255 * 256, 1.f, glm::u8vec3(255, 255, 0));
//mTransferFunction.AddNode(100 * 256, 1.f, glm::u8vec3(0, 70, 0));
//mTransferFunction.AddNode(130 * 256, 1.f, glm::u8vec3(118, 97, 71));
//mTransferFunction.AddNode(160 * 256, 1.f, glm::u8vec3(255, 255, 0));
//mTransferFunction.AddNode(255 * 256, 1.f, glm::u8vec3(255, 255, 0));
}
PVMVoxelizer::~PVMVoxelizer() {}
bool PVMVoxelizer::Initialize() { return true; }
bool PVMVoxelizer::LoadScene(const std::string& filename)
{
ReadVolumeData(filename, mWidth, mHeight, mDepth, mComponents, mVolume);
mScale = BitHelper::Log2Ceil(std::max(std::max(mWidth, mHeight), mDepth));
// TODO: Error handling
return true;
}
bool PVMVoxelizer::UnloadScene()
{
mWidth = mHeight = mDepth = mComponents = mScale = 0;
mVolume = std::vector<unsigned8>();
return true;
}
std::vector<glm::uvec3> PVMVoxelizer::GetValidCoords(unsigned8 scale)
{
if (scale == 0) return std::vector<glm::uvec3>(1, glm::uvec3(0));
std::vector<glm::uvec3> res;
Voxelize(scale, scale, glm::uvec3(0),
[&](const VoxelInfo& info, bool best) { res.push_back(info.position); },
[](const std::vector<float>& opacities)->float { return *std::max_element(opacities.begin(), opacities.end()); },
[](const std::vector<glm::u8vec3>& colors, const std::vector<float>& opacities)->glm::u8vec3 { return glm::u8vec3(0); });
return res;
}
void PVMVoxelizer::Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder,
bool colors, bool normals, bool reflectivity)
{
Voxelize(scale, partScale, partCoord, nodeAdder,
[](const std::vector<float>& opacities)->float { return (float)CollectionHelper::CalculateMean(opacities); },
[](const std::vector<glm::u8vec3>& colors, const std::vector<float>& opacities)->glm::u8vec3
{
float totalOpacity = CollectionHelper::Sum(opacities);
glm::vec3 avgColor(0);
for (size_t i = 0; i < colors.size(); i++)
avgColor += glm::vec3(colors[i]) * (opacities[i] / totalOpacity);
return glm::u8vec3((unsigned8)avgColor.x, (unsigned8)avgColor.y, (unsigned8)avgColor.z);
});
}
void PVMVoxelizer::Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord,
const std::function<void(const VoxelInfo&, bool best)>& nodeAdder,
const std::function<float(const std::vector<float> values)>& opacityFilter,
const std::function<glm::u8vec3(const std::vector<glm::u8vec3>, const std::vector<float>)>& colorFilter)
{
if (scale > mScale)
printf("Warning: volume data at a lower resolution than the requested scale.");
float opacity;
glm::u8vec3 col;
unsigned valueCount = (unsigned32)BitHelper::Exp2(mComponents * 8);
std::vector<float> opacityCache(valueCount);
std::vector<glm::u8vec3> colorCache(valueCount);
for (unsigned i = 0; i < valueCount; i++)
{
mTransferFunction.Evaluate(i, opacity, col, ColorInterpolator());
opacityCache[i] = opacity;
colorCache[i] = col;
}
unsigned32 resolution = (unsigned32)BitHelper::Exp2(partScale);
unsigned32 filterSize = mScale >= partScale ? (unsigned32)BitHelper::Exp2(mScale - partScale) : 1;
unsigned32 maxValue = 0;
unsigned32 filledCells = 0;
for (unsigned32 x = 0; x < resolution; x++)
{
if (x * filterSize >= mWidth) continue;
for (unsigned32 y = 0; y < resolution; y++)
{
if (y * filterSize >= mHeight) continue;
for (unsigned32 z = 0; z < resolution; z++)
{
if (z * filterSize >= mDepth) continue;
if (partScale < mScale)
{
// Call the filter over the values in the scene part that is enclosed by a single voxel
std::vector<float> opacities;
std::vector<glm::u8vec3> colors;
for (unsigned32 volX = x * filterSize; volX < (x + 1) * filterSize; volX++)
for (unsigned32 volY = y * filterSize; volY < (y + 1) * filterSize; volY++)
for (unsigned32 volZ = z * filterSize; volZ < (z + 1) * filterSize; volZ++)
{
size_t volIdx = (volX + volY * mWidth + volZ * (mWidth * mHeight)) * mComponents;
if (volIdx >= mVolume.size()) continue;
unsigned32 value = 0;
BitHelper::JoinBytesLittleEndian(mVolume, value, volIdx, mComponents);
if (value > maxValue) maxValue = value;
colors.push_back(colorCache[value]);
opacities.push_back(opacityCache[value]);
}
opacity = opacityFilter(opacities);
if (opacity > mMinOpacity)
col = colorFilter(colors, opacities);
}
else
{
glm::uvec3 volCoord(
x + partCoord.x * resolution,
y + partCoord.y * resolution,
z + partCoord.z * resolution);
size_t volIdx= (volCoord.x + volCoord.y * mWidth + volCoord.z * (mWidth * mHeight)) * mComponents;
unsigned32 value = 0;
BitHelper::JoinBytesLittleEndian(mVolume, value, volIdx, mComponents);
if (value > maxValue) maxValue = value;
opacity = opacityCache[value];
col = colorCache[value];
}
if (opacity > mMinOpacity)
{
filledCells++;
VoxelInfo info(glm::uvec3(x, z, y), col, glm::vec3(0), 1.f, opacity);
nodeAdder(info, true);
}
}
}
}
printf("Max value: %u, filled: %u / %u (%f %%)\n", maxValue, filledCells, mWidth * mHeight * mDepth, (float(filledCells) / float(mWidth * mHeight * mDepth)) * 100.f);
}
void PVMVoxelizer::ReadVolumeData(const std::string& filename, unsigned32& width, unsigned32& height, unsigned32& depth, unsigned32& components, std::vector<unsigned8>& data)
{
std::string filetype = filename.substr(filename.length() - 3, 3);
StringHelper::ToUpper(filetype);
if (filetype == "PVM")
{
unsigned char* volumeData = readPVMvolume(filename.c_str(), &width, &height, &depth, &components);
data = std::vector<unsigned8>(width * height * depth * components);
for (size_t i = 0; i < data.size(); i++) data[i] = volumeData[i];
delete volumeData;
}
else if (filetype == "DAT")
{
std::ifstream file(filename, std::ios::binary);
unsigned16 width16;
unsigned16 height16;
unsigned16 depth16;
Serializer<unsigned16>::Deserialize(width16, file);
Serializer<unsigned16>::Deserialize(height16, file);
Serializer<unsigned16>::Deserialize(depth16, file);
width = width16;
height = height16;
depth = depth16;
components = 2;
data = std::vector<unsigned8>(width * height * depth * components);
Serializer<unsigned8*>::Deserialize(&data[0], width * height * depth * components, file);
file.close();
}
}