Esempio n. 1
0
        /// <summary>
        /// Adds geometry elements.
        /// </summary>
        /// <param name="root">Root element.</param>
        /// <param name="dfMesh">DFMesh.</param>
        private void AddGeometry(ref _daeElement root, ref DFMesh dfMesh)
        {
            _daeElement geomLib = root.add("library_geometries");
            _daeElement geom    = geomLib.add("geometry");

            string geomId = "model" + dfMesh.ObjectId.ToString();

            geom.setAttribute("id", geomId);
            _daeElement mesh = geom.add("mesh");

            // Get source vertex data
            List <float> posArray    = new List <float>(dfMesh.TotalVertices * 3);
            List <float> normalArray = new List <float>(dfMesh.TotalVertices * 3);
            List <float> uvArray     = new List <float>(dfMesh.TotalVertices * 2);

            foreach (var subMesh in dfMesh.SubMeshes)
            {
                // Get texture dimensions for this submesh
                string archivePath     = Path.Combine(arena2Path, TextureFile.IndexToFileName(subMesh.TextureArchive));
                System.Drawing.Size sz = TextureFile.QuickSize(archivePath, subMesh.TextureRecord);

                // Collect vertex information for every plane in this submesh
                foreach (var plane in subMesh.Planes)
                {
                    foreach (var point in plane.Points)
                    {
                        // Add position data
                        posArray.Add(point.X);
                        posArray.Add(-point.Y);
                        posArray.Add(-point.Z);

                        // Add normal data
                        normalArray.Add(point.NX);
                        normalArray.Add(-point.NY);
                        normalArray.Add(-point.NZ);

                        // Add UV data
                        uvArray.Add(point.U / (float)sz.Width);
                        uvArray.Add(-(point.V / (float)sz.Height));
                    }
                }
            }

            // Add positions, normals, and texture coordinates
            AddSource(ref mesh, geomId + "-positions", "X Y Z", ref posArray);
            AddSource(ref mesh, geomId + "-normals", "X Y Z", ref normalArray);
            AddSource(ref mesh, geomId + "-uv", "S T", ref uvArray);

            // Add <vertices> element
            _daeElement vertices = mesh.add("vertices");

            vertices.setAttribute("id", geomId + "-vertices");
            _daeElement verticesInput = vertices.add("input");

            verticesInput.setAttribute("semantic", "POSITION");
            verticesInput.setAttribute("source", MakeUriRef(geomId + "-positions"));

            // Add triangle indices for each submesh
            uint vertexCount = 0;

            foreach (var subMesh in dfMesh.SubMeshes)
            {
                // Loop through all planes in this submesh
                List <uint> indexArray = new List <uint>(subMesh.TotalTriangles * (3 * 3));
                foreach (var plane in subMesh.Planes)
                {
                    // Every DFPlane is a triangle fan radiating from point 0
                    uint sharedPoint = vertexCount++;

                    // Index remaining points. There are (plane.Points.Length - 2) triangles in every plane
                    for (int tri = 0; tri < plane.Points.Length - 2; tri++)
                    {
                        // Position, Normal, UV index for shared point
                        indexArray.Add(sharedPoint);
                        indexArray.Add(sharedPoint);
                        indexArray.Add(sharedPoint);

                        // Position, Normal, UV index for vertexCount
                        indexArray.Add(vertexCount);
                        indexArray.Add(vertexCount);
                        indexArray.Add(vertexCount);

                        // Position, Normal, UV index for vertexCount + 1
                        indexArray.Add(vertexCount + 1);
                        indexArray.Add(vertexCount + 1);
                        indexArray.Add(vertexCount + 1);

                        // Increment vertexCount to next point in fan
                        vertexCount++;
                    }

                    // Increment vertexCount to start of next fan in vertex buffer
                    vertexCount++;
                }

                // Add <triangle>
                string      materialName = MakeMaterialName(subMesh.TextureArchive, subMesh.TextureRecord);
                _daeElement triangles    = mesh.add("triangles");
                triangles.setAttribute("count", (indexArray.Count / (3 * 3)).ToString());
                triangles.setAttribute("material", materialName + "-material");

                AddInput(ref triangles, "VERTEX", geomId + "-vertices", 0);
                AddInput(ref triangles, "NORMAL", geomId + "-normals", 1);
                AddInput(ref triangles, "TEXCOORD", geomId + "-uv", 2);

                // Add <p>
                _daeElement p = triangles.add("p");
                p.setCharData(UIntArrayToString(ref indexArray));
            }
        }
        private void CacheRecordSizesAndFrames(int textureArchive)
        {
            // Open texture file
            string      path        = Path.Combine(DaggerfallUnity.Instance.Arena2Path, TextureFile.IndexToFileName(textureArchive));
            TextureFile textureFile = new TextureFile(path, FileUsage.UseMemory, true);

            // Cache size and scale for each record
            recordSizes  = new Vector2[textureFile.RecordCount];
            recordFrames = new int[textureFile.RecordCount];
            for (int i = 0; i < textureFile.RecordCount; i++)
            {
                // Get size and scale of this texture
                DFSize size  = textureFile.GetSize(i);
                DFSize scale = textureFile.GetScale(i);

                // Set start size
                Vector2 startSize;
                startSize.x = size.Width;
                startSize.y = size.Height;

                // Apply scale
                Vector2 finalSize;
                int     xChange = (int)(size.Width * (scale.Width / BlocksFile.ScaleDivisor));
                int     yChange = (int)(size.Height * (scale.Height / BlocksFile.ScaleDivisor));
                finalSize.x = (size.Width + xChange);
                finalSize.y = (size.Height + yChange);

                // Set optional scale
                TextureReplacement.SetBillboardScale(textureArchive, i, ref finalSize);

                // Store final size and frame count
                recordSizes[i]  = finalSize * MeshReader.GlobalScale;
                recordFrames[i] = textureFile.GetFrameCount(i);
            }
        }
        /// <summary>
        /// Gets inventory/equip image for specified item.
        /// Image will be cached based on material and hand for faster subsequent fetches.
        /// Animated item images do not support dyes.
        /// </summary>
        /// <param name="item">Item to fetch image for.</param>
        /// <param name="removeMask">Removes mask index (e.g. around helmets) from final image.</param>
        /// <param name="forPaperDoll">Image is for paper doll.</param>
        /// <param name="allowAnimation">Read animated textures.</param>
        /// <returns>ImageData.</returns>
        public ImageData GetItemImage(DaggerfallUnityItem item, bool removeMask = false, bool forPaperDoll = false, bool allowAnimation = false)
        {
            // Get colour
            int color = (int)item.dyeColor;

            // Get archive and record indices
            int archive = item.InventoryTextureArchive;
            int record  = item.InventoryTextureRecord;

            // Paper doll handling
            if (forPaperDoll)
            {
                // 1H Weapons in right hand need record + 1
                if (item.ItemGroup == ItemGroups.Weapons && item.EquipSlot == EquipSlots.RightHand)
                {
                    if (ItemEquipTable.GetItemHands(item) == ItemHands.Either)
                    {
                        record += 1;
                    }
                }
            }
            else
            {
                // Katanas need +1 for inventory image as they use right-hand image instead of left
                if (item.IsOfTemplate(ItemGroups.Weapons, (int)Weapons.Katana))
                {
                    record += 1;
                }
            }

            // Use world texture archive if inventory texture not set
            // Examples are gold pieces and wayrest painting
            if (archive == 0 && record == 0)
            {
                archive = item.ItemTemplate.worldTextureArchive;
                record  = item.ItemTemplate.worldTextureRecord;
            }

            // Get unique key
            int key = MakeImageKey(color, archive, record, removeMask);

            // Get existing icon if in cache
            if (itemImages.ContainsKey(key))
            {
                return(itemImages[key]);
            }

            // Load image data
            string    filename = TextureFile.IndexToFileName(archive);
            ImageData data     = ImageReader.GetImageData(filename, record, 0, true, false, allowAnimation);

            if (data.type == ImageTypes.None)
            {
                throw new Exception("GetItemImage() could not load image data.");
            }

            // Fix items with known incorrect paper doll offsets
            if (archive == 237 && (record == 52 || record == 54))
            {
                // "Short shirt" template index 202 variants 2 and 5 for human female
                data.offset = new DaggerfallConnect.Utility.DFPosition(237, 43);
            }

            Texture2D tex;

            if (!forPaperDoll && TextureReplacement.TryImportTexture(archive, record, 0, item.dyeColor, out tex))
            {
                // Assign imported texture
                // Paperdoll is disabled for now
                data.texture = tex;
            }
            else
            {
                // Remove mask if requested
                if (removeMask)
                {
                    data.dfBitmap = ImageProcessing.ChangeMask(data.dfBitmap);
                }

                // Change dye or just update texture
                ItemGroups group = item.ItemGroup;
                DyeColors  dye   = (DyeColors)color;
                if (group == ItemGroups.Weapons || group == ItemGroups.Armor)
                {
                    data = ChangeDye(data, dye, DyeTargets.WeaponsAndArmor);
                }
                else if (item.ItemGroup == ItemGroups.MensClothing || item.ItemGroup == ItemGroups.WomensClothing)
                {
                    data = ChangeDye(data, dye, DyeTargets.Clothing);
                }
                else
                {
                    ImageReader.UpdateTexture(ref data);
                }
            }

            // Add to cache
            itemImages.Add(key, data);

            return(data);
        }
