public Runtime.GLTF SetModelAttributes(Runtime.GLTF wrapper, Runtime.Material material, List <Property> combo, ref glTFLoader.Schema.Gltf gltf)
        {
            // Switch from the flat plane to a model with multiple nodes
            wrapper = Common.MultiNode();
            var nodeList = new List <Runtime.Node>();

            nodeList.Add(wrapper.Scenes[0].Nodes[0]);
            nodeList.Add(wrapper.Scenes[0].Nodes[0].Children[0]);

            // Add a new child node that will inherit the transformations
            nodeList.Add((DeepCopy.CloneObject(wrapper.Scenes[0].Nodes[0])));
            nodeList[2].Name     = "Node1";
            nodeList[2].Children = null;
            nodeList[1].Children = new List <Runtime.Node>();
            nodeList[1].Children.Add(nodeList[2]);

            // Clear the vertex normal and tangent values already in the model
            foreach (var node in nodeList)
            {
                node.Mesh.MeshPrimitives[0].Normals  = null;
                node.Mesh.MeshPrimitives[0].Tangents = null;
            }

            foreach (Property property in combo)
            {
                if (property.name == Propertyname.Matrix)
                {
                    nodeList[1].Matrix = specialProperties[0].value;
                }
                else if (property.name == Propertyname.Translation ||
                         property.name == Propertyname.Translation_X ||
                         property.name == Propertyname.Translation_Y ||
                         property.name == Propertyname.Translation_Z)
                {
                    nodeList[1].Translation = property.value;
                }
                else if (property.name == Propertyname.Rotation)
                {
                    nodeList[1].Rotation = specialProperties[1].value;
                }
                else if (property.name == Propertyname.Scale)
                {
                    nodeList[1].Scale = property.value;
                }
            }

            // Apply the material to each node
            foreach (var node in nodeList)
            {
                node.Mesh.MeshPrimitives[0].Material = material;
            }

            return(wrapper);
        }
        public Runtime.GLTF SetModelAttributes(Runtime.GLTF wrapper, Runtime.Material material, List <Property> combo, ref glTFLoader.Schema.Gltf gltf)
        {
            foreach (var req in requiredProperty)
            {
                if (req.name == Propertyname.ExtensionUsed_SpecularGlossiness)
                {
                    // Initialize SpecGloss for every set
                    material.Extensions = new List <Runtime.Extensions.Extension>();
                    material.Extensions.Add(new Runtime.Extensions.PbrSpecularGlossiness());
                    if (wrapper.ExtensionsUsed == null)
                    {
                        wrapper.ExtensionsUsed = new List <string>();
                    }
                    wrapper.ExtensionsUsed = wrapper.ExtensionsUsed.Union(
                        new string[] { "KHR_materials_pbrSpecularGlossiness" }).ToList();
                }
                else if (req.name == Propertyname.BaseColorTexture)
                {
                    // Apply the fallback MetallicRoughness for every set
                    material.MetallicRoughnessMaterial = new Runtime.PbrMetallicRoughness
                    {
                        BaseColorTexture = new Runtime.Texture
                        {
                            Source = req.value
                        }
                    };
                }
            }
            var material2 = DeepCopy.CloneObject(material);

            Runtime.MeshPrimitive prim0 = null;
            Runtime.MeshPrimitive prim1 = null;
            foreach (var property in specialProperties)
            {
                if (property.name == Propertyname.Primitives_Split1)
                {
                    prim0 = new Runtime.MeshPrimitive
                    {
                        Positions = property.value.Positions,
                        Indices   = property.value.Indices,
                    };
                }
                else if (property.name == Propertyname.Primitives_Split2)
                {
                    prim1 = new Runtime.MeshPrimitive
                    {
                        Positions = property.value.Positions,
                        Indices   = property.value.Indices,
                    };
                }
                else if (property.propertyGroup == 0
                         )
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives = new List <Runtime.MeshPrimitive>
                    {
                        prim0,
                        prim1
                    };
                }

                if (property.name == Propertyname.Primitive0VertexUV0)
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = new List <List <Vector2> >();
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets.Add(
                        specialProperties.Find(e => e.name == Propertyname.Primitive0VertexUV0).value);
                }
                else if (property.name == Propertyname.Primitive1VertexUV0)
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = new List <List <Vector2> >();
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets.Add(
                        specialProperties.Find(e => e.name == Propertyname.Primitive1VertexUV0).value);
                }
            }


            foreach (Property property in combo)
            {
                switch (property.name)
                {
                case Propertyname.SpecularGlossinessOnMaterial0_Yes:
                    // Default. No changes needed
                    break;

                case Propertyname.SpecularGlossinessOnMaterial0_No:
                    material.Extensions = null;
                    break;

                case Propertyname.SpecularGlossinessOnMaterial1_Yes:
                    // Default. No changes needed
                    break;

                case Propertyname.SpecularGlossinessOnMaterial1_No:
                    material2.Extensions = null;
                    break;
                }
            }
            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Material = material;
            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Material = material2;

            return(wrapper);
        }
