// Returns the gltf mesh that corresponds to the payload, or null.
    // Currently, null is only returned if the payload's 'geometry pool is empty.
    // Pass a localXf to override the default, which is to use base.xform
    public GlTF_Node ExportMeshPayload(
        SceneStatePayload payload,
        BaseMeshPayload meshPayload,
        [CanBeNull] GlTF_Node parent,
        Matrix4x4?localXf = null)
    {
        var node = ExportMeshPayload_NoMaterial(meshPayload, parent, localXf);

        if (node != null)
        {
            IExportableMaterial exportableMaterial = meshPayload.exportableMaterial;
            if (!G.materials.ContainsKey(exportableMaterial))
            {
                var prims = node.m_mesh?.primitives;
                var attrs = (prims != null && prims.Count > 0) ? prims[0].attributes : null;
                if (attrs != null)
                {
                    ExportMaterial(payload, meshPayload.MeshNamespace, exportableMaterial, attrs);
                    Debug.Assert(G.materials.ContainsKey(exportableMaterial));
                }
            }
        }

        return(node);
    }
    // This does once-per-light work, as well as once-per-material-per-light work.
    // So this ends up being called multiple times with the same parameters, except
    // for matObjName.
    // matObjName is the name of the material being exported
    // lightObjectName is the name of the light
    public void ExportLight(LightPayload payload, IExportableMaterial exportableMaterial)
    {
        ObjectName lightNodeName = new ObjectName(payload.legacyUniqueName); // does need to be unique

        // Add the light to the scene -- this does _not_ need to be done per-material.
        // As a result, the node will generally have already been created.
        GlTF_Node node = GlTF_Node.GetOrCreate(G, lightNodeName, payload.xform, null, out _);

        node.lightNameThatDoesNothing = payload.name;

        // The names of the uniforms can be anything, really. Named after the light is the most
        // logical choice, but note that nobody checks that two lights don't have the same name.
        // Thankfully for Tilt Brush, they don't.
        // This should probably have used a guaranteed-unique name from the start but I don't want
        // to change it now because it'd break my diffs and be kind of ugly.
        string lightUniformPrefix = payload.name;

        AddUniform(exportableMaterial, lightUniformPrefix + "_matrix",
                   GlTF_Technique.Type.FLOAT_MAT4, GlTF_Technique.Semantic.MODELVIEW, node);

        // Add light color.
        GlTF_Material mtl = G.materials[exportableMaterial];
        var           val = new GlTF_Material.ColorKV {
            key   = lightUniformPrefix + "_color",
            color = payload.lightColor
        };

        mtl.values.Add(val);
        AddUniform(exportableMaterial, val.key,
                   GlTF_Technique.Type.FLOAT_VEC4, GlTF_Technique.Semantic.UNKNOWN, node);
    }
 // Public only for use by GlTF_Globals
 public GlTF_Material(GlTF_Globals globals, IExportableMaterial exportableMaterial)
     : base(globals)
 {
     this.ExportableMaterial = exportableMaterial;
     this.name = GlTF_Material.GetNameFromObject(exportableMaterial);
     // PresentationNameOverride is set by GlTF_Globals in order to make it unique-ish
 }
    // 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);
    }
    // Export a single shader vector uniform
    public void ExportShaderUniform(
        IExportableMaterial exportableMaterial, string name, Vector4 value)
    {
        GlTF_Material mtl     = G.materials[exportableMaterial];
        var           vec_val = new GlTF_Material.VectorKV {
            key = name, vector = value
        };

        mtl.values.Add(vec_val);
        AddUniform(exportableMaterial, vec_val.key,
                   GlTF_Technique.Type.FLOAT_VEC4, GlTF_Technique.Semantic.UNKNOWN);
    }
    // Should be called per material.
    public void ExportAmbientLight(IExportableMaterial exportableMaterial, Color color)
    {
        GlTF_Material mtl = G.materials[exportableMaterial];
        var           val = new GlTF_Material.ColorKV {
            key   = "ambient_light_color",
            color = color
        };

        mtl.values.Add(val);
        AddUniform(exportableMaterial, val.key,
                   GlTF_Technique.Type.FLOAT_VEC4, GlTF_Technique.Semantic.UNKNOWN);
    }
