Example #1
0
 public static CustomModelColors GetCustomColors()
 {
     if (_defaultColors == null)
     {
         // Make a default entry if none was ever set.
         _defaultColors = new CustomModelColors();
     }
     return(_defaultColors);
 }
Example #2
0
 public static void SetCustomColors(CustomModelColors c)
 {
     _defaultColors = c;
 }
Example #3
0
        /// <summary>
        /// Gets the texture maps for the model
        /// </summary>
        /// <returns>The texture maps in byte arrays inside a ModelTextureData class</returns>
        public static async Task <ModelTextureData> GetModelMaps(Tex tex, XivMtrl mtrl, CustomModelColors colors = null)
        {
            // Use static values as needed.
            if (colors == null)
            {
                colors = GetCustomColors();
            }

            var shaderInfo = mtrl.GetShaderInfo();
            var mtrlMaps   = mtrl.GetAllMapInfos();

            var texMapData = await GetTexMapData(tex, mtrl);

            var dimensions = await EqualizeTextureSizes(texMapData);

            var diffuseMap  = new List <byte>();
            var normalMap   = new List <byte>();
            var specularMap = new List <byte>();
            var emissiveMap = new List <byte>();
            var alphaMap    = new List <byte>();

            var diffuseColorList  = new List <Color>();
            var specularColorList = new List <Color>();
            var emissiveColorList = new List <Color>();

            byte[] diffusePixels = null, specularPixels = null, normalPixels = null;

            if (texMapData.Normal != null)
            {
                normalPixels = texMapData.Normal.Data;
            }

            if (texMapData.Diffuse != null)
            {
                diffusePixels = texMapData.Diffuse.Data;
            }

            if (texMapData.Specular != null)
            {
                specularPixels = texMapData.Specular.Data;
            }

            if (normalPixels == null && diffusePixels == null)
            {
                // This material doesn't actually have any readable data.

                var empty = new ModelTextureData
                {
                    Width        = 0,
                    Height       = 0,
                    Normal       = new byte[0],
                    Diffuse      = new byte[0],
                    Specular     = new byte[0],
                    Emissive     = new byte[0],
                    Alpha        = new byte[0],
                    MaterialPath = mtrl.MTRLPath.Substring(mtrl.MTRLPath.LastIndexOf('/'))
                };
                return(empty);
            }

            var dataLength = normalPixels != null ? normalPixels.Length : diffusePixels.Length;

            await Task.Run(() =>
            {
                for (var i = 3; i < dataLength; i += 4)
                {
                    // Load the individual pixels into memory.
                    Color baseNormalColor   = new Color(127, 127, 255, 255);
                    Color baseDiffuseColor  = new Color(255, 255, 255, 255);
                    Color baseSpecularColor = new Color(255, 255, 255, 255);


                    if (normalPixels != null)
                    {
                        baseNormalColor = new Color(normalPixels[i - 3], normalPixels[i - 2], normalPixels[i - 1], normalPixels[i]);
                    }

                    if (diffusePixels != null)
                    {
                        baseDiffuseColor = new Color(diffusePixels[i - 3], diffusePixels[i - 2], diffusePixels[i - 1], diffusePixels[i]);
                    }

                    if (specularPixels != null)
                    {
                        baseSpecularColor = new Color(specularPixels[i - 3], specularPixels[i - 2], specularPixels[i - 1], specularPixels[i]);
                    }

                    byte colorsetValue = baseNormalColor.A;

                    // Calculate real colors from the inputs and shader.
                    Color normalColor, diffuseColor, specularColor;
                    byte opacity;
                    ComputeShaderColors(colors, shaderInfo, baseNormalColor, baseDiffuseColor, baseSpecularColor, out normalColor, out diffuseColor, out specularColor, out opacity);
                    Color alphaColor = new Color(opacity, opacity, opacity, opacity);

                    // Apply colorset if needed.  (This could really be baked into ComputeShaderColors)
                    Color emissiveColor = new Color(0, 0, 0, 0);
                    if (mtrl.ColorSetData.Count > 0)
                    {
                        var cs = texMapData.ColorSet.Data;
                        Color finalDiffuseColor, finalSpecularColor;
                        ComputeColorsetBlending(mtrl, colorsetValue, cs, diffuseColor, specularColor, out finalDiffuseColor, out finalSpecularColor, out emissiveColor);
                        diffuseColor  = finalDiffuseColor;
                        specularColor = finalSpecularColor;
                    }

                    // White out the opacity channels where appropriate.
                    diffuseColor.A  = opacity;
                    specularColor.A = 255;
                    normalColor.A   = 255;


                    diffuseMap.AddRange(BitConverter.GetBytes(diffuseColor.ToRgba()));
                    specularMap.AddRange(BitConverter.GetBytes(specularColor.ToRgba()));
                    emissiveMap.AddRange(BitConverter.GetBytes(emissiveColor.ToRgba()));
                    alphaMap.AddRange(BitConverter.GetBytes(alphaColor.ToRgba()));
                    normalMap.AddRange(BitConverter.GetBytes(normalColor.ToRgba()));
                }
            });

            var modelTextureData = new ModelTextureData
            {
                Width        = dimensions.Width,
                Height       = dimensions.Height,
                Normal       = normalMap.ToArray(),
                Diffuse      = diffuseMap.ToArray(),
                Specular     = specularMap.ToArray(),
                Emissive     = emissiveMap.ToArray(),
                Alpha        = alphaMap.ToArray(),
                MaterialPath = mtrl.MTRLPath.Substring(mtrl.MTRLPath.LastIndexOf('/'))
            };

            return(modelTextureData);
        }
