// Adds material and sets up its dependent technique, program, shaders. This should be called
    // after adding meshes, but before populating lights, textures, etc.
    // Pass:
    //   hack - attributes of some mesh that uses this material
    public void AddMaterialWithDependencies(
        IExportableMaterial exportableMaterial,
        string meshNamespace,
        GlTF_Attributes hack)
    {
        GlTF_Material gltfMtl = G.CreateMaterial(meshNamespace, exportableMaterial);

        // Set up technique.
        GlTF_Technique tech = GlTF_Writer.CreateTechnique(G, exportableMaterial);

        gltfMtl.instanceTechniqueName = tech.name;
        GlTF_Technique.States states = null;
        if (m_techniqueStates.ContainsKey(exportableMaterial))
        {
            states = m_techniqueStates[exportableMaterial];
        }

        if (states == null)
        {
            // Unless otherwise specified the preset, enable z-buffering.
            states        = new GlTF_Technique.States();
            states.enable = new[] { GlTF_Technique.Enable.DEPTH_TEST }.ToList();
        }
        tech.states = states;
        AddAllAttributes(tech, exportableMaterial, hack);
        tech.AddDefaultUniforms(G.RTCCenter != null);

        // Add program.
        GlTF_Program program = new GlTF_Program(G);

        program.name = GlTF_Program.GetNameFromObject(exportableMaterial);
        tech.program = program.name;
        foreach (var attr in tech.attributes)
        {
            program.attributes.Add(attr.name);
        }
        G.programs.Add(program);

        // Add vertex and fragment shaders.
        GlTF_Shader vertShader = new GlTF_Shader(G);

        vertShader.name      = GlTF_Shader.GetNameFromObject(exportableMaterial, GlTF_Shader.Type.Vertex);
        program.vertexShader = vertShader.name;
        vertShader.type      = GlTF_Shader.Type.Vertex;
        vertShader.uri       = ExportFileReference.CreateHttp(exportableMaterial.VertShaderUri);
        G.shaders.Add(vertShader);

        GlTF_Shader fragShader = new GlTF_Shader(G);

        fragShader.name        = GlTF_Shader.GetNameFromObject(exportableMaterial, GlTF_Shader.Type.Fragment);
        program.fragmentShader = fragShader.name;
        fragShader.type        = GlTF_Shader.Type.Fragment;
        fragShader.uri         = ExportFileReference.CreateHttp(exportableMaterial.FragShaderUri);
        G.shaders.Add(fragShader);
    }
 /// Returns an ExportFileReference given an absolute http:// uri.
 /// Depending on settings, this may become a relative reference in the gltf.
 private ExportFileReference CreateExportFileReferenceFromHttp(string httpUri)
 {
     if (!AllowHttpUri)
     {
         string localPath = HostedUriToLocalFilename(httpUri);
         if (localPath != null)
         {
             return(ExportFileReference.GetOrCreateSafeLocal(
                        G.m_disambiguationContext,
                        Path.GetFileName(localPath), Path.GetDirectoryName(localPath),
                        "Brush_" + Path.GetFileName(localPath)));
         }
         Debug.LogWarning($"Cannot convert {httpUri} to local");
     }
     return(ExportFileReference.CreateHttp(httpUri));
 }
    /// Adds a texture parameter + uniform to the specified material.
    /// As a side effect, auto-creates textures, images, and maybe a sampler if necessary.
    /// Pass:
    ///   matObjName - the material
    ///   texParam - name of the material parameter to add
    ///   fileRef - file containing texture data
    public void AddTextureToMaterial(
        IExportableMaterial exportableMaterial, string texParam, ExportFileReference fileRef)
    {
        GlTF_Material material = G.materials[exportableMaterial];

        GlTF_Sampler sampler = GlTF_Sampler.LookupOrCreate(
            G, GlTF_Sampler.MagFilter.LINEAR, GlTF_Sampler.MinFilter.LINEAR_MIPMAP_LINEAR);

        // The names only matter for gltf1, so keep them similar for easier diffing.
        // Essentially, this names the image and texture after the first material that wanted them.
        string matNameAndParam = $"{exportableMaterial.UniqueName:D}_{texParam}";
        var    img             = GlTF_Image.LookupOrCreate(G, fileRef, proposedName: matNameAndParam);
        var    tex             = GlTF_Texture.LookupOrCreate(G, img, sampler, proposedName: matNameAndParam);

        material.values.Add(new GlTF_Material.TextureKV(key: texParam, texture: tex));

        // Add texture-related parameter and uniform.
        AddUniform(exportableMaterial,
                   texParam, GlTF_Technique.Type.SAMPLER_2D, GlTF_Technique.Semantic.UNKNOWN, null);
    }
    // Pass:
    //   meshNamespace - A string used as the "namespace" of the mesh that owns this material.
    //     Useful for uniquifying names (texture file names, material names, ...) in a
    //     human-friendly way.
    //   hack - attributes of some mesh that uses this material
    private void ExportMaterial(
        SceneStatePayload payload,
        string meshNamespace,
        IExportableMaterial exportableMaterial,
        GlTF_Attributes hack)
    {
        //
        // Set culling and blending modes.
        //
        GlTF_Technique.States states = new GlTF_Technique.States();
        m_techniqueStates[exportableMaterial] = states;
        // Everyone gets depth test
        states.enable = new[] { GlTF_Technique.Enable.DEPTH_TEST }.ToList();

        if (exportableMaterial.EnableCull)
        {
            states.enable.Add(GlTF_Technique.Enable.CULL_FACE);
        }

        if (exportableMaterial.BlendMode == ExportableMaterialBlendMode.AdditiveBlend)
        {
            states.enable.Add(GlTF_Technique.Enable.BLEND);
            // Blend array format: [srcRGB, dstRGB, srcAlpha, dstAlpha]
            states.functions["blendFuncSeparate"] =
                new GlTF_Technique.Value(G, new Vector4(1.0f, 1.0f, 1.0f, 1.0f)); // Additive.
            states.functions["depthMask"] = new GlTF_Technique.Value(G, false);   // No depth write.
            // Note: If we switch bloom to use LDR color, adding the alpha channels won't do.
            // GL_MIN would be a good choice for alpha, but it's unsupported by glTF 1.0.
        }
        else if (exportableMaterial.BlendMode == ExportableMaterialBlendMode.AlphaBlend)
        {
            states.enable.Add(GlTF_Technique.Enable.BLEND);
            // Blend array format: [srcRGB, dstRGB, srcAlpha, dstAlpha]
            // These enum values correspond to: [ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE_MINUS_SRC_ALPHA]
            states.functions["blendFuncSeparate"] =
                new GlTF_Technique.Value(G, new Vector4(1.0f, 771.0f, 1.0f, 771.0f)); // Blend.
            states.functions["depthMask"] = new GlTF_Technique.Value(G, true);
        }
        else
        {
            // Standard z-buffering: Enable depth write.
            states.functions["depthMask"] = new GlTF_Technique.Value(G, true);
        }

        // First add the material, then export any per-material attributes, such as shader uniforms.
        AddMaterialWithDependencies(exportableMaterial, meshNamespace, hack);

        // Add lighting for this material.
        AddLights(exportableMaterial, payload);

        //
        // Export shader/material parameters.
        //

        foreach (var kvp in exportableMaterial.FloatParams)
        {
            ExportShaderUniform(exportableMaterial, kvp.Key, kvp.Value);
        }
        foreach (var kvp in exportableMaterial.ColorParams)
        {
            ExportShaderUniform(exportableMaterial, kvp.Key, kvp.Value);
        }
        foreach (var kvp in exportableMaterial.VectorParams)
        {
            ExportShaderUniform(exportableMaterial, kvp.Key, kvp.Value);
        }
        foreach (var kvp in exportableMaterial.TextureSizes)
        {
            float width  = kvp.Value.x;
            float height = kvp.Value.y;
            ExportShaderUniform(exportableMaterial, kvp.Key + "_TexelSize",
                                new Vector4(1 / width, 1 / height, width, height));
        }

        //
        // Export textures.
        //
        foreach (var kvp in exportableMaterial.TextureUris)
        {
            string textureName = kvp.Key;
            string textureUri  = kvp.Value;

            ExportFileReference fileRef;
            if (ExportFileReference.IsHttp(textureUri))
            {
                // Typically this happens for textures used by BrushDescriptor materials
                fileRef = CreateExportFileReferenceFromHttp(textureUri);
            }
            else
            {
                fileRef = ExportFileReference.GetOrCreateSafeLocal(
                    G.m_disambiguationContext, textureUri, exportableMaterial.UriBase,
                    $"{meshNamespace}_{Path.GetFileName(textureUri)}");
            }

            AddTextureToMaterial(exportableMaterial, textureName, fileRef);
        }
    }