Initial commit: Final state of the master project
This commit is contained in:
29
Research/core/Voxelizer/BaseVoxelizer.h
Normal file
29
Research/core/Voxelizer/BaseVoxelizer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../Defines.h"
|
||||
#include "VoxelInfo.h"
|
||||
|
||||
class BaseVoxelizer
|
||||
{
|
||||
public:
|
||||
virtual ~BaseVoxelizer() {}
|
||||
|
||||
// Should be called before using the octreebuilder. This method initialize the parts of the voxelizer that are not dependent on the scene.
|
||||
virtual bool Initialize() = 0;
|
||||
|
||||
// Loads the scene at the given filename
|
||||
virtual bool LoadScene(const std::string& filename) = 0;
|
||||
// Unloads the currently loaded scene (freeing up memory)
|
||||
virtual bool UnloadScene() = 0;
|
||||
// Returns all coordinates that should be voxelized at a certain scale.
|
||||
virtual std::vector<glm::uvec3> GetValidCoords(unsigned8 scale) = 0;
|
||||
// Voxelizes the currently loaded scene at the given scale. Scale is given as the log_2 of the resolution (e.g. for 1024x1024x1024 use scale = 10).
|
||||
// Overload can be used to voxelize a certain subsection of the scene.
|
||||
// For each filled voxel that is found, the node adder is called. The boolean in the nodeadder is
|
||||
// used as certain voxelizers can give multiple colors for the same voxels. The "best" boolean can then be used to indicate if this is the best color for a voxel.
|
||||
virtual void Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder,
|
||||
bool colors = true, bool normals = true, bool reflectivity = true) = 0;
|
||||
virtual void Voxelize(unsigned8 scale, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder) { Voxelize(scale, scale, glm::uvec3(0), nodeAdder); }
|
||||
};
|
||||
227
Research/core/Voxelizer/PVMVoxelizer.cpp
Normal file
227
Research/core/Voxelizer/PVMVoxelizer.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
46
Research/core/Voxelizer/PVMVoxelizer.h
Normal file
46
Research/core/Voxelizer/PVMVoxelizer.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_RADIANS
|
||||
#endif
|
||||
#include <vector>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../Hashers.h"
|
||||
#include "../Util/TransferFunction.h"
|
||||
|
||||
#include "BaseVoxelizer.h"
|
||||
|
||||
class PVMVoxelizer : public BaseVoxelizer
|
||||
{
|
||||
public:
|
||||
PVMVoxelizer();
|
||||
virtual ~PVMVoxelizer() override;
|
||||
|
||||
bool Initialize() override;
|
||||
bool LoadScene(const std::string& sceneFileName) override;
|
||||
bool UnloadScene() override;
|
||||
|
||||
std::vector<glm::uvec3> GetValidCoords(unsigned8 scale) override;
|
||||
void Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder,
|
||||
bool colors = true, bool normals = true, bool reflectivity = true) override;
|
||||
// Keep other voxelize overloads from the base
|
||||
using BaseVoxelizer::Voxelize;
|
||||
|
||||
protected:
|
||||
private:
|
||||
static void ReadVolumeData(const std::string& filename, unsigned32& width, unsigned32& height, unsigned32& depth, unsigned32& components, std::vector<unsigned8>& data);
|
||||
|
||||
// Voxelize overload that allows for custom filters. Default is average.
|
||||
void 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);
|
||||
|
||||
unsigned32 mWidth, mHeight, mDepth, mComponents;
|
||||
unsigned8 mScale;
|
||||
std::vector<unsigned8> mVolume;
|
||||
TransferFunction<glm::u8vec3> mTransferFunction;
|
||||
float mMinOpacity;
|
||||
};
|
||||
|
||||
968
Research/core/Voxelizer/TriangleMeshVoxelizer.cpp
Normal file
968
Research/core/Voxelizer/TriangleMeshVoxelizer.cpp
Normal file
@@ -0,0 +1,968 @@
|
||||
#include "TriangleMeshVoxelizer.h"
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
// Singletons
|
||||
#include "../../PropertyLoader.h"
|
||||
#include "../../shaders/ShaderLoader.h"
|
||||
// Imports
|
||||
#include "../../scene/PNG.h"
|
||||
#include "../../scene/Scene.h"
|
||||
#include "../../scene/ObjLoader.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "../IntersectTests.h"
|
||||
#include "../BitHelper.h"
|
||||
|
||||
bool TriangleMeshVoxelizer::Initialize() {
|
||||
if (verbose) printf("Initializing BaseOctreeBuilder... ");
|
||||
Stopwatch watch;
|
||||
watch.Reset();
|
||||
|
||||
if (!InitializeGLFW())
|
||||
return false;
|
||||
|
||||
glewExperimental = GL_TRUE;
|
||||
if (glewInit() != GLEW_OK) {
|
||||
fprintf(stderr, "Failed to initialize GLEW\n");
|
||||
return false;
|
||||
}
|
||||
if (verbose) printf("Initialization took %d ms \n", (int)(watch.GetTime() * 1000));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TriangleMeshVoxelizer::LoadScene(const std::string& sceneFileName)
|
||||
{
|
||||
if (verbose) printf("Reading scene \"%s\"... ", sceneFileName.c_str());
|
||||
Stopwatch watch; watch.Reset();
|
||||
if (!mObjLoader->Load(sceneFileName.c_str(), mScene)) return false;
|
||||
if (verbose) printf("Done in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Prepare OpenGL for rendering
|
||||
LoadScene(mScene);
|
||||
LoadShaders(mScene);
|
||||
|
||||
// Calculate scene details
|
||||
CalculateBoundingBox(mScene);
|
||||
CalculateRootNode();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TriangleMeshVoxelizer::UnloadScene()
|
||||
{
|
||||
// Clear the texture from memory
|
||||
for (Mesh mesh : mScene.meshes)
|
||||
glDeleteTextures(1, &mTextures[mesh.texture]);
|
||||
// Clear the scene
|
||||
mScene = Scene();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<glm::uvec3> TriangleMeshVoxelizer::GetValidCoords(unsigned8 scale)
|
||||
{
|
||||
if (scale == 0) return std::vector<glm::uvec3>(1, glm::uvec3(0));
|
||||
else return CalculateValidSubtrees(scale);
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder, bool colors, bool normals, bool reflectivity)
|
||||
{
|
||||
mReadColors = colors; mReadNormals = normals; mReadReflectivity = reflectivity;
|
||||
|
||||
float stepSize = 1.f / (float)BitHelper::Exp2(scale - partScale);
|
||||
mCurPassRootSize = mRootSize * stepSize;
|
||||
mCurPassGridSize = (unsigned)(BitHelper::Exp2(partScale));
|
||||
|
||||
InitDepthPeel();
|
||||
DepthPeel(partCoord, nodeAdder);
|
||||
TerminateDepthPeel();
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::CalculateBoundingBox(Scene& scene)
|
||||
{
|
||||
// Left = minX
|
||||
sceneLeft = (*std::min_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.x < b.x; })).x;
|
||||
// Right = maxX
|
||||
sceneRight = (*std::max_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.x < b.x; })).x;
|
||||
// Bottom = minY
|
||||
sceneBottom = (*std::min_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.y < b.y; })).y;
|
||||
// Top = maxY
|
||||
sceneTop = (*std::max_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.y < b.y; })).y;
|
||||
// Near = maxZ (OpenGL is weird ;) )
|
||||
sceneNear = (*std::max_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.z < b.z; })).z;
|
||||
// Far = minZ (OpenGL is weird ;) )
|
||||
sceneFar = (*std::min_element(scene.vertices.begin(), scene.vertices.end(), [](glm::vec3 a, glm::vec3 b) { return a.z < b.z; })).z;
|
||||
|
||||
float width = sceneRight - sceneLeft;
|
||||
float height = sceneTop - sceneBottom;
|
||||
float depth = sceneNear - sceneFar;
|
||||
float cubesize = std::max(std::max(width, height), depth);
|
||||
float boundingBoxOffset = cubesize * 0.01f;
|
||||
sceneLeft -= boundingBoxOffset;
|
||||
sceneRight += boundingBoxOffset;
|
||||
sceneBottom -= boundingBoxOffset;
|
||||
sceneTop += boundingBoxOffset;
|
||||
sceneNear += boundingBoxOffset;
|
||||
sceneFar -= boundingBoxOffset;
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::CalculateRootNode()
|
||||
{
|
||||
float width = sceneRight - sceneLeft;
|
||||
float height = sceneTop - sceneBottom;
|
||||
float depth = sceneNear - sceneFar;
|
||||
|
||||
mRootSize = std::max(std::max(width, height), depth);
|
||||
|
||||
// Line below centers the scene (but puts it on the bottom)
|
||||
mRootCenter = glm::vec3(sceneLeft + (width * 0.5f), sceneBottom + (0.5f * mRootSize), sceneFar + (0.5f * depth));
|
||||
|
||||
// Line below places the scene on the left bottom far position (optimal for compression).
|
||||
//mRootCenter = glm::vec3(sceneLeft, sceneBottom, sceneFar) + 0.5f * mRootSize;
|
||||
//mRootSize *= 1.01f;
|
||||
}
|
||||
|
||||
std::vector<glm::uvec3> TriangleMeshVoxelizer::CalculateValidSubtrees(unsigned8 level)
|
||||
{
|
||||
Stopwatch watch; watch.Reset();
|
||||
if (verbose) printf("Calculating valid subtrees... ");
|
||||
unsigned steps = 1 << level;
|
||||
glm::vec3 minPosition = mRootCenter - (mRootSize * 0.5f);
|
||||
glm::vec3 maxPosition = mRootCenter + (mRootSize * 0.5f);
|
||||
glm::vec3 cellSize = glm::vec3(mRootSize) / (float)steps;
|
||||
|
||||
auto uvec3hasher = [&](glm::uvec3 v) -> std::size_t { return (size_t)(v.x + v.y * steps + v.z * steps * steps); };
|
||||
std::unordered_set < glm::uvec3, decltype(uvec3hasher)> validCoords(10, uvec3hasher);
|
||||
// For all triangles in the scene
|
||||
for (size_t i = 0; i + 2 < mScene.indices.size(); i += 3)
|
||||
{
|
||||
unsigned a = mScene.indices[i];
|
||||
unsigned b = mScene.indices[i + 1];
|
||||
unsigned c = mScene.indices[i + 2];
|
||||
glm::vec3 triangle[3] = { mScene.vertices[a], mScene.vertices[b], mScene.vertices[c] };
|
||||
// Build AABB bounding box around triangle:
|
||||
glm::vec3 triangleMin = glm::min(glm::min(triangle[0], triangle[1]), triangle[2]);
|
||||
glm::vec3 triangleMax = glm::max(glm::max(triangle[0], triangle[1]), triangle[2]);
|
||||
// Process only the cells within this bounding box
|
||||
glm::vec3 triangleMinCoordsF = glm::floor((triangleMin - minPosition) / cellSize);
|
||||
glm::uvec3 triangleMinCoords(triangleMinCoordsF.x, triangleMinCoordsF.y, triangleMinCoordsF.z);
|
||||
glm::vec3 triangleMaxCoordsF = glm::ceil((triangleMax - minPosition) / cellSize);
|
||||
glm::uvec3 triangleMaxCoords(triangleMaxCoordsF.x, triangleMaxCoordsF.y, triangleMaxCoordsF.z);
|
||||
// If there is an intersection, add the coord to validCoords
|
||||
for (unsigned x = triangleMinCoords.x; x < triangleMaxCoords.x; x++)
|
||||
for (unsigned y = triangleMinCoords.y; y < triangleMaxCoords.y; y++)
|
||||
for (unsigned z = triangleMinCoords.z; z < triangleMaxCoords.z; z++)
|
||||
{
|
||||
glm::vec3 coordF(x, y, z);
|
||||
glm::vec3 cellMinPosition = minPosition + cellSize * coordF;
|
||||
glm::vec3 cellMaxPosition = cellMinPosition + cellSize;
|
||||
if (IntersectTests::BoxTriangleIntersection(cellMinPosition, cellMaxPosition, triangle))
|
||||
{
|
||||
validCoords.insert(glm::uvec3(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<glm::uvec3> res;
|
||||
for (glm::uvec3 v : validCoords)
|
||||
res.push_back(v);
|
||||
if (verbose) printf("Found %llu / %u valid subtrees in %u ms.\n", (unsigned64)validCoords.size(), steps * steps * steps, (unsigned)(watch.GetTime() * 1000));
|
||||
return res;
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::InitDepthPeel()
|
||||
{
|
||||
Stopwatch watch; watch.Reset();
|
||||
if (verbose) printf("Initializing Depth peeling... ");
|
||||
|
||||
depthData = new std::vector<GLfloat>(mCurPassGridSize * mCurPassGridSize);
|
||||
colorData = new std::vector<GLubyte>(mReadColors ? mCurPassGridSize * mCurPassGridSize * 3 : 0);
|
||||
normalData = new std::vector<GLfloat>(mReadNormals ? mCurPassGridSize * mCurPassGridSize * 3 : 0);
|
||||
angleData = new std::vector<GLfloat>(mReadColors && interpolateColors ? mCurPassGridSize * mCurPassGridSize : 0);
|
||||
reflectivityData = new std::vector<GLfloat>(mReadReflectivity ? mCurPassGridSize * mCurPassGridSize : 0);
|
||||
|
||||
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
|
||||
glGenFramebuffers(1, &mFramebufferName);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
|
||||
|
||||
// Create a framebuffer texture containing the colors of the current frame
|
||||
//if (readColors)
|
||||
//{
|
||||
glGenTextures(1, &mColorTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mColorTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mCurPassGridSize, mCurPassGridSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
//}
|
||||
|
||||
//if (readNormals)
|
||||
//{
|
||||
glGenTextures(1, &mNormalTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mNormalTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mCurPassGridSize, mCurPassGridSize, 0, GL_RGB, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
//}
|
||||
|
||||
// Create a framebuffer texture containing the angle of the vector pointing to the camera and the normal of the triangle
|
||||
//if (readColors & interpolateColors)
|
||||
//{
|
||||
glGenTextures(1, &mAngleTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mAngleTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, mCurPassGridSize, mCurPassGridSize, 0, GL_RED, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
//}
|
||||
|
||||
glGenTextures(1, &mReflectivityTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mReflectivityTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, mCurPassGridSize, mCurPassGridSize, 0, GL_RED, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Create a first texture to contain a depth map
|
||||
glGenTextures(1, &mDepthTexture1);
|
||||
glBindTexture(GL_TEXTURE_2D, mDepthTexture1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, mCurPassGridSize, mCurPassGridSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Create a second texture to contain another depth map
|
||||
glGenTextures(1, &mDepthTexture2);
|
||||
glBindTexture(GL_TEXTURE_2D, mDepthTexture2);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, mCurPassGridSize, mCurPassGridSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Build a depthbuffer for correct depth testing in the framebuffer.
|
||||
glGenRenderbuffers(1, &mDepthRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, mCurPassGridSize, mCurPassGridSize);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
|
||||
|
||||
// Set "depthTexture" as our GL_DEPTH_ATTACHMENT
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mDepthTexture1, 0);
|
||||
//if (readColors)
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mColorTexture, 0);
|
||||
//if (readColors && interpolateColors)
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, mAngleTexture, 0);
|
||||
//if (readNormals)
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, mNormalTexture, 0);
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, mReflectivityTexture, 0);
|
||||
|
||||
// Set the list of draw buffers.
|
||||
GLenum DrawBuffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
|
||||
glDrawBuffers(4, DrawBuffers); // "3" is the size of DrawBuffers
|
||||
|
||||
// Always check that our framebuffer is ok
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
std::cout << "Framebuffer broke. Building the octree failed" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// The fullscreen quad's FBO
|
||||
static const GLfloat g_quad_vertex_buffer_data[] = {
|
||||
-1.0f, -1.0f, 0.0f,
|
||||
1.0f, -1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f,
|
||||
1.0f, -1.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f,
|
||||
};
|
||||
|
||||
glGenBuffers(1, &mQuadVertexbuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexbuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW);
|
||||
|
||||
|
||||
mQuadProgram = mShaderLoader->LoadShader("RenderTexture.vert", "RenderTexture.frag");
|
||||
mImageRenderDepthTexID = glGetUniformLocation(mQuadProgram, "depthTexture");
|
||||
mImageRenderColorTexID = glGetUniformLocation(mQuadProgram, "renderTexture");
|
||||
mImageRenderUseDepthID = glGetUniformLocation(mQuadProgram, "showDepth");
|
||||
|
||||
if (verbose) printf("Depth peeling initialized in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::DepthPeel(glm::uvec3 coord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder) {
|
||||
mMissingNodes.clear();
|
||||
glm::vec3 coordF(coord.x, coord.y, coord.z);
|
||||
mCurPassRootCenter = (mRootCenter - (mRootSize * 0.5f)) + coordF * mCurPassRootSize + mCurPassRootSize * 0.5f;
|
||||
|
||||
unsigned frames = 0;
|
||||
bool done = false;
|
||||
Direction startDir = Direction::Top;
|
||||
|
||||
Direction curDir = startDir;
|
||||
unsigned lastFrames = frames;
|
||||
|
||||
GLuint oldDepthTexture = mDepthTexture2;
|
||||
GLuint curDepthTexture = mDepthTexture1;
|
||||
|
||||
SetDirection(curDir, oldDepthTexture);
|
||||
|
||||
Stopwatch mainWatch; mainWatch.Reset();
|
||||
Stopwatch watch; watch.Reset();
|
||||
Stopwatch frameWatch; frameWatch.Reset();
|
||||
do {
|
||||
if (!pixelsLeft) {
|
||||
// Print details about the last direction
|
||||
if (verbose) printf("Processed in %6d ms, %5.2f FPS\n", (int)(watch.GetTime() * 1000), (double)(frames - lastFrames) / watch.GetTime());
|
||||
watch.Reset(); lastFrames = frames;
|
||||
|
||||
// Move to the next direction
|
||||
unsigned curDirInt = static_cast<unsigned>(curDir);
|
||||
curDir = static_cast<Direction>(++curDirInt % 3);
|
||||
// We're done if we processed all directions
|
||||
if (startDir == curDir)
|
||||
break;
|
||||
SetDirection(curDir, oldDepthTexture);
|
||||
}
|
||||
frames++;
|
||||
|
||||
// ************************************************************************************************************************************ //
|
||||
// Render to our framebuffer //
|
||||
// ************************************************************************************************************************************ //
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
|
||||
glViewport(0, 0, mCurPassGridSize, mCurPassGridSize); // Render on the whole framebuffer, complete from the lower left corner to the upper right
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, curDepthTexture, 0);
|
||||
|
||||
// Clear the screen
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
RenderScene(curDir, oldDepthTexture, (float)mCurPassGridSize, (float)mCurPassGridSize);
|
||||
|
||||
// ************************************************************************************************************************************ //
|
||||
// Render to what the user sees (before processing to improve performance) //
|
||||
// ************************************************************************************************************************************ //
|
||||
do
|
||||
{
|
||||
// Only render the debug image if the last debug image was drawn more than 1/60th of a second ago
|
||||
if (manual || frameWatch.GetTime() > 1.0 / 60.0 || frames == 1)
|
||||
{
|
||||
frameWatch.Reset();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, mWidth, mHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right
|
||||
|
||||
|
||||
// Clear the screen
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if (renderScene)
|
||||
{
|
||||
RenderScene(curDir, oldDepthTexture, (float)mWidth, (float)mHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderDebugImage(curDepthTexture);
|
||||
}
|
||||
|
||||
// Swap buffers
|
||||
glfwSwapBuffers(mWindow);
|
||||
glfwPollEvents();
|
||||
}
|
||||
} while (manual && glfwGetKey(mWindow, GLFW_KEY_SPACE) != GLFW_PRESS);
|
||||
|
||||
// Wait until the user releases the space bar
|
||||
do { glfwPollEvents(); } while (glfwGetKey(mWindow, GLFW_KEY_SPACE) == GLFW_PRESS);
|
||||
|
||||
// ************************************************************************************************************************************ //
|
||||
// Process the current layer, add all voxels to the tree //
|
||||
// ************************************************************************************************************************************ //
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
|
||||
glReadPixels(startTexX, startTexY, texWidth, texHeight, GL_DEPTH_COMPONENT, GL_FLOAT, &depthData->at(0));
|
||||
colorTexWidth = BitHelper::CeilToNearestPowerOfTwo(texWidth);
|
||||
if (mReadColors)
|
||||
{
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
// When reading the colors, read the whole image, since something goes wrong with indices that aren't a power of two it seems...
|
||||
glReadPixels(startTexX, startTexY, colorTexWidth, texHeight, GL_RGB, GL_UNSIGNED_BYTE, &colorData->at(0));
|
||||
}
|
||||
if (mReadColors && interpolateColors)
|
||||
{
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT1);
|
||||
glReadPixels(startTexX, startTexY, colorTexWidth, texHeight, GL_RED, GL_FLOAT, &angleData->at(0));
|
||||
}
|
||||
if (mReadNormals)
|
||||
{
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT2);
|
||||
glReadPixels(startTexX, startTexY, colorTexWidth, texHeight, GL_RGB, GL_FLOAT, &normalData->at(0));
|
||||
}
|
||||
if (mReadReflectivity)
|
||||
{
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT3);
|
||||
glReadPixels(startTexX, startTexY, colorTexWidth, texHeight, GL_RED, GL_FLOAT, &reflectivityData->at(0));
|
||||
}
|
||||
SetGridData(curDir, nodeAdder);
|
||||
|
||||
// Swap the old and new depthtexture, thus advancing to the next layer
|
||||
oldDepthTexture = oldDepthTexture == mDepthTexture1 ? mDepthTexture2 : mDepthTexture1;
|
||||
curDepthTexture = curDepthTexture == mDepthTexture1 ? mDepthTexture2 : mDepthTexture1;
|
||||
firstPass = false;
|
||||
} while (glfwGetKey(mWindow, GLFW_KEY_ESCAPE) != GLFW_PRESS && !done);
|
||||
|
||||
if (verbose) printf("Adding missing nodes...");
|
||||
if (mReadColors && interpolateColors)
|
||||
{
|
||||
// Add the missing nodes in the current subtree
|
||||
for (auto node : mMissingNodes)
|
||||
nodeAdder(node.second, false);
|
||||
}
|
||||
|
||||
if (verbose) printf("Depth peeling took %d ms.\n", (int)(mainWatch.GetTime() * 1000));
|
||||
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::TerminateDepthPeel()
|
||||
{
|
||||
// Cleanup the used resources from GPU
|
||||
glDeleteFramebuffers(1, &mFramebufferName);
|
||||
glDeleteRenderbuffers(1, &mDepthRenderbuffer);
|
||||
glDeleteTextures(1, &mDepthTexture1);
|
||||
glDeleteTextures(1, &mDepthTexture2);
|
||||
/*if (readColors)*/ glDeleteTextures(1, &mColorTexture);
|
||||
/*if (readNormals)*/ glDeleteTextures(1, &mNormalTexture);
|
||||
glDeleteTextures(1, &mAngleTexture);
|
||||
glDeleteTextures(1, &mReflectivityTexture);
|
||||
glDeleteBuffers(1, &mQuadVertexbuffer);
|
||||
glDeleteProgram(mQuadProgram);
|
||||
|
||||
delete depthData;
|
||||
delete colorData;
|
||||
delete normalData;
|
||||
delete angleData;
|
||||
delete reflectivityData;
|
||||
}
|
||||
|
||||
glm::ivec3 TriangleMeshVoxelizer::WorldToGrid(glm::vec3 world)
|
||||
{
|
||||
float halfRootSize = mCurPassRootSize * 0.5f;
|
||||
float left = mCurPassRootCenter.x - halfRootSize;
|
||||
float bottom = mCurPassRootCenter.y - halfRootSize;
|
||||
float far = mCurPassRootCenter.z - halfRootSize;
|
||||
return glm::ivec3(
|
||||
((world.x - left) / mCurPassRootSize) * mCurPassGridSize,
|
||||
((world.y - bottom) / mCurPassRootSize) * mCurPassGridSize,
|
||||
((world.z - far) / mCurPassRootSize) * mCurPassGridSize);
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::SetDirection(Direction dir, unsigned oldDepthTexture)
|
||||
{
|
||||
if (verbose) printf("Processing %s view... ", dir == Top ? "Top " : (dir == Side ? "Side " : "Front"));
|
||||
float one = 1;
|
||||
firstPass = true; // First pass in this direction
|
||||
pixelsLeft = true; // Assume there are at least some pixels in this direction
|
||||
glClearTexImage(oldDepthTexture, 0, GL_DEPTH_COMPONENT32, GL_FLOAT, &one); // Clear the old depth texture, makes sure we don't use old depth peeling values for a new direction
|
||||
// Reset AABB for texture read optimalization (based on scene size within octree)
|
||||
glm::ivec3 lbf = WorldToGrid(glm::vec3(sceneLeft, sceneBottom, sceneFar));
|
||||
glm::ivec3 rtn = WorldToGrid(glm::vec3(sceneRight, sceneTop, sceneNear));
|
||||
// Make sure the coordinates are actually on the grid
|
||||
lbf = glm::clamp(lbf, glm::ivec3(0), glm::ivec3(mCurPassGridSize));
|
||||
rtn = glm::clamp(rtn, glm::ivec3(0), glm::ivec3(mCurPassGridSize));
|
||||
texWidth = dir == Side ? rtn.z - lbf.z : rtn.x - lbf.x;
|
||||
texHeight = dir == Top ? rtn.z - lbf.z : rtn.y - lbf.y;
|
||||
startTexX = dir == Side ? lbf.z : lbf.x;
|
||||
startTexY = dir == Top ? mCurPassGridSize - lbf.z - texHeight : lbf.y;
|
||||
|
||||
|
||||
// Make sure we're looking in the right direction
|
||||
Transform(dir);
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::Transform(Direction dir) {
|
||||
|
||||
float halfRootSize = mCurPassRootSize * 0.5f;
|
||||
// Calculate the edges of the cube in world space
|
||||
float left = mCurPassRootCenter.x - halfRootSize;
|
||||
// float right = mCurPassRootCenter.x + halfRootSize;
|
||||
// float bottom = mCurPassRootCenter.y - halfRootSize;
|
||||
float top = mCurPassRootCenter.y + halfRootSize;
|
||||
float near = mCurPassRootCenter.z + halfRootSize;
|
||||
// float far = mCurPassRootCenter.z - halfRootSize;
|
||||
|
||||
glm::vec3 viewPoint = mCurPassRootCenter;
|
||||
switch (dir)
|
||||
{
|
||||
case Side: viewPoint.x = left; break;
|
||||
case Top: viewPoint.y = top; break;
|
||||
case Front: viewPoint.z = near; break;
|
||||
}
|
||||
|
||||
glm::mat4 mViewMatrix = glm::lookAt(viewPoint, mCurPassRootCenter, dir == Top ? glm::vec3(0, 0, -1) : glm::vec3(0, 1, 0));
|
||||
mProjectionMatrix = glm::ortho(-halfRootSize, +halfRootSize, -halfRootSize, +halfRootSize, 0.f, mCurPassRootSize);
|
||||
mMVP = mProjectionMatrix * mViewMatrix;
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::SetGridData(Direction dir, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder) {
|
||||
// Check if there are any pixels left
|
||||
pixelsLeft = false;
|
||||
unsigned minX = mCurPassGridSize, minY = mCurPassGridSize, maxX = 0, maxY = 0;
|
||||
|
||||
glm::u8vec3 color(0);
|
||||
glm::vec3 normal(0);
|
||||
float reflectivity = 0.f;
|
||||
float depth;
|
||||
float minAngle = 0.25f * sqrtf(2);
|
||||
|
||||
// From OpenGL Documentation:
|
||||
// The data for the ith pixel in the jth row is placed in location j * width + i
|
||||
for (unsigned j = 0; j < texHeight; j++) // row
|
||||
for (unsigned i = 0; i < texWidth; i++) // col
|
||||
{
|
||||
depth = depthData->at(i + j * texWidth);
|
||||
if (depth == 1.f)
|
||||
continue;
|
||||
|
||||
pixelsLeft |= true;
|
||||
|
||||
unsigned x = i + startTexX;
|
||||
unsigned y = j + startTexY;
|
||||
|
||||
// Find min and max for both directions in the image to determine bounding box for next run.
|
||||
minX = std::min(minX, x);
|
||||
minY = std::min(minY, y);
|
||||
maxX = std::max(maxX, x);
|
||||
maxY = std::max(maxY, y);
|
||||
|
||||
// Get the color of the texel
|
||||
unsigned texCoord = i + (j * colorTexWidth);
|
||||
if (mReadColors)
|
||||
{
|
||||
unsigned colorTexCoord = (3 * texCoord);
|
||||
color = glm::u8vec3(colorData->at(colorTexCoord), colorData->at(colorTexCoord + 1), colorData->at(colorTexCoord + 2));
|
||||
}
|
||||
if (mReadNormals)
|
||||
{
|
||||
unsigned normalTexCoord = (3 * texCoord);
|
||||
normal = (glm::vec3(normalData->at(normalTexCoord), normalData->at(normalTexCoord + 1), normalData->at(normalTexCoord + 2)) * 2.0f) - 1.0f;
|
||||
}
|
||||
if (mReadReflectivity)
|
||||
{
|
||||
reflectivity = reflectivityData->at(texCoord);
|
||||
}
|
||||
//color = glm::u8vec3(glm::abs(normal) * 255.0f);
|
||||
|
||||
// Based on the direction, current location and value of the pixel, mark one of the voxels.
|
||||
// We're always looking from zero to higher values in the grid
|
||||
unsigned gridDepth = (unsigned)(depth * (float)mCurPassGridSize);
|
||||
glm::uvec3 coord;
|
||||
switch (dir)
|
||||
{
|
||||
case Side: coord = glm::uvec3(gridDepth, y, x); break;
|
||||
case Top: coord = glm::uvec3(x, mCurPassGridSize - gridDepth - 1, mCurPassGridSize - y - 1); break;
|
||||
case Front: coord = glm::uvec3(x, y, mCurPassGridSize - gridDepth - 1); break;
|
||||
}
|
||||
|
||||
VoxelInfo info(coord, color, normal, 0.f, reflectivity);
|
||||
if (mReadColors && interpolateColors)
|
||||
{
|
||||
info.angle = angleData->at(texCoord);
|
||||
if (info.angle < minAngle)
|
||||
{
|
||||
auto missingNode = mMissingNodes.find(coord);
|
||||
if (missingNode == mMissingNodes.end())
|
||||
mMissingNodes.insert(std::make_pair(coord, info));
|
||||
else if (missingNode->second.angle < info.angle)
|
||||
missingNode->second = info;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
nodeAdder(info, true);
|
||||
}
|
||||
startTexX = minX;
|
||||
startTexY = minY;
|
||||
texWidth = maxX -minX + 1;
|
||||
texHeight = maxY - minY + 1;
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::LoadScene(Scene& scene)
|
||||
{
|
||||
Stopwatch watch;
|
||||
watch.Reset();
|
||||
if (verbose) printf("Loading scene... ");
|
||||
|
||||
TriangleMeshVoxelizer::Load2DTexture(mMissingTexture, interpolateColors);
|
||||
|
||||
for (Mesh mesh : scene.meshes)
|
||||
{
|
||||
TriangleMeshVoxelizer::Load2DTexture(mesh.texture, interpolateColors);
|
||||
}
|
||||
|
||||
//glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glCullFace(GL_FRONT_AND_BACK);
|
||||
|
||||
// Generate and bind vertex array object
|
||||
glGenVertexArrays(1, &mVertexArrayID);
|
||||
glBindVertexArray(mVertexArrayID);
|
||||
|
||||
// Generate vertex buffer
|
||||
glGenBuffers(1, &mVertexBuffer);
|
||||
glm::vec3 zeroVec(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
if (!scene.vertices.empty())
|
||||
glBufferData(GL_ARRAY_BUFFER, scene.vertices.size() * sizeof(glm::vec3), &scene.vertices[0], GL_STATIC_DRAW);
|
||||
else
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), &zeroVec, GL_STATIC_DRAW);
|
||||
|
||||
// Generate texture coordinate buffer
|
||||
glGenBuffers(1, &mTextureBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mTextureBuffer);
|
||||
if (!scene.uvs.empty())
|
||||
glBufferData(GL_ARRAY_BUFFER, scene.uvs.size() * sizeof(glm::vec2), &scene.uvs[0], GL_STATIC_DRAW);
|
||||
else
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2), &zeroVec, GL_STATIC_DRAW);
|
||||
|
||||
// Generate the vertex color buffer
|
||||
glGenBuffers(1, &mVertexColorBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mVertexColorBuffer);
|
||||
if (!scene.colors.empty())
|
||||
glBufferData(GL_ARRAY_BUFFER, scene.colors.size() * sizeof(glm::vec3), &scene.colors[0], GL_STATIC_DRAW);
|
||||
else
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), &zeroVec, GL_STATIC_DRAW);
|
||||
|
||||
// Generate normal buffer
|
||||
glGenBuffers(1, &mNormalBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer);
|
||||
if (!scene.normals.empty())
|
||||
glBufferData(GL_ARRAY_BUFFER, scene.normals.size() * sizeof(glm::vec3), &scene.normals[0], GL_STATIC_DRAW);
|
||||
else
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), &zeroVec, GL_STATIC_DRAW);
|
||||
|
||||
// Generate element buffer
|
||||
glGenBuffers(1, &mElementBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
|
||||
if (!scene.indices.empty())
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, scene.indices.size() * sizeof(unsigned), &scene.indices[0], GL_STATIC_DRAW);
|
||||
|
||||
if (verbose) printf("Scene loaded in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::RenderScene(Direction dir, unsigned oldDepthTexture, float viewPortWidth, float viewPortHeight)
|
||||
{
|
||||
// Enable the shaders
|
||||
glUseProgram(mDefaultProgramID);
|
||||
|
||||
// Bind old depth texture to texture unit 0
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, oldDepthTexture);
|
||||
// Tell the fragment shader to use texture unit 0 for the old depth texture
|
||||
glUniform1i(mOldDepthBufferSampler, 0);
|
||||
glUniform1i(mFirstPass, firstPass);
|
||||
glUniform1f(mDepthmargin, 1.0f / (float)mCurPassGridSize); // Half a cell margin
|
||||
glm::vec3 cameraDir((dir == Side) ? -1.f : 0, (dir == Top) ? -1.f : 0, (dir == Front) ? 1.f : 0);
|
||||
glUniform3f(mCameraDir, cameraDir.x, cameraDir.y, cameraDir.z);
|
||||
glProgramUniformMatrix4fv(mDefaultProgramID, mPropertyLoader->GetIntProperty("shader_MVP"), 1, GL_FALSE, &mMVP[0][0]);
|
||||
|
||||
// Enable vertex attributes array (positions)
|
||||
glEnableVertexAttribArray(mPropertyLoader->GetIntProperty("shader_vertexPosition"));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
glVertexAttribPointer(
|
||||
mPropertyLoader->GetIntProperty("shader_vertexPosition"), // Attribute positions
|
||||
3, // Size
|
||||
GL_FLOAT, // Type
|
||||
GL_FALSE, // Normalized
|
||||
0, // Stride
|
||||
(void*)0 // Array buffer offset
|
||||
);
|
||||
|
||||
// Enable vertex attributes array (texture coordinates)
|
||||
glEnableVertexAttribArray(mPropertyLoader->GetIntProperty("shader_vertexUV"));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mTextureBuffer);
|
||||
glVertexAttribPointer(
|
||||
mPropertyLoader->GetIntProperty("shader_vertexUV"), // Attribute texture coordinates
|
||||
2, // Size
|
||||
GL_FLOAT, // Type
|
||||
GL_FALSE, // Normalized
|
||||
0, // Stride
|
||||
(void*)0 // Array buffer offset
|
||||
);
|
||||
|
||||
// Enable vertex attributes array (vertex colors)
|
||||
glEnableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexColor")));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mVertexColorBuffer);
|
||||
glVertexAttribPointer(
|
||||
stoi(mPropertyLoader->GetProperty("shader_vertexColor")), // Attribute texture coordinates
|
||||
3, // Size
|
||||
GL_FLOAT, // Type
|
||||
GL_FALSE, // Normalized
|
||||
0, // Stride
|
||||
(void*)0 // Array buffer offset
|
||||
);
|
||||
// Enable vertex attributes array (normals)
|
||||
glEnableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexNormal")));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mNormalBuffer);
|
||||
glVertexAttribPointer(
|
||||
stoi(mPropertyLoader->GetProperty("shader_vertexNormal")), // Attribute normals
|
||||
3, // Size
|
||||
GL_FLOAT, // Type
|
||||
GL_FALSE, // Normalized
|
||||
0, // Stride
|
||||
(void*)0 // Array buffer offset
|
||||
);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Render meshes
|
||||
for (Mesh mesh : mScene.meshes) {
|
||||
// Bind texture to texture unit 1
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
auto loadedTexture = mTextures.find(mesh.texture);
|
||||
if (loadedTexture == mTextures.end() || mesh.texture == "")
|
||||
glBindTexture(GL_TEXTURE_2D, mTextures[mMissingTexture]);
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, mTextures[mesh.texture]);
|
||||
// Set texture sampler to texture unit 1
|
||||
glUniform1i(mTextureSampler, 1);
|
||||
glUniform1f(mReflectivity, mesh.reflectivity);
|
||||
|
||||
|
||||
// Draw the element arrays
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementBuffer);
|
||||
glDrawElements(
|
||||
GL_TRIANGLES, // Drawing mode
|
||||
mesh.size, // Element count
|
||||
GL_UNSIGNED_INT, // Type
|
||||
(void*)(mesh.offset * sizeof(unsigned)) // Element array buffer offset
|
||||
);
|
||||
}
|
||||
|
||||
// Disable vertex attributes arrays
|
||||
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexPosition")));
|
||||
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexUV")));
|
||||
glDisableVertexAttribArray(stoi(mPropertyLoader->GetProperty("shader_vertexNormal")));
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::RenderDebugImage(GLuint depthTexture)
|
||||
{
|
||||
// Use our shader
|
||||
glUseProgram(mQuadProgram);
|
||||
|
||||
// Bind our depth texture in Texture Unit 0
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, depthTexture);
|
||||
// Set our sampler to user Texture Unit 0 for depth texture
|
||||
glUniform1i(mImageRenderDepthTexID, 0);
|
||||
|
||||
// Bind our color texture in Texture Unit 1
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, (!mReadColors && mReadNormals) ? mNormalTexture : mColorTexture);
|
||||
glUniform1i(mImageRenderColorTexID, 1);
|
||||
|
||||
glUniform1i(mImageRenderUseDepthID, !mReadColors && !mReadNormals ? 1 : 0);
|
||||
|
||||
// 1rst attribute buffer : vertices
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexbuffer);
|
||||
glVertexAttribPointer(
|
||||
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
|
||||
3, // size
|
||||
GL_FLOAT, // type
|
||||
GL_FALSE, // normalized?
|
||||
0, // stride
|
||||
(void*)0 // array buffer offset
|
||||
);
|
||||
|
||||
// Draw the triangles !
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
|
||||
|
||||
glDisableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Returns the index of the texture to access it in the texture vector
|
||||
//************************************
|
||||
size_t TriangleMeshVoxelizer::Load2DTexture(std::string filename, bool interpolation) {
|
||||
if (textureLoaded(filename))
|
||||
return mTextures[filename];
|
||||
|
||||
unsigned textureID;
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
PNG* png = new PNG(filename.c_str());
|
||||
|
||||
if (png->Initialized())
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, png->W(), png->H(), 0, GL_RGBA, GL_UNSIGNED_BYTE, png->Data());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
if (interpolation)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
delete png;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
mTextures.insert(std::pair<std::string, unsigned>(filename, textureID));
|
||||
|
||||
return mTextures.size() - 1;
|
||||
}
|
||||
|
||||
bool TriangleMeshVoxelizer::textureLoaded(std::string filename) {
|
||||
auto it = mTextures.find(filename);
|
||||
return it != mTextures.end();
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::ReloadShaders(const Scene& scene) {
|
||||
glDeleteProgram(mDefaultProgramID);
|
||||
LoadShaders(scene);
|
||||
}
|
||||
|
||||
void TriangleMeshVoxelizer::LoadShaders(const Scene& scene)
|
||||
{
|
||||
Stopwatch watch; watch.Reset();
|
||||
if (verbose) printf("Loading shaders... ");
|
||||
std::map<std::string, std::string> additionalProperties;
|
||||
// TODO: support scenes that contain texture-based and vertex-color based meshes.
|
||||
if (scene.meshes[0].hasUVs && scene.meshes[0].hasVertexColors)
|
||||
additionalProperties.insert(std::pair<std::string, std::string>("colorType", "TEXTURE_AND_VERTEX_COLORS"));
|
||||
else if (scene.meshes[0].hasVertexColors)
|
||||
additionalProperties.insert(std::pair<std::string, std::string>("colorType", "VERTEX_COLORS"));
|
||||
else if (scene.meshes[0].hasUVs)
|
||||
additionalProperties.insert(std::pair<std::string, std::string>("colorType", "TEXTURE_COLORS"));
|
||||
else
|
||||
additionalProperties.insert(std::pair<std::string, std::string>("colorType", "NO_COLORS"));
|
||||
|
||||
// Initialize and use shader program
|
||||
mDefaultProgramID = mShaderLoader->LoadShader("DepthPeel.vert", "DepthPeel.frag", additionalProperties);
|
||||
|
||||
// Get texture ID
|
||||
mTextureSampler = glGetUniformLocation(mDefaultProgramID, "textureSampler");
|
||||
mReflectivity = glGetUniformLocation(mDefaultProgramID, "reflectivity");
|
||||
mOldDepthBufferSampler = glGetUniformLocation(mDefaultProgramID, "lastDepthMap");
|
||||
mFirstPass = glGetUniformLocation(mDefaultProgramID, "firstPass");
|
||||
mDepthmargin = glGetUniformLocation(mDefaultProgramID, "depthMargin");
|
||||
mCameraDir = glGetUniformLocation(mDefaultProgramID, "cameraDir");
|
||||
if (verbose) printf("Shaders loaded in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
|
||||
TriangleMeshVoxelizer::TriangleMeshVoxelizer() :
|
||||
depthData(NULL),
|
||||
colorData(NULL),
|
||||
normalData(NULL),
|
||||
angleData(NULL),
|
||||
mWindow(NULL)
|
||||
{
|
||||
ObjLoader::Create();
|
||||
mObjLoader = ObjLoader::Instance();
|
||||
ShaderLoader::Create();
|
||||
mShaderLoader = ShaderLoader::Instance();
|
||||
//mShaderLoader->SetShaderPath("../Research/shaders/");
|
||||
PropertyLoader::Create();
|
||||
mPropertyLoader = PropertyLoader::Instance();
|
||||
mPropertyLoader->Clear();
|
||||
mPropertyLoader->AddPropertyFile(std::string("properties.txt"));
|
||||
mPropertyLoader->AddPropertyFile(std::string("shaders/shader_properties.txt"));
|
||||
mPropertyLoader->Update();
|
||||
|
||||
mWidth = mPropertyLoader->GetIntProperty("octreebuilder_debug_width");
|
||||
mHeight = mPropertyLoader->GetIntProperty("octreebuilder_debug_height");
|
||||
manual = mPropertyLoader->GetProperty("octreebuilder_manual") != "0";
|
||||
renderScene = mPropertyLoader->GetProperty("octreebuilder_renderscene") != "0";
|
||||
verbose = mPropertyLoader->GetProperty("octreebuilder_verbose") != "0";
|
||||
interpolateColors = mPropertyLoader->GetProperty("octreebuilder_interpolate_colors") != "0";
|
||||
useCache = mPropertyLoader->GetProperty("octreebuilder_usecache") != "0";
|
||||
mMissingTexture = mPropertyLoader->GetProperty("octreebuilder_missing_material");
|
||||
}
|
||||
|
||||
TriangleMeshVoxelizer::~TriangleMeshVoxelizer() {
|
||||
|
||||
glDeleteBuffers(1, &mVertexBuffer);
|
||||
glDeleteBuffers(1, &mTextureBuffer);
|
||||
glDeleteBuffers(1, &mNormalBuffer);
|
||||
glDeleteBuffers(1, &mVertexColorBuffer);
|
||||
glDeleteBuffers(1, &mElementBuffer);
|
||||
|
||||
glDeleteProgram(mDefaultProgramID);
|
||||
|
||||
std::vector<unsigned> textureIDs;
|
||||
for (std::map<std::string, unsigned>::iterator it = mTextures.begin(); it != mTextures.end(); ++it) {
|
||||
textureIDs.push_back(it->second);
|
||||
}
|
||||
|
||||
if (!textureIDs.empty())
|
||||
glDeleteTextures((GLsizei)textureIDs.size(), &textureIDs[0]);
|
||||
mTextures.clear();
|
||||
glDeleteTextures(1, &mTextureSampler);
|
||||
|
||||
glDeleteVertexArrays(1, &mVertexArrayID);
|
||||
|
||||
glfwTerminate();
|
||||
|
||||
ObjLoader::Destroy();
|
||||
ShaderLoader::Destroy();
|
||||
PropertyLoader::Destroy();
|
||||
}
|
||||
|
||||
|
||||
bool TriangleMeshVoxelizer::Reinitialize(bool fullscreen) {
|
||||
glDeleteBuffers(1, &mVertexBuffer);
|
||||
glDeleteBuffers(1, &mTextureBuffer);
|
||||
glDeleteBuffers(1, &mNormalBuffer);
|
||||
glDeleteBuffers(1, &mElementBuffer);
|
||||
|
||||
glDeleteProgram(mDefaultProgramID);
|
||||
|
||||
std::vector<unsigned> textureIDs;
|
||||
for (std::map<std::string, unsigned>::iterator it = mTextures.begin(); it != mTextures.end(); ++it) {
|
||||
textureIDs.push_back(it->second);
|
||||
}
|
||||
|
||||
glDeleteTextures((GLsizei)textureIDs.size(), &textureIDs[0]);
|
||||
mTextures.clear();
|
||||
glDeleteTextures(1, &mTextureSampler);
|
||||
|
||||
glDeleteVertexArrays(1, &mVertexArrayID);
|
||||
|
||||
glfwTerminate();
|
||||
|
||||
return Initialize();
|
||||
}
|
||||
|
||||
bool TriangleMeshVoxelizer::InitializeGLFW() {
|
||||
// Initialize GLFW
|
||||
if (!glfwInit()) {
|
||||
fprintf(stderr, "Failed to initialize GLFW\n");
|
||||
return false;
|
||||
}
|
||||
glfwWindowHint(GLFW_SAMPLES, std::stoi(mPropertyLoader->GetProperty("anti_aliasing")));
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, std::stoi(mPropertyLoader->GetProperty("opengl_version_major")));
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, std::stoi(mPropertyLoader->GetProperty("opengl_version_minor")));
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
|
||||
// Open a window and create OpenGL context
|
||||
mWindow = glfwCreateWindow(mWidth, mHeight, mPropertyLoader->GetProperty("window_name").c_str(), NULL, NULL);
|
||||
if (mWindow == NULL) {
|
||||
fprintf(stderr, "Failed to open GLFW window.\n");
|
||||
glfwTerminate();
|
||||
return false;
|
||||
}
|
||||
glfwMakeContextCurrent(mWindow);
|
||||
|
||||
glfwSetInputMode(mWindow, GLFW_STICKY_KEYS, GL_TRUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
153
Research/core/Voxelizer/TriangleMeshVoxelizer.h
Normal file
153
Research/core/Voxelizer/TriangleMeshVoxelizer.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
#ifndef GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_RADIANS
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stdio.h>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../inc/gl/glew.h"
|
||||
#include "../../inc/gl/glfw3.h"
|
||||
#include "../../inc/glm/gtc/matrix_transform.hpp"
|
||||
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../scene/Scene.h"
|
||||
#include "BaseVoxelizer.h"
|
||||
|
||||
#include "../Hashers.h"
|
||||
|
||||
class ObjLoader;
|
||||
class ShaderLoader;
|
||||
class PropertyLoader;
|
||||
struct VoxelInfo;
|
||||
|
||||
enum Direction
|
||||
{
|
||||
Top = 0, Side = 1, Front = 2
|
||||
};
|
||||
|
||||
class TriangleMeshVoxelizer : public BaseVoxelizer
|
||||
{
|
||||
public:
|
||||
TriangleMeshVoxelizer();
|
||||
virtual ~TriangleMeshVoxelizer() override;
|
||||
|
||||
bool Initialize() override;
|
||||
bool LoadScene(const std::string& sceneFileName) override;
|
||||
bool UnloadScene() override;
|
||||
std::vector<glm::uvec3> GetValidCoords(unsigned8 scale) override;
|
||||
void Voxelize(unsigned8 scale, unsigned8 partScale, glm::uvec3 partCoord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder,
|
||||
bool colors = true, bool normals = true, bool reflectivity = true) override;
|
||||
// Keep other voxelize overloads from the base
|
||||
using BaseVoxelizer::Voxelize;
|
||||
|
||||
size_t Load2DTexture(std::string filename, bool interpolation);
|
||||
|
||||
// Settings:
|
||||
// If render scene is set to true, the actual scene is rendered instead of the image used for depth peeling
|
||||
bool renderScene;
|
||||
// If manual is set to true, the L and Space buttons should be used to manually advance a layer each time.
|
||||
bool manual;
|
||||
// If verbose mode is turned on, some details on progress will be output
|
||||
bool verbose;
|
||||
// If "interpolate colors" is on, mipmaps will be used to interpolate the colors. This means that the final octree looks more smooth, but might be harder to compress
|
||||
bool interpolateColors;
|
||||
// Boolean indicating if the tree should be regenerated or loaded from cache if available.
|
||||
bool useCache;
|
||||
protected:
|
||||
|
||||
private:
|
||||
bool Reinitialize(bool fullscreen);
|
||||
bool InitializeGLFW();
|
||||
|
||||
bool textureLoaded(std::string filename);
|
||||
std::string mMissingTexture;
|
||||
std::map<std::string, unsigned> mTextures;
|
||||
|
||||
// Loads the scene data into GPU memory
|
||||
void LoadScene(Scene& scene);
|
||||
// Compiles and loads the shaders. Binds values
|
||||
void ReloadShaders(const Scene& scene);
|
||||
void LoadShaders(const Scene& scene);
|
||||
|
||||
// Calculates an axis-aligned bounding box around the scene. Stores results in sceneLeft, sceneRight, sceneTop, sceneBottom, sceneNear, sceneFar.
|
||||
void CalculateBoundingBox(Scene& scene);
|
||||
// Calculates the size and position of the root node based on the current bounding box
|
||||
void CalculateRootNode();
|
||||
// Calculates the coordinates of non-empty subtrees at the given level that need to be evaluated in order to build te tree
|
||||
std::vector<glm::uvec3> CalculateValidSubtrees(unsigned8 level);
|
||||
|
||||
void InitDepthPeel();
|
||||
void DepthPeel(glm::uvec3 coord, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder);
|
||||
void TerminateDepthPeel();
|
||||
|
||||
// Given the depth values in the scene, marks the corresponding voxels in the grid as true
|
||||
void SetGridData(Direction dir, const std::function<void(const VoxelInfo&, bool best)>& nodeAdder);
|
||||
void SetDirection(Direction dir, unsigned oldDepthTexture);
|
||||
void Transform(Direction dir);
|
||||
// Transforms a world coordinate to a coordinate in the 3D grid
|
||||
glm::ivec3 WorldToGrid(glm::vec3 world);
|
||||
|
||||
void RenderScene(Direction dir, unsigned oldDepthTexture, float viewPortWidth, float viewPortHeight);
|
||||
void RenderDebugImage(GLuint curDepthTexture);
|
||||
|
||||
// GLFW window properties
|
||||
unsigned mWidth, mHeight;
|
||||
GLFWwindow* mWindow;
|
||||
|
||||
// Pointers to some singletons used in the application
|
||||
ObjLoader* mObjLoader;
|
||||
ShaderLoader* mShaderLoader;
|
||||
PropertyLoader* mPropertyLoader;
|
||||
|
||||
GLuint mVertexArrayID, mVertexBuffer, mTextureBuffer, mVertexColorBuffer, mNormalBuffer, mElementBuffer, mDefaultProgramID;
|
||||
GLuint mFramebufferName, mColorTexture, mNormalTexture, mDepthTexture1, mDepthTexture2, mAngleTexture, mReflectivityTexture, mDepthRenderbuffer;
|
||||
GLuint mQuadVertexbuffer, mQuadProgram, mImageRenderDepthTexID, mImageRenderColorTexID, mImageRenderUseDepthID;
|
||||
|
||||
// Buffers that are filled using glReadPixels
|
||||
std::vector<GLfloat>* depthData;
|
||||
std::vector<GLubyte>* colorData;
|
||||
std::vector<GLfloat>* normalData;
|
||||
std::vector<GLfloat>* angleData;
|
||||
std::vector<GLfloat>* reflectivityData;
|
||||
|
||||
// The scene
|
||||
Scene mScene;
|
||||
|
||||
// Variables to store details about the size of the scene
|
||||
float sceneLeft, sceneRight, sceneTop, sceneBottom, sceneNear, sceneFar;
|
||||
// The center of the root node in world space (e.g. the center of the object)
|
||||
glm::vec3 mRootCenter;
|
||||
// The edge length of the root node
|
||||
float mRootSize;
|
||||
|
||||
// Data used for calculating the current pass
|
||||
bool mReadColors, mReadNormals, mReadReflectivity;
|
||||
glm::vec3 mCurPassRootCenter;
|
||||
float mCurPassRootSize;
|
||||
unsigned mCurPassGridSize;
|
||||
unsigned8 mCurPassTreeDepth;
|
||||
|
||||
// Variables to store progress for the current direction
|
||||
unsigned startTexX, startTexY, texWidth, texHeight, colorTexWidth;
|
||||
bool firstPass, pixelsLeft;
|
||||
|
||||
// Model, view, project matrices (calculated in Transform method)
|
||||
glm::mat4 mModelMatrix, mViewMatrix, mProjectionMatrix, mMVP;
|
||||
|
||||
// OpenGL bindings
|
||||
GLuint mTextureSampler; // for binding the texture sampler
|
||||
GLuint mReflectivity;
|
||||
GLuint mOldDepthBufferSampler; // For binding the old depth map sampler
|
||||
GLuint mFirstPass; // For binding the bool detailing if this is the first pass
|
||||
GLuint mDepthmargin; // For binding the float detailing the gridsize
|
||||
GLuint mCameraDir; // For binding the camera direction (used for calculating angles)
|
||||
|
||||
bool mStaticUniformsSet; // flag that static uniforms have been set
|
||||
|
||||
std::unordered_map<glm::uvec3, VoxelInfo> mMissingNodes;
|
||||
|
||||
};
|
||||
|
||||
21
Research/core/Voxelizer/VoxelInfo.h
Normal file
21
Research/core/Voxelizer/VoxelInfo.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
|
||||
struct VoxelInfo
|
||||
{
|
||||
public:
|
||||
glm::uvec3 position;
|
||||
float angle;
|
||||
glm::vec3 normal;
|
||||
float reflectivity;
|
||||
glm::u8vec3 color;
|
||||
|
||||
inline VoxelInfo(glm::uvec3 position, glm::u8vec3 color, glm::vec3 normal, float angle, float reflectivity) :
|
||||
position(position), angle(angle), normal(normal), reflectivity(reflectivity), color(color)
|
||||
{}
|
||||
|
||||
bool operator==(const VoxelInfo& info)
|
||||
{
|
||||
return position == info.position && angle == info.angle && color == info.color && normal == info.normal;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user