vpenades / SharpGLTF

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

Duplicate materials #155

Closed wangchong666 closed 1 year ago

wangchong666 commented 1 year ago

Describe the bug duplicate materials

To Reproduce create a material

var red = new MaterialBuilder()
    .WithDoubleSide(true)
    .WithMetallicRoughness(0.9f, 0.1f)
    .WithMetallicRoughnessShader()
    .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 0, 1));

create some mesh with this material

for
       var mesh = new MeshBuilder<VERTEX>("some name");
       var prim = mesh.UsePrimitive(red);
...

gltf file:

    "meshes": [
        {
            "name": "2693696469328",
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 0
                    },
                    "indices": 1,
                    "material": 0
                }
            ]
        },
        {
            "name": "2693696469392",
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 2
                    },
                    "indices": 3,
                    "material": 1
                }
            ]
        },
        {
            "name": "2693696469456",
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 4
                    },
                    "indices": 5,
                    "material": 2
                }
            ]
        },
....

It generate one material per mesh please complete the following information:

vpenades commented 1 year ago

I have unit tests that verify that shared materials are duplicated, so you may be doing something odd.

I would need to see a working example to be able to solve the problem.

wangchong666 commented 1 year ago

@vpenades Thanks for reply. Example:

using SharpGLTF.Geometry;
using SharpGLTF.Materials;
using SharpGLTF.Schema2;
using System;
using System.Numerics;
using VERTEX = SharpGLTF.Geometry.VertexTypes.VertexPosition;
var red = new MaterialBuilder()
      .WithDoubleSide(true)
      .WithMetallicRoughness(0.9f, 0.1f)
      .WithMetallicRoughnessShader()
      .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 0, 1));

var model = ModelRoot.CreateModel();
var scene = model.UseScene(0);
var rootNode = scene.CreateNode("root");

var mesh = new MeshBuilder<VERTEX>("mesh1");

var prim = mesh.UsePrimitive(red);
prim.AddTriangle(new VERTEX(-10, 0, 0), new VERTEX(10, 0, 0), new VERTEX(0, 10, 0));
prim.AddTriangle(new VERTEX(10, 0, 0), new VERTEX(-10, 0, 0), new VERTEX(0, -10, 0));
rootNode.CreateNode("1").WithMesh(model.CreateMeshes(mesh).First());

var mesh2 = new MeshBuilder<VERTEX>("mesh2");
prim = mesh2.UsePrimitive(red);
prim.AddQuadrangle(new VERTEX(-5, 0, 3), new VERTEX(0, -5, 3), new VERTEX(5, 0, 3), new VERTEX(0, 5, 3));
rootNode.CreateNode("1").WithMesh(model.CreateMeshes(mesh2).First());
// create a scene

model.SaveAsWavefront("mesh.obj");
model.SaveGLB("mesh.glb");
model.SaveGLTF("mesh.gltf");

GLTF:

{
    "asset": {
        "copyright": "",
        "generator": "SharpGLTF 1.0.0-alpha0027",
        "version": "2.0"
    },
    "accessors": [
        {
            "name": "POSITION",
            "bufferView": 0,
            "componentType": 5126,
            "count": 4,
            "max": [
                10,
                10,
                0
            ],
            "min": [
                -10,
                -10,
                0
            ],
            "type": "VEC3"
        },
        {
            "bufferView": 1,
            "componentType": 5123,
            "count": 6,
            "type": "SCALAR"
        },
        {
            "name": "POSITION",
            "bufferView": 2,
            "componentType": 5126,
            "count": 4,
            "max": [
                5,
                5,
                3
            ],
            "min": [
                -5,
                -5,
                3
            ],
            "type": "VEC3"
        },
        {
            "bufferView": 3,
            "componentType": 5123,
            "count": 6,
            "type": "SCALAR"
        }
    ],
    "bufferViews": [
        {
            "buffer": 0,
            "byteLength": 48,
            "byteStride": 12,
            "target": 34962
        },
        {
            "buffer": 0,
            "byteLength": 12,
            "byteOffset": 96,
            "target": 34963
        },
        {
            "buffer": 0,
            "byteLength": 48,
            "byteOffset": 48,
            "byteStride": 12,
            "target": 34962
        },
        {
            "buffer": 0,
            "byteLength": 12,
            "byteOffset": 108,
            "target": 34963
        }
    ],
    "buffers": [
        {
            "byteLength": 120,
            "uri": "mesh.bin"
        }
    ],
    "materials": [
        {
            "doubleSided": true,
            "pbrMetallicRoughness": {
                "baseColorFactor": [
                    1,
                    0,
                    0,
                    1
                ],
                "metallicFactor": 0.8999999761581421,
                "roughnessFactor": 0.10000000149011612
            }
        },
        {
            "doubleSided": true,
            "pbrMetallicRoughness": {
                "baseColorFactor": [
                    1,
                    0,
                    0,
                    1
                ],
                "metallicFactor": 0.8999999761581421,
                "roughnessFactor": 0.10000000149011612
            }
        }
    ],
    "meshes": [
        {
            "name": "mesh1",
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 0
                    },
                    "indices": 1,
                    "material": 0
                }
            ]
        },
        {
            "name": "mesh2",
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 2
                    },
                    "indices": 3,
                    "material": 1
                }
            ]
        }
    ],
    "nodes": [
        {
            "name": "root",
            "children": [
                1,
                2
            ]
        },
        {
            "name": "1",
            "mesh": 0
        },
        {
            "name": "1",
            "mesh": 1
        }
    ],
    "scenes": [
        {
            "nodes": [
                0
            ]
        }
    ]
}
wangchong666 commented 1 year ago

emm, this work fine.

foreach(var m in model.CreateMeshes(mesh, mesh2))
{
    rootNode.CreateNode("1").WithMesh(m);
}
vpenades commented 1 year ago

Okey, the problem here is that you're using MeshBuilder to create a mesh and add it directly to gltf Model. When you do that, the material is converted from Toolkit.MaterialBuilder to Schema2.Material, which is a totally different thing and it's no longer possible to compare materials, producing duplications.

As you've discovered, you have one option, which is to call CreateMeshes with multiple meshes, in which case material duplication is resolved between these meshes before converting them.

The other option is that you use SceneBuilder and NodeBuilder, and at the very end, convert SceneBuilder to glTF.

wangchong666 commented 1 year ago

If use SceneBuilder and NodeBuilder ,I must create a mesh for each node,In my case , so many nodes share one mesh.

vpenades commented 1 year ago

No, you don't need to create a mesh for each node. within SceneBuilder, meshes and materials are always shared.

Sharing resources is precisely the purpose of SceneBuilder and all *Builder classes.