Beispiel #3
0
        public Runtime.GLTF SetModelAttributes(Runtime.GLTF wrapper, Runtime.Material material, List <Property> combo, ref glTFLoader.Schema.Gltf gltf)
        {
            // Determines which of the primitives will have the material and attributes applied to it
            var splitType = combo.Find(e => e.propertyGroup == 1);

            foreach (Property property in combo)
            {
                if (property.name == Propertyname.Primitives_Split1 ||
                    property.name == Propertyname.Primitives_Split2 ||
                    property.name == Propertyname.Primitives_Split3 ||
                    property.name == Propertyname.Primitives_Split4)
                {
                    // Same plane, but split into two triangle primitives
                    var primitive0 = specialProperties.Find(e => e.name == Propertyname.Primitive_0);
                    var primitive1 = specialProperties.Find(e => e.name == Propertyname.Primitive_1);
                    Runtime.MeshPrimitive prim0 = new Runtime.MeshPrimitive
                    {
                        Positions = primitive0.value.Positions,
                        Indices   = primitive0.value.Indices,
                    };
                    Runtime.MeshPrimitive prim1 = new Runtime.MeshPrimitive
                    {
                        Positions = primitive1.value.Positions,
                        Indices   = primitive1.value.Indices,
                    };
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives = new List <Runtime.MeshPrimitive>
                    {
                        prim0,
                        prim1
                    };
                }

                if (property.name == Propertyname.BaseColorTexture)
                {
                    if (material.MetallicRoughnessMaterial == null)
                    {
                        material.MetallicRoughnessMaterial = new Runtime.PbrMetallicRoughness();
                        material.MetallicRoughnessMaterial.BaseColorTexture = new Runtime.Texture();
                    }
                    material.MetallicRoughnessMaterial.BaseColorTexture.Source        = property.value;
                    material.MetallicRoughnessMaterial.BaseColorTexture.TexCoordIndex = 0;
                }

                if (property.name == Propertyname.NormalTexture)
                {
                    if (material.NormalTexture == null)
                    {
                        material.NormalTexture = new Runtime.Texture();
                    }
                    material.NormalTexture.Source        = property.value;
                    material.NormalTexture.TexCoordIndex = 0;
                }

                // Attributes set for only the first primitive
                if (splitType.name == Propertyname.Primitives_Split1)
                {
                    if (property.name == Propertyname.VertexNormal)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Normals = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Normals = null;
                    }
                    else if (property.name == Propertyname.VertexTangent)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Tangents = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Tangents = null;
                    }
                    else if (property.name == Propertyname.VertexColor_Vector4_Float)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Colors = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Colors = null;
                    }
                    else if (ReadmeStringHelper.GenerateNameWithSpaces(property.name.ToString()) ==
                             ReadmeStringHelper.GenerateNameWithSpaces(Propertyname.Primitive0VertexUV0.ToString())) // All UV0
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = new List <List <Vector2> >();
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = null;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive0VertexUV0).value);
                    }
                    else if (ReadmeStringHelper.GenerateNameWithSpaces(property.name.ToString()) ==
                             ReadmeStringHelper.GenerateNameWithSpaces(Propertyname.Primitive0VertexUV1.ToString())) // All UV1
                    {
                        if (wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets == null)
                        {
                            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = new List <List <Vector2> >();
                        }
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive0VertexUV1).value);
                    }
                }
                // Attributes set for only the second primitive
                else if (splitType.name == Propertyname.Primitives_Split2)
                {
                    if (property.name == Propertyname.VertexNormal)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Normals = null;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Normals = property.value;
                    }
                    else if (property.name == Propertyname.VertexTangent)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Tangents = null;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Tangents = property.value;
                    }
                    else if (property.name == Propertyname.VertexColor_Vector4_Float)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Colors = null;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Colors = property.value;
                    }
                    else if (ReadmeStringHelper.GenerateNameWithSpaces(property.name.ToString()) ==
                             ReadmeStringHelper.GenerateNameWithSpaces(Propertyname.Primitive1VertexUV0.ToString())) // All UV0
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = null;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = new List <List <Vector2> >();
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive1VertexUV0).value);
                    }
                    else if (ReadmeStringHelper.GenerateNameWithSpaces(property.name.ToString()) ==
                             ReadmeStringHelper.GenerateNameWithSpaces(Propertyname.Primitive1VertexUV1.ToString())) // All UV1
                    {
                        if (wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets == null)
                        {
                            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = new List <List <Vector2> >();
                        }
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive1VertexUV1).value);
                    }
                }
                // Attributes set for both of the primitives
                else if (splitType.name == Propertyname.Primitives_Split3)
                {
                    if (property.name == Propertyname.VertexNormal)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Normals = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Normals = property.value;
                    }
                    else if (property.name == Propertyname.VertexTangent)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Tangents = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Tangents = property.value;
                    }
                    else if (property.name == Propertyname.VertexColor_Vector4_Float)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Colors = property.value;
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Colors = property.value;
                    }
                    else if (property.name == Propertyname.Primitive0VertexUV0)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = new List <List <Vector2> >();
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive0VertexUV0).value);
                    }
                    else if (property.name == Propertyname.Primitive1VertexUV0)
                    {
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = new List <List <Vector2> >();
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive1VertexUV0).value);
                    }
                    else if (property.name == Propertyname.Primitive0VertexUV1)
                    {
                        if (wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets == null)
                        {
                            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = new List <List <Vector2> >();
                        }
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive0VertexUV1).value);
                    }
                    else if (property.name == Propertyname.Primitive1VertexUV1)
                    {
                        if (wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets == null)
                        {
                            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = new List <List <Vector2> >();
                        }
                        wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets.Add(
                            specialProperties.Find(e => e.name == Propertyname.Primitive1VertexUV1).value);
                    }
                }
                // Attributes set for neither of the primitives
                else if (splitType.name == Propertyname.Primitives_Split4)
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Normals = null;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Normals = null;

                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Tangents = null;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Tangents = null;

                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Colors = null;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Colors = null;

                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = null;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = null;

                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].TextureCoordSets = null;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].TextureCoordSets = null;
                }
            }

            // Material needs to be a deep copy here, or both primitives will get the same Mat.
            if (material.MetallicRoughnessMaterial != null)
            {
                if (splitType.name == Propertyname.Primitives_Split1 ||
                    splitType.name == Propertyname.Primitives_Split3)
                {
                    var mat = DeepCopy.CloneObject(material);
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Material = mat;
                }
                if (splitType.name == Propertyname.Primitives_Split2 ||
                    splitType.name == Propertyname.Primitives_Split3)
                {
                    var mat = DeepCopy.CloneObject(material);
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Material = mat;
                }
            }

            // Use the second UV if it has been set
            if (splitType.name != Propertyname.Primitives_Split4)
            {
                var prim0UV1 = combo.Find(e => e.name == Propertyname.Primitive0VertexUV1);
                var prim1UV1 = combo.Find(e => e.name == Propertyname.Primitive1VertexUV1);
                if (prim0UV1 != null)
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].
                    Material.MetallicRoughnessMaterial.BaseColorTexture.TexCoordIndex = 1;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].
                    Material.NormalTexture.TexCoordIndex = 1;
                }
                if (prim1UV1 != null)
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].
                    Material.MetallicRoughnessMaterial.BaseColorTexture.TexCoordIndex = 1;
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].
                    Material.NormalTexture.TexCoordIndex = 1;
                }
            }

            return(wrapper);
        }
        public Runtime.GLTF SetModelAttributes(Runtime.GLTF wrapper, Runtime.Material material0, List <Property> combo, ref glTFLoader.Schema.Gltf gltf)
        {
            // Same plane, but split into two triangle primitives
            var primitive1 = specialProperties.Find(e => e.name == Propertyname.Primitives_Split1);
            var primitive2 = specialProperties.Find(e => e.name == Propertyname.Primitives_Split2);

            Runtime.MeshPrimitive prim0 = new Runtime.MeshPrimitive
            {
                Positions = primitive1.value.Positions,
                Indices   = primitive1.value.Indices,
            };
            Runtime.MeshPrimitive prim2 = new Runtime.MeshPrimitive
            {
                Positions = primitive2.value.Positions,
                Indices   = primitive2.value.Indices,
            };
            wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives = new List <Runtime.MeshPrimitive>
            {
                prim0,
                prim2
            };

            // Make a second material
            var material1 = DeepCopy.CloneObject(material0);

            // Set the base color factor on both materials
            material0.MetallicRoughnessMaterial = new Runtime.PbrMetallicRoughness();
            material1.MetallicRoughnessMaterial = new Runtime.PbrMetallicRoughness();
            material0.MetallicRoughnessMaterial.BaseColorFactor = requiredProperty.Find(e => e.name == Propertyname.Material0WithBaseColorFactor).value;
            material1.MetallicRoughnessMaterial.BaseColorFactor = requiredProperty.Find(e => e.name == Propertyname.Material1WithBaseColorFactor).value;

            foreach (Property property in combo)
            {
                switch (property.name)
                {
                case Propertyname.Primitive0_Material0BaseColorFactor:
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Material = material0;
                    break;
                }

                case Propertyname.Primitive0_Material1BaseColorFactor:
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[0].Material = material1;
                    break;
                }

                case Propertyname.Primitive1_Material0BaseColorFactor:
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Material = material0;
                    break;
                }

                case Propertyname.Primitive1_Material1BaseColorFactor:
                {
                    wrapper.Scenes[0].Nodes[0].Mesh.MeshPrimitives[1].Material = material1;
                    break;
                }
                }
            }

            return(wrapper);
        }
