Model and Material loading via content manager

This commit is contained in:
Simon Lübeß
2023-01-07 12:15:14 +01:00
parent ee30920b56
commit 3da60355df
25 changed files with 859 additions and 267 deletions
+37
View File
@@ -0,0 +1,37 @@
using GlitchyEngine.Core;
using System;
namespace GlitchyEngine.Content;
class Asset : RefCounter
{
private append String _identifier;
internal IContentManager _contentManager;
/// Gets the identifier of this asset.
/// @remarks The identifier is the name with which the asset was registered in the content manager.
/// This identifier can be used to request the Asset from the content manager.
public StringView Identifier
{
get => _identifier;
set
{
_contentManager?.UpdateAssetIdentifier(this, _identifier, value);
_identifier.Set(value);
// TODO: do we need to tell the content manager, that the name changed?
}
}
// TODO: do we need unmanaged assets? Probably not...
/// Gets the content manager that manages this asset; or null if this asset isn't managed.
public IContentManager ContentManager => _contentManager;
protected ~this()
{
// TODO: crash when _contentManager is deleted first...
// TODO: unregister from content manager
_contentManager?.UnmanageAsset(this);
}
}
+29 -3
View File
@@ -54,8 +54,9 @@ namespace GlitchyEngine.Content
/// Loads the asset from the given data stream with the specified config.
/// @param file The stream containing the asset.
/// @param config The configuration which specifies the settings used to load the asset.
/// @param contentManager The content manager used to load the asset.
/// @returns The loaded asset.
IRefCounted LoadAsset(Stream file, AssetLoaderConfig config);
Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView? subAsset, IContentManager contentManager);
}
static
@@ -79,7 +80,17 @@ namespace GlitchyEngine.Content
interface IContentManager
{
/// Loads the given asset.
IRefCounted LoadAsset(StringView assetIdentifier);
Asset LoadAsset(StringView assetIdentifier);
/// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier)
void ManageAsset(Asset asset);
/// The content manager will no longer manage the asset.
void UnmanageAsset(Asset asset);
// TODO: Maybe calling UnmanageAsset -> ManageAsset is enough....
/// Provides a method for the asset to tell its content manager that the identifer changed.
void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier);
/// Returns a data stream for the given asset.
Stream GetStream(StringView assetIdentifier);
@@ -93,7 +104,22 @@ namespace GlitchyEngine.Content
class RuntimeContentManager : IContentManager
{
public IRefCounted LoadAsset(StringView identifier)
public Asset LoadAsset(StringView assetIdentifier)
{
Runtime.NotImplemented();
}
public void ManageAsset(Asset asset)
{
Runtime.NotImplemented();
}
public void UnmanageAsset(Asset asset)
{
Runtime.NotImplemented();
}
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier)
{
Runtime.NotImplemented();
}
+163
View File
@@ -5,6 +5,7 @@ using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using GlitchyEngine.Renderer.Animation;
using GlitchyEngine.World;
using System.IO;
namespace GlitchyEngine.Content
{
@@ -66,6 +67,168 @@ namespace GlitchyEngine.Content
return geoBinding;
}
public static GeometryBinding LoadMesh(Stream data, StringView meshName, int primitiveIndex)
{
// TODO: add a context to remember which buffers were loaded before so that we don't load the same data multiple times.
uint8[] rawData = new:ScopedAlloc! uint8[data.Length];
var dataReadResult = data.TryRead(rawData);
if (dataReadResult case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read data from stream. Error: {err}");
}
CGLTF.Options options = .();
CGLTF.Data* modelData;
CGLTF.Result result = CGLTF.Parse(options, (Span<uint8>)rawData, out modelData);
if (!(result case .Success))
return null;
// TODO: one buffer can be used by multiple primitives, the content manager could manage the buffers
// TODO: load with content manager
result = CGLTF.LoadBuffers(options, modelData, (char8*)null);
//result = LoadBuffersWithContentManager(options, modelData, meshName, Application.Get().ContentManager);
GeometryBinding geoBinding = null;
for (var mesh in modelData.Meshes)
{
var name = StringView(mesh.Name);
//if (name == meshName)
{
Log.EngineLogger.AssertDebug(primitiveIndex >= 0 && primitiveIndex < mesh.Primitives.Length);
geoBinding = PrimitiveToGeoBinding(mesh.Primitives[primitiveIndex]);
break;
}
}
CGLTF.Free(modelData);
return geoBinding;
}
private static CGLTF.Result LoadBuffersWithContentManager(CGLTF.Options options, CGLTF.Data* data, StringView fileName, IContentManager contentManager)
{
if (data.Buffers.Length > 0 && data.Buffers[0].Data == null && data.Buffers[0].Uri == null && !data.Bin.IsEmpty)
{
if ((uint)data.Bin.Length < data.Buffers[0].Size)
return .DataTooShort;
data.Buffers[0].Data = data.Bin.Ptr;
data.Buffers[0].DataFreeMethod = .None;
}
for (ref CGLTF.Buffer buffer in ref data.Buffers)
{
if (buffer.Data != null)
continue;
if (buffer.Uri == null)
continue;
StringView uri = StringView(buffer.Uri);
if (uri.StartsWith("data:"))
{
int commaIndex = uri.IndexOf(',');
//char* comma = strchr(uri, ',');
if (commaIndex == -1 || commaIndex >= 7 || uri.StartsWith(";base64"))
return .UnknownFormat;
StringView dataView = uri.Substring(commaIndex + 1);
#unwarn
CGLTF.Result loadBufferResult = CGLTF.LoadBuffersBase64(&options, buffer.Size, dataView.Ptr, &buffer.Data);
buffer.DataFreeMethod = .MemoryFree;
return loadBufferResult;
}
else
{
Runtime.NotImplemented();
// TODO: Request Buffer from Content Manager
//int index = uri.IndexOf("://");
//if (index == -1)
// return .UnknownFormat;
// TODO: load buffer file...
//CGLTF.Result res = //cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data);
//buffer.DataFreeMethod = cgltf_data_free_method_file_release;
/*if (res != cgltf_result_success)
{
return res;
}*/
}
}
/*
for (cgltf_size i = 0; i < data->buffers_count; ++i)
{
if (data->buffers[i].data)
{
continue;
}
const char* uri = data->buffers[i].uri;
if (uri == NULL)
{
continue;
}
if (strncmp(uri, "data:", 5) == 0)
{
const char* comma = strchr(uri, ',');
if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0)
{
cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data);
data->buffers[i].data_free_method = cgltf_data_free_method_memory_free;
if (res != cgltf_result_success)
{
return res;
}
}
else
{
return cgltf_result_unknown_format;
}
}
else if (strstr(uri, "://") == NULL && gltf_path)
{
cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data);
data->buffers[i].data_free_method = cgltf_data_free_method_file_release;
if (res != cgltf_result_success)
{
return res;
}
}
else
{
return cgltf_result_unknown_format;
}
}
*/
return .Success;
}
public static EcsEntity LoadModel(String filename, Material material, EcsWorld world,
List<AnimationClip> outClips, StringView entityName = StringView())
{