Esempio n. 4
0
        /// <summary>
        /// Makes texture results for given material.
        /// </summary>
        /// <param name="material">Unity material.</param>
        /// <param name="archive">Texture archive.</param>
        /// <param name="record">Record index.</param>
        /// <returns>Results for the given material.</returns>
        public static GetTextureResults MakeResults(Material material, int archive, int record)
        {
            string      path        = Path.Combine(DaggerfallUnity.Instance.MaterialReader.TextureReader.Arena2Path, TextureFile.IndexToFileName(archive));
            TextureFile textureFile = new TextureFile(path, FileUsage.UseMemory, true);

            return(new GetTextureResults()
            {
                albedoMap = GetTextureOrDefault(material, Uniforms.MainTex),
                normalMap = GetTextureOrDefault(material, Uniforms.BumpMap),
                emissionMap = GetTextureOrDefault(material, Uniforms.EmissionMap),
                singleRect = new Rect(0, 0, 1, 1),
                isWindow = ClimateSwaps.IsExteriorWindow(archive, record),
                isEmissive = material.HasProperty(Uniforms.EmissionMap),
                textureFile = textureFile
            });
        }
Esempio n. 5
0
        private Material GetWindowMaterial(int key, WindowStyle windowStyle)
        {
            // Reverse key (input must be a MainKeyGroup key)
            int archive, record, frame;

            ReverseTextureKey(key, out archive, out record, out frame);

            // Determine new key group based on style
            int   group;
            Color color;
            float intensity;

            switch (windowStyle)
            {
            case WindowStyle.Day:
                group     = DayWindowKeyGroup;
                color     = dfUnity.MaterialReader.DayWindowColor;
                intensity = dfUnity.MaterialReader.DayWindowIntensity;
                break;

            case WindowStyle.Night:
                group     = NightWindowKeyGroup;
                color     = dfUnity.MaterialReader.NightWindowColor;
                intensity = dfUnity.MaterialReader.NightWindowIntensity;
                break;

            case WindowStyle.Fog:
                group     = FogWindowKeyGroup;
                color     = dfUnity.MaterialReader.FogWindowColor;
                intensity = dfUnity.MaterialReader.FogWindowIntensity;
                break;

            case WindowStyle.Custom:
                group     = CustomWindowKeyGroup;
                color     = dfUnity.MaterialReader.CustomWindowColor;
                intensity = dfUnity.MaterialReader.CustomWindowIntensity;
                break;

            default:
                return(GetMaterial(archive, record));       // Just get base material with no processing
            }

            // Make new key based on group
            int newkey = MakeTextureKey((short)archive, (byte)record, (byte)0, group);

            // Check if material is already in cache
            CachedMaterial cm;

            if (materialDict.TryGetValue(newkey, out cm))
            {
                // Return same if settings have not changed since last time
                if (cm.windowColor == color &&
                    cm.windowIntensity == intensity &&
                    cm.filterMode == MainFilterMode)
                {
                    // Properties are the same
                    return(cm.material);
                }
                else
                {
                    materialDict.Remove(newkey);
                }
            }

            // Load texture file and get colour arrays
            textureReader.TextureFile.Load(Path.Combine(dfUnity.Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            Color32 alpha = new Color32(0, 0, 0, (byte)(255f / intensity));

            Color32[] diffuseColors, alphaColors;
            DFSize    sz = textureReader.TextureFile.GetWindowColors32(record, color, alpha, out diffuseColors, out alphaColors);

            // Create diffuse texture
            Texture2D diffuse = new Texture2D(sz.Width, sz.Height, TextureFormat.RGBA32, MipMaps);

            diffuse.SetPixels32(diffuseColors);
            diffuse.Apply(true);

            // Create illumin texture
            Texture2D illumin = new Texture2D(sz.Width, sz.Height, TextureFormat.RGBA32, MipMaps);

            illumin.SetPixels32(alphaColors);
            illumin.Apply(true);

            // Create new material with a self-illuminating shader
            Shader   shader   = Shader.Find(DefaultSelfIlluminShaderName);
            Material material = new Material(shader);

            material.name = FormatName(archive, record);

            // Set material textures
            material.SetTexture("_MainTex", diffuse);
            material.SetTexture("_Illum", illumin);
            material.mainTexture.filterMode = MainFilterMode;

            // Cache this window material
            CachedMaterial newcm = new CachedMaterial()
            {
                key             = newkey,
                keyGroup        = group,
                singleRect      = new Rect(0, 0, sz.Width, sz.Height),
                material        = material,
                filterMode      = MainFilterMode,
                isWindow        = true,
                windowColor     = color,
                windowIntensity = intensity,
            };

            materialDict.Add(newkey, newcm);

            return(material);
        }
Esempio n. 6
0
        /// <summary>
        /// Gets terrain metallic gloss map texture array containing each terrain tile in a seperate array slice.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>Texture2DArray or null</returns>
        public Texture2DArray GetTerrainMetallicGlossMapTextureArray(
            int archive,
            bool stayReadable = false,
            SupportedNonAlphaTextureFormats nonAlphaFormat = SupportedNonAlphaTextureFormats.RGB24)
        {
            Color32[] defaultMetallicGlossMap;

            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            int         numSlices   = 0;

            if (textureFile.RecordCount == 56)
            {
                numSlices = textureFile.RecordCount;
            }
            else
            {
                return(null);
            }

            Texture2DArray textureArray;
            int            width;
            int            height;

            // try to import first replacement texture for tile archive to determine width and height of replacement texture set (must be the same for all replacement textures for Texture2DArray)
            if (TextureReplacement.CustomMetallicGlossExist(archive, 0, 0))
            {
                Texture2D metallicGlossMap = TextureReplacement.LoadCustomMetallicGloss(archive, 0, 0);
                width  = metallicGlossMap.width;
                height = metallicGlossMap.height;
            }
            else
            {
                // create default texture array (1x1 texture)
                width  = 1;
                height = 1;
            }

            textureArray = new Texture2DArray(width, height, numSlices, TextureFormat.ARGB32, MipMaps);

            defaultMetallicGlossMap = new Color32[width * height];
            for (int i = 0; i < width * height; i++)
            {
                defaultMetallicGlossMap[i] = new Color32(0, 0, 0, 255);
            }


            // Rollout tiles into texture array
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                Texture2D metallicGlossMap;
                // Import custom texture(s)
                if (TextureReplacement.CustomMetallicGlossExist(archive, record, 0))
                {
                    metallicGlossMap = TextureReplacement.LoadCustomMetallicGloss(archive, record, 0);
                }
                else
                {
                    //continue;
                    metallicGlossMap = new Texture2D(width, height, TextureFormat.ARGB32, MipMaps);
                    metallicGlossMap.SetPixels32(defaultMetallicGlossMap);
                }

                // enforce that all custom metallicgloss map textures have the same dimension (requirement of Texture2DArray)
                if ((metallicGlossMap.width != width) || (metallicGlossMap.height != height))
                {
                    return(null);
                }

                // Insert into texture array
                textureArray.SetPixels32(metallicGlossMap.GetPixels32(), record, 0);
            }
            textureArray.Apply(true, !stayReadable);

            // Change settings for these textures
            textureArray.wrapMode   = TextureWrapMode.Clamp;
            textureArray.anisoLevel = 8;

            return(textureArray);
        }
        /// <summary>
        /// Gets a custom material for a mobile billboard with textures and configuration imported from mods.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="meshFilter">The MeshFilter of the billboard object.</param>
        /// <param name="importedTextures">All the imported textures for the archive.</param>
        /// <remarks>
        /// Seek the texture for the first frame of the first record. If found, it imports the entire archive.
        /// If this texture has an emission map the material is considered emissive and all emission maps are imported.
        /// </remarks>
        /// <returns>A material or null.</returns>
        public static Material GetMobileBillboardMaterial(int archive, MeshFilter meshFilter, ref MobileBillboardImportedTextures importedTextures)
        {
            if (!DaggerfallUnity.Settings.AssetInjection)
            {
                return(null);
            }

            Texture2D tex, emission;

            if (importedTextures.HasImportedTextures = LoadFromCacheOrImport(archive, 0, 0, true, true, out tex, out emission))
            {
                string renderMode = null;

                // Read xml configuration
                XMLManager xml;
                if (XMLManager.TryReadXml(ImagesPath, string.Format("{0:000}", archive), out xml))
                {
                    xml.TryGetString("renderMode", out renderMode);
                    importedTextures.IsEmissive = xml.GetBool("emission");
                }

                // Make material
                Material material = MakeBillboardMaterial(renderMode);

                // Enable emission
                ToggleEmission(material, importedTextures.IsEmissive |= emission != null);

                // Load texture file to get record and frame count
                string fileName    = TextureFile.IndexToFileName(archive);
                var    textureFile = new TextureFile(Path.Combine(DaggerfallUnity.Instance.Arena2Path, fileName), FileUsage.UseMemory, true);

                // Import all textures in this archive
                importedTextures.Albedo       = new Texture2D[textureFile.RecordCount][];
                importedTextures.EmissionMaps = importedTextures.IsEmissive ? new Texture2D[textureFile.RecordCount][] : null;
                for (int record = 0; record < textureFile.RecordCount; record++)
                {
                    int frames            = textureFile.GetFrameCount(record);
                    var frameTextures     = new Texture2D[frames];
                    var frameEmissionMaps = importedTextures.IsEmissive ? new Texture2D[frames] : null;

                    for (int frame = 0; frame < frames; frame++)
                    {
                        if (record != 0 || frame != 0)
                        {
                            LoadFromCacheOrImport(archive, record, frame, importedTextures.IsEmissive, true, out tex, out emission);
                        }

                        frameTextures[frame] = tex ?? ImageReader.GetTexture(fileName, record, frame, true);
                        if (frameEmissionMaps != null)
                        {
                            frameEmissionMaps[frame] = emission ?? frameTextures[frame];
                        }
                    }

                    importedTextures.Albedo[record] = frameTextures;
                    if (importedTextures.EmissionMaps != null)
                    {
                        importedTextures.EmissionMaps[record] = frameEmissionMaps;
                    }
                }

                // Update UV map
                SetUv(meshFilter);

                return(material);
            }

            return(null);
        }
Esempio n. 8
0
        /// <summary>
        /// Gets specially packed tileset atlas for terrains.
        /// This needs to be improved to create each mip level manually to further reduce artifacts.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns></returns>
        public GetTextureResults GetTerrainTilesetTexture(
            int archive,
            bool stayReadable = false,
            SupportedNonAlphaTextureFormats nonAlphaFormat = SupportedNonAlphaTextureFormats.RGB24)
        {
            const int atlasDim   = 2048;
            const int gutterSize = 32;

            GetTextureResults results = new GetTextureResults();

            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);

            if (textureFile.RecordCount != 56)
            {
                return(results);
            }

            // Rollout tiles into atlas.
            // This is somewhat inefficient, but fortunately atlases only
            // need to be generated once and can be prepared offline where
            // startup time is critical.
            int x = 0, y = 0;

            Color32[] atlasColors = new Color32[atlasDim * atlasDim];
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                // Create base image with gutter
                DFSize    sz;
                Color32[] albedo = textureFile.GetColor32(record, 0, -1, gutterSize, out sz);

                // Wrap and clamp textures based on tile
                switch (record)
                {
                // Textures that do not tile in any direction
                case 5:
                case 7:
                case 10:
                case 12:
                case 15:
                case 17:
                case 20:
                case 22:
                case 25:
                case 27:
                case 30:
                case 32:
                case 34:
                case 35:
                case 36:
                case 37:
                case 38:
                case 39:
                case 40:
                case 41:
                case 42:
                case 43:
                case 44:
                case 45:
                case 47:
                case 48:
                case 49:
                case 50:
                case 51:
                case 52:
                case 53:
                case 55:
                    ImageProcessing.ClampBorder(ref albedo, sz, gutterSize);
                    break;

                // Textures that clamp horizontally and tile vertically
                case 6:
                case 11:
                case 16:
                case 21:
                case 26:
                case 31:
                    ImageProcessing.WrapBorder(ref albedo, sz, gutterSize, false);
                    ImageProcessing.ClampBorder(ref albedo, sz, gutterSize, true, false);
                    break;

                // Textures that tile in all directions
                default:
                    ImageProcessing.WrapBorder(ref albedo, sz, gutterSize);
                    break;
                }

                // Create variants
                Color32[] rotate     = ImageProcessing.RotateColors(ref albedo, sz.Width, sz.Height);
                Color32[] flip       = ImageProcessing.FlipColors(ref albedo, sz.Width, sz.Height);
                Color32[] rotateFlip = ImageProcessing.RotateColors(ref flip, sz.Width, sz.Height);

                // Insert into atlas
                ImageProcessing.InsertColors(ref albedo, ref atlasColors, x, y, sz.Width, sz.Height, atlasDim, atlasDim);
                ImageProcessing.InsertColors(ref rotate, ref atlasColors, x + sz.Width, y, sz.Width, sz.Height, atlasDim, atlasDim);
                ImageProcessing.InsertColors(ref flip, ref atlasColors, x + sz.Width * 2, y, sz.Width, sz.Height, atlasDim, atlasDim);
                ImageProcessing.InsertColors(ref rotateFlip, ref atlasColors, x + sz.Width * 3, y, sz.Width, sz.Height, atlasDim, atlasDim);

                // Increment position
                x += sz.Width * 4;
                if (x >= atlasDim)
                {
                    y += sz.Height;
                    x  = 0;
                }
            }

            // Create Texture2D
            Texture2D albedoAtlas = new Texture2D(atlasDim, atlasDim, ParseTextureFormat(nonAlphaFormat), MipMaps);

            albedoAtlas.SetPixels32(atlasColors);
            albedoAtlas.Apply(true, !stayReadable);

            // Change settings for these textures
            albedoAtlas.wrapMode   = TextureWrapMode.Clamp;
            albedoAtlas.anisoLevel = 8;

            // Store results
            results.albedoMap = albedoAtlas;

            return(results);
        }
