* 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 "mesh.h"
#include <3d/ecs/components/mesh_component.h>
#include <3d/util/intf_picking.h>
#include <meta/api/make_callback.h>
#include "../util_interfaces.h"
#include "submesh.h"
SCENE_BEGIN_NAMESPACE()
void Mesh::Destroy()
{
if (auto arr = GetSubmeshesOrNull()) {
for (auto&& v : arr->GetValue()) {
if (auto i = interface_cast<META_NS::INotifyOnChange>(v)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
if (auto p = interface_cast<ISubMeshInternal>(v)) {
p->SetMaterialOverride(nullptr);
}
}
}
Super::Destroy();
}
void Mesh::SetMaterialOverride(const IMaterial::Ptr& material)
{
overrideMaterial_ = material;
if (auto scene = GetInternalScene()) {
const auto submeshes = SubMeshes()->GetValue();
scene->RunDirectlyOrInTask([&] {
for (auto&& s : submeshes) {
if (auto internal = interface_cast<ISubMeshInternal>(s)) {
internal->SetMaterialOverride(material);
}
}
});
}
}
IMaterial::Ptr Mesh::GetMaterialOverride() const
{
return overrideMaterial_;
}
void Mesh::SetInternalMesh(IInternalMesh::Ptr m)
{
submeshEvent_.Unsubscribe();
mesh_ = std::move(m);
if (mesh_) {
submeshEvent_.Subscribe<META_NS::IOnChanged>(
mesh_->SubMeshes()->OnChanged(), [this] { UpdateSubmeshes(SubMeshes()); });
}
}
IEcsObject::Ptr Mesh::GetMeshEcsObject() const
{
auto comp = interface_cast<IEcsObjectAccess>(mesh_);
return comp ? comp->GetEcsObject() : nullptr;
}
bool Mesh::SetEcsObject(const IEcsObject::Ptr& obj)
{
if (mesh_ && obj != GetEcsObject()) {
SetInternalMesh({});
}
if (Super::SetEcsObject(obj)) {
if (auto att = GetSelf<META_NS::IAttach>()) {
if (auto attc = att->GetAttachmentContainer(false)) {
if (auto c = attc->FindByName<IInternalMesh>("MeshComponent")) {
SetInternalMesh(c);
}
}
}
if (!mesh_) {
auto p = META_NS::GetObjectRegistry().Create<IInternalMesh>(ClassId::MeshComponent);
if (auto acc = interface_cast<IEcsObjectAccess>(p)) {
if (acc->SetEcsObject(obj)) {
SetInternalMesh(p);
if (auto scene = obj->GetScene()) {
auto ecs = scene->GetEcsContext().GetNativeEcs();
meshEntity_ = ecs->GetEntityManager().GetReferenceCounted(obj->GetEntity());
}
}
}
}
}
return mesh_ != nullptr;
}
CORE_NS::Entity Mesh::CreateEntity(const IInternalScene::Ptr& scene)
{
const auto& ecs = scene->GetEcsContext().GetNativeEcs();
const auto meshManager = CORE_NS::GetManager<CORE3D_NS::IMeshComponentManager>(*ecs);
if (!meshManager) {
return {};
}
auto ent = ecs->GetEntityManager().Create();
meshManager->Create(ent);
return ent;
}
bool Mesh::InitDynamicProperty(const META_NS::IProperty::Ptr& p, BASE_NS::string_view path)
{
if (p->GetName() == "SubMeshes") {
return UpdateSubmeshes(META_NS::ArrayProperty<ISubMesh::Ptr>(p));
}
return false;
}
void Mesh::RecalculateAABB()
{
CORE3D_NS::MinAndMax minMax;
for (const auto& submesh : SubMeshes()->GetValue()) {
minMax.minAABB = min(minMax.minAABB, submesh->AABBMin()->GetValue());
minMax.maxAABB = max(minMax.maxAABB, submesh->AABBMax()->GetValue());
}
mesh_->AABBMin()->SetValue(minMax.minAABB);
mesh_->AABBMax()->SetValue(minMax.maxAABB);
}
bool Mesh::UpdateSubmeshes(META_NS::ArrayProperty<ISubMesh::Ptr> prop)
{
if (!prop) {
return false;
}
auto& r = META_NS::GetObjectRegistry();
BASE_NS::vector<ISubMesh::Ptr> orig = prop->GetValue();
BASE_NS::vector<ISubMesh::Ptr> subs;
auto msubs = mesh_->SubMeshes()->GetValue();
for (size_t i = 0; i != msubs.size(); ++i) {
ISubMesh::Ptr t;
if (i < orig.size()) {
t = orig[i];
} else {
t = r.Create<ISubMesh>(ClassId::SubMesh);
if (auto si = interface_cast<IArrayElementIndex>(t)) {
si->SetIndex(i);
}
if (auto i = interface_cast<META_NS::INotifyOnChange>(t)) {
i->OnChanged()->AddHandler(
META_NS::MakeCallback<META_NS::IOnChanged>([this] { RecalculateAABB(); }), uintptr_t(this));
}
}
if (auto access = interface_cast<IEcsObjectAccess>(t)) {
access->SetEcsObject(GetEcsObject());
subs.push_back(t);
} else {
CORE_LOG_E("Failed to create submesh for mesh");
return false;
}
}
if (orig.size() > subs.size()) {
for (auto it = orig.begin() + subs.size(); it != orig.end(); ++it) {
if (auto i = interface_cast<META_NS::INotifyOnChange>(*it)) {
i->OnChanged()->RemoveHandler(uintptr_t(this));
}
}
}
RefreshSubmeshes(subs);
prop->SetValue(subs);
return true;
}
void Mesh::RefreshSubmeshes(const BASE_NS::vector<ISubMesh::Ptr>& subs)
{
if (auto scene = GetInternalScene()) {
scene->RunDirectlyOrInTask([&] {
for (auto&& s : subs) {
if (auto internal = interface_cast<ISubMeshInternal>(s)) {
internal->SetMaterialOverride(overrideMaterial_);
}
if (auto m = interface_cast<META_NS::IMetadata>(s)) {
for (auto&& p : m->GetProperties()) {
scene->SyncProperty(p, META_NS::EngineSyncDirection::AUTO);
}
}
}
});
}
}
META_NS::ArrayProperty<ISubMesh::Ptr> Mesh::GetSubmeshesOrNull()
{
return META_NS::ArrayProperty<ISubMesh::Ptr>(GetProperty("SubMeshes", META_NS::MetadataQuery::EXISTING));
}
SCENE_END_NAMESPACE()