Skip to content

Commit fe917ea

Browse files
committed
WIP: better handler for materials
1 parent 986ab3a commit fe917ea

6 files changed

Lines changed: 320 additions & 44 deletions

File tree

Core/Source/Lux/Asset/AssetManager/EditorAssetManager.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ namespace Lux
5858
manager.RegisterDependency(materialHandle, asset->Handle);
5959
}
6060
}
61+
62+
void RegisterMeshSourceMaterialDependencies(EditorAssetManager& manager, const Ref<MeshSource>& meshSource)
63+
{
64+
if (!meshSource)
65+
return;
66+
67+
for (AssetHandle materialHandle : meshSource->GetMaterials())
68+
{
69+
if (materialHandle)
70+
manager.RegisterDependency(materialHandle, meshSource->Handle);
71+
}
72+
}
6173
}
6274

6375
EditorAssetManager::EditorAssetManager()
@@ -810,6 +822,12 @@ namespace Lux
810822
return;
811823
}
812824

825+
if (auto meshSource = asset.As<MeshSource>())
826+
{
827+
RegisterMeshSourceMaterialDependencies(*this, meshSource);
828+
return;
829+
}
830+
813831
if (auto mesh = asset.As<Mesh>())
814832
{
815833
if (mesh->GetMeshSource())

Core/Source/Lux/Asset/AssimpMeshImporter.cpp

Lines changed: 143 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
#include <fstream>
2727
#include <unordered_map>
2828
#include <unordered_set>
29+
#include <vector>
30+
31+
#define STB_IMAGE_WRITE_IMPLEMENTATION
32+
#include <GLFW/deps/stb_image_write.h>
2933

3034
namespace Lux
3135
{
@@ -70,6 +74,78 @@ namespace Lux
7074
return !texturePath.empty() && texturePath[0] == '*';
7175
}
7276

77+
static std::string ToLower(std::string value)
78+
{
79+
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c)
80+
{
81+
return (char)std::tolower(c);
82+
});
83+
return value;
84+
}
85+
86+
static std::string GetEmbeddedTextureExtension(const aiTexture* texture)
87+
{
88+
if (!texture)
89+
return ".png";
90+
91+
std::string hint = ToLower(texture->achFormatHint);
92+
if (hint == "jpeg")
93+
hint = "jpg";
94+
95+
if (hint == "png" || hint == "jpg" || hint == "bmp" || hint == "tga")
96+
return "." + hint;
97+
98+
return ".png";
99+
}
100+
101+
static std::filesystem::path GetImportedTexturePath(const std::filesystem::path& meshPath, const std::string& textureName, const std::string& extension)
102+
{
103+
Ref<EditorAssetManager> editorAssetManager = Project::GetEditorAssetManager();
104+
if (!editorAssetManager)
105+
return {};
106+
107+
std::filesystem::path meshRelativePath = editorAssetManager->GetRelativePath(meshPath).lexically_normal();
108+
std::filesystem::path textureRelativeDirectory;
109+
if (!meshRelativePath.empty() && !meshRelativePath.is_absolute())
110+
textureRelativeDirectory = meshRelativePath.parent_path() / "Textures";
111+
else
112+
textureRelativeDirectory = std::filesystem::path("Textures") / SanitizeAssetFilename(meshPath.stem().string());
113+
114+
const std::string fileName = SanitizeAssetFilename(meshPath.stem().string() + "_" + textureName) + extension;
115+
return (textureRelativeDirectory / fileName).lexically_normal();
116+
}
117+
118+
static bool WriteEmbeddedTextureToFile(const aiTexture* texture, const std::filesystem::path& filesystemPath)
119+
{
120+
if (!texture)
121+
return false;
122+
123+
std::error_code ec;
124+
std::filesystem::create_directories(filesystemPath.parent_path(), ec);
125+
126+
if (texture->mHeight == 0)
127+
{
128+
std::ofstream stream(filesystemPath, std::ios::binary);
129+
if (!stream.is_open())
130+
return false;
131+
132+
stream.write(reinterpret_cast<const char*>(texture->pcData), texture->mWidth);
133+
return stream.good();
134+
}
135+
136+
std::vector<uint8_t> rgba(texture->mWidth * texture->mHeight * 4);
137+
for (uint32_t i = 0; i < texture->mWidth * texture->mHeight; i++)
138+
{
139+
const aiTexel& texel = texture->pcData[i];
140+
rgba[i * 4 + 0] = texel.r;
141+
rgba[i * 4 + 1] = texel.g;
142+
rgba[i * 4 + 2] = texel.b;
143+
rgba[i * 4 + 3] = texel.a;
144+
}
145+
146+
return stbi_write_png(filesystemPath.string().c_str(), texture->mWidth, texture->mHeight, 4, rgba.data(), texture->mWidth * 4) != 0;
147+
}
148+
73149
static std::filesystem::path ResolveTexturePath(const std::filesystem::path& meshPath, const aiString& assimpPath)
74150
{
75151
std::filesystem::path texturePath = std::filesystem::path(assimpPath.C_Str());
@@ -117,42 +193,85 @@ namespace Lux
117193
paths.emplace_back(resolvedPath);
118194
}
119195