Esempio n. 9
0
        /// <summary>
        /// Gets terrain normal map texture array containing each terrain tile in a seperate array slice.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>Texture2DArray or null</returns>
        public Texture2DArray GetTerrainNormalMapTextureArray(
            int archive,
            bool stayReadable = false,
            SupportedAlphaTextureFormats alphaFormat = SupportedAlphaTextureFormats.ARGB32)
        {
            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            int         numSlices   = 0;

            if (textureFile.RecordCount == 56)
            {
                numSlices = textureFile.RecordCount;
            }
            else
            {
                return(null);
            }

            Texture2DArray textureArray;

            if (!stayReadable && TryMakeTextureArrayCopyTexture(archive, numSlices, TextureMap.Normal, null, out textureArray))
            {
                return(textureArray);
            }

            int width;
            int height;

            // try to import first replacement texture for tile archive to determine width and height of replacement texture set (must be the same for all replacement textures for Texture2DArray)
            Texture2D normalMap;

            if (TextureReplacement.TryImportTexture(archive, 0, 0, TextureMap.Normal, false, out normalMap))
            {
                width  = normalMap.width;
                height = normalMap.height;
            }
            else
            {
                return(null);
            }

            textureArray = new Texture2DArray(width, height, numSlices, ParseTextureFormat(alphaFormat), MipMaps);

            // Rollout tiles into texture array
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                // Import custom texture(s)
                if (!TextureReplacement.TryImportTexture(archive, record, 0, TextureMap.Normal, false, out normalMap))
                {
                    // if current texture does not exist
                    Debug.LogErrorFormat("Terrain: imported archive {0} does not contain normal for record {1}.", archive, record);
                    return(null);
                }

                // enforce that all custom normal map textures have the same dimension (requirement of Texture2DArray)
                if ((normalMap.width != width) || (normalMap.height != height))
                {
                    Debug.LogErrorFormat("Terrain: failed to inject normal maps for archive {0}, incorrect size at record {1}.", archive, record);
                    return(null);
                }

                // Insert into texture array
                textureArray.SetPixels32(normalMap.GetPixels32(), record, 0);
            }
            textureArray.Apply(true, !stayReadable);

            // Change settings for these textures
            textureArray.wrapMode   = TextureWrapMode.Clamp;
            textureArray.anisoLevel = 8;

            return(textureArray);
        }
