vpenades / SharpGLTF

glTF reader and writer for .NET Standard
MIT License
457 stars 72 forks source link

Loading a model in SharpGLTF.Toolkit seems to create duplicate meshes #123

Closed cyril-perrin closed 2 years ago

cyril-perrin commented 2 years ago

Hello, I’m using SharpGLTF.Core to :

  1. Open an existing model var model = ModelRoot.Load("model.glb");
  2. Create a new scene var outputScene = model.UseScene("Working Scene");
  3. Copy several times a selection of nodes from the default scene to the new working scene
    for (...)
    {
    ...
    var newNode = outputScene.CreateNode("Copy of " + node.Name);
    newNode.Skin = node.Skin;
    newNode.Mesh = node.Mesh;
    newNode.LocalTransform = node.LocalTransform * transformationMatrix;
    }
  4. Set my working scene as the default scene model.DefaultScene = outputScene;

When I save the model, the resulting GLB is hardly larger than the source one, since all the nodes I created point to the same mesh (confirmed in Gestaltor). This is perfect!

However… when I load my working scene in the Toolkit using SceneBuilder.CreateFrom(), then save the GLB, the file size is much larger since all the nodes now point to their own duplicate of the same mesh.

Is this the expected behavior? Is there a way to prevent the creation of these extra meshes?

Thanks.

vpenades commented 2 years ago

Is this the expected behavior?

No, SceneBuilder has a mechanism to identify identical meshes and reuse them.

But this mechanism operate when you convert to schema gift. Are you converting multiple scenes within a single model?

cyril-perrin commented 2 years ago

Yes I'm using the code you propose here: https://github.com/vpenades/SharpGLTF/issues/79#issuecomment-734314259

var merged = new SceneBuilder();
var suzanne = SceneBuilder.Load("Suzanne.glb");
merged.AddScene(suzanne, System.Numerics.Matrix4x4.Identity);
merged.ToGltf2().SaveGLB("Suzanne2.glb");

with the attached model (Suzanne.zip). Suzanne.glb is around 2.5MB, Suzanne2.glb is 114MB. Thanks.

vpenades commented 2 years ago

yeah, that's the source of the problem.

When I developed these classes, I was not expecting many people to use multiple scenes, so all the internal optimizations to identify shared resources only work for a single scene.

The example at #79 works because it's bringing toghether two scenes from different models, so there's no mesh collision. But you're bringing two scenes from the same source model, so by converting each scene, all the mesh resources are duplicated.

I'll find a solution for this, and I'll extend the API so sceneBuilder can load multiple scenes simultaneously while sharing the resources.

vpenades commented 2 years ago

I've commited some changes that will fix the problem, you'll have to clone the repo and use the projects for a while until next release.

use case example can be seen here.

cyril-perrin commented 2 years ago

Thank you for your quick reply. I adapted my code for your latest commit. It is now:

var merged = new SceneBuilder();
var suzanne = SceneBuilder.LoadDefaultScene("Suzanne.glb");
merged.AddScene(suzanne, System.Numerics.Matrix4x4.Identity);
merged.ToGltf2().SaveGLB("Suzanne2.glb");

Using the same model (Suzanne.zip), I unfortunately experience almost the same result as before (Suzanne2.glb weights 112MB now) and in addition, Gestaltor logs "Error | Could not bind invalid texture".

Sorry if I'm missing something!

vpenades commented 2 years ago

@cyril-perrin reviewing the code I've found some additional issues.... you can try the latest changes and let me know how they work

cyril-perrin commented 2 years ago

Hello, I tested 'master' on 059a947 and I confirm that AddScene() doesn’t duplicate the meshes anymore, thanks! However, I believe something has been broken.

var suzanne = ModelRoot.Load("glTF/Suzanne.gltf", ValidationMode.TryFix);
suzanne.SaveGLB("Suzanne.glb");

Just by running this piece of code with the original Suzanne model (here), I notice that:

vpenades commented 2 years ago

Indeed, the model was not broken, but some write settings were broken and was defaulting to write the textures outside the GLB file (which is still legal)

Regarding Gestaltor not finding the textures, it's precisely because they where written outside the GLB file and you probably moved it without moving the textures.

If Gestaltor is not able to load a GLB with external textures (stored in the same directory as the GLB) then it's a gestaltor's issue.

Sorry, but I don't have a gestaltor license so I can't try that.

cyril-perrin commented 2 years ago

Thanks a lot, it now works like a charm!