Example #4
0
        private static void ComputeShaderColors(CustomModelColors colors, ShaderInfo info, Color baseNormal, Color baseDiffuse, Color baseSpecular, out Color newNormal, out Color newDiffuse, out Color newSpecular, out byte opacity)
        {
            // This is basically codifying this document: https://docs.google.com/spreadsheets/d/1kIKvVsW3fOnVeTi9iZlBDqJo6GWVn6K6BCUIRldEjhw/edit#gid=2112506802
            opacity     = 255;
            newNormal   = baseNormal;
            newDiffuse  = baseDiffuse;
            newSpecular = baseSpecular;

            // This var is technically defined in the Shaders parameters.
            // But we can use a constant copy of it for now, since it's largely non-changeable.
            const float PlayerColorMultiplier       = 1.4f;
            const float BrightPlayerColorMultiplier = 3.0f;

            if (info.Shader == MtrlShader.Standard || info.Shader == MtrlShader.Glass)
            {
                // Common
                // Base color here is diffuse if we have one...
                if (info.Preset == MtrlShaderPreset.DiffuseSpecular)
                {
                    // Has a raw diffuse.
                    newDiffuse   = baseDiffuse;
                    newDiffuse.A = baseNormal.B;

                    // Has a raw specular.
                    newSpecular = baseSpecular;
                }
                else if (info.Preset == MtrlShaderPreset.DiffuseMulti)
                {
                    // Has a raw diffuse.
                    newDiffuse = baseDiffuse;

                    // But we also have to modulate that diffuse color by the multi red channel.
                    newDiffuse = MultiplyColor(newDiffuse, baseSpecular.R);

                    newDiffuse.A = baseNormal.B;

                    // Uses multi green/blue in some fashion.
                    // We'll just show green for now.
                    newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                }
                else
                {
                    // Uses multi channel Red as a base/ao map.
                    newDiffuse   = new Color(baseSpecular.R, baseSpecular.R, baseSpecular.R, (byte)255);
                    newDiffuse.A = baseNormal.B;

                    // Uses multi green/blue in some fashion.
                    // We'll just show green for now.
                    newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                }

                // Normal is the same for all of them.
                newNormal = new Color(baseNormal.R, baseNormal.G, (byte)255, (byte)255);
                opacity   = baseNormal.B;
            }
            else if (info.Shader == MtrlShader.Furniture || info.Shader == MtrlShader.DyeableFurniture)
            {
                // Furniture
                newDiffuse = new Color(baseDiffuse.R, baseDiffuse.G, baseDiffuse.B, (byte)255);

                if (info.Shader == MtrlShader.DyeableFurniture)
                {
                    float colorInfluence = ByteToFloat(baseDiffuse.A);
                    Color furnitureColor = MultiplyColor(colors.FurnitureColor, 1.0f);

                    newDiffuse = Blend(baseDiffuse, furnitureColor, colorInfluence);
                }

                newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                newNormal   = new Color(baseNormal.R, baseNormal.G, baseNormal.B, (byte)255);

                // This needs some more research all around
            }
            else if (info.Shader == MtrlShader.Skin)
            {
                newNormal   = new Color(baseNormal.R, baseNormal.G, (byte)255, (byte)255);
                newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                opacity     = 255;

                // This is an arbitrary number.  There's likely some value in the shader params for skin that
                // tones down the specularity here, but without it the skin is hyper reflective.
                newSpecular = MultiplyColor(newSpecular, 0.25f);

                // New diffuse starts from regular diffuse file.
                // Then factors in the player's skin color multiplied by the shader value.
                float skinInfluence = ByteToFloat(baseSpecular.R);
                var   coloredSkin   = MultiplyColor(baseDiffuse, colors.SkinColor);

                newDiffuse = Blend(baseDiffuse, coloredSkin, skinInfluence);

                if (info.Preset == MtrlShaderPreset.Face)
                {
                    // Face shaders also allow for lip color.
                    var   coloredLip   = MultiplyColor(baseDiffuse, colors.LipColor);
                    float lipInfluence = ByteToFloat(baseSpecular.B);
                    newDiffuse = Blend(newDiffuse, coloredLip, lipInfluence);

                    // For lipstick, increase the specular value slightly.
                    float specAmp = 1.0f + (lipInfluence * 0.25f);
                    newSpecular = MultiplyColor(newSpecular, specAmp);

                    // Face shader supports alpha, unlike normal skin textures.
                    opacity = baseNormal.B;
                }
            }
            else if (info.Shader == MtrlShader.Hair)
            {
                newNormal   = new Color(baseNormal.R, baseNormal.G, (byte)255, (byte)255);
                newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                opacity     = baseNormal.A;

                // The influence here determines which base color we use.
                float influenceStrength = ByteToFloat(baseSpecular.A);

                // Starting from the original hair color...
                var baseColor = MultiplyColor(colors.HairColor, 1.0f);

                // Hair highlight color if available.
                var targetColor = (Color)(colors.HairHighlightColor != null ? colors.HairHighlightColor : colors.HairColor);

                // But wait! If we're actually a tattoo preset, that changes instead to tattoo color.
                if (info.Preset == MtrlShaderPreset.Face)
                {
                    targetColor = MultiplyColor(colors.TattooColor, 1.0f);
                }
                else if (info.Preset == MtrlShaderPreset.FaceBright)
                {
                    // Multiplier here is 3.0 instead of 1.4
                    targetColor = MultiplyColor(colors.TattooColor, BrightPlayerColorMultiplier / PlayerColorMultiplier);
                }

                // This gets us our actual base color.
                baseColor = Blend(baseColor, targetColor, influenceStrength);

                // Now this needs to be straight multiplied with the multi channel red.
                newDiffuse = MultiplyColor(baseColor, baseSpecular.R);
            }
            else if (info.Shader == MtrlShader.Iris)
            {
                // Eyes
                newNormal   = new Color(baseNormal.R, baseNormal.G, (byte)255, (byte)255);
                newSpecular = new Color(baseSpecular.G, baseSpecular.G, baseSpecular.G, (byte)255);
                opacity     = baseNormal.A;


                // Base color is the selected eye color.
                var baseColor = colors.EyeColor;

                // Pretty sure some data is missing in here.
                // Catchlight is also not factored in atm.

                // Now this needs to be straight multiplied with the multi channel red.
                newDiffuse = MultiplyColor(baseColor, baseSpecular.R);
            }
            else
            {
                // Fall through just shows stuff as is.
                newNormal   = baseNormal;
                newDiffuse  = baseDiffuse;
                newSpecular = baseSpecular;
            }

            // Transparency filtering.
            if (!info.TransparencyEnabled)
            {
                opacity = (byte)(opacity < 128 ? 0 : 255);
            }
        }
Example #5
0
        /// <summary>
        /// Gets the customized texture map data for a model.
        /// Null custom model colors uses the defaults at ModelTexture.GetCustomColors().
        /// </summary>
        /// <param name="gameDirectory"></param>
        /// <param name="mtrl"></param>
        /// <param name="colors"></param>
        /// <returns></returns>
        public static async Task <ModelTextureData> GetModelMaps(DirectoryInfo gameDirectory, XivMtrl mtrl, CustomModelColors colors = null)
        {
            var tex = new Tex(gameDirectory);

            return(await GetModelMaps(tex, mtrl));
        }