Esempio n. 10
0
        /// <summary>
        /// Gets terrain metallic gloss map texture array containing each terrain tile in a seperate array slice.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>Texture2DArray or null</returns>
        public Texture2DArray GetTerrainMetallicGlossMapTextureArray(
            int archive,
            bool stayReadable = false)
        {
            Color32[] defaultMetallicGlossMap;

            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            int         numSlices   = 0;

            if (textureFile.RecordCount == 56)
            {
                numSlices = textureFile.RecordCount;
            }
            else
            {
                return(null);
            }

            Texture2DArray textureArray;

            if (!stayReadable && TryMakeTextureArrayCopyTexture(archive, numSlices, TextureMap.MetallicGloss, new Color32(0, 0, 0, 255), out textureArray))
            {
                return(textureArray);
            }

            int width;
            int height;

            // try to import first replacement texture for tile archive to determine width and height of replacement texture set (must be the same for all replacement textures for Texture2DArray)
            Texture2D metallicGlossMap;

            if (TextureReplacement.TryImportTexture(archive, 0, 0, TextureMap.MetallicGloss, false, out metallicGlossMap))
            {
                width  = metallicGlossMap.width;
                height = metallicGlossMap.height;
            }
            else
            {
                // create default texture array (1x1 texture)
                width  = 1;
                height = 1;
            }

            textureArray = new Texture2DArray(width, height, numSlices, TextureFormat.ARGB32, MipMaps);

            defaultMetallicGlossMap = new Color32[width * height];
            for (int i = 0; i < width * height; i++)
            {
                defaultMetallicGlossMap[i] = new Color32(0, 0, 0, 255);
            }

            // Rollout tiles into texture array
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                // Import custom texture(s)
                if (!TextureReplacement.TryImportTexture(archive, record, 0, TextureMap.MetallicGloss, false, out metallicGlossMap))
                {
                    metallicGlossMap = new Texture2D(width, height, TextureFormat.ARGB32, MipMaps);
                    metallicGlossMap.SetPixels32(defaultMetallicGlossMap);
                }

                // enforce that all custom metallicgloss map textures have the same dimension (requirement of Texture2DArray)
                if ((metallicGlossMap.width != width) || (metallicGlossMap.height != height))
                {
                    Debug.LogErrorFormat("Terrain: failed to inject metallicgloss maps for archive {0}, incorrect size at record {1}.", archive, record);
                    return(null);
                }

                // Insert into texture array
                textureArray.SetPixels32(metallicGlossMap.GetPixels32(), record, 0);
            }
            textureArray.Apply(true, !stayReadable);

            // Change settings for these textures
            textureArray.wrapMode   = TextureWrapMode.Clamp;
            textureArray.anisoLevel = 8;

            return(textureArray);
        }
Esempio n. 11
0
        /// <summary>
        /// Gets terrain albedo texture array containing each terrain tile in a seperate array slice.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <returns>Texture2DArray or null</returns>
        public Texture2DArray GetTerrainAlbedoTextureArray(
            int archive,
            bool stayReadable = false)
        {
            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            int         numSlices   = 0;

            if (textureFile.RecordCount == 56)
            {
                numSlices = textureFile.RecordCount;
            }
            else
            {
                return(null);
            }

            Texture2DArray textureArray;

            if (!stayReadable && TryMakeTextureArrayCopyTexture(archive, numSlices, TextureMap.Albedo, null, out textureArray))
            {
                return(textureArray);
            }

            Texture2D albedoMap;

            if (TextureReplacement.TryImportTexture(archive, 0, 0, out albedoMap))
            {
                textureArray = new Texture2DArray(albedoMap.width, albedoMap.height, numSlices, TextureFormat.ARGB32, MipMaps);
            }
            else
            {
                textureArray = new Texture2DArray(textureFile.GetWidth(0), textureFile.GetWidth(1), numSlices, TextureFormat.ARGB32, MipMaps);
            }

            // Rollout tiles into texture array
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                Color32[] albedo;

                if (TextureReplacement.TryImportTexture(archive, record, 0, out albedoMap))
                {
                    // Import custom texture
                    albedo = albedoMap.GetPixels32();
                }
                else
                {
                    // Create base image with gutter
                    DFSize sz;
                    albedo = textureFile.GetColor32(record, 0, -1, 0, out sz);
                }

                // Insert into texture array
                textureArray.SetPixels32(albedo, record, 0);
            }
            textureArray.Apply(true, !stayReadable);

            // Change settings for these textures
            textureArray.wrapMode   = TextureWrapMode.Clamp;
            textureArray.anisoLevel = 8;

            return(textureArray);
        }