Exemple #7
0
 static void CreateTextureUris(pxr.UsdPrim shaderPrim,
                               IExportableMaterial material)
 {
     if (material.SupportsDetailedMaterialInfo)
     {
         foreach (var kvp in material.TextureUris)
         {
             var attr = shaderPrim.CreateAttribute(new pxr.TfToken("info:textureUris:" + kvp.Key),
                                                   SdfValueTypeNames.String);
             attr.Set(kvp.Value);
         }
     }
 }
    // Export a single shader float uniform
    public void ExportShaderUniform(
        IExportableMaterial exportableMaterial, string name, float value)
    {
        GlTF_Material mtl       = G.materials[exportableMaterial];
        var           float_val = new GlTF_Material.FloatKV {
            key   = name,
            value = value
        };

        mtl.values.Add(float_val);
        AddUniform(exportableMaterial, float_val.key,
                   GlTF_Technique.Type.FLOAT, GlTF_Technique.Semantic.UNKNOWN);
    }
Exemple #9
0
        /// Collects data from the exportable material and converts it to a ShaderSample.
        static ExportShaderSample GetShaderSample(IExportableMaterial material)
        {
            var shaderSample = new ExportShaderSample();

            shaderSample.useSpecularWorkflow.defaultValue = 1;
            shaderSample.roughness.defaultValue           = 0.5f;
            shaderSample.specularColor.defaultValue       = new Vector3(.1f, .1f, .1f);

            shaderSample.durableName = material.DurableName;
            shaderSample.uniqueName  = material.UniqueName;
            shaderSample.uriBase     = material.UriBase;
            if (material.SupportsDetailedMaterialInfo)
            {
                shaderSample.vertShaderUri = material.VertShaderUri;
                shaderSample.fragShaderUri = material.FragShaderUri;
                shaderSample.enableCull    = material.EnableCull;

                if (material.FloatParams.ContainsKey("SpecColor"))
                {
                    var c = material.ColorParams["SpecColor"].linear;
                    shaderSample.specularColor.defaultValue = new Vector3(c.r, c.g, c.b);
                }

                if (material.FloatParams.ContainsKey("Color"))
                {
                    var c = material.ColorParams["Color"].linear;
                    shaderSample.diffuseColor.defaultValue = new Vector3(c.r, c.g, c.b);
                }

                if (material.FloatParams.ContainsKey("Shininess"))
                {
                    shaderSample.roughness.defaultValue = 1.0f - material.FloatParams["Shininess"];
                }
            }
            shaderSample.blendMode      = material.BlendMode;
            shaderSample.emissiveFactor = material.EmissiveFactor;

            shaderSample.vertexLayout                = new ExportVertexLayout();
            shaderSample.vertexLayout.bUseColors     = material.VertexLayout.bUseColors;
            shaderSample.vertexLayout.bUseNormals    = material.VertexLayout.bUseNormals;
            shaderSample.vertexLayout.bUseTangents   = material.VertexLayout.bUseTangents;
            shaderSample.vertexLayout.bUseVertexIds  = material.VertexLayout.bUseVertexIds;
            shaderSample.vertexLayout.normalSemantic = material.VertexLayout.normalSemantic;
            shaderSample.vertexLayout.uv0Semantic    = material.VertexLayout.texcoord0.semantic;
            shaderSample.vertexLayout.uv0Size        = material.VertexLayout.texcoord0.size;
            shaderSample.vertexLayout.uv1Semantic    = material.VertexLayout.texcoord1.semantic;
            shaderSample.vertexLayout.uv1Size        = material.VertexLayout.texcoord1.size;

            return(shaderSample);
        }
    // Should be called per-material.
    private void AddLights(IExportableMaterial exportableMaterial,
                           ExportUtils.SceneStatePayload payload)
    {
#if DEBUG_GLTF_EXPORT
        Debug.LogFormat("Exporting {0} lights.", payload.lights.elements.Count);
#endif
        foreach (var light in payload.lights.lights)
        {
            // A light requires a node for the matrix, but has no mesh.
            ExportLight(light, exportableMaterial);
        }

        // We include the ambient light color in the material (no transform needed).
        ExportAmbientLight(exportableMaterial, RenderSettings.ambientLight);
    }
