Initial commit: Final state of the master project
This commit is contained in:
330
Research/core/BitHelper.h
Normal file
330
Research/core/BitHelper.h
Normal file
@@ -0,0 +1,330 @@
|
||||
#pragma once
|
||||
#include "Defines.h"
|
||||
#include "CompileTimeArray.h"
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include "../inc/tbb/parallel_for.h"
|
||||
|
||||
template<size_t x> struct BitCount
|
||||
{
|
||||
// The line below actually counts the number of bits set in x
|
||||
enum {
|
||||
value = (((((x & 0x55) + ((x >> 1) & 0x55)) & 0x33) + ((((x & 0x55) + ((x >> 1) & 0x55)) >> 2) & 0x33)) & 0x0F) +
|
||||
((((((x & 0x55) + ((x >> 1) & 0x55)) & 0x33) + ((((x & 0x55) + ((x >> 1) & 0x55)) >> 2) & 0x33)) >> 4) & 0x0F)
|
||||
};
|
||||
};
|
||||
|
||||
typedef generate_array<256, BitCount>::result BitCountLookupTable;
|
||||
|
||||
namespace BitHelper
|
||||
{
|
||||
inline static unsigned8 GetSet(unsigned64 x)
|
||||
{
|
||||
x = (x & 0x5555555555555555) + ((x >> 1) & 0x5555555555555555); // 0x55 = 01010101
|
||||
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); // 0x33 = 00110011
|
||||
x = (x & 0x0F0F0F0F0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F0F0F0F0F); // 0x0F = 00001111
|
||||
x = (x & 0x00FF00FF00FF00FF) + ((x >> 8) & 0x00FF00FF00FF00FF);
|
||||
x = (x & 0x0000FFFF0000FFFF) + ((x >> 16) & 0x0000FFFF0000FFFF);
|
||||
x = (x & 0x00000000FFFFFFFF) + ((x >> 32) & 0x00000000FFFFFFFF);
|
||||
return (unsigned8)x;
|
||||
}
|
||||
|
||||
inline static unsigned8 GetSet(unsigned32 v)
|
||||
{
|
||||
// From https://graphics.stanford.edu/~seander/bithacks.html, method only uses 12 operations :)
|
||||
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
|
||||
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
|
||||
return (unsigned8)((((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24); // count
|
||||
|
||||
//x -= ((x >> 1) & 0x55555555);
|
||||
//x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
|
||||
//x = (((x >> 4) + x) & 0x0f0f0f0f);
|
||||
//x += (x >> 8);
|
||||
//x += (x >> 16);
|
||||
//return (x & 0x0000003f);
|
||||
}
|
||||
|
||||
inline static unsigned8 GetSet(unsigned16 x)
|
||||
{
|
||||
x = (x & 0x5555) + ((x >> 1) & 0x5555); // 0x55 = 01010101
|
||||
x = (x & 0x3333) + ((x >> 2) & 0x3333); // 0x33 = 00110011
|
||||
x = (x & 0x0F0F) + ((x >> 4) & 0x0F0F); // 0x0F = 00001111
|
||||
x = (x & 0x00FF) + ((x >> 8) & 0x00FF);
|
||||
return (unsigned8)x;
|
||||
}
|
||||
|
||||
inline static unsigned8 GetSet(unsigned8 x)
|
||||
{
|
||||
return BitCountLookupTable::data[x];
|
||||
//// Parallel bit count
|
||||
//x = (x & 0x55) + ((x >> 1) & 0x55); // 0x55 = 01010101
|
||||
//x = (x & 0x33) + ((x >> 2) & 0x33); // 0x33 = 00110011
|
||||
//x = (x & 0x0F) + ((x >> 4) & 0x0F); // 0x0F = 00001111
|
||||
//return x;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T GetLSMaskUntil(const unsigned8 bit) { return (~((T)0)) << bit; }
|
||||
|
||||
template<typename T> inline T GetLSSingleBitMask(const unsigned8 bit) { return (T)1 << bit; }
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8> inline T GetHSSingleBitMask(const unsigned8 bit) { return (T)1 << (bitsInT - 1 - bit); }
|
||||
// Template specialization for HSSingleBitMask
|
||||
template<> inline unsigned8 GetHSSingleBitMask(const unsigned8 pos) { return (unsigned8)0x80 >> pos; }
|
||||
template<> inline unsigned16 GetHSSingleBitMask(const unsigned8 pos) { return (unsigned16)0x8000 >> pos; }
|
||||
template<> inline unsigned32 GetHSSingleBitMask(const unsigned8 pos) { return (unsigned32)0x80000000 >> pos; }
|
||||
template<> inline unsigned64 GetHSSingleBitMask(const unsigned8 pos) { return (unsigned64)0x8000000000000000 >> pos; }
|
||||
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8>
|
||||
inline T GetLSMask(const unsigned8 startIndex, const unsigned8 endIndex) {
|
||||
if (startIndex == endIndex) return (T)0;
|
||||
// Note that s needs to be defined first. If this happens in the same line, I couldn't get the compiler to make sure the size of the datatype is correct
|
||||
T s = ~((T)0);
|
||||
unsigned8 firstShift = bitsInT - endIndex;
|
||||
s <<= firstShift;
|
||||
s >>= firstShift + startIndex;
|
||||
s <<= startIndex;
|
||||
return s;
|
||||
}
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8>
|
||||
inline T GetHSMask(const unsigned8 startIndex, const unsigned8 endIndex) {
|
||||
if (startIndex == endIndex) return (T)0;
|
||||
// Note that s needs to be defined first. If this happens in the same line, I couldn't get the compiler to make sure the size of the datatype is correct
|
||||
T s = ~((T)0);
|
||||
unsigned8 firstShift = bitsInT - endIndex;
|
||||
s >>= firstShift;
|
||||
s <<= firstShift + startIndex;
|
||||
s >>= startIndex;
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8>
|
||||
inline unsigned8 GetHSSetBefore(const T value, const unsigned8 pos) { return GetSet((T)(value << (bitsInT - pos))); }
|
||||
|
||||
template<typename T> inline bool GetHS(const T value, const unsigned8 pos) { return (GetHSSingleBitMask<T>(pos) & value) != 0; }
|
||||
template<typename T> inline bool GetLS(const T value, const unsigned8 pos) { return (BitHelper::GetLSSingleBitMask<T>(pos) & value) != 0; }
|
||||
|
||||
template<typename T> inline T SetLS(const T value, const T& setMask, const T& setValue) { return (value & ~setMask) | (setMask & setValue); }
|
||||
|
||||
template<typename T> inline void SetLS(T& value, const unsigned8 pos) { value |= GetLSSingleBitMask<T>(pos); }
|
||||
template<typename T> inline void SetHS(T& value, const unsigned8 pos) { value |= GetHSSingleBitMask<T>(pos); }
|
||||
|
||||
template<typename T> inline void SetLS(T& value, const unsigned8 pos, const bool& set) {
|
||||
T bitmask = GetLSSingleBitMask<T>(pos);
|
||||
value &= ~bitmask; // Clear the bit
|
||||
if (set) value |= bitmask; // Set it if necessary
|
||||
}
|
||||
template<typename T> inline void SetHS(T& value, const unsigned8 pos, const bool& set)
|
||||
{
|
||||
T bitmask = GetHSSingleBitMask<T>(pos);
|
||||
value &= ~bitmask;
|
||||
if (set) value |= bitmask;
|
||||
}
|
||||
|
||||
inline unsigned32 CeilToNearestPowerOfTwo(unsigned32 v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
inline unsigned32 FloorToNearestPowerOfTwo(unsigned32 value) { return CeilToNearestPowerOfTwo(value) >> 1; }
|
||||
|
||||
template<typename T>
|
||||
inline bool IsPowerOfTwo(T v) { return v && !(v & (v - 1)); }
|
||||
|
||||
// Log2 = the index of the highest significant bit.
|
||||
template<typename T>
|
||||
inline unsigned8 Log2(T v) {
|
||||
unsigned8 r = 0;
|
||||
while (v >>= 1) r++;
|
||||
return (unsigned8)r;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline unsigned8 Log2Ceil(T v) {
|
||||
return Log2(v) + (IsPowerOfTwo(v) ? 0 : 1); // If v is not a power of two, the number was rounded down.
|
||||
}
|
||||
|
||||
// 2^x is equal to taking the number 1 and shifting it x indices.
|
||||
template<typename T>
|
||||
inline unsigned64 Exp2(const T& v) { return ((unsigned64)1) << v; }
|
||||
|
||||
template<typename T>
|
||||
inline T RoundToBytes(const T& v)
|
||||
{
|
||||
if ((v & GetLSMask<T>(0, 3)) != 0) return ((v >> 3) + 1) << 3;
|
||||
else return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8>
|
||||
inline T CircularShiftLeft(T v, T shift) { shift %= bitsInT; return (v << shift) | (v >> (bitsInT - shift)); }
|
||||
|
||||
template<typename T, unsigned8 bitsInT = sizeof(T) * 8>
|
||||
inline T CircularShiftRight(T v, T shift) { shift %= bitsInT; return (v >> shift) | (v << (bitsInT - shift)); }
|
||||
|
||||
template<typename T>
|
||||
inline void SplitInBytesAndMove(T value, std::vector<unsigned8>& destination, size_t offset, size_t size = sizeof(T))
|
||||
{
|
||||
SplitInBytesAndMove<T>(value, &destination[0], offset, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void SplitInBytesAndMove(T value, unsigned8* destination, size_t offset, size_t size = sizeof(T))
|
||||
{
|
||||
for (size_t byte = 0; byte < size; byte++)
|
||||
{
|
||||
size_t lsByte = size - 1 - byte;
|
||||
destination[offset + byte] = (unsigned8)((GetLSMask<T>((unsigned8)(lsByte * 8), (unsigned8)((lsByte + 1) * 8)) & value) >> (lsByte << 3));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::vector<unsigned8> SplitInBytes(T value, size_t size = sizeof(T))
|
||||
{
|
||||
std::vector<unsigned8> res(size);
|
||||
SplitInBytesAndMove(value, res, 0, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JoinBytes(const std::vector<unsigned8>& source, T& dest, size_t offset = 0, size_t size = sizeof(T)) { JoinBytes(&source[0], dest, offset, size); }
|
||||
|
||||
template<typename T>
|
||||
inline void JoinBytes(const unsigned8* source, T& dest, size_t offset = 0, size_t size = sizeof(T))
|
||||
{
|
||||
for (size_t byte = 0; byte < size; byte++)
|
||||
dest |= source[offset + byte] << ((size - byte - 1) * 8);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JoinBytesLittleEndian(const std::vector<unsigned8>& source, T& dest, size_t offset = 0, size_t size = sizeof(T)) { JoinBytes(&source[0], dest, offset, size); }
|
||||
|
||||
template<typename T>
|
||||
inline void JoinBytesLittleEndian(const unsigned8* source, T& dest, size_t offset = 0, size_t size = sizeof(T))
|
||||
{
|
||||
for (size_t byte = 0; byte < size; byte++)
|
||||
dest |= source[offset + (size - byte - 1)] << ((size - byte - 1) * 8);
|
||||
}
|
||||
|
||||
template<typename T, unsigned8 bitsInT = ((unsigned8)sizeof(T) * 8)>
|
||||
std::vector<unsigned8> GetBitMapHS(T mask)
|
||||
{
|
||||
unsigned setBits = BitHelper::GetSet(mask);
|
||||
std::vector<unsigned8> bitMap(setBits);
|
||||
unsigned index = 0;
|
||||
for (unsigned8 i = 0; i < bitsInT; i++)
|
||||
{
|
||||
if (BitHelper::GetHS(mask, i))
|
||||
bitMap[index++] = i;
|
||||
}
|
||||
return bitMap;
|
||||
}
|
||||
|
||||
// Packs the value v tightly, meaning that only the bits that are set in the mask will be kept, and they will be shifted so that they are next to each other
|
||||
template<typename T, unsigned8 bitsInType = sizeof(T) * 8>
|
||||
inline T PackTight(const T& v, const T& mask)
|
||||
{
|
||||
unsigned8 bitOffset = bitsInType - GetSet(mask);
|
||||
T res = 0;
|
||||
for (T i = 0; i < bitsInType; i++)
|
||||
{
|
||||
T curBitMask = GetHSSingleBitMask(i);
|
||||
if ((mask & curBitMask) != 0)
|
||||
{
|
||||
if ((v & curBitMask) != 0) SetHS(res, bitOffset);
|
||||
bitOffset++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T, unsigned8 bitsInType = sizeof(T) * 8>
|
||||
inline T UnpackTight(const T& v, const T& mask)
|
||||
{
|
||||
unsigned8 bitOffset = bitsInType - GetSet(mask);
|
||||
T res = 0;
|
||||
for (unsigned8 i = 0; i < bitsInType; i++)
|
||||
if (GetHS(mask, i))
|
||||
if (GetHS(v, bitOffset++)) SetHS(res, i);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Used to tightly pack a whole vector of values at once. Offset should give the offset in bits within the first packed value
|
||||
template<typename T_packed, typename T_unpacked, unsigned8 bitsInPacked = sizeof(T_packed) * 8, unsigned8 bitsInUnpacked = sizeof(T_unpacked) * 8>
|
||||
std::vector<T_packed> PackTight(const std::vector<T_unpacked>& input, const std::vector<unsigned8>& bitMap, const unsigned8 offset = 0, const size_t startIndex = 0, size_t endIndex = ~size_t(0))
|
||||
{
|
||||
assert(offset < bitsInPacked);
|
||||
if (endIndex > input.size()) endIndex = input.size();
|
||||
size_t setBits = bitMap.size();
|
||||
size_t requiredBits = setBits * (endIndex - startIndex) + offset;
|
||||
size_t outputSize = requiredBits / bitsInPacked + (((requiredBits % bitsInPacked) == 0) ? 0 : 1);
|
||||
std::vector<T_packed> packed(outputSize);
|
||||
// Compress the new data (in parallel per packed byte)
|
||||
tbb::parallel_for(size_t(0), outputSize, [&](size_t i)
|
||||
{
|
||||
size_t startBit = i * bitsInPacked;
|
||||
T_packed cur = 0;
|
||||
for (size_t j = 0; j < bitsInPacked; j++)
|
||||
{
|
||||
size_t bit = startBit + j;
|
||||
if (bit < offset) continue;
|
||||
bit -= offset;
|
||||
size_t sourceIndex = bit / setBits;
|
||||
size_t sourceBitIndex = bit % setBits;
|
||||
size_t inputIndex = startIndex + sourceIndex;
|
||||
if (inputIndex >= endIndex) break;
|
||||
auto source = input[inputIndex];
|
||||
if (GetHS(source, bitMap[sourceBitIndex]))
|
||||
SetHS(cur, (unsigned8)j);
|
||||
}
|
||||
packed[i] = cur;
|
||||
});
|
||||
return packed;
|
||||
}
|
||||
|
||||
// Used to tightly pack a whole vector of values at once. Offset should give the offset in bits within the first packed value
|
||||
template<typename T_packed, typename T_unpacked, unsigned8 bitsInPacked = sizeof(T_packed) * 8, unsigned8 bitsInUnpacked = sizeof(T_unpacked) * 8>
|
||||
std::vector<T_packed> PackTight(const std::vector<T_unpacked>& input, const T_unpacked& mask, const unsigned8 offset = 0, const size_t startIndex = 0, size_t endIndex = ~size_t(0))
|
||||
{
|
||||
return PackTight<T_packed, T_unpacked, bitsInPacked, bitsInUnpacked>(input, GetBitMapHS(mask), offset, startIndex, endIndex);
|
||||
}
|
||||
|
||||
// Unpack a single packed byte at some location.
|
||||
// packed should contain the vector with packed data
|
||||
// i gives the index of the value to unpack (unpacked value index)
|
||||
// bitMap should contain the bitMap for the mask to be used (GetBitMapHS(mask))
|
||||
// offset gives the offset from which the data starts
|
||||
// packedArrayOffset gives the index where the packed data with the current mask starts
|
||||
template<typename T_packed, typename T_unpacked, unsigned8 bitsInPacked = sizeof(T_packed) * 8, unsigned8 bitsInUnpacked = sizeof(T_unpacked) * 8>
|
||||
T_unpacked UnpackTightAt(const std::vector<T_packed>& packed, const size_t& i, const std::vector<unsigned8>& bitMap, const unsigned8& offset = 0, const size_t& packedArrayOffset = 0)
|
||||
{
|
||||
assert(offset < bitsInPacked);
|
||||
size_t setBits = bitMap.size();
|
||||
size_t divisionShift = (bitsInPacked >> 3) * 3; // (bitsInPacked / 8) * 3
|
||||
size_t modulusMask = BitHelper::GetLSMask<size_t>(0, (unsigned8)divisionShift); // Mask that captures the bits that are shifted out by the divisionShift
|
||||
|
||||
T_unpacked value = 0;
|
||||
for (size_t j = 0; j < bitMap.size(); j++)
|
||||
{
|
||||
size_t bit = i * setBits + j + offset;
|
||||
size_t byteIndex = bit >> divisionShift; // Divide by the number of bits in the packed type (gives the index of the packed value to look at)
|
||||
T_packed bitInByte = (T_packed)(bit & modulusMask); // Modulus by the number of bits in the packed type (gives the bit index within the packed type)
|
||||
if (BitHelper::GetHS(packed[packedArrayOffset + byteIndex], bitInByte)) // If the bit in the packed type is set, it should also be in the unpacked type
|
||||
SetHS(value, bitMap[j]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T_packed, typename T_unpacked, unsigned8 bitsInPacked = sizeof(T_packed) * 8, unsigned8 bitsInUnpacked = sizeof(T_unpacked) * 8>
|
||||
T_unpacked UnpackTightAt(const std::vector<T_packed>& packed, const size_t& i, const T_unpacked mask, const unsigned8& offset = 0, const size_t& packedArrayOffset = 0)
|
||||
{
|
||||
return UnpackTightAt<T_packed, T_unpacked, bitsInPacked, bitsInUnpacked>(packed, i, GetBitMapHS(mask), offset, packedArrayOffset);
|
||||
}
|
||||
};
|
||||
|
||||
201
Research/core/Camera.cpp
Normal file
201
Research/core/Camera.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "Defines.h"
|
||||
#include "Camera.h"
|
||||
#include <stdio.h>
|
||||
|
||||
Camera* Camera::mInstance = NULL;
|
||||
|
||||
void Camera::Create() {
|
||||
if (mInstance == NULL)
|
||||
mInstance = new Camera();
|
||||
}
|
||||
|
||||
void Camera::Destroy() {
|
||||
if (mInstance != NULL)
|
||||
delete mInstance;
|
||||
mInstance = NULL;
|
||||
}
|
||||
|
||||
Camera* Camera::Instance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Update camera position and orientation for both camera modes based on change in mouse position
|
||||
//************************************
|
||||
void Camera::UpdateMouse(glm::vec2 mouseChange, float dTime) {
|
||||
if (mFreeCam) {
|
||||
mHorizontalRotation += mRotationSpeed * mouseChange.x;
|
||||
mVerticalRotation += mRotationSpeed * mouseChange.y;
|
||||
mVerticalRotation = glm::min(mVerticalRotation, mPi2 - .1f);
|
||||
mVerticalRotation = glm::max(mVerticalRotation, -mPi2 + .1f);
|
||||
}
|
||||
else {
|
||||
mAzimuth += mSurfingSpeed * mouseChange.x;
|
||||
mAltitude -= mSurfingSpeed * mouseChange.y;
|
||||
mAltitude = glm::min(mAltitude, mPi2 - .1f);
|
||||
mAltitude = glm::max(mAltitude, -mPi2 + .1f);
|
||||
}
|
||||
Compute();
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Update camera position for free camera mode based on key presses
|
||||
//************************************
|
||||
void Camera::UpdateKeys(glm::vec3 movement, float dTime) {
|
||||
if (mFreeCam)
|
||||
mPosition += mMovingSpeed * dTime * (movement.x * mDirection + movement.y * mRight + movement.z * mUp);
|
||||
else
|
||||
mPivot += mMovingSpeed * dTime * glm::vec3(movement.x, movement.z, movement.y);
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Update camera zoom or field of view for both camera modes based on change in scroll
|
||||
//************************************
|
||||
void Camera::UpdateScroll(glm::vec2 offset, float dTime) {
|
||||
|
||||
// Change FOV in free camera mode
|
||||
if (mFreeCam) {
|
||||
mFOV -= mZoomSpeed * offset.y;
|
||||
mFOV = glm::clamp(mFOV, .1f, mPi2);
|
||||
}
|
||||
else {
|
||||
mPosition -= mPosition * mZoomSpeed * offset.y;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::ToggleMode() {
|
||||
mFreeCam = !mFreeCam;
|
||||
|
||||
float r = glm::length(mPosition);
|
||||
if (mFreeCam) {
|
||||
mVerticalRotation = glm::asin(-mPosition.y / r);
|
||||
mHorizontalRotation = glm::atan(-mPosition.x, -mPosition.z);
|
||||
}
|
||||
//else {
|
||||
// mAltitude = glm::asin(mPosition.y / r);
|
||||
// mAzimuth = glm::atan(mPosition.x, mPosition.z);
|
||||
//}
|
||||
|
||||
Compute();
|
||||
|
||||
//////Reset();
|
||||
}
|
||||
|
||||
bool Camera::IsFree()
|
||||
{
|
||||
return mFreeCam;
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Compute camera orientation from rotations for free camera mode or camera position from trackball camera mode
|
||||
//************************************
|
||||
void Camera::Compute() {
|
||||
if (mFreeCam) {
|
||||
|
||||
mDirection = glm::vec3(
|
||||
cos(mVerticalRotation) * sin(mHorizontalRotation),
|
||||
sin(mVerticalRotation),
|
||||
cos(mVerticalRotation) * cos(mHorizontalRotation)
|
||||
);
|
||||
mRight = glm::vec3(
|
||||
sin(mHorizontalRotation - 1.57f),
|
||||
0,
|
||||
cos(mHorizontalRotation - 1.57f)
|
||||
);
|
||||
mUp = glm::cross(mRight, mDirection);
|
||||
|
||||
}
|
||||
else {
|
||||
double r = glm::length(mPosition - mPivot);
|
||||
double x = r * cos(mAltitude) * sin(mAzimuth);
|
||||
double y = r * sin(mAltitude);
|
||||
double z = r * cos(mAltitude) * cos(mAzimuth);
|
||||
mPosition = mPivot + glm::vec3(x, y, z);
|
||||
mDirection = glm::normalize(mPivot - mPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::Reset() {
|
||||
mFOV = 1.f;//QT
|
||||
|
||||
mPosition = glm::vec3(785.f, 544.f, -847.f);//QT
|
||||
mHorizontalRotation = 0.f;//QT
|
||||
mVerticalRotation = 0.f;//QT
|
||||
mAltitude = .44f;//QT
|
||||
mAzimuth = 2.39f;//QT
|
||||
mPivot = glm::vec3(0);//QT
|
||||
|
||||
Compute();
|
||||
}
|
||||
|
||||
glm::vec3 Camera::GetPosition() const {
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
void Camera::SetPosition(glm::vec3 position)
|
||||
{
|
||||
mPosition = position;
|
||||
}
|
||||
|
||||
glm::vec3 Camera::GetDirection() const {
|
||||
return mDirection;
|
||||
}
|
||||
|
||||
void Camera::SetDirection(glm::vec3 direction)
|
||||
{
|
||||
mDirection = glm::normalize(direction);
|
||||
}
|
||||
|
||||
glm::vec3 Camera::GetTarget() const {
|
||||
if (mFreeCam)
|
||||
return mPosition + mDirection;
|
||||
else
|
||||
return mPivot;
|
||||
}
|
||||
|
||||
void Camera::SetTarget(glm::vec3 target)
|
||||
{
|
||||
if (mFreeCam)
|
||||
{
|
||||
mDirection = glm::normalize(target - mPosition);
|
||||
} else
|
||||
mPivot = mPosition;
|
||||
}
|
||||
|
||||
glm::vec3 Camera::GetPivot() const { return mPivot; }
|
||||
void Camera::SetPivot(glm::vec3 pivot) { mPivot = pivot; }
|
||||
|
||||
double Camera::GetAltitude() const { return mAltitude; }
|
||||
void Camera::SetAltitude(double altitude) { mAltitude = altitude; }
|
||||
|
||||
float Camera::GetFOV() const { return mFOV; }
|
||||
void Camera::SetFOV(float fov) { mFOV = fov; }
|
||||
|
||||
float Camera::GetNear() const { return mNear; }
|
||||
void Camera::SetNear(float near) { mNear = near; }
|
||||
|
||||
float Camera::GetFar() const { return mFar; }
|
||||
void Camera::SetFar(float far) { mFar = far; }
|
||||
|
||||
void Camera::SetMovingSpeed(float speed) { mMovingSpeed = speed; }
|
||||
float Camera::GetMovingSpeed() { return mMovingSpeed; }
|
||||
void Camera::SetRotationSpeed(float speed) { mRotationSpeed = speed; }
|
||||
float Camera::GetRotationSpeed() { return mRotationSpeed; }
|
||||
|
||||
Camera::Camera() {
|
||||
mNear = 1.f;//QT
|
||||
mFar = 500000.f;//QT
|
||||
|
||||
mMovingSpeed = 200.f;//QT
|
||||
mRotationSpeed = 0.005f;//QT
|
||||
mSurfingSpeed = 0.01f;//QT
|
||||
mZoomSpeed = 0.1f;//QT
|
||||
|
||||
mFreeCam = false;//QT
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
Camera::~Camera() {
|
||||
|
||||
}
|
||||
71
Research/core/Camera.h
Normal file
71
Research/core/Camera.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "../inc/glm/glm.hpp"
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
static void Create();
|
||||
static void Destroy();
|
||||
static Camera* Instance();
|
||||
|
||||
void UpdateMouse(glm::vec2 mouseChange, float dTime);
|
||||
void UpdateKeys(glm::vec3 movement, float dTime);
|
||||
void UpdateScroll(glm::vec2 offset, float dTime);
|
||||
void ToggleMode();
|
||||
bool IsFree();
|
||||
void Compute();
|
||||
void Reset();
|
||||
|
||||
glm::vec3 GetPosition() const;
|
||||
void SetPosition(glm::vec3 position);
|
||||
glm::vec3 GetDirection() const;
|
||||
void SetDirection(glm::vec3 direction);
|
||||
glm::vec3 GetTarget() const;
|
||||
void SetTarget(glm::vec3 target);
|
||||
glm::vec3 GetPivot() const;
|
||||
void SetPivot(glm::vec3 pivot);
|
||||
double GetAltitude() const;
|
||||
void SetAltitude(double altitude);
|
||||
float GetFOV() const;
|
||||
void SetFOV(float fov);
|
||||
float GetNear() const;
|
||||
void SetNear(float near);
|
||||
float GetFar() const ;
|
||||
void SetFar(float far);
|
||||
|
||||
void SetMovingSpeed(float speed);
|
||||
float GetMovingSpeed();
|
||||
void SetRotationSpeed(float speed);
|
||||
float GetRotationSpeed();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
Camera();
|
||||
~Camera();
|
||||
|
||||
static Camera* mInstance;
|
||||
|
||||
bool mFreeCam; // camera mode; free-moving mode or trackball
|
||||
|
||||
glm::vec3 mPosition;
|
||||
|
||||
glm::vec3 mDirection; // direction, negative z-axis, used for free camera mode
|
||||
glm::vec3 mRight; // right vector, x-axis, used for free camera mode
|
||||
glm::vec3 mUp; // up vector, y-axis, used for free camera mode
|
||||
|
||||
float mHorizontalRotation; // rotation around y-axis, used for free camera mode
|
||||
float mVerticalRotation; // rotation around x-axis, used for free camera mode
|
||||
|
||||
float mAltitude; // camera altitude/elevation, used for trackball camera mode
|
||||
float mAzimuth; // camera azimuth, used for trackball camera mode
|
||||
glm::vec3 mPivot; // pivot point, used for trackball camera mode
|
||||
|
||||
float mFOV; // field of view in y-direction
|
||||
float mNear;
|
||||
float mFar;
|
||||
|
||||
float mMovingSpeed; // free camera moving speed
|
||||
float mRotationSpeed; // free camera rotation speed
|
||||
float mSurfingSpeed; // trackball camera surfing speed
|
||||
float mZoomSpeed; // scrolling speed/sensitivity, used for zooming
|
||||
};
|
||||
16
Research/core/CameraPath.cpp
Normal file
16
Research/core/CameraPath.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "CameraPath.h"
|
||||
#include "MathHelper.h"
|
||||
#include "../inc/glm/glm.hpp"
|
||||
|
||||
CameraState CameraStateInterpolator::operator()(const CameraState& before, const CameraState& after, const double& time)
|
||||
{
|
||||
float t = (float)time;
|
||||
CameraState res;
|
||||
res.position = MathHelper::lerp(t, before.position, after.position);
|
||||
res.direction = MathHelper::slerp(t, before.direction, after.direction);
|
||||
res.target = MathHelper::lerp(t, before.target, after.target);
|
||||
res.FOV = MathHelper::lerp(t, before.FOV, after.FOV);
|
||||
res.nearPlane = MathHelper::lerp(t, before.nearPlane, after.nearPlane);
|
||||
res.farPlane = MathHelper::lerp(t, before.farPlane, after.farPlane);
|
||||
return res;
|
||||
};
|
||||
21
Research/core/CameraPath.h
Normal file
21
Research/core/CameraPath.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../inc/glm/common.hpp"
|
||||
#include "Util/Path.h"
|
||||
|
||||
struct CameraState
|
||||
{
|
||||
glm::vec3 position;
|
||||
glm::vec3 direction;
|
||||
glm::vec3 target;
|
||||
float FOV;
|
||||
float nearPlane;
|
||||
float farPlane;
|
||||
};
|
||||
|
||||
struct CameraStateInterpolator {
|
||||
CameraState operator()(const CameraState& before, const CameraState& after, const double& time);
|
||||
};
|
||||
|
||||
typedef Path<CameraState, CameraStateInterpolator> CameraPath;
|
||||
175
Research/core/CollectionHelper.h
Normal file
175
Research/core/CollectionHelper.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
#include "Defines.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "../inc/tbb/parallel_sort.h"
|
||||
#include "../inc/tbb/parallel_reduce.h"
|
||||
#include "../inc/tbb/blocked_range.h"
|
||||
#include "../inc/glm/glm.hpp"
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
class CollectionHelper
|
||||
{
|
||||
public:
|
||||
// Modifies the sequence so that it only contains unique elements. To do this quickly, the elements will be sorted first, meaning the original order is lost.
|
||||
template<typename T, typename Compare = std::less<T>>
|
||||
inline static void Unique(std::vector<T>& data, Compare comparer = Compare())
|
||||
{
|
||||
tbb::parallel_sort(data.begin(), data.end(), comparer);
|
||||
data.erase(std::unique(data.begin(), data.end()), data.end());
|
||||
data.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Modifies the sequence so that it only contains unique elements. To do this quickly, the elements will be sorted first, meaning the original order is lost.
|
||||
template<typename T, typename Compare = std::less<T>>
|
||||
inline static void UniqueSerial(std::vector<T>& data, Compare comparer = Compare())
|
||||
{
|
||||
std::sort(data.begin(), data.end(), comparer);
|
||||
data.erase(std::unique(data.begin(), data.end()), data.end());
|
||||
data.shrink_to_fit();
|
||||
}
|
||||
|
||||
template<typename T, typename Compare = std::less<T>>
|
||||
inline static size_t CountUnique(const std::vector<T>& data, Compare comparer = Compare())
|
||||
{
|
||||
// Copy the data
|
||||
std::vector<T> copy(data.size());
|
||||
std::copy(data.begin(), data.end(), copy.begin());
|
||||
Unique(copy, comparer);
|
||||
return copy.size();
|
||||
}
|
||||
|
||||
// Walks through the given data vector and checks if it contains a certain value. O(n) performance.
|
||||
template<typename T>
|
||||
inline static bool Contains(const std::vector<T>& data, const T& value)
|
||||
{
|
||||
bool contains = false;
|
||||
for (auto mat : data)
|
||||
{
|
||||
if (mat == value)
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return contains;
|
||||
}
|
||||
|
||||
template<typename T, typename Compare = std::less<T>>
|
||||
inline static size_t MinIndex(const std::vector<T>& data, Compare comparer = Compare())
|
||||
{
|
||||
// Finds the minimum value according to some comparer
|
||||
return tbb::parallel_reduce(tbb::blocked_range<size_t>(0, data.size()), size_t(0),
|
||||
[&](const tbb::blocked_range<size_t>& r, size_t init)
|
||||
{
|
||||
for (size_t i = r.begin(); i != r.end(); i++)
|
||||
if (comparer(data[i], data[init]))
|
||||
init = i;
|
||||
return init;
|
||||
},
|
||||
[&](size_t value1, size_t value2)
|
||||
{
|
||||
if (comparer(data[value1], data[value2]))
|
||||
return value1;
|
||||
else
|
||||
return value2;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static std::unordered_map<T, size_t> GetIndexMap(const std::vector<T>& data)
|
||||
{
|
||||
std::unordered_map<T, size_t> res;
|
||||
for (size_t i = 0; i < data.size(); i++)
|
||||
res.insert(std::pair<T, size_t>(data[i], i));
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static void PrintContents(const std::vector<T>& data, size_t count = 0)
|
||||
{
|
||||
if (count == 0) count = data.size();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
std::cout << std::to_string(data[i]).c_str();
|
||||
if (i < count - 1)
|
||||
std::cout << ", ";
|
||||
else
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static T Sum(const std::vector<T>& data)
|
||||
{
|
||||
return std::accumulate(data.begin(), data.end(), (T)0);
|
||||
}
|
||||
|
||||
// Calculates the max given some comparer. Comparer should be a functor that returns true if the first value passed is the biggest value.
|
||||
template<typename T, typename Comparer = std::greater<T>>
|
||||
inline static T Max(const std::vector<T>& data, Comparer comp = std::greater<T>())
|
||||
{
|
||||
if (data.empty()) return T();
|
||||
T max = data[0];
|
||||
for (const T& value : data)
|
||||
if (comp(value, max)) max = value;
|
||||
return max;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static double CalculateMean(const std::vector<T>& data)
|
||||
{
|
||||
return ((double)Sum(data)) / ((double)data.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static double CalculateVariance(const std::vector<T>& data, double mean)
|
||||
{
|
||||
double variance = 0;
|
||||
for (T v : data)
|
||||
{
|
||||
double meanDiff = ((double)v) - mean;
|
||||
variance += meanDiff * meanDiff;
|
||||
}
|
||||
variance /= (double)data.size();
|
||||
return variance;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static double CalculateVariance(const std::vector<T>& data)
|
||||
{
|
||||
return CalculateVariance(data, CalculateMean(data));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static T CalculateMedian(const std::vector<T>& data)
|
||||
{
|
||||
std::vector<T> dataCopy(data.size());
|
||||
std::copy(data.begin(), data.end(), dataCopy.begin());
|
||||
tbb::parallel_sort(dataCopy.begin(), dataCopy.end());
|
||||
return dataCopy[dataCopy.size() / 2];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static size_t Count(const std::vector<T>& data, const T& value)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (T v : data) if (v == value) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static void PrintStats(const std::vector<T>& data)
|
||||
{
|
||||
|
||||
double mean = CalculateMean(data);
|
||||
unsigned32 zeroCount = (unsigned32)Count(data, 0);
|
||||
double variance = CalculateVariance(data);
|
||||
T median = CalculateMedian(data);
|
||||
|
||||
printf("Size: %llu, Zero count: %u, Mean: %f, Median: %i, Variance: %f\n", data.size(), zeroCount, mean, median, variance);
|
||||
}
|
||||
};
|
||||
|
||||
244
Research/core/ColorHelper.cpp
Normal file
244
Research/core/ColorHelper.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "ColorHelper.h"
|
||||
#include <algorithm>
|
||||
|
||||
// Conversion matrix from RGB to XYZ (assuming sRGB values in 0-1 range, with D65 as reference white
|
||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
const glm::mat3 ColorHelper::matRGBtoXYZ = glm::transpose(glm::mat3(
|
||||
0.4124564, 0.3575761, 0.1804375,
|
||||
0.2126729, 0.7151522, 0.0721750,
|
||||
0.0193339, 0.1191920, 0.9503041
|
||||
));
|
||||
|
||||
// Conversion matrix from XYZ to sRGB (assuming D65 is used as reference white)
|
||||
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
const glm::mat3 ColorHelper::matXYZtoRGB = glm::transpose(glm::mat3(
|
||||
3.2404542, -1.5371385, -0.4985314,
|
||||
-0.9692660, 1.8760108, 0.0415560,
|
||||
0.0556434, -0.2040259, 1.0572252
|
||||
));
|
||||
|
||||
const glm::vec3 ColorHelper::D65WhitePoint = glm::vec3(0.95047, 1.00, 1.08883);
|
||||
const glm::vec3 ColorHelper::invD65WhitePoint = 1.f / ColorHelper::D65WhitePoint;
|
||||
|
||||
const float ColorHelper::inv255 = 1.f / 255.f;
|
||||
|
||||
const float ColorHelper::tLimit = 0.008856f;
|
||||
const float ColorHelper::tInvLimit = 6.f / 29.f;
|
||||
const float ColorHelper::epsilon = 0.008856f;
|
||||
const float ColorHelper::kappa = 903.3f;
|
||||
|
||||
// K-values for Delta-E calculation
|
||||
const float ColorHelper::kl = 1;
|
||||
const float ColorHelper::kc = 1;
|
||||
const float ColorHelper::kh = 1;
|
||||
|
||||
const float ColorHelper::K1 = 0.045f;
|
||||
const float ColorHelper::K2 = 0.015f;
|
||||
|
||||
|
||||
// Based on:
|
||||
// http://en.wikipedia.org/wiki/Lab_color_space
|
||||
glm::vec3 ColorHelper::RGBtoLAB(glm::u8vec3 rgb)
|
||||
{
|
||||
glm::vec3 xyz = RGBtoXYZ(rgb);
|
||||
return XYZtoLAB(xyz);
|
||||
}
|
||||
|
||||
glm::u8vec3 ColorHelper::LABtoRGB(glm::vec3 lab)
|
||||
{
|
||||
glm::vec3 xyz = LABtoXYZ(lab);
|
||||
return XYZtoRGB(xyz);
|
||||
}
|
||||
|
||||
glm::vec3 ColorHelper::XYZtoLAB(glm::vec3 xyz)
|
||||
{
|
||||
glm::vec3 n = xyz * invD65WhitePoint;
|
||||
float fx = f(n.x);
|
||||
float fy = f(n.y);
|
||||
float fz = f(n.z);
|
||||
return glm::vec3(
|
||||
116.f * fy - 16.f,
|
||||
500.f * (fx - fy),
|
||||
200.f * (fy - fz)
|
||||
);
|
||||
}
|
||||
float ColorHelper::f(float t)
|
||||
{
|
||||
if (t > epsilon)
|
||||
// t ^ (1 / 3)
|
||||
return std::pow(t, 0.3333333333f);
|
||||
else
|
||||
//(1 / 3) * (29 / 6) ^ 2 * t + (4 / 29)
|
||||
return (kappa * t + 16.f) / 116.f;
|
||||
}
|
||||
|
||||
glm::vec3 ColorHelper::LABtoXYZ(glm::vec3 lab)
|
||||
{
|
||||
float fy = (lab.x + 16.f) / 116.f;
|
||||
float fx = lab.y * 0.002f + fy;
|
||||
float fz = fy - 0.005f * lab.z;
|
||||
|
||||
float xr = fx * fx * fx;
|
||||
if (xr <= epsilon) xr = (116.f * fx - 16.f) / kappa;
|
||||
|
||||
float yr = 0;
|
||||
if (lab.x > kappa * epsilon)
|
||||
yr = std::pow((lab.x + 16.f) / 116.f, 3.f);
|
||||
else
|
||||
yr = lab.x / kappa;
|
||||
|
||||
float zr = fz * fz * fz;
|
||||
if (zr <= epsilon) zr = (116.f * fz - 16.f) / kappa;
|
||||
|
||||
return glm::vec3(xr, yr, zr) * D65WhitePoint;
|
||||
}
|
||||
float ColorHelper::fInv(float t)
|
||||
{
|
||||
if (t > tInvLimit)
|
||||
// t ^ 3
|
||||
return std::pow(t, 3.f);
|
||||
else
|
||||
// 3 * (6 / 29) ^ 2 * (t - (4 / 29))
|
||||
return 0.12841854934f * (t - 0.13793103448f);
|
||||
}
|
||||
|
||||
glm::vec3 ColorHelper::RGBtoXYZ(glm::u8vec3 rgb)
|
||||
{
|
||||
glm::vec3 lrgb(toLinearRGB(rgb.x), toLinearRGB(rgb.y), toLinearRGB(rgb.z));
|
||||
return matRGBtoXYZ * lrgb;
|
||||
}
|
||||
|
||||
glm::u8vec3 ColorHelper::XYZtoRGB(glm::vec3 xyz)
|
||||
{
|
||||
glm::vec3 lrgb = matXYZtoRGB * xyz;
|
||||
return glm::u8vec3(fromLinearRGB(lrgb.x), fromLinearRGB(lrgb.y), fromLinearRGB(lrgb.z));
|
||||
}
|
||||
|
||||
glm::u8vec3 ColorHelper::RGBtoHSV(glm::u8vec3 rgb)
|
||||
{
|
||||
glm::u8vec3 hsv;
|
||||
unsigned char rgbMin, rgbMax;
|
||||
|
||||
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
|
||||
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
|
||||
|
||||
hsv.z = rgbMax;
|
||||
if (hsv.z == 0)
|
||||
{
|
||||
hsv.x = 0;
|
||||
hsv.y = 0;
|
||||
return hsv;
|
||||
}
|
||||
|
||||
hsv.y = 255 * long(rgbMax - rgbMin) / hsv.z;
|
||||
if (hsv.y == 0)
|
||||
{
|
||||
hsv.x = 0;
|
||||
return hsv;
|
||||
}
|
||||
|
||||
if (rgbMax == rgb.r)
|
||||
hsv.x = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
|
||||
else if (rgbMax == rgb.g)
|
||||
hsv.x = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
|
||||
else
|
||||
hsv.x = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
|
||||
|
||||
return hsv;
|
||||
}
|
||||
|
||||
glm::u8vec3 ColorHelper::HSVtoRGB(glm::u8vec3 hsv)
|
||||
{
|
||||
glm::u8vec3 rgb;
|
||||
unsigned char region, remainder, p, q, t;
|
||||
|
||||
if (hsv.y == 0)
|
||||
{
|
||||
rgb.r = hsv.z;
|
||||
rgb.g = hsv.z;
|
||||
rgb.b = hsv.z;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
region = hsv.x / 43;
|
||||
remainder = (hsv.x - (region * 43)) * 6;
|
||||
|
||||
p = (hsv.z * (255 - hsv.y)) >> 8;
|
||||
q = (hsv.z * (255 - ((hsv.y * remainder) >> 8))) >> 8;
|
||||
t = (hsv.z * (255 - ((hsv.y * (255 - remainder)) >> 8))) >> 8;
|
||||
|
||||
switch (region)
|
||||
{
|
||||
case 0:
|
||||
rgb.r = hsv.z; rgb.g = t; rgb.b = p;
|
||||
break;
|
||||
case 1:
|
||||
rgb.r = q; rgb.g = hsv.z; rgb.b = p;
|
||||
break;
|
||||
case 2:
|
||||
rgb.r = p; rgb.g = hsv.z; rgb.b = t;
|
||||
break;
|
||||
case 3:
|
||||
rgb.r = p; rgb.g = q; rgb.b = hsv.z;
|
||||
break;
|
||||
case 4:
|
||||
rgb.r = t; rgb.g = p; rgb.b = hsv.z;
|
||||
break;
|
||||
default:
|
||||
rgb.r = hsv.z; rgb.g = p; rgb.b = q;
|
||||
break;
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
float ColorHelper::toLinearRGB(unsigned8 c)
|
||||
{
|
||||
float cf = float(c) * inv255;
|
||||
if (cf <= 0.04045f)
|
||||
return cf / 12.92f;
|
||||
else
|
||||
return std::pow((cf + 0.055f) / 1.055f, 2.4f);
|
||||
|
||||
}
|
||||
unsigned8 ColorHelper::fromLinearRGB(float c)
|
||||
{
|
||||
if (c <= 0.0031308f)
|
||||
return (unsigned8)std::round(12.92f * c * 255.f);
|
||||
else
|
||||
// ((1 + 0.055) * c) ^ (1 / 2.4) - 0.055
|
||||
return (unsigned8)std::round((1.055 * std::pow(c, 1.f / 2.4f) - 0.055) * 255.f);
|
||||
}
|
||||
|
||||
float ColorHelper::GetDeltaEFromLAB(glm::vec3 lab1, glm::vec3 lab2)
|
||||
{
|
||||
// Implementation based on: http://en.wikipedia.org/wiki/Color_difference
|
||||
// Calculation
|
||||
float deltaL = lab1.x - lab2.x;
|
||||
float C1 = std::sqrt(lab1.y * lab1.y + lab1.z * lab1.z);
|
||||
float C2 = std::sqrt(lab2.y * lab2.y + lab2.z * lab2.z);
|
||||
float deltaC = C1 - C2;
|
||||
float deltaA = lab1.y - lab2.y;
|
||||
float deltaB = lab1.z - lab2.z;
|
||||
float deltaH = std::sqrt(deltaA * deltaA + deltaB * deltaB - deltaC * deltaC);
|
||||
float SL = 1.f;
|
||||
float SC = 1.f + K1 * C1;
|
||||
float SH = 1.f + K2 * C1;
|
||||
|
||||
// Delta E = \sqrt{ \left(\frac{ \Delta L^* }{k_L S_L}\right) ^ 2 + \left(\frac{ \Delta C^*_{ ab } }{k_C S_C}\right) ^ 2 + \left(\frac{ \Delta H^*_{ ab } }{k_H S_H}\right) ^ 2 }
|
||||
|
||||
float term1 = deltaL / (kl * SL);
|
||||
float term2 = deltaC / (kc * SC);
|
||||
float term3 = deltaH / (kh * SH);
|
||||
return std::sqrt(term1 * term1 + term2 * term2 + term3 * term3);
|
||||
}
|
||||
|
||||
float ColorHelper::GetDeltaEFromXYZ(glm::vec3 xyz1, glm::vec3 xyz2)
|
||||
{
|
||||
return GetDeltaEFromLAB(XYZtoLAB(xyz1), XYZtoLAB(xyz2));
|
||||
}
|
||||
|
||||
float ColorHelper::GetDeltaEFromRGB(glm::u8vec3 rgb1, glm::u8vec3 rgb2)
|
||||
{
|
||||
return GetDeltaEFromLAB(RGBtoLAB(rgb1), RGBtoLAB(rgb2));
|
||||
}
|
||||
49
Research/core/ColorHelper.h
Normal file
49
Research/core/ColorHelper.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "../inc/glm/glm.hpp"
|
||||
#include "Defines.h"
|
||||
|
||||
class ColorHelper
|
||||
{
|
||||
public:
|
||||
static glm::vec3 RGBtoLAB(glm::u8vec3 rgb);
|
||||
static glm::u8vec3 LABtoRGB(glm::vec3 lab);
|
||||
static glm::vec3 XYZtoLAB(glm::vec3 xyz);
|
||||
static glm::vec3 LABtoXYZ(glm::vec3 lab);
|
||||
static glm::vec3 RGBtoXYZ(glm::u8vec3 rgb);
|
||||
static glm::u8vec3 XYZtoRGB(glm::vec3 xyz);
|
||||
static glm::u8vec3 RGBtoHSV(glm::u8vec3 rgb);
|
||||
static glm::u8vec3 HSVtoRGB(glm::u8vec3 hsv);
|
||||
|
||||
// Use the CIE94 standard for calculating the visual difference between two colors in CIELAB space (Delta E)
|
||||
static float GetDeltaEFromLAB(glm::vec3 lab1, glm::vec3 lab2);
|
||||
// Use the CIE94 standard for calculating the visual difference between two colors in CIE XYZ space (Delta E)
|
||||
static float GetDeltaEFromXYZ(glm::vec3 xyz1, glm::vec3 xyz2);
|
||||
// Use the CIE94 standard for calculating the visual difference between two colors in RGB (Delta E)
|
||||
static float GetDeltaEFromRGB(glm::u8vec3 rgb1, glm::u8vec3 rgb2);
|
||||
|
||||
private:
|
||||
static const glm::mat3 matRGBtoXYZ;
|
||||
static const glm::mat3 matXYZtoRGB;
|
||||
static const float inv255;
|
||||
static const glm::vec3 D65WhitePoint;
|
||||
static const glm::vec3 invD65WhitePoint;
|
||||
static const float epsilon;
|
||||
static const float kappa;
|
||||
|
||||
// k_L, k_C and k_H are usually 1. For the textile industry k_L = 2
|
||||
static const float kl;
|
||||
static const float kc;
|
||||
static const float kh;
|
||||
|
||||
// K_1 and K_2 are standard values. These are for the graphical arts industry.
|
||||
static const float K1;
|
||||
static const float K2;
|
||||
|
||||
static float toLinearRGB(unsigned8 c);
|
||||
static unsigned8 fromLinearRGB(float c);
|
||||
static float f(float t);
|
||||
static float fInv(float t);
|
||||
static const float tLimit;
|
||||
static const float tInvLimit;
|
||||
};
|
||||
|
||||
29
Research/core/Comparers.h
Normal file
29
Research/core/Comparers.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "Defines.h"
|
||||
#include "../inc/glm/common.hpp"
|
||||
|
||||
struct u8vec3comparer {
|
||||
bool operator()(const glm::u8vec3& color1, const glm::u8vec3& color2) const
|
||||
{
|
||||
if (color1.r != color2.r)
|
||||
return color1.r < color2.r;
|
||||
else if (color1.g != color2.g)
|
||||
return color1.g < color2.g;
|
||||
else if (color1.b != color2.b)
|
||||
return color1.b < color2.b;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct HashComparer
|
||||
{
|
||||
bool operator()(const T& x, const T& y) const
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
return hasher.operator()(x) < hasher.operator()(y);
|
||||
}
|
||||
};
|
||||
19
Research/core/CompatibilityHelper.h
Normal file
19
Research/core/CompatibilityHelper.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include<sstream>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
namespace std {
|
||||
template<typename T>
|
||||
std::string to_string(T value) {
|
||||
//create an output string stream
|
||||
std::ostringstream os;
|
||||
|
||||
//throw the value into the string stream
|
||||
os << value;
|
||||
|
||||
//convert the string stream into a string and return
|
||||
return os.str();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
24
Research/core/CompileTimeArray.h
Normal file
24
Research/core/CompileTimeArray.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// These templates make sure that arrays can be generated at compile time. Ideal for lookup tables!
|
||||
template<unsigned... args> struct ArrayHolder {
|
||||
static const unsigned data[sizeof...(args)];
|
||||
};
|
||||
|
||||
template<unsigned... args>
|
||||
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };
|
||||
|
||||
template<size_t N, template<size_t> class F, unsigned... args>
|
||||
struct generate_array_impl {
|
||||
typedef typename generate_array_impl<N - 1, F, F<N>::value, args...>::result result;
|
||||
};
|
||||
|
||||
template<template<size_t> class F, unsigned... args>
|
||||
struct generate_array_impl<0, F, args...> {
|
||||
typedef ArrayHolder<F<0>::value, args...> result;
|
||||
};
|
||||
|
||||
template<size_t N, template<size_t> class F>
|
||||
struct generate_array {
|
||||
typedef typename generate_array_impl<N - 1, F>::result result;
|
||||
};
|
||||
212
Research/core/Controls.cpp
Normal file
212
Research/core/Controls.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "../inc/gl/glfw3.h"
|
||||
#include "../inc/glm/glm.hpp"
|
||||
#include "../Renderer.h"
|
||||
#include "Defines.h"
|
||||
#include "Camera.h"
|
||||
#include "Controls.h"
|
||||
|
||||
|
||||
Controls* Controls::mInstance = NULL;
|
||||
const float Controls::RUNNINGSPEEDRATIO = 2.f;
|
||||
const float Controls::CRAWLSPEEDRATIO = 0.5f;
|
||||
const float Controls::SPEEDINCREASERATIO = 1.25f;
|
||||
|
||||
void Controls::Create(GLFWwindow* window, bool fullscreen) {
|
||||
if (mInstance == NULL)
|
||||
mInstance = new Controls(window, fullscreen);
|
||||
}
|
||||
|
||||
void Controls::Destroy() {
|
||||
if (mInstance != NULL)
|
||||
delete mInstance;
|
||||
mInstance = NULL;
|
||||
}
|
||||
|
||||
Controls* Controls::Instance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
void Controls::Run() {
|
||||
|
||||
if (mLastTime == 0)
|
||||
mLastTime = float(glfwGetTime());
|
||||
|
||||
mCurrentTime = float(glfwGetTime());
|
||||
mDeltaTime = mCurrentTime - mLastTime;
|
||||
mLastTime = mCurrentTime;
|
||||
|
||||
mOldMouseX = mMouseX;
|
||||
mOldMouseY = mMouseY;
|
||||
glfwGetCursorPos(mWindow, &mMouseX, &mMouseY);
|
||||
|
||||
ListenToMouse();
|
||||
ListenToKeys();
|
||||
}
|
||||
|
||||
void Controls::ListenToMouse() {
|
||||
if (!mDragMouse || glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)
|
||||
mCamera->UpdateMouse(glm::vec2(mOldMouseX - mMouseX, mOldMouseY - mMouseY), mDeltaTime);
|
||||
}
|
||||
|
||||
void Controls::ListenToKeys() {
|
||||
|
||||
if (CheckKey(GLFW_KEY_ESCAPE))//QT
|
||||
mState = State::EXIT;
|
||||
if (CheckKey(GLFW_KEY_SPACE, true))//QT
|
||||
mState = mState == State::PAUSED ? State::RUNNING : State::PAUSED;
|
||||
|
||||
glm::vec3 movement(0);
|
||||
movement.x += CheckKey(GLFW_KEY_UP) || CheckKey(GLFW_KEY_W);//QT
|
||||
movement.x -= CheckKey(GLFW_KEY_DOWN) || CheckKey(GLFW_KEY_S);
|
||||
movement.y += CheckKey(GLFW_KEY_RIGHT) || CheckKey(GLFW_KEY_D);
|
||||
movement.y -= CheckKey(GLFW_KEY_LEFT) || CheckKey(GLFW_KEY_A);
|
||||
movement.z += CheckKey(GLFW_KEY_Q);
|
||||
movement.z -= CheckKey(GLFW_KEY_E);
|
||||
if (CheckKey(GLFW_KEY_LEFT_SHIFT))
|
||||
movement *= RUNNINGSPEEDRATIO;
|
||||
if (CheckKey(GLFW_KEY_LEFT_CONTROL))
|
||||
movement *= CRAWLSPEEDRATIO;
|
||||
|
||||
if (CheckKey(GLFW_KEY_MINUS, true))
|
||||
{
|
||||
mCamera->SetMovingSpeed(mCamera->GetMovingSpeed() * (1.f / SPEEDINCREASERATIO));
|
||||
//mCamera->SetRotationSpeed(mCamera->GetRotationSpeed() * (1.f / SPEEDINCREASERATIO));
|
||||
}
|
||||
if (CheckKey(GLFW_KEY_EQUAL, true))
|
||||
{
|
||||
mCamera->SetMovingSpeed(mCamera->GetMovingSpeed() * SPEEDINCREASERATIO);
|
||||
//mCamera->SetRotationSpeed(mCamera->GetRotationSpeed() * SPEEDINCREASERATIO);
|
||||
}
|
||||
|
||||
mCamera->UpdateKeys(movement, mDeltaTime);
|
||||
|
||||
if (CheckKey(GLFW_KEY_C, true)) // C: Toggle camera mode
|
||||
mCamera->ToggleMode();
|
||||
if (CheckKey(GLFW_KEY_X, true)) // X: Reset the camera to original position
|
||||
mCamera->Reset();
|
||||
|
||||
if (CheckKey(GLFW_KEY_L, true)) // L: Update light direction
|
||||
Renderer::Instance()->UpdateLightDirection();
|
||||
if (CheckKey(GLFW_KEY_K, true)) // K: Turn on/off light
|
||||
Renderer::Instance()->ToggleLight();
|
||||
if (CheckKey(GLFW_KEY_J, true)) // J: Toggle AO mode
|
||||
Renderer::Instance()->ToggleAOMode();
|
||||
if (CheckKey(GLFW_KEY_H, true)) // H: Toggle reflections
|
||||
Renderer::Instance()->ToggleReflections();
|
||||
|
||||
if (CheckKey(GLFW_KEY_R, true)) // R: Reload the shaders
|
||||
Renderer::Instance()->ReloadShaders();
|
||||
|
||||
if (CheckKey(GLFW_KEY_F, true)) // F: Toggles fullscreen
|
||||
{
|
||||
mFullScreen = !mFullScreen;
|
||||
Renderer::Instance()->ToggleFullscreen(mFullScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckKey(GLFW_KEY_LEFT_BRACKET, true)) // [: Decreases the octree rendering depth (for improved performance and debugging)
|
||||
Renderer::Instance()->DecreaseOctreeRenderLevel();
|
||||
if (CheckKey(GLFW_KEY_RIGHT_BRACKET, true)) // ]: Increases the octree render depth
|
||||
Renderer::Instance()->IncreaseOctreeRenderLevel();
|
||||
|
||||
if (CheckKey(GLFW_KEY_P, true)) // P: take a screenshot
|
||||
Renderer::Instance()->TakeScreenshot();
|
||||
|
||||
if (CheckKey(GLFW_KEY_V, true)) // V: Starts/Stops recording a camera path
|
||||
Renderer::Instance()->ToggleRecording();
|
||||
if (CheckKey(GLFW_KEY_I, true)) // I: Clear the lightpath recording
|
||||
Renderer::Instance()->ClearLightPath();
|
||||
if (CheckKey(GLFW_KEY_O, true)) // O: Add node to the lightpath
|
||||
Renderer::Instance()->AddLightPathNode();
|
||||
if (CheckKey(GLFW_KEY_T, true))
|
||||
{
|
||||
if (CheckKey(GLFW_KEY_LEFT_CONTROL)) // Ctrl + T: Replace recording by rotation recording
|
||||
Renderer::Instance()->SetRotationRecording();
|
||||
else // T: Clear the recording
|
||||
Renderer::Instance()->ClearCameraPath();
|
||||
}
|
||||
if (CheckKey(GLFW_KEY_Y, true)) // Y: Add node to the recording path, Ctrl+Y removes the last node from the recording
|
||||
{
|
||||
if (CheckKey(GLFW_KEY_LEFT_CONTROL))
|
||||
Renderer::Instance()->RemoveCameraPathNode();
|
||||
else
|
||||
Renderer::Instance()->AddCameraPathNode();
|
||||
}
|
||||
|
||||
if (CheckKey(GLFW_KEY_B, true)) // B: Stores a recording (screenshot at each position along a path)
|
||||
Renderer::Instance()->StoreRecording();
|
||||
if (CheckKey(GLFW_KEY_G, true)) // G: Starts/Stops a benchmark (plays back recording and stores all frame times)
|
||||
Renderer::Instance()->RunBenchmark();
|
||||
|
||||
if (CheckKey(GLFW_KEY_N, true)) // N: Stores the current viewpoint to a file
|
||||
Renderer::Instance()->SaveLastViewpoint();
|
||||
if (CheckKey(GLFW_KEY_M, true)) // M: Loads the last stored viewpoint
|
||||
Renderer::Instance()->LoadLastViewpoint();
|
||||
|
||||
// 0-9: Go to one of the preset locations from settings
|
||||
if (CheckKey(GLFW_KEY_0, true)) Renderer::Instance()->SetPresetLocation(0);
|
||||
if (CheckKey(GLFW_KEY_1, true)) Renderer::Instance()->SetPresetLocation(1);
|
||||
if (CheckKey(GLFW_KEY_2, true)) Renderer::Instance()->SetPresetLocation(2);
|
||||
if (CheckKey(GLFW_KEY_3, true)) Renderer::Instance()->SetPresetLocation(3);
|
||||
if (CheckKey(GLFW_KEY_4, true)) Renderer::Instance()->SetPresetLocation(4);
|
||||
if (CheckKey(GLFW_KEY_5, true)) Renderer::Instance()->SetPresetLocation(5);
|
||||
if (CheckKey(GLFW_KEY_6, true)) Renderer::Instance()->SetPresetLocation(6);
|
||||
if (CheckKey(GLFW_KEY_7, true)) Renderer::Instance()->SetPresetLocation(7);
|
||||
if (CheckKey(GLFW_KEY_8, true)) Renderer::Instance()->SetPresetLocation(8);
|
||||
if (CheckKey(GLFW_KEY_9, true)) Renderer::Instance()->SetPresetLocation(9);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//************************************
|
||||
// Check if key is pressed; for lock-sensitive keys only return true if key unlocked
|
||||
//************************************
|
||||
bool Controls::CheckKey(int key, bool lock) {
|
||||
|
||||
bool result = glfwGetKey(mWindow, key) == GLFW_PRESS && (!lock || !mLocked[key]);
|
||||
|
||||
if (glfwGetKey(mWindow, key) == GLFW_RELEASE)
|
||||
mLocked[key] = false;
|
||||
|
||||
if (glfwGetKey(mWindow, key) == GLFW_PRESS)
|
||||
mLocked[key] = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Controls::Scroll(GLFWwindow* window, double xoffset, double yoffset) {
|
||||
Controls* controller = Controls::mInstance;
|
||||
controller->mCamera->UpdateScroll(glm::vec2(xoffset, yoffset), controller->mDeltaTime);
|
||||
};
|
||||
|
||||
|
||||
Controls::State Controls::GetState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
Controls::Controls(GLFWwindow* window, bool fullscreen) {
|
||||
|
||||
mWindow = window;
|
||||
|
||||
mState = State::RUNNING;//QT
|
||||
mFullScreen = fullscreen;//QT
|
||||
|
||||
mLocked.resize(512);
|
||||
mMouseX = 0.5;
|
||||
mMouseY = 0.5;
|
||||
mDragMouse = true;//QT
|
||||
|
||||
mLastTime = 0;
|
||||
mCurrentTime = 0;
|
||||
mDeltaTime = 0;
|
||||
|
||||
Camera::Create();
|
||||
mCamera = Camera::Instance();
|
||||
glfwSetScrollCallback(mWindow, this->Scroll);
|
||||
}
|
||||
|
||||
|
||||
Controls::~Controls() {
|
||||
mLocked.clear();
|
||||
}
|
||||
51
Research/core/Controls.h
Normal file
51
Research/core/Controls.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
class Camera;
|
||||
struct GLFWwindow;
|
||||
|
||||
class Controls {
|
||||
public:
|
||||
enum State { RUNNING, PAUSED, EXIT };
|
||||
static void Create(GLFWwindow* window, bool fullscreen);
|
||||
static void Destroy();
|
||||
static Controls* Instance();
|
||||
|
||||
void Run();
|
||||
|
||||
State GetState();
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
Controls(GLFWwindow* window, bool fullscreen);
|
||||
~Controls();
|
||||
|
||||
void ListenToMouse();
|
||||
void ListenToKeys();
|
||||
bool CheckKey(int key, bool lock = false);
|
||||
|
||||
static void Scroll(GLFWwindow* window, double xoffset, double yoffset);
|
||||
|
||||
static Controls* mInstance;
|
||||
GLFWwindow* mWindow;
|
||||
Camera* mCamera;
|
||||
bool mDragMouse;
|
||||
|
||||
State mState;
|
||||
bool mFullScreen;
|
||||
|
||||
std::vector<bool> mLocked; // input is locked for pressed keys to prevent continuous event firing
|
||||
double mMouseX;
|
||||
double mMouseY;
|
||||
double mOldMouseX;
|
||||
double mOldMouseY;
|
||||
|
||||
float mLastTime;
|
||||
float mCurrentTime;
|
||||
float mDeltaTime;
|
||||
|
||||
static const float RUNNINGSPEEDRATIO;
|
||||
static const float CRAWLSPEEDRATIO;
|
||||
static const float SPEEDINCREASERATIO;
|
||||
};
|
||||
61
Research/core/Defines.h
Normal file
61
Research/core/Defines.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#define mPi 3.14159265358979323846f
|
||||
#define mPi2 mPi/2.f
|
||||
#define mPi3 mPi/3.f
|
||||
#define mPi4 mPi/4.f
|
||||
#define mPi6 mPi/6.f
|
||||
#define mPi8 mPi/8.f
|
||||
#define mPi10 mPi/10.f
|
||||
|
||||
#define m2Pi mPi*2.f
|
||||
#define m4Pi mPi*4.f
|
||||
|
||||
#define mPi_1 1.f/mPi
|
||||
|
||||
#define unsigned8 unsigned char
|
||||
#define unsigned16 unsigned short
|
||||
#define unsigned32 unsigned int
|
||||
#define unsigned64 unsigned long long
|
||||
|
||||
#define ChildIndex unsigned char
|
||||
#define FRONT_BOTTOM_LEFT 0
|
||||
#define FRONT_BOTTOM_RIGHT 1
|
||||
#define FRONT_TOP_LEFT 2
|
||||
#define FRONT_TOP_RIGHT 3
|
||||
#define BACK_BOTTOM_LEFT 4
|
||||
#define BACK_BOTTOM_RIGHT 5
|
||||
#define BACK_TOP_LEFT 6
|
||||
#define BACK_TOP_RIGHT 7
|
||||
|
||||
#define TreeType unsigned char
|
||||
#define STANDARD 0
|
||||
#define MULTIROOT 1
|
||||
#define HIERARCHICAL 2
|
||||
#define ONLYMATERIAL 3
|
||||
#define UNIQUEINDEX 4
|
||||
#define BITTREES 5
|
||||
#define RANDOM 6
|
||||
|
||||
// Only allow parallel delete on windows...
|
||||
#if _WIN32 || _WIN64
|
||||
#define ALLOW_PARALLEL_DELETE
|
||||
#endif
|
||||
|
||||
// Check windows
|
||||
#if _WIN32 || _WIN64
|
||||
#if _WIN64
|
||||
#define ENVIRONMENT64
|
||||
#else
|
||||
#define ENVIRONMENT32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Check GCC
|
||||
#if __GNUC__
|
||||
#if __x86_64__ || __ppc64__
|
||||
#define ENVIRONMENT64
|
||||
#else
|
||||
#define ENVIRONMENT32
|
||||
#endif
|
||||
#endif
|
||||
48
Research/core/Hashers.h
Normal file
48
Research/core/Hashers.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "Defines.h"
|
||||
#include "../inc/glm/common.hpp"
|
||||
#include "BitHelper.h"
|
||||
|
||||
namespace std {
|
||||
// Perfect hash
|
||||
template<>
|
||||
struct hash<glm::u8vec3> {
|
||||
size_t operator()(glm::u8vec3 const &value) const {
|
||||
return (((size_t) value.x) << 16) + (((size_t) value.y) << 8) + value.z;
|
||||
}
|
||||
};
|
||||
|
||||
// Perfect hash for 64 bit
|
||||
template<>
|
||||
struct hash<glm::u16vec3> {
|
||||
size_t operator()(glm::u16vec3 const &value) const {
|
||||
#ifdef ENVIRONMENT64
|
||||
return (((size_t)value.x) << 32) | (((size_t)value.y) << 16) | (size_t)value.z;
|
||||
#else
|
||||
return (((size_t) value.x) << 16) ^ (((size_t) value.y) << 8) ^ (size_t) value.z;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<glm::u16vec2> {
|
||||
size_t operator()(glm::u16vec2 const &value) const {
|
||||
// Perfect hash even for 32 bits
|
||||
return (size_t) value.x << 16 | value.y;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<glm::uvec3> {
|
||||
size_t operator()(glm::uvec3 const &value) const {
|
||||
#ifdef ENVIRONMENT64
|
||||
return (((size_t)value.x) << 32) ^ (((size_t)value.y) << 16) ^ (size_t)value.z;
|
||||
#else
|
||||
return BitHelper::CircularShiftLeft(value.x, unsigned(16)) ^
|
||||
BitHelper::CircularShiftLeft(value.y, unsigned(8)) ^ value.z;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
81
Research/core/IntersectTests.cpp
Normal file
81
Research/core/IntersectTests.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "IntersectTests.h"
|
||||
|
||||
//************************************
|
||||
// Check if AABB and triangle intersect using the separating axis theorem
|
||||
//************************************
|
||||
bool IntersectTests::BoxTriangleIntersection(glm::vec3 minPosition, glm::vec3 maxPosition, glm::vec3 triangle[3]) {
|
||||
|
||||
// Box center and half vector
|
||||
glm::vec3 c = .5f * (minPosition + maxPosition);
|
||||
glm::vec3 h = maxPosition - c;
|
||||
|
||||
// Translate triangle to center box at origin
|
||||
glm::vec3 v0 = triangle[0] - c;
|
||||
glm::vec3 v1 = triangle[1] - c;
|
||||
glm::vec3 v2 = triangle[2] - c;
|
||||
|
||||
// Crossproduct(edge from tri, {x,y,z}-directin), this gives 3x3=9 more tests
|
||||
glm::vec2 hx = glm::vec2(h.y, h.z);
|
||||
glm::vec2 hy = glm::vec2(h.x, h.z);
|
||||
glm::vec2 hz = glm::vec2(h.x, h.y);
|
||||
|
||||
glm::vec2 v0x = glm::vec2(v0.y, v0.z);
|
||||
glm::vec2 v0y = glm::vec2(v0.x, v0.z);
|
||||
glm::vec2 v0z = glm::vec2(v0.x, v0.y);
|
||||
glm::vec2 v1x = glm::vec2(v1.y, v1.z);
|
||||
glm::vec2 v1y = glm::vec2(v1.x, v1.z);
|
||||
glm::vec2 v1z = glm::vec2(v1.x, v1.y);
|
||||
glm::vec2 v2x = glm::vec2(v2.y, v2.z);
|
||||
glm::vec2 v2y = glm::vec2(v2.x, v2.z);
|
||||
glm::vec2 v2z = glm::vec2(v2.x, v2.y);
|
||||
|
||||
// Triangle edges and normal
|
||||
glm::vec3 e0 = glm::vec3(v1 - v0);
|
||||
glm::vec3 fe0 = glm::abs(e0);
|
||||
if (AxisTest(e0.z, e0.y, fe0.z, fe0.y, v0x, v2x, hx) ||
|
||||
AxisTest(e0.z, e0.x, fe0.z, fe0.x, v0y, v2y, hy) ||
|
||||
AxisTest(e0.y, e0.x, fe0.y, fe0.x, v1z, v2z, hz))
|
||||
return false;
|
||||
|
||||
glm::vec3 e1 = glm::vec3(v2 - v1);
|
||||
glm::vec3 fe1 = glm::abs(e1);
|
||||
if (AxisTest(e1.z, e1.y, fe1.z, fe1.y, v0x, v2x, hx) ||
|
||||
AxisTest(e1.z, e1.x, fe1.z, fe1.x, v0y, v2y, hy) ||
|
||||
AxisTest(e1.y, e1.x, fe1.y, fe1.x, v0z, v1z, hz))
|
||||
return false;
|
||||
|
||||
glm::vec3 e2 = glm::vec3(v0 - v2);
|
||||
glm::vec3 fe2 = glm::abs(e2);
|
||||
if (AxisTest(e2.z, e2.y, fe2.z, fe2.y, v0x, v1x, hx) ||
|
||||
AxisTest(e2.z, e2.x, fe2.z, fe2.x, v0y, v1y, hy) ||
|
||||
AxisTest(e2.y, e2.x, fe2.y, fe2.x, v1z, v2z, hz))
|
||||
return false;
|
||||
|
||||
// Test box against minimal AABB around triangle
|
||||
glm::vec3 vMin = glm::min(v0, v1);
|
||||
vMin = glm::min(vMin, v2);
|
||||
glm::vec3 vMax = glm::max(v0, v1);
|
||||
vMax = glm::max(vMax, v2);
|
||||
|
||||
if (glm::any(glm::greaterThan(vMin, h)) || glm::any(glm::lessThan(vMax, -h)))
|
||||
return false;
|
||||
|
||||
// Test box against triangle plane
|
||||
glm::vec3 n = glm::cross(e0, e1);
|
||||
vMin = -glm::sign(n) * h - v0;
|
||||
vMax = glm::sign(n) * h - v0;
|
||||
if (glm::dot(n, vMin) > 0.f || glm::dot(n, vMax) < 0.f)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Help method
|
||||
//************************************
|
||||
bool IntersectTests::AxisTest(float a, float b, float fa, float fb, glm::vec2 v0, glm::vec2 v1, glm::vec2 h) {
|
||||
float p0 = a*v0.x - b*v0.y;
|
||||
float p1 = a*v1.x - b*v1.y;
|
||||
float rad = fa * h.x + fb * h.y;
|
||||
return glm::min(p0, p1) > rad || glm::max(p0, p1) < -rad;
|
||||
}
|
||||
10
Research/core/IntersectTests.h
Normal file
10
Research/core/IntersectTests.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "../inc/glm/glm.hpp"
|
||||
|
||||
class IntersectTests
|
||||
{
|
||||
public:
|
||||
static bool BoxTriangleIntersection(glm::vec3 minPosition, glm::vec3 maxPosition, glm::vec3 triangle[3]);
|
||||
private:
|
||||
static bool AxisTest(float a, float b, float fa, float fb, glm::vec2 v0, glm::vec2 v1, glm::vec2 h);
|
||||
};
|
||||
22
Research/core/LightPath.cpp
Normal file
22
Research/core/LightPath.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "LightPath.h"
|
||||
#include "MathHelper.h"
|
||||
#include "../inc/glm/glm.hpp"
|
||||
|
||||
LightState LightStateInterpolator::operator()(const LightState& before, const LightState& after, const double& time)
|
||||
{
|
||||
float t = (float)time;
|
||||
LightState res;
|
||||
res.lightType = before.lightType;
|
||||
res.active = t < 0.5 ? before.active : after.active;
|
||||
|
||||
if (before.lightType != after.lightType)
|
||||
res.lightProps = before.lightProps;
|
||||
else
|
||||
{
|
||||
if (res.lightType == LightState::LightType::Directional)
|
||||
res.lightProps = MathHelper::slerp(t, before.lightProps, after.lightProps);
|
||||
else if (res.lightType == LightState::LightType::Point)
|
||||
res.lightProps = MathHelper::lerp(t, before.lightProps, after.lightProps);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
21
Research/core/LightPath.h
Normal file
21
Research/core/LightPath.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../inc/glm/common.hpp"
|
||||
#include "../scene/DirectionalLight.h"
|
||||
#include "../scene/PointLight.h"
|
||||
#include "Util/Path.h"
|
||||
|
||||
struct LightState
|
||||
{
|
||||
enum LightType { Point, Directional };
|
||||
glm::vec3 lightProps;
|
||||
LightType lightType;
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct LightStateInterpolator {
|
||||
LightState operator()(const LightState& before, const LightState& after, const double& time);
|
||||
};
|
||||
|
||||
typedef Path<LightState, LightStateInterpolator> LightPath;
|
||||
39
Research/core/MathHelper.h
Normal file
39
Research/core/MathHelper.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "Defines.h"
|
||||
#include "../inc/glm/glm.hpp"
|
||||
|
||||
class MathHelper
|
||||
{
|
||||
public:
|
||||
// Given a time (t between 0 and 1) and two values, linearly interpolates between the values
|
||||
template<typename T>
|
||||
static T lerp(float t, T a, T b)
|
||||
{
|
||||
return ((T(1) - t) * a) + (b * t);
|
||||
}
|
||||
|
||||
// Spherical linear interpolation of two vectors (start and end)
|
||||
static glm::vec3 slerp(float t, glm::vec3 a, glm::vec3 b)
|
||||
{
|
||||
// Dot product - the cosine of the angle between 2 vectors.
|
||||
float dot = glm::dot(a, b);
|
||||
// Clamp it to be in the range of Acos()
|
||||
// This may be unnecessary, but floating point
|
||||
// precision can be a fickle mistress.
|
||||
glm::clamp(dot, -1.0f, 1.0f);
|
||||
// Acos(dot) returns the angle between start and end,
|
||||
// And multiplying that by percent returns the angle between
|
||||
// start and the final result.
|
||||
float theta = glm::acos(dot) * t;
|
||||
glm::vec3 relativeVec = glm::normalize(b - a * dot); // Orthonormal basis
|
||||
// The final result.
|
||||
return ((a * glm::cos(theta)) + (relativeVec * glm::sin(theta)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static T DivideRoundUp(T numerator, T denominator)
|
||||
{
|
||||
return (numerator / denominator) + (numerator % denominator == 0 ? 0 : 1);
|
||||
}
|
||||
};
|
||||
|
||||
87
Research/core/OctreeBuilder/BaseMaterialOctreeBuilder.h
Normal file
87
Research/core/OctreeBuilder/BaseMaterialOctreeBuilder.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#include "BaseOctreeBuilder.h"
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include <unordered_set>
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../scene/Material/SmallNormal.h"
|
||||
#include "../../scene/Material/ColorAndNormal.h"
|
||||
#include "../../scene/Material/ColorAndNormalAndValue.h"
|
||||
#include "../../scene/Material/ColorAndOpacity.h"
|
||||
#include "../Voxelizer/VoxelInfo.h"
|
||||
|
||||
template<typename T> struct MaterialAbbreviation { std::string operator()() const { return ""; } };
|
||||
template<> struct MaterialAbbreviation<Color> { std::string operator()() const { return "c"; } };
|
||||
template<> struct MaterialAbbreviation<SmallNormal> { std::string operator()() const { return "n"; } };
|
||||
template<> struct MaterialAbbreviation<ColorAndNormal> { std::string operator()() const { return "cn"; } };
|
||||
template<> struct MaterialAbbreviation<ColorAndOpacity> { std::string operator()() const { return "co"; } };
|
||||
template<> struct MaterialAbbreviation<ColorAndNormalAndValue> { std::string operator()() const { return "cnr"; } };
|
||||
|
||||
template <typename T> struct MaterialName { std::string operator()() const { return "" } };
|
||||
template<> struct MaterialName<Color> { std::string operator()() const { return "color"; } };
|
||||
template<> struct MaterialName<SmallNormal> { std::string operator()() const { return "normal"; } };
|
||||
template<> struct MaterialName<ColorAndNormal> { std::string operator()() const { return "color and normal"; } };
|
||||
template<> struct MaterialName<ColorAndOpacity> { std::string operator()() const { return "color and opacity"; } };
|
||||
template<> struct MaterialName<ColorAndNormalAndValue> { std::string operator()() const { return "color, normal and value"; } };
|
||||
|
||||
template<typename T> struct DefaultMaterials { std::vector<T> operator()() const { return std::vector<T>(); } };
|
||||
template<> struct DefaultMaterials<SmallNormal> { std::vector<SmallNormal> operator()() const { return SmallNormal::GetAll(); } };
|
||||
|
||||
template<typename T>
|
||||
class BaseMaterialOctreeBuilder : public BaseOctreeBuilder
|
||||
{
|
||||
public:
|
||||
BaseMaterialOctreeBuilder() : BaseOctreeBuilder() {}
|
||||
~BaseMaterialOctreeBuilder() override {}
|
||||
protected:
|
||||
T GetMaterial(const VoxelInfo& voxel) const { printf("Error: Material not implemented!"); }
|
||||
bool RequiresColors() const override { return true; }
|
||||
bool RequiresNormals() const override { return true; }
|
||||
bool RequiresReflectivity() const override { return true; }
|
||||
|
||||
virtual void PreProcessNode(const glm::uvec3& coordinate, const T& material) {}
|
||||
void PreProcessNode(const VoxelInfo& voxel) override { PreProcessNode(voxel.position, GetMaterial(voxel)); }
|
||||
virtual void PreProcessMissingNode(const glm::uvec3& coordinate, const T& material) {}
|
||||
void PreProcessMissingNode(const VoxelInfo& voxel) override { PreProcessMissingNode(voxel.position, GetMaterial(voxel)); }
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
virtual void AddNode(const glm::uvec3& coordinate, const T& color) = 0;
|
||||
void AddNode(const VoxelInfo& voxel) override { AddNode(voxel.position, GetMaterial(voxel)); }
|
||||
virtual void AddMissingNode(const glm::uvec3& coordinate, const T& color) { printf("Warning: Missing nodes not caught!"); }
|
||||
void AddMissingNode(const VoxelInfo& voxel) override { AddMissingNode(voxel.position, GetMaterial(voxel)); }
|
||||
private:
|
||||
};
|
||||
|
||||
template<> bool BaseMaterialOctreeBuilder<Color>::RequiresNormals() const { return false; }
|
||||
template<> bool BaseMaterialOctreeBuilder<Color>::RequiresReflectivity() const { return false; }
|
||||
template<> Color BaseMaterialOctreeBuilder<Color>::GetMaterial(const VoxelInfo& voxel) const { return Color(voxel.color); }
|
||||
|
||||
// Hack to render the normals (debugging)
|
||||
//template<> BaseMaterialOctreeBuilder<Color>::BaseMaterialOctreeBuilder() { readColors = false; readNormals = true; }
|
||||
//template<> Color BaseMaterialOctreeBuilder<Color>::GetMaterial(const VoxelInfo& voxel)
|
||||
//{
|
||||
// glm::vec3 scaledNormal = SmallNormal(voxel.normal).Get();
|
||||
// scaledNormal = scaledNormal * 128.0f + 128.f;
|
||||
// return Color(glm::u8vec3(scaledNormal));
|
||||
//
|
||||
//}
|
||||
|
||||
template<> bool BaseMaterialOctreeBuilder<SmallNormal>::RequiresColors() const { return false; }
|
||||
template<> bool BaseMaterialOctreeBuilder<SmallNormal>::RequiresReflectivity() const { return false; }
|
||||
template<> SmallNormal BaseMaterialOctreeBuilder<SmallNormal>::GetMaterial(const VoxelInfo& voxel) const { return SmallNormal(voxel.normal); }
|
||||
|
||||
template<> bool BaseMaterialOctreeBuilder<ColorAndNormal>::RequiresReflectivity() const { return false; }
|
||||
template<> ColorAndNormal BaseMaterialOctreeBuilder<ColorAndNormal>::GetMaterial(const VoxelInfo& voxel) const {
|
||||
return ColorAndNormal(voxel.color, voxel.normal);
|
||||
}
|
||||
|
||||
template<> ColorAndNormalAndValue BaseMaterialOctreeBuilder<ColorAndNormalAndValue>::GetMaterial(const VoxelInfo& voxel) const
|
||||
{
|
||||
return ColorAndNormalAndValue(voxel.color, NormalAndValue(voxel.normal, (unsigned8)(voxel.reflectivity * 255.0f)));
|
||||
}
|
||||
|
||||
template<> bool BaseMaterialOctreeBuilder<ColorAndOpacity>::RequiresNormals() const { return false; }
|
||||
template<> ColorAndOpacity BaseMaterialOctreeBuilder<ColorAndOpacity>::GetMaterial(const VoxelInfo& voxel) const
|
||||
{
|
||||
return ColorAndOpacity(voxel.color, (unsigned32)(glm::clamp(voxel.reflectivity, 0.f, 1.f) * 65535.f));
|
||||
}
|
||||
105
Research/core/OctreeBuilder/BaseOctreeBuilder.cpp
Normal file
105
Research/core/OctreeBuilder/BaseOctreeBuilder.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "BaseOctreeBuilder.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
#include "../Voxelizer/VoxelInfo.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeBuilderStatistics.h"
|
||||
#include "../Voxelizer/BaseVoxelizer.h"
|
||||
|
||||
OctreeBuilderStatistics BaseOctreeBuilder::BuildOctree(BaseVoxelizer& voxelizer, unsigned8 level, unsigned8 maxSingle)
|
||||
{
|
||||
// Initialize some variables used for building
|
||||
unsigned8 singlePassDepth = (maxSingle < level) ? maxSingle : level;
|
||||
unsigned8 singlePassLevel = level - singlePassDepth;
|
||||
maxTreeDepth = level;
|
||||
maxSinglePassLayers = maxSingle;
|
||||
OctreeBuilderStatistics stats(level);
|
||||
stats.type = GetTreeType();
|
||||
Stopwatch watch;
|
||||
Stopwatch mainWatch; mainWatch.Reset();
|
||||
|
||||
// Find out which subtrees to builds
|
||||
watch.Reset();
|
||||
mValidCoords = voxelizer.GetValidCoords(singlePassLevel);
|
||||
std::sort(mValidCoords.begin(), mValidCoords.end(), [&](const glm::uvec3& coord1, const glm::uvec3& coord2) { return SubTreeCompare(coord1, coord2); });
|
||||
stats.validCoordBuildtime = watch.GetTime();
|
||||
|
||||
// Do a pass through all voxels in the scene for preprocessing
|
||||
watch.Reset();
|
||||
if (UsePreprocessing())
|
||||
{
|
||||
Stopwatch smallWatch;
|
||||
InitPreprocessing();
|
||||
unsigned i = 0;
|
||||
for (const glm::uvec3& coord : mValidCoords)
|
||||
{
|
||||
smallWatch.Reset();
|
||||
if (verbose) printf("Preprocessing subtree %llu / %llu at coordinate (%u, %u, %u).\n", (unsigned64)(i + 1), (unsigned64)(mValidCoords.size()), coord.x, coord.y, coord.z);
|
||||
InitCurPreprocessPass(coord);
|
||||
voxelizer.Voxelize(level, singlePassDepth, coord, [&](const VoxelInfo& info, bool best)
|
||||
{
|
||||
if (best) PreProcessNode(info);
|
||||
else PreProcessMissingNode(info);
|
||||
}, RequiresColors(), RequiresNormals(), RequiresReflectivity());
|
||||
FinalizeCurPreprocessPass(coord);
|
||||
if (verbose) printf("Subtree processed in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
i++;
|
||||
}
|
||||
FinalizePreprocessing();
|
||||
}
|
||||
|
||||
// Voxelize the scene and build the tree
|
||||
InitTree();
|
||||
Stopwatch smallWatch;
|
||||
unsigned i = 0;
|
||||
for (const glm::uvec3& coord : mValidCoords)
|
||||
{
|
||||
smallWatch.Reset();
|
||||
if (verbose) printf("Processing subtree %llu / %llu at coordinate (%u, %u, %u).\n", (unsigned64)(i + 1), (unsigned64)(mValidCoords.size()), coord.x, coord.y, coord.z);
|
||||
if (!IsSinglePass() && !mForceRebuild && CancelCurPassTree(coord))
|
||||
{
|
||||
if (verbose) printf("Subtree processing cancelled.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
InitCurPassTree(coord);
|
||||
watch.Reset();
|
||||
voxelizer.Voxelize(level, singlePassDepth, coord, [&](const VoxelInfo& info, bool best)
|
||||
{
|
||||
if (best) AddNode(info);
|
||||
else AddMissingNode(info);
|
||||
}, RequiresColors(), RequiresNormals(), RequiresReflectivity());
|
||||
stats.depthPeelingTime += watch.GetTime(); watch.Reset();
|
||||
FinalizeCurPassTree(coord);
|
||||
stats.finalizationTime += watch.GetTime();
|
||||
if (verbose) printf("Subtree processed in %d ms.\n", (int)(smallWatch.GetTime() * 1000));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
watch.Reset();
|
||||
FinalizeTree();
|
||||
stats.finalizationTime += watch.GetTime();
|
||||
|
||||
// Calculate and show statistics
|
||||
stats.totalTime = mainWatch.GetTime();
|
||||
stats.octreeNodesPerLevel = GetOctreeNodesPerLevel();
|
||||
stats.dagNodesPerLevel = GetNodesPerLevel();
|
||||
|
||||
if (verbose) stats.Print();
|
||||
|
||||
TerminateTree();
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
bool BaseOctreeBuilder::SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const
|
||||
{
|
||||
if (coord1.z != coord2.z)
|
||||
return coord1.z < coord2.z;
|
||||
if (coord1.y != coord2.y)
|
||||
return coord1.y < coord2.y;
|
||||
if (coord1.x != coord2.x)
|
||||
return coord1.x < coord2.x;
|
||||
return false;
|
||||
}
|
||||
91
Research/core/OctreeBuilder/BaseOctreeBuilder.h
Normal file
91
Research/core/OctreeBuilder/BaseOctreeBuilder.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#ifndef GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_RADIANS
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
struct VoxelInfo;
|
||||
struct OctreeBuilderStatistics;
|
||||
class BaseVoxelizer;
|
||||
|
||||
// BaseOctreeBuilder handles octree generation with any base voxelizer.
|
||||
// Note that this class is abstract and will call several methods that any actual octree builder can implement.
|
||||
class BaseOctreeBuilder
|
||||
{
|
||||
public:
|
||||
BaseOctreeBuilder() :
|
||||
mForceRebuild(false)
|
||||
{}
|
||||
virtual ~BaseOctreeBuilder() {}
|
||||
|
||||
// Given a voxelizer with a loaded scene, buil
|
||||
OctreeBuilderStatistics BuildOctree(BaseVoxelizer& voxelizer, unsigned8 level, unsigned8 maxSingle);
|
||||
|
||||
virtual std::string GetTreeType() = 0;
|
||||
|
||||
void SetOutputFile(std::string outputFile) { mOutputFile = outputFile; }
|
||||
std::string GetOutputFile() const { return mOutputFile; }\
|
||||
|
||||
void SetVerbose(bool verbose) { this->verbose = verbose; }
|
||||
bool GetVerbose() const { return verbose; }
|
||||
|
||||
void SetForceRebuild(bool force) { mForceRebuild = force; }
|
||||
bool GetForceRebuild() const { return mForceRebuild; }
|
||||
protected:
|
||||
virtual bool UsePreprocessing() const { return false; }
|
||||
virtual bool RequiresColors() const = 0;
|
||||
virtual bool RequiresNormals() const = 0;
|
||||
virtual bool RequiresReflectivity() const = 0;
|
||||
|
||||
// Used to sort the subtrees for optimal building speed (some trees might be quicker to build in a certain order)
|
||||
// Should return true if the first coord should go before the second
|
||||
virtual bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const;
|
||||
|
||||
// Functions to override for preprocessing
|
||||
virtual bool CancelPreprocessing() { return false; }
|
||||
virtual void InitPreprocessing() {}
|
||||
virtual void InitCurPreprocessPass(glm::uvec3 coordinate) {}
|
||||
virtual void PreProcessNode(const VoxelInfo& voxel) { printf("Warning: PreProcessNode() not implemented!"); }
|
||||
virtual void PreProcessMissingNode(const VoxelInfo& voxel) {}
|
||||
virtual void FinalizeCurPreprocessPass(glm::uvec3 coord) {}
|
||||
virtual void FinalizePreprocessing() {}
|
||||
|
||||
// Functions to override for main tree building
|
||||
virtual void InitTree() = 0;
|
||||
virtual bool CancelCurPassTree(const glm::uvec3& coord) { return false; }
|
||||
virtual void InitCurPassTree(glm::uvec3 coord) = 0;
|
||||
virtual void AddNode(const VoxelInfo& voxel) = 0;
|
||||
virtual void AddMissingNode(const VoxelInfo& voxel) = 0;
|
||||
virtual void FinalizeCurPassTree(glm::uvec3 coord) = 0;
|
||||
virtual void FinalizeTree() = 0;
|
||||
virtual void TerminateTree() = 0;
|
||||
|
||||
virtual std::vector<size_t> GetOctreeNodesPerLevel() = 0;
|
||||
virtual std::vector<size_t> GetNodesPerLevel() = 0;
|
||||
|
||||
// Some getters that are available during construction
|
||||
// Returns "True" if this tree will be processed in a single pass
|
||||
bool IsSinglePass() const { return maxSinglePassLayers >= maxTreeDepth; }
|
||||
// Returns the depth of the main tree
|
||||
unsigned8 GetTreeDepth() const { return maxTreeDepth; }
|
||||
// Returns the maximum depth of a tree processing in a single pass
|
||||
unsigned8 GetSinglePassTreeDepth() const { return maxTreeDepth < maxSinglePassLayers ? maxTreeDepth : maxSinglePassLayers; }
|
||||
// Returns the level at which subtrees will be appended (e.g. TreeDepth - SinglePassTreeDepth)
|
||||
unsigned8 GetAppendedTreeLevel() const { return IsSinglePass() ? 0 : (maxTreeDepth - maxSinglePassLayers); }
|
||||
// Returns all coordinates that contain triangles in this scene.
|
||||
const std::vector<glm::uvec3>& GetValidCoords() const { return mValidCoords; }
|
||||
|
||||
bool verbose;
|
||||
private:
|
||||
// Override cancelling of subtree building
|
||||
bool mForceRebuild;
|
||||
std::vector<glm::uvec3> mValidCoords;
|
||||
std::string mOutputFile;
|
||||
unsigned8 maxSinglePassLayers;
|
||||
unsigned8 maxTreeDepth;
|
||||
};
|
||||
|
||||
23
Research/core/OctreeBuilder/BaseStandardOctreeBuilder.h
Normal file
23
Research/core/OctreeBuilder/BaseStandardOctreeBuilder.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "BaseOctreeBuilder.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../Voxelizer/VoxelInfo.h"
|
||||
|
||||
class BaseStandardOctreeBuilder : public BaseOctreeBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
bool RequiresColors() const override { return false; }
|
||||
bool RequiresNormals() const override { return false; }
|
||||
bool RequiresReflectivity() const override { return false; }
|
||||
protected:
|
||||
virtual void PreProcessNode(const glm::uvec3& coordinate) {}
|
||||
void PreProcessNode(const VoxelInfo& voxel) override { PreProcessNode(voxel.position); }
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
virtual void AddNode(const glm::uvec3& coordinate) = 0;
|
||||
void AddNode(const VoxelInfo& voxel) override { AddNode(voxel.position); }
|
||||
virtual void AddMissingNode(const glm::uvec3& coordinate) { printf("Warning: Missing nodes not caught!"); }
|
||||
void AddMissingNode(const VoxelInfo& voxel) override { AddMissingNode(voxel.position); }
|
||||
private:
|
||||
};
|
||||
28
Research/core/OctreeBuilder/ColorAndValueQuantizerFactory.h
Normal file
28
Research/core/OctreeBuilder/ColorAndValueQuantizerFactory.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../Comparers.h"
|
||||
#include "ColorQuantizerFactory.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorAndValueQuantizer.h"
|
||||
#include "../../scene/Material/SmallNormal.h"
|
||||
#include "../../scene/Material/BitsMaterial.h"
|
||||
|
||||
template<typename T, typename TCompare>
|
||||
class ColorAndValueQuantizerFactory
|
||||
{
|
||||
public:
|
||||
static ColorAndValueQuantizer<T, TCompare>* Create(std::string quantizerType)
|
||||
{
|
||||
BaseColorQuantizer* colorQuantizer = ColorQuantizerFactory::Create(quantizerType);
|
||||
if (colorQuantizer == NULL) return NULL;
|
||||
return new ColorAndValueQuantizer<T, TCompare>(colorQuantizer);
|
||||
}
|
||||
static std::string GetNormalizedType(std::string quantizerType) { return ColorQuantizerFactory::GetNormalizedType(quantizerType); }
|
||||
|
||||
// Returns a string that can be used as a regex to match all color quantizer types and parameters
|
||||
static std::string GetRegexMatcher() { return ColorQuantizerFactory::GetRegexMatcher(); }
|
||||
};
|
||||
|
||||
typedef ColorAndValueQuantizerFactory<SmallNormal, NormalCompare> ColorAndNormalQuantizerFactory;
|
||||
typedef ColorAndValueQuantizerFactory<BitsMaterial<16>, BitsMaterialComparer<16>> ColorAndOpacityQuantizerFactory;
|
||||
typedef ColorAndValueQuantizerFactory<MaterialPair<SmallNormal, BitsMaterial<8>>, MaterialPairCompare<SmallNormal, NormalCompare, BitsMaterial<8>, BitsMaterialComparer<8>>> ColorAndNormalAndValueQuantizerFactory;
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "MaterialMultiRootOctreeBuilder.h"
|
||||
#include "../../scene/Material/ColorChannel.h"
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<ColorChannel>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coordinate, ColorChannel());
|
||||
|
||||
unsigned8 mask = 0xFF << (8 - mBitsPerChannel);
|
||||
|
||||
mCurPassTree->AddLeafNode(coordinate, 0, ColorChannel(color.GetR() & mask));
|
||||
mCurPassTree->AddLeafNode(coordinate, 1, ColorChannel(color.GetG() & mask));
|
||||
mCurPassTree->AddLeafNode(coordinate, 2, ColorChannel(color.GetB() & mask));
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string MaterialMultiRootOctreeBuilder<ColorChannel>::GetTreeType()
|
||||
{
|
||||
return "mcc";
|
||||
}
|
||||
63
Research/core/OctreeBuilder/ColorQuantizerFactory.cpp
Normal file
63
Research/core/OctreeBuilder/ColorQuantizerFactory.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "ColorQuantizerFactory.h"
|
||||
#include "../ColorHelper.h"
|
||||
#include <regex>
|
||||
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/MaxErrorClusterer.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/XiangClusterer.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/XiangCIELABClusterer.h"
|
||||
|
||||
float ColorQuantizerFactory::GetMaxCIELABError(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizationMap)
|
||||
{
|
||||
float maxError = 0;
|
||||
for (auto value : *quantizationMap)
|
||||
{
|
||||
float error = ColorHelper::GetDeltaEFromRGB(value.first, value.second);
|
||||
if (error > maxError)
|
||||
maxError = error;
|
||||
}
|
||||
return maxError;
|
||||
}
|
||||
|
||||
BaseColorQuantizer* ColorQuantizerFactory::Create(std::string quantizerType)
|
||||
{
|
||||
if (quantizerType == "") return NULL;
|
||||
std::string maxErrorMatcherRegexStr = "de([0-9]*.[0-9]+|[0-9]+.?)";
|
||||
std::string xiangLabMatcherRegexStr = "lab([0-9]+)";
|
||||
std::string xiangMatcherRegexStr = "([0-9]+)";
|
||||
std::regex maxErrorMatcher(maxErrorMatcherRegexStr);
|
||||
std::regex xiangLabMatcher(xiangLabMatcherRegexStr);
|
||||
std::regex xiangMatcher(xiangMatcherRegexStr);
|
||||
std::smatch sm;
|
||||
|
||||
if (std::regex_match(quantizerType, sm, maxErrorMatcher))
|
||||
{
|
||||
float maxError = std::stof(sm.str(1));
|
||||
return new MaxErrorClusterer(maxError);
|
||||
}
|
||||
else if (std::regex_match(quantizerType, sm, xiangLabMatcher))
|
||||
{
|
||||
unsigned colorCount = std::stoi(sm.str(1));
|
||||
return new XiangCIELABClusterer(colorCount);
|
||||
}
|
||||
else if (std::regex_match(quantizerType, sm, xiangMatcher))
|
||||
{
|
||||
unsigned colorCount = std::stoi(sm.str(1));
|
||||
return new XiangClusterer(colorCount);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string ColorQuantizerFactory::GetNormalizedType(std::string quantizerType)
|
||||
{
|
||||
auto quantizer = Create(quantizerType);
|
||||
if (quantizer == NULL) return "";
|
||||
std::string normalizedType = quantizer->GetQuantizerDescriptor();
|
||||
delete quantizer;
|
||||
return normalizedType;
|
||||
}
|
||||
|
||||
std::string ColorQuantizerFactory::GetRegexMatcher()
|
||||
{
|
||||
return "[0-9]+|lab[0-9]+|de([0-9]*.[0-9]+|[0-9]+)";
|
||||
}
|
||||
17
Research/core/OctreeBuilder/ColorQuantizerFactory.h
Normal file
17
Research/core/OctreeBuilder/ColorQuantizerFactory.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../Comparers.h"
|
||||
|
||||
class BaseColorQuantizer;
|
||||
class ColorQuantizerFactory
|
||||
{
|
||||
public:
|
||||
static float GetMaxCIELABError(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizationMap);
|
||||
static BaseColorQuantizer* Create(std::string quantizerType);
|
||||
static std::string GetNormalizedType(std::string quantizerType);
|
||||
|
||||
// Returns a string that can be used as a regex to match all color quantizer types and parameters
|
||||
static std::string GetRegexMatcher();
|
||||
};
|
||||
|
||||
79
Research/core/OctreeBuilder/CompressedTextureFactory.h
Normal file
79
Research/core/OctreeBuilder/CompressedTextureFactory.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include "../../scene/TextureCompressor/CompressedTexture.h"
|
||||
#include "../../scene/TextureCompressor/BlockCompressedTexture.h"
|
||||
#include "../../scene/TextureCompressor/TightlyPackedTexture.h"
|
||||
#include "../../scene/TextureCompressor/PaletteBlockTexture.h"
|
||||
#include "../../scene/TextureCompressor/DagBasedTexture.h"
|
||||
#include "../../scene/TextureCompressor/MultiRootBasedTexture.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
enum CompressionType
|
||||
{
|
||||
BASIC, BLOCK, TIGHT, TIGHTBLOCK, PALETTEBLOCK, DAGBASED, MULTIROOTBASED
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CompressedTextureFactory
|
||||
{
|
||||
public:
|
||||
static CompressionType GetCompressionType(std::string type)
|
||||
{
|
||||
if (type == "b")
|
||||
return BASIC;
|
||||
if (type == "p")
|
||||
return PALETTEBLOCK;
|
||||
if (type == "d")
|
||||
return DAGBASED;
|
||||
if (type == "m")
|
||||
return MULTIROOTBASED;
|
||||
if (std::regex_match(type, std::regex("[0-9]+")))
|
||||
return BLOCK;
|
||||
if (std::regex_match(type, std::regex("t([0-9]+)")))
|
||||
return TIGHTBLOCK;
|
||||
return TIGHT;
|
||||
}
|
||||
|
||||
static CompressedTexture<T>* GetCompressedTexture(std::string type)
|
||||
{
|
||||
CompressedTexture<T>* texture = NULL;
|
||||
std::smatch sm;
|
||||
if (type == "" || type == "t")
|
||||
{
|
||||
texture = new TightlyPackedTexture<T>();
|
||||
}
|
||||
else if (type == "b") // b for basic texture (no compression)
|
||||
{
|
||||
texture = new BasicTexture<T>();
|
||||
}
|
||||
else if (type == "p") // p for palette
|
||||
{
|
||||
texture = new PaletteBlockTexture<T>();
|
||||
}
|
||||
else if (type == "d")
|
||||
{
|
||||
texture = new DagBasedTexture<T>();
|
||||
}
|
||||
else if (type == "m")
|
||||
{
|
||||
return new MultiRootBasedTexture<T>();
|
||||
}
|
||||
else if (std::regex_match(type, sm, std::regex("[0-8]+")))
|
||||
{
|
||||
unsigned blockSize = (unsigned)std::stoi(sm.str(0));
|
||||
texture = new BlockCompressedTexture<T>(blockSize);
|
||||
}
|
||||
else if (std::regex_match(type, sm, std::regex("t([0-8]+)")))
|
||||
{
|
||||
unsigned blockSize = (unsigned)std::stoi(sm.str(1));
|
||||
texture = new BlockCompressedTexture<T, TightlyPackedTexture<T>>(blockSize);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
// Returns a string that can be used as a regex to match all compressed texture types
|
||||
static std::string GetRegexMatcher()
|
||||
{
|
||||
return std::string("p|b|d|m|[0-9]+|t[0-9]*");
|
||||
}
|
||||
};
|
||||
245
Research/core/OctreeBuilder/HierarchicalMaterialOctreeBuilder.h
Normal file
245
Research/core/OctreeBuilder/HierarchicalMaterialOctreeBuilder.h
Normal file
@@ -0,0 +1,245 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include <unordered_set>
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../scene/Octree/MaterialLibraryTree.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
|
||||
|
||||
template<typename T, typename Comparer>
|
||||
class HierarchicalMaterialOctreeBuilder : public BaseMaterialOctreeBuilder<T>
|
||||
{
|
||||
public:
|
||||
HierarchicalMaterialOctreeBuilder(BaseQuantizer<T, Comparer>* quantizer = NULL) :
|
||||
mTree(NULL),
|
||||
mMaterialLibrary(NULL),
|
||||
mReduceMaterials(quantizer != NULL),
|
||||
mSceneMaterials(std::vector<T>()),
|
||||
mReplacers(NULL),
|
||||
mQuantizer(quantizer)
|
||||
{}
|
||||
|
||||
~HierarchicalMaterialOctreeBuilder() override
|
||||
{
|
||||
if (mReplacers != NULL)
|
||||
delete mReplacers;
|
||||
if (mTree != NULL)
|
||||
delete mTree;
|
||||
if (mMaterialLibrary != NULL)
|
||||
delete mMaterialLibrary;
|
||||
}
|
||||
|
||||
std::string GetTreeType() {
|
||||
return "h" + MaterialAbbreviation<T>()() +
|
||||
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
|
||||
}
|
||||
protected:
|
||||
bool MaterialHasDefaults() const { return !DefaultMaterials<T>()().empty(); }
|
||||
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* CreateTree(unsigned8 depth) const
|
||||
{
|
||||
return new MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>(depth);
|
||||
}
|
||||
|
||||
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* CreateTree(unsigned8 depth, MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>* lib) const
|
||||
{
|
||||
return new MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>(depth, lib);
|
||||
}
|
||||
|
||||
std::string GetSinglePassTreeFilename(glm::uvec3 coord)
|
||||
{
|
||||
char buffer[255];
|
||||
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool UsePreprocessing() const override { return !MaterialHasDefaults(); }
|
||||
void PreProcessNode(const glm::uvec3& coordinate, const T& mat) override { mSceneMaterials.push_back(mat); }
|
||||
void PreProcessMissingNode(const glm::uvec3& coordinate, const T& mat) override { mSceneMaterials.push_back(mat); }
|
||||
void FinalizeCurPreprocessPass(glm::uvec3 coord) override { CalculateUniqueSceneMaterials(); }
|
||||
|
||||
void InitTree() override
|
||||
{
|
||||
// Get scene materials if this is a material type that doesn't need preprocessing
|
||||
if (mSceneMaterials.empty())
|
||||
mSceneMaterials = DefaultMaterials<T>()();
|
||||
|
||||
if (mReduceMaterials)
|
||||
{
|
||||
// Quantize the scene materials
|
||||
CalculateQuantizedMaterials();
|
||||
// Store the quantized values in the mSceneMaterials
|
||||
mSceneMaterials = std::vector<T>();
|
||||
for (auto it = mReplacers->begin(); it != mReplacers->end(); it++)
|
||||
mSceneMaterials.push_back(it->second);
|
||||
CalculateUniqueSceneMaterials();
|
||||
}
|
||||
|
||||
// Initialize the material library
|
||||
Stopwatch watch; watch.Reset();
|
||||
if (verbose) printf("Initializing the material library...");
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>();
|
||||
for (auto mat : mSceneMaterials)
|
||||
mMaterialLibrary->AddMaterial(mat);
|
||||
mMaterialLibrary->Finalize();
|
||||
if (verbose) printf("Material library initialized in %u ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
void FinalizeTree() override
|
||||
{
|
||||
Stopwatch watch;
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
// If this tree was constructred in steps, we should have cache files by now to build the main tree from
|
||||
unsigned8 mainTreeDepth = GetTreeDepth();
|
||||
unsigned8 subTreeDepth = GetSinglePassTreeDepth();
|
||||
unsigned8 mainTreeLevel = mainTreeDepth - subTreeDepth;
|
||||
mTree = CreateTree(GetTreeDepth(), mMaterialLibrary);
|
||||
|
||||
unsigned32 i = 1;
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
{
|
||||
if (verbose) printf("Reading subtree at %u / %u (%u, %u, %u) from cache...\n", i, (unsigned32)GetValidCoords().size(), coord.x, coord.y, coord.z);
|
||||
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* subTree = (MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>*)OctreeLoader::ReadCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
|
||||
if (verbose) printf("Appending subtree...");
|
||||
watch.Reset();
|
||||
mTree->AppendAndMerge(coord, mainTreeLevel, subTree);
|
||||
delete subTree;
|
||||
if (verbose) printf("Subtree appended in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
i++;
|
||||
}
|
||||
|
||||
mTree->ClearOrphans();
|
||||
}
|
||||
|
||||
// Propagate the materials
|
||||
watch.Reset();
|
||||
if (verbose) printf("Propagating materials in the tree... ");
|
||||
mTree->PropagateMaterials();
|
||||
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Generate the material texture
|
||||
watch.Reset();
|
||||
if (verbose) printf("Generating material texture... ");
|
||||
mTree->GetMaterialTexture();
|
||||
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Delete the cache files
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
OctreeLoader::DeleteCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
}
|
||||
}
|
||||
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
mTree = NULL;
|
||||
mSceneMaterials.clear();
|
||||
if (mReplacers != NULL) delete mReplacers;
|
||||
mReplacers = NULL;
|
||||
}
|
||||
|
||||
bool CancelCurPassTree(const glm::uvec3& coord) override
|
||||
{
|
||||
return OctreeLoader::VerifyCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
}
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
mTree = CreateTree(GetSinglePassTreeDepth(), mMaterialLibrary);
|
||||
}
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
Stopwatch watch;
|
||||
|
||||
// Remove unused leafs and stuff
|
||||
mTree->ClearOrphans();
|
||||
|
||||
// Convert the subtree to a DAG
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG();
|
||||
if (verbose) printf("Converting took %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
|
||||
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
// Write the subtree to a cache file
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetSinglePassTreeFilename(coord), verbose);
|
||||
|
||||
delete mTree;
|
||||
mTree = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and material
|
||||
void AddNode(const glm::uvec3& coordinate, const T& mat) override
|
||||
{
|
||||
auto replacer = mat;
|
||||
if (mReduceMaterials)
|
||||
{
|
||||
auto replacerPair = mReplacers->find(mat);
|
||||
if (replacerPair != mReplacers->end())
|
||||
replacer = replacerPair->second;
|
||||
else
|
||||
{
|
||||
replacer = ParallelNearestFinder<T>()(mat, mSceneMaterials);
|
||||
mReplacers->insert(std::make_pair(mat, replacer));
|
||||
}
|
||||
}
|
||||
mTree->AddLeafNode(coordinate, replacer);
|
||||
}
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const T& mat) override
|
||||
{
|
||||
if (!mTree->HasLeaf(coordinate))
|
||||
AddNode(coordinate, mat);
|
||||
}
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
|
||||
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
|
||||
|
||||
void CalculateUniqueSceneMaterials() { CollectionHelper::Unique<T, Comparer>(mSceneMaterials); }
|
||||
void CalculateQuantizedMaterials()
|
||||
{
|
||||
if (verbose) printf("Quantizing/merging %llu materials...", (unsigned64)(mSceneMaterials.size()));
|
||||
Stopwatch watch; watch.Reset();
|
||||
|
||||
DebugQuantize();
|
||||
mReplacers = mQuantizer->QuantizeMaterials(mSceneMaterials);
|
||||
|
||||
if (verbose) printf("Quantized materials in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
watch.Reset();
|
||||
}
|
||||
|
||||
void DebugQuantize() {}
|
||||
|
||||
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* mTree;
|
||||
|
||||
bool mReduceMaterials;
|
||||
std::vector<T> mSceneMaterials;
|
||||
MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>* mMaterialLibrary;
|
||||
std::map<T, T, Comparer>* mReplacers;
|
||||
BaseQuantizer<T, Comparer>* mQuantizer;
|
||||
};
|
||||
|
||||
//template<> void HierarchicalMaterialOctreeBuilder<Color, ColorCompare>::DebugQuantize()
|
||||
//{
|
||||
// std::vector<std::string> types = { "lab256", "lab1024", "lab4096", "lab16384" };
|
||||
// for (std::string type : types)
|
||||
// {
|
||||
// printf("%s: ", type.c_str());
|
||||
// BaseColorQuantizer* quantizer = ColorQuantizerFactory::Create(type);
|
||||
// auto temp = quantizer->QuantizeMaterials(mSceneMaterials);
|
||||
// delete temp;
|
||||
// }
|
||||
//}
|
||||
|
||||
template<> bool HierarchicalMaterialOctreeBuilder<SmallNormal, NormalCompare>::MaterialHasDefaults() const { return false; }
|
||||
template<> void HierarchicalMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>::PreProcessMissingNode(const glm::uvec3& coordinate, const ColorAndNormal& mat)
|
||||
{
|
||||
mSceneMaterials.push_back(mat);
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
#include "MaterialLibraryMultiRootOctreeBuilder.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeLoader.h"
|
||||
#include "../CollectionHelper.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
#include "../../inc/tbb/concurrent_queue.h"
|
||||
|
||||
MaterialLibraryMultiRootOctreeBuilder::MaterialLibraryMultiRootOctreeBuilder(BaseQuantizer<Color, ColorCompare>* quantizer) :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mIntermediateTree(NULL),
|
||||
mQuantizeColors(quantizer != NULL),
|
||||
mQuantizer(quantizer),
|
||||
mSceneColors(std::vector<Color>()),
|
||||
mColorReplacers(std::unordered_map<Color, Color>())
|
||||
{
|
||||
// TODO: when building in steps, use preprocessing to find the materials in the upper levels
|
||||
}
|
||||
MaterialLibraryMultiRootOctreeBuilder::~MaterialLibraryMultiRootOctreeBuilder() {}
|
||||
|
||||
bool MaterialLibraryMultiRootOctreeBuilder::IsLimitedColors() const { return mQuantizeColors || !IsSinglePass(); }
|
||||
|
||||
bool MaterialLibraryMultiRootOctreeBuilder::UsePreprocessing() const { return IsLimitedColors(); }
|
||||
|
||||
std::string MaterialLibraryMultiRootOctreeBuilder::GetTreeType() { return "blc" + (mQuantizer == NULL ? "" : mQuantizer->GetQuantizerDescriptor()); }
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::InitTree()
|
||||
{
|
||||
mTree = new MaterialLibraryMultiRoot<Color, ColorCompare>(GetTreeDepth());
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::FinalizeTree()
|
||||
{
|
||||
// Store the amount of nodes the octree had for the first few levels
|
||||
auto nodesPerLevel = mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::TerminateTree()
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
mSceneColors.clear();
|
||||
mColorReplacers.clear();
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
mIntermediateTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
|
||||
mIntermediateTree->UseLeafMap(true);
|
||||
|
||||
//for (auto color : mSceneColors)
|
||||
// mIntermediateTree->AddMaterial(Color(color));
|
||||
//mIntermediateTree->FinalizeMaterials();
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
Stopwatch watch;
|
||||
|
||||
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
// Convert the intermediate tree to a DAG before building the material library multiroot tree:
|
||||
if (verbose) printf("Converting intermediate tree to a DAG...\n");
|
||||
watch.Reset();
|
||||
mIntermediateTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Propagate the materials in the intermediate tree
|
||||
if (verbose) printf("Propagating materials in the intermediate tree...");
|
||||
watch.Reset();
|
||||
mIntermediateTree->PropagateMaterials(Color::WeightedAverage);
|
||||
if (verbose) printf("Propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Quantize the colors in the material tree
|
||||
if (IsLimitedColors())
|
||||
{
|
||||
if (verbose) printf("Replacing materials by their quantized counterparts.");
|
||||
std::vector<Color> curPassMaterials = mIntermediateTree->GetUniqueMaterials();
|
||||
CollectionHelper::Unique(curPassMaterials, ColorCompare());
|
||||
std::vector<Color> curPassQuantizedMaterials(curPassMaterials.size());
|
||||
|
||||
if (verbose) printf(".");
|
||||
// For each material in the curPassMaterials, find the closest material
|
||||
tbb::concurrent_queue<size_t> newReplacers;
|
||||
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](const size_t& i)
|
||||
{
|
||||
auto cachedReplacer = mColorReplacers.find(curPassMaterials[i]);
|
||||
if (cachedReplacer != mColorReplacers.end())
|
||||
curPassQuantizedMaterials[i] = cachedReplacer->second;
|
||||
else
|
||||
{
|
||||
curPassQuantizedMaterials[i] = NearestFinder<Color>()(curPassMaterials[i], mSceneColors);
|
||||
newReplacers.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the newly found replacers to the cache
|
||||
for (auto i = newReplacers.unsafe_begin(); i != newReplacers.unsafe_end(); ++i)
|
||||
mColorReplacers.insert(std::pair<Color, Color>(curPassMaterials[*i], curPassQuantizedMaterials[*i]));
|
||||
|
||||
// Update the current scene color map
|
||||
std::unordered_map<Color, Color> quantizedColors;
|
||||
for (size_t i = 0; i < curPassMaterials.size(); i++)
|
||||
quantizedColors.insert(std::pair<Color, Color>(curPassMaterials[i], curPassQuantizedMaterials[i]));
|
||||
|
||||
if (verbose) printf(".");
|
||||
mIntermediateTree->ReplaceMaterials(quantizedColors);
|
||||
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
if (verbose) printf("%llu new materials quantized, cache now contains %llu materials.\n", (unsigned64)newReplacers.unsafe_size(), (unsigned64)mColorReplacers.size());
|
||||
}
|
||||
|
||||
// Build the actual MaterialLibaryMultiRootTree based on the intermediate tree
|
||||
if (verbose) printf("Building multiroot tree based on intermediate tree...");
|
||||
watch.Reset();
|
||||
auto curPassTree = new MaterialLibraryMultiRoot<Color, ColorCompare>(mIntermediateTree->GetMaxLevel());
|
||||
if (IsLimitedColors()) curPassTree->CreateMaterialLibrary(mSceneColors);
|
||||
curPassTree->BaseOn(mIntermediateTree, false);
|
||||
delete mIntermediateTree;
|
||||
|
||||
curPassTree->ShaveEquals();
|
||||
curPassTree->ToDAG();
|
||||
curPassTree->FillEmptySpace(false);
|
||||
if (verbose) printf("Multiroot tree build in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Convert the current pass tree to a DAG
|
||||
if (verbose) printf("Converting the current pass multiroot tree to a DAG...\n");
|
||||
watch.Reset();
|
||||
curPassTree->ToDAG();
|
||||
if (verbose) printf("Converted in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
auto octreeNodesPerLevel = curPassTree->GetOctreeNodesPerLevel();
|
||||
|
||||
if (IsSinglePass()) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = curPassTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf("Appending subtree... ");
|
||||
watch.Reset();
|
||||
mTree->Append(coord, GetAppendedTreeLevel(), curPassTree);
|
||||
delete curPassTree;
|
||||
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG(GetAppendedTreeLevel());
|
||||
if (verbose) printf("Converted in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mIntermediateTree;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
if (!mIntermediateTree->HasLeaf(coordinate))
|
||||
AddNode(coordinate, color);
|
||||
}
|
||||
void MaterialLibraryMultiRootOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
if (IsLimitedColors())
|
||||
{
|
||||
auto replacer = mColorReplacers.find(color);
|
||||
if (replacer == mColorReplacers.end())
|
||||
{
|
||||
Color replacement = NearestFinder<Color>()(color, mSceneColors);
|
||||
mColorReplacers.insert(std::pair<Color, Color>(color, replacement));
|
||||
replacer = mColorReplacers.find(color);
|
||||
}
|
||||
assert(replacer != mColorReplacers.end());
|
||||
mIntermediateTree->AddLeafNode(coordinate, replacer->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
mIntermediateTree->AddLeafNode(coordinate, Color(color));
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// ---------------------------------PREPROCESSING------------------------------------
|
||||
//-----------------------------------------------------------------------------------
|
||||
void MaterialLibraryMultiRootOctreeBuilder::PreProcessNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mSceneColors.push_back(color);
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::FinalizePreprocessing()
|
||||
{
|
||||
CalculateUniqueSceneColors();
|
||||
if (mQuantizer != NULL)
|
||||
{
|
||||
if (verbose) printf("Quantizing %llu materials...", (unsigned64)mSceneColors.size());
|
||||
Stopwatch watch; watch.Reset();
|
||||
auto quantizedMaterials = mQuantizer->QuantizeMaterials(mSceneColors);
|
||||
mSceneColors.clear();
|
||||
for (auto quantizedMaterial : *quantizedMaterials)
|
||||
{
|
||||
mColorReplacers.insert(std::pair<Color, Color>(quantizedMaterial.first, quantizedMaterial.second));
|
||||
mSceneColors.push_back(quantizedMaterial.second);
|
||||
}
|
||||
CalculateUniqueSceneColors();
|
||||
if (verbose) printf("Quantized in %u ms, %llu materials left\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mSceneColors.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Color color : mSceneColors)
|
||||
mColorReplacers.insert(std::pair<Color, Color>(color, color));
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialLibraryMultiRootOctreeBuilder::CalculateUniqueSceneColors()
|
||||
{
|
||||
// Remove duplicates from scene colors
|
||||
CollectionHelper::Unique(mSceneColors, ColorCompare());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// -----------------------------------STATISTICS-------------------------------------
|
||||
//-----------------------------------------------------------------------------------
|
||||
std::vector<size_t> MaterialLibraryMultiRootOctreeBuilder::GetOctreeNodesPerLevel()
|
||||
{
|
||||
return mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
std::vector<size_t> MaterialLibraryMultiRootOctreeBuilder::GetNodesPerLevel()
|
||||
{
|
||||
return mTree->GetNodesPerLevel();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../scene/Octree/MaterialLibraryMultiRootTree.h"
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
#include "../Comparers.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
|
||||
|
||||
class MaterialLibraryMultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
|
||||
{
|
||||
public:
|
||||
MaterialLibraryMultiRootOctreeBuilder(BaseQuantizer<Color, ColorCompare>* quantizer = NULL);
|
||||
~MaterialLibraryMultiRootOctreeBuilder() override;
|
||||
|
||||
std::string GetTreeType() override;
|
||||
protected:
|
||||
// Initialize the main tree
|
||||
void InitTree() override;
|
||||
void FinalizeTree() override;
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override;
|
||||
|
||||
bool IsLimitedColors() const;
|
||||
bool UsePreprocessing() const override;
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override;
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override;
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
|
||||
void PreProcessNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
void FinalizePreprocessing() override;
|
||||
void CalculateUniqueSceneColors();
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override;
|
||||
std::vector<size_t> GetNodesPerLevel() override;
|
||||
private:
|
||||
MaterialLibraryMultiRoot<Color, ColorCompare>* mTree;
|
||||
|
||||
MaterialTree<Color, ColorCompare>* mIntermediateTree;
|
||||
|
||||
bool mQuantizeColors;
|
||||
BaseQuantizer<Color, ColorCompare>* mQuantizer;
|
||||
std::vector<Color> mSceneColors;
|
||||
std::unordered_map<Color, Color> mColorReplacers;
|
||||
};
|
||||
|
||||
113
Research/core/OctreeBuilder/MaterialMultiRootOctreeBuilder.h
Normal file
113
Research/core/OctreeBuilder/MaterialMultiRootOctreeBuilder.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include "../../scene/Octree/LeafMaterialMultiRootTree.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeLoader.h"
|
||||
|
||||
template <typename T>
|
||||
class MaterialMultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
|
||||
{
|
||||
private:
|
||||
LeafMaterialMultiRootTree<T>* mTree;
|
||||
|
||||
LeafMaterialMultiRootTree<T>* mCurPassTree;
|
||||
|
||||
unsigned8 mBitsPerChannel;
|
||||
|
||||
std::string mTreeType;
|
||||
|
||||
public:
|
||||
MaterialMultiRootOctreeBuilder(std::string treeType, unsigned8 bitsPerChannel) :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mCurPassTree(NULL),
|
||||
mBitsPerChannel(bitsPerChannel),
|
||||
mTreeType(treeType)
|
||||
{}
|
||||
|
||||
MaterialMultiRootOctreeBuilder(std::string treeType) : MaterialMultiRootOctreeBuilder(treeType, 8) {}
|
||||
~MaterialMultiRootOctreeBuilder() override {}
|
||||
|
||||
std::string GetTreeType() override { bool explicitSpecializationGiven = false; assert(explicitSpecializationGiven); return ""; }
|
||||
protected:
|
||||
// Initialize the main tree
|
||||
void InitTree() override
|
||||
{
|
||||
mTree = (LeafMaterialMultiRootTree<T>*)OctreeLoader::CreateTree(mTreeType, GetTreeDepth());
|
||||
}
|
||||
|
||||
void FinalizeTree() override
|
||||
{
|
||||
// Convert the final tree to a DAG
|
||||
if (verbose) printf("Converting final tree to DAG...\n");
|
||||
Stopwatch watch; watch.Reset();
|
||||
mTree->ToDAG();
|
||||
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
|
||||
}
|
||||
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
}
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
mCurPassTree = (LeafMaterialMultiRootTree<T>*)OctreeLoader::CreateTree(mTreeType, GetSinglePassTreeDepth());
|
||||
}
|
||||
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
Stopwatch watch;
|
||||
|
||||
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
if (IsSinglePass()) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = mCurPassTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
|
||||
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
|
||||
// 209.922 seconds without early converting
|
||||
// 163.645 seconds with early converting
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
mCurPassTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (verbose) printf("Appending subtree... ");
|
||||
mTree->Append(coord, GetAppendedTreeLevel(), mCurPassTree);
|
||||
delete mCurPassTree;
|
||||
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
mTree->ToDAG(GetAppendedTreeLevel());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mCurPassTree;
|
||||
}
|
||||
}
|
||||
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override
|
||||
{
|
||||
if (!mCurPassTree->HasLeaf(coordinate))
|
||||
AddNode(coordinate, color);
|
||||
}
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const Color& color) override
|
||||
{
|
||||
((Root*)mCurPassTree)->AddLeafNode(coordinate);
|
||||
}
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
|
||||
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
|
||||
};
|
||||
|
||||
151
Research/core/OctreeBuilder/MultiBitsMultiRootOctreeBuilder.h
Normal file
151
Research/core/OctreeBuilder/MultiBitsMultiRootOctreeBuilder.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
#include "MaterialMultiRootOctreeBuilder.h"
|
||||
#include "../../scene/Octree/MultiRootBitsTree.h"
|
||||
#include "../BitHelper.h"
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<1>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<1>(1));
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
for (unsigned8 i = 0; i < mBitsPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<1>(color[c], i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, mBitsPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<2>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 2;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<2>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<2>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<3>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 3;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<3>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<3>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<4>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 4;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<4>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<4>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<5>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 5;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<5>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<5>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<6>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 6;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<6>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<6>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<7>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
unsigned N = 7;
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<7>(1));
|
||||
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
for (unsigned8 c = 0; c < 3; c++)
|
||||
{
|
||||
unsigned8 maskedColor = color[c] & mask;
|
||||
for (unsigned8 i = 0; i < treesPerChannel; i++)
|
||||
{
|
||||
auto material = BitsMaterial<7>(maskedColor, N * i);
|
||||
if (material.GetValue() != 0)
|
||||
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void MaterialMultiRootOctreeBuilder<BitsMaterial<8>>::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<8>());
|
||||
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
|
||||
|
||||
mCurPassTree->AddLeafNode(coordinate, 0, BitsMaterial<8>(color.GetR() & mask));
|
||||
mCurPassTree->AddLeafNode(coordinate, 1, BitsMaterial<8>(color.GetG() & mask));
|
||||
mCurPassTree->AddLeafNode(coordinate, 2, BitsMaterial<8>(color.GetB() & mask));
|
||||
}
|
||||
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<1>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b1"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<2>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b2"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<3>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b3"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<4>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b4"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<5>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b5"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<6>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b6"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<7>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b7"; }
|
||||
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<8>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b8"; }
|
||||
120
Research/core/OctreeBuilder/MultiRootOctreeBuilder.cpp
Normal file
120
Research/core/OctreeBuilder/MultiRootOctreeBuilder.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "MultiRootOctreeBuilder.h"
|
||||
#include "../../scene/Octree/MultiRootTree.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeLoader.h"
|
||||
|
||||
MultiRootOctreeBuilder::MultiRootOctreeBuilder(unsigned8 bitsPerChannel) :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mCurPassTree(NULL),
|
||||
mBitsPerChannel(bitsPerChannel)
|
||||
{}
|
||||
|
||||
MultiRootOctreeBuilder::MultiRootOctreeBuilder() : MultiRootOctreeBuilder(8)
|
||||
{}
|
||||
|
||||
MultiRootOctreeBuilder::~MultiRootOctreeBuilder() {}
|
||||
|
||||
std::string MultiRootOctreeBuilder::GetTreeType() { return "m" + std::to_string(mBitsPerChannel); }
|
||||
|
||||
void MultiRootOctreeBuilder::InitTree()
|
||||
{
|
||||
mTree = new MultiRootTree<>(GetTreeDepth(), 3 * mBitsPerChannel);
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::FinalizeTree()
|
||||
{
|
||||
// Convert the final tree to a DAG
|
||||
Stopwatch watch;
|
||||
if (verbose) printf("Converting final tree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG();
|
||||
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::TerminateTree()
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
mCurPassTree = new MultiRootTree<>(GetSinglePassTreeDepth(), 3 * mBitsPerChannel);
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
Stopwatch watch;
|
||||
|
||||
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
if (IsSinglePass()) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = mCurPassTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
|
||||
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
|
||||
// 209.922 seconds without early converting
|
||||
// 163.645 seconds with early converting
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
mCurPassTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (verbose) printf("Appending subtree... ");
|
||||
mTree->Append(coord, GetAppendedTreeLevel(), mCurPassTree);
|
||||
delete mCurPassTree;
|
||||
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
mTree->ToDAG(GetAppendedTreeLevel());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mCurPassTree;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::UpdateMultiRoot(glm::uvec3 coord, glm::u8 color, unsigned32 slaveRootOffset)
|
||||
{
|
||||
//glm::u8 testColor = 0;
|
||||
// Add nodes to the tree for the color
|
||||
for (unsigned8 i = 0; i < mBitsPerChannel; i++)
|
||||
{
|
||||
glm::u8 mask = 1 << i;
|
||||
if ((color & mask) == mask)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coord, slaveRootOffset + i);
|
||||
//testColor += mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
if (!mCurPassTree->HasLeaf(coordinate))
|
||||
AddNode(coordinate, color);
|
||||
}
|
||||
|
||||
void MultiRootOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coordinate);
|
||||
|
||||
UpdateMultiRoot(coordinate, color.GetR(), 0);
|
||||
UpdateMultiRoot(coordinate, color.GetG(), mBitsPerChannel);
|
||||
UpdateMultiRoot(coordinate, color.GetB(), 2 * mBitsPerChannel);
|
||||
}
|
||||
|
||||
std::vector<size_t> MultiRootOctreeBuilder::GetOctreeNodesPerLevel()
|
||||
{
|
||||
return mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
std::vector<size_t> MultiRootOctreeBuilder::GetNodesPerLevel()
|
||||
{
|
||||
return mTree->GetNodesPerLevel();
|
||||
}
|
||||
40
Research/core/OctreeBuilder/MultiRootOctreeBuilder.h
Normal file
40
Research/core/OctreeBuilder/MultiRootOctreeBuilder.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include "../../scene/Octree/MultiRootTree.h"
|
||||
|
||||
class MultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
|
||||
{
|
||||
public:
|
||||
MultiRootOctreeBuilder();
|
||||
MultiRootOctreeBuilder(unsigned8 bitsPerChannel);
|
||||
~MultiRootOctreeBuilder() override;
|
||||
|
||||
std::string GetTreeType() override;
|
||||
protected:
|
||||
// Initialize the main tree
|
||||
void InitTree() override;
|
||||
void FinalizeTree() override;
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override;
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override;
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override;
|
||||
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override;
|
||||
std::vector<size_t> GetNodesPerLevel() override;
|
||||
private:
|
||||
void UpdateMultiRoot(glm::uvec3 coordinate, glm::u8 color, unsigned32 slaveRootOffset);
|
||||
|
||||
MultiRootTree<>* mTree;
|
||||
|
||||
MultiRootTree<>* mCurPassTree;
|
||||
|
||||
unsigned8 mBitsPerChannel;
|
||||
};
|
||||
|
||||
24
Research/core/OctreeBuilder/NormalQuantizerFactory.cpp
Normal file
24
Research/core/OctreeBuilder/NormalQuantizerFactory.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "NormalQuantizerFactory.h"
|
||||
|
||||
#include "../../scene/Material/MaterialQuantizer/NormalQuantizer.h"
|
||||
|
||||
BaseQuantizer<SmallNormal, NormalCompare>* NormalQuantizerFactory::Create(std::string quantizerType)
|
||||
{
|
||||
if (quantizerType == "") return NULL;
|
||||
unsigned8 bits = std::stoi(quantizerType);
|
||||
return new NormalQuantizer(bits);
|
||||
}
|
||||
|
||||
std::string NormalQuantizerFactory::GetNormalizedType(std::string quantizerType)
|
||||
{
|
||||
auto quantizer = Create(quantizerType);
|
||||
if (quantizer == NULL) return "";
|
||||
std::string normalizedType = quantizer->GetQuantizerDescriptor();
|
||||
delete quantizer;
|
||||
return normalizedType;
|
||||
}
|
||||
|
||||
std::string NormalQuantizerFactory::GetRegexMatcher()
|
||||
{
|
||||
return "4|6|8|10|12|14|16|18|20|22|24|26|28|30|32";
|
||||
}
|
||||
18
Research/core/OctreeBuilder/NormalQuantizerFactory.h
Normal file
18
Research/core/OctreeBuilder/NormalQuantizerFactory.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
|
||||
#include "../../scene/Material/SmallNormal.h"
|
||||
|
||||
typedef BaseQuantizer<SmallNormal, NormalCompare> BaseNormalQuantizer;
|
||||
|
||||
class NormalQuantizerFactory
|
||||
{
|
||||
public:
|
||||
static BaseNormalQuantizer* Create(std::string quantizerType);
|
||||
static std::string GetNormalizedType(std::string quantizerType);
|
||||
|
||||
// Returns a string that can be used as a regex to match all normal quantizer types and parameters
|
||||
static std::string GetRegexMatcher();
|
||||
};
|
||||
|
||||
217
Research/core/OctreeBuilder/OctreeBuilder.cpp
Normal file
217
Research/core/OctreeBuilder/OctreeBuilder.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "OctreeBuilder.h"
|
||||
#include <regex>
|
||||
|
||||
#include "TreeTypeParser.h"
|
||||
#include "SettingsParser.h"
|
||||
#include "../PathHelper.h"
|
||||
#include "../StringHelper.h"
|
||||
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../scene/Material/SmallNormal.h"
|
||||
#include "../../scene/Material/ColorAndOpacity.h"
|
||||
#include "../../scene/Material/ColorAndNormal.h"
|
||||
#include "../../scene/Material/ColorAndNormalAndValue.h"
|
||||
|
||||
#include "../../scene/Octree/BaseTree.h"
|
||||
#include "../../scene/Octree/ColorChannelMultiRootTree.h"
|
||||
#include "../../scene/Octree/MultiRootBitsTree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryTree.h"
|
||||
|
||||
#include "ColorQuantizerFactory.h"
|
||||
#include "NormalQuantizerFactory.h"
|
||||
#include "ColorAndValueQuantizerFactory.h"
|
||||
#include "CompressedTextureFactory.h"
|
||||
|
||||
#include "../Voxelizer/TriangleMeshVoxelizer.h"
|
||||
#include "../Voxelizer/PVMVoxelizer.h"
|
||||
|
||||
#include "StandardOctreeBuilder.h"
|
||||
#include "MultiRootOctreeBuilder.h"
|
||||
#include "ColorChannelMultiRootOctreeBuilder.h"
|
||||
#include "MultiBitsMultiRootOctreeBuilder.h"
|
||||
#include "HierarchicalMaterialOctreeBuilder.h"
|
||||
#include "UniqueIndexMaterialOctreeBuilder.h"
|
||||
#include "UniqueIndexShiftColoredOctreeBuilder.h"
|
||||
#include "RandomOctreeBuilder.h"
|
||||
#include "MaterialLibraryMultiRootOctreeBuilder.h"
|
||||
|
||||
#include "../../PropertyLoader.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
|
||||
std::string OctreeBuilder::mTreeType = "s";
|
||||
|
||||
OctreeBuilder::OctreeBuilder() {}
|
||||
|
||||
|
||||
OctreeBuilder::~OctreeBuilder() {}
|
||||
|
||||
BaseOctreeBuilder* OctreeBuilder::mInstance = NULL;
|
||||
BaseQuantizer<Color, ColorCompare>* OctreeBuilder::mColorQuantizer = NULL;
|
||||
BaseQuantizer<SmallNormal, NormalCompare>* OctreeBuilder::mNormalQuantizer = NULL;
|
||||
BaseQuantizer<ColorAndNormal, ColorAndNormalCompare>* OctreeBuilder::mColorAndNormalQuantizer = NULL;
|
||||
BaseQuantizer<ColorAndOpacity, ColorAndOpacityCompare>* OctreeBuilder::mColorAndOpacityQuantizer = NULL;
|
||||
BaseQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>* OctreeBuilder::mColorAndNormalAndValueQuantizer = NULL;
|
||||
|
||||
BaseOctreeBuilder* OctreeBuilder::Instance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
void OctreeBuilder::SetTreeType(std::string type)
|
||||
{
|
||||
if (mInstance != NULL && type != mTreeType)
|
||||
{
|
||||
Destroy();
|
||||
Create(type);
|
||||
}
|
||||
}
|
||||
std::string OctreeBuilder::GetTreeType()
|
||||
{
|
||||
return mTreeType;
|
||||
}
|
||||
|
||||
void OctreeBuilder::Create()
|
||||
{
|
||||
// Destroy any old octree builders (to prevent memory leaks)
|
||||
Destroy();
|
||||
|
||||
// Create the new octree builder
|
||||
Create(SettingsParser::GetTreeTypeFromSettings());
|
||||
}
|
||||
|
||||
void OctreeBuilder::Destroy()
|
||||
{
|
||||
if (mInstance != NULL)
|
||||
delete mInstance;
|
||||
mInstance = NULL;
|
||||
if (mColorQuantizer != NULL)
|
||||
delete mColorQuantizer;
|
||||
mColorQuantizer = NULL;
|
||||
if (mColorAndNormalQuantizer != NULL) delete mColorAndNormalQuantizer;
|
||||
mColorAndNormalQuantizer = NULL;
|
||||
}
|
||||
|
||||
OctreeBuilderStatistics OctreeBuilder::BuildOctree()
|
||||
{
|
||||
PropertyLoader::Create();
|
||||
return BuildOctree(SettingsParser::GetMaxLevelFromSettings(), PropertyLoader::Instance()->GetProperty("obj_to_dag"), PropertyLoader::Instance()->GetProperty("dag_file"));
|
||||
}
|
||||
|
||||
OctreeBuilderStatistics OctreeBuilder::BuildOctree(unsigned8 depth)
|
||||
{
|
||||
PropertyLoader::Create();
|
||||
return BuildOctree(depth, PropertyLoader::Instance()->GetProperty("obj_to_dag"), PropertyLoader::Instance()->GetProperty("dag_file"));
|
||||
}
|
||||
|
||||
OctreeBuilderStatistics OctreeBuilder::BuildOctree(unsigned8 depth, std::string sceneFile, std::string dagFile)
|
||||
{
|
||||
mInstance->SetOutputFile(dagFile);
|
||||
mInstance->SetVerbose(SettingsParser::GetVerboseFromSettings());
|
||||
mInstance->SetForceRebuild(SettingsParser::GetForceRebuildFromSettings());
|
||||
std::string filetype = PathHelper::GetExtension(sceneFile);
|
||||
StringHelper::ToUpper(filetype);
|
||||
BaseVoxelizer* voxelizer;
|
||||
if (filetype == "PVM" || filetype == "DAT")
|
||||
voxelizer = new PVMVoxelizer();
|
||||
else
|
||||
voxelizer = new TriangleMeshVoxelizer();
|
||||
voxelizer->Initialize();
|
||||
voxelizer->LoadScene(sceneFile);
|
||||
OctreeBuilderStatistics res = mInstance->BuildOctree(*voxelizer, depth, (unsigned8)PropertyLoader::Instance()->GetIntProperty("octreebuilder_max_single_pass_layers"));
|
||||
voxelizer->UnloadScene();
|
||||
delete voxelizer;
|
||||
return res;
|
||||
}
|
||||
|
||||
void OctreeBuilder::Create(std::string type)
|
||||
{
|
||||
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(type);
|
||||
if (descr.material == "c")
|
||||
mColorQuantizer = ColorQuantizerFactory::Create(descr.quantizer);
|
||||
else if (descr.material == "n")
|
||||
mNormalQuantizer = NormalQuantizerFactory::Create(descr.quantizer);
|
||||
else if (descr.material == "cn")
|
||||
mColorAndNormalQuantizer = ColorAndNormalQuantizerFactory::Create(descr.quantizer);
|
||||
else if (descr.material == "cnr")
|
||||
mColorAndNormalAndValueQuantizer = ColorAndNormalAndValueQuantizerFactory::Create(descr.quantizer);
|
||||
else if (descr.material == "co")
|
||||
mColorAndOpacityQuantizer = ColorAndOpacityQuantizerFactory::Create(descr.quantizer);
|
||||
|
||||
|
||||
|
||||
if (descr.globalType == UNIQUEINDEX)
|
||||
{
|
||||
// Parse the number of levels without material information (LOD) if it isn't empty.
|
||||
unsigned8 levelsWithoutMaterial = TreeTypeParser::GetLevelsWithoutMaterials(descr);
|
||||
if (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's')
|
||||
{
|
||||
assert(descr.material == "c");
|
||||
mInstance = new UniqueIndexShiftColoredOctreeBuilder(descr.textureCompressor, mColorQuantizer, levelsWithoutMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (descr.material == "c")
|
||||
mInstance = new UniqueIndexMaterialOctreeBuilder<Color, ColorCompare>(descr.textureCompressor, mColorQuantizer, levelsWithoutMaterial);
|
||||
else if (descr.material == "n")
|
||||
mInstance = new UniqueIndexMaterialOctreeBuilder<SmallNormal, NormalCompare>(descr.textureCompressor, mNormalQuantizer, levelsWithoutMaterial);
|
||||
else if (descr.material == "cn")
|
||||
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>(descr.textureCompressor, mColorAndNormalQuantizer, levelsWithoutMaterial);
|
||||
else if (descr.material == "cnr")
|
||||
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>(descr.textureCompressor, mColorAndNormalAndValueQuantizer, levelsWithoutMaterial);
|
||||
else if (descr.material == "co")
|
||||
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndOpacity, ColorAndOpacityCompare>(descr.textureCompressor, mColorAndOpacityQuantizer, levelsWithoutMaterial);
|
||||
}
|
||||
}
|
||||
else if (descr.globalType == HIERARCHICAL && descr.additionalTypeInfo == "")
|
||||
{
|
||||
if (descr.material == "c")
|
||||
mInstance = new HierarchicalMaterialOctreeBuilder<Color, ColorCompare>(mColorQuantizer);
|
||||
else if (descr.material == "n")
|
||||
mInstance = new HierarchicalMaterialOctreeBuilder<SmallNormal, NormalCompare>(mNormalQuantizer);
|
||||
else if (descr.material == "cn")
|
||||
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>(mColorAndNormalQuantizer);
|
||||
else if (descr.material == "cnr")
|
||||
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>(mColorAndNormalAndValueQuantizer);
|
||||
else if (descr.material == "co")
|
||||
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndOpacity, ColorAndOpacityCompare>(mColorAndOpacityQuantizer);
|
||||
}
|
||||
else if (descr.globalType == MULTIROOT)
|
||||
{
|
||||
if (descr.additionalTypeInfo == "cc")
|
||||
mInstance = new MaterialMultiRootOctreeBuilder<ColorChannel>(type);
|
||||
else if (descr.additionalTypeInfo == "" || (descr.additionalTypeInfo.size() == 1 && descr.additionalTypeInfo[0] >= 49 && descr.additionalTypeInfo[0] <= 56))
|
||||
{
|
||||
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
|
||||
mInstance = new MultiRootOctreeBuilder(bitsPerChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
|
||||
unsigned8 bitsPerTree = TreeTypeParser::GetBitsPerTree(descr);
|
||||
switch (bitsPerTree)
|
||||
{
|
||||
case 1: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<1>>(type, bitsPerChannel); break;
|
||||
case 2: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<2>>(type, bitsPerChannel); break;
|
||||
case 3: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<3>>(type, bitsPerChannel); break;
|
||||
case 4: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<4>>(type, bitsPerChannel); break;
|
||||
case 5: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<5>>(type, bitsPerChannel); break;
|
||||
case 6: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<6>>(type, bitsPerChannel); break;
|
||||
case 7: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<7>>(type, bitsPerChannel); break;
|
||||
case 8: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<8>>(type, bitsPerChannel); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (descr.globalType == RANDOM)
|
||||
{
|
||||
mInstance = new RandomOctreeBuilder();
|
||||
}
|
||||
else if (descr.globalType == BITTREES)
|
||||
{
|
||||
if (descr.additionalTypeInfo == "l" && descr.material == "c")
|
||||
mInstance = new MaterialLibraryMultiRootOctreeBuilder(mColorQuantizer);
|
||||
}
|
||||
else
|
||||
{
|
||||
mInstance = new StandardOctreeBuilder();
|
||||
}
|
||||
mTreeType = type;
|
||||
}
|
||||
46
Research/core/OctreeBuilder/OctreeBuilder.h
Normal file
46
Research/core/OctreeBuilder/OctreeBuilder.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include "OctreeBuilderStatistics.h"
|
||||
#include "BaseOctreeBuilder.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorAndValueQuantizer.h"
|
||||
//#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
|
||||
//#include "../../scene/Material/SmallNormal.h"
|
||||
|
||||
class Root;
|
||||
class BaseColorQuantizer;
|
||||
class SmallNormal;
|
||||
struct NormalCompare;
|
||||
class TriangleMeshVoxelizer;
|
||||
class PVMVoxelizer;
|
||||
|
||||
// Singleton wrapper that reads the settings and creates the correct octreebuilder
|
||||
class OctreeBuilder
|
||||
{
|
||||
friend BaseOctreeBuilder;
|
||||
public:
|
||||
static void Create();
|
||||
static void SetTreeType(std::string type);
|
||||
static std::string GetTreeType();
|
||||
|
||||
static void Destroy();
|
||||
static OctreeBuilderStatistics BuildOctree();
|
||||
static OctreeBuilderStatistics BuildOctree(unsigned8 depth);
|
||||
static OctreeBuilderStatistics BuildOctree(unsigned8 depth, std::string sceneFile, std::string dagFile);
|
||||
static BaseOctreeBuilder* Instance();
|
||||
|
||||
private:
|
||||
static BaseOctreeBuilder* mInstance;
|
||||
static BaseQuantizer<Color, ColorCompare>* mColorQuantizer;
|
||||
static BaseQuantizer<SmallNormal, NormalCompare>* mNormalQuantizer;
|
||||
static BaseQuantizer<ColorAndNormal, ColorAndNormalCompare>* mColorAndNormalQuantizer;
|
||||
static BaseQuantizer<ColorAndOpacity, ColorAndOpacityCompare>* mColorAndOpacityQuantizer;
|
||||
static BaseQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>* mColorAndNormalAndValueQuantizer;
|
||||
|
||||
static void Create(std::string type);
|
||||
|
||||
static std::string mTreeType;
|
||||
|
||||
OctreeBuilder();
|
||||
~OctreeBuilder();
|
||||
};
|
||||
|
||||
73
Research/core/OctreeBuilder/OctreeBuilderStatistics.h
Normal file
73
Research/core/OctreeBuilder/OctreeBuilderStatistics.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../Defines.h"
|
||||
|
||||
struct OctreeBuilderStatistics
|
||||
{
|
||||
public:
|
||||
unsigned8 levels;
|
||||
std::string type;
|
||||
|
||||
double validCoordBuildtime;
|
||||
double depthPeelingTime;
|
||||
double finalizationTime;
|
||||
double totalTime;
|
||||
|
||||
std::vector<size_t> octreeNodesPerLevel;
|
||||
std::vector<size_t> dagNodesPerLevel;
|
||||
|
||||
OctreeBuilderStatistics(unsigned8 levels)
|
||||
{
|
||||
this->levels = levels;
|
||||
validCoordBuildtime = depthPeelingTime = finalizationTime = totalTime = 0;
|
||||
octreeNodesPerLevel = std::vector<size_t>(levels + 1);
|
||||
dagNodesPerLevel = std::vector<size_t>(levels + 1);
|
||||
}
|
||||
|
||||
size_t GetOctreeNodeCount() const
|
||||
{
|
||||
size_t octreeNodeCount = 0;
|
||||
for (unsigned8 level = 0; level <= levels; level++)
|
||||
octreeNodeCount += octreeNodesPerLevel[level];
|
||||
return octreeNodeCount;
|
||||
}
|
||||
|
||||
size_t GetDAGNodeCount() const
|
||||
{
|
||||
size_t dagNodeCount = 0;
|
||||
for (unsigned8 level = 0; level <= levels; level++)
|
||||
dagNodeCount += dagNodesPerLevel[level];
|
||||
return dagNodeCount;
|
||||
}
|
||||
|
||||
long double GetCompression() const
|
||||
{
|
||||
return (long double)GetOctreeNodeCount() / (long double)GetDAGNodeCount();
|
||||
}
|
||||
|
||||
long double GetCompression(unsigned8 level) const
|
||||
{
|
||||
return (long double)octreeNodesPerLevel[level] / (long double)dagNodesPerLevel[level];
|
||||
}
|
||||
|
||||
void Print() const
|
||||
{
|
||||
printf("DAG created with the following properties:\n");
|
||||
printf("Total time : %10u ms\n", (unsigned)(totalTime * 1000));
|
||||
printf("Valid subtree calculation time : %10u ms\n", (unsigned)(validCoordBuildtime * 1000));
|
||||
printf("Depth peeling time : %10u ms\n", (unsigned)(depthPeelingTime * 1000));
|
||||
printf("Subtree finalization time : %10u ms\n", (unsigned)(finalizationTime * 1000));
|
||||
|
||||
printf("Total nodes : %10zu\n", GetOctreeNodeCount());
|
||||
printf("DAG nodes : %10zu\n", GetDAGNodeCount());
|
||||
printf("Compression : %10.2Lf\n", GetCompression());
|
||||
printf("\n");
|
||||
|
||||
|
||||
printf("Statistics per level:\n");
|
||||
printf("Level Octree (#) DAG (#) Compression\n");
|
||||
for (unsigned8 level = 0; level <= levels; level++)
|
||||
printf("%5d %12zu %12zu %12.2Lf\n", level, octreeNodesPerLevel[level], dagNodesPerLevel[level], GetCompression(level));
|
||||
}
|
||||
};
|
||||
476
Research/core/OctreeBuilder/OctreeConverter.cpp
Normal file
476
Research/core/OctreeBuilder/OctreeConverter.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
#include "OctreeConverter.h"
|
||||
#include "OctreeLoader.h"
|
||||
#include "SettingsParser.h"
|
||||
#include "TreeTypeParser.h"
|
||||
#include "ColorQuantizerFactory.h"
|
||||
#include "CompressedTextureFactory.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
|
||||
#include "../../PropertyLoader.h"
|
||||
#include <regex>
|
||||
|
||||
#include "../../scene/Octree/BaseTree.h"
|
||||
#include "../../scene/Octree/Tree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryTree.h"
|
||||
#include "../../scene/Octree/HierarchicalShiftingColoredTree.h"
|
||||
#include "../../scene/Octree/HierarchicalColorsOnlyTree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
bool OctreeConverter::Convert()
|
||||
{
|
||||
PropertyLoader::Create();
|
||||
auto propertyLoader = PropertyLoader::Instance();
|
||||
if (!propertyLoader->GetBoolProperty("octreebuilder_tryconvert") || !propertyLoader->GetBoolProperty("octreebuilder_usecache"))
|
||||
return false;
|
||||
|
||||
bool verbose = propertyLoader->GetProperty("octreebuilder_verbose") != "0";
|
||||
if (verbose) printf("Trying to convert an existing tree to the wanted treetype.\n");
|
||||
|
||||
std::string octreeType = propertyLoader->GetProperty("octree_type");
|
||||
unsigned8 maxLevel = propertyLoader->GetIntProperty("shader_max_level");
|
||||
std::string filename = propertyLoader->GetProperty("dag_file");
|
||||
|
||||
bool res = ConvertTo(octreeType, maxLevel, filename, verbose);
|
||||
if (verbose)
|
||||
{
|
||||
if (res) printf("Conversion succeeded\n");
|
||||
if (!res) printf("No valid conversion found\n");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertTo(std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Go through all .oct files that can support the given dag file.
|
||||
// For each of the files found, try conversion.
|
||||
std::string dagStrippedFilename = dagFileName;
|
||||
const size_t last_slash_idx = dagStrippedFilename.find_last_of("\\/");
|
||||
if (std::string::npos != last_slash_idx)
|
||||
{
|
||||
dagStrippedFilename.erase(0, last_slash_idx + 1);
|
||||
}
|
||||
|
||||
std::string validFileWildcard = std::string(dagFileName + "*.oct");
|
||||
std::regex fileNameMatcher(dagStrippedFilename + "_([0-9]+)_(.+).oct");
|
||||
std::smatch sm;
|
||||
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE hFind = FindFirstFile(validFileWildcard.c_str(), &FindFileData);
|
||||
LARGE_INTEGER filesize;
|
||||
// Find all convertable files
|
||||
std::vector<FileInfo> convertableFiles;
|
||||
while (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// Try conversion
|
||||
std::string foundFilename(FindFileData.cFileName);
|
||||
filesize.LowPart = FindFileData.nFileSizeLow;
|
||||
filesize.HighPart = FindFileData.nFileSizeHigh;
|
||||
// Extract the level and type from the filename
|
||||
if (std::regex_match(foundFilename, sm, fileNameMatcher))
|
||||
{
|
||||
unsigned8 sourceDepth = std::stoi(sm.str(1));
|
||||
std::string sourceType = sm.str(2);
|
||||
if (CanConvert(sourceType, destinationType, sourceDepth, destinationDepth))
|
||||
convertableFiles.push_back(FileInfo(foundFilename, sourceType, sourceDepth, filesize.QuadPart));
|
||||
}
|
||||
|
||||
if (!FindNextFile(hFind, &FindFileData))
|
||||
{
|
||||
FindClose(hFind);
|
||||
hFind = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
// Sort them on filesize, assuming that smaller files are always quicker to convert
|
||||
std::sort(convertableFiles.begin(), convertableFiles.end(), [&](const FileInfo& a, const FileInfo& b)
|
||||
{
|
||||
return a.filesize < b.filesize;
|
||||
});
|
||||
for (const FileInfo& info : convertableFiles)
|
||||
{
|
||||
if (Convert(info.treeType, destinationType, info.depth, destinationDepth, dagFileName, verbose))
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if a certain conversion is valid (theore
|
||||
bool OctreeConverter::CanConvert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth)
|
||||
{
|
||||
// Converting to the same type always works
|
||||
if (sourceType == destinationType && sourceDepth == destinationDepth)
|
||||
return true;
|
||||
|
||||
if (sourceDepth != destinationDepth)
|
||||
return false; // Can't change the depth...
|
||||
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
|
||||
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
|
||||
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
|
||||
|
||||
// Any tree type can be converted to standard if the source depth is at least as deep as the destination depth
|
||||
if (destDescr.globalType == STANDARD) return true;
|
||||
else if (destDescr.globalType == HIERARCHICAL)
|
||||
{
|
||||
return sourceDepth == destinationDepth
|
||||
&& sourceDescr.globalType == HIERARCHICAL
|
||||
&& sourceDescr.additionalTypeInfo == "" && (destDescr.additionalTypeInfo == "" || destDescr.additionalTypeInfo == "s")
|
||||
&& destDescr.material == sourceDescr.material
|
||||
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType);
|
||||
}
|
||||
else if (destDescr.globalType == UNIQUEINDEX)
|
||||
{
|
||||
return sourceDepth == destinationDepth
|
||||
&& sourceDescr.globalType == UNIQUEINDEX && sourceDescr.material == "c"
|
||||
&& destDescr.material == "c"
|
||||
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType)
|
||||
&& sourceDescr.additionalTypeInfo == "" && destDescr.additionalTypeInfo == "";
|
||||
}
|
||||
else if (destDescr.globalType == ONLYMATERIAL)
|
||||
{
|
||||
return sourceDepth == destinationDepth
|
||||
&& destDescr.material == "c" && sourceDescr.material == "c"
|
||||
&& sourceDescr.globalType == HIERARCHICAL
|
||||
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeConverter::Convert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
if (!CanConvert(sourceType, destinationType, sourceDepth, destinationDepth)) return false;
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
switch (destDescr.globalType)
|
||||
{
|
||||
case HIERARCHICAL:
|
||||
if (destDescr.material == "c")
|
||||
{
|
||||
if (destDescr.additionalTypeInfo == "s")
|
||||
return ConvertToHierarchicalColorShift(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
|
||||
else
|
||||
return ConvertToHierarchicalColored(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
|
||||
}
|
||||
break;
|
||||
case UNIQUEINDEX: return ConvertToUniqueIndex(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
|
||||
case STANDARD: return ConvertToStandard(sourceType, sourceDepth, destinationDepth, dagFileName, verbose);
|
||||
case ONLYMATERIAL: return ConvertToOnlyMaterials(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertToStandard(std::string sourceType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
if (verbose) printf("Found valid octree file: %s, converting...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
|
||||
BaseTree* originalTree = OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
|
||||
if (originalTree == NULL) return false;
|
||||
Tree<>* standardTree = (Tree<>*)OctreeLoader::CreateTree("s", destinationDepth);
|
||||
originalTree->Shave(destinationDepth);
|
||||
standardTree->MoveShallow(originalTree);
|
||||
delete originalTree;
|
||||
standardTree->ToDAG();
|
||||
OctreeLoader::WriteCache(standardTree, "s", dagFileName, verbose);
|
||||
delete standardTree;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CompressHierarchicalTreeColors(MaterialLibraryTree<Color, ColorCompare>* tree, std::string compressor)
|
||||
{
|
||||
std::vector<Color> leafMaterials = tree->GetLeafMaterials();
|
||||
tbb::parallel_sort(leafMaterials.begin(), leafMaterials.end(), ColorCompare());
|
||||
leafMaterials.erase(std::unique(leafMaterials.begin(), leafMaterials.end()), leafMaterials.end());
|
||||
std::vector<glm::u8vec3> leafColors(leafMaterials.size());
|
||||
tbb::parallel_for(size_t(0), leafMaterials.size(), [&](size_t i)
|
||||
{
|
||||
leafColors[i] = leafMaterials[i].GetColor();
|
||||
});
|
||||
tbb::parallel_sort(leafColors.begin(), leafColors.end(), u8vec3comparer());
|
||||
leafColors.erase(std::unique(leafColors.begin(), leafColors.end()), leafColors.end());
|
||||
|
||||
auto colorQuantizer = ColorQuantizerFactory::Create(compressor);
|
||||
auto colorReplacers = colorQuantizer->QuantizeColors(leafColors);
|
||||
std::map<Color, Color, ColorCompare> materialReplacers;
|
||||
for (auto colorReplacer = colorReplacers->begin(); colorReplacer != colorReplacers->end(); std::advance(colorReplacer, 1))
|
||||
materialReplacers.insert(std::make_pair(Color(colorReplacer->first), Color(colorReplacer->second)));
|
||||
delete colorReplacers;
|
||||
delete colorQuantizer;
|
||||
tree->ReplaceLeafMaterials(materialReplacers);
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertToHierarchicalColored(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
// Only works if trees are of the same depth. Higher depth will mean the colors are averaged which might lead to wrong results
|
||||
if (sourceDepth != destinationDepth)
|
||||
return false;
|
||||
|
||||
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
|
||||
// Make sure that the destination type is actually what is expected
|
||||
if (destDescr.globalType != HIERARCHICAL || destDescr.material != "c" || destDescr.additionalTypeInfo != "")
|
||||
return false;
|
||||
// Only colored and hierarchical trees can be converted, so they need to match this regex
|
||||
if (sourceDescr.globalType != HIERARCHICAL || sourceDescr.additionalTypeInfo != "" || sourceDescr.material != "c")
|
||||
return false;
|
||||
|
||||
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
|
||||
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
|
||||
|
||||
// Only trees with full colors or the same compression scheme as the destination type can be compressed
|
||||
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
|
||||
return false;
|
||||
|
||||
// Now there are 4 options:
|
||||
// Source is hierarchical fully colored
|
||||
// Source is not-hierarchical fully colored
|
||||
// Source is hierarchical compressed colored
|
||||
// Source is not-hierarchical compressed colored
|
||||
if (sourceDescr.globalType == HIERARCHICAL)
|
||||
{
|
||||
// Apparently we already have the desired type, so just return true
|
||||
if (normalizedDestinationQuantizerType == normalizedSourceQuantizerType)
|
||||
return true;
|
||||
|
||||
// Convertert fully colored hierarchical root to compressed colored hierarchical root
|
||||
if (verbose) printf("Converting fully colored tree in %s to compressed colored tree...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
|
||||
MaterialLibraryTree<Color, ColorCompare>* source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
|
||||
if (source == NULL)
|
||||
{
|
||||
if (verbose) printf("Reading the source tree failed...\n");
|
||||
return false;
|
||||
}
|
||||
if (verbose) printf("Quantizing original materials...\n");
|
||||
CompressHierarchicalTreeColors(source, normalizedDestinationQuantizerType);
|
||||
if (verbose) printf("Clearing propagation...\n");
|
||||
source->ClearPropagation();
|
||||
|
||||
if (verbose) printf("Recalculating DAG (since the materials are now compressed).\n");
|
||||
source->ToDAG();
|
||||
if (verbose) printf("Propagating materials...\n");
|
||||
source->PropagateMaterials();
|
||||
|
||||
OctreeLoader::WriteCache(source, destinationType, dagFileName, verbose);
|
||||
delete source;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertToUniqueIndex(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
// Conversion currently only allowed from other UniqueIndexRoots
|
||||
if (sourceDepth != destinationDepth) return false;
|
||||
|
||||
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
|
||||
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
|
||||
|
||||
// Check the source and destination type to be valid
|
||||
if (destDescr.globalType != UNIQUEINDEX || destDescr.material != "c")
|
||||
return false;
|
||||
if ((/*sourceDescr.globalType != HIERARCHICAL && sourceDescr.globalType != COLORED && */sourceDescr.globalType != UNIQUEINDEX) || sourceDescr.material != "c")
|
||||
return false;
|
||||
// Only trees with full colors or the same compression scheme as the destination type can be compressed
|
||||
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
|
||||
return false;
|
||||
if (sourceDescr.additionalTypeInfo != "" || destDescr.additionalTypeInfo != "")
|
||||
return false;
|
||||
|
||||
if (sourceDescr.globalType == UNIQUEINDEX)
|
||||
{
|
||||
// Check if the current type is not already the desired type
|
||||
if (sourceDescr.textureCompressor == destDescr.quantizer && normalizedDestinationQuantizerType == normalizedSourceQuantizerType) return true;
|
||||
|
||||
// If not, load the source tree
|
||||
if (verbose) printf("Reading tree in %s...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
|
||||
MaterialLibraryUniqueIndexTree<Color, ColorCompare>* source = (MaterialLibraryUniqueIndexTree<Color, ColorCompare>*)OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
|
||||
if (source == NULL)
|
||||
{
|
||||
if (verbose) printf("Reading the source tree failed...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
|
||||
{
|
||||
// Quantize all materials
|
||||
if (verbose) printf("Quantizing materials...");
|
||||
Stopwatch watch; watch.Reset();
|
||||
std::vector<Color> materials = source->GetUniqueMaterials();
|
||||
BaseColorQuantizer* colorQuantizer = ColorQuantizerFactory::Create(normalizedDestinationQuantizerType);
|
||||
std::map<Color, Color, ColorCompare>* colorReplacers = colorQuantizer->QuantizeMaterials(materials);
|
||||
delete colorQuantizer;
|
||||
std::unordered_map<Color, Color> colorReplacersUnorderedMap;
|
||||
for (auto colorReplacer : *colorReplacers) colorReplacersUnorderedMap.insert(colorReplacer);
|
||||
delete colorReplacers;
|
||||
if (verbose) printf("Materials quantized in %u ms.\n Replacing materials...", (unsigned)(watch.GetTime() * 1000.0));
|
||||
watch.Reset();
|
||||
source->ReplaceMaterials(colorReplacersUnorderedMap);
|
||||
if (verbose) printf("Materials replaced in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
|
||||
}
|
||||
|
||||
if (sourceDescr.textureCompressor != destDescr.textureCompressor)
|
||||
{
|
||||
// Replace the compressed texture type
|
||||
if (verbose) printf("Replacing texture compressor...");
|
||||
Stopwatch watch; watch.Reset();
|
||||
source->ReplaceCompressedTexture(CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(destDescr.textureCompressor));
|
||||
if (verbose) printf("Texture compressor replaced in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf("Recompressing texture with quantized materials...");
|
||||
Stopwatch watch; watch.Reset();
|
||||
source->Recompress();
|
||||
if (verbose) printf("Done in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
|
||||
}
|
||||
|
||||
OctreeLoader::WriteCache(source, destinationType, dagFileName, verbose);
|
||||
delete source;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertToHierarchicalColorShift(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
if (sourceDepth != destinationDepth)
|
||||
return false;
|
||||
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
|
||||
// Make sure the source and destination type is valid
|
||||
assert(destDescr.globalType == HIERARCHICAL && destDescr.material == "c" && destDescr.additionalTypeInfo == "s");
|
||||
assert(sourceDescr.globalType == HIERARCHICAL && sourceDescr.material == "c");
|
||||
|
||||
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
|
||||
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
|
||||
|
||||
// Only trees with full colors or the same compression scheme as the destination type can be compressed
|
||||
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
|
||||
return false;
|
||||
|
||||
// Remove the second characted, as that is the one that indicates that we want a shifting tree
|
||||
std::string hierarchicalColoredDestinationType = std::string(destinationType).erase(1, 1);
|
||||
|
||||
// If we're dealing with a normal "colored octree" that isn't hierarchical, or has a different compression, convert it to hierarchical first:
|
||||
if (TreeTypeParser::GetGlobalType(sourceType) != HIERARCHICAL || normalizedDestinationQuantizerType != normalizedSourceQuantizerType)
|
||||
{
|
||||
if (verbose) printf("Checking cache for hierarchical tree with correct compression...");
|
||||
if (OctreeLoader::VerifyCache(hierarchicalColoredDestinationType, sourceDepth, dagFileName))
|
||||
{
|
||||
if (verbose) printf("Cache file found.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf("Nothing found.\nConverting non-hierarchical tree %s to hierarchical tree with correct compression.\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
|
||||
if (OctreeConverter::ConvertToHierarchicalColored(sourceType, hierarchicalColoredDestinationType, sourceDepth, destinationDepth, dagFileName, verbose))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// By now we're sure that a fully colored or compressed colored hierarchical octree with the same compression exists
|
||||
// Let's convert it to a shifter:
|
||||
if (verbose) printf("Reading source tree to new shifting tree...");
|
||||
auto source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName);
|
||||
auto dest = (HierarchicalShiftingColoredTree*)OctreeLoader::CreateTree(destinationType, destinationDepth);
|
||||
std::vector<Color> sourceMaterials = source->GetMaterials();
|
||||
dest->MoveShallow(source);
|
||||
delete source;
|
||||
dest->SetMaterials(sourceMaterials);
|
||||
if (verbose) printf("\nReplacing original colors by color shifts...\n");
|
||||
dest->ReplaceColorsByShifts();
|
||||
if (verbose) printf("Converting the tree containing color shifts to a DAG...\n");
|
||||
dest->ToDAG();
|
||||
|
||||
OctreeLoader::WriteCache(dest, destinationType, dagFileName, verbose);
|
||||
delete dest;
|
||||
if (verbose) printf("Conversion succeeded");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool OctreeConverter::ConvertToOnlyMaterials(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
if (sourceDepth != destinationDepth)
|
||||
return false;
|
||||
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
if (destDescr.globalType != ONLYMATERIAL)
|
||||
return false;
|
||||
|
||||
if (destDescr.material == "c")
|
||||
return ConvertToOnlyColors(sourceType, destinationType, destinationDepth, dagFileName, verbose);
|
||||
|
||||
// No valid type found
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool OctreeConverter::ConvertToOnlyColors(std::string sourceType, std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
|
||||
{
|
||||
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
|
||||
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
|
||||
|
||||
// Make sure that the destination type is actually what is expected
|
||||
if (destDescr.globalType != ONLYMATERIAL || destDescr.material != "c") // Make sure the destination is actual the type we want
|
||||
return false;
|
||||
|
||||
|
||||
// Only colored and hierarchical trees can be converted to only color trees
|
||||
if (sourceDescr.globalType != HIERARCHICAL || sourceDescr.material != "c")
|
||||
return false;
|
||||
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
|
||||
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
|
||||
|
||||
// Only trees with full colors or the same compression scheme as the destination type can be compressed
|
||||
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
|
||||
return false;
|
||||
|
||||
// Remove the second characted, as that is the one that indicates that we want a shifting tree
|
||||
std::string hierarchicalColoredDestinationType = "h" + std::string(destinationType).erase(0, 1);
|
||||
|
||||
// If we're dealing with a normal "colored octree" that isn't hierarchical, or has a different compression, convert it to hierarchical first:
|
||||
if (TreeTypeParser::GetGlobalType(sourceType) != HIERARCHICAL || normalizedDestinationQuantizerType != normalizedSourceQuantizerType)
|
||||
{
|
||||
if (verbose) printf("Checking cache for hierarchical tree with correct compression...");
|
||||
if (OctreeLoader::VerifyCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName))
|
||||
{
|
||||
if (verbose) printf("Cache file found.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf("Nothing found.\nConverting non-hierarchical tree %s to hierarchical tree with correct compression.\n", OctreeLoader::GetFullFilename(sourceType, destinationDepth, dagFileName).c_str());
|
||||
if (OctreeConverter::ConvertToHierarchicalColored(sourceType, hierarchicalColoredDestinationType, destinationDepth, destinationDepth, dagFileName, verbose))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// By now we're sure that a fully colored or compressed colored hierarchical octree with the same compression exists
|
||||
// Let's convert it to a tree with only colors:
|
||||
if (verbose) printf("Reading source tree and moving to new tree with colors only...");
|
||||
auto source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName);
|
||||
auto dest = (HierarchicalColorsOnlyTree*)OctreeLoader::CreateTree(destinationType, destinationDepth);
|
||||
std::vector<Color> sourceMaterials = source->GetMaterials();
|
||||
|
||||
dest->MoveShallow(source);
|
||||
delete source;
|
||||
dest->SetMaterials(sourceMaterials);
|
||||
if (verbose) printf("\nConverting to DAG while merging empty nodes...\n");
|
||||
auto quantizer = ColorQuantizerFactory::Create(normalizedDestinationQuantizerType);
|
||||
dest->ToDAG(quantizer);
|
||||
|
||||
OctreeLoader::WriteCache(dest, destinationType, dagFileName, verbose);
|
||||
delete dest;
|
||||
|
||||
if (verbose) printf("Conversion succeeded");
|
||||
return true;
|
||||
}
|
||||
36
Research/core/OctreeBuilder/OctreeConverter.h
Normal file
36
Research/core/OctreeBuilder/OctreeConverter.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../Defines.h"
|
||||
class OctreeConverter
|
||||
{
|
||||
|
||||
public:
|
||||
// Calls ConvertTo for the required tree type based on the settings
|
||||
static bool Convert();
|
||||
|
||||
// Given some destination type, tries to find some cache file that can be converted
|
||||
static bool ConvertTo(std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
// Given a source and destination type, converts one to the other and stores the converted tree. Returns true if conversion succeeded
|
||||
static bool Convert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
static bool CanConvert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDept);
|
||||
private:
|
||||
|
||||
static bool ConvertToStandard(std::string sourceType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
static bool ConvertToHierarchicalColored(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
static bool ConvertToHierarchicalColorShift(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
static bool ConvertToUniqueIndex(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
|
||||
static bool ConvertToOnlyMaterials(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
|
||||
static bool ConvertToOnlyColors(std::string sourceType, std::string destinationType, unsigned8 destinationDepth, std::string dagFilename, bool verbose = false);
|
||||
|
||||
struct FileInfo{
|
||||
|
||||
std::string filename;
|
||||
unsigned64 filesize;
|
||||
std::string treeType;
|
||||
unsigned8 depth;
|
||||
|
||||
FileInfo(std::string filename, std::string treetype, unsigned8 depth, unsigned64 filesize) : filename(filename), filesize(filesize), treeType(treetype), depth(depth) { }
|
||||
};
|
||||
};
|
||||
|
||||
782
Research/core/OctreeBuilder/OctreeLoader.cpp
Normal file
782
Research/core/OctreeBuilder/OctreeLoader.cpp
Normal file
@@ -0,0 +1,782 @@
|
||||
#include "OctreeLoader.h"
|
||||
|
||||
#include "TreeTypeParser.h"
|
||||
#include "SettingsParser.h"
|
||||
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../scene/Material/SmallNormal.h"
|
||||
#include "../../scene/Material/ColorAndNormal.h"
|
||||
#include "../../scene/Material/ColorAndNormalAndValue.h"
|
||||
#include "../../scene/Material/ColorAndOpacity.h"
|
||||
|
||||
#include "../../scene/Octree/BaseTree.h"
|
||||
#include "../../scene/Octree/Tree.h"
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
#include "../../scene/Octree/MultiRootTree.h"
|
||||
#include "../../scene/Octree/ColorChannelMultiRootTree.h"
|
||||
#include "../../scene/Octree/MultiRootBitsTree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryTree.h"
|
||||
#include "../../scene/Octree/HierarchicalShiftingColoredTree.h"
|
||||
#include "../../scene/Octree/HierarchicalColorsOnlyTree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
|
||||
#include "../../scene/Octree/UniqueIndexShiftTree.h"
|
||||
#include "../../scene/Octree/MaterialLibraryMultiRootTree.h"
|
||||
#include "../../scene/Octree/IMaterialTexture.h"
|
||||
#include "../../scene/Octree/IBlockTexture.h"
|
||||
|
||||
#include "../../scene/PoolBuilder/StandardPoolBuilder.h"
|
||||
|
||||
#include "CompressedTextureFactory.h"
|
||||
#include "PoolBuilderFactory.h"
|
||||
#include "../../scene/Material/MaterialLibraryPointer.h"
|
||||
#include "../../scene/Material/SignedIntMaterial.h"
|
||||
|
||||
#include "ColorQuantizerFactory.h"
|
||||
|
||||
#include "../../PropertyLoader.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "../StringHelper.h"
|
||||
#include "../MathHelper.h"
|
||||
|
||||
bool OctreeLoader::VerifyCache()
|
||||
{
|
||||
bool useCache = SettingsParser::GetUseCacheFromSettings();
|
||||
if (!useCache)
|
||||
return false;
|
||||
|
||||
bool verbose = SettingsParser::GetVerboseFromSettings();
|
||||
if (verbose) printf("Verifying cache... ");
|
||||
Stopwatch watch; watch.Reset();
|
||||
|
||||
auto octreeType = SettingsParser::GetTreeTypeFromSettings();
|
||||
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
|
||||
std::string filename = SettingsParser::GetFilenameFromSettings();
|
||||
|
||||
bool cacheValid = VerifyCache(octreeType, maxLevel, filename);
|
||||
|
||||
if (verbose) printf("Cache is %s, confirmed in %d ms\n", cacheValid ? "valid" : "invalid", (int)(watch.GetTime() * 1000));
|
||||
|
||||
return cacheValid;
|
||||
}
|
||||
|
||||
bool OctreeLoader::VerifyCache(std::string treeType, unsigned8 maxLevel, std::string filename)
|
||||
{
|
||||
std::string outputFile = GetFullFilename(treeType, maxLevel, filename);
|
||||
auto tree = CreateTree(treeType, maxLevel);
|
||||
bool cacheValid = tree->VerifyTree(outputFile.c_str());
|
||||
delete tree;
|
||||
return cacheValid;
|
||||
}
|
||||
|
||||
BaseTree* OctreeLoader::ReadCache()
|
||||
{
|
||||
bool verbose = SettingsParser::GetVerboseFromSettings();
|
||||
if (verbose) printf("Reading cache... ");
|
||||
Stopwatch watch; watch.Reset();
|
||||
|
||||
auto octreeType = SettingsParser::GetTreeTypeFromSettings();
|
||||
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
|
||||
std::string filename = SettingsParser::GetFilenameFromSettings();
|
||||
|
||||
auto tree = ReadCache(octreeType, maxLevel, filename);
|
||||
|
||||
if (verbose) printf("Read tree with %llu nodes succesfully in %u ms\n", (unsigned64)tree->GetNodeCount(), (unsigned)(watch.GetTime() * 1000));
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
BaseTree* OctreeLoader::ReadCache(std::string treeType, unsigned8 maxLevel, std::string filename, bool verbose)
|
||||
{
|
||||
Stopwatch watch;
|
||||
watch.Reset();
|
||||
|
||||
std::string outputFile = GetFullFilename(treeType, maxLevel, filename);
|
||||
if (verbose) printf("Reading file %s.oct.", outputFile.c_str());
|
||||
|
||||
auto tree = CreateTree(treeType, maxLevel);
|
||||
bool readSuccessful = tree->ReadTree(outputFile.c_str());
|
||||
if (!readSuccessful)
|
||||
{
|
||||
delete tree;
|
||||
if (verbose) printf("Reading .oct file failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
if (verbose) printf("Read .oct file in %u ms.\n", (unsigned)(watch.GetTime() * 1000));
|
||||
|
||||
if (verbose) tree->PrintDebugInfo();
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
void OctreeLoader::DeleteCache(std::string treeType, unsigned8 maxLevel, std::string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::remove(Tree<>::GetOctreeFileName(GetFullFilename(treeType, maxLevel, filename).c_str()).c_str());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Deleting cache file %s failed", GetFullFilename(treeType.c_str(), maxLevel, filename).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool OctreeLoader::WriteCache(BaseTree* tree, std::string treeType, std::string filename, bool verbose)
|
||||
{
|
||||
std::string outputFile = GetFullFilename(treeType, tree->GetMaxLevel(), filename);
|
||||
// Write the final tree to a file
|
||||
Stopwatch watch; watch.Reset();
|
||||
|
||||
if (verbose) printf("Writing DAG to \"%s\"... ", outputFile.c_str());
|
||||
|
||||
bool res = tree->WriteTree(outputFile.c_str());
|
||||
|
||||
if (verbose) printf("Writing took %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
return res;
|
||||
}
|
||||
|
||||
bool OctreeLoader::VerifyPool()
|
||||
{
|
||||
bool useCache = SettingsParser::GetUsePoolCacheFromSettings();
|
||||
if (!useCache)
|
||||
return false;
|
||||
|
||||
std::string filename = SettingsParser::GetFilenameFromSettings();
|
||||
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
|
||||
std::string treeType = SettingsParser::GetTreeTypeFromSettings();
|
||||
|
||||
return VerifyPool(treeType, maxLevel, filename);
|
||||
}
|
||||
|
||||
bool OctreeLoader::VerifyPool(std::string treeType, unsigned8 maxLevel, std::string filename)
|
||||
{
|
||||
auto fullFileName = GetFullFilename(treeType, maxLevel, filename);
|
||||
StandardPoolBuilder builder;
|
||||
return builder.VerifyCachedPool(fullFileName, maxLevel);
|
||||
}
|
||||
|
||||
bool OctreeLoader::BuildNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose)
|
||||
{
|
||||
Stopwatch watch;
|
||||
if (verbose) printf("Building node pool (%s)...", PoolBuilderFactory::GetReadableName(poolType).c_str());
|
||||
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
|
||||
watch.Reset();
|
||||
builder->BuildPool(tree, outPool);
|
||||
outPoolSize = (unsigned32)MathHelper::DivideRoundUp(outPool.size(), TEXTURESIZE * TEXTURESIZE);
|
||||
// Try and write the nodepool
|
||||
builder->WritePool(poolFullFilename, outPool);
|
||||
delete builder;
|
||||
|
||||
tree->WriteAdditionalPool(poolFullFilename.c_str());
|
||||
//if (outPoolSize < 16) outPoolSize = 16;
|
||||
outPool.resize(TEXTURESIZE * TEXTURESIZE * outPoolSize);
|
||||
if (verbose) printf("Nodepool of size %u generated in %u ms.\n", outPoolSize, (unsigned)(watch.GetTime() * 1000));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OctreeLoader::ReadNodePool(BaseTree* tree, std::string poolType, std::string poolFileName, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose)
|
||||
{
|
||||
Stopwatch watch;
|
||||
watch.Reset();
|
||||
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
|
||||
if (verbose) printf("Reading node pool (%s) directly... ", PoolBuilderFactory::GetReadableName(poolType).c_str());
|
||||
bool success = builder->ReadPool(poolFileName, outPool);
|
||||
if (success)
|
||||
{
|
||||
// if reading succeeded, return the
|
||||
outPoolSize = (unsigned32)MathHelper::DivideRoundUp(outPool.size(), TEXTURESIZE * TEXTURESIZE);
|
||||
//if (outPoolSize < 16) outPoolSize = 16;
|
||||
outPool.resize(TEXTURESIZE * TEXTURESIZE * outPoolSize);
|
||||
tree->ReadAdditionalPool(poolFileName.c_str());
|
||||
if (verbose) printf("Reading node pool succeeded in %u ms.\n", (unsigned)(watch.GetTime() * 1000));
|
||||
} else
|
||||
if (verbose) printf("Reading directly failed.\n");
|
||||
delete builder;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool OctreeLoader::HasMaterialTexture(BaseTree* tree)
|
||||
{
|
||||
// If the tree can be cast to an IMaterialTexture interface, it has a material texture
|
||||
return dynamic_cast<IMaterialTexture*>(tree) != NULL;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetMaterialTexture(BaseTree* tree, std::string treeType, std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize)
|
||||
{
|
||||
if (HasMaterialTexture(tree))
|
||||
{
|
||||
auto materialTextureTree = dynamic_cast<IMaterialTexture*>(tree);
|
||||
outMaterialTexture = materialTextureTree->GetMaterialTexture();
|
||||
outMaterialTextureSize = materialTextureTree->GetMaterialTextureSize();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeLoader::HasBlockTexture(BaseTree* tree)
|
||||
{
|
||||
return dynamic_cast<IBlockTexture*>(tree) != NULL;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetBlockPointerPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPointerPool)
|
||||
{
|
||||
if (HasBlockTexture(tree))
|
||||
{
|
||||
auto uTree = dynamic_cast<IBlockTexture*>(tree);
|
||||
outBlockPointerPool = uTree->GetBlockPointerPool();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetBlockPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPool)
|
||||
{
|
||||
if (HasBlockTexture(tree))
|
||||
{
|
||||
auto uTree = dynamic_cast<IBlockTexture*>(tree);
|
||||
outBlockPool = uTree->GetBlockPool();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctreeLoader::HasAdditionalProperties(BaseTree* tree)
|
||||
{
|
||||
return dynamic_cast<IAdditionalProperties*>(tree) != NULL;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetAdditionalProperties(BaseTree* tree, std::string treeType, std::string poolType, std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
if (HasAdditionalProperties(tree))
|
||||
{
|
||||
auto uTree = dynamic_cast<IAdditionalProperties*>(tree);
|
||||
outAdditionalProperties = uTree->GetAdditionalProperties();
|
||||
}
|
||||
// Some types have specific additional properties:
|
||||
auto descr = TreeTypeParser::GetTreeTypeDescriptor(treeType);
|
||||
|
||||
std::string material = "MT_NONE";
|
||||
if (descr.material == "c") material = "MT_COLOR";
|
||||
else if (descr.material == "n") material = "MT_NORMAL";
|
||||
else if (descr.material == "cn") material = "MT_COLORNORMAL";
|
||||
else if (descr.material == "cnr") material = "MT_COLORNORMALREFLECTIVITY";
|
||||
else if (descr.material == "co") material = "MT_COLOROPACITY";
|
||||
outAdditionalProperties.insert(std::make_pair("Material", material));
|
||||
|
||||
if (descr.material == "n" || descr.material == "cn" || descr.material == "cnr") outAdditionalProperties.insert(std::make_pair("NormalSize", std::to_string(SmallNormal::BITS)));
|
||||
|
||||
std::string globalTypeString;
|
||||
switch (descr.globalType)
|
||||
{
|
||||
case MULTIROOT: globalTypeString = "TT_MULTIROOT"; break;
|
||||
case HIERARCHICAL: globalTypeString = (descr.additionalTypeInfo == "s") ? "TT_HIERARCHICALSHIFT" : "TT_HIERARCHICAL"; break;
|
||||
case ONLYMATERIAL: globalTypeString = "TT_ONLYMATERIAL"; break;
|
||||
case UNIQUEINDEX: globalTypeString = (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's') ? "TT_UNIQUEINDEXSHIFT" : "TT_UNIQUEINDEX"; break;
|
||||
case RANDOM: globalTypeString = "TT_RANDOM"; break;
|
||||
case BITTREES: globalTypeString = "TT_BITTREES"; break;
|
||||
case STANDARD: default: globalTypeString = "TT_STANDARD"; break;
|
||||
}
|
||||
outAdditionalProperties.insert(std::pair<std::string, std::string>("TreeType", globalTypeString));
|
||||
|
||||
std::string poolTypeString;
|
||||
PoolBuilderType poolTypeEnum = PoolBuilderFactory::GetType(poolType);
|
||||
switch (poolTypeEnum)
|
||||
{
|
||||
case ORIGINAL: poolTypeString = "PT_ORIGINAL"; break;
|
||||
case STANDARDPOOL: poolTypeString = "PT_STANDARD"; break;
|
||||
case ADAPTIVEDIRECT1: poolTypeString = "PT_ADAPTIVEDIRECT1"; break;
|
||||
case ADAPTIVELOOKUP1: poolTypeString = "PT_ADAPTIVELOOKUP1"; break;
|
||||
case ADAPTIVEDIRECT2: poolTypeString = "PT_ADAPTIVEDIRECT2"; break;
|
||||
case ADAPTIVELOOKUP2: poolTypeString = "PT_ADAPTIVELOOKUP2"; break;
|
||||
case VIRTUALNODES: poolTypeString = "PT_VIRTUALNODES"; break;
|
||||
}
|
||||
outAdditionalProperties.insert(std::make_pair("PoolType", poolTypeString));
|
||||
|
||||
if (descr.globalType == UNIQUEINDEX)
|
||||
{
|
||||
std::string compressionType = "";
|
||||
CompressionType cType = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressionType(descr.textureCompressor);
|
||||
if (cType == BASIC)
|
||||
compressionType = "BASIC_PACK";
|
||||
else if (cType == TIGHT)
|
||||
compressionType = "TIGHT_PACK";
|
||||
else if (cType == BLOCK)
|
||||
compressionType = "BLOCK_PACK";
|
||||
else if (cType == TIGHTBLOCK)
|
||||
compressionType = "TIGHTBLOCK_PACK";
|
||||
else if (cType == PALETTEBLOCK)
|
||||
compressionType = "PALETTE_PACK";
|
||||
else if (cType == DAGBASED)
|
||||
compressionType = "DAGBASED_PACK";
|
||||
else if (cType == MULTIROOTBASED)
|
||||
compressionType = "MULTIROOTBASED_PACK";
|
||||
outAdditionalProperties.insert(std::pair<std::string, std::string>("TextureCompression", compressionType));
|
||||
}
|
||||
|
||||
if (descr.globalType == MULTIROOT)
|
||||
{
|
||||
outAdditionalProperties.insert(std::pair<std::string, std::string>("BitsPerTree", std::to_string(TreeTypeParser::GetBitsPerTree(descr))));
|
||||
outAdditionalProperties.insert(std::pair<std::string, std::string>("BitsPerChannel", std::to_string(TreeTypeParser::GetBitsPerChannel(descr))));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool OctreeLoader::GetPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
return GetPool(SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
|
||||
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outMaterialPool, outMaterialPoolSize,
|
||||
outBlockPointerPool, outBlockPool,
|
||||
outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
|
||||
}
|
||||
bool OctreeLoader::GetPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose)
|
||||
{
|
||||
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
|
||||
bool succes = false;
|
||||
switch (globalType)
|
||||
{
|
||||
case HIERARCHICAL: case BITTREES:
|
||||
succes = OctreeLoader::GetHierarchicalPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, verbose); break;
|
||||
case ONLYMATERIAL:
|
||||
succes = OctreeLoader::GetOnlyMaterialPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialPool, outMaterialPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, verbose); break;
|
||||
case UNIQUEINDEX:
|
||||
succes = OctreeLoader::GetUniqueIndexPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outBlockPointerPool, outBlockPool, outAdditionalProperties, verbose); break;
|
||||
default:
|
||||
succes = OctreeLoader::GetStandardPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outAdditionalProperties, verbose); break;
|
||||
}
|
||||
return succes;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetStandardPool(std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
return GetStandardPool(
|
||||
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
|
||||
outPool, outPoolSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
|
||||
}
|
||||
|
||||
|
||||
bool OctreeLoader::GetStandardPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties, bool verbose)
|
||||
{
|
||||
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
|
||||
// Standard, Colored and Multiroot all use the same type of pool
|
||||
if (globalType != STANDARD && globalType != MULTIROOT && globalType != RANDOM)
|
||||
return false;
|
||||
|
||||
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
|
||||
if (usePoolCache)
|
||||
{
|
||||
// Try and read the pool cache
|
||||
// Create a root of the correct type to try and read the pool
|
||||
BaseTree* tree = CreateTree(treeType, maxLevel);
|
||||
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (readSucces && verbose) PrintGPUMemoryRequirements(outPool.size(), 0, 0, 0);
|
||||
delete tree;
|
||||
if (readSucces)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
|
||||
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
|
||||
if (tree == NULL)
|
||||
return false;
|
||||
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (verbose && succes) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
|
||||
delete tree;
|
||||
return succes;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetHierarchicalPool(std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
return GetHierarchicalPool(
|
||||
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
|
||||
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetHierarchicalPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose)
|
||||
{
|
||||
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
|
||||
if (globalType != HIERARCHICAL && globalType != BITTREES)
|
||||
return false;
|
||||
|
||||
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
|
||||
if (usePoolCache)
|
||||
{
|
||||
// Try and read the pool cache
|
||||
// Create a root of the correct type to try and read the pool
|
||||
BaseTree* tree = CreateTree(treeType, maxLevel);
|
||||
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
if (readSucces) readSucces &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (readSucces && verbose) PrintGPUMemoryRequirements(outPool.size(), outMaterialTexture.size(), 0, 0);
|
||||
delete tree;
|
||||
if (readSucces)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
|
||||
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
|
||||
if (tree == NULL)
|
||||
return false;
|
||||
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
succes &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (verbose) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
|
||||
delete tree;
|
||||
return succes;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetOnlyMaterialPool(
|
||||
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
return GetOnlyMaterialPool(
|
||||
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
|
||||
outGeometryPool, outGeometryPoolSize, outMaterialPool, outMaterialPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
|
||||
}
|
||||
bool OctreeLoader::GetOnlyMaterialPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose)
|
||||
{
|
||||
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
|
||||
if (globalType != ONLYMATERIAL)
|
||||
return false;
|
||||
|
||||
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
|
||||
bool geometryReadSucces = false;
|
||||
bool materialReadSucces = false;
|
||||
if (usePoolCache)
|
||||
{
|
||||
// Try and read the pool cache for both the
|
||||
// Create a root of the correct type to try and read the pool
|
||||
BaseTree* geometryTree = CreateTree("s", maxLevel);
|
||||
geometryReadSucces = ReadNodePool(geometryTree, poolType, GetFullFilename("s", maxLevel, filename), outGeometryPool, outGeometryPoolSize);
|
||||
delete geometryTree;
|
||||
|
||||
BaseTree* materialTree = CreateTree(treeType, maxLevel);
|
||||
materialReadSucces &= ReadNodePool(materialTree, poolType, poolFilename, outMaterialPool, outMaterialPoolSize, verbose);
|
||||
if (materialReadSucces)
|
||||
materialReadSucces &= GetMaterialTexture(materialTree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
|
||||
if (materialReadSucces) materialReadSucces &= GetAdditionalProperties(materialTree, treeType, poolType, outAdditionalProperties);
|
||||
|
||||
delete materialTree;
|
||||
if (geometryReadSucces && materialReadSucces)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
|
||||
if (!geometryReadSucces)
|
||||
{
|
||||
BaseTree* geometryTree = ReadCache("s", maxLevel, filename, verbose);
|
||||
if (geometryTree == NULL)
|
||||
return false;
|
||||
geometryReadSucces = BuildNodePool(geometryTree, poolType, poolFilename, outGeometryPool, outGeometryPoolSize, verbose);
|
||||
delete geometryTree;
|
||||
}
|
||||
if (!materialReadSucces)
|
||||
{
|
||||
BaseTree* materialTree = ReadCache(treeType, maxLevel, filename, verbose);
|
||||
if (materialTree == NULL)
|
||||
return false;
|
||||
materialReadSucces = BuildNodePool(materialTree, poolType, poolFilename, outMaterialPool, outMaterialPoolSize, verbose);
|
||||
materialReadSucces &= GetMaterialTexture(materialTree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
if (materialReadSucces) materialReadSucces &= GetAdditionalProperties(materialTree, treeType, poolType, outAdditionalProperties);
|
||||
delete materialTree;
|
||||
}
|
||||
return materialReadSucces && geometryReadSucces;
|
||||
}
|
||||
|
||||
bool OctreeLoader::GetUniqueIndexPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties)
|
||||
{
|
||||
return GetUniqueIndexPool(
|
||||
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
|
||||
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outBlockPointerPool, outBlockPool, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
|
||||
}
|
||||
bool OctreeLoader::GetUniqueIndexPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose)
|
||||
{
|
||||
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
|
||||
if (globalType != UNIQUEINDEX)
|
||||
return false;
|
||||
|
||||
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
|
||||
if (usePoolCache)
|
||||
{
|
||||
// Try and read the pool cache
|
||||
// Create a root of the correct type to try and read the pool
|
||||
BaseTree* tree = CreateTree(treeType, maxLevel);
|
||||
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
if (readSucces) readSucces &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
if (readSucces) readSucces &= GetBlockPointerPool(tree, treeType, outBlockPointerPool);
|
||||
if (readSucces) readSucces &= GetBlockPool(tree, treeType, outBlockPool);
|
||||
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (readSucces && verbose)
|
||||
PrintGPUMemoryRequirements(outPool.size(), outMaterialTexture.size(), outBlockPool.size(), outBlockPointerPool.size());
|
||||
delete tree;
|
||||
if (readSucces)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
|
||||
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
|
||||
if (tree == NULL)
|
||||
return false;
|
||||
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
|
||||
if (succes) succes &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
|
||||
if (succes) succes &= GetBlockPointerPool(tree, treeType, outBlockPointerPool);
|
||||
if (succes) succes &= GetBlockPool(tree, treeType, outBlockPool);
|
||||
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
|
||||
if (verbose) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
|
||||
delete tree;
|
||||
return succes;
|
||||
}
|
||||
|
||||
size_t OctreeLoader::GetGPUMemoryRequirements()
|
||||
{
|
||||
return OctreeLoader::GetGPUMemoryRequirements(SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings());
|
||||
}
|
||||
|
||||
size_t OctreeLoader::GetGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename)
|
||||
{
|
||||
BaseTree* tree = OctreeLoader::ReadCache(treeType, maxLevel, filename);
|
||||
return GetGPUMemoryRequirements(treeType, poolType, tree);
|
||||
}
|
||||
|
||||
size_t OctreeLoader::GetGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree)
|
||||
{
|
||||
// Every tree has the nodepool.
|
||||
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
|
||||
size_t treePoolSize = builder->GetPoolSize(tree);
|
||||
delete builder;
|
||||
size_t res = treePoolSize;
|
||||
|
||||
if (HasMaterialTexture(tree))
|
||||
{
|
||||
auto materialTree = dynamic_cast<IMaterialTexture*>(tree);
|
||||
size_t materialTextureSize = materialTree->GetMaterialTextureSize();
|
||||
res += materialTextureSize * materialTextureSize * materialTree->GetMaterialTextureChannelsPerPixel(); // 2D texture with the given channels per pixel
|
||||
}
|
||||
if (HasBlockTexture(tree))
|
||||
{
|
||||
auto blockTextureTree = dynamic_cast<IBlockTexture*>(tree);
|
||||
size_t blockPointerPoolMemoryRequirements = blockTextureTree->GetBlockPointerPoolSize();
|
||||
size_t blockPoolMemoryRequirements = blockTextureTree->GetBlockPoolSize();
|
||||
size_t blockTextureMemoryRequirements = blockPointerPoolMemoryRequirements + blockPoolMemoryRequirements;
|
||||
res += blockTextureMemoryRequirements;
|
||||
printf("Block texture required %.2f MB. (main: %.2f MB, additional: %.2f MB)\n",
|
||||
(double)blockTextureMemoryRequirements / (1024.0 * 1024.0),
|
||||
(double)blockPoolMemoryRequirements / (1024.0 * 1024.0),
|
||||
(double)blockPointerPoolMemoryRequirements / (1024.0 * 1024.0));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t OctreeLoader::GetMainTreeGPUMemoryRequirements()
|
||||
{
|
||||
std::string treeType = SettingsParser::GetTreeTypeFromSettings();
|
||||
BaseTree* tree = OctreeLoader::ReadCache(treeType, SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings());
|
||||
size_t res = GetGPUMemoryRequirements(treeType, SettingsParser::GetPoolTypeFromSettings(), tree);
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t OctreeLoader::GetMainTreeGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree)
|
||||
{
|
||||
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
|
||||
size_t treePoolSize = builder->GetPoolSize(tree);
|
||||
delete builder;
|
||||
return TEXTURESIZE * TEXTURESIZE * MathHelper::DivideRoundUp(treePoolSize, TEXTURESIZE * TEXTURESIZE);
|
||||
}
|
||||
|
||||
void OctreeLoader::PrintGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, BaseTree* tree)
|
||||
{
|
||||
size_t memoryRequirements = GetGPUMemoryRequirements(treeType, poolType, tree);
|
||||
printf("Tree requires %11zu bytes (%8.2f MB).\n", memoryRequirements, (double)memoryRequirements / 1048576.0);
|
||||
}
|
||||
|
||||
void OctreeLoader::PrintGPUMemoryRequirements(size_t poolSize, size_t materialTextureSize, size_t blockPoolSize, size_t blockPointerPoolSize)
|
||||
{
|
||||
size_t memoryRequirements = poolSize + materialTextureSize + blockPoolSize + blockPointerPoolSize;
|
||||
printf("Tree requires %11zu bytes (%8.2f MB).\n", memoryRequirements, (double)memoryRequirements / 1048576.0);
|
||||
}
|
||||
|
||||
std::string OctreeLoader::GetFullFilename()
|
||||
{
|
||||
PropertyLoader::Create();
|
||||
auto propertyLoader = PropertyLoader::Instance();
|
||||
|
||||
std::string filename = propertyLoader->GetProperty("dag_file");
|
||||
unsigned8 maxLevel = (unsigned8)std::stoi(propertyLoader->GetProperty("shader_max_level"));
|
||||
std::string treeType = propertyLoader->GetProperty("octree_type");
|
||||
|
||||
return GetFullFilename(treeType, maxLevel, filename);
|
||||
}
|
||||
|
||||
std::string OctreeLoader::GetFullFilename(std::string treeType, unsigned8 maxLevel, std::string filename)
|
||||
{
|
||||
// Hack: make sure filenames are consistent for soem special tree types
|
||||
if (treeType == "m") treeType = "m8";
|
||||
|
||||
// Convert compression method to normalized compression method
|
||||
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(treeType);
|
||||
if (descr.quantizer != "")
|
||||
{
|
||||
StringHelper::Replace(treeType, descr.quantizer, ColorQuantizerFactory::GetNormalizedType(descr.quantizer));
|
||||
}
|
||||
if (descr.globalType == UNIQUEINDEX && descr.additionalTypeInfo != "")
|
||||
{
|
||||
unsigned8 lod = TreeTypeParser::GetLevelsWithoutMaterials(descr);
|
||||
if (lod != 0)
|
||||
StringHelper::Replace(treeType, descr.additionalTypeInfo, std::to_string(lod) + "lod");
|
||||
}
|
||||
|
||||
return filename + "_" + std::to_string(maxLevel) + "_" + treeType;
|
||||
}
|
||||
|
||||
BaseTree* OctreeLoader::CreateTree(std::string type, unsigned8 maxLevel)
|
||||
{
|
||||
BaseTree* tree = NULL;
|
||||
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(type);
|
||||
|
||||
switch (descr.globalType)
|
||||
{
|
||||
case HIERARCHICAL:
|
||||
{
|
||||
if (descr.material == "c")
|
||||
{
|
||||
if (descr.additionalTypeInfo == "s")
|
||||
tree = new HierarchicalShiftingColoredTree(maxLevel);
|
||||
else
|
||||
tree = new MaterialLibraryTree<Color, ColorCompare>(maxLevel);
|
||||
}
|
||||
else if (descr.material == "n")
|
||||
tree = new MaterialLibraryTree<SmallNormal, NormalCompare, SmallNormal::BYTES>(maxLevel);
|
||||
else if (descr.material == "cn")
|
||||
tree = new MaterialLibraryTree<ColorAndNormal, ColorAndNormalCompare, ColorAndNormal::CHANNELSPERPIXEL>(maxLevel);
|
||||
else if (descr.material == "cnr")
|
||||
tree = new MaterialLibraryTree<ColorAndNormalAndValue, ColorAndNormalAndValueCompare, ColorAndNormalAndValue::CHANNELSPERPIXEL>(maxLevel);
|
||||
else if (descr.material == "co")
|
||||
tree = new MaterialLibraryTree<ColorAndOpacity, ColorAndOpacityCompare, ColorAndOpacity::CHANNELSPERPIXEL>(maxLevel);
|
||||
break;
|
||||
}
|
||||
case ONLYMATERIAL:
|
||||
{
|
||||
tree = new HierarchicalColorsOnlyTree(maxLevel);
|
||||
break;
|
||||
}
|
||||
case UNIQUEINDEX:
|
||||
{
|
||||
unsigned8 levelsWithoutMaterials = TreeTypeParser::GetLevelsWithoutMaterials(descr);
|
||||
if (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's')
|
||||
{
|
||||
assert(descr.material != "n");
|
||||
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(descr.textureCompressor);
|
||||
tree = new UniqueIndexShiftTree<Color, ColorCompare>(maxLevel, texture, levelsWithoutMaterials);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(descr.textureCompressor);
|
||||
if (descr.material == "c")
|
||||
tree = new MaterialLibraryUniqueIndexTree<Color, ColorCompare>(maxLevel, texture, levelsWithoutMaterials);
|
||||
else if (descr.material == "n")
|
||||
tree = new MaterialLibraryUniqueIndexTree<SmallNormal, NormalCompare, SmallNormal::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
|
||||
else if (descr.material == "cn")
|
||||
tree = new MaterialLibraryUniqueIndexTree<ColorAndNormal, ColorAndNormalCompare, ColorAndNormal::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
|
||||
else if (descr.material == "cnr")
|
||||
tree = new MaterialLibraryUniqueIndexTree<ColorAndNormalAndValue, ColorAndNormalAndValueCompare, ColorAndNormalAndValue::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
|
||||
else if (descr.material == "co")
|
||||
tree = new MaterialLibraryUniqueIndexTree<ColorAndOpacity, ColorAndOpacityCompare, ColorAndOpacity::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
|
||||
else
|
||||
delete texture;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MULTIROOT:
|
||||
{
|
||||
std::vector<std::string> additionalTypes(8);
|
||||
additionalTypes[0] = "1";
|
||||
additionalTypes[1] = "2";
|
||||
additionalTypes[2] = "3";
|
||||
additionalTypes[3] = "4";
|
||||
additionalTypes[4] = "5";
|
||||
additionalTypes[5] = "6";
|
||||
additionalTypes[6] = "7";
|
||||
additionalTypes[7] = "8";
|
||||
if (descr.additionalTypeInfo == "cc")
|
||||
{
|
||||
tree = new LeafMaterialMultiRootTree<ColorChannel>(maxLevel, 3);
|
||||
}
|
||||
else if (CollectionHelper::Contains(additionalTypes, descr.additionalTypeInfo))
|
||||
{
|
||||
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
|
||||
tree = new MultiRootTree<>(maxLevel, 3 * bitsPerChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
|
||||
unsigned8 bitsPerTree = TreeTypeParser::GetBitsPerTree(descr);
|
||||
unsigned8 treesPerChannel = bitsPerChannel / bitsPerTree + (bitsPerChannel % bitsPerTree == 0 ? 0 : 1);
|
||||
switch (bitsPerTree)
|
||||
{
|
||||
case 1: tree = new LeafMaterialMultiRootTree<BitsMaterial<1>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 2: tree = new LeafMaterialMultiRootTree<BitsMaterial<2>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 3: tree = new LeafMaterialMultiRootTree<BitsMaterial<3>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 4: tree = new LeafMaterialMultiRootTree<BitsMaterial<4>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 5: tree = new LeafMaterialMultiRootTree<BitsMaterial<5>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 6: tree = new LeafMaterialMultiRootTree<BitsMaterial<6>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 7: tree = new LeafMaterialMultiRootTree<BitsMaterial<7>>(maxLevel, 3 * treesPerChannel); break;
|
||||
case 8: tree = new LeafMaterialMultiRootTree<BitsMaterial<8>>(maxLevel, 3 * treesPerChannel); break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RANDOM:
|
||||
{
|
||||
tree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(maxLevel); break;
|
||||
}
|
||||
case BITTREES:
|
||||
{
|
||||
if (descr.additionalTypeInfo == "l")
|
||||
tree = new MaterialLibraryMultiRoot<Color, ColorCompare>(maxLevel);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
tree = new Tree<>(maxLevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
109
Research/core/OctreeBuilder/OctreeLoader.h
Normal file
109
Research/core/OctreeBuilder/OctreeLoader.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "OctreeBuilderStatistics.h"
|
||||
#include "../Defines.h"
|
||||
#include "../../inc/glm/vec3.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class BaseTree;
|
||||
|
||||
// Singleton wrapper that reads the settings and creates the correct octreebuilder
|
||||
class OctreeLoader
|
||||
{
|
||||
public:
|
||||
static const size_t TEXTURESIZE = 1024;
|
||||
|
||||
static BaseTree* CreateTree(std::string type, unsigned8 maxLevel);
|
||||
|
||||
// Reads the settings and verifies that the current file exists. Returns false if usecache is turned off.
|
||||
static bool VerifyCache();
|
||||
// Verify the cache. Returns true if the cache is valid.
|
||||
static bool VerifyCache(std::string treeType, unsigned8 maxLevel, std::string filename);
|
||||
static BaseTree* ReadCache();
|
||||
static BaseTree* ReadCache(std::string treeType, unsigned8 maxLevel, std::string filename, bool verbose = false);
|
||||
static void DeleteCache(std::string treeType, unsigned8 maxLevel, std::string filename);
|
||||
static bool WriteCache(BaseTree* tree, std::string treeType, std::string filename, bool verbose);
|
||||
|
||||
static bool VerifyPool();
|
||||
static bool VerifyPool(std::string treeType, unsigned8 maxLevel, std::string filename);
|
||||
|
||||
static bool GetPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties);
|
||||
static bool GetPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose = false);
|
||||
|
||||
static bool GetStandardPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties);
|
||||
static bool GetStandardPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties, bool verbose = false);
|
||||
|
||||
static bool GetHierarchicalPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties);
|
||||
static bool GetHierarchicalPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose = false);
|
||||
|
||||
static bool GetOnlyMaterialPool(
|
||||
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties);
|
||||
static bool GetOnlyMaterialPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
|
||||
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose = false);
|
||||
|
||||
static bool GetUniqueIndexPool(
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties);
|
||||
static bool GetUniqueIndexPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
|
||||
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
|
||||
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
|
||||
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
|
||||
std::map<std::string, std::string>& outAdditionalProperties,
|
||||
bool verbose = false);
|
||||
|
||||
// Returns the GPU memory requirements in bytes for the current settings (returns 0 if no tree is found)
|
||||
static size_t GetGPUMemoryRequirements();
|
||||
static size_t GetGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename);
|
||||
static size_t GetGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree);
|
||||
|
||||
static size_t GetMainTreeGPUMemoryRequirements();
|
||||
static size_t GetMainTreeGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree);
|
||||
|
||||
static std::string GetFullFilename();
|
||||
static std::string GetFullFilename(std::string treeType, unsigned8 maxLevel, std::string filename);
|
||||
private:
|
||||
static bool BuildNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose = false);
|
||||
static bool ReadNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose = false);
|
||||
static bool GetMaterialTexture(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize);
|
||||
static bool GetBlockPointerPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPointerPool);
|
||||
static bool GetBlockPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPool);
|
||||
static bool GetAdditionalProperties(BaseTree* tree, std::string treeType, std::string poolType, std::map<std::string, std::string>& outAdditionalProperties);
|
||||
|
||||
static bool HasMaterialTexture(BaseTree* treeType);
|
||||
static bool HasBlockTexture(BaseTree* treeType);
|
||||
static bool HasAdditionalProperties(BaseTree* treeType);
|
||||
|
||||
static size_t CeilToTextureSize(size_t size);
|
||||
static void PrintGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, BaseTree* tree);
|
||||
static void PrintGPUMemoryRequirements(size_t poolSize, size_t materialTextureSize, size_t blockPoolSize, size_t blockPointerPoolSize);
|
||||
};
|
||||
|
||||
76
Research/core/OctreeBuilder/PoolBuilderFactory.h
Normal file
76
Research/core/OctreeBuilder/PoolBuilderFactory.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "../../scene/PoolBuilder/StandardPoolBuilder.h"
|
||||
#include "../../scene/PoolBuilder/AdaptivePointerPoolBuilder.h"
|
||||
#include "../../scene/PoolBuilder/OriginalPoolBuilder.h"
|
||||
#include "../../scene/PoolBuilder/VirtualNodePoolBuilder.h"
|
||||
|
||||
enum PoolBuilderType
|
||||
{
|
||||
ORIGINAL, STANDARDPOOL, ADAPTIVELOOKUP1, ADAPTIVEDIRECT1, ADAPTIVELOOKUP2, ADAPTIVEDIRECT2, VIRTUALNODES
|
||||
};
|
||||
|
||||
class PoolBuilderFactory
|
||||
{
|
||||
public:
|
||||
static PoolBuilderType GetType(std::string type)
|
||||
{
|
||||
if (type == "o") return ORIGINAL;
|
||||
if (type == "s") return STANDARDPOOL;
|
||||
if (type == "al" || type.substr(0, 3) == "al2") return ADAPTIVELOOKUP2;
|
||||
if (type == "ad" || type.substr(0, 3) == "ad2") return ADAPTIVEDIRECT2;
|
||||
if (type.substr(0, 3) == "al1") return ADAPTIVELOOKUP1;
|
||||
if (type.substr(0, 3) == "ad1") return ADAPTIVEDIRECT1;
|
||||
if (type == "v") return VIRTUALNODES;
|
||||
return STANDARDPOOL;
|
||||
}
|
||||
|
||||
static BasePoolBuilder<BaseTree>* Create(std::string strType)
|
||||
{
|
||||
PoolBuilderType type = GetType(strType);
|
||||
if (type == ADAPTIVEDIRECT1 || type == ADAPTIVEDIRECT2 || type == ADAPTIVELOOKUP1 || type == ADAPTIVELOOKUP2)
|
||||
{
|
||||
unsigned8 maskSize = (type == ADAPTIVEDIRECT1 || type == ADAPTIVELOOKUP1) ? 1 : 2;
|
||||
bool useLookupTable = type == ADAPTIVELOOKUP1 || type == ADAPTIVELOOKUP2;
|
||||
std::vector<unsigned8> bitSizes = maskSize == 1 ? std::vector<unsigned8> { 1 } : std::vector<unsigned8>{ 1, 2, 3 };
|
||||
if (strType.length() > 3)
|
||||
{
|
||||
unsigned8 requiredNums = (unsigned8)(BitHelper::Exp2(maskSize) - 1);
|
||||
std::string bitSizesStr = strType.substr(3, requiredNums);
|
||||
for (unsigned8 i = 0; i < BitHelper::Exp2(maskSize) - 1; i++)
|
||||
bitSizes[i] = bitSizesStr[i] - '0';
|
||||
}
|
||||
if (maskSize == 1)
|
||||
return new AdaptivePointerPoolBuilder(useLookupTable, 1, bitSizes[0]);
|
||||
else
|
||||
return new AdaptivePointerPoolBuilder(useLookupTable, 2, bitSizes[0], bitSizes[1], bitSizes[2]);
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case ORIGINAL: return new OriginalPoolBuilder(); break;
|
||||
case VIRTUALNODES: return new VirtualNodePoolBuilder(); break;
|
||||
case STANDARDPOOL: default: return new StandardPoolBuilder(); break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string GetReadableName(std::string strType)
|
||||
{
|
||||
PoolBuilderType type = GetType(strType);
|
||||
switch (type)
|
||||
{
|
||||
case ORIGINAL: return "original";
|
||||
case STANDARDPOOL: return "standard";
|
||||
case ADAPTIVELOOKUP2: return "adaptive \\w lookup table";
|
||||
case ADAPTIVEDIRECT2: return "adaptive \\w direct pointers";
|
||||
case ADAPTIVELOOKUP1: return "adaptive \\w lookup table";
|
||||
case ADAPTIVEDIRECT1: return "adaptive \\w direct pointers";
|
||||
case VIRTUALNODES: return "virtual nodes";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a string that can be used as a regex to match all compressed texture types
|
||||
static std::string GetRegexMatcher()
|
||||
{
|
||||
return std::string("o|s|ad(1[1-4]?|2([1-4][1-4][1-4])?)|al(1[1-4]?|2([1-4][1-4][1-4])?)|v");
|
||||
}
|
||||
};
|
||||
109
Research/core/OctreeBuilder/RandomOctreeBuilder.cpp
Normal file
109
Research/core/OctreeBuilder/RandomOctreeBuilder.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "RandomOctreeBuilder.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeLoader.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
|
||||
RandomOctreeBuilder::RandomOctreeBuilder() :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mCurPassTree(NULL),
|
||||
mCurPassTreeCoord(glm::uvec3(0))
|
||||
{}
|
||||
RandomOctreeBuilder::~RandomOctreeBuilder() {}
|
||||
|
||||
std::string RandomOctreeBuilder::GetTreeType() { return "r"; }
|
||||
|
||||
void RandomOctreeBuilder::InitTree()
|
||||
{
|
||||
mTree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(GetTreeDepth());
|
||||
}
|
||||
|
||||
void RandomOctreeBuilder::FinalizeTree()
|
||||
{
|
||||
// Convert the octree to a DAG
|
||||
Stopwatch watch;
|
||||
if (verbose) printf("Converting final tree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG();
|
||||
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
|
||||
}
|
||||
|
||||
void RandomOctreeBuilder::TerminateTree()
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
}
|
||||
|
||||
void RandomOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
mCurPassTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
|
||||
mCurPassTreeCoord = coord;
|
||||
}
|
||||
|
||||
void RandomOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
Stopwatch watch;
|
||||
unsigned8 mainTreeLevel = mTree->GetMaxLevel() - mCurPassTree->GetMaxLevel();
|
||||
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
//std::vector<BitsMaterial<1>> randomMaterials(mCurPassTree->GetNodeCount());
|
||||
//tbb::parallel_for(size_t(0), randomMaterials.size(), [&](size_t i) { randomMaterials[i] = BitsMaterial<1>((std::rand() < (RAND_MAX / 2)) ? 1 : 0); });
|
||||
//mCurPassTree->SetMaterials(randomMaterials);
|
||||
|
||||
mCurPassTree->PropagateMaterials([](const std::vector<Color>& materials, const std::vector<float>& weights){ return Color::WeightedAverage(materials, weights); });
|
||||
std::vector<Color> colors = mCurPassTree->GetMaterials();
|
||||
std::vector<BitsMaterial<1>> bitMaterials(mCurPassTree->GetNodeCount());
|
||||
unsigned8 shift = 0;
|
||||
tbb::parallel_for(size_t(0), bitMaterials.size(), [&](size_t i) { bitMaterials[i] = BitsMaterial<1>((colors[i].GetColor().r & (1 << shift)) >> shift); });
|
||||
auto mCurPassBitTree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(mCurPassTree->GetMaxLevel());
|
||||
mCurPassBitTree->MoveShallow(mCurPassTree);
|
||||
mCurPassBitTree->SetMaterials(bitMaterials);
|
||||
|
||||
delete mCurPassTree;
|
||||
|
||||
if (mainTreeLevel == 0) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = mCurPassBitTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Convert the subtree to a DAG first, this saved time when appending and converting the total tree
|
||||
//Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
|
||||
// 209.922 seconds without early converting
|
||||
// 163.645 seconds with early converting
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
mCurPassBitTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (verbose) printf("Appending subtree... ");
|
||||
mTree->Append(mCurPassTreeCoord, mainTreeLevel, mCurPassBitTree);
|
||||
delete mCurPassBitTree;
|
||||
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
mTree->ToDAG(mainTreeLevel);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mCurPassTree;
|
||||
}
|
||||
}
|
||||
|
||||
void RandomOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPassTree->AddLeafNode(coordinate, color);
|
||||
}
|
||||
|
||||
std::vector<size_t> RandomOctreeBuilder::GetOctreeNodesPerLevel()
|
||||
{
|
||||
return mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
std::vector<size_t> RandomOctreeBuilder::GetNodesPerLevel()
|
||||
{
|
||||
return mTree->GetNodesPerLevel();
|
||||
}
|
||||
|
||||
38
Research/core/OctreeBuilder/RandomOctreeBuilder.h
Normal file
38
Research/core/OctreeBuilder/RandomOctreeBuilder.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
#include "../Comparers.h"
|
||||
#include "../../scene/Material/BitsMaterial.h"
|
||||
#include "../../scene/Material/Color.h"
|
||||
|
||||
class RandomOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
|
||||
{
|
||||
public:
|
||||
RandomOctreeBuilder();
|
||||
~RandomOctreeBuilder() override;
|
||||
|
||||
std::string GetTreeType() override;
|
||||
protected:
|
||||
// Initialize the main tree
|
||||
void InitTree() override;
|
||||
void FinalizeTree() override;
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override;
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override;
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override;
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override;
|
||||
std::vector<size_t> GetNodesPerLevel() override;
|
||||
private:
|
||||
MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>* mTree;
|
||||
|
||||
MaterialTree<Color, ColorCompare>* mCurPassTree;
|
||||
glm::uvec3 mCurPassTreeCoord;
|
||||
};
|
||||
|
||||
22
Research/core/OctreeBuilder/SettingsParser.cpp
Normal file
22
Research/core/OctreeBuilder/SettingsParser.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "SettingsParser.h"
|
||||
#include "../../PropertyLoader.h"
|
||||
|
||||
PropertyLoader* GetPropertyLoader()
|
||||
{
|
||||
PropertyLoader::Create();
|
||||
return PropertyLoader::Instance();
|
||||
}
|
||||
|
||||
std::string SettingsParser::GetTreeTypeFromSettings() { return GetPropertyLoader()->GetProperty("octree_type"); }
|
||||
std::string SettingsParser::GetPoolTypeFromSettings() { return GetPropertyLoader()->GetProperty("pool_type"); }
|
||||
unsigned8 SettingsParser::GetMaxLevelFromSettings() { return (unsigned8)GetPropertyLoader()->GetIntProperty("shader_max_level"); }
|
||||
std::string SettingsParser::GetFilenameFromSettings() { return GetPropertyLoader()->GetProperty("dag_file"); }
|
||||
bool SettingsParser::GetUsePoolCacheFromSettings()
|
||||
{
|
||||
// Only use the pool cache if the octreebuilder cache is used as well
|
||||
return GetPropertyLoader()->GetBoolProperty("renderer_usecache") && GetPropertyLoader()->GetBoolProperty("octreebuilder_usecache");
|
||||
}
|
||||
|
||||
bool SettingsParser::GetUseCacheFromSettings() { return GetPropertyLoader()->GetBoolProperty("octreebuilder_usecache"); }
|
||||
bool SettingsParser::GetForceRebuildFromSettings() { return GetPropertyLoader()->GetBoolProperty("octreebuilder_forcerebuild"); }
|
||||
bool SettingsParser::GetVerboseFromSettings() { return GetPropertyLoader()->GetBoolProperty("verbose"); }
|
||||
17
Research/core/OctreeBuilder/SettingsParser.h
Normal file
17
Research/core/OctreeBuilder/SettingsParser.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "../Defines.h"
|
||||
|
||||
class SettingsParser
|
||||
{
|
||||
public:
|
||||
static std::string GetTreeTypeFromSettings();
|
||||
static std::string GetPoolTypeFromSettings();
|
||||
static unsigned8 GetMaxLevelFromSettings();
|
||||
static std::string GetFilenameFromSettings();
|
||||
static bool GetUsePoolCacheFromSettings();
|
||||
static bool GetUseCacheFromSettings();
|
||||
static bool GetForceRebuildFromSettings();
|
||||
static bool GetVerboseFromSettings();
|
||||
};
|
||||
|
||||
122
Research/core/OctreeBuilder/StandardOctreeBuilder.cpp
Normal file
122
Research/core/OctreeBuilder/StandardOctreeBuilder.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "StandardOctreeBuilder.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
#include "OctreeLoader.h"
|
||||
|
||||
#define DAGFINALTREE
|
||||
|
||||
StandardOctreeBuilder::StandardOctreeBuilder() :
|
||||
BaseStandardOctreeBuilder(),
|
||||
mTree(NULL)
|
||||
{}
|
||||
|
||||
StandardOctreeBuilder::~StandardOctreeBuilder() {}
|
||||
|
||||
std::string StandardOctreeBuilder::GetTreeType() { return "s"; }
|
||||
|
||||
void StandardOctreeBuilder::FinalizeTree()
|
||||
{
|
||||
// Read all subtrees from cache and append them
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
Stopwatch watch;
|
||||
mTree = new Tree<>(GetTreeDepth());
|
||||
|
||||
int i = 1;
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
{
|
||||
if (verbose) printf("Reading subtree %u / %u at (%u, %u, %u) from cache...\n", i, (unsigned32)(GetValidCoords().size()), coord.x, coord.y, coord.z);
|
||||
Tree<>* subTree = (Tree<>*)OctreeLoader::ReadCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
|
||||
if (subTree != NULL)
|
||||
{
|
||||
if (verbose) printf("Appending subtree... ");
|
||||
watch.Reset();
|
||||
mTree->AppendAndMerge(coord, GetAppendedTreeLevel(), subTree);
|
||||
//mTree->Append(mCurPassTreeCoord, mainTreeLevel, mCurPassTree);
|
||||
delete subTree;
|
||||
if (verbose) printf("Appended subtree in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
//mTree->SortNodes();
|
||||
//#ifdef DAGFINALTREE
|
||||
// if (verbose) printf("Converting current tree to DAG...\n");
|
||||
// mTree->ToDAG(mainTreeLevel);
|
||||
//#endif
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Delete the cache files
|
||||
if (verbose) printf("Deleting cache...");
|
||||
watch.Reset();
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
OctreeLoader::DeleteCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
if (verbose) printf("Cache deleted in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
void StandardOctreeBuilder::TerminateTree()
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), GetVerbose());
|
||||
delete mTree;
|
||||
}
|
||||
|
||||
bool StandardOctreeBuilder::CancelCurPassTree(const glm::uvec3& coord)
|
||||
{
|
||||
return OctreeLoader::VerifyCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
}
|
||||
|
||||
void StandardOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
mTree = new Tree<>(GetSinglePassTreeDepth());
|
||||
}
|
||||
|
||||
void StandardOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
Stopwatch watch;
|
||||
|
||||
if (!mTree->IsEmpty()) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree.
|
||||
// If it is a single pass tree it needs to be converted anyway :)
|
||||
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, run in Release mode with pool):
|
||||
// 209.922 seconds without early converting
|
||||
// 163.645 seconds with early converting
|
||||
if (verbose) printf("Converting %s to DAG...\n", IsSinglePass() ? "tree" : "subtree");
|
||||
watch.Reset();
|
||||
//mCurPassTree->SortNodes();
|
||||
mTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
if (verbose) printf("Writing subtree to cache...");
|
||||
watch.Reset();
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetSinglePassTreeFilename(coord), verbose);
|
||||
if (verbose) printf("Wrote cache in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
delete mTree;
|
||||
mTree = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StandardOctreeBuilder::AddNode(const glm::uvec3& coordinate)
|
||||
{
|
||||
mTree->AddLeafNode(coordinate);
|
||||
}
|
||||
|
||||
std::vector<size_t> StandardOctreeBuilder::GetOctreeNodesPerLevel()
|
||||
{
|
||||
return mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
std::vector<size_t> StandardOctreeBuilder::GetNodesPerLevel()
|
||||
{
|
||||
return mTree->GetNodesPerLevel();
|
||||
}
|
||||
|
||||
std::string StandardOctreeBuilder::GetSinglePassTreeFilename(const glm::uvec3& coord) const
|
||||
{
|
||||
char buffer[255];
|
||||
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
|
||||
return std::string(buffer);
|
||||
}
|
||||
35
Research/core/OctreeBuilder/StandardOctreeBuilder.h
Normal file
35
Research/core/OctreeBuilder/StandardOctreeBuilder.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "BaseStandardOctreeBuilder.h"
|
||||
#include "../../scene/Octree/Tree.h"
|
||||
|
||||
class StandardOctreeBuilder : public BaseStandardOctreeBuilder
|
||||
{
|
||||
public:
|
||||
StandardOctreeBuilder();
|
||||
~StandardOctreeBuilder() override;
|
||||
|
||||
std::string GetTreeType() override;
|
||||
protected:
|
||||
void InitTree() override {}
|
||||
// Finalize the tree
|
||||
void FinalizeTree() override;
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override;
|
||||
|
||||
// Cancel the cur pass tree if there's already a cache file available for it
|
||||
bool CancelCurPassTree(const glm::uvec3& coord) override;
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override;
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override;
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate) override;
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override;
|
||||
std::vector<size_t> GetNodesPerLevel() override;
|
||||
private:
|
||||
std::string GetSinglePassTreeFilename(const glm::uvec3& coord) const;
|
||||
Tree<>* mTree;
|
||||
};
|
||||
|
||||
137
Research/core/OctreeBuilder/TreeTypeParser.cpp
Normal file
137
Research/core/OctreeBuilder/TreeTypeParser.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "TreeTypeParser.h"
|
||||
#include "ColorQuantizerFactory.h"
|
||||
#include "NormalQuantizerFactory.h"
|
||||
#include "CompressedTextureFactory.h"
|
||||
#include <regex>
|
||||
|
||||
TreeType TreeTypeParser::GetGlobalType(std::string type)
|
||||
{
|
||||
char globalTypeChar = type[0];
|
||||
switch (globalTypeChar)
|
||||
{
|
||||
case 'm': return MULTIROOT;
|
||||
case 'h': return HIERARCHICAL;
|
||||
case 'o': return ONLYMATERIAL;
|
||||
case 'u': return UNIQUEINDEX;
|
||||
case 'r': return RANDOM;
|
||||
case 'b': return BITTREES;
|
||||
default: return STANDARD;
|
||||
}
|
||||
}
|
||||
|
||||
TreeTypeDescriptor TreeTypeParser::GetTreeTypeDescriptor(std::string type)
|
||||
{
|
||||
TreeTypeDescriptor res;
|
||||
std::smatch sm;
|
||||
res.globalType = GetGlobalType(type);
|
||||
switch (res.globalType)
|
||||
{
|
||||
case ONLYMATERIAL:
|
||||
{
|
||||
std::string regexStr = "(o)(c)(" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
|
||||
std::regex normalMatcher(regexStr);
|
||||
if (std::regex_match(type, sm, normalMatcher))
|
||||
{
|
||||
res.material = sm.str(2);
|
||||
res.quantizer = sm.str(3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HIERARCHICAL:
|
||||
{
|
||||
std::string regexStr = "(h(s?))(c|n|cn|cnr|co)(" + NormalQuantizerFactory::GetRegexMatcher() + "|" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
|
||||
std::regex normalMatcher(regexStr);
|
||||
if (std::regex_match(type, sm, normalMatcher))
|
||||
{
|
||||
res.additionalTypeInfo = sm.str(2);
|
||||
res.material = sm.str(3);
|
||||
res.quantizer = sm.str(4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UNIQUEINDEX:
|
||||
{
|
||||
std::string regexStr = "u(s?([0-9]+lod)?)?(" + CompressedTextureFactory<MaterialLibraryPointer>::GetRegexMatcher() + ")?(c|n|cn|cnr|co)(" + NormalQuantizerFactory::GetRegexMatcher() + "|" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
|
||||
std::regex uniqueIndexMatcher(regexStr);
|
||||
if (std::regex_match(type, sm, uniqueIndexMatcher))
|
||||
{
|
||||
res.additionalTypeInfo = sm.str(1);
|
||||
res.textureCompressor = sm.str(3);
|
||||
res.material = sm.str(4);
|
||||
res.quantizer = sm.str(5);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MULTIROOT:
|
||||
{
|
||||
std::string regexStr = "m(cc|(([1-8]?)(b[1-8])?))";
|
||||
std::regex multiRootMatcher(regexStr);
|
||||
if (std::regex_match(type, sm, multiRootMatcher))
|
||||
{
|
||||
res.additionalTypeInfo = sm.str(1);
|
||||
res.material = "c";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BITTREES:
|
||||
{
|
||||
std::string regexStr = "blc(" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
|
||||
std::regex bittreesMatcher(regexStr);
|
||||
if (std::regex_match(type, sm, bittreesMatcher))
|
||||
{
|
||||
res.additionalTypeInfo = "l";
|
||||
res.material = "c";
|
||||
res.quantizer = sm.str(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
unsigned8 TreeTypeParser::GetBitsPerTree(const TreeTypeDescriptor& type)
|
||||
{
|
||||
if (type.globalType != MULTIROOT) return 0;
|
||||
if (type.additionalTypeInfo == "")
|
||||
// Standard multiroot, one bit per channel
|
||||
return 1;
|
||||
if (type.additionalTypeInfo == "cc")
|
||||
{ // Color Channel per root
|
||||
return GetBitsPerChannel(type);
|
||||
}
|
||||
std::regex multiRootBitMatcher("[0-8]?b([1-8])");
|
||||
std::smatch sm;
|
||||
if (std::regex_match(type.additionalTypeInfo, sm, multiRootBitMatcher))
|
||||
{ // MultiBitMultiRoot tree
|
||||
return std::stoi(sm.str(1));
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
unsigned8 TreeTypeParser::GetBitsPerChannel(const TreeTypeDescriptor& type)
|
||||
{
|
||||
if (type.globalType != MULTIROOT) return 0;
|
||||
std::regex multiRootMatcher("([0-8])(b[1-8])?");
|
||||
std::smatch sm;
|
||||
if (std::regex_match(type.additionalTypeInfo, sm, multiRootMatcher))
|
||||
{
|
||||
return std::stoi(sm.str(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned8 TreeTypeParser::GetLevelsWithoutMaterials(const TreeTypeDescriptor& type)
|
||||
{
|
||||
if (type.globalType != UNIQUEINDEX) return 0;
|
||||
std::regex lodMatcher("s?([0-9]+)lod");
|
||||
std::smatch sm;
|
||||
unsigned8 lod = 0;
|
||||
if (std::regex_match(type.additionalTypeInfo, sm, lodMatcher))
|
||||
lod = std::stoi(sm.str(1));
|
||||
return lod;
|
||||
}
|
||||
23
Research/core/OctreeBuilder/TreeTypeParser.h
Normal file
23
Research/core/OctreeBuilder/TreeTypeParser.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include <string>
|
||||
|
||||
struct TreeTypeDescriptor
|
||||
{
|
||||
TreeType globalType;
|
||||
std::string additionalTypeInfo;
|
||||
std::string textureCompressor;
|
||||
std::string material;
|
||||
std::string quantizer;
|
||||
};
|
||||
|
||||
class TreeTypeParser
|
||||
{
|
||||
public:
|
||||
static TreeType GetGlobalType(std::string type);
|
||||
static TreeTypeDescriptor GetTreeTypeDescriptor(std::string type);
|
||||
static unsigned8 GetBitsPerTree(const TreeTypeDescriptor& type);
|
||||
static unsigned8 GetBitsPerChannel(const TreeTypeDescriptor& type);
|
||||
static unsigned8 GetLevelsWithoutMaterials(const TreeTypeDescriptor& type);
|
||||
};
|
||||
|
||||
364
Research/core/OctreeBuilder/UniqueIndexMaterialOctreeBuilder.h
Normal file
364
Research/core/OctreeBuilder/UniqueIndexMaterialOctreeBuilder.h
Normal file
@@ -0,0 +1,364 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include <unordered_set>
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../scene/Material/MaterialLibraryPointer.h"
|
||||
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
|
||||
template<typename T, typename Comparer>
|
||||
class UniqueIndexMaterialOctreeBuilder : public BaseMaterialOctreeBuilder<T>
|
||||
{
|
||||
private:
|
||||
static std::string GetSubtreeTextureCompressionType() { return "b"; } // Basic texture compression (e.g. no texture compression)
|
||||
typedef MaterialLibraryUniqueIndexTree<T, Comparer, T::CHANNELSPERPIXEL> FinalTreeType;
|
||||
typedef MaterialTree<T, Comparer> IntermediateTreeType;
|
||||
public:
|
||||
UniqueIndexMaterialOctreeBuilder(std::string textureCompressionType, BaseQuantizer<T, Comparer>* quantizer = NULL, unsigned32 levelsWithoutMaterials = 0) :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mReduceMaterials(quantizer != NULL),
|
||||
mTextureCompressionType(textureCompressionType),
|
||||
mLevelsWithoutMaterials(levelsWithoutMaterials),
|
||||
mSceneMaterials(std::vector<T>()),
|
||||
mMaterialReplacers(std::unordered_map<T, T>()),
|
||||
mCurPreprocessPassMaterials(std::vector<T>()),
|
||||
mMainTreeMaterials(std::vector<std::pair<glm::uvec3, T>>()),
|
||||
mIntermediateTree(NULL),
|
||||
mQuantizer(quantizer)
|
||||
{}
|
||||
|
||||
UniqueIndexMaterialOctreeBuilder(std::string textureCompressionType, unsigned32 levelsWithoutMaterials = 0) :
|
||||
UniqueIndexMaterialOctreeBuilder(textureCompressionType, NULL, levelsWithoutMaterials)
|
||||
{}
|
||||
|
||||
~UniqueIndexMaterialOctreeBuilder() override
|
||||
{
|
||||
if (mIntermediateTree != NULL) delete mIntermediateTree;
|
||||
if (mTree != NULL) delete mTree;
|
||||
}
|
||||
|
||||
std::string GetTreeType() override
|
||||
{
|
||||
return "u" +
|
||||
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
|
||||
+ mTextureCompressionType + MaterialAbbreviation<T>()() +
|
||||
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
|
||||
}
|
||||
|
||||
std::string GetSubtreeType()
|
||||
{
|
||||
return "u" +
|
||||
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
|
||||
+ GetSubtreeTextureCompressionType() + MaterialAbbreviation<T>()() +
|
||||
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
|
||||
}
|
||||
protected:
|
||||
bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const override
|
||||
{
|
||||
// Sort them so that the trees with the lowest index will be processed first.
|
||||
// Since indexes are given in a depth-first order, and the lowest indexes are given to the trees with the highest ChildIndex in each level,
|
||||
for (unsigned8 bit = GetAppendedTreeLevel(); bit > 0; bit--)
|
||||
{
|
||||
unsigned mask = 1 << (bit - 1);
|
||||
if ((coord1.z & mask) != (coord2.z & mask))
|
||||
return (coord1.z & mask) > (coord2.z & mask);
|
||||
if ((coord1.y & mask) != (coord2.y & mask))
|
||||
return (coord1.y & mask) > (coord2.y & mask);
|
||||
if ((coord1.x & mask) != (coord2.x & mask))
|
||||
return (coord1.x & mask) > (coord2.x & mask);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize the main tree
|
||||
void InitTree() override
|
||||
{
|
||||
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(mTextureCompressionType);
|
||||
mTree = new FinalTreeType(GetTreeDepth(), texture, mLevelsWithoutMaterials);
|
||||
IntermediateTreeType* tempTree = new IntermediateTreeType(GetTreeDepth());
|
||||
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
for (auto coordMaterial : mMainTreeMaterials)
|
||||
{
|
||||
tempTree->SetMaterial(coordMaterial.first, GetAppendedTreeLevel(), coordMaterial.second);
|
||||
}
|
||||
tempTree->PropagateMaterials(T::WeightedAverage);
|
||||
mTree->BaseOn(tempTree);
|
||||
}
|
||||
|
||||
mFirstPass = true;
|
||||
}
|
||||
|
||||
bool UsePreprocessing() const override { return !IsSinglePass(); }
|
||||
void InitPreprocessing() override
|
||||
{
|
||||
mSceneMaterials.clear();
|
||||
mMaterialReplacers.clear();
|
||||
mMainTreeMaterials.clear();
|
||||
}
|
||||
void InitCurPreprocessPass(glm::uvec3 coordinate) override
|
||||
{
|
||||
mCurPreprocessPassMaterials.clear();
|
||||
}
|
||||
void PreProcessNode(const glm::uvec3& coordinate, const T& color) override { mCurPreprocessPassMaterials.push_back(color); }
|
||||
void FinalizeCurPreprocessPass(glm::uvec3 coord) override
|
||||
{
|
||||
if (mCurPreprocessPassMaterials.empty())
|
||||
return;
|
||||
tbb::parallel_sort(mCurPreprocessPassMaterials, Comparer());
|
||||
std::vector<T> uniqueMaterials;
|
||||
std::vector<float> uniqueMaterialWeights;
|
||||
unsigned curMaterialCount = 0;
|
||||
T lastSeenMaterial = mCurPreprocessPassMaterials[0];
|
||||
for (auto color : mCurPreprocessPassMaterials)
|
||||
{
|
||||
if (!(lastSeenMaterial == color))
|
||||
{
|
||||
uniqueMaterials.push_back(lastSeenMaterial);
|
||||
uniqueMaterialWeights.push_back((float)curMaterialCount);
|
||||
lastSeenMaterial = color;
|
||||
curMaterialCount = 0;
|
||||
}
|
||||
curMaterialCount++;
|
||||
}
|
||||
T avgMaterial = T::WeightedAverage(uniqueMaterials, uniqueMaterialWeights);
|
||||
mMainTreeMaterials.push_back(std::pair<glm::uvec3, T>(coord, avgMaterial));
|
||||
|
||||
// Append the unique colors to the scene colors and compress them to keep the memory usage acceptable
|
||||
mSceneMaterials.insert(mSceneMaterials.end(), uniqueMaterials.begin(), uniqueMaterials.end());
|
||||
tbb::parallel_sort(mSceneMaterials, Comparer());
|
||||
mSceneMaterials.erase(std::unique(mSceneMaterials.begin(), mSceneMaterials.end()), mSceneMaterials.end());
|
||||
}
|
||||
|
||||
void QuantizeSceneMaterials()
|
||||
{
|
||||
if (mQuantizer == NULL || !mReduceMaterials) return;
|
||||
if (verbose) printf("Quantizing/merging %llu %s...", (unsigned64)mSceneMaterials.size(), MaterialName<T>()());
|
||||
Stopwatch watch; watch.Reset();
|
||||
auto quantizedSceneMaterials = mQuantizer->QuantizeMaterials(mSceneMaterials);
|
||||
|
||||
// Replace the list of scene colors with the quantized scene colors
|
||||
mSceneMaterials.clear();
|
||||
for (auto color : *quantizedSceneMaterials)
|
||||
mSceneMaterials.push_back(color.second);
|
||||
tbb::parallel_sort(mSceneMaterials, Comparer());
|
||||
mSceneMaterials.erase(std::unique(mSceneMaterials.begin(), mSceneMaterials.end()), mSceneMaterials.end());
|
||||
mSceneMaterials.shrink_to_fit();
|
||||
|
||||
// Build a dictionary for quick lookup of original scene colors and their quantized counterparts
|
||||
mMaterialReplacers = std::unordered_map<T, T>();
|
||||
for (auto color : *quantizedSceneMaterials)
|
||||
mMaterialReplacers.insert(std::make_pair(color.first, color.second));
|
||||
|
||||
// Clear the cur preprocessMaterials (to free up memory during tree construction)
|
||||
mCurPreprocessPassMaterials = std::vector<T>();
|
||||
delete quantizedSceneMaterials;
|
||||
|
||||
// Replace the old colors by the new ones
|
||||
if (verbose) printf("Quantized %s in %d ms\n", MaterialName<T>()(), (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
void FinalizeTree() override
|
||||
{
|
||||
Stopwatch watch;
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
unsigned32 i = 1;
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
{
|
||||
{ // Scope subTree variable
|
||||
if (verbose) printf("Reading subtree %u / %u at (%u, %u, %u) from cache...\n", i, (unsigned32)(GetValidCoords().size()), coord.x, coord.y, coord.z);
|
||||
FinalTreeType* subTree = (FinalTreeType*)OctreeLoader::ReadCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
|
||||
if (subTree != NULL)
|
||||
{
|
||||
// Append the subtree to the main tree
|
||||
if (verbose) printf("Appending subtree... ");;
|
||||
watch.Reset();
|
||||
mTree->Append(coord, GetAppendedTreeLevel(), subTree);
|
||||
delete subTree;
|
||||
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the new part of the main tree to a DAG
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG(GetAppendedTreeLevel());
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the material texture
|
||||
watch.Reset();
|
||||
if (verbose) printf("Generating material texture... ");
|
||||
mTree->GetMaterialTexture();
|
||||
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Delete the cache files
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
if (verbose) printf("Deleting cache...");
|
||||
watch.Reset();
|
||||
for (const glm::uvec3& coord : GetValidCoords())
|
||||
OctreeLoader::DeleteCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
if (verbose) printf("Cache deleted in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
mTree = NULL;
|
||||
mSceneMaterials.clear();
|
||||
mMaterialReplacers.clear();
|
||||
mMainTreeMaterials.clear();
|
||||
}
|
||||
|
||||
// Don't build this subtree again if a cache file exists for it
|
||||
bool CancelCurPassTree(const glm::uvec3& coord) override
|
||||
{
|
||||
return OctreeLoader::VerifyCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
|
||||
}
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
mIntermediateTree = new IntermediateTreeType(GetSinglePassTreeDepth());
|
||||
mIntermediateTree->UseLeafMap(false);
|
||||
}
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override
|
||||
{
|
||||
Stopwatch watch;
|
||||
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
mIntermediateTree->ToDAG();
|
||||
|
||||
// Propagate the materials
|
||||
watch.Reset();
|
||||
if (verbose) printf("Propagating materials in subtree... ");
|
||||
mIntermediateTree->PropagateMaterials(T::WeightedAverage);
|
||||
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (mFirstPass)
|
||||
{
|
||||
std::vector<T> uniqueMaterials = mIntermediateTree->GetUniqueMaterials();
|
||||
mSceneMaterials.insert(mSceneMaterials.end(), uniqueMaterials.begin(), uniqueMaterials.end());
|
||||
QuantizeSceneMaterials();
|
||||
mFirstPass = false;
|
||||
}
|
||||
|
||||
// Replace the materials by their quantized counterparts
|
||||
if (mQuantizer != NULL)
|
||||
{
|
||||
if (verbose) printf("Replacing materials by their quantized counterparts.");
|
||||
watch.Reset();
|
||||
std::vector<T> curPassMaterials = mIntermediateTree->GetMaterials();
|
||||
std::vector<T> curPassQuantizedMaterials(curPassMaterials.size());
|
||||
auto quickQuantizer = dynamic_cast<QuickQuantizer<T>*>(mQuantizer);
|
||||
|
||||
if (verbose) printf(".");
|
||||
// For each material in the curPassMaterials, find the closest material
|
||||
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](size_t i)
|
||||
{
|
||||
auto cachedReplacer = mMaterialReplacers.find(curPassMaterials[i]);
|
||||
if (cachedReplacer != mMaterialReplacers.end())
|
||||
curPassQuantizedMaterials[i] = cachedReplacer->second;
|
||||
else
|
||||
{
|
||||
// If we can use the quick quantizer, try it
|
||||
if (quickQuantizer != NULL)
|
||||
{
|
||||
auto quickQuantizedValue = quickQuantizer->Quantize(curPassMaterials[i]);
|
||||
auto quickQuantizedCachedReplacer = mMaterialReplacers.find(quickQuantizedValue);
|
||||
if (quickQuantizedCachedReplacer != mMaterialReplacers.end())
|
||||
curPassQuantizedMaterials[i] = quickQuantizedCachedReplacer->second;
|
||||
return;
|
||||
}
|
||||
curPassQuantizedMaterials[i] = NearestFinder<T>()(curPassMaterials[i], mSceneMaterials);
|
||||
}
|
||||
});
|
||||
// Update the current scene color map
|
||||
std::unordered_map<T, T> quantizedMaterials;
|
||||
for (size_t i = 0; i < curPassMaterials.size(); i++)
|
||||
quantizedMaterials.insert(std::make_pair(curPassMaterials[i], curPassQuantizedMaterials[i]));
|
||||
|
||||
if (verbose) printf(".");
|
||||
mIntermediateTree->ReplaceMaterials(quantizedMaterials);
|
||||
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
// Create the UniqueIndexTree for this current pass
|
||||
std::string subtreeCompressionType = mTextureCompressionType;
|
||||
if (!IsSinglePass())
|
||||
subtreeCompressionType = GetSubtreeTextureCompressionType();
|
||||
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(subtreeCompressionType);
|
||||
auto curPassTree = new FinalTreeType(GetSinglePassTreeDepth(), texture, mLevelsWithoutMaterials);
|
||||
|
||||
// Finalize the current pass tree
|
||||
if (verbose) printf("Finalizing subtree...");
|
||||
watch.Reset();
|
||||
curPassTree->BaseOn(mIntermediateTree); // Note that BaseOn will delete the intermediate tree, no need to do that manually
|
||||
mIntermediateTree = NULL;
|
||||
if (verbose) printf("Finalized in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
curPassTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (IsSinglePass()) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = curPassTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
OctreeLoader::WriteCache(curPassTree, GetSubtreeType(), GetSinglePassTreeFilename(coord), verbose);
|
||||
delete curPassTree;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mIntermediateTree;
|
||||
}
|
||||
}
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const T& color) override { mIntermediateTree->AddLeafNode(coordinate, color); }
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const T& color) override { if (!mIntermediateTree->HasLeaf(coordinate)) AddNode(coordinate, color); }
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
|
||||
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
|
||||
private:
|
||||
std::string GetSinglePassTreeFilename(glm::uvec3 coord)
|
||||
{
|
||||
char buffer[255];
|
||||
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
FinalTreeType* mTree;
|
||||
bool mReduceMaterials;
|
||||
bool mFirstPass;
|
||||
std::string mTextureCompressionType;
|
||||
unsigned32 mLevelsWithoutMaterials;
|
||||
|
||||
std::vector<T> mSceneMaterials;
|
||||
std::unordered_map<T, T> mMaterialReplacers;
|
||||
std::vector<T> mCurPreprocessPassMaterials;
|
||||
std::vector<std::pair<glm::uvec3, T>> mMainTreeMaterials;
|
||||
|
||||
IntermediateTreeType* mIntermediateTree;
|
||||
|
||||
BaseQuantizer<T, Comparer>* mQuantizer;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
#include "UniqueIndexShiftColoredOctreeBuilder.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "OctreeLoader.h"
|
||||
#include "CompressedTextureFactory.h"
|
||||
|
||||
#include "../Hashers.h"
|
||||
#include "../Comparers.h"
|
||||
#include "../Util/Stopwatch.h"
|
||||
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../scene/Material/SignedIntMaterial.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
|
||||
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include "../../inc/tbb/concurrent_queue.h"
|
||||
|
||||
|
||||
UniqueIndexShiftColoredOctreeBuilder::UniqueIndexShiftColoredOctreeBuilder(std::string textureCompressionType, BaseQuantizer<Color, ColorCompare>* quantizer, unsigned32 levelsWithoutMaterials) :
|
||||
BaseMaterialOctreeBuilder(),
|
||||
mTree(NULL),
|
||||
mReduceColors(quantizer != NULL),
|
||||
mTextureCompressionType(textureCompressionType),
|
||||
mLevelsWithoutMaterials(levelsWithoutMaterials),
|
||||
mSceneColors(std::vector<Color>()),
|
||||
mColorReplacers(std::unordered_map<Color, Color>()),
|
||||
mCurPreprocessPassColors(std::vector<Color>()),
|
||||
mMainTreeColors(std::vector<std::pair<glm::uvec3, Color>>()),
|
||||
mIntermediateTree(NULL),
|
||||
mQuantizer(quantizer)
|
||||
{}
|
||||
|
||||
UniqueIndexShiftColoredOctreeBuilder::~UniqueIndexShiftColoredOctreeBuilder() {}
|
||||
|
||||
std::string UniqueIndexShiftColoredOctreeBuilder::GetTreeType() {
|
||||
return "us" +
|
||||
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
|
||||
+ mTextureCompressionType + "c" +
|
||||
(mReduceColors ? mQuantizer->GetQuantizerDescriptor() : "");
|
||||
}
|
||||
|
||||
bool UniqueIndexShiftColoredOctreeBuilder::UsePreprocessing() const
|
||||
{
|
||||
return !IsSinglePass() || mReduceColors;
|
||||
}
|
||||
|
||||
bool UniqueIndexShiftColoredOctreeBuilder::SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const
|
||||
{
|
||||
// Sort them so that the trees with the lowest index will be processed first.
|
||||
// Since indexes are given in a depth-first order, and the lowest indexes are given to the trees with the highest ChildIndex in each level,
|
||||
for (unsigned8 bit = GetTreeDepth() - GetSinglePassTreeDepth(); bit > 0; bit--)
|
||||
{
|
||||
unsigned mask = 1 << (bit - 1);
|
||||
if ((coord1.z & mask) != (coord2.z & mask))
|
||||
return (coord1.z & mask) > (coord2.z & mask);
|
||||
if ((coord1.y & mask) != (coord2.y & mask))
|
||||
return (coord1.y & mask) > (coord2.y & mask);
|
||||
if ((coord1.x & mask) != (coord2.x & mask))
|
||||
return (coord1.x & mask) > (coord2.x & mask);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::InitTree()
|
||||
{
|
||||
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(mTextureCompressionType);
|
||||
mTree = new UniqueIndexShiftTree<Color, ColorCompare>(GetTreeDepth(), texture, mLevelsWithoutMaterials);
|
||||
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
MaterialTree<Color, ColorCompare>* tempTree = new MaterialTree<Color, ColorCompare>(GetTreeDepth());
|
||||
mTree->PrepareForMaterials(mSceneColors);
|
||||
for (auto coordColor : mMainTreeColors)
|
||||
{
|
||||
tempTree->SetMaterial(coordColor.first, GetAppendedTreeLevel(), coordColor.second);
|
||||
}
|
||||
tempTree->PropagateMaterials(Color::WeightedAverage);
|
||||
ReplaceByQuantized(tempTree);
|
||||
mTree->BaseOn(tempTree);
|
||||
// BaseOn calls delete on tempTree
|
||||
}
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::FinalizeTree()
|
||||
{
|
||||
Stopwatch watch;
|
||||
// Generate the material texture
|
||||
watch.Reset();
|
||||
if (verbose) printf("Generating material texture... ");
|
||||
mTree->GetMaterialTexture();
|
||||
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::TerminateTree()
|
||||
{
|
||||
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
|
||||
delete mTree;
|
||||
mSceneColors.clear();
|
||||
mColorReplacers.clear();
|
||||
mMainTreeColors.clear();
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
mIntermediateTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
|
||||
//mIntermediateTree->UseLeafMap(true);
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::ReplaceByQuantized(MaterialTree<Color, ColorCompare>* tree)
|
||||
{
|
||||
std::vector<Color> curPassMaterials = tree->GetUniqueMaterials();
|
||||
CollectionHelper::Unique(curPassMaterials, ColorCompare());
|
||||
std::vector<Color> curPassQuantizedMaterials(curPassMaterials.size());
|
||||
|
||||
if (verbose) printf(".");
|
||||
// For each material in the curPassMaterials, find the closest material
|
||||
tbb::concurrent_queue<size_t> newReplacers;
|
||||
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](const size_t& i)
|
||||
{
|
||||
auto cachedReplacer = mColorReplacers.find(curPassMaterials[i]);
|
||||
if (cachedReplacer != mColorReplacers.end())
|
||||
curPassQuantizedMaterials[i] = cachedReplacer->second;
|
||||
else
|
||||
{
|
||||
curPassQuantizedMaterials[i] = NearestFinder<Color>()(curPassMaterials[i], mSceneColors);
|
||||
newReplacers.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the newly found replacers to the cache
|
||||
for (auto i = newReplacers.unsafe_begin(); i != newReplacers.unsafe_end(); ++i)
|
||||
mColorReplacers.insert(std::pair<Color, Color>(curPassMaterials[*i], curPassQuantizedMaterials[*i]));
|
||||
|
||||
// Update the current scene color map
|
||||
std::unordered_map<Color, Color> quantizedColors;
|
||||
for (size_t i = 0; i < curPassMaterials.size(); i++)
|
||||
quantizedColors.insert(std::pair<Color, Color>(curPassMaterials[i], curPassQuantizedMaterials[i]));
|
||||
|
||||
if (verbose) printf(".");
|
||||
tree->ReplaceMaterials(quantizedColors);
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
|
||||
{
|
||||
Stopwatch watch;
|
||||
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
|
||||
{
|
||||
// Propagate the materials
|
||||
watch.Reset();
|
||||
if (verbose) printf("Propagating materials in subtree... ");
|
||||
mIntermediateTree->PropagateMaterials(Color::WeightedAverage);
|
||||
if (!IsSinglePass())
|
||||
{
|
||||
Color expectedRootColor;
|
||||
for (auto color : mMainTreeColors)
|
||||
if (color.first == coord)
|
||||
{
|
||||
expectedRootColor = color.second;
|
||||
break;
|
||||
}
|
||||
mIntermediateTree->SetMaterial(glm::uvec3(0), 0, expectedRootColor);
|
||||
}
|
||||
|
||||
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Replace the materials by their quantized counterparts
|
||||
// This is also necessary
|
||||
if (mQuantizer != NULL || !IsSinglePass())
|
||||
{
|
||||
if (verbose) printf("Replacing materials by their quantized counterparts.");
|
||||
watch.Reset();
|
||||
ReplaceByQuantized(mIntermediateTree);
|
||||
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
|
||||
|
||||
// Create the UniqueIndexTree for this current pass
|
||||
std::string subtreeCompressionType = mTextureCompressionType;
|
||||
if (!IsSinglePass()) subtreeCompressionType = "b";
|
||||
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(subtreeCompressionType);
|
||||
UniqueIndexShiftTree<Color, ColorCompare>* curPassTree = new UniqueIndexShiftTree<Color, ColorCompare>(GetSinglePassTreeDepth(), texture, mLevelsWithoutMaterials);
|
||||
if (!IsSinglePass())
|
||||
curPassTree->PrepareForMaterials(mSceneColors);
|
||||
|
||||
// Finalize the current pass tree
|
||||
if (verbose) printf("Finalizing subtree...");
|
||||
watch.Reset();
|
||||
curPassTree->BaseOn(mIntermediateTree); // Note that BaseOn will delete the intermediate tree, no need to do that manually
|
||||
if (verbose) printf("Finalized in %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Convert the subtree to a DAG first, this saved time (and valueable memory) when appending and converting the total tree
|
||||
if (verbose) printf("Converting subtree to DAG...\n");
|
||||
watch.Reset();
|
||||
curPassTree->ToDAG();
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
if (IsSinglePass()) // Means we just constructed the root, so no need to append
|
||||
{
|
||||
delete mTree;
|
||||
mTree = curPassTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append the subtree to the main tree
|
||||
if (verbose) printf("Appending subtree... ");;
|
||||
watch.Reset();
|
||||
mTree->Append(coord, GetAppendedTreeLevel(), curPassTree);
|
||||
delete curPassTree;
|
||||
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
|
||||
// Convert the new part of the main tree to a DAG
|
||||
if (verbose) printf("Converting current tree to DAG...\n");
|
||||
watch.Reset();
|
||||
mTree->ToDAG(GetAppendedTreeLevel());
|
||||
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete mIntermediateTree;
|
||||
}
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::InitPreprocessing()
|
||||
{
|
||||
mSceneColors.clear();
|
||||
mColorReplacers.clear();
|
||||
mMainTreeColors.clear();
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::InitCurPreprocessPass(glm::uvec3 coordinate)
|
||||
{
|
||||
mCurPreprocessPassColors.clear();
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::PreProcessNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
mCurPreprocessPassColors.push_back(color);
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::FinalizeCurPreprocessPass(glm::uvec3 coord)
|
||||
{
|
||||
if (mCurPreprocessPassColors.empty())
|
||||
return;
|
||||
tbb::parallel_sort(mCurPreprocessPassColors, ColorCompare());
|
||||
std::vector<Color> uniqueColors;
|
||||
std::vector<float> uniqueColorWeights;
|
||||
unsigned curColorCount = 0;
|
||||
Color lastSeenColor = mCurPreprocessPassColors[0];
|
||||
for (auto color : mCurPreprocessPassColors)
|
||||
{
|
||||
if (!(lastSeenColor == color))
|
||||
{
|
||||
uniqueColors.push_back(lastSeenColor);
|
||||
uniqueColorWeights.push_back((float)curColorCount);
|
||||
lastSeenColor = color;
|
||||
}
|
||||
curColorCount++;
|
||||
}
|
||||
Color avgColor = Color::WeightedAverage(uniqueColors, uniqueColorWeights);
|
||||
mMainTreeColors.push_back(std::pair<glm::uvec3, Color>(coord, avgColor));
|
||||
|
||||
// Append the unique colors to the scene colors and compress them to keep the memory usage acceptable
|
||||
mSceneColors.insert(mSceneColors.end(), uniqueColors.begin(), uniqueColors.end());
|
||||
tbb::parallel_sort(mSceneColors, ColorCompare());
|
||||
mSceneColors.erase(std::unique(mSceneColors.begin(), mSceneColors.end()), mSceneColors.end());
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::FinalizePreprocessing()
|
||||
{
|
||||
// Quantize the scene colors
|
||||
if (mQuantizer == NULL)
|
||||
{
|
||||
if (verbose) printf("Scene contains %llu colors...\n", (unsigned64)mSceneColors.size());
|
||||
|
||||
mColorReplacers = std::unordered_map<Color, Color>();
|
||||
for (auto color : mSceneColors)
|
||||
mColorReplacers.insert(std::pair<Color, Color>(color, color));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf("Quantizing/merging %llu colors...", (unsigned64)mSceneColors.size());
|
||||
Stopwatch watch; watch.Reset();
|
||||
|
||||
auto quantizedSceneColors = mQuantizer->QuantizeMaterials(mSceneColors);
|
||||
|
||||
// Replace the list of scene colors with the quantized scene colors
|
||||
mSceneColors.clear();
|
||||
for (auto color : *quantizedSceneColors)
|
||||
mSceneColors.push_back(color.second);
|
||||
tbb::parallel_sort(mSceneColors, ColorCompare());
|
||||
mSceneColors.erase(std::unique(mSceneColors.begin(), mSceneColors.end()), mSceneColors.end());
|
||||
mSceneColors.shrink_to_fit();
|
||||
|
||||
// Build a dictionary for quick lookup of original scene colors and their quantized counterparts
|
||||
mColorReplacers = std::unordered_map<Color, Color>();
|
||||
for (auto color : *quantizedSceneColors)
|
||||
mColorReplacers.insert(std::pair<Color, Color>(Color(color.first), Color(color.second)));
|
||||
|
||||
delete quantizedSceneColors;
|
||||
|
||||
// Replace the old colors by the new ones
|
||||
if (verbose) printf("Quantized colors in %d ms\n", (int)(watch.GetTime() * 1000));
|
||||
}
|
||||
// Clear the cur preprocessColors (to free up memory during tree construction)
|
||||
mCurPreprocessPassColors = std::vector<Color>();
|
||||
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
|
||||
{
|
||||
if (!mIntermediateTree->HasLeaf(coordinate))
|
||||
AddNode(coordinate, color);
|
||||
}
|
||||
|
||||
void UniqueIndexShiftColoredOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& colorVec)
|
||||
{
|
||||
mIntermediateTree->AddLeafNode(coordinate, Color(colorVec));
|
||||
}
|
||||
|
||||
std::vector<size_t> UniqueIndexShiftColoredOctreeBuilder::GetOctreeNodesPerLevel()
|
||||
{
|
||||
return mTree->GetOctreeNodesPerLevel();
|
||||
}
|
||||
|
||||
std::vector<size_t> UniqueIndexShiftColoredOctreeBuilder::GetNodesPerLevel()
|
||||
{
|
||||
return mTree->GetNodesPerLevel();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "BaseMaterialOctreeBuilder.h"
|
||||
#include <unordered_set>
|
||||
#include "../../scene/Material/Color.h"
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../scene/Octree/UniqueIndexShiftTree.h"
|
||||
#include "../../scene/Octree/MaterialTree.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
|
||||
|
||||
struct u8vec3comparer;
|
||||
struct ColorCompare;
|
||||
|
||||
class UniqueIndexShiftColoredOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
|
||||
{
|
||||
public:
|
||||
UniqueIndexShiftColoredOctreeBuilder(std::string textureCompressionType, BaseQuantizer<Color, ColorCompare>* quantizer = NULL, unsigned32 levelsWithoutMaterials = 0);
|
||||
~UniqueIndexShiftColoredOctreeBuilder() override;
|
||||
|
||||
std::string GetTreeType() override;
|
||||
protected:
|
||||
bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const override;
|
||||
|
||||
// Initialize the main tree
|
||||
void InitTree() override;
|
||||
|
||||
bool UsePreprocessing() const override;
|
||||
void InitPreprocessing() override;
|
||||
void InitCurPreprocessPass(glm::uvec3 coordinate) override;
|
||||
void PreProcessNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
void FinalizeCurPreprocessPass(glm::uvec3 coord) override;
|
||||
void FinalizePreprocessing() override;
|
||||
|
||||
void FinalizeTree() override;
|
||||
// Step to finalize the main tree (for example storing it to a file)
|
||||
void TerminateTree() override;
|
||||
|
||||
// Initialize the tree for the current pass.
|
||||
void InitCurPassTree(glm::uvec3 coord) override;
|
||||
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
|
||||
void FinalizeCurPassTree(glm::uvec3 coord) override;
|
||||
|
||||
// Should add a node to the current pass tree at the given coordinate and color
|
||||
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() override;
|
||||
std::vector<size_t> GetNodesPerLevel() override;
|
||||
void ReplaceByQuantized(MaterialTree<Color, ColorCompare>* intermediateTree);
|
||||
private:
|
||||
UniqueIndexShiftTree<Color, ColorCompare>* mTree;
|
||||
bool mReduceColors;
|
||||
std::string mTextureCompressionType;
|
||||
unsigned32 mLevelsWithoutMaterials;
|
||||
|
||||
std::vector<Color> mSceneColors;
|
||||
std::unordered_map<Color, Color> mColorReplacers;
|
||||
std::vector<Color> mCurPreprocessPassColors;
|
||||
std::vector<std::pair<glm::uvec3, Color>> mMainTreeColors;
|
||||
|
||||
MaterialTree<Color, ColorCompare>* mIntermediateTree;
|
||||
|
||||
BaseQuantizer<Color, ColorCompare>* mQuantizer;
|
||||
};
|
||||
|
||||
19
Research/core/PathHelper.cpp
Normal file
19
Research/core/PathHelper.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "PathHelper.h"
|
||||
#include "Defines.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
std::string PathHelper::GetExtension(std::string path)
|
||||
{
|
||||
return path.substr(path.find_last_of(".") + 1);
|
||||
}
|
||||
|
||||
std::string PathHelper::GetFilename(std::string path)
|
||||
{
|
||||
const size_t last_slash_idx = path.find_last_of("\\/");
|
||||
if (std::string::npos != last_slash_idx)
|
||||
{
|
||||
path.erase(0, last_slash_idx + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
10
Research/core/PathHelper.h
Normal file
10
Research/core/PathHelper.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "Defines.h"
|
||||
#include <string>
|
||||
|
||||
namespace PathHelper
|
||||
{
|
||||
extern std::string GetExtension(std::string path);
|
||||
extern std::string GetFilename(std::string path);
|
||||
};
|
||||
|
||||
158
Research/core/Serializer.h
Normal file
158
Research/core/Serializer.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include "Defines.h"
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
template<typename T, typename size_type = unsigned32>
|
||||
struct Serializer
|
||||
{
|
||||
static void Serialize(const T& value, std::ostream& out)
|
||||
{
|
||||
out.write((char*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
static void Deserialize(T& value, std::istream& in)
|
||||
{
|
||||
in.read((char*)&value, sizeof(value));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename size_type>
|
||||
struct Serializer<std::string, size_type>
|
||||
{
|
||||
static void Serialize(const std::string& value, std::ostream& out)
|
||||
{
|
||||
size_type size = (size_type)value.size();
|
||||
out.write((char*)&size, sizeof(size));
|
||||
out.write((char*)&value[0], sizeof(char) * size);
|
||||
}
|
||||
|
||||
static void Deserialize(std::string& value, std::istream& in)
|
||||
{
|
||||
size_type size;
|
||||
in.read((char*)&size, sizeof(size));
|
||||
std::vector<char> res(size);
|
||||
in.read((char*)&res[0], sizeof(char) * size);
|
||||
value = std::string(res.begin(), res.end());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename size_type>
|
||||
struct Serializer<std::vector<T>, size_type>
|
||||
{
|
||||
static void Serialize(const std::vector<T>& value, std::ostream& out)
|
||||
{
|
||||
size_type size = (size_type)value.size();
|
||||
out.write((char*)&size, sizeof(size));
|
||||
if (size > 0)
|
||||
for (size_t i = 0; i < size; i++)
|
||||
Serializer<T, size_type>::Serialize(value[i], out);
|
||||
}
|
||||
|
||||
static void Deserialize(std::vector<T>& value, std::istream& in)
|
||||
{
|
||||
size_type size;
|
||||
in.read((char*)&size, sizeof(size));
|
||||
value.resize(size);
|
||||
if (size > 0)
|
||||
for (size_t i = 0; i < size; i++)
|
||||
Serializer<T, size_type>::Deserialize(value[i], in);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename size_type>
|
||||
struct Serializer <std::vector<unsigned32>, size_type>
|
||||
{
|
||||
static void Serialize(const std::vector<unsigned32>& value, std::ostream& out)
|
||||
{
|
||||
size_type size = (size_type)value.size();
|
||||
out.write((char*)&size, sizeof(size));
|
||||
if (size > 0)
|
||||
out.write((char*)&value[0], sizeof(unsigned32) * size);
|
||||
}
|
||||
|
||||
static void Deserialize(std::vector<unsigned32>& value, std::istream& in)
|
||||
{
|
||||
size_type size;
|
||||
in.read((char*)&size, sizeof(size));
|
||||
value.resize(size);
|
||||
if (size > 0)
|
||||
in.read((char*)&value[0], sizeof(unsigned32) * size);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename size_type>
|
||||
struct Serializer <std::vector<unsigned8>, size_type>
|
||||
{
|
||||
static void Serialize(const std::vector<unsigned8>& value, std::ostream& out)
|
||||
{
|
||||
size_type size = (size_type)value.size();
|
||||
out.write((char*)&size, sizeof(size));
|
||||
if (size > 0)
|
||||
out.write((char*)&value[0], sizeof(unsigned8) * size);
|
||||
}
|
||||
|
||||
static void Deserialize(std::vector<unsigned8>& value, std::istream& in)
|
||||
{
|
||||
size_type size;
|
||||
in.read((char*)&size, sizeof(size));
|
||||
value.resize(size);
|
||||
if (size > 0)
|
||||
in.read((char*)&value[0], sizeof(unsigned8) * size);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2, typename size_type>
|
||||
struct Serializer<std::map<T1, T2>, size_type>
|
||||
{
|
||||
static void Serialize(const std::map<T1, T2>& map, std::ostream& out)
|
||||
{
|
||||
// Write the additional properties to a file
|
||||
size_type size = (size_type)map.size();
|
||||
out.write((char*)&size, sizeof(size));
|
||||
if (size > 0)
|
||||
for (auto pair : map)
|
||||
{
|
||||
Serializer<T1, size_type>::Serialize(pair.first, out);
|
||||
Serializer<T2, size_type>::Serialize(pair.second, out);
|
||||
}
|
||||
}
|
||||
|
||||
static void Deserialize(std::map<T1, T2>& map, std::istream& in)
|
||||
{
|
||||
map.clear();
|
||||
|
||||
size_type size = 0;
|
||||
in.read((char*)&size, sizeof(size));
|
||||
|
||||
if (size > 0)
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
T1 first;
|
||||
Serializer<T1>::Deserialize(first, in);
|
||||
|
||||
T2 second;
|
||||
Serializer<T2>::Deserialize(second, in);
|
||||
map.insert(std::pair<T1, T2>(first, second));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Serializer<T*>
|
||||
{
|
||||
static void Serialize(const T* source, size_t count, std::ostream& out)
|
||||
{
|
||||
if (count == 0) return;
|
||||
out.write((char*)source, sizeof(T) * count);
|
||||
}
|
||||
|
||||
static void Deserialize(const T* source, size_t count, std::istream& in)
|
||||
{
|
||||
if (count == 0) return;
|
||||
in.read((char*)source, sizeof(T) * count);
|
||||
}
|
||||
};
|
||||
16
Research/core/StringHelper.cpp
Normal file
16
Research/core/StringHelper.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "StringHelper.h"
|
||||
#include<algorithm>
|
||||
|
||||
bool StringHelper::Replace(std::string& str, const std::string& from, const std::string& to)
|
||||
{
|
||||
size_t startPos = str.find(from);
|
||||
if (startPos == std::string::npos)
|
||||
return false;
|
||||
str.replace(startPos, from.length(), to);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StringHelper::ToUpper(std::string& str)
|
||||
{
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
}
|
||||
8
Research/core/StringHelper.h
Normal file
8
Research/core/StringHelper.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <string>
|
||||
|
||||
class StringHelper
|
||||
{
|
||||
public:
|
||||
static bool Replace(std::string& str, const std::string& toReplace, const std::string& replaceBy);
|
||||
static void ToUpper(std::string& str);
|
||||
};
|
||||
490
Research/core/Util/BinaryTree.h
Normal file
490
Research/core/Util/BinaryTree.h
Normal file
@@ -0,0 +1,490 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include "../BitHelper.h"
|
||||
#include "../CollectionHelper.h"
|
||||
#include "../Serializer.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
template<typename T>
|
||||
class BinaryTree
|
||||
{
|
||||
private:
|
||||
template<typename U>
|
||||
struct BinaryTreeNode
|
||||
{
|
||||
private:
|
||||
BinaryTreeNode<U>* mChildren[2];
|
||||
BinaryTree<U>* mTree;
|
||||
T mMaterial;
|
||||
|
||||
inline unsigned8 GetChildIndex(size_t coordinate, unsigned8 levelsLeft) const {
|
||||
return BitHelper::GetLS(coordinate, levelsLeft) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline BinaryTreeNode<U>* GetNodeAt(size_t coordinate, unsigned8 levelsLeft) const
|
||||
{
|
||||
return mChildren[GetChildIndex(coordinate, levelsLeft)];
|
||||
}
|
||||
public:
|
||||
BinaryTreeNode(BinaryTree<U>* tree) :
|
||||
mTree(tree)
|
||||
{
|
||||
mChildren[0] = mChildren[1] = NULL;
|
||||
}
|
||||
|
||||
~BinaryTreeNode() {}
|
||||
|
||||
BinaryTreeNode* GetChild(unsigned8 index) const
|
||||
{
|
||||
assert(index < 2);
|
||||
return mChildren[index];
|
||||
}
|
||||
|
||||
void SetChild(BinaryTreeNode<U>* child, unsigned8 index) { mChildren[index] = child; }
|
||||
|
||||
size_t GetChildCount() const
|
||||
{
|
||||
return
|
||||
(mChildren[0] == NULL ? 0 : 1) +
|
||||
(mChildren[1] == NULL ? 0 : 1);
|
||||
}
|
||||
bool IsLeaf() const { return mChildren[0] == NULL && mChildren[1] == NULL; }
|
||||
|
||||
BinaryTreeNode<U>* AddNode(size_t coordinate, unsigned8 levelsLeft)
|
||||
{
|
||||
auto node = GetNodeAt(coordinate, levelsLeft);
|
||||
if (node == NULL)
|
||||
{
|
||||
node = mTree->Create();
|
||||
SetChild(node, GetChildIndex(coordinate, levelsLeft));
|
||||
}
|
||||
if (levelsLeft == 0)
|
||||
return node;
|
||||
else
|
||||
return node->AddNode(coordinate, levelsLeft - 1);
|
||||
}
|
||||
|
||||
U GetValueAt(size_t coordinate, unsigned8 levelsLeft) const
|
||||
{
|
||||
auto node = GetNodeAt(coordinate, levelsLeft);
|
||||
if (node == NULL) return GetValue();
|
||||
if (levelsLeft == 0) return node->GetValue();
|
||||
return node->GetValueAt(coordinate, levelsLeft - 1);
|
||||
}
|
||||
|
||||
U GetValue() const { return mMaterial; }
|
||||
|
||||
void SetValue(const T& material) { mMaterial = material; }
|
||||
|
||||
void Traverse(const std::function<void(const BinaryTreeNode<U>*)>& f) const
|
||||
{
|
||||
f.operator()(this);
|
||||
if (mChildren[0] != NULL) mChildren[0]->Traverse(f);
|
||||
if (mChildren[1] != NULL) mChildren[1]->Traverse(f);
|
||||
}
|
||||
|
||||
static bool Compare(BinaryTreeNode<U>* a, BinaryTreeNode<U>* b)
|
||||
{
|
||||
if (a->GetChild(0) != b->GetChild(0)) return (size_t)a->GetChild(0) < (size_t)b->GetChild(0);
|
||||
if (a->GetChild(1) != b->GetChild(1)) return (size_t)a->GetChild(1) < (size_t)b->GetChild(1);
|
||||
return a->GetValue() < b->GetValue();
|
||||
}
|
||||
|
||||
static bool Equals(BinaryTreeNode<U>* a, BinaryTreeNode<U>* b)
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a == NULL || b == NULL) return false;
|
||||
return a->GetChild(0) == b->GetChild(0) && a->GetChild(1) == b->GetChild(1) && a->GetValue() == b->GetValue();
|
||||
}
|
||||
};
|
||||
|
||||
unsigned8 mDepth;
|
||||
BinaryTreeNode<T>* mRoot;
|
||||
std::vector<BinaryTreeNode<T>*> mNodePool;
|
||||
|
||||
std::vector<BinaryTreeNode<T>*> GetNodes()
|
||||
{
|
||||
std::vector<BinaryTreeNode<T>*> res;
|
||||
std::function<void(const BinaryTreeNode<T>*)> nodeFinder = [&](const BinaryTreeNode<T>*) { res.push_back(this); };
|
||||
mRoot->Traverse(nodeFinder);
|
||||
CollectionHelper::Unique(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ShiftDown(unsigned8 levels)
|
||||
{
|
||||
// Shifts the root down the given number of levels
|
||||
if (levels == 0) return;
|
||||
|
||||
for (unsigned8 i = 0; i < levels; i++)
|
||||
{
|
||||
BinaryTreeNode<T>* newRoot = Create();
|
||||
newRoot->SetChild(mRoot, 0);
|
||||
BinaryTreeNode<T>* oldRoot = mRoot;
|
||||
mRoot = newRoot;
|
||||
mNodePool[0] = newRoot;
|
||||
mNodePool[mNodePool.size() - 1] = oldRoot;
|
||||
}
|
||||
}
|
||||
|
||||
static void CalculateNodeLevelsRecursive(BinaryTreeNode<T>* node, unsigned8 level, std::vector<unsigned8>& nodeLevels, const std::unordered_map<BinaryTreeNode<T>*, size_t>& nodeIndices)
|
||||
{
|
||||
assert(nodeIndices.find(node) != nodeIndices.end());
|
||||
auto nodeIndex = nodeIndices.find(node);
|
||||
nodeLevels[nodeIndex->second] = level;
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
auto childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
CalculateNodeLevelsRecursive(childNode, level + 1, nodeLevels, nodeIndices);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the levels of all nodes. Levels are stored in the same order as the node pool.
|
||||
std::vector<unsigned8> CalculateNodeLevels() const
|
||||
{
|
||||
auto nodeIndices = CollectionHelper::GetIndexMap(mNodePool);
|
||||
std::vector<unsigned8> nodeLevels(mNodePool.size());
|
||||
CalculateNodeLevelsRecursive(mRoot, 0, nodeLevels, nodeIndices);
|
||||
return nodeLevels;
|
||||
}
|
||||
|
||||
inline static size_t GetNodePointer(const size_t& index, const size_t& pointerSize, const size_t& valueSize, const size_t& firstLeafIndex, const bool& onlyLeafsContainValues)
|
||||
{
|
||||
if (!onlyLeafsContainValues)
|
||||
return 2 + index * (pointerSize * 2 + valueSize);
|
||||
else
|
||||
{
|
||||
size_t pointer = 2 + std::min(index, firstLeafIndex) * (pointerSize * 2);
|
||||
if (index > firstLeafIndex)
|
||||
pointer += (index - firstLeafIndex - 1) * (pointerSize * 2 + valueSize);
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
public:
|
||||
BinaryTree()
|
||||
{
|
||||
mDepth = 0;
|
||||
mRoot = Create();
|
||||
}
|
||||
~BinaryTree()
|
||||
{
|
||||
tbb::parallel_for_each(mNodePool.begin(), mNodePool.end(), [](BinaryTreeNode<T>* node) { delete node; });
|
||||
mNodePool.clear();
|
||||
}
|
||||
|
||||
void AddLeafNode(size_t coordinate) { AddNode(coordinate, mDepth); }
|
||||
void AddNode(size_t coordinate, unsigned8 level)
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
mRoot->AddNode(coordinate, level);
|
||||
}
|
||||
|
||||
T GetValueAtLeaf(size_t coordinate) const { return GetValueAtNode(coordinate, mDepth); }
|
||||
T GetValueAtNode(size_t coordinate, unsigned8 level) const
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
return mRoot->GetValueAt(coordinate, level);
|
||||
}
|
||||
void SetValueAtLeaf(size_t coordinate, T value) { SetValueAtNode(coordinate, mDepth, value); }
|
||||
void SetValueAtNode(size_t coordinate, unsigned8 level, T value)
|
||||
{
|
||||
assert(level <= mDepth);
|
||||
auto node = mRoot->AddNode(coordinate, level);
|
||||
node->SetValue(value);
|
||||
}
|
||||
|
||||
BinaryTreeNode<T>* Create()
|
||||
{
|
||||
BinaryTreeNode<T>* newNode = new BinaryTreeNode<T>(this);
|
||||
mNodePool.push_back(newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
void SetDepth(unsigned8 wantedDepth, bool shiftExisting)
|
||||
{
|
||||
if (mDepth != wantedDepth)
|
||||
{
|
||||
if (wantedDepth < mDepth)
|
||||
ShaveUntil(wantedDepth);
|
||||
else
|
||||
ShiftDown(wantedDepth - mDepth);
|
||||
}
|
||||
mDepth = wantedDepth;
|
||||
}
|
||||
|
||||
// Deletes all nodes of which the level is greater than the given level
|
||||
void ShaveUntil(unsigned8 level)
|
||||
{
|
||||
// Calculate the level for all nodes
|
||||
std::vector<unsigned8> nodeLevels = CalculateNodeLevels();
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
if (nodeLevels[i] > level)
|
||||
{
|
||||
delete mNodePool[i];
|
||||
mNodePool[i] = NULL;
|
||||
}
|
||||
else if (nodeLevels[i] == level)
|
||||
{
|
||||
// Set all pointers to NULL:
|
||||
for (unsigned8 child = 0; child < 2; child++) mNodePool[i]->SetChild(NULL, child);
|
||||
}
|
||||
mNodePool.erase(std::remove_if(mNodePool.begin(), mNodePool.end(), [](const BinaryTreeNode<T>* node) { return node == NULL; }));
|
||||
}
|
||||
|
||||
void ReplaceValues(const std::unordered_map<T, T>& replacers)
|
||||
{
|
||||
tbb::parallel_for(size_t(0), mNodePool.size(), [&](const size_t& i)
|
||||
{
|
||||
T curValue = mNodePool[i]->GetValue();
|
||||
auto replacer = replacers.find(curValue);
|
||||
if (replacer != replacers.end())
|
||||
mNodePool[i]->SetValue(replacer->second);
|
||||
});
|
||||
}
|
||||
|
||||
void Serialize(std::ostream& file) const
|
||||
{
|
||||
Serializer<unsigned8>::Serialize(mDepth, file);
|
||||
|
||||
// Write the number of nodes in the binary tree:
|
||||
Serializer<unsigned64>::Serialize((unsigned64)mNodePool.size(), file);
|
||||
|
||||
// Write the node materials
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
Serializer<T>::Serialize(mNodePool[i]->GetValue(), file);
|
||||
|
||||
// Write (consequtively) the child pointers
|
||||
std::unordered_map<BinaryTreeNode<T>*, size_t> nodeIndices = CollectionHelper::GetIndexMap(mNodePool);
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
// Use 0 as NULL pointer, add 1 to all actual pointers
|
||||
BinaryTreeNode<T>* childNode = mNodePool[i]->GetChild(child);
|
||||
unsigned64 pointer = 0;
|
||||
if (childNode != NULL)
|
||||
{
|
||||
assert(nodeIndices.find(childNode) != nodeIndices.end());
|
||||
pointer = nodeIndices[childNode] + 1;
|
||||
}
|
||||
Serializer<unsigned64>::Serialize(pointer, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Deserialize(std::istream& file)
|
||||
{
|
||||
if (mNodePool.size() > 1)
|
||||
{
|
||||
for (auto node = mNodePool.begin(); node != mNodePool.end(); node++)
|
||||
delete *node;
|
||||
mNodePool.resize(1);
|
||||
}
|
||||
|
||||
Serializer<unsigned8>::Deserialize(mDepth, file);
|
||||
|
||||
unsigned64 nodeCount;
|
||||
Serializer<unsigned64>::Deserialize(nodeCount, file);
|
||||
// The root is always already created, so create nodeCount - 1 nodes:
|
||||
for (size_t i = 0; i < nodeCount - 1; i++) Create();
|
||||
|
||||
// Deserialize the materials for each node
|
||||
T dummy;
|
||||
for (size_t i = 0; i < nodeCount; i++)
|
||||
{
|
||||
Serializer<T>::Deserialize(dummy, file);
|
||||
mNodePool[i]->SetValue(dummy);
|
||||
}
|
||||
|
||||
// Create all pointers
|
||||
for (size_t i = 0; i < mNodePool.size(); i++)
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
unsigned64 pointer;
|
||||
Serializer<unsigned64>::Deserialize(pointer, file);
|
||||
if (pointer != 0)
|
||||
mNodePool[i]->SetChild(mNodePool[pointer - 1], child);
|
||||
}
|
||||
}
|
||||
|
||||
// Converts the current binary tree to a DAG, meaning that all duplicate nodes are removed.
|
||||
void ToDAG()
|
||||
{
|
||||
// Fill the current layer with all leaf nodes
|
||||
std::vector<BinaryTreeNode<T>*> dagNodePool(1, mRoot);
|
||||
std::vector<BinaryTreeNode<T>*> currentLayer;
|
||||
std::unordered_set<BinaryTreeNode<T>*> nodesLeft;
|
||||
std::unordered_map<BinaryTreeNode<T>*, std::vector<BinaryTreeNode<T>*>> parentsMap;
|
||||
for (auto node : mNodePool)
|
||||
{
|
||||
if (node->IsLeaf()) currentLayer.push_back(node);
|
||||
else
|
||||
{
|
||||
nodesLeft.insert(node);
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
auto childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
{
|
||||
auto parent = parentsMap.find(childNode);
|
||||
if (parent == parentsMap.end())
|
||||
parentsMap.insert(std::make_pair(childNode, std::vector<BinaryTreeNode<T>*>(1, node)));
|
||||
else
|
||||
parent->second.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!currentLayer.empty() && currentLayer[0] != mRoot)
|
||||
{
|
||||
// Find unique nodes and replace them
|
||||
tbb::parallel_sort(currentLayer.begin(), currentLayer.end(), [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b) { return BinaryTreeNode<T>::Compare(a, b); });
|
||||
BinaryTreeNode<T>* cur = NULL;
|
||||
std::vector<std::pair<BinaryTreeNode<T>*, BinaryTreeNode<T>*>> replacements;
|
||||
std::vector<BinaryTreeNode<T>*> nextLayer;
|
||||
size_t uniqueNodes = 0;
|
||||
for (auto node : currentLayer)
|
||||
{
|
||||
if (BinaryTreeNode<T>::Equals(node, cur))
|
||||
// Make sure that all nodes are replaced by their equals
|
||||
replacements.push_back(std::make_pair(node, cur));
|
||||
else
|
||||
{
|
||||
uniqueNodes++;
|
||||
cur = node;
|
||||
dagNodePool.push_back(cur);
|
||||
}
|
||||
auto parents = parentsMap.find(node);
|
||||
if (parents != parentsMap.end())
|
||||
nextLayer.insert(nextLayer.end(), parents->second.begin(), parents->second.end());
|
||||
}
|
||||
CollectionHelper::Unique(nextLayer, [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b) { return BinaryTreeNode<T>::Compare(a, b); });
|
||||
if (uniqueNodes != currentLayer.size())
|
||||
{
|
||||
for (auto replacement : replacements)
|
||||
{
|
||||
auto toReplace = replacement.first;
|
||||
auto replacer = replacement.second;
|
||||
auto parentsIt = parentsMap.find(toReplace);
|
||||
if (parentsIt == parentsMap.end())
|
||||
continue;
|
||||
std::vector<BinaryTreeNode<T>*> parents = parentsIt->second;
|
||||
for (auto parent : parents)
|
||||
{
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
if (parent->GetChild(child) == toReplace)
|
||||
parent->SetChild(replacer, child);
|
||||
}
|
||||
}
|
||||
delete toReplace;
|
||||
}
|
||||
}
|
||||
currentLayer = nextLayer;
|
||||
}
|
||||
mNodePool = dagNodePool;
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Serialize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
// The first byte contains the number of bytes per pointer
|
||||
unsigned8 pointerSize = GetSerializedPointerByteSize(onlyLeafsContainValues);
|
||||
unsigned8 valueSize = GetMaterialByteSize();
|
||||
|
||||
std::vector<unsigned8> res(GetSerializedByteCount(onlyLeafsContainValues), 0);
|
||||
res[0] = mDepth;
|
||||
res[1] = pointerSize;
|
||||
|
||||
std::vector<BinaryTreeNode<T>*> nodePoolCopy(mNodePool.size());
|
||||
std::copy(mNodePool.begin(), mNodePool.end(), nodePoolCopy.begin());
|
||||
size_t firstLeafIndex = ~size_t(0);
|
||||
if (onlyLeafsContainValues)
|
||||
{
|
||||
tbb::parallel_sort(nodePoolCopy.begin() + 1, nodePoolCopy.end(), [](BinaryTreeNode<T>* a, BinaryTreeNode<T>* b)
|
||||
{
|
||||
return !(a->IsLeaf()) && (b->IsLeaf());
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < nodePoolCopy.size(); i++)
|
||||
{
|
||||
BinaryTreeNode<T>* node = nodePoolCopy[i];
|
||||
if (node->IsLeaf() && firstLeafIndex > i)
|
||||
firstLeafIndex = i;
|
||||
}
|
||||
}
|
||||
std::unordered_map<BinaryTreeNode<T>*, size_t> nodeIndices = CollectionHelper::GetIndexMap(nodePoolCopy);
|
||||
|
||||
for (size_t i = 0; i < nodePoolCopy.size(); i++)
|
||||
{
|
||||
BinaryTreeNode<T>* node = nodePoolCopy[i];
|
||||
size_t nodePointer = GetNodePointer(i, pointerSize, valueSize, firstLeafIndex, onlyLeafsContainValues);
|
||||
// Write the node pointers
|
||||
for (unsigned8 child = 0; child < 2; child++)
|
||||
{
|
||||
BinaryTreeNode<T>* childNode = node->GetChild(child);
|
||||
if (childNode != NULL)
|
||||
{
|
||||
assert(nodeIndices.find(childNode) != nodeIndices.end());
|
||||
size_t childIndex = nodeIndices[childNode];
|
||||
size_t pointer = GetNodePointer(childIndex, pointerSize, valueSize, firstLeafIndex, onlyLeafsContainValues);
|
||||
BitHelper::SplitInBytesAndMove(pointer, res, nodePointer + pointerSize * child, pointerSize);
|
||||
}
|
||||
}
|
||||
// Then write the node content/value
|
||||
if (!onlyLeafsContainValues || node->IsLeaf())
|
||||
{
|
||||
std::vector<unsigned8> serializedValue = node->GetValue().Serialize();
|
||||
std::move(serializedValue.begin(), serializedValue.end(), res.begin() + nodePointer + pointerSize * 2);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t GetNodeCount() const { return mNodePool.size(); }
|
||||
size_t GetLeafNodeCount() const {
|
||||
size_t leafNodeCount = 0;
|
||||
for (auto node : mNodePool) if (node->IsLeaf()) leafNodeCount++;
|
||||
return leafNodeCount;
|
||||
}
|
||||
|
||||
static size_t GetSerializedByteCount(const size_t& nodeCount, const size_t& leafCount, const size_t& pointerSize, const size_t& valueSize, const bool& onlyLeafsContainValues)
|
||||
{
|
||||
return (onlyLeafsContainValues ? leafCount : nodeCount) * valueSize + (2 * pointerSize) * nodeCount;
|
||||
}
|
||||
|
||||
size_t GetSerializedByteCount(bool onlyLeafsContainValues) const
|
||||
{
|
||||
return GetSerializedByteCount(GetNodeCount(), GetLeafNodeCount(), GetSerializedPointerByteSize(onlyLeafsContainValues), GetMaterialByteSize(), onlyLeafsContainValues);
|
||||
}
|
||||
|
||||
unsigned8 GetSerializedNodeByteSize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
return 2 * GetSerializedPointerByteSize(onlyLeafsContainValues) + GetMaterialByteSize();
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialByteSize() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
unsigned8 GetSerializedPointerByteSize(bool onlyLeafsContainValues) const
|
||||
{
|
||||
// Count the number of leaf nodes:
|
||||
size_t leafNodeCount = 0;
|
||||
if (onlyLeafsContainValues)
|
||||
leafNodeCount = GetLeafNodeCount();
|
||||
bool fits = false;
|
||||
unsigned8 pointerSize = 0;
|
||||
while (!fits)
|
||||
{
|
||||
++pointerSize;
|
||||
size_t requiredBytes = GetSerializedByteCount(GetNodeCount(), leafNodeCount, pointerSize, GetMaterialByteSize(), onlyLeafsContainValues);
|
||||
fits = BitHelper::Exp2(8 * pointerSize) > requiredBytes;
|
||||
}
|
||||
return pointerSize;
|
||||
}
|
||||
};
|
||||
223
Research/core/Util/BlockVector.h
Normal file
223
Research/core/Util/BlockVector.h
Normal file
@@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include "IndexIterator.h"
|
||||
//#include "../BitHelper.h"
|
||||
|
||||
template <typename T, unsigned8 BLOCK_POINTER_BITS = 14>
|
||||
class BlockVector
|
||||
{
|
||||
public:
|
||||
const static unsigned64 BLOCK_SIZE = 1 << BLOCK_POINTER_BITS;
|
||||
const static size_t BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
|
||||
protected:
|
||||
typedef std::vector<T> block;
|
||||
std::vector<block*> mData;
|
||||
size_t mSize;
|
||||
|
||||
inline size_t block_count(size_t n) const { return (n >> BLOCK_POINTER_BITS) + ((n % BLOCK_SIZE == 0) ? 0 : 1); }
|
||||
inline size_t block_index(size_t i) const { return i >> BLOCK_POINTER_BITS; }
|
||||
inline size_t item_index(size_t i) const { return i & BLOCK_SIZE_MASK; }
|
||||
|
||||
void shrink_blocks(size_t newBlockCount)
|
||||
{
|
||||
// If the size is smaller, delete blocks that are no longer needed.
|
||||
for (size_t i = newBlockCount; i < mData.size(); i++)
|
||||
delete mData[i];
|
||||
}
|
||||
void init_blocks(size_t oldSize = 0)
|
||||
{
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
mData[i] = new block(BLOCK_SIZE);
|
||||
}
|
||||
void init_blocks(const T& value, size_t oldSize)
|
||||
{
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
mData[i] = new block(BLOCK_SIZE, value);
|
||||
}
|
||||
public:
|
||||
typedef IndexIterator<const BlockVector<T>, const T> const_iterator;
|
||||
typedef IndexIterator<BlockVector<T>, T> iterator;
|
||||
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||
|
||||
BlockVector() : mData(std::vector<std::array<T, BLOCK_SIZE>*>()), mSize(0) {}
|
||||
BlockVector(size_t capacity) :
|
||||
mData(std::vector<block*>()),
|
||||
mSize(0)
|
||||
{
|
||||
reserve(capacity);
|
||||
}
|
||||
BlockVector(const BlockVector& r)
|
||||
{
|
||||
mData = std::vector<block*>(r.mData.size());
|
||||
init(0);
|
||||
for (size_t block = 0; block < r.mData.size(); block++)
|
||||
std::copy(r.mData[block]->begin(), r.mData[block]->end(), mData[block]->begin());
|
||||
mSize = r.mSize;
|
||||
}
|
||||
BlockVector(BlockVector&& r)
|
||||
{
|
||||
mData = std::move(r.mData); r.mData.clear();
|
||||
mSize = std::move(r.mSize); r.mSize = 0;
|
||||
}
|
||||
|
||||
virtual ~BlockVector()
|
||||
{
|
||||
for (block* arr : mData)
|
||||
{
|
||||
arr->clear();
|
||||
delete arr;
|
||||
}
|
||||
mData.clear();
|
||||
}
|
||||
|
||||
// Capacity
|
||||
size_t size() const { return mSize; }
|
||||
size_t max_size() const { return mData.max_size() * mSize; }
|
||||
|
||||
void reserve(size_t n)
|
||||
{
|
||||
size_t newBlockCount = block_count(n);
|
||||
if (mData.size() >= newBlockCount) return; // big enough
|
||||
size_t oldSize = mData.size();
|
||||
mData.resize(newBlockCount);
|
||||
for (size_t i = oldSize; i < mData.size(); i++)
|
||||
{
|
||||
mData[i] = new block();
|
||||
mData[i]->reserve(BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// Shrinks the vector so it contains n entries.
|
||||
void shrink(size_t n)
|
||||
{
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
|
||||
void resize(size_t n)
|
||||
{
|
||||
size_t oldBlockCount = mData.size();
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount, NULL);
|
||||
init_blocks(oldBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
void resize(size_t n, const T& val)
|
||||
{
|
||||
size_t oldBlockCount = mData.size();
|
||||
size_t newBlockCount = block_count(n);
|
||||
shrink_blocks(newBlockCount);
|
||||
mData.resize(newBlockCount, NULL);
|
||||
init_blocks(val, oldBlockCount);
|
||||
mSize = n;
|
||||
}
|
||||
|
||||
inline size_t capacity() const { return mData.size() * BLOCK_SIZE; }
|
||||
inline bool empty() const { return mData.empty(); }
|
||||
void shrink_to_fit() { resize(mSize); }
|
||||
|
||||
// Element access
|
||||
const T& at(size_t i) const { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
|
||||
T& at(size_t i) { assert(i < mSize); return mData[block_index(i)]->at(item_index(i)); }
|
||||
const T& operator[](size_t i) const { return at(i); }
|
||||
T& operator[](size_t i) { return at(i); }
|
||||
const T& front() const { return at(0); }
|
||||
T& front() { return at(0); }
|
||||
const T& back() const { return at(mSize - 1); }
|
||||
T& back() { return at(mSize - 1); }
|
||||
|
||||
// Modifiers
|
||||
void push_back(const T& val)
|
||||
{
|
||||
// Increase the size if it doesn't fit
|
||||
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
|
||||
// Put the item in the last position
|
||||
block* b = mData[block_index(mSize)];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->push_back(val);
|
||||
else mData->at(itemIndex) = val;
|
||||
mSize++;
|
||||
}
|
||||
void push_back(T&& val)
|
||||
{
|
||||
// Increase the size if it doesn't fit
|
||||
if (block_index(mSize) >= mData.size()) reserve(mSize + 1);
|
||||
// Put the item in the last position
|
||||
block* b = mData[block_index(mSize)];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->push_back(val);
|
||||
else b->at(itemIndex) = val;
|
||||
mSize++;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace_back(Args&&... args)
|
||||
{
|
||||
size_t blockIndex = block_index(mSize);
|
||||
if (blockIndex >= mData.size()) reserve(mSize + 1);
|
||||
block* b = mData[blockIndex];
|
||||
size_t itemIndex = item_index(mSize);
|
||||
if (b->size() == itemIndex) b->emplace_back(std::forward<Args>(args)...);
|
||||
else b->at(itemIndex) = T(std::forward<Args>(args)...);
|
||||
mSize++;
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
mSize--;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (block* block : mData)
|
||||
delete block;
|
||||
mData.clear();
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Iterators
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(this, 0);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_iterator(this, mSize);
|
||||
}
|
||||
|
||||
const_reverse_iterator rbegin() const
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
const_reverse_iterator rend() const
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator(this, mSize);
|
||||
}
|
||||
|
||||
reverse_iterator rbegin()
|
||||
{
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
reverse_iterator rend()
|
||||
{
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
};
|
||||
101
Research/core/Util/BoolArray.cpp
Normal file
101
Research/core/Util/BoolArray.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "BoolArray.h"
|
||||
#include "../BitHelper.h"
|
||||
#include <assert.h>
|
||||
|
||||
BoolArray::BoolArray(size_t size, bool value) :
|
||||
mData(std::vector<unsigned8>(BitHelper::RoundToBytes(size) / 8, value ? 255 : 0)),
|
||||
mSize(size)
|
||||
{}
|
||||
|
||||
BoolArray::~BoolArray() {}
|
||||
|
||||
void BoolArray::Set(const size_t& i, bool value)
|
||||
{
|
||||
if (i > mSize) Resize(i + 1);
|
||||
size_t byteIndex = GetByteIndex(i);
|
||||
unsigned8 bitIndex = GetBitIndex(i);
|
||||
unsigned8 bitMask = BitHelper::GetHSSingleBitMask<unsigned8>(bitIndex);
|
||||
mData[byteIndex] &= ~bitMask;
|
||||
if (value) mData[byteIndex] |= bitMask;
|
||||
}
|
||||
|
||||
void BoolArray::SetRange(const size_t& start, const size_t& end, bool value)
|
||||
{
|
||||
if (end < start) return;
|
||||
if (end > mSize) Resize(end);
|
||||
|
||||
size_t startByteIndex = GetByteIndex(start);
|
||||
size_t endByteIndex = GetByteIndex(end);
|
||||
|
||||
unsigned8 bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), 8);
|
||||
if (startByteIndex == endByteIndex)
|
||||
bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), GetBitIndex(end));
|
||||
// Set the bits in the first block:
|
||||
mData[startByteIndex] &= ~bitMask;
|
||||
if (value) mData[startByteIndex] |= bitMask;
|
||||
if (startByteIndex == endByteIndex) return; // We're done if only one byte is affected
|
||||
|
||||
// Set the bits in all bytes between the start and the end bytes
|
||||
unsigned8 allSetValue = value ? 0xFF : 0x00;
|
||||
for (size_t byte = startByteIndex + 1; byte < endByteIndex; byte++)
|
||||
mData[byte] = allSetValue;
|
||||
|
||||
// Set the required bits in the end byte
|
||||
if (end % 8 != 0)
|
||||
{
|
||||
bitMask = BitHelper::GetHSMask<unsigned8>(0, GetBitIndex(end));
|
||||
mData[endByteIndex] &= ~bitMask;
|
||||
if (value) mData[endByteIndex] |= bitMask;
|
||||
}
|
||||
}
|
||||
|
||||
bool BoolArray::Any(const size_t& start, size_t end) const
|
||||
{
|
||||
if (end >= mSize) end = mSize - 1;
|
||||
if (end <= start) return false;
|
||||
|
||||
size_t startByteIndex = GetByteIndex(start);
|
||||
size_t endByteIndex = GetByteIndex(end);
|
||||
|
||||
if (startByteIndex == endByteIndex)
|
||||
return (BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), GetBitIndex(end)) & mData[startByteIndex]) != 0;
|
||||
|
||||
// Check the first block
|
||||
unsigned8 bitMask = BitHelper::GetHSMask<unsigned8>(GetBitIndex(start), 8);
|
||||
if ((mData[startByteIndex] & bitMask) != 0) return true;
|
||||
|
||||
// Check the bytes inbetween
|
||||
for (size_t byte = startByteIndex + 1; byte < endByteIndex; byte++)
|
||||
if ((mData[byte] & 0xFF) != 0) return true;
|
||||
|
||||
// Check the last bits
|
||||
return (mData[endByteIndex] & BitHelper::GetHSMask<unsigned8>(0, GetBitIndex(end))) != 0;
|
||||
}
|
||||
|
||||
bool BoolArray::Get(const size_t& i) const
|
||||
{
|
||||
assert(i < mSize);
|
||||
return BitHelper::GetHS(mData[GetByteIndex(i)], GetBitIndex(i));
|
||||
}
|
||||
void BoolArray::Resize(const size_t& size)
|
||||
{
|
||||
mSize = size;
|
||||
mData.resize(BitHelper::RoundToBytes(size) / 8);
|
||||
}
|
||||
|
||||
void BoolArray::Clear()
|
||||
{
|
||||
mSize = 0;
|
||||
mData.clear();
|
||||
mData.shrink_to_fit();
|
||||
}
|
||||
|
||||
void BoolArray::ShrinkToFit()
|
||||
{
|
||||
mData.shrink_to_fit();
|
||||
}
|
||||
|
||||
bool BoolArray::operator[](const size_t& i) const
|
||||
{
|
||||
return Get(i);
|
||||
}
|
||||
31
Research/core/Util/BoolArray.h
Normal file
31
Research/core/Util/BoolArray.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
|
||||
class BoolArray
|
||||
{
|
||||
public:
|
||||
BoolArray(size_t size, bool value = false);
|
||||
BoolArray() : BoolArray(0) {}
|
||||
~BoolArray();
|
||||
|
||||
void Set(const size_t& i, bool value);
|
||||
bool Get(const size_t& i) const;
|
||||
void SetRange(const size_t& start, const size_t& end, bool value);
|
||||
bool Any(const size_t& start = 0, size_t end = ~size_t(0)) const;
|
||||
size_t Size() const { return mSize; }
|
||||
bool Empty() const { return mSize == 0; }
|
||||
void Resize(const size_t& size);
|
||||
void Clear();
|
||||
void ShrinkToFit();
|
||||
|
||||
bool operator[](const size_t& i) const;
|
||||
private:
|
||||
static inline size_t GetByteIndex(const size_t& i) { return i >> 3; }
|
||||
static inline unsigned8 GetBitIndex(const size_t& i) { return i & 0x7; }
|
||||
|
||||
size_t mSize;
|
||||
std::vector<unsigned8> mData;
|
||||
};
|
||||
|
||||
187
Research/core/Util/IndexIterator.h
Normal file
187
Research/core/Util/IndexIterator.h
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
#include <iterator>
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
class IndexIterator : public std::iterator<std::random_access_iterator_tag, T>
|
||||
{
|
||||
protected:
|
||||
size_t mIndex;
|
||||
ContainerT* mContainer;
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::value_type
|
||||
value_type;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::difference_type
|
||||
difference_type;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::reference
|
||||
reference;
|
||||
typedef
|
||||
typename iterator<std::random_access_iterator_tag, T>::pointer
|
||||
pointer;
|
||||
|
||||
|
||||
IndexIterator() : mContainer(NULL), mIndex(0) {}
|
||||
IndexIterator(ContainerT* container) : mContainer(container), mIndex(0) {}
|
||||
IndexIterator(ContainerT* container, size_t index) : mContainer(container), mIndex(index) {}
|
||||
IndexIterator(const IndexIterator<ContainerT, T>& r) : mContainer(r.mContainer), mIndex(r.mIndex) {}
|
||||
|
||||
IndexIterator& operator=(const IndexIterator& r)
|
||||
{
|
||||
mIndex = r.mIndex; mContainer = r.mContainer; return *this;
|
||||
}
|
||||
|
||||
IndexIterator& operator++() // PREFIX
|
||||
{
|
||||
++mIndex; return *this;
|
||||
}
|
||||
|
||||
IndexIterator& operator--() // PREFIX
|
||||
{
|
||||
--mIndex; return *this;
|
||||
}
|
||||
|
||||
IndexIterator operator++(int) // POSTFIX
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex++);
|
||||
}
|
||||
|
||||
IndexIterator operator--(int) // POSTFIX
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex--);
|
||||
}
|
||||
|
||||
IndexIterator operator+(const difference_type& n) const
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex + n);
|
||||
}
|
||||
|
||||
IndexIterator& operator+=(const difference_type& n)
|
||||
{
|
||||
mIndex += n; return *this;
|
||||
}
|
||||
|
||||
IndexIterator operator-(const difference_type& n) const
|
||||
{
|
||||
return IndexIterator(mContainer, mIndex - n);
|
||||
}
|
||||
|
||||
IndexIterator& operator-=(const difference_type& n)
|
||||
{
|
||||
mIndex -= n; return *this;
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return mContainer->operator[](mIndex);
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return &mContainer->operator[](mIndex);
|
||||
}
|
||||
|
||||
reference operator[](const difference_type& n) const
|
||||
{
|
||||
return mContainer->operator[](mIndex + n);
|
||||
}
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator==(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator!=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator<(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator>(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator<=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend bool operator>=(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend typename IndexIterator<ContainerTypeT, TypeT>::difference_type operator+(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
|
||||
template<typename ContainerTypeT, typename TypeT>
|
||||
friend typename IndexIterator<ContainerTypeT, TypeT>::difference_type operator-(
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r1,
|
||||
const IndexIterator<ContainerTypeT, TypeT>& r2);
|
||||
};
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator==(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
return (r1.mIndex == r2.mIndex && r1.mContainer == r2.mContainer);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator!=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
return ((r1.mIndex != r2.mIndex) || (r1.mContainer != r2.mContainer));
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator<(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer < r2.mContainer;
|
||||
return (r1.mIndex < r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator>(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer > r2.mContainer;
|
||||
return (r1.mIndex > r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator<=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer < r2.mContainer;
|
||||
return (r1.mIndex <= r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
bool operator>=(const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
if (r1.mContainer != r2.mContainer) return r1.mContainer > r2.mContainer;
|
||||
return (r1.mIndex >= r2.mIndex);
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
typename IndexIterator<ContainerT, T>::difference_type operator+(
|
||||
const IndexIterator<ContainerT, T>& r1,
|
||||
const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
assert(r1.mContainer == r2.mContainer);
|
||||
return r1.mIndex + r2.mIndex;
|
||||
}
|
||||
|
||||
template<typename ContainerT, typename T>
|
||||
typename IndexIterator<ContainerT, T>::difference_type operator-(
|
||||
const IndexIterator<ContainerT, T>& r1, const IndexIterator<ContainerT, T>& r2)
|
||||
{
|
||||
assert(r1.mContainer == r2.mContainer);
|
||||
return r1.mIndex - r2.mIndex;
|
||||
}
|
||||
131
Research/core/Util/ObjectPool.h
Normal file
131
Research/core/Util/ObjectPool.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include "BoolArray.h"
|
||||
#include "BlockVector.h"
|
||||
#include "../CollectionHelper.h"
|
||||
|
||||
#define USE_BLOCK_VECTOR
|
||||
|
||||
// Class for memory efficiency: stores all objects of some type in a vector. Pointers to the object can then be requested.
|
||||
template<typename T>
|
||||
class ObjectPool
|
||||
{
|
||||
private:
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
BlockVector<T> mData;
|
||||
#else
|
||||
std::vector<T> mData;
|
||||
#endif
|
||||
BoolArray mMarkedForDeletion;
|
||||
const size_t mBaseCapacity = 0;
|
||||
|
||||
bool AnyMarkedForDeletion() const { return !mMarkedForDeletion.Empty(); }
|
||||
bool MarkedForDeletion(size_t index) const { return index < mMarkedForDeletion.Size() && mMarkedForDeletion[index]; }
|
||||
void MarkForDeletion(size_t index)
|
||||
{
|
||||
if (index >= mMarkedForDeletion.Size())
|
||||
mMarkedForDeletion.Resize(index + 1);
|
||||
mMarkedForDeletion.Set(index, true);
|
||||
}
|
||||
public:
|
||||
ObjectPool(size_t initialCapacity = 1) :
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
mData(BlockVector<T>(initialCapacity)),
|
||||
#else
|
||||
mData(std::vector<T>()),
|
||||
#endif
|
||||
mMarkedForDeletion(BoolArray()),
|
||||
mBaseCapacity(initialCapacity)
|
||||
{
|
||||
#ifndef USE_BLOCK_VECTOR
|
||||
mData.reserve(initialCapacity);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Adds a new item to the pool.
|
||||
template <class... Args>
|
||||
T* Create(Args&&... args)
|
||||
{
|
||||
//// If any nodes are marked for deletion, reuse their position
|
||||
//if (!mMarkedForDeletion.empty())
|
||||
//{
|
||||
// T* res = &mData[mMarkedForDeletion.top()];
|
||||
// mMarkedForDeletion.pop();
|
||||
// return res;
|
||||
//}
|
||||
//else // Else, resize the data array to create some space for this new node
|
||||
//{
|
||||
mData.emplace_back(std::forward<Args>(args)...);
|
||||
return &mData[mData.size() - 1];
|
||||
//}
|
||||
}
|
||||
|
||||
// Moves the node from the given memory position into this pool. Returns the new pointer to this node
|
||||
T* Add(T* value)
|
||||
{
|
||||
mData.emplace_back(std::move(*value));
|
||||
return &mData[mData.size() - 1];
|
||||
}
|
||||
|
||||
void Delete(size_t i) { MarkForDeletion(i); }
|
||||
|
||||
// Removes all nodes that are marked for deletion.
|
||||
void Clean()
|
||||
{
|
||||
// There is only stuff to clean if there are nodes marked for deletion
|
||||
if (!AnyMarkedForDeletion()) return;
|
||||
|
||||
size_t newIdx = 0;
|
||||
for (size_t oldIdx = 0; oldIdx < mData.size(); oldIdx++)
|
||||
// If this node should not be deleted, move it to the new position.
|
||||
if (!MarkedForDeletion(oldIdx))
|
||||
{
|
||||
if (oldIdx != newIdx)
|
||||
mData[newIdx] = std::move(mData[oldIdx]);
|
||||
//std::swap(mData[newIdx], mData[oldIdx]);
|
||||
newIdx++;
|
||||
}
|
||||
#ifdef USE_BLOCK_VECTOR
|
||||
mData.shrink(newIdx);
|
||||
mData.reserve(mBaseCapacity);
|
||||
#else
|
||||
mData.erase(mData.begin() + newIdx, mData.end());
|
||||
mData.shrink_to_fit();
|
||||
mData.reserve(mBaseCapacity);
|
||||
#endif
|
||||
mMarkedForDeletion = BoolArray();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mData.clear();
|
||||
mMarkedForDeletion.Clear();
|
||||
}
|
||||
|
||||
template<typename Comparer = std::less<T>>
|
||||
void Sort(size_t startIdx, size_t endIdx, const Comparer& comparer = Comparer())
|
||||
{
|
||||
// Check if any node are deleted in the range that needs to be sorted:
|
||||
if (AnyMarkedForDeletion() && mMarkedForDeletion.Any(startIdx, endIdx))
|
||||
Clean();
|
||||
tbb::parallel_sort(mData.begin() + startIdx, mData.begin() + endIdx, comparer);
|
||||
}
|
||||
|
||||
inline size_t Size() const { return mData.size(); }
|
||||
|
||||
inline const T* operator[](size_t i) const
|
||||
{
|
||||
if (MarkedForDeletion(i)) return NULL;
|
||||
return &mData.at(i);
|
||||
}
|
||||
|
||||
inline T* operator[](size_t i)
|
||||
{
|
||||
if (MarkedForDeletion(i)) return NULL;
|
||||
return &mData.at(i);
|
||||
}
|
||||
};
|
||||
119
Research/core/Util/Path.h
Normal file
119
Research/core/Util/Path.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "../Defines.h"
|
||||
#include "../Serializer.h"
|
||||
|
||||
// Path stores a recording detailing any type.
|
||||
// Interpolator should be a functor with a method specification:
|
||||
// T operator()(const T& a, const T& b, double time)
|
||||
// Where a and b should be interpolated between, and time is a value between 0 and 1, indicating the interpolation value
|
||||
template<typename T, typename Interpolator>
|
||||
class Path
|
||||
{
|
||||
private:
|
||||
std::vector<T> mFrames;
|
||||
std::vector<double> mFrameTimes;
|
||||
|
||||
public:
|
||||
Path() : mFrames(std::vector<T>()), mFrameTimes(std::vector<double>()) {}
|
||||
~Path() {}
|
||||
|
||||
void SetStateAtTime(const double& time, const T& state)
|
||||
{
|
||||
// Find the position of this new state:
|
||||
auto pos = std::lower_bound(mFrameTimes.begin(), mFrameTimes.end(), time);
|
||||
size_t idx = pos - mFrameTimes.begin();
|
||||
mFrames.insert(mFrames.begin() + idx, state);
|
||||
mFrameTimes.insert(pos, time);
|
||||
}
|
||||
|
||||
T GetStateAtTime(const double& time) const
|
||||
{
|
||||
// Find the frames between which the requested time takes place
|
||||
size_t afterIdx = std::upper_bound(mFrameTimes.begin(), mFrameTimes.end(), time) - mFrameTimes.begin();
|
||||
size_t beforeIdx = afterIdx - 1;
|
||||
if (afterIdx == mFrameTimes.size()) // End of the recording, return the last camera state
|
||||
return mFrames.back();
|
||||
if (afterIdx == 0)
|
||||
return mFrames.front();
|
||||
|
||||
// Now linearly interpolate the frames:
|
||||
double t = ((time - mFrameTimes[beforeIdx]) / (mFrameTimes[afterIdx] - mFrameTimes[beforeIdx]));
|
||||
|
||||
const T& before = mFrames[beforeIdx];
|
||||
const T& after = mFrames[afterIdx];
|
||||
|
||||
return Interpolator()(before, after, t);
|
||||
}
|
||||
|
||||
double GetLength() const {
|
||||
if (Empty()) return 0;
|
||||
return mFrameTimes.back() - mFrameTimes.front();
|
||||
}
|
||||
|
||||
size_t Size() const { return mFrames.size(); }
|
||||
|
||||
/// Removes the camera path node at index i
|
||||
void RemoveAt(size_t i)
|
||||
{
|
||||
mFrames.erase(mFrames.begin() + i);
|
||||
mFrameTimes.erase(mFrames.begin() + i);
|
||||
}
|
||||
|
||||
/// Removes the last stored camera path node
|
||||
void RemoveLast()
|
||||
{
|
||||
mFrames.pop_back();
|
||||
mFrameTimes.pop_back();
|
||||
}
|
||||
|
||||
// Makes sure the frametimes go from 0 to the length of the recording
|
||||
void Normalize()
|
||||
{
|
||||
if (Empty()) return;
|
||||
double offset = mFrameTimes[0];
|
||||
for (size_t i = 0; i < mFrameTimes.size(); i++)
|
||||
mFrameTimes[i] = mFrameTimes[i] - offset;
|
||||
}
|
||||
bool Empty() const { return mFrames.empty(); }
|
||||
void Clear()
|
||||
{
|
||||
mFrames.clear();
|
||||
mFrameTimes.clear();
|
||||
}
|
||||
|
||||
void WriteToFile(const std::string& filename)
|
||||
{
|
||||
std::ofstream file(filename, std::ios::binary);
|
||||
unsigned32 recordingLength = (unsigned32)mFrames.size();
|
||||
Serializer<unsigned32>::Serialize(recordingLength, file);
|
||||
if (recordingLength > 0)
|
||||
{
|
||||
Serializer<T*>::Serialize(&mFrames[0], mFrames.size(), file);
|
||||
Serializer<double*>::Serialize(&mFrameTimes[0], mFrames.size(), file);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool ReadFromFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (file.good()) {
|
||||
unsigned32 recordingLength = 0;
|
||||
Serializer<unsigned32>::Deserialize(recordingLength, file);
|
||||
mFrames.resize(recordingLength);
|
||||
mFrameTimes.resize(recordingLength);
|
||||
if (recordingLength > 0)
|
||||
{
|
||||
Serializer<T*>::Deserialize(&mFrames[0], mFrames.size(), file);
|
||||
Serializer<double*>::Deserialize(&mFrameTimes[0], mFrames.size(), file);
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
64
Research/core/Util/SmallDynamicArray.h
Normal file
64
Research/core/Util/SmallDynamicArray.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#include "../Defines.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// Wrapper for standard C array that attempts to minimize memory usage. The user of this class should keep track of the size of the array to correctly be able to insert etc.
|
||||
template<typename T>
|
||||
class SmallDynamicArray
|
||||
{
|
||||
public:
|
||||
SmallDynamicArray() : mData(NULL) {}
|
||||
SmallDynamicArray(size_t size) : mData(new T[size]) {}
|
||||
SmallDynamicArray(SmallDynamicArray&& source) // Move ctor
|
||||
{
|
||||
mData = source.mData;
|
||||
source.mData = NULL;
|
||||
}
|
||||
~SmallDynamicArray() { if (mData != NULL) delete[] mData; }
|
||||
|
||||
SmallDynamicArray& operator=(SmallDynamicArray&& source) // Move assignment operator
|
||||
{
|
||||
mData = source.mData;
|
||||
source.mData = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void Set(const size_t& i, T value) { mData[i] = value; }
|
||||
inline T& Get(const size_t& i) const { return mData[i]; }
|
||||
inline T& operator[](const size_t& i) const { return mData[i]; }
|
||||
inline void SetRange(const T* source, size_t begin, size_t end) { std::copy(source + begin, source + end, mData); }
|
||||
void Clear() { delete[] mData; mData = NULL; }
|
||||
inline bool IsEmpty() const { return mData == NULL; }
|
||||
|
||||
void Resize(size_t oldSize, const size_t& newSize)
|
||||
{
|
||||
T* newData = new T[newSize];
|
||||
if (!IsEmpty())
|
||||
{
|
||||
std::copy(mData, mData + std::min(oldSize, newSize), newData);
|
||||
delete[] mData;
|
||||
}
|
||||
mData = newData;
|
||||
}
|
||||
|
||||
void Insert(size_t index, const T& value, size_t curSize)
|
||||
{
|
||||
unsigned* newData = new unsigned[curSize + 1];
|
||||
// Move the data over to a new array with the correct size
|
||||
if (mData != NULL)
|
||||
{
|
||||
std::copy(mData, mData + index, newData);
|
||||
std::copy(mData + index, mData + curSize, newData + index + 1);
|
||||
delete[] mData;
|
||||
}
|
||||
newData[index] = value;
|
||||
mData = newData;
|
||||
}
|
||||
|
||||
private:
|
||||
T* mData;
|
||||
};
|
||||
|
||||
20
Research/core/Util/Stopwatch.cpp
Normal file
20
Research/core/Util/Stopwatch.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "Stopwatch.h"
|
||||
|
||||
Stopwatch::Stopwatch()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
Stopwatch::~Stopwatch()
|
||||
{
|
||||
}
|
||||
|
||||
void Stopwatch::Reset()
|
||||
{
|
||||
begin = clock();
|
||||
}
|
||||
|
||||
double Stopwatch::GetTime()
|
||||
{
|
||||
return double(clock() - begin) / CLOCKS_PER_SEC;
|
||||
}
|
||||
17
Research/core/Util/Stopwatch.h
Normal file
17
Research/core/Util/Stopwatch.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <ctime>
|
||||
|
||||
class Stopwatch
|
||||
{
|
||||
public:
|
||||
Stopwatch();
|
||||
~Stopwatch();
|
||||
|
||||
void Reset();
|
||||
// Returns the time in seconds
|
||||
double GetTime();
|
||||
|
||||
private:
|
||||
clock_t begin;
|
||||
};
|
||||
|
||||
71
Research/core/Util/TransferFunction.h
Normal file
71
Research/core/Util/TransferFunction.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
#include "../Defines.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "../MathHelper.h"
|
||||
|
||||
template<typename T>
|
||||
class TransferFunction
|
||||
{
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
unsigned32 value;
|
||||
float opacity;
|
||||
T material;
|
||||
|
||||
Node(unsigned32 value, float opacity, T mat) : value(value), opacity(opacity), material(mat) {}
|
||||
Node(unsigned32 value) : value(value), opacity(0), material(T()) {}
|
||||
};
|
||||
|
||||
struct NodeCompare { bool operator()(const Node& a, const Node& b) { return a.value < b.value; } };
|
||||
|
||||
TransferFunction()
|
||||
{
|
||||
}
|
||||
|
||||
~TransferFunction() { mFunction.clear(); }
|
||||
|
||||
void AddNode(unsigned32 value, float opacity, T mat)
|
||||
{
|
||||
Node node(value, opacity, mat);
|
||||
auto pos = LowerBound(node);
|
||||
if (pos != mFunction.end() && pos->value == value)
|
||||
mFunction[pos - mFunction.begin()] = node;
|
||||
else
|
||||
mFunction.insert(pos, node);
|
||||
}
|
||||
|
||||
template<typename Interpolator>
|
||||
void Evaluate(unsigned32 value, float& outOpacity, T& outMat, Interpolator interpolator) const
|
||||
{
|
||||
Node node(value);
|
||||
// Set default values
|
||||
outOpacity = 1.f;
|
||||
outMat = T();
|
||||
// Find the current node
|
||||
auto pos = LowerBound(node);
|
||||
if (pos == mFunction.end()) return;
|
||||
|
||||
Node after = *pos;
|
||||
if (pos == mFunction.begin())
|
||||
{
|
||||
outOpacity = after.opacity;
|
||||
outMat = after.material;
|
||||
return;
|
||||
}
|
||||
pos--;
|
||||
Node before = *pos;
|
||||
float t = float(value - before.value) / float(after.value - before.value);
|
||||
outMat = interpolator(before.material, after.material, t);
|
||||
outOpacity = MathHelper::lerp(t, before.opacity, after.opacity);
|
||||
}
|
||||
private:
|
||||
typename std::vector<Node>::const_iterator LowerBound(const Node& node) const
|
||||
{
|
||||
return std::lower_bound(mFunction.begin(), mFunction.end(), node, NodeCompare());
|
||||
}
|
||||
|
||||
std::vector<Node> mFunction;
|
||||
};
|
||||
|
||||
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