Esempio n. 12
0
        /// <summary>
        /// Loads all vertices for this DFMesh.
        /// </summary>
        /// <param name="model">Model object.</param>
        private void LoadVertices(ref ModelData model)
        {
            // Allocate vertex buffer
            model.Vertices = new VertexPositionNormalTextureBump[model.DFMesh.TotalVertices];

            // Track min and max vectors for bounding box
            Vector3 min = new Vector3(0, 0, 0);
            Vector3 max = new Vector3(0, 0, 0);

            // Loop through all submeshes
            int vertexCount = 0;

            foreach (DFMesh.DFSubMesh dfSubMesh in model.DFMesh.SubMeshes)
            {
                // Get texture dimensions for this submesh
                string archivePath     = Path.Combine(arena2Path, TextureFile.IndexToFileName(dfSubMesh.TextureArchive));
                System.Drawing.Size sz = TextureFile.QuickSize(archivePath, dfSubMesh.TextureRecord);

                // Ensure texture dimensions are POW2 as TextureManager will be emitting POW2 textures
                int     width  = (PowerOfTwo.IsPowerOfTwo(sz.Width)) ? sz.Width : PowerOfTwo.NextPowerOfTwo(sz.Width);
                int     height = (PowerOfTwo.IsPowerOfTwo(sz.Height)) ? sz.Height : PowerOfTwo.NextPowerOfTwo(sz.Height);
                Vector2 scale  = new Vector2(
                    (float)sz.Width / (float)width,
                    (float)sz.Height / (float)height);

                // Loop through all planes in this submesh
                foreach (DFMesh.DFPlane dfPlane in dfSubMesh.Planes)
                {
                    // Copy each point in this plane to vertex buffer
                    foreach (DFMesh.DFPoint dfPoint in dfPlane.Points)
                    {
                        // Daggerfall uses a different axis layout than XNA.
                        // The Y and Z axes should be inverted so the model is displayed correctly.
                        // This also requires a change to winding order in LoadIndices().
                        Vector3 position = new Vector3(dfPoint.X, -dfPoint.Y, -dfPoint.Z) * GlobalScale;
                        Vector3 normal   = new Vector3(dfPoint.NX, -dfPoint.NY, -dfPoint.NZ);

                        // Store vertex data
                        model.Vertices[vertexCount].Position          = position;
                        model.Vertices[vertexCount].Normal            = normal;
                        model.Vertices[vertexCount].TextureCoordinate = new Vector2(
                            (dfPoint.U / sz.Width) * scale.X,
                            (dfPoint.V / sz.Height) * scale.Y);

                        // Inrement count
                        vertexCount++;

                        // Compare min and max vectors
                        if (position.X < min.X)
                        {
                            min.X = position.X;
                        }
                        if (position.Y < min.Y)
                        {
                            min.Y = position.Y;
                        }
                        if (position.Z < min.Z)
                        {
                            min.Z = position.Z;
                        }
                        if (position.X > max.X)
                        {
                            max.X = position.X;
                        }
                        if (position.Y > max.Y)
                        {
                            max.Y = position.Y;
                        }
                        if (position.Z > max.Z)
                        {
                            max.Z = position.Z;
                        }
                    }
                }
            }

            // Create bounding box
            model.BoundingBox = new BoundingBox(min, max);

            // Find model centre
            Vector3 modelCenter;

            modelCenter.X = min.X + (max.X - min.X) / 2;
            modelCenter.Y = min.Y + (max.Y - min.Y) / 2;
            modelCenter.Z = min.Z + (max.Z - min.Z) / 2;

            // Find model radius
            float modelRadius;

            modelRadius = Vector3.Distance(min, max) / 2;

            // Create bounding sphere
            model.BoundingSphere = new BoundingSphere(modelCenter, modelRadius);
        }
Esempio n. 13
0
        /// <summary>
        /// Gets Unity textures from Daggerfall texture with all options.
        /// Returns all supported texture maps for Standard shader in one call.
        /// </summary>
        /// <param name="settings">Get texture settings.</param>
        /// <param name="alphaTextureFormat">Alpha TextureFormat.</param>
        /// <param name="textureImport">Options for import of custom textures.</param>
        /// <returns>GetTextureResults.</returns>
        public GetTextureResults GetTexture2D(
            GetTextureSettings settings,
            SupportedAlphaTextureFormats alphaTextureFormat = SupportedAlphaTextureFormats.ARGB32,
            TextureImport textureImport = TextureImport.None)
        {
            GetTextureResults results = new GetTextureResults();

            // Check if window or auto-emissive
            bool isWindow   = ClimateSwaps.IsExteriorWindow(settings.archive, settings.record);
            bool isEmissive = (settings.autoEmission) ? IsEmissive(settings.archive, settings.record) : false;

            // Override readable flag when user has set preference in material reader
            if (DaggerfallUnity.Instance.MaterialReader.ReadableTextures)
            {
                settings.stayReadable = true;
            }

            // Disable mipmaps in retro mode
            if (DaggerfallUnity.Settings.RetroRenderingMode > 0 && !DaggerfallUnity.Settings.UseMipMapsInRetroMode)
            {
                mipMaps = false;
            }

            // Assign texture file
            TextureFile textureFile;

            if (settings.textureFile == null)
            {
                textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(settings.archive)), FileUsage.UseMemory, true);
            }
            else
            {
                textureFile = settings.textureFile;
            }

            // Get starting DFBitmap and albedo Color32 array
            DFSize   sz;
            DFBitmap srcBitmap = textureFile.GetDFBitmap(settings.record, settings.frame);

            Color32[] albedoColors = textureFile.GetColor32(srcBitmap, settings.alphaIndex, settings.borderSize, out sz);

            // Sharpen source image
            if (settings.sharpen)
            {
                albedoColors = ImageProcessing.Sharpen(ref albedoColors, sz.Width, sz.Height);
            }

            // Dilate edges
            if (settings.borderSize > 0 && settings.dilate && !settings.copyToOppositeBorder)
            {
                ImageProcessing.DilateColors(ref albedoColors, sz);
            }

            // Copy to opposite border
            if (settings.borderSize > 0 && settings.copyToOppositeBorder)
            {
                ImageProcessing.WrapBorder(ref albedoColors, sz, settings.borderSize);
            }

            // Set albedo texture
            Texture2D albedoMap;

            if (!TextureReplacement.TryImportTexture(settings.archive, settings.record, settings.frame, TextureMap.Albedo, textureImport, !settings.stayReadable, out albedoMap))
            {
                // Create albedo texture
                albedoMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(alphaTextureFormat), MipMaps);
                albedoMap.SetPixels32(albedoColors);
                albedoMap.Apply(true, !settings.stayReadable);
            }

            // Adjust mipmap bias of albedo map when retro mode rendering is enabled
            if (albedoMap && DaggerfallUnity.Settings.RetroRenderingMode > 0)
            {
                albedoMap.mipMapBias = -0.75f;
            }

            // Set normal texture (always import normal if present on disk)
            Texture2D normalMap         = null;
            bool      normalMapImported = TextureReplacement.TryImportTexture(settings.archive, settings.record, settings.frame, TextureMap.Normal, textureImport, !settings.stayReadable, out normalMap);

            if (!normalMapImported && settings.createNormalMap && textureFile.SolidType == TextureFile.SolidTypes.None)
            {
                // Create normal texture - must be ARGB32
                // Normal maps are bypassed for solid-colour textures
                Color32[] normalColors;
                normalColors = ImageProcessing.GetBumpMap(ref albedoColors, sz.Width, sz.Height);
                normalColors = ImageProcessing.ConvertBumpToNormals(ref normalColors, sz.Width, sz.Height, settings.normalStrength);
                normalMap    = new Texture2D(sz.Width, sz.Height, TextureFormat.ARGB32, MipMaps);
                normalMap.SetPixels32(normalColors);
                normalMap.Apply(true, !settings.stayReadable);
            }

            // Import emission map or create basic emissive texture
            Texture2D emissionMap    = null;
            bool      resultEmissive = false;

            if (TextureReplacement.TryImportTexture(settings.archive, settings.record, settings.frame, TextureMap.Emission, textureImport, !settings.stayReadable, out emissionMap))
            {
                // Always import emission if present on disk
                resultEmissive = true;
            }
            else
            {
                if (settings.createEmissionMap || (settings.autoEmission && isEmissive) && !isWindow)
                {
                    // Just reuse albedo map for basic colour emission
                    emissionMap    = albedoMap;
                    resultEmissive = true;
                }

                // Windows need special handling as only glass parts are emissive
                if ((settings.createEmissionMap || settings.autoEmissionForWindows) && isWindow)
                {
                    // Create custom emission texture for glass area of windows
                    Color32[] emissionColors = textureFile.GetWindowColors32(srcBitmap);
                    emissionMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(alphaTextureFormat), MipMaps);
                    emissionMap.SetPixels32(emissionColors);
                    emissionMap.Apply(true, !settings.stayReadable);
                    resultEmissive = true;
                }

                // Lights need special handling as this archive contains a mix of emissive and non-emissive flats
                // This can cause problems with atlas packing due to mismatch between albedo and emissive texture counts
                if ((settings.createEmissionMap || settings.autoEmission) && settings.archive == LightsTextureArchive)
                {
                    // For the unlit flats we create a null-emissive black texture
                    if (!isEmissive)
                    {
                        Color32[] emissionColors = new Color32[albedoMap.width * albedoMap.height];
                        emissionMap = new Texture2D(albedoMap.width, albedoMap.height, ParseTextureFormat(alphaTextureFormat), MipMaps);
                        emissionMap.SetPixels32(emissionColors);
                        emissionMap.Apply(true, !settings.stayReadable);
                        resultEmissive = true;
                    }
                }
            }

            // Shrink UV rect to compensate for internal border
            float ru = 1f / sz.Width;
            float rv = 1f / sz.Height;

            results.singleRect = new Rect(
                settings.borderSize * ru,
                settings.borderSize * rv,
                (sz.Width - settings.borderSize * 2) * ru,
                (sz.Height - settings.borderSize * 2) * rv);

            // Store results
            results.albedoMap   = albedoMap;
            results.normalMap   = normalMap;
            results.emissionMap = emissionMap;
            results.isWindow    = isWindow;
            results.isEmissive  = resultEmissive;
            results.textureFile = textureFile;

            return(results);
        }