Beispiel #5
0
        public Animation_NodeMisc(List <string> imageList)
        {
            var baseColorTexture = new Texture {
                Source = UseTexture(imageList, "BaseColor_Cube")
            };

            // There are no common properties in this model group that are reported in the readme.

            Model CreateModel(Action <List <Property>, List <AnimationChannel>, List <Node>, List <Animation> > setProperties)
            {
                var properties        = new List <Property>();
                var cubeMeshPrimitive = MeshPrimitive.CreateCube();

                // Apply the common properties to the gltf.
                cubeMeshPrimitive.Material = new Runtime.Material
                {
                    PbrMetallicRoughness = new PbrMetallicRoughness
                    {
                        BaseColorTexture = new TextureInfo {
                            Texture = baseColorTexture
                        },
                    },
                };
                var channels = new List <AnimationChannel>
                {
                    new AnimationChannel()
                };
                var nodes = new List <Node>
                {
                    new Node(),
                };
                var animations = new List <Animation>
                {
                    new Animation
                    {
                        Channels = channels
                    }
                };

                // Apply the properties that are specific to this gltf.
                setProperties(properties, channels, nodes, animations);

                // Create the gltf object.
                foreach (var node in nodes)
                {
                    node.Mesh = new Runtime.Mesh
                    {
                        MeshPrimitives = new List <Runtime.MeshPrimitive>
                        {
                            cubeMeshPrimitive
                        }
                    };
                }
                GLTF gltf = CreateGLTF(() => new Scene
                {
                    Nodes = nodes
                });

                gltf.Animations = animations;
                return(new Model
                {
                    Properties = properties,
                    GLTF = gltf,
                    Animated = true,
                });
            }

            void SetTranslationChannelTarget(AnimationChannel channel, Node node)
            {
                channel.Target = new AnimationChannelTarget
                {
                    Node = node,
                    Path = AnimationChannelTargetPath.Translation,
                };
            }

            void SetRotationChannelTarget(AnimationChannel channel, Node node)
            {
                channel.Target = new AnimationChannelTarget
                {
                    Node = node,
                    Path = AnimationChannelTargetPath.Rotation,
                };
            }

            void SetLinearSamplerForTranslation(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                        2.0f,
                        4.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        new Vector3(-0.1f, 0.0f, 0.0f),
                        new Vector3(0.1f, 0.0f, 0.0f),
                        new Vector3(-0.1f, 0.0f, 0.0f),
                    }),
                };
            }

            void SetLinearSamplerForHorizontalRotation(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                        1.0f,
                        2.0f,
                        3.0f,
                        4.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(-90.0f), 0.0f, 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                    }),
                };
            }

            void SetLinearSamplerForVerticalRotation(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                        1.0f,
                        2.0f,
                        3.0f,
                        4.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        Quaternion.CreateFromYawPitchRoll(0.0f, FloatMath.ToRadians(90.0f), 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(0.0f, FloatMath.ToRadians(-90.0f), 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(0.0f, FloatMath.ToRadians(90.0f), 0.0f),
                    }),
                };
            }

            void SetLinearSamplerForConstantRotation(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                        6.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        Quaternion.CreateFromYawPitchRoll(-FloatMath.Pi / 3.0f, 0.0f, 0.0f),
                        Quaternion.CreateFromYawPitchRoll(-FloatMath.Pi / 3.0f, 0.0f, 0.0f),
                    }),
                };
            }

            void SetLinearSamplerForTranslationStartsAboveZero(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        2.0f,
                        4.0f,
                        6.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        new Vector3(0.0f, -0.1f, 0.0f),
                        new Vector3(0.0f, 0.1f, 0.0f),
                        new Vector3(0.0f, -0.1f, 0.0f),
                    }),
                };
            }

            void SetLinearSamplerWithOneKey(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        0.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        new Vector3(-0.1f, 0.0f, 0.0f),
                    }),
                };
            }

            void SetLinearSamplerForRotationThatStartsAboveZero(AnimationChannel channel)
            {
                channel.Sampler = new AnimationSampler
                {
                    Interpolation = AnimationSamplerInterpolation.Linear,
                    Input         = Data.Create(new[]
                    {
                        1.0f,
                        2.0f,
                        3.0f,
                        4.0f,
                        5.0f,
                    }),
                    Output = Data.Create(new[]
                    {
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(-90.0f), 0.0f, 0.0f),
                        Quaternion.Identity,
                        Quaternion.CreateFromYawPitchRoll(FloatMath.ToRadians(90.0f), 0.0f, 0.0f),
                    }),
                };
            }

            void CreateMultipleChannelsWithUniqueTargets(List <AnimationChannel> channels, Node node)
            {
                // The first channel is already added as a common property.
                channels.Add(new AnimationChannel());

                SetTranslationChannelTarget(channels[0], node);
                SetRotationChannelTarget(channels[1], node);

                var samplerPropertiesList = new List <Property>();

                SetLinearSamplerForTranslation(channels[0]);
                SetLinearSamplerForHorizontalRotation(channels[1]);
            }

            void CreateMultipleChannelsWithDifferentTimes(List <AnimationChannel> channels, Node node)
            {
                // The first channel is already added as a common property.
                channels.Add(new AnimationChannel());

                SetTranslationChannelTarget(channels[0], node);
                SetRotationChannelTarget(channels[1], node);

                SetLinearSamplerForTranslationStartsAboveZero(channels[0]);
                SetLinearSamplerForRotationThatStartsAboveZero(channels[1]);
            }

            void CreateMultipleChannelsForDifferentNodes(List <AnimationChannel> channels, Node node0, Node node1)
            {
                // The first channel is already added as a common property.
                channels.Add(new AnimationChannel());

                SetRotationChannelTarget(channels[0], node0);
                SetRotationChannelTarget(channels[1], node1);

                SetLinearSamplerForHorizontalRotation(channels[0]);
                SetLinearSamplerForVerticalRotation(channels[1]);
            }

            Models = new List <Model>
            {
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Multiple channels
                    CreateMultipleChannelsWithUniqueTargets(channels, nodes[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There are two channels. The first channel targets translation. The second channel targets rotation. The start and end times of both channels are `0.0` and `4.0` respectively."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Curve that doesn't start at zero
                    SetRotationChannelTarget(channels[0], nodes[0]);
                    SetLinearSamplerForRotationThatStartsAboveZero(channels[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There is one channel with a non-zero start time. The channel targets rotation. The start time is `1.0`."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Two channels with different start/end times
                    CreateMultipleChannelsWithDifferentTimes(channels, nodes[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There are two channels with different start and end times. The first channel targets translation with start and end times of `2.0` and `6.0` respectively. The second channel targets rotation with start and end times of `1.0` and `5.0` respectively."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Has only one key
                    SetTranslationChannelTarget(channels[0], nodes[0]);
                    SetLinearSamplerWithOneKey(channels[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There is one channel with only one keyframe. The channel targets translation with a value of <code>[-0.1,&nbsp;0.0,&nbsp;0.0]</code>."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Creates a second node based on the existing node, and applies a transform to help differentiate them.
                    nodes.Add(DeepCopy.CloneObject(nodes[0]));
                    nodes[0].Translation = new Vector3(-0.2f, 0.0f, 0.0f);
                    nodes[1].Translation = new Vector3(0.2f, 0.0f, 0.0f);
                    nodes[0].Scale       = new Vector3(0.5f, 0.5f, 0.5f);
                    nodes[1].Scale       = new Vector3(0.5f, 0.5f, 0.5f);

                    // One animation, two channels for two nodes.
                    CreateMultipleChannelsForDifferentNodes(channels, nodes[0], nodes[1]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There are two channels with different nodes. The first channel targets the left node and rotation along the X axis. The second channel targets the right node and rotation along the Y axis."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Rotate the model, and then apply the same target animation to it (Animation overrides)
                    nodes[0].Rotation = Quaternion.CreateFromYawPitchRoll(FloatMath.Pi / 3.0f, 0.0f, 0.0f);
                    SetRotationChannelTarget(channels[0], nodes[0]);
                    SetLinearSamplerForConstantRotation(channels[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There is one channel that targets a node. The node has a rotation of <code>[0.0,&nbsp;0.5,&nbsp;0.0,&nbsp;0.866]</code>. The channel overrides the rotation of the node to a different constant value of <code>[0.0,&nbsp;-0.5,&nbsp;0.0,&nbsp;0.866]</code>."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Rotate the model, and then apply an translation animation to it (Animation doesn't override rotation)
                    nodes[0].Rotation = Quaternion.CreateFromYawPitchRoll(FloatMath.Pi / 3.0f, 0.0f, 0.0f);
                    SetTranslationChannelTarget(channels[0], nodes[0]);
                    SetLinearSamplerForTranslation(channels[0]);
                    properties.Add(new Property(PropertyName.Description,
                                                "There is one channel that targets a node. The node has a rotation of <code>[0.0,&nbsp;0.5,&nbsp;0.0,&nbsp;0.866]</code>. The channel targets the translation of the node."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Two animations. One rotates, the other translates. They should not interact or bleed across.
                    var channel = new AnimationChannel();
                    SetTranslationChannelTarget(channel, nodes[0]);
                    SetLinearSamplerForTranslation(channel);
                    animations.Add(new Animation
                    {
                        Channels = new[]
                        {
                            channel
                        }
                    });

                    // The first animation is already added as an empty common property.
                    SetRotationChannelTarget(channels[0], nodes[0]);
                    SetLinearSamplerForHorizontalRotation(channels[0]);

                    properties.Add(new Property(PropertyName.Description,
                                                "There are two animations, each with one channel. The first animation's channel targets rotation. The second animation's channel targets translation."));
                }),
                CreateModel((properties, channels, nodes, animations) =>
                {
                    // Multiple channels, but one omits node (the channel with no target node is ignored per the spec).
                    CreateMultipleChannelsForDifferentNodes(channels, null, nodes[0]);

                    properties.Add(new Property(PropertyName.Description,
                                                "There are two channels. The first channel has a rotation along the X axis but does not specify a node. The second channel does target the node and has a rotation along the Y axis."));
                }),
            };

            GenerateUsedPropertiesList();
        }