Initial commit: Final state of the master project
This commit is contained in:
40
Research/scene/Material/BaseMaterial.h
Normal file
40
Research/scene/Material/BaseMaterial.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include <string>
|
||||
#include "../../core/Hashers.h"
|
||||
|
||||
class BaseMaterial
|
||||
{
|
||||
public:
|
||||
// Should return a 10-bit per channel precision vec3
|
||||
virtual glm::u16vec3 GetProperties() const = 0;
|
||||
// Should update the materials in such a way that GetProperties should return the given properties
|
||||
virtual void SetProperties(glm::u16vec3 properties) = 0;
|
||||
|
||||
virtual std::string GetTypeSuffix() const = 0;
|
||||
|
||||
inline bool operator==(const BaseMaterial &other) const
|
||||
{
|
||||
auto otherV = other.GetProperties();
|
||||
auto thisV = GetProperties();
|
||||
return otherV == thisV;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<BaseMaterial> {
|
||||
size_t operator()(BaseMaterial const &value) const {
|
||||
glm::u16vec3 properties = value.GetProperties();
|
||||
// Since materials are only allowed to store 10 bits per channel, we should be able to hash them
|
||||
// perfectly in 32 bits.
|
||||
#ifdef ENVIRONMENT64
|
||||
return std::hash<glm::u16vec3>()(properties);
|
||||
#else
|
||||
unsigned short mask = 0x3F;
|
||||
return (((size_t) (properties.x & mask)) << 20) | (((size_t) (properties.y & mask)) << 10) |
|
||||
(size_t) (properties.z & mask);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
170
Research/scene/Material/BitsMaterial.h
Normal file
170
Research/scene/Material/BitsMaterial.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../core/BitHelper.h"
|
||||
#include "../../core/Defines.h"
|
||||
#include <cmath>
|
||||
|
||||
template <unsigned N>
|
||||
class BitsMaterial
|
||||
{
|
||||
public:
|
||||
const static size_t BYTECOUNT = N / 8 + ((N % 8) == 0 ? 0 : 1); // Calculate the space needed to store the required number of bits
|
||||
const static unsigned8 BITS = N;
|
||||
private:
|
||||
unsigned8 mValue[BYTECOUNT];
|
||||
|
||||
static size_t GrabBits(size_t value, unsigned8 startBit, unsigned8 length) {
|
||||
return ((BitHelper::GetLSMask<size_t>(startBit, startBit + length)) & value) >> startBit;
|
||||
}
|
||||
static size_t GrabBits(size_t value, unsigned8 startBit) { return GrabBits(value, startBit, N); }
|
||||
|
||||
inline void Init(size_t value)
|
||||
{
|
||||
for (unsigned8 i = 0; i < BYTECOUNT; i++)
|
||||
mValue[BYTECOUNT - i - 1] = (unsigned8)GrabBits(value, i * 8, 8);
|
||||
}
|
||||
public:
|
||||
BitsMaterial() { mValue[0] = 0; }
|
||||
BitsMaterial(size_t value) { Init(value); }
|
||||
// Grab some bits from the number and store them in this material
|
||||
BitsMaterial(unsigned value, unsigned8 startBit) { Init(GrabBits(value, startBit)); }
|
||||
~BitsMaterial() {}
|
||||
|
||||
bool Empty() { return mValue == 0; }
|
||||
|
||||
glm::u16vec3 GetProperties() const {
|
||||
unsigned32 value = (unsigned32)GetValue();
|
||||
return glm::u16vec3(GrabBits(value, 21, 11), GrabBits(value, 10, 11), GrabBits(value, 0, 10));
|
||||
}
|
||||
void SetProperties(glm::u16vec3 material)
|
||||
{
|
||||
unsigned32 value = (((unsigned32)material.x) << 21) | (((unsigned32)material.y & BitHelper::GetLSMask<unsigned32>(0, 11)) << 10) | (((unsigned32)material.z) & BitHelper::GetLSMask<unsigned32>(0, 10));
|
||||
Init(value);
|
||||
}
|
||||
|
||||
size_t GetValue() const {
|
||||
size_t value = 0;
|
||||
for (unsigned8 i = 0; i < BYTECOUNT; i++)
|
||||
value |= mValue[i] << ((BYTECOUNT - i - 1) >> 3); // Get the value, shift it to the correct position, and add it to the GetValue() result
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string GetTypeSuffix() const {
|
||||
return "b" + std::to_string(N);
|
||||
}
|
||||
|
||||
static float Distance(BitsMaterial a, BitsMaterial b)
|
||||
{
|
||||
size_t maxValue = BitHelper::Exp2(N);
|
||||
return (float)std::abs((float)b.GetValue() - (float)a.GetValue()) / (float)maxValue;
|
||||
}
|
||||
|
||||
static BitsMaterial Average(const std::vector<BitsMaterial>& values)
|
||||
{
|
||||
size_t sum = 0;
|
||||
for (auto value : values) sum += value.GetValue();
|
||||
sum /= values.size();
|
||||
return BitsMaterial(sum);
|
||||
}
|
||||
|
||||
static BitsMaterial WeightedAverage(const std::vector<BitsMaterial>& values, std::vector<float> weights)
|
||||
{
|
||||
float sum = 0;
|
||||
for (size_t i = 0; i < values.size(); i++) sum += (float)values[i].GetValue() * weights[i];
|
||||
sum /= (float)CollectionHelper::Sum(weights);
|
||||
return BitsMaterial((size_t)sum);
|
||||
}
|
||||
|
||||
|
||||
void SetLS(size_t index, bool value)
|
||||
{
|
||||
size_t byte = index >> 8;
|
||||
unsigned8 mask = BitHelper::GetLSSingleBitMask<unsigned8>(index & 0x07);
|
||||
// Clear the bit
|
||||
mValue[byte] &= ~mask;
|
||||
// Set the bit
|
||||
if (value) mValue[byte] |= mask;
|
||||
}
|
||||
|
||||
bool GetLS(size_t index)
|
||||
{
|
||||
size_t byte = index >> 8;
|
||||
size_t bit = index % 8;
|
||||
return BitHelper::GetLS(mValue[byte], (unsigned8)bit);
|
||||
}
|
||||
|
||||
void SetHS(size_t index, bool value)
|
||||
{
|
||||
SetLS(N - index - 1, value);
|
||||
}
|
||||
|
||||
bool GetHS(size_t index)
|
||||
{
|
||||
return GetLS(N - index - 1);
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Serialize() const {
|
||||
std::vector<unsigned8> res(BYTECOUNT);
|
||||
for (size_t i = 0; i < BYTECOUNT; i++) res[i] = mValue[i];
|
||||
return res;
|
||||
}
|
||||
|
||||
void Deserialize(std::vector<unsigned8> value) {
|
||||
assert(value.size() == BYTECOUNT);
|
||||
for (size_t i = 0; i < BYTECOUNT; i++) mValue[i] = value[i];
|
||||
}
|
||||
|
||||
void Serialize(std::ostream& stream) const {
|
||||
stream.write((char*)&mValue[0], BYTECOUNT);
|
||||
}
|
||||
void Deserialize(std::istream& stream) {
|
||||
stream.read((char*)&mValue[0], BYTECOUNT);
|
||||
}
|
||||
|
||||
bool operator==(const BitsMaterial<N>& other) const {
|
||||
for (unsigned i = 0; i < BYTECOUNT; i++)
|
||||
if (mValue[i] != other.mValue[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator<(const BitsMaterial<N>& other) const
|
||||
{
|
||||
for (unsigned8 i = 0; i < BYTECOUNT; i++)
|
||||
if (mValue[i] != other.mValue[i]) return mValue[i] < other.mValue[i];
|
||||
return false; // If they are equal, return false
|
||||
}
|
||||
|
||||
operator unsigned() const
|
||||
{
|
||||
return (unsigned32)GetValue();
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<unsigned N> struct hash <BitsMaterial<N>>
|
||||
{
|
||||
size_t operator()(BitsMaterial<N> const& value) const
|
||||
{
|
||||
// Check if a perfect hash is possible
|
||||
if (N < (sizeof(size_t) * 8))
|
||||
{
|
||||
return value.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: make a hash for all bytes
|
||||
return value.GetValue();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<unsigned N>
|
||||
struct BitsMaterialComparer
|
||||
{
|
||||
bool operator()(const BitsMaterial<N>& a, const BitsMaterial<N>& b)
|
||||
{
|
||||
return a.GetValue() < b.GetValue();
|
||||
}
|
||||
};
|
||||
56
Research/scene/Material/Block.h
Normal file
56
Research/scene/Material/Block.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
|
||||
template<typename T>
|
||||
struct Block
|
||||
{
|
||||
public:
|
||||
Block() { mData = std::vector<T>(); }
|
||||
|
||||
Block(unsigned size)
|
||||
{
|
||||
mData = std::vector<T>(size);
|
||||
}
|
||||
Block(const std::vector<T>& data)
|
||||
{
|
||||
// Copy the given vector to mData
|
||||
mData = std::vector<T>(data);
|
||||
}
|
||||
Block(const std::vector<T>& data, const size_t& startIndex, const size_t& endIndex)
|
||||
{
|
||||
mData = std::vector<T>(data.begin() + startIndex, data.begin() + endIndex);
|
||||
}
|
||||
~Block() {}
|
||||
|
||||
size_t size() const { return mData.size(); }
|
||||
const T& Get(const size_t& i) const { return mData[i]; }
|
||||
const std::vector<T>& GetData() const { return mData; }
|
||||
void Set(const size_t& i, T v) { mData[i] = v; }
|
||||
|
||||
template<typename Compare>
|
||||
void Sort(const Compare& comparer)
|
||||
{
|
||||
std::sort(mData.begin(), mData.end(), comparer);
|
||||
}
|
||||
|
||||
template<typename Compare>
|
||||
void ParallelSort(const Compare& comparer)
|
||||
{
|
||||
tbb::parallel_sort(mData.begin(), mData.end(), comparer);
|
||||
}
|
||||
|
||||
bool operator==(const Block<T>& other) const
|
||||
{
|
||||
if (other.size() != this->size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < this->size(); i++)
|
||||
if (other.Get(i) != this->Get(i)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const T& operator[](size_t i) const { return mData[i]; }
|
||||
private:
|
||||
std::vector<T> mData;
|
||||
};
|
||||
38
Research/scene/Material/BlockBasedMaterialLibrary.h
Normal file
38
Research/scene/Material/BlockBasedMaterialLibrary.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "MaterialLibrary.h"
|
||||
#include "Block.h"
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class BlockBasedMaterialLibrary : public MaterialLibrary<T, Comparer, channelsPerPixel>
|
||||
{
|
||||
private:
|
||||
std::vector<Block<T>> mBlocks;
|
||||
std::vector<size_t> mBlocksImportance;
|
||||
public:
|
||||
BlockBasedMaterialLibrary() : MaterialLibrary<T, Comparer, channelsPerPixel>() {}
|
||||
BlockBasedMaterialLibrary(std::vector<unsigned char> texture, unsigned short textureSize, MaterialLibraryPointer highestMaterialIndex) : MaterialLibrary<T, Comparer, channelsPerPixel>(texture, textureSize, highestMaterialIndex)
|
||||
{ }
|
||||
|
||||
// Copy constructor
|
||||
BlockBasedMaterialLibrary(const BlockBasedMaterialLibrary& other) : MaterialLibrary<T, Comparer, channelsPerPixel>(other)
|
||||
{
|
||||
this->mBlocks = other.mBlocks;
|
||||
this->mBlocksImportance = other.mBlocksImportance;
|
||||
}
|
||||
|
||||
~BlockBasedMaterialLibrary() {}
|
||||
|
||||
//void Finalize()
|
||||
//{
|
||||
// std::srand(time(0));
|
||||
// // TODO: optimize the texture to get the lowest cost for the blocks
|
||||
// std::random_shuffle(mMaterials->begin(), mMaterials->end());
|
||||
// MaterialLibrary::Finalize(false);
|
||||
//}
|
||||
};
|
||||
131
Research/scene/Material/Color.cpp
Normal file
131
Research/scene/Material/Color.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "Color.h"
|
||||
#include "../../core/ColorHelper.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/MathHelper.h"
|
||||
#include "../../inc/tbb/parallel_reduce.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
#include "../../inc/tbb/blocked_range.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
Color::Color()
|
||||
{
|
||||
mColor = glm::u8vec3(0);
|
||||
}
|
||||
|
||||
Color::Color(glm::u8vec3 color)
|
||||
{
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
glm::u16vec3 Color::GetProperties() const
|
||||
{
|
||||
return glm::u16vec3(mColor);
|
||||
}
|
||||
|
||||
void Color::SetProperties(glm::u16vec3 color)
|
||||
{
|
||||
mColor = glm::u8vec3(color);
|
||||
}
|
||||
|
||||
glm::u8vec3 Color::GetColor() const
|
||||
{
|
||||
return mColor;
|
||||
}
|
||||
|
||||
void Color::SetColor(glm::u8vec3 color)
|
||||
{
|
||||
mColor = color;;
|
||||
}
|
||||
|
||||
glm::vec3 Color::GetLAB() const
|
||||
{
|
||||
return ColorHelper::RGBtoLAB(mColor);
|
||||
}
|
||||
|
||||
Color Color::Average(const std::vector<Color>& colors)
|
||||
{
|
||||
glm::vec3 colorLabSum;
|
||||
for (auto color : colors)
|
||||
colorLabSum += ColorHelper::RGBtoLAB(color.GetColor());
|
||||
glm::vec3 colorLabAvg = colorLabSum / (float)colors.size();
|
||||
return Color(ColorHelper::LABtoRGB(colorLabAvg));
|
||||
}
|
||||
|
||||
Color Color::WeightedAverage(const std::vector<Color>& colors, const std::vector<float>& weights)
|
||||
{
|
||||
// Calculate the weighted average
|
||||
glm::vec3 colorLabSum;
|
||||
std::vector<glm::vec3> labColors(colors.size());
|
||||
float weightSum = 0;
|
||||
for (unsigned i = 0; i < colors.size(); i++)
|
||||
{
|
||||
labColors[i] = colors[i].GetLAB();
|
||||
colorLabSum += labColors[i] * weights[i];
|
||||
weightSum += weights[i];
|
||||
}
|
||||
glm::vec3 colorLabAvg = colorLabSum / weightSum;
|
||||
glm::vec3 res = colorLabAvg;
|
||||
|
||||
return Color(ColorHelper::LABtoRGB(res));
|
||||
}
|
||||
|
||||
float Color::Distance(const Color& color1, const Color& color2)
|
||||
{
|
||||
return ColorHelper::GetDeltaEFromRGB(color1.GetColor(), color2.GetColor()) / 130.f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Color Color::Interpolate(const Color& color1, const Color& color2, float value)
|
||||
{
|
||||
glm::u8vec3 hsv1 = ColorHelper::RGBtoHSV(color1.GetColor());
|
||||
glm::u8vec3 hsv2 = ColorHelper::RGBtoHSV(color2.GetColor());
|
||||
glm::u8vec3 hsvRes;
|
||||
hsvRes.r = MathHelper::lerp(value, hsv1.r, hsv2.r);
|
||||
hsvRes.g = MathHelper::lerp(value, hsv1.g, hsv2.g);
|
||||
hsvRes.b = MathHelper::lerp(value, hsv1.b, hsv2.b);
|
||||
return Color(ColorHelper::HSVtoRGB(hsvRes));
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Color::Serialize() const
|
||||
{
|
||||
std::vector<unsigned8> res(3);
|
||||
res[0] = mColor.r;
|
||||
res[1] = mColor.g;
|
||||
res[2] = mColor.b;
|
||||
return res;
|
||||
}
|
||||
void Color::Deserialize(const std::vector<unsigned8>& value)
|
||||
{
|
||||
assert(value.size() == 3);
|
||||
mColor.r = value[0];
|
||||
mColor.g = value[1];
|
||||
mColor.b = value[2];
|
||||
}
|
||||
|
||||
bool Color::operator==(const Color& color) const
|
||||
{
|
||||
return this->GetColor() == color.GetColor();
|
||||
}
|
||||
|
||||
bool Color::operator!=(const Color& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
Color& Color::operator=(const unsigned& value)
|
||||
{
|
||||
mColor.x = (value & 0x00FF0000) >> 16;
|
||||
mColor.y = (value & 0x0000FF00) >> 8;
|
||||
mColor.z = (value & 0x000000FF);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color::operator unsigned() const
|
||||
{
|
||||
return mColor.x << 16 | mColor.y << 8 | mColor.z;
|
||||
}
|
||||
|
||||
std::string Color::GetTypeSuffix() const { return "c"; }
|
||||
109
Research/scene/Material/Color.h
Normal file
109
Research/scene/Material/Color.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "NearestFinder.h"
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../core/Comparers.h"
|
||||
#include "../../core/ColorHelper.h"
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
const static unsigned8 CHANNELSPERPIXEL = 3;
|
||||
const static unsigned8 BITS = 24;
|
||||
|
||||
Color();
|
||||
Color(glm::u8vec3 color);
|
||||
|
||||
glm::u16vec3 GetProperties() const;
|
||||
void SetProperties(glm::u16vec3 color);
|
||||
|
||||
glm::vec3 GetLAB() const;
|
||||
static Color Average(const std::vector<Color>& colors);
|
||||
static Color WeightedAverage(const std::vector<Color>& colors, const std::vector<float>& weights);
|
||||
// Interpolates between the two colors. Value should be in range [0, 1), and it use to determine where to interpolate
|
||||
static Color Interpolate(const Color& color1, const Color& color2, float value);
|
||||
// Returns the color that is nearest to the given color (O(N))
|
||||
static float Distance(const Color& color1, const Color& color2);
|
||||
|
||||
glm::u8vec3 GetColor() const;
|
||||
void SetColor(glm::u8vec3 color);
|
||||
|
||||
inline unsigned8 GetR() const { return mColor.r; }
|
||||
inline unsigned8 GetG() const { return mColor.g; }
|
||||
inline unsigned8 GetB() const { return mColor.b; }
|
||||
|
||||
std::string GetTypeSuffix() const;
|
||||
|
||||
std::vector<unsigned8> Serialize() const;
|
||||
void Deserialize(const std::vector<unsigned8>& value);
|
||||
|
||||
bool operator==(const Color& color) const;
|
||||
bool operator!=(const Color& color) const;
|
||||
|
||||
// Assignment operator, used for easy access to different kinds of materials
|
||||
Color& operator=(const unsigned& source);
|
||||
operator unsigned() const;
|
||||
unsigned8 operator[](unsigned8 i) const
|
||||
{
|
||||
return mColor[i];
|
||||
}
|
||||
private:
|
||||
glm::u8vec3 mColor;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Color> {
|
||||
size_t operator()(const Color &value) const {
|
||||
glm::u8vec3 color = value.GetColor();
|
||||
return std::hash<glm::u8vec3>()(color);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct ColorCompare
|
||||
{
|
||||
bool operator()(const Color& color1, const Color& color2) const
|
||||
{
|
||||
return u8vec3comparer()(color1.GetColor(), color2.GetColor());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> struct NearestFinder<Color>
|
||||
{
|
||||
Color operator()(const Color& source, const std::vector<Color>& colors)
|
||||
{
|
||||
glm::vec3 lab = source.GetLAB();
|
||||
|
||||
Color nearestColor;
|
||||
float minDistance = std::numeric_limits<float>::max();
|
||||
for (auto color : colors)
|
||||
{
|
||||
float distance = ColorHelper::GetDeltaEFromLAB(color.GetLAB(), lab);
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestColor = color;
|
||||
}
|
||||
}
|
||||
return nearestColor;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct ParallelNearestFinder<Color>
|
||||
{
|
||||
Color operator()(const Color& source, const std::vector<Color>& colors)
|
||||
{
|
||||
// Calculate all distances:
|
||||
glm::vec3 lab = source.GetLAB();
|
||||
std::vector<float> distances(colors.size());
|
||||
tbb::parallel_for(size_t(0), colors.size(), [&](size_t i)
|
||||
{
|
||||
float res = ColorHelper::GetDeltaEFromLAB(colors[i].GetLAB(), lab);
|
||||
if (std::isnan(res)) res = std::numeric_limits<float>::max();
|
||||
distances[i] = res;
|
||||
});
|
||||
return colors[CollectionHelper::MinIndex(distances)];
|
||||
}
|
||||
};
|
||||
6
Research/scene/Material/ColorAndNormal.h
Normal file
6
Research/scene/Material/ColorAndNormal.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "ColorAndValue.h"
|
||||
#include "SmallNormal.h"
|
||||
|
||||
typedef ColorAndValue<SmallNormal> ColorAndNormal;
|
||||
typedef ColorAndValueCompare<SmallNormal, NormalCompare> ColorAndNormalCompare;
|
||||
9
Research/scene/Material/ColorAndNormalAndValue.h
Normal file
9
Research/scene/Material/ColorAndNormalAndValue.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "ColorAndValue.h"
|
||||
#include "SmallNormal.h"
|
||||
#include "BitsMaterial.h"
|
||||
|
||||
typedef MaterialPair<SmallNormal, BitsMaterial<8>> NormalAndValue;
|
||||
typedef MaterialPairCompare<SmallNormal, NormalCompare, BitsMaterial<8>, BitsMaterialComparer<8>> NormalAndValueCompare;
|
||||
typedef ColorAndValue<NormalAndValue> ColorAndNormalAndValue;
|
||||
typedef ColorAndValueCompare<NormalAndValue, NormalAndValueCompare> ColorAndNormalAndValueCompare;
|
||||
6
Research/scene/Material/ColorAndOpacity.h
Normal file
6
Research/scene/Material/ColorAndOpacity.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "ColorAndValue.h"
|
||||
#include "BitsMaterial.h"
|
||||
|
||||
typedef ColorAndValue<BitsMaterial<16>> ColorAndOpacity;
|
||||
typedef ColorAndValueCompare<BitsMaterial<16>, BitsMaterialComparer<16>> ColorAndOpacityCompare;
|
||||
110
Research/scene/Material/ColorAndValue.h
Normal file
110
Research/scene/Material/ColorAndValue.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include "NearestFinder.h"
|
||||
#include "Color.h"
|
||||
#include "MaterialPair.h"
|
||||
#include "../../core/ColorHelper.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
|
||||
template<typename T>
|
||||
struct ParallelNearestFinder<MaterialPair<Color, T>>
|
||||
{
|
||||
MaterialPair<Color, T> operator()(const MaterialPair<Color, T>& source, const std::vector<MaterialPair<Color, T>>& values) const
|
||||
{
|
||||
// Get the color differences
|
||||
glm::vec3 lab = source.GetFirst().GetLAB();
|
||||
|
||||
std::vector<float> distances(values.size());
|
||||
tbb::parallel_for(size_t(0), values.size(), [&](size_t i)
|
||||
{
|
||||
float res = ColorHelper::GetDeltaEFromLAB(values[i].GetFirst().GetLAB(), lab);
|
||||
if (std::isnan(res)) res = std::numeric_limits<float>::max();
|
||||
distances[i] = res;
|
||||
});
|
||||
|
||||
size_t colorMinIndex = CollectionHelper::MinIndex(distances);
|
||||
float colorMinDistance = distances[colorMinIndex];
|
||||
float epsilon = 0.2f;
|
||||
std::vector<size_t> minIndices;
|
||||
float maxAllowedError = colorMinDistance + epsilon;
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
if (distances[i] <= maxAllowedError) minIndices.push_back(i);
|
||||
|
||||
std::vector<T> minValues;
|
||||
for (size_t i = 0; i < minIndices.size(); i++) minValues.push_back(values[minIndices[i]].GetSecond());
|
||||
T minValue = ParallelNearestFinder<T>()(source.GetSecond(), minValues);
|
||||
|
||||
for (size_t i = 0; i < minIndices.size(); i++) if (values[minIndices[i]].GetSecond() == minValue) return values[minIndices[i]];
|
||||
return values[minIndices[0]];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct NearestFinder<MaterialPair<Color, T>>
|
||||
{
|
||||
MaterialPair<Color, T> operator()(const MaterialPair<Color, T>& source, const std::vector<MaterialPair<Color, T>>& values) const
|
||||
{
|
||||
glm::vec3 lab = source.GetFirst().GetLAB();
|
||||
float minDistance = std::numeric_limits<float>::max();
|
||||
std::vector<float> distances(values.size());
|
||||
Color lastColor = values[values.size() - 1].GetFirst();
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
const Color& color = values[i].GetFirst();
|
||||
if (color == lastColor)
|
||||
distances[i] = distances[i - 1];
|
||||
else
|
||||
{
|
||||
distances[i] = ColorHelper::GetDeltaEFromLAB(color.GetLAB(), lab);
|
||||
lastColor = color;
|
||||
if (distances[i] < minDistance) minDistance = distances[i];
|
||||
}
|
||||
}
|
||||
|
||||
float epsilon = 0.5f;
|
||||
std::vector<size_t> allowedColors;
|
||||
float maxAllowedError = minDistance + epsilon;
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
if (distances[i] <= maxAllowedError) allowedColors.push_back(i);
|
||||
|
||||
|
||||
std::vector<size_t> minIndices;
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
if (distances[i] <= maxAllowedError) minIndices.push_back(i);
|
||||
|
||||
std::vector<T> minValues;
|
||||
for (size_t i = 0; i < minIndices.size(); i++) minValues.push_back(values[minIndices[i]].GetSecond());
|
||||
T minValue = NearestFinder<T>()(source.GetSecond(), minValues);
|
||||
|
||||
for (size_t i = 0; i < minIndices.size(); i++) if (values[minIndices[i]].GetSecond() == minValue) return values[minIndices[i]];
|
||||
return values[minIndices[0]];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//// Assignment operator, used for easy access to different kinds of materials
|
||||
//ColorAndValue& operator=(const unsigned& source)
|
||||
//{
|
||||
// mValue = source & 0x3FF;
|
||||
// glm::u8vec3 color;
|
||||
// color.r = (source >> (10 + 14 - 1)) & 0xFE;
|
||||
// color.g = (source >> (10 + 7 - 1)) & 0xFE;
|
||||
// color.b = (source >> (10 - 1)) & 0xFE;
|
||||
// mColor = Color(color);
|
||||
//}
|
||||
//operator unsigned() const
|
||||
//{
|
||||
// // Value is stored in the 10 lowest siginificant bits
|
||||
// unsigned32 res = (unsigned32)mValue;
|
||||
// // Colors are stored in the 21 highest significant bits (7 bits per channel, doesn't fit otherwise...)
|
||||
// res |= ((unsigned32)mColor.GetR() & 0xFE) << (10 + 14 - 1);
|
||||
// res |= ((unsigned32)mColor.GetG() & 0xFE) << (10 + 7 - 1);
|
||||
// res |= ((unsigned32)mColor.GetB() & 0xFE) << (10 - 1);
|
||||
// return res;
|
||||
//}
|
||||
|
||||
template<typename T> using ColorAndValue = MaterialPair<Color, T>;
|
||||
template<typename T, typename TCompare> using ColorAndValueCompare = MaterialPairCompare<Color, ColorCompare, T, TCompare>;
|
||||
23
Research/scene/Material/ColorChannel.cpp
Normal file
23
Research/scene/Material/ColorChannel.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "ColorChannel.h"
|
||||
|
||||
ColorChannel::ColorChannel() { mValue = 0; }
|
||||
ColorChannel::ColorChannel(unsigned8 value) { mValue = value; }
|
||||
ColorChannel::~ColorChannel() {}
|
||||
|
||||
std::vector<unsigned8> ColorChannel::Serialize() const
|
||||
{
|
||||
return std::vector<unsigned8>(1, mValue);
|
||||
}
|
||||
|
||||
void ColorChannel::Deserialize(const std::vector<unsigned8>& value)
|
||||
{
|
||||
assert(value.size() == 1);
|
||||
mValue = value[0];
|
||||
}
|
||||
|
||||
glm::u16vec3 ColorChannel::GetProperties() const { return glm::u16vec3(mValue); }
|
||||
void ColorChannel::SetProperties(glm::u16vec3 material) { mValue = (unsigned char)material.x; }
|
||||
|
||||
unsigned8 ColorChannel::GetValue() const { return mValue; }
|
||||
|
||||
ColorChannel::operator unsigned() const { return (unsigned)mValue; }
|
||||
40
Research/scene/Material/ColorChannel.h
Normal file
40
Research/scene/Material/ColorChannel.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
class ColorChannel
|
||||
{
|
||||
public:
|
||||
ColorChannel();
|
||||
ColorChannel(unsigned8 value);
|
||||
~ColorChannel();
|
||||
|
||||
glm::u16vec3 GetProperties() const;
|
||||
void SetProperties(glm::u16vec3 material);
|
||||
|
||||
std::vector<unsigned8> Serialize() const;
|
||||
void Deserialize(const std::vector<unsigned8>& value);
|
||||
|
||||
unsigned8 GetValue() const;
|
||||
|
||||
std::string GetTypeSuffix() const { return "cc"; }
|
||||
|
||||
operator unsigned() const;
|
||||
|
||||
private:
|
||||
unsigned8 mValue;
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<ColorChannel>
|
||||
{
|
||||
size_t operator()(const ColorChannel &value) const
|
||||
{
|
||||
return value.GetValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
277
Research/scene/Material/MaterialLibrary.h
Normal file
277
Research/scene/Material/MaterialLibrary.h
Normal file
@@ -0,0 +1,277 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "MaterialLibraryPointer.h"
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
// T should be some type with a method called "Serialize()".
|
||||
// This method should return some type that can be iterated over using operator [], and each item of the iteration should be an unsigned char (unsigned8) to put in the texture colors.
|
||||
// If sizeof(result) for the value is used, it should give the number of bytes needed to store in pixels.
|
||||
// An example of a working return type is std::vector<unsigned8>, glm::u8vec3, and unsigned char[].
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class MaterialLibrary
|
||||
{
|
||||
private:
|
||||
std::map<T, MaterialLibraryPointer, Comparer> mMaterialPointers;
|
||||
bool finalized = false;
|
||||
unsigned short mTextureSize;
|
||||
|
||||
protected:
|
||||
const size_t SIZE_OF_MATERIAL = sizeof(T);
|
||||
const unsigned32 PIXELS_PER_MATERIAL = (unsigned32)(SIZE_OF_MATERIAL / channelsPerPixel + (SIZE_OF_MATERIAL % channelsPerPixel == 0 ? 0 : 1));
|
||||
|
||||
std::vector<T>* mMaterials;
|
||||
|
||||
inline MaterialLibraryPointer WrapSetIndex(size_t setIndex) const
|
||||
{
|
||||
assert(finalized);
|
||||
setIndex *= PIXELS_PER_MATERIAL;
|
||||
return MaterialLibraryPointer(
|
||||
(unsigned16)(setIndex % (size_t)mTextureSize),
|
||||
(unsigned16)(setIndex / (size_t)mTextureSize)
|
||||
);
|
||||
}
|
||||
|
||||
inline size_t GetSetIndex(MaterialLibraryPointer textureIndex) const { assert(finalized); return (textureIndex.x + textureIndex.y * mTextureSize) / PIXELS_PER_MATERIAL; }
|
||||
|
||||
inline void RebuildMaterialPointers()
|
||||
{
|
||||
assert(finalized);
|
||||
mMaterialPointers.clear();
|
||||
// Build the result material pointers map:
|
||||
for (size_t i = 0; i < mMaterials->size(); i++)
|
||||
mMaterialPointers.insert(std::pair<T, MaterialLibraryPointer>(mMaterials->at(i), WrapSetIndex(i)));
|
||||
}
|
||||
public:
|
||||
MaterialLibrary() :
|
||||
mMaterialPointers(std::map<T, MaterialLibraryPointer, Comparer>()),
|
||||
finalized(false),
|
||||
mTextureSize(0),
|
||||
mMaterials(new std::vector<T>())
|
||||
{}
|
||||
|
||||
MaterialLibrary(std::vector<unsigned char> texture, unsigned short textureSize, MaterialLibraryPointer highestMaterialIndex) : MaterialLibrary()
|
||||
{
|
||||
ReadTexture(texture, textureSize, highestMaterialIndex);
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
MaterialLibrary(const MaterialLibrary& other) :
|
||||
mMaterialPointers(std::map<T, MaterialLibraryPointer, Comparer>(other.mMaterialPointers)),
|
||||
finalized(other.finalized),
|
||||
mTextureSize(other.mTextureSize),
|
||||
mMaterials(new std::vector<T>(*other.mMaterials))
|
||||
{}
|
||||
|
||||
~MaterialLibrary() { delete mMaterials; }
|
||||
|
||||
void Serialize(std::ostream& file)
|
||||
{
|
||||
unsigned16 materialTextureSize = GetTextureSize();
|
||||
MaterialLibraryPointer maxTextureIndex = GetMaxTextureIndex();
|
||||
std::vector<unsigned8> texture = GetTexture();
|
||||
|
||||
// The material texture size and max texture index used to be stored in one 32 bit unsigned integer.
|
||||
// However, this caused problems if the material texture contained more then 1024 colors.
|
||||
// Therefore, we now use 48 bits for this, precluded by an empty header in the old style, to ensure that the new style can be recognized correctly.
|
||||
Serializer<unsigned32>::Serialize(0, file);
|
||||
Serializer<unsigned16>::Serialize(materialTextureSize, file);
|
||||
Serializer<MaterialLibraryPointer>::Serialize(maxTextureIndex, file);
|
||||
Serializer<unsigned8*>::Serialize(&texture[0], (size_t)materialTextureSize * (size_t)materialTextureSize * (size_t)channelsPerPixel, file);
|
||||
}
|
||||
|
||||
void Deserialize(std::istream& file)
|
||||
{
|
||||
unsigned16 materialTextureSize = 0;
|
||||
MaterialLibraryPointer maxTextureIndex = MaterialLibraryPointer(0);
|
||||
|
||||
// If the first unsigned32 of a material library is 0, then the new style information is placed after it.
|
||||
// If it isn't 0, then use the old style
|
||||
unsigned materialTextureSizeSummary;
|
||||
Serializer<unsigned>::Deserialize(materialTextureSizeSummary, file);
|
||||
if (materialTextureSizeSummary == 0)
|
||||
{
|
||||
// New style (supports bigger libraries)
|
||||
Serializer<unsigned16>::Deserialize(materialTextureSize, file);
|
||||
Serializer<MaterialLibraryPointer>::Deserialize(maxTextureIndex, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Old style
|
||||
unsigned mask1 = BitHelper::GetLSMask<unsigned32>(20, 30);
|
||||
unsigned mask2 = BitHelper::GetLSMask<unsigned32>(10, 20);
|
||||
unsigned mask3 = BitHelper::GetLSMask<unsigned32>(0, 10);
|
||||
maxTextureIndex.x = (mask1 & materialTextureSizeSummary) >> 20;
|
||||
maxTextureIndex.y = (mask2 & materialTextureSizeSummary) >> 10;
|
||||
materialTextureSize = BitHelper::CeilToNearestPowerOfTwo(mask3 & materialTextureSizeSummary);
|
||||
}
|
||||
|
||||
size_t textureArraySize = (size_t)materialTextureSize * (size_t)materialTextureSize * (size_t)channelsPerPixel;
|
||||
std::vector<unsigned8> texture(textureArraySize);
|
||||
Serializer<unsigned8*>::Deserialize(&texture[0], textureArraySize, file);
|
||||
ReadTexture(texture, materialTextureSize, maxTextureIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AddMaterial(const T& material) {
|
||||
if(!finalized) mMaterials->push_back(material);
|
||||
}
|
||||
void RemoveMaterial(const T& material) { if (!finalized) mMaterials->erase(material); }
|
||||
void Finalize(bool filterUnique = true)
|
||||
{
|
||||
if (finalized) return;
|
||||
|
||||
if (filterUnique)
|
||||
CollectionHelper::Unique(*mMaterials, Comparer());
|
||||
|
||||
mTextureSize = GetTextureSize();
|
||||
finalized = true;
|
||||
|
||||
RebuildMaterialPointers();
|
||||
}
|
||||
bool IsFinalized() const { return finalized; }
|
||||
std::vector<T> GetMaterials() const
|
||||
{
|
||||
std::vector<T> materials(mMaterials->size());
|
||||
tbb::parallel_for(size_t(0), mMaterials->size(), [&](size_t i)
|
||||
{
|
||||
materials[i] = mMaterials->at(i);
|
||||
});
|
||||
return materials;
|
||||
}
|
||||
|
||||
T GetNearestMaterial(T material) const {
|
||||
return NearestFinder<T>()(material, *mMaterials);
|
||||
}
|
||||
T GetMaterial(MaterialLibraryPointer materialPointer)
|
||||
{
|
||||
assert(finalized);
|
||||
return mMaterials->at(GetSetIndex(materialPointer));
|
||||
}
|
||||
std::vector<std::pair<T, MaterialLibraryPointer>> GetMaterialTextureIndices() const
|
||||
{
|
||||
assert(finalized);
|
||||
std::vector<std::pair<T, MaterialLibraryPointer>> materials;
|
||||
size_t setIndex = 0;
|
||||
for (T material : (*mMaterials))
|
||||
{
|
||||
auto textureIndex = WrapSetIndex(setIndex++);
|
||||
materials.push_back(std::pair<T, MaterialLibraryPointer>(material, textureIndex));
|
||||
}
|
||||
return materials;
|
||||
}
|
||||
MaterialLibraryPointer GetTextureIndex(const T& material) const
|
||||
{
|
||||
assert(finalized);
|
||||
auto materialIt = mMaterialPointers.find(material);
|
||||
if (materialIt == mMaterialPointers.end())
|
||||
return GetTextureIndex(GetNearestMaterial(material));
|
||||
return materialIt->second;
|
||||
}
|
||||
|
||||
bool Contains(const T& material) const
|
||||
{
|
||||
auto materialIt = mMaterialPointers.find(material);
|
||||
return materialIt != mMaterialPointers.end();
|
||||
}
|
||||
|
||||
MaterialLibraryPointer GetMaxTextureIndex() const
|
||||
{
|
||||
return WrapSetIndex(mMaterials->size());
|
||||
}
|
||||
|
||||
unsigned short GetTextureSize() const {
|
||||
if (finalized) return mTextureSize;
|
||||
if (mMaterials->empty())
|
||||
return 0;
|
||||
size_t requiredPixels = PIXELS_PER_MATERIAL * mMaterials->size();
|
||||
if (requiredPixels < 4) requiredPixels = 4; // Make sure the texture is always 2x2
|
||||
unsigned16 v = (unsigned16)std::ceil(std::sqrt(double(requiredPixels)));
|
||||
return BitHelper::CeilToNearestPowerOfTwo(v);
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetTexture() const
|
||||
{
|
||||
std::vector<unsigned8> texture(mTextureSize * mTextureSize * channelsPerPixel);
|
||||
size_t i = 0;
|
||||
for (T material : (*mMaterials))
|
||||
{
|
||||
auto materialProperties = material.Serialize();
|
||||
unsigned props = (unsigned)materialProperties.size();
|
||||
unsigned prop = 0;
|
||||
assert(i + channelsPerPixel * PIXELS_PER_MATERIAL <= texture.size());
|
||||
|
||||
for (unsigned pixel = 0; pixel < PIXELS_PER_MATERIAL; pixel++)
|
||||
{
|
||||
for (unsigned8 channel = 0; channel < channelsPerPixel; channel++)
|
||||
{
|
||||
if (prop < props)
|
||||
texture[i + channel] = materialProperties[prop];
|
||||
prop++;
|
||||
}
|
||||
i += channelsPerPixel;
|
||||
}
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
void ReadTexture(std::vector<unsigned char> texture, unsigned textureSize, MaterialLibraryPointer maxTextureIndex)
|
||||
{
|
||||
mTextureSize = textureSize;
|
||||
unsigned props = PIXELS_PER_MATERIAL * channelsPerPixel;
|
||||
std::vector<unsigned8> curMatProps(SIZE_OF_MATERIAL);
|
||||
unsigned maxIndex = maxTextureIndex.x + maxTextureIndex.y * textureSize;
|
||||
bool endFound = maxIndex != 0;
|
||||
bool firstEmptyMaterialFound = false;
|
||||
size_t i = 0;
|
||||
while (!(endFound && i >= maxIndex * channelsPerPixel))
|
||||
{
|
||||
// Read the materials from the current pixels
|
||||
for (size_t j = 0; j < SIZE_OF_MATERIAL; j++)
|
||||
curMatProps[j] = texture[i + j];
|
||||
if (!endFound)
|
||||
{
|
||||
bool allZero = true;
|
||||
for (unsigned char prop : curMatProps)
|
||||
if (prop != 0)
|
||||
{
|
||||
allZero = false;
|
||||
break;
|
||||
}
|
||||
if (firstEmptyMaterialFound && allZero) endFound = true;
|
||||
firstEmptyMaterialFound |= allZero;
|
||||
}
|
||||
|
||||
T curMat;
|
||||
curMat.Deserialize(curMatProps);
|
||||
AddMaterial(curMat);
|
||||
|
||||
i += props;
|
||||
}
|
||||
finalized = true;
|
||||
RebuildMaterialPointers();
|
||||
}
|
||||
|
||||
bool operator==(const MaterialLibrary& other) const
|
||||
{
|
||||
// Check if the material count and finalized state are equal
|
||||
if (this->IsFinalized() != other.IsFinalized()
|
||||
|| this->mMaterials->size() != other.mMaterials->size())
|
||||
return false;
|
||||
|
||||
// Check if the materials are equal
|
||||
for (size_t i = 0; i < mMaterials->size(); i++)
|
||||
if (this->mMaterials->at(i) != other.mMaterials->at(i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const MaterialLibrary& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
55
Research/scene/Material/MaterialLibraryPointer.h
Normal file
55
Research/scene/Material/MaterialLibraryPointer.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "../../inc/glm/common.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include <vector>
|
||||
|
||||
class MaterialLibraryPointer :
|
||||
public glm::u16vec2
|
||||
{
|
||||
public:
|
||||
MaterialLibraryPointer() : glm::u16vec2() {}
|
||||
MaterialLibraryPointer(const unsigned16 v) : glm::u16vec2(v) {}
|
||||
MaterialLibraryPointer(const unsigned16 x, const unsigned16 y) : glm::u16vec2(x, y) {}
|
||||
~MaterialLibraryPointer() {}
|
||||
|
||||
MaterialLibraryPointer& operator=(const unsigned& source)
|
||||
{
|
||||
x = (source & 0xFFFF0000) >> 16;
|
||||
y = source & 0x0000FFFF;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Serialize()
|
||||
{
|
||||
return BitHelper::SplitInBytes(this->operator unsigned());
|
||||
}
|
||||
|
||||
operator unsigned() const
|
||||
{
|
||||
return (((unsigned)x) << 16) | y;
|
||||
}
|
||||
|
||||
bool operator==(const MaterialLibraryPointer& other) const
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
bool operator!=(const MaterialLibraryPointer& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
// Perfect hash
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<MaterialLibraryPointer>
|
||||
{
|
||||
size_t operator()(MaterialLibraryPointer const &value) const
|
||||
{
|
||||
return (unsigned) value;
|
||||
}
|
||||
};
|
||||
}
|
||||
169
Research/scene/Material/MaterialPair.h
Normal file
169
Research/scene/Material/MaterialPair.h
Normal file
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
#include "Color.h"
|
||||
#include "../../core/ColorHelper.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../core/Comparers.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
|
||||
template<typename U, typename V>
|
||||
class MaterialPair
|
||||
{
|
||||
private:
|
||||
U mFirst;
|
||||
V mSecond;
|
||||
public:
|
||||
static const unsigned8 BITS = U::BITS + V::BITS;
|
||||
static const unsigned8 CHANNELSPERPIXEL = BITS % 32 == 0 ? 4 : 3;
|
||||
|
||||
MaterialPair() : mFirst(U()), mSecond(V()) {}
|
||||
MaterialPair(U first, V second) : mFirst(first), mSecond(second) {}
|
||||
MaterialPair(std::pair<U, V> pair) : mFirst(pair.first), mSecond(pair.second) {}
|
||||
template<typename UC, typename VC>
|
||||
MaterialPair(UC first, VC second) : MaterialPair(U(first), V(second)) {}
|
||||
|
||||
const U& GetFirst() const { return mFirst; }
|
||||
void SetFirst(U value) { mFirst = value; }
|
||||
const V& GetSecond() const { return mSecond; }
|
||||
void SetSecond(V value) { mSecond = value; }
|
||||
|
||||
static MaterialPair Average(const std::vector<MaterialPair>& values)
|
||||
{
|
||||
std::vector<U> firsts(values.size());
|
||||
std::vector<V> seconds(values.size());
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
firsts[i] = values[i].GetFirst();
|
||||
seconds[i] = values[i].GetSecond();
|
||||
}
|
||||
return MaterialPair(U::Average(first), V::Average(seconds));
|
||||
}
|
||||
static MaterialPair WeightedAverage(const std::vector<MaterialPair>& values, const std::vector<float>& weights)
|
||||
{
|
||||
std::vector<U> firsts(values.size());
|
||||
std::vector<V> seconds(values.size());
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
firsts[i] = values[i].GetFirst();
|
||||
seconds[i] = values[i].GetSecond();
|
||||
}
|
||||
return MaterialPair(U::WeightedAverage(firsts, weights), V::WeightedAverage(seconds, weights));
|
||||
}
|
||||
|
||||
static float Distance(const MaterialPair& a, const MaterialPair& b)
|
||||
{
|
||||
return (U::Distance(a.GetFirst(), a.GetSecond()) + V::Distance(a.GetSecond(), b.GetSecond())) * 0.5f;
|
||||
}
|
||||
|
||||
std::string GetTypeSuffix() const { return GetFirst().GetTypeSuffix() + GetSecond().GetTypeSuffix(); }
|
||||
|
||||
std::vector<unsigned8> Serialize() const {
|
||||
std::vector<unsigned8> firstSerialized = GetFirst().Serialize();
|
||||
std::vector<unsigned8> secondSerialized = GetSecond().Serialize();
|
||||
firstSerialized.insert(firstSerialized.end(), secondSerialized.begin(), secondSerialized.end());
|
||||
return firstSerialized;
|
||||
};
|
||||
|
||||
void Deserialize(const std::vector<unsigned8>& value)
|
||||
{
|
||||
mFirst.Deserialize(std::vector<unsigned8>(value.begin(), value.begin() + sizeof(mFirst)));
|
||||
mSecond.Deserialize(std::vector<unsigned8>(value.begin() + sizeof(mFirst), value.end()));
|
||||
}
|
||||
|
||||
bool operator==(const MaterialPair& v) const { return v.mFirst == mFirst && v.mSecond == mSecond; }
|
||||
bool operator!=(const MaterialPair& n) const { return !(n == *this); }
|
||||
|
||||
//// Assignment operator, used for easy access to different kinds of materials
|
||||
//ColorAndValue& operator=(const unsigned& source)
|
||||
//{
|
||||
// mValue = source & 0x3FF;
|
||||
// glm::u8vec3 color;
|
||||
// color.r = (source >> (10 + 14 - 1)) & 0xFE;
|
||||
// color.g = (source >> (10 + 7 - 1)) & 0xFE;
|
||||
// color.b = (source >> (10 - 1)) & 0xFE;
|
||||
// mColor = Color(color);
|
||||
//}
|
||||
//operator unsigned() const
|
||||
//{
|
||||
// // Value is stored in the 10 lowest siginificant bits
|
||||
// unsigned32 res = (unsigned32)mValue;
|
||||
// // Colors are stored in the 21 highest significant bits (7 bits per channel, doesn't fit otherwise...)
|
||||
// res |= ((unsigned32)mColor.GetR() & 0xFE) << (10 + 14 - 1);
|
||||
// res |= ((unsigned32)mColor.GetG() & 0xFE) << (10 + 7 - 1);
|
||||
// res |= ((unsigned32)mColor.GetB() & 0xFE) << (10 - 1);
|
||||
// return res;
|
||||
//}
|
||||
};
|
||||
|
||||
template<typename U, typename V>
|
||||
struct NearestFinder<MaterialPair<U, V>>
|
||||
{
|
||||
MaterialPair<U, V> operator()(const MaterialPair<U, V>& source, const std::vector<MaterialPair<U, V>>& values)
|
||||
{
|
||||
std::vector<float> distances(values.size());
|
||||
// Distances are defined as the sum.
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
distances[i] = U::Distance(source.GetFirst(), values[i].GetFirst()) + V::Distance(source.GetSecond(), values[i].GetSecond());
|
||||
auto minIt = std::min_element(distances.begin(), distances.end());
|
||||
size_t minIndex = std::distance(distances.begin(), minIt);
|
||||
return values[minIndex];
|
||||
}
|
||||
};
|
||||
template<typename U, typename V>
|
||||
struct ParallelNearestFinder<MaterialPair<U, V>>
|
||||
{
|
||||
MaterialPair<U, V> operator()(const MaterialPair<U, V>& source, const std::vector<MaterialPair<U, V>>& values)
|
||||
{
|
||||
std::vector<float> distances(values.size());
|
||||
// Distances are defined as the sum.
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
distances[i] = U::Distance(source.GetFirst(), values[i].GetFirst()) + V::Distance(source.GetSecond(), values[i].GetSecond());
|
||||
auto minIt = std::min_element(distances.begin(), distances.end());
|
||||
size_t minIndex = std::distance(distances.begin(), minIt);
|
||||
return values[minIndex];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace std {
|
||||
template<typename U, typename V>
|
||||
struct hash<MaterialPair<U, V>> {
|
||||
size_t operator()(const MaterialPair<U, V> &value) const {
|
||||
#ifdef ENVIRONMENT64
|
||||
size_t cHash = std::hash<U>()(value.GetFirst());
|
||||
size_t nHash = std::hash<V>()(value.GetSecond());
|
||||
return cHash | BitHelper::CircularShiftLeft<size_t>(nHash, V::BITS);
|
||||
#else
|
||||
return (unsigned32)value;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename U, typename UCompare, typename V, typename VCompare>
|
||||
struct MaterialPairCompare
|
||||
{
|
||||
bool operator()(const MaterialPair<U, V>& a, const MaterialPair<U, V>& b) const
|
||||
{
|
||||
if (a.GetFirst() != b.GetFirst()) return UCompare()(a.GetFirst(), b.GetFirst());
|
||||
return VCompare()(a.GetSecond(), b.GetSecond());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename U, typename V>
|
||||
struct Serializer<MaterialPair<U, V>>
|
||||
{
|
||||
static void Serialize(const MaterialPair<U, V>& value, std::ostream& out)
|
||||
{
|
||||
Serializer<U>::Serialize(value.GetFirst(), out);
|
||||
Serializer<V>::Serialize(value.GetSecond(), out);
|
||||
}
|
||||
|
||||
static void Deserialize(MaterialPair<U, V>& value, std::istream& in)
|
||||
{
|
||||
U first; Serializer<U>::Deserialize(first, in); value.SetFirst(first);
|
||||
V second; Serializer<V>::Deserialize(second, in); value.SetSecond(second);
|
||||
}
|
||||
};
|
||||
18
Research/scene/Material/MaterialQuantizer/BaseQuantizer.h
Normal file
18
Research/scene/Material/MaterialQuantizer/BaseQuantizer.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
template<typename T, typename Comparer>
|
||||
class BaseQuantizer
|
||||
{
|
||||
public:
|
||||
virtual std::map<T, T, Comparer>* QuantizeMaterials(std::vector<T> materials) const = 0;
|
||||
virtual std::string GetQuantizerDescriptor() const = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class QuickQuantizer
|
||||
{
|
||||
public:
|
||||
virtual T Quantize(const T& material) const = 0;
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "BaseQuantizer.h"
|
||||
#include "ColorQuantizer/BaseColorQuantizer.h"
|
||||
#include "../ColorAndNormal.h"
|
||||
#include "../ColorAndOpacity.h"
|
||||
#include "../ColorAndNormalAndValue.h"
|
||||
#include "../../../core/CollectionHelper.h"
|
||||
|
||||
// Basically quantizes the colors using the given color quantizer and leaves the values as-is (assuming they are quantized enough already)
|
||||
// Deleting this object also deletes the associated color quantizer
|
||||
template<typename T, typename TCompare>
|
||||
class ColorAndValueQuantizer : public BaseQuantizer<ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>
|
||||
{
|
||||
private:
|
||||
BaseColorQuantizer* mColorQuantizer;
|
||||
public:
|
||||
ColorAndValueQuantizer(BaseColorQuantizer* colorQuantizer)
|
||||
: mColorQuantizer(colorQuantizer)
|
||||
{}
|
||||
|
||||
~ColorAndValueQuantizer()
|
||||
{
|
||||
delete mColorQuantizer;
|
||||
}
|
||||
|
||||
virtual std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>* QuantizeMaterials(std::vector<ColorAndValue<T>> materials) const
|
||||
{
|
||||
std::vector<Color> colors(materials.size());
|
||||
for (size_t i = 0; i < materials.size(); i++) colors[i] = materials[i].GetFirst();
|
||||
CollectionHelper::Unique(colors, ColorCompare());
|
||||
auto quantizedColors = mColorQuantizer->QuantizeMaterials(colors);
|
||||
std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>* res = new std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>();
|
||||
for (auto mat : materials)
|
||||
{
|
||||
ColorAndValue<T> replacer(quantizedColors->at(mat.GetFirst()), mat.GetSecond());
|
||||
res->insert(std::make_pair(mat, replacer));
|
||||
}
|
||||
delete quantizedColors;
|
||||
return res;
|
||||
}
|
||||
virtual std::string GetQuantizerDescriptor() const { return mColorQuantizer->GetQuantizerDescriptor(); }
|
||||
};
|
||||
|
||||
typedef ColorAndValueQuantizer<ColorAndNormal, ColorAndNormalCompare> ColorAndNormalQuantizer;
|
||||
typedef ColorAndValueQuantizer<ColorAndOpacity, ColorAndOpacityCompare> ColorAndOpacityQuantizer;
|
||||
typedef ColorAndValueQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare> ColorAndNormalAndValueQuantizer;
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "BaseColorQuantizer.h"
|
||||
#include "../../../../inc/tbb/parallel_for.h"
|
||||
|
||||
std::map<Color, Color, ColorCompare>* BaseColorQuantizer::QuantizeMaterials(std::vector<Color> materials) const
|
||||
{
|
||||
// Convert the "Colors" to u8vec3
|
||||
std::vector<glm::u8vec3> colors(materials.size());
|
||||
tbb::parallel_for(size_t(0), materials.size(), [&](size_t i)
|
||||
{
|
||||
colors[i] = materials[i].GetColor();
|
||||
});
|
||||
|
||||
// Quantize the colors
|
||||
auto quantizedColors = QuantizeColors(colors);
|
||||
//PrintQuantizationStatistics(quantizedColors);
|
||||
|
||||
// Find out what the quantized materials are
|
||||
std::map<Color, Color, ColorCompare>* quantizedMaterials = new std::map<Color, Color, ColorCompare>();
|
||||
for (auto quantizedColor : *quantizedColors)
|
||||
{
|
||||
quantizedMaterials->insert(std::pair<Color, Color>(Color(quantizedColor.first), Color(quantizedColor.second)));
|
||||
glm::vec3 error = glm::abs(glm::vec3(quantizedColor.first) - glm::vec3(quantizedColor.second));
|
||||
}
|
||||
delete quantizedColors;
|
||||
return quantizedMaterials;
|
||||
}
|
||||
|
||||
void BaseColorQuantizer::PrintQuantizationStatistics(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizedColors) const
|
||||
{
|
||||
glm::vec3 sumError(0);
|
||||
unsigned maxError = 0;
|
||||
glm::uvec3 maxErrorValue(0);
|
||||
float sumDeltaE = 0;
|
||||
float maxDeltaE = 0;
|
||||
for (auto quantizedColor : *quantizedColors)
|
||||
{
|
||||
glm::vec3 error = glm::abs(glm::vec3(quantizedColor.first) - glm::vec3(quantizedColor.second));
|
||||
sumError += error;
|
||||
unsigned errorU = error.r + error.g + error.b;
|
||||
if (errorU > maxError)
|
||||
{
|
||||
maxError = errorU;
|
||||
maxErrorValue = error;
|
||||
}
|
||||
float deltaE = ColorHelper::GetDeltaEFromRGB(quantizedColor.first, quantizedColor.second);
|
||||
if (deltaE == deltaE) // Only sum if it is not NaN...
|
||||
sumDeltaE += deltaE;
|
||||
if (deltaE > maxDeltaE) maxDeltaE = deltaE;
|
||||
}
|
||||
glm::vec3 meanError = sumError / float(quantizedColors->size());
|
||||
float meanDeltaE = sumDeltaE / float(quantizedColors->size());
|
||||
printf("Mean errors: (%f, %f, %f), Max errors: (%u, %u, %u), Mean delta-E: %f, Max delta-E: %f\n", meanError.x, meanError.y, meanError.z, maxErrorValue.x, maxErrorValue.y, maxErrorValue.z, meanDeltaE, maxDeltaE);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "../BaseQuantizer.h"
|
||||
#include "../../Color.h"
|
||||
|
||||
class BaseColorQuantizer : public BaseQuantizer<Color, ColorCompare>
|
||||
{
|
||||
public:
|
||||
virtual ~BaseColorQuantizer() {}
|
||||
|
||||
void PrintQuantizationStatistics(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizedColors) const;
|
||||
std::map<Color, Color, ColorCompare>* QuantizeMaterials(std::vector<Color> materials) const override;
|
||||
virtual std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const = 0;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "ColorBitCutter.h"
|
||||
|
||||
ColorBitCutter::ColorBitCutter(unsigned8 bitCount)
|
||||
{
|
||||
assert(bitCount <= 8);
|
||||
mBitCount = bitCount;
|
||||
}
|
||||
ColorBitCutter::~ColorBitCutter() {}
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* ColorBitCutter::QuantizeColors(std::vector<glm::u8vec3> colors) const
|
||||
{
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* res = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
|
||||
unsigned8 mask = BitHelper::GetHSMask<unsigned8>(0, mBitCount);
|
||||
for (glm::u8vec3 color : colors)
|
||||
{
|
||||
glm::u8vec3 replacementColor = glm::u8vec3(
|
||||
color.x & mask,
|
||||
color.y & mask,
|
||||
color.z & mask);
|
||||
res->insert(std::pair<glm::u8vec3, glm::u8vec3>(color, replacementColor));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
std::string ColorBitCutter::GetQuantizerDescriptor() const
|
||||
{
|
||||
return "b" + std::to_string(mBitCount);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "../../../../inc/glm/common.hpp"
|
||||
#include "../../../../core/Comparers.h"
|
||||
#include "BaseColorQuantizer.h"
|
||||
|
||||
class ColorBitCutter : public BaseColorQuantizer
|
||||
{
|
||||
public:
|
||||
ColorBitCutter(unsigned8 bitCount);
|
||||
~ColorBitCutter() override;
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
|
||||
std::string GetQuantizerDescriptor() const override;
|
||||
|
||||
private:
|
||||
unsigned8 mBitCount;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "MaxErrorClusterer.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <forward_list>
|
||||
|
||||
#include "../../../../inc/glm/geometric.hpp"
|
||||
|
||||
// Quantize the colors in CIELAB-space clusters that have a maximum size maxDistance.
|
||||
MaxErrorClusterer::MaxErrorClusterer(float maxDistance)
|
||||
{
|
||||
mMaxDistance = maxDistance;
|
||||
}
|
||||
MaxErrorClusterer::~MaxErrorClusterer() { }
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* MaxErrorClusterer::QuantizeColors(std::vector<glm::u8vec3> colors) const
|
||||
{
|
||||
// Transform all colors to CIELAB space:
|
||||
ListNode<Color>* firstUnclusteredColor = new ListNode<Color>();
|
||||
ListNode<Color>* cur = firstUnclusteredColor;
|
||||
for (auto rgb : colors)
|
||||
{
|
||||
Color color(rgb);
|
||||
cur->color = color;
|
||||
cur->next = new ListNode<Color>();
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
auto res = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
|
||||
while (firstUnclusteredColor != NULL)
|
||||
{
|
||||
Color curColor = firstUnclusteredColor->color;
|
||||
// Pop the head of the "list"
|
||||
{
|
||||
auto nextUnclusteredColor = firstUnclusteredColor->next;
|
||||
delete firstUnclusteredColor;
|
||||
firstUnclusteredColor = nextUnclusteredColor;
|
||||
}
|
||||
// Create a new cluster for this unclustered color
|
||||
auto cluster = std::vector<Color>();
|
||||
cluster.push_back(curColor);
|
||||
|
||||
// Loop through the unclustered colors
|
||||
if (firstUnclusteredColor != NULL)
|
||||
{
|
||||
cur = firstUnclusteredColor;
|
||||
ListNode<Color>* last = NULL;
|
||||
ListNode<Color>* next = cur->next;
|
||||
while (cur != NULL)
|
||||
{
|
||||
next = cur->next;
|
||||
if (ColorHelper::GetDeltaEFromLAB(cur->color.lab, curColor.lab) <= mMaxDistance)
|
||||
{
|
||||
// If we are close enough to the last color, add the current color to the cluster
|
||||
cluster.push_back(cur->color);
|
||||
|
||||
// Delete the current node from the list of unclustered nodes
|
||||
delete cur;
|
||||
|
||||
// If this isn't the first node in the list, make sure the last node points to this node
|
||||
if (last != NULL)
|
||||
{
|
||||
last->next = next;
|
||||
cur = last;
|
||||
}
|
||||
else
|
||||
{ // If this is the first node, we just deleted it, so replace the current first node by the next node
|
||||
firstUnclusteredColor = next;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this node wasn't add to the cluster, the last node should be updated before advancing
|
||||
last = cur;
|
||||
}
|
||||
// Advance one position
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the center of the current cluster
|
||||
glm::vec3 clusterSum(0);
|
||||
glm::u8vec3 clusterReplacementColor = curColor.rgb;
|
||||
for (auto color : cluster)
|
||||
res->insert(std::pair<glm::u8vec3, glm::u8vec3>(color.rgb, clusterReplacementColor));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string MaxErrorClusterer::GetQuantizerDescriptor() const
|
||||
{
|
||||
char str[7];
|
||||
sprintf(str, "%.2f", mMaxDistance);
|
||||
auto formattedMaxDistance = std::string(str);
|
||||
return "de" + formattedMaxDistance;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "BaseColorQuantizer.h"
|
||||
#include "../../../../core/ColorHelper.h"
|
||||
|
||||
class MaxErrorClusterer :
|
||||
public BaseColorQuantizer
|
||||
{
|
||||
public:
|
||||
// Quantize the colors in CIELAB-space clusters that have a maximum size maxDistance.
|
||||
// This is done using a greedy clustering algorithm that adds all colors to the cluster of a color if they're less than half the distance away.
|
||||
MaxErrorClusterer(float maxDistance);
|
||||
~MaxErrorClusterer() override;
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
|
||||
std::string GetQuantizerDescriptor() const override;
|
||||
protected:
|
||||
float mMaxDistance;
|
||||
private:
|
||||
template<typename T>
|
||||
struct ListNode
|
||||
{
|
||||
public:
|
||||
T color;
|
||||
ListNode<T>* next = NULL;
|
||||
|
||||
ListNode(T value) { color = value; }
|
||||
ListNode() { }
|
||||
};
|
||||
struct Color
|
||||
{
|
||||
glm::u8vec3 rgb;
|
||||
glm::vec3 lab;
|
||||
|
||||
Color(glm::u8vec3 rgb)
|
||||
{
|
||||
this->rgb = rgb;
|
||||
lab = ColorHelper::RGBtoLAB(rgb);
|
||||
}
|
||||
|
||||
Color() : Color(glm::u8vec3(0)) {}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "XiangCIELABClusterer.h"
|
||||
#include "../../../../core/ColorHelper.h"
|
||||
#include <string>
|
||||
|
||||
XiangCIELABClusterer::XiangCIELABClusterer(unsigned quantizedColorCount) : XiangClusterer(quantizedColorCount) {}
|
||||
XiangCIELABClusterer::~XiangCIELABClusterer() {}
|
||||
|
||||
glm::vec3 XiangCIELABClusterer::ScaleColor(glm::u8vec3 color) const
|
||||
{
|
||||
return ColorHelper::RGBtoLAB(color);
|
||||
}
|
||||
glm::u8vec3 XiangCIELABClusterer::ScaleBackColor(glm::vec3 color) const
|
||||
{
|
||||
return ColorHelper::LABtoRGB(color);
|
||||
}
|
||||
|
||||
std::string XiangCIELABClusterer::GetQuantizerDescriptor() const
|
||||
{
|
||||
return "lab" + std::to_string(mQuantizedColorCount);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "XiangClusterer.h"
|
||||
|
||||
class XiangCIELABClusterer :
|
||||
public XiangClusterer
|
||||
{
|
||||
public:
|
||||
// Quantize the colors to the given amount of colors, using the default RGB scaling, 0.5:1.0:0.25.
|
||||
XiangCIELABClusterer(unsigned quantizedColorCount);
|
||||
virtual ~XiangCIELABClusterer() override;
|
||||
|
||||
std::string GetQuantizerDescriptor() const override;
|
||||
protected:
|
||||
glm::vec3 ScaleColor(glm::u8vec3 color) const override;
|
||||
glm::u8vec3 ScaleBackColor(glm::vec3 color) const override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "XiangClusterer.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../../../../inc/glm/geometric.hpp"
|
||||
|
||||
XiangClusterer::XiangClusterer(unsigned quantizedColorCount, glm::vec3 colorScale)
|
||||
{
|
||||
mQuantizedColorCount = quantizedColorCount;
|
||||
mColorScale = colorScale;
|
||||
mInvColorScale = 1.0f / mColorScale;
|
||||
}
|
||||
|
||||
XiangClusterer::XiangClusterer(unsigned quantizedColorCount) : XiangClusterer(quantizedColorCount, glm::vec3(0.5, 1.0, 0.25)) {}
|
||||
|
||||
XiangClusterer::~XiangClusterer()
|
||||
{
|
||||
}
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* XiangClusterer::QuantizeColors(std::vector<glm::u8vec3> colors) const
|
||||
{
|
||||
// Set the initial cluster head to the first color in the set
|
||||
std::vector<glm::vec3> h(mQuantizedColorCount);
|
||||
h[0] = *colors.begin();
|
||||
|
||||
// Initialize all colors to be in the same cluster (cluster 0)
|
||||
std::vector<ClusterColor*> B(colors.size());
|
||||
int i = 0;
|
||||
for (auto color : colors)
|
||||
{
|
||||
ClusterColor* cur = new ClusterColor();
|
||||
cur->originalColor = color;
|
||||
cur->color = ScaleColor(color);
|
||||
cur->centerDistance = glm::distance(glm::vec3(color), h[0]);
|
||||
cur->clusterID = 0;
|
||||
B[i++] = cur;
|
||||
}
|
||||
|
||||
ClusterColor* max = B[0];
|
||||
|
||||
// Start the algorithm: in each iteration create a new cluster
|
||||
for (unsigned x = 1; x < mQuantizedColorCount; x++)
|
||||
{
|
||||
// Find the point with the maximum distance to the cluster center
|
||||
for (auto color : B)
|
||||
if (color->centerDistance > max->centerDistance)
|
||||
max = color;
|
||||
// Define this point as the center of a new cluster
|
||||
max->clusterID = x;
|
||||
h[x] = max->color;
|
||||
|
||||
// Move all colors that are closer to this new cluster than to their current cluster center over to the new cluster.
|
||||
for (auto color : B)
|
||||
{
|
||||
float newDistance = glm::distance(color->color, h[x]);
|
||||
if (newDistance < color->centerDistance)
|
||||
{
|
||||
color->clusterID = x;
|
||||
color->centerDistance = newDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find all cluster centers:
|
||||
std::vector<glm::vec3> clusterSums(mQuantizedColorCount);
|
||||
std::vector<float> invClusterCounts(mQuantizedColorCount);
|
||||
for (auto color : B)
|
||||
{
|
||||
clusterSums[color->clusterID] += color->color;
|
||||
invClusterCounts[color->clusterID]++;
|
||||
}
|
||||
// Inverse the cluster counts:
|
||||
for (size_t i = 0; i < invClusterCounts.size(); i++) invClusterCounts[i] = 1.0f / invClusterCounts[i];
|
||||
|
||||
// Calculate the color replacers
|
||||
std::vector<glm::u8vec3> clusterReplacers(mQuantizedColorCount);
|
||||
for (unsigned i = 0; i < mQuantizedColorCount; i++)
|
||||
clusterReplacers[i] = ScaleBackColor(clusterSums[i] * invClusterCounts[i]);
|
||||
|
||||
// Now that we have the clusters, build the replacement map
|
||||
auto replacements = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
|
||||
for (auto color : B)
|
||||
replacements->insert(std::pair<glm::u8vec3, glm::u8vec3>(color->originalColor, clusterReplacers[color->clusterID]));
|
||||
|
||||
for (auto color : B)
|
||||
delete color;
|
||||
|
||||
return replacements;
|
||||
}
|
||||
|
||||
glm::vec3 XiangClusterer::ScaleColor(glm::u8vec3 color) const
|
||||
{
|
||||
return glm::vec3(color) * mColorScale;
|
||||
}
|
||||
|
||||
glm::u8vec3 XiangClusterer::ScaleBackColor(glm::vec3 color) const
|
||||
{
|
||||
return glm::u8vec3(glm::round(color * mInvColorScale));
|
||||
}
|
||||
|
||||
std::string XiangClusterer::GetQuantizerDescriptor() const
|
||||
{
|
||||
return std::to_string(mQuantizedColorCount);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "BaseColorQuantizer.h"
|
||||
|
||||
class XiangClusterer :
|
||||
public BaseColorQuantizer
|
||||
{
|
||||
public:
|
||||
XiangClusterer(unsigned quantizedColorCount, glm::vec3 colorScale);
|
||||
// Quantize the colors to the given amount of colors, using the default RGB scaling, 0.5:1.0:0.25.
|
||||
XiangClusterer(unsigned quantizedColorCount);
|
||||
~XiangClusterer() override;
|
||||
|
||||
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
|
||||
std::string GetQuantizerDescriptor() const override;
|
||||
protected:
|
||||
virtual glm::vec3 ScaleColor(glm::u8vec3 color) const;
|
||||
virtual glm::u8vec3 ScaleBackColor(glm::vec3 color) const;
|
||||
unsigned mQuantizedColorCount;
|
||||
private:
|
||||
glm::vec3 mColorScale;
|
||||
glm::vec3 mInvColorScale;
|
||||
|
||||
struct ClusterColor
|
||||
{
|
||||
public:
|
||||
glm::u8vec3 originalColor;
|
||||
glm::vec3 color;
|
||||
float centerDistance;
|
||||
int clusterID;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "NormalQuantizer.h"
|
||||
#include "../../../core/BitHelper.h"
|
||||
|
||||
|
||||
NormalQuantizer::NormalQuantizer(unsigned8 bits): mBits(bits)
|
||||
{
|
||||
// Bits must be an even number, as there are two channels for an ONV
|
||||
assert(bits % 2 == 0);
|
||||
|
||||
// Precalculate the channel mask
|
||||
unsigned32 channelStartHS = 32 - SmallNormal::BITS / 2;
|
||||
mChannelMask = BitHelper::GetHSMask<unsigned32>(channelStartHS, channelStartHS + mBits / 2);
|
||||
}
|
||||
|
||||
std::map<SmallNormal, SmallNormal, NormalCompare>* NormalQuantizer::QuantizeMaterials(std::vector<SmallNormal> normals) const
|
||||
{
|
||||
std::map<SmallNormal, SmallNormal, NormalCompare>* res = new std::map<SmallNormal, SmallNormal, NormalCompare>();
|
||||
for (const SmallNormal& normal : normals)
|
||||
{
|
||||
res->insert(std::make_pair(normal, Quantize(normal)));
|
||||
}
|
||||
if (mBits <= QUANTIZE_ALL_UP_TO)
|
||||
{
|
||||
unsigned32 maxPerChannel = 1 << (mBits / 2);
|
||||
unsigned8 shift = (SmallNormal::BITS - mBits) / 2;
|
||||
for (unsigned32 x = 0; x < maxPerChannel; x++)
|
||||
for (unsigned32 y = 0; y < maxPerChannel; y++)
|
||||
{
|
||||
SmallNormal v;
|
||||
v.SetXComponent(x << shift);
|
||||
v.SetYComponent(y << shift);
|
||||
res->insert(std::make_pair(v, v));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SmallNormal NormalQuantizer::Quantize(const SmallNormal& normal) const
|
||||
{
|
||||
SmallNormal quantized;
|
||||
quantized.SetXComponent(normal.GetXComponent() & mChannelMask);
|
||||
quantized.SetYComponent(normal.GetYComponent() & mChannelMask);
|
||||
return quantized;
|
||||
}
|
||||
|
||||
std::string NormalQuantizer::GetQuantizerDescriptor() const
|
||||
{
|
||||
return std::to_string(mBits);
|
||||
}
|
||||
20
Research/scene/Material/MaterialQuantizer/NormalQuantizer.h
Normal file
20
Research/scene/Material/MaterialQuantizer/NormalQuantizer.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "../SmallNormal.h"
|
||||
#include "BaseQuantizer.h"
|
||||
|
||||
// Super simple quantizer: takes the original value, sets some bits to zero so that only the given number of bits remain
|
||||
class NormalQuantizer : public BaseQuantizer<SmallNormal, NormalCompare>, public QuickQuantizer<SmallNormal>
|
||||
{
|
||||
public:
|
||||
NormalQuantizer(unsigned8 bits);
|
||||
std::map<SmallNormal, SmallNormal, NormalCompare>* QuantizeMaterials(std::vector<SmallNormal> materials) const override;
|
||||
std::string GetQuantizerDescriptor() const override;
|
||||
SmallNormal Quantize(const SmallNormal& normal) const override;
|
||||
private:
|
||||
// if the number of bits is smaller than or equal to this number, all possible quantization values will be added.
|
||||
static const unsigned8 QUANTIZE_ALL_UP_TO = 14;
|
||||
unsigned8 mBits;
|
||||
unsigned32 mChannelMask;
|
||||
};
|
||||
38
Research/scene/Material/NearestFinder.h
Normal file
38
Research/scene/Material/NearestFinder.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
|
||||
template<typename T>
|
||||
struct NearestFinder
|
||||
{
|
||||
T operator()(const T& source, const std::vector<T>& values) const
|
||||
{
|
||||
assert(!values.empty());
|
||||
float min = T::Distance(source, values[0]);
|
||||
size_t minIndex = 0;
|
||||
for (size_t i = 0; i < values.size(); i++)
|
||||
{
|
||||
float distance = T::Distance(source, values[i]);
|
||||
if (distance < min)
|
||||
{
|
||||
min = distance;
|
||||
minIndex = i;
|
||||
}
|
||||
}
|
||||
return values[minIndex];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ParallelNearestFinder
|
||||
{
|
||||
T operator()(const T& source, const std::vector<T>& values) const
|
||||
{
|
||||
assert(!values.empty());
|
||||
std::vector<float> distances(values.size());
|
||||
tbb::parallel_for(size_t(0), values.size(), [&](size_t i) { distances[i] = T::Distance(source, values[i]); });
|
||||
return values[CollectionHelper::MinIndex(distances)];
|
||||
}
|
||||
};
|
||||
88
Research/scene/Material/SignedIntMaterial.h
Normal file
88
Research/scene/Material/SignedIntMaterial.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "../../core/BitHelper.h"
|
||||
|
||||
// Sort of like signed int, but the sign is stored in the first bit of the value, instead of storing it as 0xFFFFFFFF
|
||||
struct SignedIntMaterial
|
||||
{
|
||||
private:
|
||||
const static unsigned VALUE_MASK = 0x7FFFFFFF;
|
||||
const static unsigned SIGN_MASK = 0x80000000;
|
||||
|
||||
unsigned mValue;
|
||||
|
||||
inline unsigned GetValue() const { return mValue & VALUE_MASK; }
|
||||
inline void SetValue(unsigned value)
|
||||
{
|
||||
mValue &= ~VALUE_MASK;
|
||||
mValue |= value & VALUE_MASK;
|
||||
}
|
||||
|
||||
inline bool IsNegative() const { return (mValue & SIGN_MASK) != 0; }
|
||||
inline void SetIsNegative(bool isNegative)
|
||||
{
|
||||
mValue &= ~SIGN_MASK;
|
||||
if (isNegative) mValue |= SIGN_MASK;
|
||||
}
|
||||
public:
|
||||
SignedIntMaterial() : mValue(0) {}
|
||||
|
||||
SignedIntMaterial(unsigned value, bool negative)
|
||||
{
|
||||
SetValue(value);
|
||||
SetIsNegative(negative);
|
||||
}
|
||||
SignedIntMaterial(int value) : SignedIntMaterial(abs(value), value < 0) {}
|
||||
~SignedIntMaterial() {}
|
||||
|
||||
SignedIntMaterial& operator=(const int& source)
|
||||
{
|
||||
mValue = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<unsigned8> Serialize()
|
||||
{
|
||||
return BitHelper::SplitInBytes(this->operator unsigned());
|
||||
}
|
||||
|
||||
operator unsigned() const
|
||||
{
|
||||
return (unsigned)mValue;
|
||||
}
|
||||
|
||||
operator int() const
|
||||
{
|
||||
return (int)GetValue() * (IsNegative() ? -1 : 1);
|
||||
}
|
||||
|
||||
bool operator==(const SignedIntMaterial& other) const
|
||||
{
|
||||
return mValue == other.mValue;
|
||||
}
|
||||
|
||||
bool operator!=(const SignedIntMaterial& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const SignedIntMaterial& other) const
|
||||
{
|
||||
return this->operator int() < (int)other;
|
||||
}
|
||||
};
|
||||
|
||||
// Perfect hash
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<SignedIntMaterial>
|
||||
{
|
||||
size_t operator()(SignedIntMaterial const &value) const
|
||||
{
|
||||
return (unsigned)value;
|
||||
}
|
||||
};
|
||||
}
|
||||
185
Research/scene/Material/SmallNormal.h
Normal file
185
Research/scene/Material/SmallNormal.h
Normal file
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
#include "BaseMaterial.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Hashers.h"
|
||||
#include "../../core/Comparers.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
|
||||
// 8 bit normal representation using octahedron normal vectors
|
||||
// https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/
|
||||
// Technique proposed in:
|
||||
// Meyer, Q., Süßmuth, J., Sußner, G., Stamminger, M., & Greiner, G. (2010, June).
|
||||
// On Floating Point Normal Vectors.
|
||||
// In Computer Graphics Forum (Vol. 29, No. 4, pp. 1405-1409). Blackwell Publishing Ltd.
|
||||
class SmallNormal
|
||||
{
|
||||
public:
|
||||
static const unsigned8 BITS = 12;
|
||||
static const unsigned8 BYTES = BITS / 8 + (((BITS % 8) == 0) ? 0 : 1);
|
||||
static const unsigned8 CHANNELSPERPIXEL = BYTES;
|
||||
static const unsigned32 SINGLECOORDMASK = ((1 << (BITS / 2)) - 1);
|
||||
static const unsigned32 MAXCOORDVALUE = SINGLECOORDMASK;
|
||||
private:
|
||||
unsigned8 mData[BYTES];
|
||||
|
||||
inline unsigned32 normal() const
|
||||
{
|
||||
unsigned32 res = 0;
|
||||
BitHelper::JoinBytes(mData, res, 0, BYTES);
|
||||
return res;
|
||||
}
|
||||
inline void normal(unsigned32 value)
|
||||
{
|
||||
BitHelper::SplitInBytesAndMove(value, mData, 0, BYTES);
|
||||
}
|
||||
|
||||
inline static float DiscrMult() { return (float)MAXCOORDVALUE; }
|
||||
|
||||
// Wraps a vector between [-1, 1]
|
||||
glm::vec2 Wrap(glm::vec2 v) const
|
||||
{
|
||||
return (1.0f - glm::abs(glm::vec2(v.y, v.x))) * (glm::vec2(v.x >= 0 ? 1 : -1, v.y >= 0 ? 1 : -1));
|
||||
}
|
||||
public:
|
||||
|
||||
SmallNormal() { normal(0); }
|
||||
SmallNormal(glm::vec3 normal)
|
||||
{
|
||||
Set(normal);
|
||||
}
|
||||
|
||||
// Returns the x component of the octahedral normal vector as an unsigned integer between 0 and MAXCOORDVALUE
|
||||
unsigned32 GetXComponent() const { return normal() >> (BITS >> 1); }
|
||||
// Returns the y component of the octahedral normal vector as an unsigned integer between 0 and MAXCOORDVALUE
|
||||
unsigned32 GetYComponent() const { return normal() & SINGLECOORDMASK; }
|
||||
// Sets the x component of the octahedral normal vector as an unsigned integer. Should be between 0 and MAXCOORDVALUE
|
||||
void SetXComponent(unsigned32 value) { assert(value <= MAXCOORDVALUE); unsigned32 mNormal = normal(); mNormal &= SINGLECOORDMASK; mNormal |= value << (BITS / 2); normal(mNormal); }
|
||||
// Sets the y component of the octahedral normal vector as an unsigned integer. Should be between 0 and MAXCOORDVALUE
|
||||
void SetYComponent(unsigned32 value) { assert(value <= MAXCOORDVALUE); unsigned32 mNormal = normal(); mNormal &= ~SINGLECOORDMASK; mNormal |= value & SINGLECOORDMASK; normal(mNormal); }
|
||||
|
||||
void Set(glm::vec3 n)
|
||||
{
|
||||
n /= (abs(n.x) + abs(n.y) + abs(n.z));
|
||||
glm::vec2 res(n.x, n.y);
|
||||
if (n.z < 0)
|
||||
res = Wrap(res);
|
||||
res = res * 0.5f + 0.5f;
|
||||
SetXComponent((unsigned32)(res.x * DiscrMult()));
|
||||
SetYComponent((unsigned32)(res.y * DiscrMult()));
|
||||
|
||||
//if (n.x == 0 && n.y == 0 && n.z == 0) mNormal = 0;
|
||||
//else
|
||||
//{
|
||||
// n = glm::normalize(n);
|
||||
// // Calculate spherical coordinates, normalized between 0 and 1
|
||||
// glm::vec2 sphericalCoords = ((glm::vec2(atan2(n.y, n.x) / mPi, n.z) + 1.0f) * 0.5f);
|
||||
// // Store them using 4 bits per channel
|
||||
// SetXComponent((unsigned8)(sphericalCoords.x * 16.0f));
|
||||
// SetYComponent((unsigned8)(sphericalCoords.y * 16.0f));
|
||||
//}
|
||||
}
|
||||
glm::vec3 Get() const
|
||||
{
|
||||
glm::vec2 enc(((float)GetXComponent()) / DiscrMult(), ((float)GetYComponent()) / DiscrMult());
|
||||
enc = enc * 2.0f - 1.0f;
|
||||
|
||||
glm::vec3 n(enc.x, enc.y, 1.0 - abs(enc.x) - abs(enc.y));
|
||||
if (n.z < 0)
|
||||
{
|
||||
glm::vec2 wrapped = Wrap(enc);
|
||||
n.x = wrapped.x;
|
||||
n.y = wrapped.y;
|
||||
}
|
||||
return glm::normalize(n);
|
||||
|
||||
//// Extract the spherical coordinates
|
||||
//glm::vec2 sphericalCoords(((float)GetXComponent()) / 16.0f, ((float)GetYComponent()) / 16.0f);
|
||||
//// Normalize back
|
||||
//sphericalCoords = (sphericalCoords * 2.0f) - 1.0f;
|
||||
//glm::vec2 scth(sin(sphericalCoords.x), cos(sphericalCoords.y));
|
||||
//glm::vec2 scphi(sqrt(1.0 - sphericalCoords.y*sphericalCoords.y), sphericalCoords.y);
|
||||
//return glm::normalize(glm::vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y));
|
||||
}
|
||||
|
||||
unsigned32 GetValue() const { return normal(); }
|
||||
void SetValue(unsigned32 value) { normal(value); }
|
||||
|
||||
glm::u16vec3 GetProperties() const { return glm::u16vec3(0, 0, normal()); }
|
||||
void SetProperties(glm::u16vec3 props) { normal(props.z); }
|
||||
|
||||
static SmallNormal Average(const std::vector<SmallNormal>& normals)
|
||||
{
|
||||
glm::vec3 sum(0);
|
||||
for (SmallNormal n : normals)
|
||||
sum += n.Get();
|
||||
if (sum.x == 0 && sum.y == 0 && sum.z == 0) return SmallNormal();
|
||||
return SmallNormal(glm::normalize(sum / (float)normals.size()));
|
||||
}
|
||||
static SmallNormal WeightedAverage(const std::vector<SmallNormal>& normals, const std::vector<float>& weights)
|
||||
{
|
||||
glm::vec3 sum(0);
|
||||
for (size_t i = 0; i < normals.size(); i++)
|
||||
sum += normals[i].Get() * weights[i];
|
||||
if (sum.x == 0 && sum.y == 0 && sum.z == 0) return SmallNormal();
|
||||
return SmallNormal(glm::normalize(sum / CollectionHelper::Sum(weights)));
|
||||
}
|
||||
|
||||
static float Distance(const SmallNormal a, const SmallNormal b)
|
||||
{
|
||||
return glm::distance(a.Get(), b.Get()) * 0.5f;
|
||||
}
|
||||
|
||||
std::string GetTypeSuffix() const { return "n"; }
|
||||
|
||||
std::vector<unsigned8> Serialize() const
|
||||
{
|
||||
std::vector<unsigned8> res(BYTES);
|
||||
for (unsigned8 i = 0; i < BYTES; i++) res[i] = mData[i];
|
||||
return res;
|
||||
}
|
||||
void Deserialize(const std::vector<unsigned8>& value) {
|
||||
assert(value.size() == BYTES);
|
||||
for (unsigned8 i = 0; i < BYTES; i++) mData[i] = value[i];
|
||||
}
|
||||
|
||||
void Serialize(std::ostream& stream) const { Serializer<unsigned8*>::Serialize(&mData[0], BYTES, stream); }
|
||||
void Deserialize(std::istream& stream) { Serializer<unsigned8*>::Deserialize(&mData[0], BYTES, stream); }
|
||||
|
||||
bool operator==(const SmallNormal& n) const { return n.normal() == normal(); }
|
||||
bool operator!=(const SmallNormal& n) const { return !(n == *this); }
|
||||
|
||||
// Assignment operator, used for easy access to different kinds of materials
|
||||
SmallNormal& operator=(const unsigned& source) { normal(source); }
|
||||
operator unsigned() const { return normal(); }
|
||||
|
||||
static std::vector<SmallNormal> GetAll()
|
||||
{
|
||||
std::vector<SmallNormal> all(((size_t)1) << BITS);
|
||||
for (unsigned32 i = 0; i < (unsigned32)all.size(); i++)
|
||||
{
|
||||
SmallNormal n;
|
||||
n.SetValue(i);
|
||||
all[i] = n;
|
||||
}
|
||||
return all;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<SmallNormal> {
|
||||
size_t operator()(const SmallNormal &value) const {
|
||||
return value.GetValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct NormalCompare
|
||||
{
|
||||
bool operator()(const SmallNormal& n1, const SmallNormal& n2) const
|
||||
{
|
||||
return n1.GetValue() < n2.GetValue();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user