LumixEngine/src/engine/reflection.cpp

311 lines
7.7 KiB
C++

#include "engine/reflection.h"
#include "engine/allocator.h"
#include "engine/allocators.h"
#include "engine/hash.h"
#include "engine/log.h"
#include "engine/stream.h"
#include "engine/string.h"
#include "engine/world.h"
namespace Lumix::reflection {
namespace detail {
StringView normalizeTypeName(StringView type_name) {
StringView res = type_name;
if (startsWith(res, "struct ")) res.removePrefix(7);
if (startsWith(res, "Lumix::")) res.removePrefix(7);
while (res.size() > 0 && res[0] == ' ') res.removePrefix(1);
while (res.size() > 0 && res.back() == ' ') res.removeSuffix(1);
return res;
}
} // namespace detail
struct Context {
Module* first_module = nullptr;
RegisteredComponent component_bases[ComponentType::MAX_TYPES_COUNT];
u32 components_count = 0;
};
static Context& getContext() {
static Context ctx;
return ctx;
}
const char* declCodeToName(const char* decl_code) {
const char* c = decl_code;
while (*c) ++c;
while (*c != ':' && c > decl_code) --c;
if (*c == ':') ++c;
return c;
}
Array<FunctionBase*>& allFunctions() {
static Array<FunctionBase*> fncs(getGlobalAllocator());
return fncs;
}
Array<StructBase*>& allStructs() {
static Array<StructBase*> structs(getGlobalAllocator());
return structs;
}
ComponentBase::ComponentBase(IAllocator& allocator)
: props(allocator)
, functions(allocator)
{}
void ComponentBase::visit(IPropertyVisitor& visitor) const {
for (const PropertyBase* prop : props) {
prop->visit(visitor);
}
}
const ComponentBase* getComponent(ComponentType cmp_type) {
return getContext().component_bases[cmp_type.index].cmp;
}
const PropertyBase* getProperty(ComponentType cmp_type, const char* prop_name) {
const ComponentBase* cmp = getComponent(cmp_type);
if (!cmp) return nullptr;
for (PropertyBase* prop : cmp->props) {
if (equalStrings(prop->name, prop_name)) return prop;
}
return nullptr;
}
Module::Module(IAllocator& allocator)
: cmps(allocator)
, functions(allocator)
, events(allocator)
{}
builder::builder(IAllocator& allocator)
: allocator(allocator)
{
module = LUMIX_NEW(allocator, Module)(allocator);
}
void builder::registerCmp(ComponentBase* cmp) {
getContext().component_bases[cmp->component_type.index].cmp = cmp;
getContext().component_bases[cmp->component_type.index].name_hash = RuntimeHash(cmp->name);
getContext().component_bases[cmp->component_type.index].module_hash = RuntimeHash(module->name);
module->cmps.push(cmp);
}
ComponentType getComponentTypeFromHash(RuntimeHash hash)
{
for (u32 i = 0, c = getContext().components_count; i < c; ++i) {
if (getContext().component_bases[i].name_hash == hash) {
return {(i32)i};
}
}
ASSERT(false);
return {-1};
}
const PropertyBase* getPropertyFromHash(StableHash hash) {
const Context& ctx = getContext();
for (u32 i = 0; i < ctx.components_count; ++i) {
const RegisteredComponent& cmp = ctx.component_bases[i];
for (PropertyBase* prop : cmp.cmp->props) {
RollingStableHasher hasher;
hasher.begin();
hasher.update(cmp.cmp->name, stringLength(cmp.cmp->name));
hasher.update(prop->name, stringLength(prop->name));
if (hasher.end64() == hash) return prop;
}
}
return nullptr;
}
StableHash getPropertyHash(ComponentType cmp_type, const char* property_name) {
RollingStableHasher hasher;
hasher.begin();
const ComponentBase* cmp = getComponent(cmp_type);
if (!cmp) return StableHash();
hasher.update(cmp->name, stringLength(cmp->name));
hasher.update(property_name, stringLength(property_name));
return hasher.end64();
}
bool componentTypeExists(const char* id) {
Context& ctx = getContext();
const RuntimeHash name_hash(id);
for (u32 i = 0, c = ctx.components_count; i < c; ++i) {
if (ctx.component_bases[i].name_hash == name_hash) {
return true;
}
}
return false;
}
ComponentType getComponentType(const char* name)
{
Context& ctx = getContext();
const RuntimeHash name_hash(name);
for (u32 i = 0, c = ctx.components_count; i < c; ++i) {
if (ctx.component_bases[i].name_hash == name_hash) {
return {(i32)i};
}
}
if (ctx.components_count == ComponentType::MAX_TYPES_COUNT) {
logError("Too many component types");
return INVALID_COMPONENT_TYPE;
}
RegisteredComponent& type = ctx.component_bases[getContext().components_count];
type.name_hash = name_hash;
++ctx.components_count;
//logInfo("Component type ", name, ", hash ", name_hash.getHashValue());
return {i32(getContext().components_count - 1)};
}
Module* getFirstModule() {
return getContext().first_module;
}
void DynamicProperties::visit(IPropertyVisitor& visitor) const { visitor.visit(*this); }
Span<const RegisteredComponent> getComponents() {
return Span(getContext().component_bases, getContext().components_count);
}
struct RadiansAttribute : IAttribute
{
Type getType() const override { return RADIANS; }
};
struct MultilineAttribute : IAttribute
{
Type getType() const override { return MULTILINE; }
};
struct NoUIAttribute : IAttribute {
Type getType() const override { return NO_UI; }
};
builder build_module(const char* name) {
builder res(getGlobalAllocator());
Context& ctx = getContext();
res.module->next = ctx.first_module;
ctx.first_module = res.module;
res.module->name = name;
return res;
}
builder& builder::radiansAttribute() {
auto* a = LUMIX_NEW(allocator, RadiansAttribute);
last_prop->attributes.push(a);
return *this;
}
builder& builder::colorAttribute() {
auto* a = LUMIX_NEW(allocator, ColorAttribute);
last_prop->attributes.push(a);
return *this;
}
builder& builder::noUIAttribute() {
auto* a = LUMIX_NEW(allocator, NoUIAttribute);
last_prop->attributes.push(a);
return *this;
}
builder& builder::multilineAttribute() {
auto* a = LUMIX_NEW(allocator, MultilineAttribute);
last_prop->attributes.push(a);
return *this;
}
builder& builder::minAttribute(float value) {
auto* a = LUMIX_NEW(allocator, MinAttribute)(value);
last_prop->attributes.push(a);
return *this;
}
builder& builder::clampAttribute(float min, float max) {
auto* a = LUMIX_NEW(allocator, ClampAttribute)(min, max);
last_prop->attributes.push(a);
return *this;
}
builder& builder::resourceAttribute(ResourceType type) {
auto* a = LUMIX_NEW(allocator, ResourceAttribute)(type);
last_prop->attributes.push(a);
return *this;
}
builder& builder::end_array() {
array = nullptr;
last_prop = nullptr;
return *this;
}
builder& builder::icon(const char* icon) {
module->cmps.back()->icon = icon;
return *this;
}
void builder::addProp(PropertyBase* p) {
if (array) {
array->children.push(p);
}
else {
module->cmps.back()->props.push(p);
p->cmp = module->cmps.back();
}
last_prop = p;
}
BlobProperty::BlobProperty(IAllocator& allocator)
: PropertyBase(allocator)
{}
void BlobProperty::visit(struct IPropertyVisitor& visitor) const {
visitor.visit(*this);
}
void BlobProperty::getValue(ComponentUID cmp, u32 idx, OutputMemoryStream& stream) const {
getter(cmp.module, (EntityRef)cmp.entity, idx, stream);
}
void BlobProperty::setValue(ComponentUID cmp, u32 idx, InputMemoryStream& stream) const {
setter(cmp.module, (EntityRef)cmp.entity, idx, stream);
}
ArrayProperty::ArrayProperty(IAllocator& allocator)
: PropertyBase(allocator)
, children(allocator)
{}
u32 ArrayProperty::getCount(ComponentUID cmp) const {
return counter(cmp.module, (EntityRef)cmp.entity);
}
void ArrayProperty::addItem(ComponentUID cmp, u32 idx) const {
adder(cmp.module, (EntityRef)cmp.entity, idx);
}
void ArrayProperty::removeItem(ComponentUID cmp, u32 idx) const {
remover(cmp.module, (EntityRef)cmp.entity, idx);
}
void ArrayProperty::visit(struct IPropertyVisitor& visitor) const {
visitor.visit(*this);
}
void ArrayProperty::visitChildren(struct IPropertyVisitor& visitor) const {
for (PropertyBase* prop : children) {
prop->visit(visitor);
}
}
} // namespace Lumix::reflection