/// <summary>
        /// Precalculate and cache billboard scale for every record.
        /// This will change based on animation state and orientation.
        /// Cache this to array so it only needs to be calculated once.
        /// Also store number of frames for state animations.
        /// </summary>
        /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param>
        /// <param name="archive">Texture archive index derived from type and gender.</param>
        private void CacheRecordSizesAndFrames(DaggerfallUnity dfUnity, int archive)
        {
            // Open texture file
            string      path        = Path.Combine(dfUnity.Arena2Path, TextureFile.IndexToFileName(archive));
            TextureFile textureFile = new TextureFile(path, FileUsage.UseMemory, true);

            // Cache size and scale for each record
            summary.RecordSizes  = new Vector2[textureFile.RecordCount];
            summary.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(archive, i, ref finalSize);

                // Store final size and frame count
                summary.RecordSizes[i]  = finalSize * MeshReader.GlobalScale;
                summary.RecordFrames[i] = textureFile.GetFrameCount(i);
            }
        }
Ejemplo n.º 2
0
        private void SetEnemy()
        {
            // Get random enemy
            int         index       = Random.Range(0, GameObjectHelper.EnemyDict.Count);
            MobileEnemy mobileEnemy = GameObjectHelper.EnemyDict.ElementAt(index).Value;

            // Get random texture
            int         archive     = Random.value < 0.5f ? mobileEnemy.MaleTexture : mobileEnemy.FemaleTexture;
            string      fileName    = string.Format("TEXTURE.{0}", archive);
            TextureFile textureFile = new TextureFile(Path.Combine(DaggerfallUnity.Instance.Arena2Path, fileName), FileUsage.UseMemory, true);
            int         record      = Random.Range(0, textureFile.RecordCount);
            int         frame       = Random.Range(0, textureFile.GetFrameCount(record));

            // Set fields
            enemyTexture = ImageReader.GetTexture(fileName, record, frame, true);
            enemyName    = TextManager.Instance.GetLocalizedEnemyName(mobileEnemy.ID);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Import textures for all records and frames of this enemy.
        /// </summary>
        public static void SetEnemyImportedTextures(int archive, MeshFilter meshFilter, ref EnemyImportedTextures importedTextures)
        {
            if (!DaggerfallUnity.Settings.MeshAndTextureReplacement)
            {
                return;
            }

            // Check first texture.
            Texture2D tex;
            bool      hasImportedTextures = LoadFromCacheOrImport(archive, 0, 0, out tex);

            if (importedTextures.HasImportedTextures = hasImportedTextures)
            {
                string fileName    = TextureFile.IndexToFileName(archive);
                var    textureFile = new TextureFile(Path.Combine(DaggerfallUnity.Instance.Arena2Path, fileName), FileUsage.UseMemory, true);

                // Import all textures in this archive
                var textures = new List <List <Texture2D> >();
                for (int record = 0; record < textureFile.RecordCount; record++)
                {
                    int frames        = textureFile.GetFrameCount(record);
                    var frameTextures = new List <Texture2D>();
                    for (int frame = 0; frame < frames; frame++)
                    {
                        if ((record != 0 || frame != 0) && !LoadFromCacheOrImport(archive, record, frame, out tex))
                        {
                            Debug.LogErrorFormat("Imported archive {0} does not contain texture for record {1}, frame {2}!", archive, record, frame);
                            tex = ImageReader.GetTexture(fileName, record, frame, true);
                        }
                        frameTextures.Add(tex);
                    }
                    textures.Add(frameTextures);
                }

                // Update UV map
                SetUv(meshFilter);

                // Save results
                importedTextures.Textures = textures;
            }
        }
        /// <summary>
        /// Precalculate and cache billboard scale for every record.
        /// This will change based on animation state and orientation.
        /// Cache this to array so it only needs to be calculated once.
        /// Also store number of frames for state animations.
        /// </summary>
        /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param>
        /// <param name="archive">Texture archive index derived from type and gender.</param>
        private void CacheRecordSizesAndFrames(DaggerfallUnity dfUnity, int archive)
        {
            // Open texture file
            string      path        = Path.Combine(dfUnity.Arena2Path, TextureFile.IndexToFileName(archive));
            TextureFile textureFile = new TextureFile(path, FileUsage.UseMemory, true);

            // Cache size and scale for each record
            summary.RecordSizes  = new Vector2[textureFile.RecordCount];
            summary.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);

                // Get eventual custom scale
                if ((summary.CustomMaterial.isCustom) && (XMLManager.XmlFileExist(archive, i)))
                {
                    Vector2 enemyScale = XMLManager.GetScale(TextureReplacement.GetName(archive, i), TextureReplacement.TexturesPath);
                    finalSize.x *= enemyScale.x;
                    finalSize.y *= enemyScale.y;
                }

                // Store final size and frame count
                summary.RecordSizes[i]  = finalSize * MeshReader.GlobalScale;
                summary.RecordFrames[i] = textureFile.GetFrameCount(i);
            }
        }
        /// <summary>
        /// Reads any Daggerfall image file to ImageData package.
        /// </summary>
        /// <param name="filename">Name of standalone file as it appears in arena2 folder.</param>
        /// <param name="record">Which image record to read for multi-image files.</param>
        /// <param name="frame">Which frame to read for multi-frame images.</param>
        /// <param name="hasAlpha">Enable this for image cutouts.</param>
        /// <param name="createTexture">Create a Texture2D.</param>
        /// <param name="createAllFrameTextures">Creates a Texture2D for every frame in a TEXTURE file (if greater than 1 frames).</param>
        /// <param name="alphaIndex">Set palette index for alpha checks (default is 0).</param>
        /// <returns>ImageData. If result.type == ImageTypes.None then read failed.</returns>
        public static ImageData GetImageData(string filename, int record = 0, int frame = 0, bool hasAlpha = false, bool createTexture = true, bool createAllFrameTextures = false, int alphaIndex = 0)
        {
            // Check API ready
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;

            if (!dfUnity.IsReady)
            {
                return(new ImageData());
            }

            // Parse image file type
            ImageTypes fileType;

            try
            {
                fileType = ParseFileType(filename);
            }
            catch
            {
                return(new ImageData());
            }

            // Create base image data
            ImageData imageData = new ImageData();

            imageData.type       = fileType;
            imageData.filename   = filename;
            imageData.record     = record;
            imageData.frame      = frame;
            imageData.hasAlpha   = hasAlpha;
            imageData.alphaIndex = alphaIndex;

            // Read supported image files
            DFBitmap dfBitmap = null;

            DFBitmap[] dfBitmapAllFrames = null;
            switch (fileType)
            {
            case ImageTypes.TEXTURE:
                TextureFile textureFile = new TextureFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                textureFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, textureFile.PaletteName));
                dfBitmap = textureFile.GetDFBitmap(record, frame);
                int frameCount = textureFile.GetFrameCount(record);
                if (createAllFrameTextures && frameCount > 1)
                {
                    dfBitmapAllFrames = new DFBitmap[frameCount];
                    for (int i = 0; i < frameCount; i++)
                    {
                        dfBitmapAllFrames[i] = textureFile.GetDFBitmap(record, i);
                    }
                }
                imageData.offset = textureFile.GetOffset(record);
                imageData.scale  = textureFile.GetScale(record);
                imageData.size   = textureFile.GetSize(record);

                // Texture pack support
                int archive = AssetInjection.TextureReplacement.FileNameToArchive(filename);
                if (createTexture && AssetInjection.TextureReplacement.TryImportTexture(archive, record, frame, out imageData.texture))
                {
                    createTexture = false;
                }
                if (createAllFrameTextures && frameCount > 1 && AssetInjection.TextureReplacement.TryImportTexture(archive, record, out imageData.animatedTextures))
                {
                    createAllFrameTextures = false;
                }

                break;

            case ImageTypes.IMG:
                ImgFile imgFile = new ImgFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                imgFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, imgFile.PaletteName));
                dfBitmap         = imgFile.GetDFBitmap();
                imageData.offset = imgFile.ImageOffset;
                imageData.scale  = new DFSize();
                imageData.size   = imgFile.GetSize(0);

                // Texture pack support
                if (createTexture && AssetInjection.TextureReplacement.TryImportImage(filename, false, out imageData.texture))
                {
                    createTexture = false;
                }

                break;

            case ImageTypes.CIF:
            case ImageTypes.RCI:
                CifRciFile cifFile = new CifRciFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                cifFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, cifFile.PaletteName));
                dfBitmap         = cifFile.GetDFBitmap(record, frame);
                imageData.offset = cifFile.GetOffset(record);
                imageData.scale  = new DFSize();
                imageData.size   = cifFile.GetSize(record);

                // Texture pack support
                if (createTexture && AssetInjection.TextureReplacement.TryImportCifRci(filename, record, frame, false, out imageData.texture))
                {
                    createTexture = false;
                }

                break;

            case ImageTypes.CFA:
                CfaFile cfaFile = new CfaFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                cfaFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, cfaFile.PaletteName));

                dfBitmap         = cfaFile.GetDFBitmap(record, frame);
                imageData.offset = new DFPosition(0, 0);
                imageData.scale  = new DFSize();
                imageData.size   = cfaFile.GetSize(record);

                // Texture pack support
                if (createTexture && AssetInjection.TextureReplacement.TryImportCifRci(filename, record, frame, false, out imageData.texture))
                {
                    createTexture = false;
                }

                break;

            case ImageTypes.BSS:
                BssFile bssFile = new BssFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                bssFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, bssFile.PaletteName));

                dfBitmap         = bssFile.GetDFBitmap(record, frame);
                imageData.offset = new DFPosition(0, 0);
                imageData.scale  = new DFSize();
                imageData.size   = bssFile.GetSize(record);

                // Texture pack support
                if (createTexture && AssetInjection.TextureReplacement.TryImportCifRci(filename, record, frame, false, out imageData.texture))
                {
                    createTexture = false;
                }

                break;

            case ImageTypes.GFX:
                GfxFile gfxFile = new GfxFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                gfxFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, gfxFile.PaletteName));

                dfBitmap         = gfxFile.GetDFBitmap(record, frame);
                imageData.offset = new DFPosition(0, 0);
                imageData.scale  = new DFSize();
                imageData.size   = gfxFile.GetSize(record);

                // Texture pack support
                if (createTexture && AssetInjection.TextureReplacement.TryImportCifRci(filename, record, frame, false, out imageData.texture))
                {
                    createTexture = false;
                }

                break;

            default:
                return(new ImageData());
            }

            // Store bitmap
            imageData.dfBitmap = dfBitmap;
            imageData.width    = dfBitmap.Width;
            imageData.height   = dfBitmap.Height;

            // Create Texture2D
            if (createTexture)
            {
                // Get colors array
                Color32[] colors = GetColors(imageData);
                if (colors == null)
                {
                    return(new ImageData());
                }

                // Create new Texture2D
                imageData.texture = GetTexture(colors, imageData.width, imageData.height);
            }

            // Create animated Texture2D frames
            if (createAllFrameTextures && dfBitmapAllFrames != null)
            {
                imageData.animatedTextures = new Texture2D[dfBitmapAllFrames.Length];
                for (int i = 0; i < dfBitmapAllFrames.Length; i++)
                {
                    ImageData curFrame = imageData;
                    curFrame.dfBitmap = dfBitmapAllFrames[i];
                    Color32[] colors = GetColors(curFrame);
                    imageData.animatedTextures[i] = GetTexture(colors, imageData.width, imageData.height);
                }
            }

            return(imageData);
        }
        /// <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);
        }
        /// <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);
        }