Initial commit: Final state of the master project

This commit is contained in:
2017-09-16 09:41:37 +02:00
commit 696180d43b
832 changed files with 169717 additions and 0 deletions

330
Research/core/BitHelper.h Normal file
View 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
View 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
View 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
};

View 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;
};

View 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;

View 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);
}
};

View 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));
}

View 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
View 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);
}
};

View 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

View 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
View 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
View 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
View 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
View 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
}
};
}

View 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;
}

View 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);
};

View 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
View 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;

View 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);
}
};

View 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));
}

View 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;
}

View 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;
};

View 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:
};

View 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;

View File

@@ -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";
}

View 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]+)";
}

View 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();
};

View 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]*");
}
};

View 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);
}

View File

@@ -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();
}

View File

@@ -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;
};

View 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(); }
};

View 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"; }

View 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();
}

View 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;
};

View 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";
}

View 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();
};

View 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;
}

View 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();
};

View 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));
}
};

View 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;
}

View 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) { }
};
};

View 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;
}

View 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);
};

View 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");
}
};

View 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();
}

View 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;
};

View 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"); }

View 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();
};

View 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);
}

View 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;
};

View 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;
}

View 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);
};

View 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;
};

View File

@@ -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();
}

View File

@@ -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;
};

View 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;
}

View 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
View 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);
}
};

View 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);
}

View 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);
};

View 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;
}
};

View 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());
}
};

View 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);
}

View 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;
};

View 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;
}

View 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
View 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;
}
};

View 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;
};

View 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;
}

View 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;
};

View 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;
};

View 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); }
};

View 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();
}
}

View 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;
};

View 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;
}

View 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;
};

View 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;
}
};