Example #1
0
        public void WriteTwoModelsWithSharedTexture()
        {
            TestContext.CurrentContext.AttachShowDirLink();
            TestContext.CurrentContext.AttachGltfValidatorLinks();

            var tex1Bytes = System.IO.File.ReadAllBytes(System.IO.Path.Combine(AssetsPath, "shannon.png"));
            var tex2Bytes = System.IO.File.ReadAllBytes(System.IO.Path.Combine(AssetsPath, "Texture1.jpg"));

            var tex1 = tex1Bytes.AttachToCurrentTest("shared-shannon.png");
            var tex2 = tex2Bytes.AttachToCurrentTest("subdir\\shared-in-dir-Texture1.jpg");

            // create a material using our shared texture
            var material1 = new MaterialBuilder()
                            .WithUnlitShader()
                            .WithBaseColor(tex1);

            // create a material using our shared texture
            var material2 = new MaterialBuilder()
                            .WithUnlitShader()
                            .WithBaseColor(tex2);

            // create a simple cube mesh
            var mesh1 = new Cube <MaterialBuilder>(material1).ToMesh(Matrix4x4.Identity);
            var mesh2 = new Cube <MaterialBuilder>(material2).ToMesh(Matrix4x4.Identity);
            var scene = new SceneBuilder();

            scene.AddRigidMesh(mesh1, Matrix4x4.CreateTranslation(-2, 0, 0));
            scene.AddRigidMesh(mesh2, Matrix4x4.CreateTranslation(2, 0, 0));

            var gltf = scene.ToGltf2();

            // define the texture sharing hook; this is a pretty naive approach, but it's good
            // enough to demonstrate how it works.

            string imageSharingHook(WriteContext ctx, string uri, Memory.MemoryImage image)
            {
                Assert.IsTrue(new string[] { tex1, tex2 }.Contains(image.SourcePath));

                if (File.Exists(image.SourcePath))
                {
                    // image.SourcePath is an absolute path, we must make it relative to ctx.CurrentDirectory

                    var currDir = ctx.CurrentDirectory.FullName + "\\";

                    // if the shared texture can be reached by the model in its directory, reuse the texture.
                    if (image.SourcePath.StartsWith(currDir, StringComparison.OrdinalIgnoreCase))
                    {
                        // we've found the shared texture!, return the uri relative to the model:
                        return(image.SourcePath.Substring(currDir.Length));
                    }

                    // TODO: Here we could also try to find a texture equivalent to MemoryImage in the
                    // CurrentDirectory even if it has a different name, to minimize texture duplication.
                }

                // we were unable to reuse the shared texture,
                // default to write our own texture.

                image.SaveToFile(Path.Combine(ctx.CurrentDirectory.FullName, uri));

                return(uri);
            }

            var settings = new WriteSettings();

            settings.ImageWriting       = ResourceWriteMode.SatelliteFile;
            settings.ImageWriteCallback = imageSharingHook;

            // save the model several times:

            var path1 = gltf.AttachToCurrentTest("model1.glb", settings);
            var path2 = gltf.AttachToCurrentTest("model2.glb", settings);
            var path3 = gltf.AttachToCurrentTest("model3.gltf", settings);

            var satellites1 = ModelRoot.GetSatellitePaths(path1);
            var satellites2 = ModelRoot.GetSatellitePaths(path2);
            var satellites3 = ModelRoot.GetSatellitePaths(path3);

            Assert.IsTrue(satellites1.Contains("shared-shannon.png"));
            Assert.IsTrue(satellites1.Contains("subdir/shared-in-dir-Texture1.jpg"));

            Assert.IsTrue(satellites2.Contains("shared-shannon.png"));
            Assert.IsTrue(satellites2.Contains("subdir/shared-in-dir-Texture1.jpg"));

            Assert.IsTrue(satellites3.Contains("shared-shannon.png"));
            Assert.IsTrue(satellites3.Contains("subdir/shared-in-dir-Texture1.jpg"));
        }
Example #2
0
        public static bool AreEqualByContent(MaterialBuilder x, MaterialBuilder y)
        {
            #pragma warning disable IDE0041 // Use 'is null' check
            if (Object.ReferenceEquals(x, y))
            {
                return(true);
            }
            if (Object.ReferenceEquals(x, null))
            {
                return(false);
            }
            if (Object.ReferenceEquals(y, null))
            {
                return(false);
            }
            #pragma warning restore IDE0041 // Use 'is null' check

            // Although .Name is not strictly a material property,
            // it identifies a specific material during Runtime that
            // might be relevant and needs to be preserved.
            // If an author needs materials to be merged, it's better
            // to keep the Name as null, or to use a common name like "Default".

            if (x.Name != y.Name)
            {
                return(false);
            }
            if (x.AlphaMode != y.AlphaMode)
            {
                return(false);
            }
            if (x.AlphaCutoff != y.AlphaCutoff)
            {
                return(false);
            }
            if (x.DoubleSided != y.DoubleSided)
            {
                return(false);
            }
            if (x.ShaderStyle != y.ShaderStyle)
            {
                return(false);
            }

            if (!AreEqualByContent(x._CompatibilityFallbackMaterial, y._CompatibilityFallbackMaterial))
            {
                return(false);
            }

            // gather all unique channel keys used by both materials.

            var channelKeys = x._Channels
                              .Concat(y._Channels)
                              .Select(item => item.Key)
                              .Distinct();

            foreach (var ckey in channelKeys)
            {
                var xc = x.GetChannel(ckey);
                var yc = y.GetChannel(ckey);

                if (!ChannelBuilder.AreEqual(xc, yc))
                {
                    return(false);
                }
            }

            return(true);
        }