Esempio n. 14
0
        /// <summary>
        /// Gets Unity textures from Daggerfall texture with all options.
        /// Returns all supported texture maps for Standard shader in one call.
        /// </summary>
        /// <param name="settings">Get texture settings.</param>
        /// <param name="alphaTextureFormat">Alpha TextureFormat.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>GetTextureResults.</returns>
        public GetTextureResults GetTexture2D(
            GetTextureSettings settings,
            SupportedAlphaTextureFormats alphaTextureFormat = SupportedAlphaTextureFormats.RGBA32,
            SupportedNonAlphaTextureFormats nonAlphaFormat  = SupportedNonAlphaTextureFormats.RGB24)
        {
            GetTextureResults results = new GetTextureResults();

            // Check if window or auto-emissive
            bool isWindow   = ClimateSwaps.IsExteriorWindow(settings.archive, settings.record);
            bool isEmissive = (settings.autoEmission) ? IsEmissive(settings.archive, settings.record) : false;

            // Assign texture file
            TextureFile textureFile;

            if (settings.textureFile == null)
            {
                textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(settings.archive)), FileUsage.UseMemory, true);
            }
            else
            {
                textureFile = settings.textureFile;
            }

            // Get starting DFBitmap and albedo Color32 array
            DFSize   sz;
            DFBitmap srcBitmap = textureFile.GetDFBitmap(settings.record, settings.frame);

            Color32[] albedoColors = textureFile.GetColor32(srcBitmap, settings.alphaIndex, settings.borderSize, out sz);

            // Sharpen source image
            if (settings.sharpen)
            {
                albedoColors = ImageProcessing.Sharpen(ref albedoColors, sz.Width, sz.Height);
            }

            // Dilate edges
            if (settings.borderSize > 0 && settings.dilate && !settings.copyToOppositeBorder)
            {
                ImageProcessing.DilateColors(ref albedoColors, sz);
            }

            // Copy to opposite border
            if (settings.borderSize > 0 && settings.copyToOppositeBorder)
            {
                ImageProcessing.WrapBorder(ref albedoColors, sz, settings.borderSize);
            }

            // Create albedo texture
            Texture2D albedoMap = null;

            if (settings.alphaIndex < 0)
            {
                albedoMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(nonAlphaFormat), MipMaps);
            }
            else
            {
                albedoMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(alphaTextureFormat), MipMaps);
            }
            albedoMap.SetPixels32(albedoColors);
            albedoMap.Apply(true, !settings.stayReadable);

            // Create normal texture - must be ARGB32
            // Normal maps are bypassed for solid-colour textures
            Texture2D normalMap = null;

            if (settings.createNormalMap && textureFile.SolidType == TextureFile.SolidTypes.None)
            {
                Color32[] normalColors;
                normalColors = ImageProcessing.GetBumpMap(ref albedoColors, sz.Width, sz.Height);
                normalColors = ImageProcessing.ConvertBumpToNormals(ref normalColors, sz.Width, sz.Height, settings.normalStrength);
                normalMap    = new Texture2D(sz.Width, sz.Height, TextureFormat.ARGB32, MipMaps);
                normalMap.SetPixels32(normalColors);
                normalMap.Apply(true, !settings.stayReadable);
            }

            // Create basic emissive texture
            Texture2D emissionMap    = null;
            bool      resultEmissive = false;

            if (settings.createEmissionMap || (settings.autoEmission && isEmissive) && !isWindow)
            {
                // Just reuse albedo map for basic colour emission
                emissionMap    = albedoMap;
                resultEmissive = true;
            }

            // Windows need special handling as only glass parts are emissive
            if ((settings.createEmissionMap || settings.autoEmissionForWindows) && isWindow)
            {
                // Create custom emission texture for glass area of windows
                Color32[] emissionColors = textureFile.GetWindowColors32(srcBitmap);
                emissionMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(alphaTextureFormat), MipMaps);
                emissionMap.SetPixels32(emissionColors);
                emissionMap.Apply(true, !settings.stayReadable);
                resultEmissive = true;
            }

            // Lights need special handling as this archive contains a mix of emissive and non-emissive flats
            // This can cause problems with atlas packing due to mismatch between albedo and emissive texture counts
            if ((settings.createEmissionMap || settings.autoEmission) && settings.archive == LightsTextureArchive)
            {
                // For the unlit flats we create a null-emissive black texture
                if (!isEmissive)
                {
                    Color32[] emissionColors = new Color32[sz.Width * sz.Height];
                    emissionMap = new Texture2D(sz.Width, sz.Height, ParseTextureFormat(alphaTextureFormat), MipMaps);
                    emissionMap.SetPixels32(emissionColors);
                    emissionMap.Apply(true, !settings.stayReadable);
                    resultEmissive = true;
                }
            }

            // Shrink UV rect to compensate for internal border
            float ru = 1f / sz.Width;
            float rv = 1f / sz.Height;

            results.singleRect = new Rect(
                settings.borderSize * ru,
                settings.borderSize * rv,
                (sz.Width - settings.borderSize * 2) * ru,
                (sz.Height - settings.borderSize * 2) * rv);

            // Store results
            results.albedoMap   = albedoMap;
            results.normalMap   = normalMap;
            results.emissionMap = emissionMap;
            results.isWindow    = isWindow;
            results.isEmissive  = resultEmissive;
            results.textureFile = textureFile;

            return(results);
        }
Esempio n. 15
0
        /// <summary>
        /// Gets inventory/equip image for specified item.
        /// Image will be cached based on material and hand for faster subsequent fetches.
        /// </summary>
        /// <param name="item">Item to fetch image for.</param>
        /// <param name="removeMask">Removes mask index (e.g. around helmets) from final image.</param>
        /// <param name="forPaperDoll">Image is for paper doll.</param>
        /// <returns>ImageData.</returns>
        public ImageData GetItemImage(DaggerfallUnityItem item, bool removeMask = false, bool forPaperDoll = false)
        {
            // Get colour
            int color = (int)item.dyeColor;

            // Get archive and record indices
            int archive = item.InventoryTextureArchive;
            int record  = item.InventoryTextureRecord;

            // Paper doll handling
            if (forPaperDoll)
            {
                // 1H Weapons in right hand need record + 1
                if (item.ItemGroup == ItemGroups.Weapons && item.EquipSlot == EquipSlots.RightHand)
                {
                    if (ItemEquipTable.GetItemHands(item) == ItemHands.Either)
                    {
                        record += 1;
                    }
                }
            }

            // Get unique key
            int key = MakeImageKey(color, archive, record, removeMask);

            // Get existing icon if in cache
            if (itemImages.ContainsKey(key))
            {
                return(itemImages[key]);
            }

            // Load image data
            string    filename = TextureFile.IndexToFileName(archive);
            ImageData data     = ImageReader.GetImageData(filename, record, 0, true, false);

            if (data.type == ImageTypes.None)
            {
                throw new Exception("GetItemImage() could not load image data.");
            }

            // Remove mask if requested
            if (removeMask)
            {
                data.dfBitmap = ImageProcessing.ChangeMask(data.dfBitmap);
            }

            // Change dye or just update texture
            ItemGroups group = item.ItemGroup;
            DyeColors  dye   = (DyeColors)color;

            if (group == ItemGroups.Weapons || group == ItemGroups.Armor)
            {
                data = ChangeDye(data, dye, DyeTargets.WeaponsAndArmor);
            }
            else if (item.ItemGroup == ItemGroups.MensClothing || item.ItemGroup == ItemGroups.WomensClothing)
            {
                data = ChangeDye(data, dye, DyeTargets.Clothing);
            }
            else
            {
                ImageReader.UpdateTexture(ref data);
            }

            // Add to cache
            itemImages.Add(key, data);

            return(data);
        }
