Example #1
0
        /// <summary>
        /// Import materials step by step. Call this until it returns false.
        /// </summary>
        /// <param name="info">Progress information to be updated</param>
        /// <returns>Return true if in progress, false otherwise.</returns>
        public bool BuildMaterials(ProgressInfo info)
        {
            if (materialData == null)
            {
                Debug.LogWarning("No material library defined.");
                return(false);
            }
            if (info.materialsLoaded >= materialData.Count)
            {
                return(false);
            }
            MaterialData matData = materialData[info.materialsLoaded];

            info.materialsLoaded++;
            if (currMaterials.ContainsKey(matData.materialName))
            {
                Debug.LogWarning("Duplicate material found: " + matData.materialName + ". Repeated occurence ignored");
            }
            else
            {
                currMaterials.Add(matData.materialName, BuildMaterial(matData));
            }
            return(info.materialsLoaded < materialData.Count);
        }
Example #2
0
        /// <summary>
        /// Parse bump parameters.
        /// </summary>
        /// <param name="param">list of paramers</param>
        /// <param name="mtlData">material data to be updated</param>
        /// <remarks>Only the bump map texture path is used here.</remarks>
        /// <seealso cref="https://github.com/hammmm/unity-obj-loader"/>
        private void ParseBumpParameters(string[] param, MaterialData mtlData)
        {
            Regex regexNumber = new Regex(@"^[-+]?[0-9]*\.?[0-9]+$");

            var bumpParams = new Dictionary <string, BumpParamDef>();

            bumpParams.Add("bm", new BumpParamDef("bm", "string", 1, 1));
            bumpParams.Add("clamp", new BumpParamDef("clamp", "string", 1, 1));
            bumpParams.Add("blendu", new BumpParamDef("blendu", "string", 1, 1));
            bumpParams.Add("blendv", new BumpParamDef("blendv", "string", 1, 1));
            bumpParams.Add("imfchan", new BumpParamDef("imfchan", "string", 1, 1));
            bumpParams.Add("mm", new BumpParamDef("mm", "string", 1, 1));
            bumpParams.Add("o", new BumpParamDef("o", "number", 1, 3));
            bumpParams.Add("s", new BumpParamDef("s", "number", 1, 3));
            bumpParams.Add("t", new BumpParamDef("t", "number", 1, 3));
            bumpParams.Add("texres", new BumpParamDef("texres", "string", 1, 1));
            int    pos      = 1;
            string filename = null;

            while (pos < param.Length)
            {
                if (!param[pos].StartsWith("-"))
                {
                    filename = param[pos];
                    pos++;
                    continue;
                }
                // option processing
                string optionName = param[pos].Substring(1);
                pos++;
                if (!bumpParams.ContainsKey(optionName))
                {
                    continue;
                }
                BumpParamDef def  = bumpParams[optionName];
                ArrayList    args = new ArrayList();
                int          i    = 0;
                bool         isOptionNotEnough = false;
                for (; i < def.valueNumMin; i++, pos++)
                {
                    if (pos >= param.Length)
                    {
                        isOptionNotEnough = true;
                        break;
                    }
                    if (def.valueType == "number")
                    {
                        Match match = regexNumber.Match(param[pos]);
                        if (!match.Success)
                        {
                            isOptionNotEnough = true;
                            break;
                        }
                    }
                    args.Add(param[pos]);
                }
                if (isOptionNotEnough)
                {
                    Debug.Log("bump variable value not enough for option:" + optionName + " of material:" + mtlData.materialName);
                    continue;
                }
                for (; i < def.valueNumMax && pos < param.Length; i++, pos++)
                {
                    if (def.valueType == "number")
                    {
                        Match match = regexNumber.Match(param[pos]);
                        if (!match.Success)
                        {
                            break;
                        }
                    }
                    args.Add(param[pos]);
                }
                // TODO: some processing of options
                Debug.Log("found option: " + optionName + " of material: " + mtlData.materialName + " args: " + string.Concat(args.ToArray()));
            }
            // set the file name, if found
            // TODO: other parsed parameters are not used for now
            if (filename != null)
            {
                mtlData.bumpTexPath = filename;
            }
        }
