/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include
#include
#include
#include <3d/ecs/components/material_component.h>
#include <3d/ecs/components/name_component.h>
#include <3d/ecs/components/render_handle_component.h>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ecs_listener.h"
#include
SCENE_BEGIN_NAMESPACE()
class EcsObject
: public META_NS::ObjectFwd {
using ObjectBase =
META_NS::ObjectFwd;
META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(
SCENE_NS::IEcsObject, uint8_t, ConnectionStatus, SCENE_NS::IEcsObject::ECS_OBJ_STATUS_DISCONNECTED)
bool Build(const IMetadata::Ptr& /*data*/) override
{
return true;
}
CORE_NS::IEcs::Ptr GetEcs() const override
{
return ecsInstance_;
}
CORE_NS::Entity GetEntity() const override
{
return entity_;
}
void SetEntity(CORE_NS::IEcs::Ptr ecs, CORE_NS::Entity entity) override
{
BindObject(nullptr, entity_);
// Should we allow setting a valid ecs with invalid entity?
if (CORE_NS::EntityUtil::IsValid(entity)) {
BindObject(ecs, entity);
}
}
void SetCommonListener(BASE_NS::shared_ptr listener) override
{
if (auto commonListener = listener_.lock()) {
for (auto& componentManager : componentManagers_) {
commonListener->RemoveEntity(entity_, GetSelf(), *componentManager->manager);
}
componentManagers_.clear();
}
listener_ = listener;
}
void BindObject(CORE_NS::IEcs::Ptr ecsInstance, CORE_NS::Entity entity) override
{
if (ecsInstance && CORE_NS::EntityUtil::IsValid(entity)) {
ecsInstance_ = ecsInstance;
entity_ = ecsInstance->GetEntityManager().GetReferenceCounted(entity);
assert(componentManagers_.size() == 0);
UpdateMetaCache();
if (auto commonListener = listener_.lock()) {
for (auto& componentManager : componentManagers_) {
commonListener->AddEntity(entity, GetSelf(), *componentManager->manager);
}
}
// Nb. we may reach connected state even we don't have listener or single valid component to listen
META_ACCESS_PROPERTY(ConnectionStatus)->SetValue(SCENE_NS::IEcsObject::ECS_OBJ_STATUS_CONNECTED);
} else if (ecsInstance_) {
if (auto commonListener = listener_.lock()) {
for (auto& componentManager : componentManagers_) {
commonListener->RemoveEntity(
entity, GetSelf(), *componentManager->manager);
}
}
for (auto& componentManager : componentManagers_) {
componentManager->valueManager->RemoveAll();
for (auto& prop : componentManager->properties) {
RemoveProperty(prop);
}
}
attachments_.clear();
componentManagers_.clear();
entity_ = {};
ecsInstance_.reset();
META_ACCESS_PROPERTY(ConnectionStatus)->SetValue(SCENE_NS::IEcsObject::ECS_OBJ_STATUS_DISCONNECTED);
}
}
~EcsObject() override
{
BindObject(nullptr, entity_);
}
void DoEntityEvent(CORE_NS::IEcs::EntityListener::EventType type, const CORE_NS::Entity& /*entity*/) override
{
switch (type) {
case CORE_NS::IEcs::EntityListener::EventType::CREATED: {
break;
}
case CORE_NS::IEcs::EntityListener::EventType::ACTIVATED: {
break;
}
case CORE_NS::IEcs::EntityListener::EventType::DEACTIVATED: {
break;
}
case CORE_NS::IEcs::EntityListener::EventType::DESTROYED: {
// detach from the entity. (stop listening for events, and detach the properties)
BindObject(nullptr, entity_);
break;
}
};
}
void DoComponentEvent(CORE_NS::IEcs::ComponentListener::EventType type,
const CORE_NS::IComponentManager& componentManager, const CORE_NS::Entity& entity) override
{
if (entity!=entity_) {
CORE_LOG_F("Invalid event for %s entity %" PRIx64 " handler object entity %" PRIx64,BASE_NS::string(componentManager.GetName()).c_str(),entity.id,CORE_NS::Entity(entity_).id);
return;
}
if (type == CORE_NS::IEcs::ComponentListener::EventType::CREATED) {
// new component added. add it's properties
}
if (type == CORE_NS::IEcs::ComponentListener::EventType::DESTROYED) {
// component removed. remove the properties.
}
if (type == CORE_NS::IEcs::ComponentListener::EventType::MODIFIED) {
// possibly re-evaluate because of change to one of the components..
//CORE_LOG_F("[%p] Modified event for %s entity %llx handler object entity %llx",this, BASE_NS::string(componentManager.GetName()).c_str(),entity.id,CORE_NS::Entity(entity_).id);
UpdateProperties(componentManager);
}
}
void UpdateMetaCache() const
{
auto obj = const_cast(this);
obj->UpdateMetaCache();
}
void DefineTargetProperties(
BASE_NS::unordered_map> names) override
{
CORE_LOG_E("DefineTargetProperties");
if (ecsInstance_) {
UpdateMetaCache();
}
}
BASE_NS::unordered_map> names_;
void UpdateMetaCache()
{
CORE_LOG_E("UpdateMetaCache");
BASE_NS::vector managers;
ecsInstance_->GetComponents(entity_, managers);
bool addAll = names_.empty();
for (auto cm : managers) {
auto handle = cm->GetData(entity_);
if (!handle) {
break;
}
if (auto* papi = handle->Owner()) {
BASE_NS::shared_ptr m;
for(auto&& v : componentManagers_) {
if(v->manager == cm) {
m = v;
break;
}
}
if(!m) {
m = ComponentManager::Create(*cm);
componentManagers_.push_back(m);
}
// Add a special "Name" property containing the name from a NameComponent
if (cm->GetName() == "NameComponent") {
for (int i = 0; i < papi->PropertyCount(); i++) {
const auto md = papi->MetaData(i);
if (md->type == CORE_NS::PropertyType::STRING_T && md->name == "name") {
m->valueManager->ConstructValue(META_NS::EnginePropertyParams{handle, *md}, {});
auto prop = GetPropertyByName("Name");
if (!prop) {
prop = META_NS::ConstructProperty("Name");
AddProperty(prop);
}
SetEngineValueToProperty(prop, m->valueManager->GetEngineValue("name"));
m->properties.push_back(prop);
}
}
}
BASE_NS::string name { cm->GetName() };
BASE_NS::vector values;
AddEngineValuesRecursively(m->valueManager, handle, META_NS::EngineValueOptions{name, &values, true});
for(auto&& v : values) {
if(auto prop = GetPropertyByName(v->GetName())) {
SetEngineValueToProperty(prop, v);
} else {
AddProperty(m->valueManager->ConstructProperty(v->GetName()));
}
}
}
}
}
void UpdateProperties(const CORE_NS::IComponentManager& cm)
{
auto* papi = cm.GetData(entity_)->Owner();
if (!papi) {
return;
}
for (auto& active_mgr : componentManagers_) {
if (active_mgr->manager == &cm) {
active_mgr->valueManager->Sync(META_NS::EngineSyncDirection::FROM_ENGINE);
return;
}
}
}
virtual BASE_NS::vector GetAttachments() override
{
return attachments_;
}
void AddAttachment(CORE_NS::Entity entity) override
{
attachments_.push_back(entity);
}
void RemoveAttachment(CORE_NS::Entity entity) override
{
// assuming that entities can be there only once
for (auto attachment = attachments_.cbegin(); attachment != attachments_.cend(); attachment++) {
if (attachment->id == entity.id) {
attachments_.erase(attachment);
break;
}
}
}
void Activate() override {}
void Deactivate() override {}
public:
EcsObject(const META_NS::IMetadata::Ptr& data) {}
EcsObject() {}
private:
CORE_NS::IEcs::Ptr ecsInstance_ {};
CORE_NS::EntityReference entity_;
BASE_NS::weak_ptr listener_ {};
uint64_t firstOffset_ { 0 };
struct ComponentManager {
static BASE_NS::shared_ptr Create(CORE_NS::IComponentManager& manager)
{
return BASE_NS::shared_ptr { new ComponentManager { &manager,
META_NS::GetObjectRegistry().Create(
META_NS::ClassId::EngineValueManager) } };
}
CORE_NS::IComponentManager* manager;
META_NS::IEngineValueManager::Ptr valueManager;
BASE_NS::vector properties;
};
BASE_NS::vector> componentManagers_;
BASE_NS::vector attachments_;
};
void RegisterEcsObject()
{
META_NS::GetObjectRegistry().RegisterObjectType();
}
void UnregisterEcsObject()
{
META_NS::GetObjectRegistry().UnregisterObjectType();
}
SCENE_END_NAMESPACE()