Exemple #11
0
        /// Authors USD Texture and PrimvarReader shader nodes for the given exportable material.
        ///
        /// Note that textureUris are stored as metadata and only the "Export Texture" is authored as a
        /// true texture in the shading network. This is due to the fact that the actual material textures
        /// are not currently exported with the USD file, but export textures are.
        ///
        /// Returns the texture path if a texture node was created, otherwise null.
        static string CreateAlphaTexture(USD.NET.Scene scene,
                                         string shaderPath,
                                         IExportableMaterial material)
        {
            // Currently, only export texture is previewed in USD.
            // Create an input parameter to read the texture, e.g. inputs:_MainTex.
            if (!material.HasExportTexture())
            {
                return(null);
            }

            string texFile = SanitizeIdentifier(material.DurableName)
                             + System.IO.Path.GetExtension(material.GetExportTextureFilename());

            // Establish paths in the USD scene.
            string texturePath = GetTexturePath(material, "MainTex", shaderPath);
            string primvarPath = GetPrimvarPath(material, "uv", texturePath);

            // Create the texture Prim.
            var texture = new ExportTextureSample();

            // Connect the texture to the file on disk.
            texture.file.defaultValue = new pxr.SdfAssetPath(texFile);
            texture.st.SetConnectedPath(primvarPath, "outputs:result");
            scene.Write(texturePath, texture);

            if (scene.GetPrimAtPath(new pxr.SdfPath(primvarPath)) == null)
            {
                if (material.VertexLayout.texcoord0.size == 2)
                {
                    var primvar = new PrimvarReader2fSample("uv");
                    scene.Write(primvarPath, primvar);
                }
                else if (material.VertexLayout.texcoord0.size == 3)
                {
                    var primvar = new PrimvarReader3fSample("uv");
                    scene.Write(primvarPath, primvar);
                }
                else if (material.VertexLayout.texcoord0.size == 4)
                {
                    var primvar = new PrimvarReader4fSample("uv");
                    scene.Write(primvarPath, primvar);
                }
            }

            return(texturePath);
        }
    /// 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);
    }
    // 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);
    }
    // Adds to gltfMesh the glTF dependencies (primitive, material, technique, program, shaders)
    // required by unityMesh, using matObjName for naming the various material-related glTF
    // components. This does not add any geometry from the mesh (that's done separately using
    // GlTF_Mesh.Populate()).
    //
    // This does not create the material either. It adds a reference to a material that
    // presumably will be created very soon (if it hasn't previously been created).
    private void AddMeshDependencies(
        ObjectName meshName, IExportableMaterial exportableMaterial, GlTF_Mesh gltfMesh,
        GlTF_VertexLayout gltfLayout)
    {
        GlTF_Primitive primitive = new GlTF_Primitive(
            new GlTF_Attributes(G, meshName, gltfLayout));

        GlTF_Accessor indexAccessor = G.CreateAccessor(
            GlTF_Accessor.GetNameFromObject(meshName, "indices_0"),
            GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.USHORT,
            isNonVertexAttributeAccessor: true);

        primitive.indices = indexAccessor;
        if (gltfMesh.primitives.Count > 0)
        {
            Debug.LogError("More than one primitive per mesh is unimplemented and unsupported");
        }
        gltfMesh.primitives.Add(primitive);

        // This needs to be a forward-reference (ie, by name) because G.materials[exportableMaterial]
        // may not have been created yet.
        primitive.materialName = GlTF_Material.GetNameFromObject(exportableMaterial);
    }
Exemple #15
0
 /// Memoized version of CreateFbxMaterial
 /// Guarantees 1:1 correspondence between IEM, FbxMaterial, and FbxMaterial.name
 public FbxSurfaceMaterial GetOrCreateFbxMaterial(
     string meshNamespace,
     IExportableMaterial exportableMaterial)
 {
     // Unity's able to ensure a 1:1 correspondence between FBX materials and generated Unity
     // materials. However, users like TBT who go through the OnAssignMaterialModel interface cannot
     // distinguish between "two unique materials with the same name" and "one material being
     // used multiple times".
     //
     // Since TBT can't detect reference-equality of FbxMaterial, we have to help it by
     // making name-equality the same as reference-equality. IOW distinct materials need
     // distinct names.
     if (m_createdMaterials.TryGetValue(exportableMaterial, out FbxSurfaceMaterial mtl))
     {
         return(mtl);
     }
     else
     {
         FbxSurfaceMaterial newMtl = ExportFbx.CreateFbxMaterial(
             this, meshNamespace, exportableMaterial, m_createdMaterialNames);
         m_createdMaterials[exportableMaterial] = newMtl;
         return(newMtl);
     }
 }
 // Returns the name used by the actual GlTF_Material.
 // This is what is referred to throughout the code as "matName" or "material.name".
 public static string GetNameFromObject(IExportableMaterial exportableMaterial)
 {
     return($"material_{exportableMaterial.UniqueName:D}");
 }
    // 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);
            }
        }
    }
