Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ce5690e
Added Assimp include path to OvEditor
Gopmyc Apr 8, 2026
979f4c3
Added model material and texture import pipeline
Gopmyc Apr 8, 2026
d7746b6
Fixed skinning fallback for selection rendering
Gopmyc Apr 8, 2026
cd26037
Synced regenerated model materials across scene instances
Gopmyc Apr 8, 2026
91b8604
Reverted outline skinning fallback change
Gopmyc Apr 8, 2026
c223140
Fixed skinning detection in picking render pass
Gopmyc Apr 8, 2026
6e3633f
Added Assimp include path to OvEditor
Gopmyc Apr 8, 2026
8ca3243
Added model material and texture import pipeline
Gopmyc Apr 8, 2026
a00a0ea
Synced regenerated model materials across scene instances
Gopmyc Apr 8, 2026
3501087
Fixed skinning detection in picking render pass
Gopmyc Apr 8, 2026
08b4595
Extended model loading with embedded asset extraction
Gopmyc Apr 9, 2026
27aea9e
Added embedded model resource resolution in managers
Gopmyc Apr 9, 2026
28ed40d
Moved model material import workflow out of editor
Gopmyc Apr 9, 2026
625484e
Fixed runtime fallback for embedded model materials
Gopmyc Apr 9, 2026
cdf5e2d
Added embedded asset path helpers
Gopmyc Apr 9, 2026
0a24a23
Preserved explicit default materials in embedded fallback
Gopmyc Apr 10, 2026
d50b8df
Merge branch '303' of https://github.com/Gopmyc/Overload into 303
Gopmyc Apr 10, 2026
917976e
Fixed Assimp embedded texture lookup for named references
Gopmyc Apr 10, 2026
65764c8
Improved embedded material runtime binding and fallback handling
Gopmyc Apr 10, 2026
5f5169f
Fixed material fallback assignment for models without embedded data
Gopmyc Apr 10, 2026
c791903
Restored model material generation entries in Asset Browser
Gopmyc Apr 10, 2026
2a09ed9
Merged origin/main into 303
Gopmyc Apr 10, 2026
46baa24
Fixed false parallax activation for embedded model materials
Gopmyc Apr 11, 2026
41a9f46
Merged origin/main into 303
Gopmyc Apr 14, 2026
17645ac
Refactored model metadata loading and helper scoping
Gopmyc Apr 14, 2026
3e4580f
Added explicit error feedback when scene loading fails
Gopmyc Apr 14, 2026
0654ec1
Replaced embedded model pointer accessors with OptRef
Gopmyc Apr 14, 2026
deff986
Decoupled embedded material setup from OvCore asset paths
Gopmyc Apr 14, 2026
7c9b784
Simplified embedded texture manager runtime paths
Gopmyc Apr 14, 2026
466981f
Merged origin/main into 303
Gopmyc Apr 14, 2026
7344dd0
Replaced shader definition pointer access with optional reference
Gopmyc Apr 14, 2026
f533105
Renamed ModelMetadata struct for naming consistency
Gopmyc Apr 14, 2026
6d21bc8
Removed scene load error popup and kept log-only failure
Gopmyc Apr 14, 2026
b10513b
Merged origin/main into 303
Gopmyc Apr 14, 2026
c37e3ff
Simplified texture metadata defaults and embedded context typing
Gopmyc Apr 14, 2026
6968d38
Moved embedded material filling logic into material renderer
Gopmyc Apr 14, 2026
6107108
Fixed material inspector refresh after embedded fallback reassignment
Gopmyc Apr 14, 2026
165a2ba
Replaced deserialization state flag with explicit model update flag
Gopmyc Apr 14, 2026
0afbf82
Refreshed embedded materials when model updates request overwrite
Gopmyc Apr 14, 2026
22b8563
Simplified Lua ModelRenderer SetModel binding
Gopmyc Apr 14, 2026
8698f45
Merged origin/main into 303
Gopmyc Apr 15, 2026
b97565b
Simplified CModelRenderer model change flow
Gopmyc Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ namespace OvCore::ECS::Components
*/
void UpdateMaterialList();

/**
* Apply embedded model materials for empty/default slots
*/
void ApplyEmbeddedMaterialFallback();

/**
* Defines an element of the user matrix
* @param p_row
Expand Down Expand Up @@ -161,4 +166,4 @@ namespace OvCore::ECS::Components
{
static constexpr std::string_view Name = "class OvCore::ECS::Components::CMaterialRenderer";
};
}
}
52 changes: 52 additions & 0 deletions Sources/OvCore/src/OvCore/ECS/Components/CMaterialRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
* @licence: MIT
*/