Esempio n. 16
0
        /// <summary>
        /// Gets Texture2D atlas from Daggerfall texture archive.
        /// Every record and frame in the archive will be added to atlas.
        /// An array of rects will be returned with sub-texture rect for each record index and frame.
        /// Currently supports one archive per atlas. Super-atlas packing (multiple archives) is in the works.
        /// </summary>
        /// <param name="settings">Get texture settings.</param>
        /// <param name="alphaTextureFormat">Alpha TextureFormat.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>GetTextureResults.</returns>
        public GetTextureResults GetTexture2DAtlas(
            GetTextureSettings settings,
            SupportedAlphaTextureFormats alphaTextureFormat = SupportedAlphaTextureFormats.RGBA32,
            SupportedNonAlphaTextureFormats nonAlphaFormat  = SupportedNonAlphaTextureFormats.RGB24)
        {
            GetTextureResults results = new GetTextureResults();

            // Individual textures must remain readable to pack into atlas
            bool stayReadable = settings.stayReadable;

            settings.stayReadable = true;

            // Assign texture file
            TextureFile textureFile;

            if (settings.textureFile == null)
            {
                textureFile          = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(settings.archive)), FileUsage.UseMemory, true);
                settings.textureFile = textureFile;
            }
            else
            {
                textureFile = settings.textureFile;
            }

            // Create lists
            results.atlasSizes       = new List <Vector2>(textureFile.RecordCount);
            results.atlasScales      = new List <Vector2>(textureFile.RecordCount);
            results.atlasOffsets     = new List <Vector2>(textureFile.RecordCount);
            results.atlasFrameCounts = new List <int>(textureFile.RecordCount);

            // Read every texture in archive
            bool               hasNormalMaps    = false;
            bool               hasEmissionMaps  = false;
            bool               hasAnimation     = false;
            List <Texture2D>   albedoTextures   = new List <Texture2D>();
            List <Texture2D>   normalTextures   = new List <Texture2D>();
            List <Texture2D>   emissionTextures = new List <Texture2D>();
            List <RecordIndex> indices          = new List <RecordIndex>();

            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                // Get record index and frame count
                settings.record = record;
                int frames = textureFile.GetFrameCount(record);
                if (frames > 1)
                {
                    hasAnimation = true;
                }

                // Get record information
                DFSize      size   = textureFile.GetSize(record);
                DFSize      scale  = textureFile.GetScale(record);
                DFPosition  offset = textureFile.GetOffset(record);
                RecordIndex ri     = new RecordIndex()
                {
                    startIndex = albedoTextures.Count,
                    frameCount = frames,
                    width      = size.Width,
                    height     = size.Height,
                };
                indices.Add(ri);
                for (int frame = 0; frame < frames; frame++)
                {
                    settings.frame = frame;
                    GetTextureResults nextTextureResults = GetTexture2D(settings, alphaTextureFormat, nonAlphaFormat);
                    albedoTextures.Add(nextTextureResults.albedoMap);
                    if (nextTextureResults.normalMap != null)
                    {
                        normalTextures.Add(nextTextureResults.normalMap);
                        hasNormalMaps = true;
                    }
                    if (nextTextureResults.emissionMap != null)
                    {
                        emissionTextures.Add(nextTextureResults.emissionMap);
                        hasEmissionMaps = true;
                    }
                }

                results.atlasSizes.Add(new Vector2(size.Width, size.Height));
                results.atlasScales.Add(new Vector2(scale.Width, scale.Height));
                results.atlasOffsets.Add(new Vector2(offset.X, offset.Y));
                results.atlasFrameCounts.Add(frames);
                results.textureFile = textureFile;
            }

            // Pack albedo textures into atlas and get our rects
            Texture2D atlasAlbedoMap = new Texture2D(settings.atlasMaxSize, settings.atlasMaxSize, ParseTextureFormat(alphaTextureFormat), MipMaps);

            Rect[] rects = atlasAlbedoMap.PackTextures(albedoTextures.ToArray(), settings.atlasPadding, settings.atlasMaxSize, !stayReadable);

            // Pack normal textures into atlas
            Texture2D atlasNormalMap = null;

            if (hasNormalMaps)
            {
                // Normals must be ARGB32
                atlasNormalMap = new Texture2D(settings.atlasMaxSize, settings.atlasMaxSize, TextureFormat.ARGB32, MipMaps);
                atlasNormalMap.PackTextures(normalTextures.ToArray(), settings.atlasPadding, settings.atlasMaxSize, !stayReadable);
            }

            // Pack emission textures into atlas
            // TODO: Change this as packing not consistent
            Texture2D atlasEmissionMap = null;

            if (hasEmissionMaps)
            {
                // Repacking to ensure correct mix of lit and unlit
                atlasEmissionMap = new Texture2D(settings.atlasMaxSize, settings.atlasMaxSize, ParseTextureFormat(alphaTextureFormat), MipMaps);
                atlasEmissionMap.PackTextures(emissionTextures.ToArray(), settings.atlasPadding, settings.atlasMaxSize, !stayReadable);
            }

            // Add to results
            if (results.atlasRects == null)
            {
                results.atlasRects = new List <Rect>(rects.Length);
            }
            if (results.atlasIndices == null)
            {
                results.atlasIndices = new List <RecordIndex>(indices.Count);
            }
            results.atlasRects.AddRange(rects);
            results.atlasIndices.AddRange(indices);

            // Shrink UV rect to compensate for internal border
            float ru          = 1f / atlasAlbedoMap.width;
            float rv          = 1f / atlasAlbedoMap.height;
            int   finalBorder = settings.borderSize + settings.atlasShrinkUVs;

            for (int i = 0; i < results.atlasRects.Count; i++)
            {
                Rect rct = results.atlasRects[i];
                rct.xMin += finalBorder * ru;
                rct.xMax -= finalBorder * ru;
                rct.yMin += finalBorder * rv;
                rct.yMax -= finalBorder * rv;
                results.atlasRects[i] = rct;
            }

            // Store results
            results.albedoMap       = atlasAlbedoMap;
            results.normalMap       = atlasNormalMap;
            results.emissionMap     = atlasEmissionMap;
            results.isAtlasAnimated = hasAnimation;
            results.isEmissive      = hasEmissionMaps;

            return(results);
        }
Esempio n. 17
0
        /// <summary>
        /// Gets terrain normal map texture array containing each terrain tile in a seperate array slice.
        /// </summary>
        /// <param name="archive">Archive index.</param>
        /// <param name="stayReadable">Texture should stay readable.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>Texture2DArray or null</returns>
        public Texture2DArray GetTerrainNormalMapTextureArray(
            int archive,
            bool stayReadable = false,
            SupportedAlphaTextureFormats alphaFormat = SupportedAlphaTextureFormats.RGBA32)
        {
            // Load texture file and check count matches terrain tiles
            TextureFile textureFile = new TextureFile(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);
            int         numSlices   = 0;

            if (textureFile.RecordCount == 56)
            {
                numSlices = textureFile.RecordCount;
            }
            else
            {
                return(null);
            }

            Texture2DArray textureArray;
            int            width;
            int            height;

            // try to import first replacement texture for tile archive to determine width and height of replacement texture set (must be the same for all replacement textures for Texture2DArray)
            if (TextureReplacement.CustomNormalExist(archive, 0, 0))
            {
                Texture2D normalMap = TextureReplacement.LoadCustomNormal(archive, 0, 0);
                width  = normalMap.width;
                height = normalMap.height;
            }
            else
            {
                return(null);
            }

            textureArray = new Texture2DArray(width, height, numSlices, TextureFormat.ARGB32, MipMaps);

            // Rollout tiles into texture array
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                Texture2D normalMap;
                // Import custom texture(s)
                if (TextureReplacement.CustomNormalExist(archive, record, 0))
                {
                    normalMap = TextureReplacement.LoadCustomNormal(archive, record, 0);
                }
                else // if current texture does not exist
                {
                    return(null);
                }

                // enforce that all custom normal map textures have the same dimension (requirement of Texture2DArray)
                if ((normalMap.width != width) || (normalMap.height != height))
                {
                    return(null);
                }

                // Insert into texture array
                textureArray.SetPixels32(normalMap.GetPixels32(), record, 0);
            }
            textureArray.Apply(true, !stayReadable);

            // Change settings for these textures
            textureArray.wrapMode   = TextureWrapMode.Clamp;
            textureArray.anisoLevel = 8;

            return(textureArray);
        }