Exemple #18
0
        // Helper for GetModelMeshPayloads() -- this is the level 4 iteration.
        // Pass:
        //   mesh / meshMaterials - the Mesh to generate geometry for
        //   payload - the payload being exported
        //   prefab - the owner "prefab"
        //   meshIndex - the index of this Mesh within its owner "prefab"
        static IEnumerable <PrefabGeometry> GetPrefabGameObjMeshes(
            Mesh mesh, Material[] meshMaterials,
            SceneStatePayload payload, Model prefab, int meshIndex)
        {
            if (meshMaterials.Length != mesh.subMeshCount)
            {
                throw new ArgumentException("meshMaterials length");
            }
            string exportName = prefab.GetExportName();

            IExportableMaterial[] exportableMaterials = meshMaterials.Select(
                mat => prefab.GetExportableMaterial(mat)).ToArray();
            VertexLayout?[] layouts = exportableMaterials.Select(iem => iem?.VertexLayout).ToArray();
            // TODO(b/142396408)
            // Is it better to write color/texcoord that we don't need, just to satisfy the layout?
            // Or should we remove color/texcoord from the layout if the Mesh doesn't have them?
            // Right now we do the former. I think the fix should probably be in the collector;
            // it shouldn't return an exportableMaterial that is a superset of what the mesh contains.
            GeometryPool[] subMeshPools = GeometryPool.FromMesh(
                mesh, layouts, fallbackColor: Color.white, useFallbackTexcoord: true);

            for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++)
            {
                // See if this material is a Blocks material.
                Material mat = meshMaterials[subMeshIndex];

                // At import time, ImportMaterialCollector now ensures that _every_ UnityEngine.Material
                // imported also comes with an IExportableMaterial of some kind (BrushDescriptor for
                // Blocks/TB, and DynamicExportableMaterial for everything else). This lookup shouldn't fail.
                IExportableMaterial exportableMaterial = exportableMaterials[subMeshIndex];
                if (exportableMaterial == null)
                {
                    Debug.LogWarning($"Model {prefab.HumanName} has a non-exportable material {mat.name}");
                    continue;
                }

                GeometryPool geo = subMeshPools[subMeshIndex];
                // TODO(b/140634751): lingering sanity-checking; we can probably remove this for M24
                if (geo.Layout.bUseColors)
                {
                    Debug.Assert(geo.m_Colors.Count == geo.NumVerts);
                }

                // Important: This transform should only be applied once per prefab, since the pools are
                // shared among all the instances, and cannot include any mesh-local transformations.
                // If the pools share data (which is currently impossible), then additionally
                // the transform should only be applied once to each set of vertex data.
                ExportUtils.ConvertUnitsAndChangeBasis(geo, payload);

                if (payload.reverseWinding)
                {
                    // There are many ways of reversing winding; choose the one that matches Unity's fbx flip
                    ExportUtils.ReverseTriangleWinding(geo, 1, 2);
                }

                yield return(new PrefabGeometry
                {
                    model = prefab,
                    name = exportName + "_" + meshIndex + "_" + subMeshIndex,
                    pool = geo,
                    exportableMaterial = exportableMaterial,
                });
            }
        }
    // 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);
        }
    }
Exemple #20
0
 // Example: /Sketch/Strokes/Materials/Material_Light
 static string GetMaterialPath(IExportableMaterial material, string rootPath)
 {
     return(rootPath + "/Materials/Material_" + SanitizeIdentifier(material.DurableName));
 }
Exemple #21
0
 // Example: /Sketch/Strokes/Materials/Material_Light/Shader_Light
 static string GetShaderPath(IExportableMaterial material, string parentMaterialPath)
 {
     return(parentMaterialPath + "/Shader_" + SanitizeIdentifier(material.DurableName));
 }