#include <algorithm>
#include <format>
#include <string_view>
#include <tinyxml2.h>

#include <OvCore/ECS/Actor.h>
#include <OvCore/ECS/Components/CMaterialRenderer.h>
#include <OvCore/ECS/Components/CModelRenderer.h>
#include <OvCore/Global/ServiceLocator.h>
#include <OvCore/ResourceManagement/MaterialManager.h>
#include <OvRendering/Resources/Parsers/EmbeddedAssetPath.h>

#include <OvTools/Utils/PathParser.h>

Expand All @@ -24,6 +27,11 @@
#include <OvUI/Widgets/Texts/TextColored.h>
#include <OvUI/Widgets/Visual/Separator.h>

namespace
{
constexpr std::string_view kDefaultMaterialPath = ":Materials\\Default.ovmat";
Comment thread
Gopmyc marked this conversation as resolved.
Outdated
}

OvCore::ECS::Components::CMaterialRenderer::CMaterialRenderer(ECS::Actor & p_owner) : AComponent(p_owner)
{
m_materials.fill(nullptr);
Expand Down Expand Up @@ -142,6 +150,7 @@ void OvCore::ECS::Components::CMaterialRenderer::OnDeserialize(tinyxml2::XMLDocu
}

UpdateMaterialList();
ApplyEmbeddedMaterialFallback();

OvCore::Helpers::Serializer::DeserializeUint32(p_doc, p_node, "visibility_flags", reinterpret_cast<uint32_t&>(m_visibilityFlags));
}
Expand Down Expand Up @@ -249,6 +258,49 @@ void OvCore::ECS::Components::CMaterialRenderer::UpdateMaterialList()
}
}

void OvCore::ECS::Components::CMaterialRenderer::ApplyEmbeddedMaterialFallback()
{
auto* modelRenderer = owner.GetComponent<CModelRenderer>();
if (!modelRenderer)
{
return;
}

const auto* model = modelRenderer->GetModel();
if (!model)
{
return;
}

auto& materialManager = Global::ServiceLocator::Get<ResourceManagement::MaterialManager>();
auto* defaultMaterial = materialManager.GetResource(std::string{ kDefaultMaterialPath });

const uint8_t materialCount = static_cast<uint8_t>(std::min(
model->GetMaterialNames().size(),
static_cast<size_t>(kMaxMaterialCount)
));

for (uint8_t i = 0; i < materialCount; ++i)
{
auto* currentMaterial = GetMaterialAtIndex(i);
const bool shouldOverride = !currentMaterial;
if (!shouldOverride)
{
continue;
}

const auto embeddedMaterialPath = OvRendering::Resources::Parsers::MakeEmbeddedMaterialPath(model->path, i);
if (auto* embeddedMaterial = materialManager.GetResource(embeddedMaterialPath))
{
SetMaterialAtIndex(i, *embeddedMaterial);
}
else if (defaultMaterial)
{
SetMaterialAtIndex(i, *defaultMaterial);
}
}
}