Example #3
0
        /// <summary>
        /// Build a Unity Material from MaterialData
        /// </summary>
        /// <param name="md">material data</param>
        /// <returns>Unity material</returns>
        private Material BuildMaterial(MaterialData md)
        {
            string shaderName   = "Standard"; // (md.illumType == 2) ? "Standard (Specular setup)" : "Standard";
            bool   specularMode = false;      // (md.specularTex != null);

            ModelUtil.MtlBlendMode mode = md.overallAlpha < 1.0f ? ModelUtil.MtlBlendMode.TRANSPARENT : ModelUtil.MtlBlendMode.OPAQUE;

            bool useUnlit = buildOptions != null && buildOptions.litDiffuse &&
                            md.diffuseTex != null &&
                            md.bumpTex == null &&
                            md.opacityTex == null &&
                            md.specularTex == null &&
                            !md.hasReflectionTex;

            bool?diffuseIsTransparent = null;

            if (useUnlit)
            {
                // do not use unlit shader if the texture has transparent pixels
                diffuseIsTransparent = ModelUtil.ScanTransparentPixels(md.diffuseTex, ref mode);
            }

            if (useUnlit && !diffuseIsTransparent.Value)
            {
                shaderName = "Unlit/Texture";
            }
            else if (specularMode)
            {
                shaderName = "Standard (Specular setup)";
            }
            Material newMaterial = new Material(Shader.Find(shaderName)); // "Standard (Specular setup)"

            newMaterial.name = md.materialName;

            float shinLog = Mathf.Log(md.shininess, 2);
            // get the metallic value from the shininess
            float metallic = Mathf.Clamp01(shinLog / 10.0f);
            // get the smoothness from the shininess
            float smoothness = Mathf.Clamp01(shinLog / 10.0f);

            if (specularMode)
            {
                newMaterial.SetColor("_SpecColor", md.specularColor);
                newMaterial.SetFloat("_Shininess", md.shininess / 1000.0f);
                //m.color = new Color( md.diffuse.r, md.diffuse.g, md.diffuse.b, md.alpha);
            }
            else
            {
                newMaterial.SetFloat("_Metallic", metallic);
                //m.SetFloat( "_Glossiness", md.shininess );
            }


            if (md.diffuseTex != null)
            {
                // diffuse

                if (md.opacityTex != null)
                {
                    // diffuse + opacity:
                    // update diffuse texture if an opacity map was found
                    int       w             = md.diffuseTex.width;
                    int       h             = md.diffuseTex.width;
                    Texture2D albedoTexture = new Texture2D(w, h, TextureFormat.ARGB32, false);
                    Color     col           = new Color();
                    for (int x = 0; x < albedoTexture.width; x++)
                    {
                        for (int y = 0; y < albedoTexture.height; y++)
                        {
                            col    = md.diffuseTex.GetPixel(x, y);
                            col.a *= md.opacityTex.GetPixel(x, y).grayscale;
                            // blend diffuse and opacity textures
                            albedoTexture.SetPixel(x, y, col);
                        }
                    }
                    albedoTexture.name = md.diffuseTexPath;
                    albedoTexture.Apply();
                    // mode = ModelUtil.MtlBlendMode.TRANSPARENT;
                    // The map_d value is multiplied by the d value --> Fade mode
                    mode = ModelUtil.MtlBlendMode.FADE;
#if UNITY_EDITOR
                    if (!string.IsNullOrEmpty(alternativeTexPath))
                    {
                        string texAssetPath = AssetDatabase.GetAssetPath(md.opacityTex);
                        if (!string.IsNullOrEmpty(texAssetPath))
                        {
                            EditorUtil.SaveAndReimportPngTexture(ref albedoTexture, texAssetPath, "_alpha");
                        }
                    }
#endif
                    newMaterial.SetTexture("_MainTex", albedoTexture);
                }
                else
                {// md.opacityTex == null
                    // diffuse without opacity: if there are transparent pixels ==> transparent material
                    if (!diffuseIsTransparent.HasValue)
                    {
                        diffuseIsTransparent = ModelUtil.ScanTransparentPixels(md.diffuseTex, ref mode);
                    }
                    newMaterial.SetTexture("_MainTex", md.diffuseTex);
                }
                //Debug.LogFormat("Diffuse set for {0}",m.name);
            }
            else if (md.opacityTex != null)
            {
                // opacity without diffuse
                //mode = ModelUtil.MtlBlendMode.TRANSPARENT;
                mode = ModelUtil.MtlBlendMode.FADE;
                int       w             = md.opacityTex.width;
                int       h             = md.opacityTex.width;
                Texture2D albedoTexture = new Texture2D(w, h, TextureFormat.ARGB32, false);
                Color     col           = new Color();
                bool      detected      = false;
                for (int x = 0; x < albedoTexture.width; x++)
                {
                    for (int y = 0; y < albedoTexture.height; y++)
                    {
                        col   = md.diffuseColor;
                        col.a = md.overallAlpha * md.opacityTex.GetPixel(x, y).grayscale;
                        ModelUtil.DetectMtlBlendFadeOrCutout(col.a, ref mode, ref detected);
                        //if (md.alpha == 1.0f && col.a == 0.0f) mode = ModelUtil.MtlBlendMode.CUTOUT;
                        albedoTexture.SetPixel(x, y, col);
                    }
                }
                albedoTexture.name = md.diffuseTexPath;
                albedoTexture.Apply();
#if UNITY_EDITOR
                if (!string.IsNullOrEmpty(alternativeTexPath))
                {
                    string texAssetPath = AssetDatabase.GetAssetPath(md.opacityTex);
                    if (!string.IsNullOrEmpty(texAssetPath))
                    {
                        EditorUtil.SaveAndReimportPngTexture(ref albedoTexture, texAssetPath, "_op");
                    }
                }
#endif
                newMaterial.SetTexture("_MainTex", albedoTexture);
            }

            md.diffuseColor.a = md.overallAlpha;
            newMaterial.SetColor("_Color", md.diffuseColor);

            md.emissiveColor.a = md.overallAlpha;
            newMaterial.SetColor("_EmissionColor", md.emissiveColor);
            if (md.emissiveColor.r > 0 || md.emissiveColor.g > 0 || md.emissiveColor.b > 0)
            {
                newMaterial.EnableKeyword("_EMISSION");
            }

            if (md.bumpTex != null)
            {
                // bump map defined

                // TODO: if importing assets do not create a nomal map, change importer settings

                // let (improperly) assign a normal map to the bumb map
                // if the file name contains a specific tag
                // TODO: customize normal map tag
                if (md.bumpTexPath.Contains("_normal_map"))
                {
                    newMaterial.EnableKeyword("_NORMALMAP");
                    newMaterial.SetFloat("_BumpScale", 0.25f); // lower the bump effect with the normal map
                    newMaterial.SetTexture("_BumpMap", md.bumpTex);
                }
                else
                {
                    // calculate normal map
                    Texture2D normalMap = ModelUtil.HeightToNormalMap(md.bumpTex);
#if UNITY_EDITOR
                    if (!string.IsNullOrEmpty(alternativeTexPath))
                    {
                        string texAssetPath = AssetDatabase.GetAssetPath(md.bumpTex);
                        if (!string.IsNullOrEmpty(texAssetPath))
                        {
                            EditorUtil.SaveAndReimportPngTexture(ref normalMap, texAssetPath, "_nm", true);
                        }
                    }
                    else
#endif
                    {
                        newMaterial.SetTexture("_BumpMap", normalMap);
                        //newMaterial.SetTexture("_BumpMap", md.bumpTex);
                        newMaterial.EnableKeyword("_NORMALMAP");
                        newMaterial.SetFloat("_BumpScale", 1.0f); // adjust the bump effect with the normal map
                    }
                }
            }

            if (md.specularTex != null)
            {
                Texture2D glossTexture = new Texture2D(md.specularTex.width, md.specularTex.height, TextureFormat.ARGB32, false);
                Color     col          = new Color();
                float     pix          = 0.0f;
                for (int x = 0; x < glossTexture.width; x++)
                {
                    for (int y = 0; y < glossTexture.height; y++)
                    {
                        pix = md.specularTex.GetPixel(x, y).grayscale;

                        // red = metallic

                        col.r = metallic * pix;// md.specular.grayscale*pix;
                        col.g = col.r;
                        col.b = col.r;

                        // alpha = smoothness

                        // if reflecting set maximum smoothness value, else use a precomputed value
                        if (md.hasReflectionTex)
                        {
                            col.a = pix;
                        }
                        else
                        {
                            col.a = pix * smoothness;
                        }

                        glossTexture.SetPixel(x, y, col);
                    }
                }
                glossTexture.Apply();
#if UNITY_EDITOR
                if (!string.IsNullOrEmpty(alternativeTexPath))
                {
                    string texAssetPath = AssetDatabase.GetAssetPath(md.specularTex);
                    if (!string.IsNullOrEmpty(texAssetPath))
                    {
                        EditorUtil.SaveAndReimportPngTexture(ref glossTexture, texAssetPath, "_spec");
                    }
                }
#endif

                if (specularMode)
                {
                    newMaterial.EnableKeyword("_SPECGLOSSMAP");
                    newMaterial.SetTexture("_SpecGlossMap", glossTexture);
                }
                else
                {
                    newMaterial.EnableKeyword("_METALLICGLOSSMAP");
                    newMaterial.SetTexture("_MetallicGlossMap", glossTexture);
                }

                //m.SetTexture( "_MetallicGlossMap", md.specularLevelTex );
            }

            // replace the texture with Unity environment reflection
            if (md.hasReflectionTex)
            {
                if (md.overallAlpha < 1.0f)
                {
                    Color col = Color.white;
                    col.a = md.overallAlpha;
                    newMaterial.SetColor("_Color", col);
                    mode = ModelUtil.MtlBlendMode.FADE;
                }
                // the "amount of" info is missing, using a default value
                if (md.specularTex != null)
                {
                    newMaterial.SetFloat("_Metallic", metallic);// 1.0f);
                }
                // usually the reflection texture is not blurred
                newMaterial.SetFloat("_Glossiness", 1.0f);
            }

            ModelUtil.SetupMaterialWithBlendMode(newMaterial, mode);

            //#if UNITY_EDITOR
            //        if (!string.IsNullOrEmpty(alternateTexPath))
            //        {
            //            string path = alternateTexPath + "../Materials/" + m.name + ".mat";
            //            path = path.Replace("Textures/../", "");
            //            Debug.LogFormat("Creating material asset in {0}", path);
            //            AssetDatabase.CreateAsset(m, path);
            //        m = AssetDatabase.LoadAssetAtPath<Material>(path);
            //        }
            //#endif
            return(newMaterial);
        }