Exemple #22
0
        /// Authors a USD Material, Shader, parameters and connections between the two.
        /// The USD shader structure consists of a Material, which is connected to a shader output. The
        /// Shader consists of input parameters which are either connected to other shaders or in the case
        /// of public parameters, back to the material which is the public interface for the shading
        /// network.
        ///
        /// This function creates a material, shader, inputs, outputs, zero or more textures, and for each
        /// texture, a single primvar reader node to read the UV data from the geometric primitive.
        static string CreateMaterialNetwork(USD.NET.Scene scene,
                                            IExportableMaterial material,
                                            string rootPath = null)
        {
            var matSample = new ExportMaterialSample();

            // Used scene object paths.
            string materialPath = GetMaterialPath(material, rootPath);
            string shaderPath   = GetShaderPath(material, materialPath);
            string displayColorPrimvarReaderPath   = GetPrimvarPath(material, "displayColor", shaderPath);
            string displayOpacityPrimvarReaderPath = GetPrimvarPath(material, "displayOpacity", shaderPath);

            // The material was already created.
            if (scene.GetPrimAtPath(materialPath) != null)
            {
                return(materialPath);
            }

            // Ensure the root material path is defined in the scene.
            scene.Stage.DefinePrim(new pxr.SdfPath(rootPath));

            // Connect the materail surface to the output of the shader.
            matSample.surface.SetConnectedPath(shaderPath, "outputs:result");
            scene.Write(materialPath, matSample);

            // Create the shader and conditionally connect the diffuse color to the MainTex output.
            var shaderSample = GetShaderSample(material);
            var texturePath  = CreateAlphaTexture(scene, shaderPath, material);

            if (texturePath != null)
            {
                // A texture was created, so connect the opacity input to the texture output.
                shaderSample.opacity.SetConnectedPath(texturePath, "outputs:a");
            }
            else
            {
                // TODO: currently primvars:displayOpacity is not multiplied when an alpha texture is
                //                present. However, this only affects the USD preview. The correct solution
                //                requires a multiply node in the shader graph, but this does not yet exist.
                scene.Write(displayOpacityPrimvarReaderPath, new PrimvarReader1fSample("displayOpacity"));
                shaderSample.opacity.SetConnectedPath(displayOpacityPrimvarReaderPath, "outputs:result");
            }

            // Create a primvar reader to read primvars:displayColor.
            scene.Write(displayColorPrimvarReaderPath, new PrimvarReader3fSample("displayColor"));

            // Connect the diffuse color to the primvar reader.
            shaderSample.diffuseColor.SetConnectedPath(displayColorPrimvarReaderPath, "outputs:result");

            scene.Write(shaderPath, shaderSample);

            //
            // Everything below is ad-hoc data, which is written using the low level USD API.
            // It consists of the Unity shader parameters and the non-exported texture URIs.
            // Also note that scene.GetPrimAtPath will return null when the prim is InValid,
            // so there is no need to call IsValid() on the resulting prim.
            //
            var shadeMaterial = new pxr.UsdShadeMaterial(scene.GetPrimAtPath(materialPath));
            var shadeShader   = new pxr.UsdShadeShader(scene.GetPrimAtPath(shaderPath));

            if (material.SupportsDetailedMaterialInfo)
            {
                CreateShaderInputs(shadeShader, shadeMaterial, material.FloatParams);
                CreateShaderInputs(shadeShader, shadeMaterial, material.ColorParams);
                CreateShaderInputs(shadeShader, shadeMaterial, material.VectorParams);
            }

            CreateTextureUris(shadeShader.GetPrim(), material);

            return(materialPath);
        }
Exemple #23
0
 // Example: /Sketch/Strokes/Materials/Material_Light/Shader_Light/Texture_MainTex
 static string GetTexturePath(IExportableMaterial material, string textureName, string parentShaderPath)
 {
     return(parentShaderPath + "/Texture_" + SanitizeIdentifier(textureName));
 }