void OvCore::ECS::Components::CMaterialRenderer::SetUserMatrixElement(uint32_t p_row, uint32_t p_column, float p_value)
{
if (p_row < 4 && p_column < 4)
Expand Down
3 changes: 3 additions & 0 deletions Sources/OvCore/src/OvCore/ECS/Components/CModelRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ OvCore::ECS::Components::CModelRenderer::CModelRenderer(ECS::Actor& p_owner) : A
m_modelChangedEvent += [this]
{
if (auto materialRenderer = owner.GetComponent<CMaterialRenderer>())
{
materialRenderer->UpdateMaterialList();
materialRenderer->ApplyEmbeddedMaterialFallback();
}

if (auto skinnedMeshRenderer = owner.GetComponent<CSkinnedMeshRenderer>())
skinnedMeshRenderer->NotifyModelChanged();
Expand Down
154 changes: 154 additions & 0 deletions Sources/OvCore/src/OvCore/ResourceManagement/MaterialManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,156 @@

#include "OvCore/ResourceManagement/MaterialManager.h"

#include <optional>
#include <string_view>

#include <OvCore/Global/ServiceLocator.h>
#include <OvCore/ResourceManagement/ModelManager.h>
#include <OvCore/ResourceManagement/ShaderManager.h>
#include <OvCore/ResourceManagement/TextureManager.h>
#include <OvRendering/Resources/Parsers/EmbeddedAssetPath.h>

namespace
{
constexpr std::string_view kStandardShaderPath = ":Shaders\\Standard.ovfx";

struct EmbeddedMaterialContext
{
std::string modelPath;
const OvRendering::Resources::Model* model = nullptr;
const OvRendering::Resources::EmbeddedMaterialData* materialData = nullptr;
};

std::optional<EmbeddedMaterialContext> ResolveEmbeddedMaterialContext(const std::filesystem::path& p_path)
{
using namespace OvRendering::Resources::Parsers;

const auto embeddedAssetPath = ParseEmbeddedAssetPath(p_path.string());
if (!embeddedAssetPath)
{
return std::nullopt;
}

const auto materialIndex = ParseEmbeddedMaterialIndex(embeddedAssetPath->assetName);
if (!materialIndex)
{
return std::nullopt;
}

auto* model = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::ModelManager>().GetResource(embeddedAssetPath->modelPath);
if (!model)
{
return std::nullopt;
}

const auto* embeddedMaterial = model->GetEmbeddedMaterial(materialIndex.value());
if (!embeddedMaterial)
{
return std::nullopt;
}

return EmbeddedMaterialContext{
.modelPath = embeddedAssetPath->modelPath,
.model = model,
.materialData = embeddedMaterial
};
}

bool BindEmbeddedTextureProperty(
OvCore::Resources::Material& p_material,
const EmbeddedMaterialContext& p_context,
const std::optional<uint32_t>& p_textureIndex,
const std::string_view p_uniformName
)
{
if (!p_textureIndex.has_value())
{
return false;
}

const auto* embeddedTexture = p_context.model->GetEmbeddedTexture(p_textureIndex.value());
if (!embeddedTexture)
{
return false;
}

const std::string extension = embeddedTexture->extension.empty() ? "bin" : embeddedTexture->extension;
const std::string texturePath = OvRendering::Resources::Parsers::MakeEmbeddedTexturePath(
p_context.modelPath,
p_textureIndex.value(),
extension
);

if (auto* texture = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::TextureManager>().GetResource(texturePath))
{
p_material.TrySetProperty(std::string{ p_uniformName }, texture);
return true;
}

return false;
}

bool ConfigureEmbeddedMaterial(
OvCore::Resources::Material& p_material,
const EmbeddedMaterialContext& p_context
)
{
auto* shader = OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::ShaderManager>()[std::string{ kStandardShaderPath }];
if (!shader)
{
return false;
}

const auto& materialData = *p_context.materialData;

p_material.SetShader(shader);
p_material.SetFeatures(OvRendering::Data::FeatureSet{});

p_material.TrySetProperty("u_Albedo", materialData.albedo);
p_material.TrySetProperty("u_Metallic", materialData.metallic);
p_material.TrySetProperty("u_Roughness", materialData.roughness);
p_material.TrySetProperty("u_EmissiveColor", materialData.emissiveColor);
p_material.TrySetProperty("u_EmissiveIntensity", materialData.emissiveIntensity);

const bool normalTextureBound = BindEmbeddedTextureProperty(p_material, p_context, materialData.normalTexture, "u_NormalMap");
const bool heightTextureBound = BindEmbeddedTextureProperty(p_material, p_context, materialData.heightTexture, "u_HeightMap");

BindEmbeddedTextureProperty(p_material, p_context, materialData.albedoTexture, "u_AlbedoMap");
BindEmbeddedTextureProperty(p_material, p_context, materialData.metallicTexture, "u_MetallicMap");
BindEmbeddedTextureProperty(p_material, p_context, materialData.roughnessTexture, "u_RoughnessMap");
BindEmbeddedTextureProperty(p_material, p_context, materialData.ambientOcclusionTexture, "u_AmbientOcclusionMap");
BindEmbeddedTextureProperty(p_material, p_context, materialData.emissiveTexture, "u_EmissiveMap");
BindEmbeddedTextureProperty(p_material, p_context, materialData.opacityTexture, "u_MaskMap");

if (materialData.normalMapping || normalTextureBound)
{
p_material.AddFeature("NORMAL_MAPPING");
}

if (materialData.parallaxMapping || heightTextureBound)
{
p_material.AddFeature("PARALLAX_MAPPING");
}

return true;
}
}

OvCore::Resources::Material * OvCore::ResourceManagement::MaterialManager::CreateResource(const std::filesystem::path & p_path)
{
if (const auto embeddedMaterialContext = ResolveEmbeddedMaterialContext(p_path))
{
auto* material = new OvCore::Resources::Material{};
if (ConfigureEmbeddedMaterial(*material, embeddedMaterialContext.value()))
{
const_cast<std::string&>(material->path) = p_path.string(); // Force the resource path to fit the given path
return material;
}

delete material;
return nullptr;
}

std::string realPath = GetRealPath(p_path).string();

Resources::Material* material = OvCore::Resources::Loaders::MaterialLoader::Create(realPath);
Expand All @@ -26,6 +174,12 @@ void OvCore::ResourceManagement::MaterialManager::DestroyResource(OvCore::Resour

void OvCore::ResourceManagement::MaterialManager::ReloadResource(OvCore::Resources::Material* p_resource, const std::filesystem::path& p_path)
{
if (const auto embeddedMaterialContext = ResolveEmbeddedMaterialContext(p_path))
{
ConfigureEmbeddedMaterial(*p_resource, embeddedMaterialContext.value());
return;
}

std::string realPath = GetRealPath(p_path).string();
OvCore::Resources::Loaders::MaterialLoader::Reload(*p_resource, realPath);
}
65 changes: 63 additions & 2 deletions Sources/OvCore/src/OvCore/ResourceManagement/ModelManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#include "OvCore/ResourceManagement/ModelManager.h"

#include <OvCore/Global/ServiceLocator.h>
#include <OvCore/ResourceManagement/MaterialManager.h>
#include <OvCore/ResourceManagement/TextureManager.h>
#include <OvRendering/Resources/Parsers/EmbeddedAssetPath.h>
#include <OvTools/Filesystem/IniFile.h>

OvRendering::Resources::Parsers::EModelParserFlags GetAssetMetadata(const std::string& p_path)
Comment thread
Gopmyc marked this conversation as resolved.
Outdated
Expand Down Expand Up @@ -49,10 +53,60 @@ OvRendering::Resources::Parsers::EModelParserFlags GetAssetMetadata(const std::s
return flags;
}

bool ShouldGenerateEmbeddedAssets(const std::string& p_path)
Comment thread
Gopmyc marked this conversation as resolved.
Outdated
{
auto metaFile = OvTools::Filesystem::IniFile(p_path + ".meta");
return metaFile.GetOrDefault("GENERATE_EMBEDDED_ASSETS", true);
}

template<typename TResourceManager, typename TIndexParser>
void ReloadEmbeddedResourcesForModelByType(
TResourceManager& p_resourceManager,
const std::string& p_modelPath,
TIndexParser p_parseIndex
)
{
for (auto& [resourcePath, resource] : p_resourceManager.GetResources())
{
(void)resource;
const auto embeddedAssetPath = OvRendering::Resources::Parsers::ParseEmbeddedAssetPath(resourcePath.string());
if (!embeddedAssetPath || embeddedAssetPath->modelPath != p_modelPath)
{
continue;
}

if (!p_parseIndex(embeddedAssetPath->assetName))
{
continue;
}

p_resourceManager.AResourceManager::ReloadResource(resourcePath);
}
}

void ReloadEmbeddedModelResources(const std::string& p_modelPath)
Comment thread
Gopmyc marked this conversation as resolved.
Outdated
{
ReloadEmbeddedResourcesForModelByType(
OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::MaterialManager>(),
p_modelPath,
[](const std::string& p_assetName) { return OvRendering::Resources::Parsers::ParseEmbeddedMaterialIndex(p_assetName).has_value(); }
);

ReloadEmbeddedResourcesForModelByType(
OvCore::Global::ServiceLocator::Get<OvCore::ResourceManagement::TextureManager>(),
p_modelPath,
[](const std::string& p_assetName) { return OvRendering::Resources::Parsers::ParseEmbeddedTextureIndex(p_assetName).has_value(); }
);
}

OvRendering::Resources::Model* OvCore::ResourceManagement::ModelManager::CreateResource(const std::filesystem::path& p_path)
{
std::string realPath = GetRealPath(p_path).string();
auto model = OvRendering::Resources::Loaders::ModelLoader::Create(realPath, GetAssetMetadata(realPath));
auto model = OvRendering::Resources::Loaders::ModelLoader::Create(
realPath,
GetAssetMetadata(realPath),
ShouldGenerateEmbeddedAssets(realPath)
);
if (model)
{
const_cast<std::string&>(model->path) = p_path.string(); // Force the resource path to fit the given path
Expand All @@ -69,5 +123,12 @@ void OvCore::ResourceManagement::ModelManager::DestroyResource(OvRendering::Reso
void OvCore::ResourceManagement::ModelManager::ReloadResource(OvRendering::Resources::Model* p_resource, const std::filesystem::path& p_path)
{
std::string realPath = GetRealPath(p_path).string();
OvRendering::Resources::Loaders::ModelLoader::Reload(*p_resource, realPath, GetAssetMetadata(realPath));
OvRendering::Resources::Loaders::ModelLoader::Reload(
*p_resource,
realPath,
GetAssetMetadata(realPath),
ShouldGenerateEmbeddedAssets(realPath)
);

ReloadEmbeddedModelResources(p_path.string());
}
Loading