-
Notifications
You must be signed in to change notification settings - Fork 489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for new architecture for UnityGLTF library #259
base: main
Are you sure you want to change the base?
Conversation
Blake Gross seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
vNext/UnityGLTFv2.cs
Outdated
/// <param name="textureId">Texture to load from glTF object.</param> | ||
/// <returns>The created Unity object</returns> | ||
public Task<Texture2D> ImportTextureAsync( | ||
UnityGLTFObject unityGLTFFObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unityGLTFFObject
has two FFs
vNext/UnityGLTFv2.cs
Outdated
/// <param name="nodeId">Node of the glTF object to load.</param> | ||
/// <returns>The created Unity object</returns> | ||
public Task<GameObject> ImportNodeAsync( | ||
UnityGLTFObject unityGLTFFObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unityGLTFFObject
has two FFs
vNext/UnityGLTF_vnext_proposal.md
Outdated
|
||
The first change I would like to make is to simplify the API. Currently the number of constructors and configuration options is overwhelming in creating a GLTFSceneImporter. | ||
|
||
public static class UnityGLTFLoader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block of code is out of date... delete?
vNext/UnityGLTFv2.cs
Outdated
/// <summary> | ||
/// Handles importing object from schema into Unity and handles exporting of objects from Unity into schema | ||
/// </summary> | ||
public partial class UnityGLTFImporter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I would prefer a class named Importer in a UnityGLTF namespace. Then people can choose to either write UnityGLTF.Importer (one additional character) or using the namespace and just refer to it as Importer. I know there are many varying opinions on this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Partial classes are also kind of hard to hunt down as well.
vNext/UnityGLTFv2.cs
Outdated
/// <param name="unityGLTFFObject">Object which contains information to parse</param> | ||
/// <param name="sceneId">Scene of the glTF to load</param> | ||
/// <returns>The created Unity object</returns> | ||
public Task<GameObject> ImportSceneAsync( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these *Async methods should have a CancellationToken as the last parameter. This is very standard for async methods, and gives us the option of supporting cancellation.
vNext/UnityGLTFv2.cs
Outdated
/// </summary> | ||
public class IDataLoader | ||
{ | ||
Task<Stream> LoadStream(string uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LoadStreamAsync, plus add a CancellationToken parameter.
{ | ||
public UnityGLTFImporterConfig(); | ||
public UnityGLTFImporterConfig(GLTFExtensionRegistry registry, GLTFImportOptions importOptions); | ||
public ImporterConfig(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this paramterless constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default constructor is used for when there are no options that are being set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just pass in null if you don't want to set options? It seems weird that the options are to either provide extensions and options, or nothing.
@@ -66,7 +73,7 @@ public class UnityGLTFImporterConfig | |||
/// </summary> | |||
public class IDataLoader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface, not class
Also wondering about the name of this interface... I wonder if something like IResourceLocator would be more self explanatory? Not sure...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really locating resources. It's only resolving them to a stream.
@@ -66,7 +73,7 @@ public class UnityGLTFImporterConfig | |||
/// </summary> | |||
public class IDataLoader | |||
{ | |||
Task<Stream> LoadStream(string uri); | |||
Task<Stream> LoadStreamAsync(string uri, CancellationToken ct = CancellationToken.None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about Uri uri
instead of string uri
. Just require at the contract level that a valid Uri is passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's not necessarily a web uri, which seems to be what the uri class is built around. Maybe path is a better parameter name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URI documentation says that it supports the file://
protocol, which in turn supports relative URIs. Seems like that would be enough.
vNext/UnityGLTFv2.cs
Outdated
/// <param name="unityGLTFObject">Object that is being loaded</param> | ||
/// <param name="sceneId">Index object which resolves to object in GLTFRoot</param> | ||
/// <returns>The loaded glTF scene as a GameObject hierarchy</returns> | ||
Task<ExtensionReturnObject<GameObject>> CreateSceneAsync(Importer importer, UnityGLTFObject unityGLTFObject, GLTF.Schema.SceneId sceneId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the Importer passed in here? Is it to allow the method to call the default load logic for a scene or something? If so, we should probably pass in an interface with the intended subset of the functionality. Otherwise, it is confusing how you are supposed to use this from the context of an extension (unless it is really expected that you might call any method on the importer from this context). Alternatively, we could restrict this further to only having the ability to invoke the base functionality for the type in question (e.g. from this context, you could call just one method for default scene loading, and in CreateNodeAsync you could call just one method for default node loading, etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this layer of intention could be implemented as an IExtensionContext interface instance passed into the function, where Importer implements IExtensionContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mirrors the functionality of Babylon. An extension may want to call any of the functions on the loader. I suppose there is a limit to what extension could want to do (an image load is not going to probably edit the animation hierarchy). I'm not sure how an interface would look different than the internal loader functions.
One thought is that the Importer
interface is publicly created and there is an ImporterImpl
which is not able to be publicly constructed but all methods are publicly accessible
vNext/UnityGLTFv2.cs
Outdated
/// <param name="unityGLTFObject">Object that is being loaded</param> | ||
/// <param name="sceneId">Index object which resolves to object in GLTFRoot</param> | ||
/// <returns>The loaded glTF scene as a GameObject hierarchy</returns> | ||
Task<ExtensionReturnObject<GameObject>> CreateSceneAsync(Importer importer, UnityGLTFObject unityGLTFObject, GLTF.Schema.SceneId sceneId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the UnityGLTFObject passed in here? Is it so the extension can basically cache data in it? If so, then I guess this implies that the UnityGLTFObject is not opaque from the standpoint of the consumer of this API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that UnityGLTFObject has the cache of data inside which may be needed for the extension
@@ -175,7 +209,8 @@ public async void LoadGLBs() | |||
UnityGLTFObject boxObject = new UnityGLTFObject("http://samplemodels/box.glb"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this assuming that deserialization is synchronous? Seems like this would naturally lead people down a bad path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is just passing in a uri that will be resolved during the actual load
vNext/UnityGLTFv2.cs
Outdated
UnityGLTFObject unityGLTFFObject, | ||
int sceneId = -1 | ||
public virtual Task<GameObject> ImportSceneAsync( | ||
UnityGLTFObject unityGLTFObject, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole UnityGLTFObject stateful wrapper still seems like a confusing part of this design to me. I think I'm now leaning more towards just passing in the raw deserialized data structure for the gltf root, and additionally (optionally) passing in an AssetCache by ref. Any problems with that approach?
Also, if we went that route, it would be very natural to have an ImportSceneAsync extension method that takes a path instead of a gltf root object, and incorporate async deserialization there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't enforce that asset caches and paired to a glTF object. It's just decoupling the two rather than having them stored in an object. I should update the design to have UnityGLTFObject expose the AssetCache.
vNext/UnityGLTFv2.cs
Outdated
/// <param name="unityGLTFObject">Object that is being loaded</param> | ||
/// <param name="sceneId">Index object which resolves to object in GLTFRoot</param> | ||
/// <returns>The loaded glTF scene as a GameObject hierarchy</returns> | ||
Task<ExtensionReturnObject<GameObject>> CreateSceneAsync(Importer importer, UnityGLTFObject unityGLTFObject, GLTF.Schema.SceneId sceneId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass the CancellationToken to the gltf extension methods as well.
public class GLTFExportOptions | ||
{ | ||
/// <summary>Whether to write the object out as a GLB</summary> | ||
bool ShouldWriteGLB { get; set ; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it not make more sense for this to just be a parameter of the actual Export* method(s)? Is it not possible to have a single exporter instance that exports both gltf and glb?
Handled | ||
} | ||
|
||
// Example calling pattern: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer valid, correct?
Active work has slowed down on this. We are more taking the approach now of slowly changing the existing code to more closely match the proposed architecture. Anyone who wants to help out can! |
No description provided.