Exemple #24
0
        internal static FbxSurfaceMaterial CreateFbxMaterial(
            FbxExportGlobals G, string meshNamespace, IExportableMaterial exportableMaterial,
            HashSet <string> createdMaterialNames)
        {
            string materialName;

            if (exportableMaterial is BrushDescriptor)
            {
                // Toolkit uses this guid (in "N" format) to look up a BrushDescriptor.
                // See Toolkit's ModelImportSettings.GetDescriptorForStroke
                materialName = $"{exportableMaterial.UniqueName:N}_{meshNamespace}_{exportableMaterial.DurableName}";
            }
            else if (exportableMaterial is DynamicExportableMaterial dem)
            {
                // Comes from {fbx,obj,gltf,...} import from {Poly, Media Library}
                // This is a customized version of a BrushDescriptor -- almost certainly
                // Pbr{Blend,Opaque}{Double,Single}Sided with maybe an added texture and
                // some of its params customized.
                // TBT will merge the material created by Unity for this FbxMaterial with
                // the premade material it has for the parent guid.
                materialName = $"{dem.Parent.m_Guid:N}_{meshNamespace}_{dem.DurableName}";
            }
            else
            {
                Debug.LogWarning($"Unknown class {exportableMaterial.GetType().Name}");
                materialName = $"{meshNamespace}_{exportableMaterial.DurableName}";
            }
            // If only ExportFbx were a non-static class we could merge it with FbxExportGlobals
            materialName = ExportUtils.CreateUniqueName(materialName, createdMaterialNames);

            FbxSurfaceLambert material = FbxSurfaceLambert.Create(G.m_scene, materialName);

            material.Ambient.Set(new FbxDouble3(0, 0, 0));
            material.Diffuse.Set(new FbxDouble3(1.0, 1.0, 1.0));
            if (exportableMaterial.EmissiveFactor > 0)
            {
                material.EmissiveFactor.Set(exportableMaterial.EmissiveFactor);
                material.Emissive.Set(new FbxDouble3(1.0, 1.0, 1.0));
            }
            if (exportableMaterial.BlendMode != ExportableMaterialBlendMode.None)
            {
                var blendMode = FbxProperty.Create(material, Globals.FbxStringDT, "BlendMode");
                switch (exportableMaterial.BlendMode)
                {
                case ExportableMaterialBlendMode.AlphaMask:
                    blendMode.SetString(new FbxString("AlphaMask"));
                    material.TransparencyFactor.Set(0.2);
                    break;

                case ExportableMaterialBlendMode.AdditiveBlend:
                    blendMode.SetString(new FbxString("AdditiveBlend"));
                    break;
                }
            }

            // Export the texture
            if (exportableMaterial.HasExportTexture())
            {
                // This is not perfectly unique, but it is good enough for fbx export
                // better would be to use <durable>_<guid> but that's ugly, and nobody uses
                // the textures anyway, so... let's leave well enough alone for now.
                string albedoTextureName = exportableMaterial.DurableName;
                var    fullTextureDir    = Path.Combine(G.m_outputDir, kRelativeTextureDir);
                if (!Directory.Exists(fullTextureDir))
                {
                    if (!FileUtils.InitializeDirectoryWithUserError(fullTextureDir))
                    {
                        throw new IOException("Cannot write textures");
                    }
                }
                string   src             = exportableMaterial.GetExportTextureFilename();
                var      textureFileName = albedoTextureName + ".png";
                var      textureFilePath = Path.Combine(fullTextureDir, textureFileName);
                FileInfo srcInfo         = new FileInfo(src);
                if (srcInfo.Exists && !new FileInfo(textureFilePath).Exists)
                {
                    srcInfo.CopyTo(textureFilePath);
                }

                FbxFileTexture texture = FbxFileTexture.Create(G.m_scene, albedoTextureName + "_texture");
                texture.SetFileName(textureFilePath);
                texture.SetTextureUse(FbxTexture.ETextureUse.eStandard);
                texture.SetMappingType(FbxTexture.EMappingType.eUV);
                texture.SetMaterialUse(FbxFileTexture.EMaterialUse.eModelMaterial);
                texture.UVSet.Set(new FbxString("uv0"));
                material.Diffuse.ConnectSrcObject(texture);
                material.TransparentColor.ConnectSrcObject(texture);
            }
            else
            {
                foreach (var kvp in exportableMaterial.TextureUris)
                {
                    string parameterName = kvp.Key;
                    string textureUri    = kvp.Value;
                    if (ExportFileReference.IsHttp(textureUri))
                    {
                        // fbx can't deal with http references to textures
                        continue;
                    }
                    ExportFileReference fileRef = ExportFileReference.GetOrCreateSafeLocal(
                        G.m_disambiguationContext, textureUri, exportableMaterial.UriBase,
                        $"{meshNamespace}_{Path.GetFileName(textureUri)}");
                    AddTextureToMaterial(G, fileRef, material, parameterName);
                }
            }
            return(material);
        }
Exemple #25
0
 // Example: /Sketch/Strokes/Materials/Material_Light/Shader_Light/Texture_MainTex/Primvar_Uv
 static string GetPrimvarPath(IExportableMaterial material, string primvarName, string parentTexturePath)
 {
     return(parentTexturePath + "/Primvar_" + SanitizeIdentifier(primvarName));
 }