+ using HashType = uint32_t;
+
+ // Hash bit layout
+ // +--+--+--+--+--+--+--+
+ // |31|30|29|28|27|..| 0| bit index
+ // +--+--+--+--+--+--+--+
+ // \_/ - Set is used
+ // \______/ - Type enum
+ // \_______/ - Set ID
+ static const HashType SET_SHIFT{31};
+ static const HashType TYPE_SHIFT{28};
+ static const HashType SET_MASK{0x01u << SET_SHIFT};
+ static const HashType TYPE_MASK{0x07 << TYPE_SHIFT};
+ static const HashType SET_ID_MASK{0x0fffffff};
+
+ static HashType ToHash(Type type, bool set, HashType setIndex)
+ {
+ return ((set << SET_SHIFT) & SET_MASK) | ((static_cast<HashType>(type) << TYPE_SHIFT) & TYPE_MASK) | (setIndex & SET_ID_MASK);
+ }
+
+ static Attribute::Type TypeFromHash(HashType hash)
+ {
+ return static_cast<Type>((hash & TYPE_MASK) >> TYPE_SHIFT);
+ }
+
+ static bool SetFromHash(HashType hash)
+ {
+ return (hash & SET_SHIFT) != 0;
+ }
+
+ static HashType SetIdFromHash(HashType hash)
+ {
+ return (hash & SET_ID_MASK);
+ }
+
+ /**
+ * Convert to Type + setIndex, where setIndex is N for that attr, e.g. "JOINTS_1" => {JOINTS_N, 1}
+ */
+ static HashType HashFromString(const char* s, size_t len);
+
+ /**
+ * Convert to type only, there is no set for POSITION, NORMALS or TANGENT.
+ */
+ static Attribute::Type TargetFromString(const char* s, size_t len);