Example #4
0
        /// <summary>
        /// Parse the material library lines to get material data.
        /// </summary>
        /// <param name="lines">lines read from the material library file</param>
        /// <param name="mtlData">list of material data</param>
        private void ParseMaterialData(string[] lines, List <MaterialData> mtlData)
        {
            MaterialData current = new MaterialData();

            char[] separators = new char[] { ' ', '\t' };
            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i].Trim();

                // remove comments
                if (line.IndexOf("#") != -1)
                {
                    line = line.Substring(0, line.IndexOf("#"));
                }
                string[] p = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                if (p.Length == 0 || string.IsNullOrEmpty(p[0]))
                {
                    continue;
                }
                string parameters = null;
                if (line.Length > p[0].Length)
                {
                    parameters = line.Substring(p[0].Length + 1).Trim();
                }
                try
                {
                    switch (p[0])
                    {
                    case "newmtl":
                        current = new MaterialData();
                        current.materialName = DataSet.FixMaterialName(parameters);
                        mtlData.Add(current);
                        break;

                    case "Ka":     // Ambient component (not supported)
                        current.ambientColor = StringsToColor(p);
                        break;

                    case "Kd":     // Diffuse component
                        current.diffuseColor = StringsToColor(p);
                        break;

                    case "Ks":     // Specular component
                        current.specularColor = StringsToColor(p);
                        break;

                    case "Ke":     // Specular component
                        current.emissiveColor = StringsToColor(p);
                        break;

                    case "Ns":     // Specular exponent --> shininess
                        current.shininess = ParseFloat(p[1]);
                        break;

                    case "d":     // dissolve into the background (1=opaque, 0=transparent)
                        current.overallAlpha = p.Length > 1 && p[1] != "" ? ParseFloat(p[1]) : 1.0f;
                        break;

                    case "Tr":     // Transparency
                        current.overallAlpha = p.Length > 1 && p[1] != "" ? 1.0f - ParseFloat(p[1]) : 1.0f;
                        break;

                    case "map_KD":
                    case "map_Kd":     // Color texture, diffuse reflectivity
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            current.diffuseTexPath = parameters;
                        }
                        break;

                    // TODO: different processing needed, options not supported
                    case "map_Ks":     // specular reflectivity of the material
                    case "map_kS":
                    case "map_Ns":     // Scalar texture for specular exponent
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            current.specularTexPath = parameters;
                        }
                        break;

                    case "map_bump":     // Bump map texture
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            current.bumpTexPath = parameters;
                        }
                        break;

                    case "bump":
                        ParseBumpParameters(p, current);
                        break;

                    case "map_opacity":
                    case "map_d":     // Scalar texture modulating the dissolve into the background
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            current.opacityTexPath = parameters;
                        }
                        break;

                    case "illum":     // Illumination model. 1 - diffuse, 2 - specular (not used)
                        current.illumType = int.Parse(p[1]);
                        break;

                    case "refl":     // reflection map (replaced with Unity environment reflection)
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            current.hasReflectionTex = true;
                        }
                        break;

                    case "map_Ka":     // ambient reflectivity color texture
                    case "map_kA":
                        if (!string.IsNullOrEmpty(parameters))
                        {
                            Debug.Log("Map not supported:" + line);
                        }
                        break;

                    default:
                        //Debug.Log("this line was not processed :" + line);
                        break;
                    }
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("Error at line {0} in mtl file: {1}", i + 1, e);
                }
            }
        }