#ifndef ComponentWrapper_h__ #define ComponentWrapper_h__ #include #include #include "../Common.h" #include "../GLM.h" #include "Entity.h" #include "ComponentInfo.h" #include "DirtySet.h" #include "Util/Any.h" class World; struct ComponentWrapper { ComponentWrapper(const ComponentInfo& componentInfo, char* data, ::DirtyBitField* dirtyBitField, World* world); World* m_World; const ComponentInfo& Info; const ::EntityID EntityID; char* Data; ::DirtyBitField* DirtyBitField = nullptr; ComponentInfo::EnumType Enum(const char* fieldName, const char* enumKey); bool Dirty(DirtySetType type, const std::string& fieldName); void SetDirty(DirtySetType type, const std::string& fieldName, bool dirty = true); void SetAllDirty(const std::string& fieldName, bool dirty = true); template T& Field(const std::string& name) { const ComponentInfo::Field_t& field = Info.Fields.at(name); if (sizeof(T) > field.Stride) { std::stringstream message; message << "Type size of \"" << typeid(T).name() << "\" doesn't match size of component field \"" << Info.Name << "." << name << "\"!"; throw new std::runtime_error(message.str().c_str()); } return *reinterpret_cast(&Data[field.Offset]); } template void SetField(const std::string& name, const T& value) { Field(name) = value; SetAllDirty(name); } // Specialization for string literals template void SetField(const std::string& name, const char(&value)[N]) { SetField(name, std::string(value)); } void Copy(ComponentWrapper& destination) { // Copy trivial data memcpy(destination.Data, Data, Info.Stride); // Duplicate strings SolidifyStrings(destination); } // When component data has been copied, strings need to be reconstructed or they'll refer to the same data! static void SolidifyStrings(ComponentWrapper& component) { for (auto& name : component.Info.StringFields) { std::size_t offset = component.Info.Fields.at(name).Offset; std::string value = *reinterpret_cast(component.Data + offset); new (component.Data + offset) std::string(value); } } // This needs to be called to properly free component data, because strings. static void Destroy(ComponentInfo info, char* data) { // Call std::string destructors for (auto& name : info.StringFields) { std::size_t offset = info.Fields.at(name).Offset; auto field = reinterpret_cast(data + offset); field->~basic_string(); } } struct SubscriptProxy { friend struct ComponentWrapper; public: SubscriptProxy(ComponentWrapper* component, std::string fieldName) : m_Component(component) , m_FieldName(fieldName) { } ComponentWrapper* m_Component; std::string m_FieldName; public: // Return the integer value of an enum type key for this field ComponentInfo::EnumType Enum(const char* enumKey) { return m_Component->Enum(m_FieldName.c_str(), enumKey); } bool Dirty(DirtySetType type) { return m_Component->Dirty(type, m_FieldName); } void SetDirty(DirtySetType type, bool dirty = true) { m_Component->SetDirty(type, m_FieldName, dirty); } void SetAllDirty(bool dirty = true) { m_Component->SetAllDirty(m_FieldName, dirty); } operator const double&() { return m_Component->Field(m_FieldName); } operator const float&() { return m_Component->Field(m_FieldName); } operator const int&() { return m_Component->Field(m_FieldName); } operator const glm::vec3&() { return m_Component->Field(m_FieldName); } operator const glm::vec4&() { return m_Component->Field(m_FieldName); } operator const glm::quat&() { return m_Component->Field(m_FieldName); } operator const bool&() { return m_Component->Field(m_FieldName); } operator const std::string&() { return m_Component->Field(m_FieldName); } // Don't allow non-const references // If this wasn't deleted, the above overloads would still get called for some reason... template < typename T, typename = typename std::enable_if::value>::type > //operator T&() = delete; operator T&() { static_assert(constexpr(false), "https://github.com/teamfisk/TacticalZ/pull/212"); } // Value assignment template void operator=(const T& val) { m_Component->SetField(m_FieldName, val); } // Specialization for string literals template void operator=(const char(&val)[N]) { m_Component->SetField(m_FieldName, val); } }; SubscriptProxy operator[](const std::string& propertyName) { return SubscriptProxy(this, propertyName); } }; struct FIELDLOL { }; // lol template struct FieldBase : FIELDLOL { FieldBase(ComponentWrapper::SubscriptProxy& Proxy) : Proxy(Proxy) , Data(Proxy.m_Component->Field(Proxy.m_FieldName)) { } void SetAllDirty() { Proxy.SetAllDirty(); } FieldBase& operator=(const FieldBase& rhs) { Data = rhs.Data; SetAllDirty(); return *this; } FieldBase& operator=(const T& rhs) { Data = rhs; SetAllDirty(); return *this; } template FieldBase& operator+=(const T2& rhs) { Data += rhs; SetAllDirty(); return *this; } template FieldBase& operator-=(const T2& rhs) { Data -= rhs; SetAllDirty(); return *this; } template FieldBase& operator*=(const T2& rhs) { Data *= rhs; SetAllDirty(); return *this; } template FieldBase& operator/=(const T2& rhs) { Data /= rhs; SetAllDirty(); return *this; } template FieldBase& operator%=(const T2& rhs) { Data %= rhs; SetAllDirty(); return *this; } template FieldBase& operator&=(const T2& rhs) { Data &= rhs; SetAllDirty(); return *this; } template FieldBase& operator|=(const T2& rhs) { Data |= rhs; SetAllDirty(); return *this; } template FieldBase& operator^=(const T2& rhs) { Data ^= rhs; SetAllDirty(); return *this; } template FieldBase& operator<<=(const T2& rhs) { Data <<= rhs; SetAllDirty(); return *this; } template FieldBase& operator>>=(const T2& rhs) { Data >>= rhs; SetAllDirty(); return *this; } T operator+() const { return +Data; } T operator-() const { return -Data; } T operator~() const { return ~Data; } FieldBase& operator++() { Data++; SetAllDirty(); return *this; } T operator++(int) { T tmp = Data; operator++(); return tmp; } FieldBase& operator--() { Data--; SetAllDirty(); return *this; } T operator--(int) { T tmp = Data; operator--(); return tmp; } operator const T&() const { return Data; } const T& operator*() const { return operator const T&(); } protected: ComponentWrapper::SubscriptProxy Proxy; T& Data; }; template struct Field : FieldBase { using FieldBase::FieldBase; using FieldBase::operator=; }; template <> struct Field : FieldBase { using FieldBase::FieldBase; using FieldBase::operator=; glm::vec3::value_type x() const { return Data.x; } void x(glm::vec3::value_type val) { Data.x = val; SetAllDirty(); } glm::vec3::value_type y() const { return Data.y; } void y(glm::vec3::value_type val) { Data.y = val; SetAllDirty(); } glm::vec3::value_type z() const { return Data.z; } void z(glm::vec3::value_type val) { Data.z = val; SetAllDirty(); } template friend glm::vec3 operator+(const Field& lhs, const T& rhs) { return static_cast(lhs) + glm::vec3(rhs); } template friend glm::vec3 operator-(const Field& lhs, const T& rhs) { return static_cast(lhs) - glm::vec3(rhs); } template friend glm::vec3 operator*(const Field& lhs, const T& rhs) { return static_cast(lhs) * glm::vec3(rhs); } template friend glm::vec3 operator/(const Field& lhs, const T& rhs) { return static_cast(lhs) / glm::vec3(rhs); } template friend glm::vec3 operator+(const T& lhs, const Field& rhs) { return glm::vec3(lhs) + static_cast(rhs); } template friend glm::vec3 operator-(const T& lhs, const Field& rhs) { return glm::vec3(lhs) - static_cast(rhs); } template friend glm::vec3 operator*(const T& lhs, const Field& rhs) { return glm::vec3(lhs) * static_cast(rhs); } template friend glm::vec3 operator/(const T& lhs, const Field& rhs) { return glm::vec3(lhs) / static_cast(rhs); } friend glm::vec3& operator+=(glm::vec3& lhs, const Field& rhs) { lhs += *rhs; return lhs; } }; template <> struct Field : FieldBase { //Field(glm::vec4& Data) // : FieldBase(Data) //{ } using FieldBase::FieldBase; using FieldBase::operator=; glm::vec4::value_type x() const { return Data.x; } void x(glm::vec4::value_type val) { Data.x = val; SetAllDirty(); } glm::vec4::value_type y() const { return Data.y; } void y(glm::vec4::value_type val) { Data.y = val; SetAllDirty(); } glm::vec4::value_type z() const { return Data.z; } void z(glm::vec4::value_type val) { Data.z = val; SetAllDirty(); } glm::vec4::value_type w() const { return Data.w; } void w(glm::vec4::value_type val) { Data.w = val; SetAllDirty(); } template friend glm::vec4 operator+(const Field& lhs, const T& rhs) { return static_cast(lhs) + glm::vec4(rhs); } template friend glm::vec4 operator-(const Field& lhs, const T& rhs) { return static_cast(lhs) - glm::vec4(rhs); } template friend glm::vec4 operator*(const Field& lhs, const T& rhs) { return static_cast(lhs) * glm::vec4(rhs); } template friend glm::vec4 operator/(const Field& lhs, const T& rhs) { return static_cast(lhs) / glm::vec4(rhs); } template friend glm::vec4 operator+(const T& lhs, const Field& rhs) { return glm::vec4(lhs) + static_cast(rhs); } template friend glm::vec4 operator-(const T& lhs, const Field& rhs) { return glm::vec4(lhs) - static_cast(rhs); } template friend glm::vec4 operator*(const T& lhs, const Field& rhs) { return glm::vec4(lhs) * static_cast(rhs); } template friend glm::vec4 operator/(const T& lhs, const Field& rhs) { return glm::vec4(lhs) / static_cast(rhs); } friend glm::vec4& operator+=(glm::vec4& lhs, const Field& rhs) { lhs += *rhs; return lhs; } }; // A component wrapper that "owns" its data through a shared pointer struct SharedComponentWrapper : ComponentWrapper { SharedComponentWrapper(const ComponentInfo& componentInfo, boost::shared_array data) : ComponentWrapper(componentInfo, data.get(), nullptr, nullptr) , m_DataReference(data) { } private: boost::shared_array m_DataReference; }; // TODO: Move this to Tests once entity importing is finished class ComponentWrapperFactory { public: ComponentWrapperFactory() = default; ComponentWrapperFactory(std::string componentTypeName, unsigned int allocation = 0) { m_ComponentInfo.Name = componentTypeName; m_ComponentInfo.Meta = std::make_shared(); m_ComponentInfo.Meta->Allocation = allocation; } template void AddProperty(std::string fieldName, T defaultValue) { auto& field = m_ComponentInfo.Fields[fieldName]; field.Name = fieldName; field.Type = typeid(T).name(); field.Offset = m_ComponentInfo.Stride; field.Stride = sizeof(T); m_ComponentInfo.FieldsInOrder.push_back(field.Name); if (field.Type == typeid(std::string).name()) { field.Type = "string"; m_ComponentInfo.StringFields.push_back(field.Name); } m_ComponentInfo.Stride += sizeof(T); m_DefaultValues.push_back(std::make_pair(field, defaultValue)); } ComponentInfo& Finalize() { m_ComponentInfo.Defaults = boost::shared_array(new char[m_ComponentInfo.Stride]); std::size_t offset = 0; for (auto& pair : m_DefaultValues) { if (pair.first.Type == "string") { new (m_ComponentInfo.Defaults.get() + offset) std::string(*reinterpret_cast(pair.second.Data.get())); } else { memcpy(m_ComponentInfo.Defaults.get() + offset, pair.second.Data.get(), pair.second.Size); } offset += pair.second.Size; } return m_ComponentInfo; } operator ComponentInfo&() { return Finalize(); } private: ComponentInfo m_ComponentInfo; std::vector> m_DefaultValues; }; #endif