// Adds a glTF attribute, as described by name, type, and semantic, to the given technique tech.
 private static void AddAttribute(string name, GlTF_Technique.Type type,
                                  GlTF_Technique.Semantic semantic, GlTF_Technique tech)
 {
     GlTF_Technique.Parameter tParam = new GlTF_Technique.Parameter();
     tParam.name     = name;
     tParam.type     = type;
     tParam.semantic = semantic;
     tech.parameters.Add(tParam);
     GlTF_Technique.Attribute tAttr = new GlTF_Technique.Attribute();
     tAttr.name  = "a_" + name;
     tAttr.param = tParam.name;
     tech.attributes.Add(tAttr);
 }
    // Adds a glTF uniform, as described by name, type, and semantic, to the given technique tech. If
    // node is non-null, that is also included (e.g. for lights).
    private void AddUniform(
        IExportableMaterial exportableMaterial,
        string name, GlTF_Technique.Type type,
        GlTF_Technique.Semantic semantic, GlTF_Node node = null)
    {
        //var techName = GlTF_Technique.GetNameFromObject(matObjName);
        var tech = GlTF_Writer.GetTechnique(G, exportableMaterial);

        GlTF_Technique.Parameter tParam = new GlTF_Technique.Parameter();
        tParam.name     = name;
        tParam.type     = type;
        tParam.semantic = semantic;
        if (node != null)
        {
            tParam.node = node;
        }
        tech.parameters.Add(tParam);
        GlTF_Technique.Uniform tUniform = new GlTF_Technique.Uniform();
        tUniform.name  = "u_" + name;
        tUniform.param = tParam.name;
        tech.uniforms.Add(tUniform);
    }
    // Updates glTF technique tech by adding all relevant attributes.
    // Pass:
    //   mesh - (optional) the attributes of some mesh that uses this material, for sanity-checking.
    private void AddAllAttributes(
        GlTF_Technique tech, IExportableMaterial exportableMaterial, GlTF_Attributes mesh)
    {
        GlTF_VertexLayout layout = new GlTF_VertexLayout(G, exportableMaterial.VertexLayout);

        if (mesh != null)
        {
            GlTF_VertexLayout meshLayout = mesh.m_layout;
            if (layout != meshLayout)
            {
                if (meshLayout.GetTexcoordSize(2) > 0)
                {
                    // We funnel timestamps in through GeometryPool.texcoord2 and write them out as
                    // _TB_TIMESTAMP.  Thus, the Pool's layout has a texcoord2 but the material's layout does
                    // not. This is a mismatch between the mesh data and the material props, but:
                    // 1. Timestamp isn't intended to be funneled to the material, so it's correct that the
                    //    material layoutdoesn't have texcoord2
                    // 2. It's fine if material attrs are a subset of the mesh attrs
                    // 3. This only affects gltf1; only materials with techniques need to enum their attrs.
                    // Maybe this check should be layout.IsSubset(meshLayout).
                    /* ignore this mismatch */
                }
                else
                {
                    Debug.LogWarning($"Layout for {exportableMaterial.DurableName} doesn't match mesh's");
                }
            }
        }

        // Materials are things that are shared across multiple meshes.
        // Material creation shouldn't depend on data specific to a particular mesh.
        // But it does. It's a hack.
        // Rather than do something reasonable like this:
        //
        //   Create material's technique's attributes based on layout
        //   Create mesh and its accessors based on layout
        //
        // We do this:
        //
        //   Create mesh and its accessors based on layout
        //     Lazily create the material used by the mesh
        //       Create material's technique's attributes based on the accessors
        //       of the last mesh we created
        AddAttribute("position", layout.PositionInfo.techniqueType,
                     GlTF_Technique.Semantic.POSITION, tech);
        if (layout.NormalInfo != null)
        {
            AddAttribute("normal", layout.NormalInfo.Value.techniqueType,
                         GlTF_Technique.Semantic.NORMAL, tech);
        }
        if (layout.ColorInfo != null)
        {
            AddAttribute("color", layout.ColorInfo.Value.techniqueType,
                         GlTF_Technique.Semantic.COLOR, tech);
        }
        if (layout.TangentInfo != null)
        {
            AddAttribute("tangent", layout.TangentInfo.Value.techniqueType,
                         GlTF_Technique.Semantic.TANGENT, tech);
        }
        // TODO: remove; this accessor isn't used. Instead, shaders use texcoord1.w
        if (layout.PackVertexIdIntoTexcoord1W)
        {
            AddAttribute("vertexId", GlTF_Technique.Type.FLOAT /* hardcoded, but this is gong away */,
                         GlTF_Technique.Semantic.UNKNOWN, tech);
        }
        for (int i = 0; i < 4; ++i)
        {
            var texcoordInfo = layout.GetTexcoordInfo(i);
            if (texcoordInfo != null)
            {
                GlTF_Technique.Semantic semantic = GlTF_Technique.Semantic.TEXCOORD_0 + i;
                AddAttribute($"texcoord{i}", texcoordInfo.Value.techniqueType, semantic, tech);
            }
        }
    }