Esempio n. 18
0
        private void DisplayAboutGUI()
        {
            EditorGUILayout.Space();
            ShowCustomBillboardFoldout = GUILayoutHelper.Foldout(ShowCustomBillboardFoldout, new GUIContent("Custom"), () =>
            {
                var propCustomArchive = Prop("customArchive");
                var propCustomRecord  = Prop("customRecord");
                GUILayoutHelper.Indent(() =>
                {
                    propCustomArchive.intValue = EditorGUILayout.IntField(new GUIContent("Archive", "Set texture archive index (e.g. TEXTURE.210 is 210)"), propCustomArchive.intValue);
                    propCustomRecord.intValue  = EditorGUILayout.IntField(new GUIContent("Record", "Set texture record index (between 0-n)"), propCustomRecord.intValue);
                    if (GUILayout.Button("Set Billboard Texture"))
                    {
                        try
                        {
                            dfBillboard.SetMaterial(propCustomArchive.intValue, propCustomRecord.intValue);
                        }
                        catch (Exception ex)
                        {
                            Debug.Log("Failed to set custom billboard texture. Exception: " + ex.Message);
                        }
                    }
                    if (GUILayout.Button("Align To Surface"))
                    {
                        GameObjectHelper.AlignBillboardToGround(dfBillboard.gameObject, dfBillboard.Summary.Size, 4);
                    }
                });
            });

            EditorGUILayout.Space();
            ShowAboutBillboardFoldout = GUILayoutHelper.Foldout(ShowAboutBillboardFoldout, new GUIContent("About"), () =>
            {
                GUILayoutHelper.Indent(() =>
                {
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("File", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(TextureFile.IndexToFileName(dfBillboard.Summary.Archive), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("Index", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(dfBillboard.Summary.Record.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                    //GUILayoutHelper.Horizontal(() =>
                    //{
                    //    EditorGUILayout.LabelField("In Dungeon", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                    //    EditorGUILayout.SelectableLabel(dfBillboard.Summary.InDungeon.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    //});
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("Is Mobile", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(dfBillboard.Summary.IsMobile.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                    if (dfBillboard.Summary.IsMobile)
                    {
                        GUILayoutHelper.Horizontal(() =>
                        {
                            EditorGUILayout.LabelField("Flags", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                            EditorGUILayout.SelectableLabel(dfBillboard.Summary.Flags.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                        });
                        GUILayoutHelper.Horizontal(() =>
                        {
                            EditorGUILayout.LabelField("Type", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                            EditorGUILayout.SelectableLabel(dfBillboard.Summary.FixedEnemyType.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                        });
                    }
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("Is Atlased", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(dfBillboard.Summary.AtlasedMaterial.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("Is Animated", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(dfBillboard.Summary.AnimatedMaterial.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                    GUILayoutHelper.Horizontal(() =>
                    {
                        EditorGUILayout.LabelField("Current Frame", GUILayout.Width(EditorGUIUtility.labelWidth - 4));
                        EditorGUILayout.SelectableLabel(dfBillboard.Summary.CurrentFrame.ToString(), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
                    });
                });
            });
        }
        /// <summary>
        /// Gets Texture2D atlas from Daggerfall texture archive.
        /// Every record and frame in the archive will be added to atlas.
        /// An array of rects will be returned with sub-texture rect for each record index and frame.
        /// Used for non-tiling textures like flats and ground tiles.
        /// </summary>
        /// <param name="archive">Archive index to create atlas from.</param>
        /// <param name="alphaIndex">Index to receive transparent alpha.</param>
        /// <param name="padding">Number of pixels padding around each sub-texture.</param>
        /// <param name="maxAtlasSize">Max size of atlas.</param>
        /// <param name="rectsOut">Array of rects, one for each record sub-texture and frame.</param>
        /// <param name="indicesOut">Array of record indices into rect array, accounting for animation frames.</param>
        /// <param name="border">Number of pixels internal border around each texture.</param>
        /// <param name="dilate">Blend texture into surrounding empty pixels.</param>
        /// <param name="shrinkUVs">Number of extra pixels to shrink UV rect.</param>
        /// <param name="copyToOppositeBorder">Copy texture edges to opposite border. Requires border, will overwrite dilate.</param>
        /// <returns>Texture2D atlas or null.</returns>
        public Texture2D GetTexture2DAtlas(
            int archive,
            int alphaIndex,
            int padding,
            int maxAtlasSize,
            out Rect[] rectsOut,
            out RecordIndex[] indicesOut,
            out Vector2[] sizesOut,
            out Vector2[] scalesOut,
            out Vector2[] offsetsOut,
            int border,
            bool dilate,
            int shrinkUVs             = 0,
            bool copyToOppositeBorder = false,
            bool makeNoLongerReadable = true)
        {
            // Ready check
            if (!ReadyCheck())
            {
                rectsOut   = null;
                indicesOut = null;
                sizesOut   = null;
                scalesOut  = null;
                offsetsOut = null;
                return(null);
            }

            // Load texture file
            textureFile.Load(Path.Combine(Arena2Path, TextureFile.IndexToFileName(archive)), FileUsage.UseMemory, true);

            // Read every texture in archive
            Rect               rect;
            List <Texture2D>   textures = new List <Texture2D>();
            List <RecordIndex> indices  = new List <RecordIndex>();

            sizesOut   = new Vector2[textureFile.RecordCount];
            scalesOut  = new Vector2[textureFile.RecordCount];
            offsetsOut = new Vector2[textureFile.RecordCount];
            for (int record = 0; record < textureFile.RecordCount; record++)
            {
                int         frames = textureFile.GetFrameCount(record);
                DFSize      size   = textureFile.GetSize(record);
                DFSize      scale  = textureFile.GetScale(record);
                DFSize      offset = textureFile.GetOffset(record);
                RecordIndex ri     = new RecordIndex()
                {
                    startIndex = textures.Count,
                    frameCount = frames,
                    width      = size.Width,
                    height     = size.Height,
                };
                indices.Add(ri);
                for (int frame = 0; frame < frames; frame++)
                {
                    textures.Add(GetTexture2D(archive, record, frame, alphaIndex, out rect, border, dilate, copyToOppositeBorder, false));
                }

                sizesOut[record]   = new Vector2(size.Width, size.Height);
                scalesOut[record]  = new Vector2(scale.Width, scale.Height);
                offsetsOut[record] = new Vector2(offset.Width, offset.Height);
            }

            // Pack textures into atlas
            Texture2D atlas = new Texture2D(maxAtlasSize, maxAtlasSize, TextureFormat.RGBA32, MipMaps);

            rectsOut   = atlas.PackTextures(textures.ToArray(), padding, maxAtlasSize, makeNoLongerReadable);
            indicesOut = indices.ToArray();

            // Shrink UV rect to compensate for internal border
            float ru = 1f / atlas.width;
            float rv = 1f / atlas.height;

            border += shrinkUVs;
            for (int i = 0; i < rectsOut.Length; i++)
            {
                Rect rct = rectsOut[i];
                rct.xMin   += border * ru;
                rct.xMax   -= border * ru;
                rct.yMin   += border * rv;
                rct.yMax   -= border * rv;
                rectsOut[i] = rct;
            }

            return(atlas);
        }