vpenades / SharpGLTF

glTF reader and writer for .NET Standard
MIT License
467 stars 75 forks source link

Extract a submesh ? #49

Closed EricBeetsOfficial-Opuscope closed 4 years ago

EricBeetsOfficial-Opuscope commented 4 years ago

Hi !

It's a great project !

I was wondering if it's possible to extract a sub mesh ? I mean, for instance, create a ModelRoot from a Mesh (and save it ..) ?

Thanks in advance Eric

vpenades commented 4 years ago

It is possible, but not as trivial as it should.

Keep in mind that a mesh depends on its materials, the materials on textures, the textures on images, etc, so you have to take that into account when exporting a sub-mesh.

Also, it is nearly impossible to edit a loaded gltf to remove all the meshes and resources except the ones you want to export.

That being said, the best approach you can follow is this:

var sceneBuilder = srcModel.DefaultScene.ToSceneBuilder();      
var finalScene = sceneBuilder.ToGltf2();      
finalScene.Save(...);

Some notes about Toolkit's SceneBuilder: Right now it's API is designed only to append new meshes, and it does not have any API to remove elements, yet.

But in this case, and unlike glTF, SceneBuilder elements can be shared between multiple SceneBuilder instances, so you can create a new SceneBuilder, add the mesh you want to export from the source's SceneBuilder, and then export it.

I have not tested that particular workflow, let me know if it works for you.

vpenades commented 4 years ago

Hi, I've added an example on how to extract and save individual meshes:

https://github.com/vpenades/SharpGLTF/blob/16c7137f48a82e9a100e6c83dd306e686d620168/tests/SharpGLTF.Toolkit.Tests/Scenes/SceneBuilderTests.cs#L538

EricBeetsOfficial-Opuscope commented 4 years ago

Hi,

Thanks a lot, it was what I having done and it works ;) But, finally, I used another way (as I didn't need material, but just geometry):

    ModelRoot modelRoot = ModelRoot.CreateModel();
    // Default material
    Material materialToExport = modelRoot.CreateMaterial();
    // Create Mesh
    Mesh meshToExport = modelRoot.CreateMesh(this.name);
    foreach(MeshPrimitive meshPrimitive in mesh.Primitives)
    {
        var positions = meshPrimitive.GetVertexAccessor("POSITION").AsVector3Array().ToArray();
        var normals = meshPrimitive.GetVertexAccessor("NORMAL").AsVector3Array().ToArray();
        var texcoord = meshPrimitive.GetVertexAccessor("TEXCOORD_0").AsVector2Array().ToArray();
        var indices = (int[])(object)meshPrimitive.GetIndexAccessor().AsIndicesArray().ToArray();
        meshToExport.CreatePrimitive().WithVertexAccessor("POSITION", positions)
                                              .WithVertexAccessor("NORMAL", normals)
                                              .WithVertexAccessor("TEXCOORD_0", texcoord)
                                              .WithIndicesAccessor(PrimitiveType.TRIANGLES, indices)
                                              .WithMaterial(materialToExport);
    }
    Scene sceneModel = modelRoot.UseScene("Default");
    sceneModel.CreateNode("RootNode").WithMesh(meshToExport);
    modelRoot.SaveGLB(this.pathFile);

Thank you again for your project and your reactivity (next step: get the model bounding box ;) )

vpenades commented 4 years ago

It's actually much easier if you use the Toolkit extensions:

var mesh = modelRoot.LogicalMeshes[17]; // get the mesh we want to extract

var meshBuilder = mesh.ToMeshBuilder(); // Toolkit extension extracts the mesh to a MeshBuilder

var sceneBuilder = new SceneBuilder(); // create a new scene that will act as a container of the mesh.

sceneBuilder.AddRigidMesh(meshBuilder, Matrix4x4.Indentity);

var gltf = sceneBuilder.ToGltf2();
gltf.SaveGLB(this.pathFile);