Initial commit: Final state of the master project
This commit is contained in:
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