2026-03-26 20:43:17 +08:00
|
|
|
#include "Components/MeshRendererComponent.h"
|
|
|
|
|
|
|
|
|
|
#include "Core/Asset/ResourceManager.h"
|
|
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Components {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
std::string ToStdString(const Containers::String& value) {
|
|
|
|
|
return std::string(value.CStr());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 03:03:36 +08:00
|
|
|
std::string EncodeAssetRef(const Resources::AssetRef& assetRef) {
|
|
|
|
|
if (!assetRef.IsValid()) {
|
|
|
|
|
return std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ToStdString(assetRef.assetGuid.ToString()) + "," +
|
|
|
|
|
std::to_string(assetRef.localID) + "," +
|
|
|
|
|
std::to_string(static_cast<int>(assetRef.resourceType));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TryDecodeAssetRef(const std::string& value, Resources::AssetRef& outRef) {
|
|
|
|
|
const size_t firstComma = value.find(',');
|
|
|
|
|
const size_t secondComma = firstComma == std::string::npos ? std::string::npos : value.find(',', firstComma + 1);
|
|
|
|
|
if (firstComma == std::string::npos || secondComma == std::string::npos) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outRef.assetGuid = Resources::AssetGUID::ParseOrDefault(Containers::String(value.substr(0, firstComma).c_str()));
|
|
|
|
|
outRef.localID = static_cast<Resources::LocalID>(std::stoull(value.substr(firstComma + 1, secondComma - firstComma - 1)));
|
|
|
|
|
outRef.resourceType = static_cast<Resources::ResourceType>(std::stoi(value.substr(secondComma + 1)));
|
|
|
|
|
return outRef.IsValid();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 20:43:17 +08:00
|
|
|
std::vector<std::string> SplitMaterialPaths(const std::string& value) {
|
|
|
|
|
std::vector<std::string> paths;
|
2026-03-27 15:32:37 +08:00
|
|
|
if (value.empty()) {
|
|
|
|
|
return paths;
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-27 15:32:37 +08:00
|
|
|
size_t start = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
const size_t separator = value.find('|', start);
|
|
|
|
|
if (separator == std::string::npos) {
|
|
|
|
|
paths.push_back(value.substr(start));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
paths.push_back(value.substr(start, separator - start));
|
|
|
|
|
start = separator + 1;
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 03:03:36 +08:00
|
|
|
std::vector<Resources::AssetRef> SplitMaterialRefs(const std::string& value) {
|
|
|
|
|
std::vector<Resources::AssetRef> refs;
|
|
|
|
|
if (value.empty()) {
|
|
|
|
|
return refs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t start = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
const size_t separator = value.find('|', start);
|
|
|
|
|
const std::string token = separator == std::string::npos
|
|
|
|
|
? value.substr(start)
|
|
|
|
|
: value.substr(start, separator - start);
|
|
|
|
|
|
|
|
|
|
Resources::AssetRef ref;
|
|
|
|
|
TryDecodeAssetRef(token, ref);
|
|
|
|
|
refs.push_back(ref);
|
|
|
|
|
|
|
|
|
|
if (separator == std::string::npos) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
start = separator + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return refs;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 20:43:17 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
Resources::Material* MeshRendererComponent::GetMaterial(size_t index) const {
|
|
|
|
|
return index < m_materials.size() ? m_materials[index].Get() : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Resources::ResourceHandle<Resources::Material>& MeshRendererComponent::GetMaterialHandle(size_t index) const {
|
|
|
|
|
static const Resources::ResourceHandle<Resources::Material> kNullHandle;
|
|
|
|
|
return index < m_materials.size() ? m_materials[index] : kNullHandle;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 14:52:00 +08:00
|
|
|
const std::string& MeshRendererComponent::GetMaterialPath(size_t index) const {
|
|
|
|
|
static const std::string kEmptyPath;
|
|
|
|
|
return index < m_materialPaths.size() ? m_materialPaths[index] : kEmptyPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::SetMaterialPath(size_t index, const std::string& materialPath) {
|
|
|
|
|
EnsureMaterialSlot(index);
|
|
|
|
|
m_materialPaths[index] = materialPath;
|
|
|
|
|
if (materialPath.empty()) {
|
|
|
|
|
m_materials[index].Reset();
|
2026-04-02 03:03:36 +08:00
|
|
|
m_materialRefs[index].Reset();
|
2026-03-27 14:52:00 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_materials[index] = Resources::ResourceManager::Get().Load<Resources::Material>(materialPath.c_str());
|
2026-04-02 03:03:36 +08:00
|
|
|
if (!Resources::ResourceManager::Get().TryGetAssetRef(materialPath.c_str(), Resources::ResourceType::Material, m_materialRefs[index])) {
|
|
|
|
|
m_materialRefs[index].Reset();
|
|
|
|
|
}
|
2026-03-27 14:52:00 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 20:43:17 +08:00
|
|
|
void MeshRendererComponent::SetMaterial(size_t index, const Resources::ResourceHandle<Resources::Material>& material) {
|
|
|
|
|
EnsureMaterialSlot(index);
|
|
|
|
|
m_materials[index] = material;
|
|
|
|
|
m_materialPaths[index] = MaterialPathFromHandle(material);
|
2026-04-02 03:03:36 +08:00
|
|
|
if (m_materialPaths[index].empty() ||
|
|
|
|
|
!Resources::ResourceManager::Get().TryGetAssetRef(m_materialPaths[index].c_str(), Resources::ResourceType::Material, m_materialRefs[index])) {
|
|
|
|
|
m_materialRefs[index].Reset();
|
|
|
|
|
}
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::SetMaterial(size_t index, Resources::Material* material) {
|
|
|
|
|
SetMaterial(index, Resources::ResourceHandle<Resources::Material>(material));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::SetMaterials(const std::vector<Resources::ResourceHandle<Resources::Material>>& materials) {
|
|
|
|
|
m_materials = materials;
|
|
|
|
|
m_materialPaths.resize(materials.size());
|
2026-04-02 03:03:36 +08:00
|
|
|
m_materialRefs.resize(materials.size());
|
2026-03-26 20:43:17 +08:00
|
|
|
for (size_t i = 0; i < materials.size(); ++i) {
|
|
|
|
|
m_materialPaths[i] = MaterialPathFromHandle(materials[i]);
|
2026-04-02 03:03:36 +08:00
|
|
|
if (m_materialPaths[i].empty() ||
|
|
|
|
|
!Resources::ResourceManager::Get().TryGetAssetRef(m_materialPaths[i].c_str(), Resources::ResourceType::Material, m_materialRefs[i])) {
|
|
|
|
|
m_materialRefs[i].Reset();
|
|
|
|
|
}
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::ClearMaterials() {
|
|
|
|
|
m_materials.clear();
|
|
|
|
|
m_materialPaths.clear();
|
2026-04-02 03:03:36 +08:00
|
|
|
m_materialRefs.clear();
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::Serialize(std::ostream& os) const {
|
|
|
|
|
os << "materials=";
|
|
|
|
|
for (size_t i = 0; i < m_materialPaths.size(); ++i) {
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
os << "|";
|
|
|
|
|
}
|
|
|
|
|
os << m_materialPaths[i];
|
|
|
|
|
}
|
|
|
|
|
os << ";";
|
2026-04-02 03:03:36 +08:00
|
|
|
os << "materialRefs=";
|
|
|
|
|
for (size_t i = 0; i < m_materialRefs.size(); ++i) {
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
os << "|";
|
|
|
|
|
}
|
|
|
|
|
os << EncodeAssetRef(m_materialRefs[i]);
|
|
|
|
|
}
|
|
|
|
|
os << ";";
|
2026-03-26 20:43:17 +08:00
|
|
|
os << "castShadows=" << (m_castShadows ? 1 : 0) << ";";
|
|
|
|
|
os << "receiveShadows=" << (m_receiveShadows ? 1 : 0) << ";";
|
|
|
|
|
os << "renderLayer=" << m_renderLayer << ";";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::Deserialize(std::istream& is) {
|
|
|
|
|
ClearMaterials();
|
|
|
|
|
m_castShadows = true;
|
|
|
|
|
m_receiveShadows = true;
|
|
|
|
|
m_renderLayer = 0;
|
|
|
|
|
|
|
|
|
|
std::string token;
|
2026-04-02 03:03:36 +08:00
|
|
|
std::vector<Resources::AssetRef> pendingMaterialRefs;
|
2026-03-26 20:43:17 +08:00
|
|
|
while (std::getline(is, token, ';')) {
|
|
|
|
|
if (token.empty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t eqPos = token.find('=');
|
|
|
|
|
if (eqPos == std::string::npos) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string key = token.substr(0, eqPos);
|
|
|
|
|
const std::string value = token.substr(eqPos + 1);
|
|
|
|
|
|
|
|
|
|
if (key == "materials") {
|
|
|
|
|
m_materialPaths = SplitMaterialPaths(value);
|
|
|
|
|
m_materials.resize(m_materialPaths.size());
|
2026-04-02 03:03:36 +08:00
|
|
|
m_materialRefs.resize(m_materialPaths.size());
|
|
|
|
|
} else if (key == "materialRefs") {
|
|
|
|
|
pendingMaterialRefs = SplitMaterialRefs(value);
|
2026-03-26 20:43:17 +08:00
|
|
|
} else if (key == "castShadows") {
|
|
|
|
|
m_castShadows = (std::stoi(value) != 0);
|
|
|
|
|
} else if (key == "receiveShadows") {
|
|
|
|
|
m_receiveShadows = (std::stoi(value) != 0);
|
|
|
|
|
} else if (key == "renderLayer") {
|
|
|
|
|
m_renderLayer = static_cast<uint32_t>(std::stoul(value));
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-02 03:03:36 +08:00
|
|
|
|
|
|
|
|
if (pendingMaterialRefs.size() > m_materialPaths.size()) {
|
|
|
|
|
m_materialPaths.resize(pendingMaterialRefs.size());
|
|
|
|
|
m_materials.resize(pendingMaterialRefs.size());
|
|
|
|
|
m_materialRefs.resize(pendingMaterialRefs.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < m_materialPaths.size(); ++i) {
|
|
|
|
|
if (i < pendingMaterialRefs.size() && pendingMaterialRefs[i].IsValid()) {
|
|
|
|
|
m_materialRefs[i] = pendingMaterialRefs[i];
|
|
|
|
|
m_materials[i] = Resources::ResourceManager::Get().Load<Resources::Material>(pendingMaterialRefs[i]);
|
|
|
|
|
if (m_materials[i].Get() != nullptr) {
|
|
|
|
|
m_materialPaths[i] = MaterialPathFromHandle(m_materials[i]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetMaterialPath(i, m_materialPaths[i]);
|
|
|
|
|
}
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MeshRendererComponent::EnsureMaterialSlot(size_t index) {
|
|
|
|
|
if (index >= m_materials.size()) {
|
|
|
|
|
m_materials.resize(index + 1);
|
|
|
|
|
m_materialPaths.resize(index + 1);
|
2026-04-02 03:03:36 +08:00
|
|
|
m_materialRefs.resize(index + 1);
|
2026-03-26 20:43:17 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string MeshRendererComponent::MaterialPathFromHandle(const Resources::ResourceHandle<Resources::Material>& material) {
|
|
|
|
|
return material.Get() != nullptr ? ToStdString(material->GetPath()) : std::string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Components
|
|
|
|
|
} // namespace XCEngine
|