120-
static AssetHandle ImportTextureReference(const std::filesystem::path& meshPath, const aiScene* scene, aiMaterial* material, const std::initializer_list<aiTextureType>& textureTypes, std::unordered_map<std::string, AssetHandle>& textureCache)
196+
static AssetHandle ImportTexturePathReference(const std::filesystem::path& meshPath, const aiScene* scene, const aiString& texturePath, std::unordered_map<std::string, AssetHandle>& textureCache)
121197
{
122198
Ref<EditorAssetManager> editorAssetManager = Project::GetEditorAssetManager();
123-
if (!editorAssetManager || !material)
199+
if (!editorAssetManager)
124200
return 0;
125201

126-
for (aiTextureType textureType : textureTypes)
127-
{
128-
if (material->GetTextureCount(textureType) == 0)
129-
continue;
202+
const std::string rawPath = texturePath.C_Str();
203+
if (rawPath.empty())
204+
return 0;
130205

131-
aiString texturePath;
132-
if (material->GetTexture(textureType, 0, &texturePath) != AI_SUCCESS)
133-
continue;
206+
if (const aiTexture* embeddedTexture = scene ? scene->GetEmbeddedTexture(rawPath.c_str()) : nullptr)
207+
{
208+
const std::string cacheKey = meshPath.lexically_normal().generic_string() + "::embedded::" + rawPath;
209+
if (auto it = textureCache.find(cacheKey); it != textureCache.end())
210+
return it->second;
134211

135-
const std::string rawPath = texturePath.C_Str();
136-
if (rawPath.empty() || IsEmbeddedTextureReference(rawPath) || (scene && scene->GetEmbeddedTexture(rawPath.c_str())))
137-
continue;
212+
const std::string extension = GetEmbeddedTextureExtension(embeddedTexture);
213+
const std::filesystem::path textureRelativePath = GetImportedTexturePath(meshPath, rawPath, extension);
214+
if (textureRelativePath.empty())
215+
return 0;
138216

139-
std::filesystem::path resolvedPath = FindTexturePath(meshPath, texturePath);
140-
if (resolvedPath.empty())
217+
const std::filesystem::path textureFilesystemPath = Project::GetActiveAssetDirectory() / textureRelativePath;
218+
if (!std::filesystem::exists(textureFilesystemPath) && !WriteEmbeddedTextureToFile(embeddedTexture, textureFilesystemPath))
141219
{
142-
LUX_CORE_WARN("AssimpMeshImporter: texture '{}' referenced by '{}' was not found", rawPath, meshPath.filename().string());
143-
continue;
220+
LUX_CORE_WARN("AssimpMeshImporter: failed to extract embedded texture '{}' from '{}'", rawPath, meshPath.filename().string());
221+
return 0;
144222
}
145223

146-
const std::string cacheKey = resolvedPath.lexically_normal().generic_string();
147-
if (auto it = textureCache.find(cacheKey); it != textureCache.end())
148-
return it->second;
149-
150-
AssetHandle textureHandle = editorAssetManager->ImportAsset(resolvedPath);
224+
AssetHandle textureHandle = editorAssetManager->ImportAsset(textureRelativePath);
151225
if (textureHandle && AssetManager::GetAssetType(textureHandle) == AssetType::Texture)
152226
{
153227
textureCache[cacheKey] = textureHandle;
154228
return textureHandle;
155229
}
230+
231+
return 0;
232+
}
233+
234+
if (IsEmbeddedTextureReference(rawPath))
235+
return 0;
236+
237+
std::filesystem::path resolvedPath = FindTexturePath(meshPath, texturePath);
238+
if (resolvedPath.empty())
239+
{
240+
LUX_CORE_WARN("AssimpMeshImporter: texture '{}' referenced by '{}' was not found", rawPath, meshPath.filename().string());
241+
return 0;
242+
}
243+
244+
const std::string cacheKey = resolvedPath.lexically_normal().generic_string();
245+
if (auto it = textureCache.find(cacheKey); it != textureCache.end())
246+
return it->second;
247+
248+
AssetHandle textureHandle = editorAssetManager->ImportAsset(resolvedPath);
249+
if (textureHandle && AssetManager::GetAssetType(textureHandle) == AssetType::Texture)
250+
{
251+
textureCache[cacheKey] = textureHandle;
252+
return textureHandle;
253+
}
254+
255+
return 0;
256+
}
257+
258+
static AssetHandle ImportTextureReference(const std::filesystem::path& meshPath, const aiScene* scene, aiMaterial* material, const std::initializer_list<aiTextureType>& textureTypes, std::unordered_map<std::string, AssetHandle>& textureCache)
259+
{
260+
if (!material)
261+
return 0;
262+
263+
for (aiTextureType textureType : textureTypes)
264+
{
265+
if (material->GetTextureCount(textureType) == 0)
266+
continue;
267+
268+
aiString texturePath;
269+
if (material->GetTexture(textureType, 0, &texturePath) != AI_SUCCESS)
270+
continue;
271+
272+
AssetHandle textureHandle = ImportTexturePathReference(meshPath, scene, texturePath, textureCache);
273+
if (textureHandle)
274+
return textureHandle;
156275
}
157276

158277
return 0;
@@ -271,7 +390,7 @@ namespace Lux
271390
if (!scene)
272391
return texturePaths;
273392

274-
constexpr std::array<aiTextureType, 9> textureTypes = {
393+
constexpr std::array<aiTextureType, 10> textureTypes = {
275394
aiTextureType_BASE_COLOR,
276395
aiTextureType_DIFFUSE,
277396
aiTextureType_NORMAL_CAMERA,
@@ -280,7 +399,8 @@ namespace Lux
280399
aiTextureType_METALNESS,
281400
aiTextureType_DIFFUSE_ROUGHNESS,
282401
aiTextureType_SHININESS,
283-
aiTextureType_EMISSIVE
402+
aiTextureType_EMISSIVE,
403+
aiTextureType_UNKNOWN
284404
};
285405

286406
for (uint32_t materialIndex = 0; materialIndex < scene->mNumMaterials; materialIndex++)

Core/Source/Lux/Asset/MeshRuntimeSerializer.cpp

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "Lux/Renderer/MaterialAsset.h"
88
#include "Lux/Renderer/Mesh.h"
99
#include "Lux/Renderer/Renderer.h"
10+
#include "Lux/Renderer/Shader.h"
1011

1112
namespace Lux
1213
{
@@ -119,6 +120,11 @@ namespace Lux
119120
stream.WriteArray(meshMaterials);
120121
file.Data.MaterialArraySize = (stream.GetStreamPosition() - streamOffset) - file.Data.MaterialArrayOffset;
121122
}
123+
else
124+
{
125+
file.Data.MaterialArrayOffset = 0;
126+
file.Data.MaterialArraySize = 0;
127+
}
122128

123129
file.Data.VertexBufferOffset = stream.GetStreamPosition() - streamOffset;
124130
stream.WriteArray(meshSource->m_Vertices);
@@ -155,6 +161,7 @@ namespace Lux
155161
stream.ReadRaw<MeshSourceFile::Metadata>(file.Data);
156162
const auto& metadata = file.Data;
157163
const bool hasMaterials = (metadata.Flags & (uint32_t)MeshSourceFile::MeshFlags::HasMaterials) != 0;
164+
meshSource->m_BoundingBox = metadata.BoundingBox;
158165

159166
stream.SetStreamPosition(streamOffset + metadata.NodeArrayOffset);
160167
stream.ReadArray(meshSource->m_Nodes);
@@ -172,9 +179,31 @@ namespace Lux
172179
for (size_t i = 0; i < meshMaterials.size(); i++)
173180
{
174181
const auto& meshMaterial = meshMaterials[i];
175-
Ref<Shader> shader = Renderer::GetShaderLibrary()->Get(meshMaterial.ShaderName);
176-
Ref<Material> material = Material::Create(shader, meshMaterial.MaterialName);
177-
Ref<MaterialAsset> materialAsset = Ref<MaterialAsset>::Create(material);
182+
Ref<Shader> shader;
183+
if (Ref<ShaderLibrary> shaderLibrary = Renderer::GetShaderLibrary())
184+
{
185+
const auto& shaders = shaderLibrary->GetShaders();
186+
if (!meshMaterial.ShaderName.empty())
187+
{
188+
if (auto it = shaders.find(meshMaterial.ShaderName); it != shaders.end())
189+
shader = it->second;
190+
}
191+
192+
if (!shader)
193+
{
194+
if (auto it = shaders.find("LuxPBR_Static"); it != shaders.end())
195+
shader = it->second;
196+
}
197+
}
198+
199+
Ref<MaterialAsset> materialAsset;
200+
if (shader)
201+
materialAsset = Ref<MaterialAsset>::Create(Material::Create(shader, meshMaterial.MaterialName.empty() ? "RuntimeMeshMaterial" : meshMaterial.MaterialName));
202+
else
203+
materialAsset = Ref<MaterialAsset>::Create(false);
204+
205+
if (!materialAsset || !materialAsset->GetMaterial())
206+
continue;
178207

179208
materialAsset->SetAlbedoColor(meshMaterial.AlbedoColor);
180209
materialAsset->SetEmission(meshMaterial.Emission);
@@ -202,6 +231,28 @@ namespace Lux
202231
if (!meshSource->m_Indices.empty())
203232
meshSource->m_IndexBuffer = IndexBuffer::Create(Buffer(meshSource->m_Indices.data(), (uint32_t)(meshSource->m_Indices.size() * sizeof(Index))));
204233

234+
for (uint32_t i = 0; i < (uint32_t)meshSource->m_Submeshes.size(); i++)
235+
{
236+
const Submesh& submesh = meshSource->m_Submeshes[i];
237+
const uint32_t firstTriangle = submesh.BaseIndex / 3;
238+
const uint32_t triangleCount = submesh.IndexCount / 3;
239+
for (uint32_t triangle = 0; triangle < triangleCount; triangle++)
240+
{
241+
const uint32_t indexOffset = firstTriangle + triangle;
242+
if (indexOffset >= meshSource->m_Indices.size())
243+
break;
244+
245+
const Index& index = meshSource->m_Indices[indexOffset];
246+
const uint32_t v0 = submesh.BaseVertex + index.V1;
247+
const uint32_t v1 = submesh.BaseVertex + index.V2;
248+
const uint32_t v2 = submesh.BaseVertex + index.V3;
249+
if (v0 >= meshSource->m_Vertices.size() || v1 >= meshSource->m_Vertices.size() || v2 >= meshSource->m_Vertices.size())
250+
continue;
251+
252+
meshSource->m_TriangleCache[i].emplace_back(meshSource->m_Vertices[v0], meshSource->m_Vertices[v1], meshSource->m_Vertices[v2]);
253+
}
254+
}
255+
205256
return meshSource;
206257
}
207258
}

Core/Source/Lux/Editor/SceneHierarchyPanel.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ namespace Lux {
8585

8686
const std::filesystem::path relativePath = std::filesystem::path("Meshes") / "Source" / "Default" / filename;
8787
AssetHandle handle = editorAssetManager->GetAssetHandleFromFilePath(relativePath);
88+
if (!handle)
89+
{
90+
const std::filesystem::path filesystemPath = Project::GetActiveAssetDirectory() / relativePath;
91+
if (std::filesystem::exists(filesystemPath))
92+
handle = editorAssetManager->ImportAsset(filesystemPath);
93+
}
94+
8895
if (!handle || AssetManager::GetAssetType(handle) != AssetType::MeshSource)
8996
return 0;
9097

0 commit comments

Comments
 (0)