186 lines
6.3 KiB
C++
186 lines
6.3 KiB
C++
#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();
|
|
}
|
|
};
|