Open cpetry opened 7 months ago
hey, should be possible with public GLTFSceneImporter(GLTFRoot rootNode, Stream gltfStream, ImportOptions options)
, there you can set a stream
SOLUTION
If anyone is still interested, this is how I did it for my webGL Application:
protected override void LoadFromBytes(byte[] data)
{
// Dataloader with empty constructor to skip check for directory name
var importOptions = new ImportOptions
{
DataLoader = new UnityWebRequestLoader("")
};
// Parse the stream
GLTFParser.ParseJson(stream, out var gltfRoot);
stream.Position = 0;
var loader = new GLTFSceneImporter(gltfRoot, stream, importOptions)
{
// For webGL Builds: disable multithreading
IsMultithreaded = false
};
// Load the scene by memorystream
loader.LoadScene(-1, true);
}
If anyone has a better approach, please let me know!
Old Comment History
.
.
.
**Original comment:**
I do have the same "problem". I want to load a glb file in webGL.
After I used a filebrowser, I do get a byte[] array, which I fed into a memorystream and then created a new SceneImporter with the stream and importOptions:
```
IEnumerator LoadGLB(byte[] data)
{
MemoryStream stream = new MemoryStream(data);
var importOptions = new ImportOptions
{
AnimationMethod = AnimationMethod.MecanimHumanoid
};
var sceneImporter = new GLTFSceneImporter(
null,
stream,
importOptions
);
yield return sceneImporter.LoadScene(-1, true, AfterImportFinished);
}
```
But at initialization, in the constructor of GltfSceneImporter the "VerifyDataLoader" Method gets called, which checks for a directoryname - which I dont have if I am using a stream as input:
```
public GLTFSceneImporter(GLTFRoot rootNode, Stream gltfStream, ImportOptions options)
: this(options)
{
_gltfRoot = rootNode;
if (gltfStream != null)
{
_gltfStream = new GLBStream
{
Stream = gltfStream,
StartPosition = gltfStream.Position
};
}
VerifyDataLoader();
}
private void VerifyDataLoader()
{
if (_options.DataLoader == null)
{
if (_options.ExternalDataLoader == null)
{
_options.DataLoader = new UnityWebRequestLoader(URIHelper.GetDirectoryName(_gltfFileName));
_gltfFileName = URIHelper.GetFileFromUri(new Uri(_gltfFileName));
}
else
{
_options.DataLoader = LegacyLoaderWrapper.Wrap(_options.ExternalDataLoader);
}
}
}
```
If I am trying to use it, unity throws a NullReferenceException, because the "_gltfFileName" is empty.
For now I dont know how to use it - any recommendations?
.
.
.
**EDIT:** _Okay I tried with the UnityWebRequestLoader but it only works in editor playmode, built to webGL it doesnt. This is my code so far, only working in Editor:_
```
IEnumerator LoadGLB(string uri)
{
// uri contains host and blob of my browser chosen file
// something like: http://127.0.0.1:5500/39550693-4ad4-4e87-8856-83ac8b84ed85
var importOptions = new ImportOptions
{
AnimationMethod = AnimationMethod.MecanimHumanoid
};
var sceneImporter = new GLTFSceneImporter(
uri,
importOptions
);
yield return sceneImporter.LoadScene(-1, true, CreatePuppetBehaviour);
}
```
And this was my working code for uploading a file to my WebGL APP, using a different gltf plugin, which worked by loading from byte[]:
IEnumerator LoadGLB(string uri)
{
using UnityWebRequest uwr = UnityWebRequest.Get(uri);
yield return uwr.SendWebRequest();
if (uwr.error != null) Debug.Log(uwr.error);
else
{
byte[] result = new byte[uwr.downloadHandler.data.Length];
System.Array.Copy(uwr.downloadHandler.data, 0, result, 0, uwr.downloadHandler.data.Length);
importer.LoadGLBFromBytes(result);
}
}
I feel like something in between is missing. Maybe it needs to copy the buffer?
.
.
.
**EDIT 2:** Okay I am pretty sure it is the "System.Threading.Tasks" include, that WebGL can't handle.
So only Option B) is qualified, as I can handle the WebRequest download single-threaded.
~A) Use the built-in UnityWebRequestLoader, but need to understand what am I missing for now~ (not working in WebGL)
**B) Use the Stream approach, but need to work around the filename check**
@pfcDorn What would you suggest?
I am trying again now like this with glb files and the "fake" UnityWebRequestLoader:
private async UniTask<GameObject> LoadWithUnityGLTF(byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
var glb = GLBBuilder.ConstructFromStream(stream);
_unityGltf.DataLoader = new UnityGLTF.Loader.UnityWebRequestLoader("");
var importer = new UnityGLTF.GLTFSceneImporter(glb.Root, stream, _unityGltf);
await importer.LoadScene(-1, showSceneObj: true);
return importer.LastLoadedScene;
}
}
But I get lots of mesh errors like these and of course no valid model:
Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 204, VertexCount: 80 UnityEngine.Mesh:SetIndices (int[],UnityEngine.MeshTopology,int,bool,int)
I also tried creating my own MemoryLoader with the IDataLoader interface but this didn't work as well.
private class MemoryDataLoader : UnityGLTF.Loader.IDataLoader
{
private readonly MemoryStream _memoryStream;
public MemoryDataLoader(MemoryStream memoryStream)
{
_memoryStream = memoryStream;
}
public async Task<Stream> LoadStreamAsync(string relativeFilePath)
{
return _memoryStream;
}
}
Any ideas?
Hey, can you take a look on this branch: https://github.com/KhronosGroup/UnityGLTF/tree/fix/load-from-stream I made some changes, so you should only need these lines to load from a stream:
var stream = new FileStream(filePath, FileMode.Open);
var importOptions = new ImportOptions();
var importer = new GLTFSceneImporter(stream, importOptions);
await importer.LoadSceneAsync();
stream.Dispose();
Generally, you should only load GLBs which contains all textures and data. References to files would not work.
Let me know if it's working for you :)
Btw: You already has found a working solution with a custom DataLoader:
private class StreamDataLoader : UnityGLTF.Loader.IDataLoader
{
private readonly Stream _stream;
public StreamDataLoader(Stream stream)
{
_stream = stream;
}
public async Task<Stream> LoadStreamAsync(string relativeFilePath)
{
return _stream;
}
}
var stream = new FileStream(filePath, FileMode.Open);
var importOptions = new ImportOptions();
importOptions.DataLoader = new StreamDataLoader(stream);
GLTFParser.ParseJson(stream, out var gltfRoot);
var importer = new GLTFSceneImporter(gltfRoot, stream, importOptions);
await importer.LoadSceneAsync();
stream.Dispose();
Works like a charm! Thanks a lot for this.
I didn't manage to get it to work with the custom DataLoader but with your first example on the separate branch. Please add documentation somewhere. Can be closed once merged
Hello,
I'd like to load a byte[] or MemoryStream as a glb file without a file path from script (not using Inspector at all). Is this possible? Didn't find anything in examples or tests.