public static void AddNatureFlats(
            DaggerfallUnity dfUnity,
            ref DFBlock blockData,
            Transform parent = null,
            ClimateNatureSets climateNature = ClimateNatureSets.SubTropical,
            ClimateSeason climateSeason     = ClimateSeason.Summer)
        {
            int archive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason);

            // Add block scenery
            for (int y = 0; y < 16; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    // Get scenery item
                    DFBlock.RmbGroundScenery scenery = blockData.RmbBlock.FldHeader.GroundData.GroundScenery[x, 15 - y];

                    // Ignore 0 as this appears to be a marker/waypoint of some kind
                    if (scenery.TextureRecord > 0)
                    {
                        // Spawn billboard gameobject
                        GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(archive, scenery.TextureRecord, parent);
                        Vector3    billboardPosition = new Vector3(
                            x * BlocksFile.TileDimension,
                            NatureFlatsOffsetY,
                            y * BlocksFile.TileDimension + BlocksFile.TileDimension) * MeshReader.GlobalScale;

                        // Set transform
                        go.transform.position = billboardPosition;
                    }
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Add nature billboards.
        /// </summary>
        public static void AddNatureFlats(
            ref DFBlock blockData,
            Transform flatsParent,
            DaggerfallBillboardBatch billboardBatch = null,
            ClimateNatureSets climateNature         = ClimateNatureSets.TemperateWoodland,
            ClimateSeason climateSeason             = ClimateSeason.Summer)
        {
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;

            if (!dfUnity.IsReady)
            {
                return;
            }

            for (int y = 0; y < 16; y++)
            {
                for (int x = 0; x < 16; x++)
                {
                    // Get scenery item - ignore indices -1 (empty) and 0 (marker/waypoint of some kind)
                    DFBlock.RmbGroundScenery scenery = blockData.RmbBlock.FldHeader.GroundData.GroundScenery[x, 15 - y];
                    if (scenery.TextureRecord < 1)
                    {
                        continue;
                    }

                    // Calculate position
                    Vector3 billboardPosition = new Vector3(
                        x * BlocksFile.TileDimension,
                        natureFlatsOffsetY,
                        y * BlocksFile.TileDimension + BlocksFile.TileDimension) * MeshReader.GlobalScale;

                    // Get Archive
                    int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason);

                    // Import custom 3d gameobject instead of flat
                    if (MeshReplacement.ImportCustomFlatGameobject(natureArchive, scenery.TextureRecord, billboardPosition, flatsParent) != null)
                    {
                        continue;
                    }

                    // Add billboard to batch or standalone
                    if (billboardBatch != null)
                    {
                        billboardBatch.AddItem(scenery.TextureRecord, billboardPosition);
                    }
                    else
                    {
                        GameObject go = GameObjectHelper.CreateDaggerfallBillboardGameObject(natureArchive, scenery.TextureRecord, flatsParent);
                        go.transform.position = billboardPosition;
                        AlignBillboardToBase(go);
                    }
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Converts an archive index to new climate and season.
        /// Will return same index if climate or season not supported.
        /// </summary>
        /// <param name="archive">Archive index of starting texture.</param>
        /// <param name="climate">Climate base to apply.</param>
        /// <param name="season">Climate season to apply</param>
        /// <returns>Archive index of new texture.</returns>
        public static int ApplyClimate(int archive, int record, ClimateBases climate, ClimateSeason season)
        {
            // Get climate texture info
            ClimateTextureInfo ci = ClimateSwaps.GetClimateTextureInfo(archive);

            // Ignore non-climate textures
            if (ci.textureSet == DFLocation.ClimateTextureSet.None)
            {
                return(archive);
            }

            // Handle missing Swamp textures
            if (climate == ClimateBases.Swamp)
            {
                switch (ci.textureSet)
                {
                case DFLocation.ClimateTextureSet.Interior_TempleInt:
                case DFLocation.ClimateTextureSet.Interior_MarbleFloors:
                    return(archive);
                }
            }

            // Bypass winter swaps in desert climates entirely
            // There are too many bad swaps, and you never see this variant in game
            if (climate == ClimateBases.Desert)
            {
                ci.supportsWinter = false;
            }

            // Handle swamp climate sets with missing winter textures
            if (climate == ClimateBases.Swamp)
            {
                switch (ci.textureSet)
                {
                case DFLocation.ClimateTextureSet.Exterior_Castle:
                case DFLocation.ClimateTextureSet.Exterior_MagesGuild:
                    ci.supportsWinter = false;
                    break;
                }
            }

            // Handle archives with missing winter textures
            if (archive == 82 && record > 1 ||
                archive == 77)
            {
                ci.supportsWinter = false;
            }

            // Flag to suppress climate index
            // Certain textures have a winter variant but are climate-specific
            bool suppressClimateIndex = false;

            switch (archive)
            {
            case 75:
            case 76:
            case 77:
            case 79:
            case 80:
            case 82:
            case 83:
                suppressClimateIndex = true;
                break;
            }

            // Calculate new index
            int climateIndex = 0;

            if (archive < 500 && !suppressClimateIndex)
            {
                climateIndex = (int)FromUnityClimateBase(climate) + (int)ci.textureSet;
                if (season == ClimateSeason.Winter && ci.supportsWinter)
                {
                    climateIndex += (int)DFLocation.ClimateWeather.Winter;
                }
                else if (season == ClimateSeason.Rain && ci.supportsRain)
                {
                    climateIndex += (int)DFLocation.ClimateWeather.Rain;
                }
            }
            else
            {
                climateIndex = archive;
                if (season == ClimateSeason.Winter && ci.supportsWinter)
                {
                    climateIndex += (int)DFLocation.ClimateWeather.Winter;
                }
            }

            return(climateIndex);
        }
Esempio n. 4
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>
        /// <param name="allowImport">Import texture from disk if present.</param>
        /// <returns>GetTextureResults.</returns>
        public GetTextureResults GetTexture2D(
            GetTextureSettings settings,
            SupportedAlphaTextureFormats alphaTextureFormat = SupportedAlphaTextureFormats.RGBA32,
            SupportedNonAlphaTextureFormats nonAlphaFormat  = SupportedNonAlphaTextureFormats.RGB24,
            bool allowImport = true)
        {
            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;
            }

            // 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 = null;

            if (allowImport && TextureReplacement.CustomTextureExist(settings.archive, settings.record, settings.frame))
            {
                // Import albedo texture
                albedoMap = TextureReplacement.LoadCustomTexture(settings.archive, settings.record, settings.frame);
            }
            else
            {
                // Create albedo texture
                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);
            }

            // Set normal texture
            Texture2D normalMap = null;

            if (allowImport && TextureReplacement.CustomNormalExist(settings.archive, settings.record, settings.frame))
            {
                // Always import normal if present on disk
                normalMap = TextureReplacement.LoadCustomNormal(settings.archive, settings.record, settings.frame);
            }
            else if (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 (allowImport && TextureReplacement.CustomEmissionExist(settings.archive, settings.record, settings.frame))
            {
                // Always import emission if present on disk
                emissionMap    = TextureReplacement.LoadCustomEmission(settings.archive, settings.record, settings.frame);
                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[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. 5
0
        /// <summary>
        /// Layout a complete RMB block game object.
        /// Can be used standalone or as part of a city build.
        /// </summary>
        public static GameObject CreateRMBBlockGameObject(
            string blockName,
            bool addGroundPlane          = true,
            DaggerfallRMBBlock cloneFrom = null,
            DaggerfallBillboardBatch natureBillboardBatch  = null,
            DaggerfallBillboardBatch lightsBillboardBatch  = null,
            DaggerfallBillboardBatch animalsBillboardBatch = null,
            TextureAtlasBuilder miscBillboardAtlas         = null,
            DaggerfallBillboardBatch miscBillboardBatch    = null,
            ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland,
            ClimateSeason climateSeason     = ClimateSeason.Summer)
        {
            // Get DaggerfallUnity
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;

            if (!dfUnity.IsReady)
            {
                return(null);
            }

            // Create base object
            DFBlock    blockData;
            GameObject go = RMBLayout.CreateBaseGameObject(blockName, out blockData, cloneFrom);

            // Create flats node
            GameObject flatsNode = new GameObject("Flats");

            flatsNode.transform.parent = go.transform;

            // Create lights node
            GameObject lightsNode = new GameObject("Lights");

            lightsNode.transform.parent = go.transform;

            // If billboard batching is enabled but user has not specified
            // a batch, then make our own auto batch for this block
            bool autoLightsBatch  = false;
            bool autoNatureBatch  = false;
            bool autoAnimalsBatch = false;

            if (dfUnity.Option_BatchBillboards)
            {
                if (natureBillboardBatch == null)
                {
                    autoNatureBatch = true;
                    int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason);
                    natureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, flatsNode.transform);
                }
                if (lightsBillboardBatch == null)
                {
                    autoLightsBatch      = true;
                    lightsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, flatsNode.transform);
                }
                if (animalsBillboardBatch == null)
                {
                    autoAnimalsBatch      = true;
                    animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, flatsNode.transform);
                }
            }

            // Layout light billboards and gameobjects
            RMBLayout.AddLights(ref blockData, flatsNode.transform, lightsNode.transform, lightsBillboardBatch);

            // Layout nature billboards
            RMBLayout.AddNatureFlats(ref blockData, flatsNode.transform, natureBillboardBatch, climateNature, climateSeason);

            // Layout all other flats
            RMBLayout.AddMiscBlockFlats(ref blockData, flatsNode.transform, animalsBillboardBatch, miscBillboardAtlas, miscBillboardBatch);

            // Add ground plane
            if (addGroundPlane)
            {
                RMBLayout.AddGroundPlane(ref blockData, go.transform);
            }

            // Apply auto batches
            if (autoNatureBatch)
            {
                natureBillboardBatch.Apply();
            }
            if (autoLightsBatch)
            {
                lightsBillboardBatch.Apply();
            }
            if (autoAnimalsBatch)
            {
                animalsBillboardBatch.Apply();
            }

            return(go);
        }
Esempio n. 6
0
        /// <summary>
        /// Add subrecord (building) exterior block flats.
        /// </summary>
        public static void AddExteriorBlockFlats(
            ref DFBlock blockData,
            Transform flatsParent,
            Transform lightsParent,
            int mapId,
            int locationIndex,
            ClimateNatureSets climateNature = ClimateNatureSets.TemperateWoodland,
            ClimateSeason climateSeason     = ClimateSeason.Summer)
        {
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;

            if (!dfUnity.IsReady)
            {
                return;
            }

            // Get Nature Archive
            int natureArchive = ClimateSwaps.GetNatureArchive(climateNature, climateSeason);

            foreach (DFBlock.RmbSubRecord subRecord in blockData.RmbBlock.SubRecords)
            {
                Vector3 subRecordPosition = new Vector3(subRecord.XPos, 0, -subRecord.ZPos) * MeshReader.GlobalScale;

                foreach (DFBlock.RmbBlockFlatObjectRecord obj in subRecord.Exterior.BlockFlatObjectRecords)
                {
                    // Don't add building exterior editor flats since they can't be used by any DFU systems
                    int archive = obj.TextureArchive;
                    if (archive == TextureReader.EditorFlatsTextureArchive)
                    {
                        continue;
                    }

                    // Calculate position
                    Vector3 billboardPosition = new Vector3(
                        obj.XPos,
                        -obj.YPos + blockFlatsOffsetY,
                        obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale;

                    billboardPosition += subRecordPosition;

                    // Add natures using correct climate set archive
                    if (archive >= (int)DFLocation.ClimateTextureSet.Nature_RainForest && archive <= (int)DFLocation.ClimateTextureSet.Nature_Mountains_Snow)
                    {
                        archive             = natureArchive;
                        billboardPosition.z = natureFlatsOffsetY;
                    }

                    GameObject go         = MeshReplacement.ImportCustomFlatGameobject(archive, obj.TextureRecord, billboardPosition, flatsParent);
                    bool       isImported = go != null;
                    if (!isImported)
                    {
                        // Add standalone billboard gameobject
                        go = GameObjectHelper.CreateDaggerfallBillboardGameObject(archive, obj.TextureRecord, flatsParent);
                        go.transform.position = billboardPosition;
                        AlignBillboardToBase(go);
                    }

                    // Add animal sound
                    if (archive == TextureReader.AnimalsTextureArchive)
                    {
                        AddAnimalAudioSource(go, obj.TextureRecord);
                    }

                    // If flat record has a non-zero faction id, then it's an exterior NPC
                    if (obj.FactionID != 0)
                    {
                        // Add RMB data to billboard
                        Billboard dfBillboard = go.GetComponent <Billboard>();
                        if (dfBillboard != null)
                        {
                            dfBillboard.SetRMBPeopleData(obj.FactionID, obj.Flags, obj.Position);
                        }

                        // Add StaticNPC behaviour
                        StaticNPC npc = go.AddComponent <StaticNPC>();
                        npc.SetLayoutData(obj, mapId, locationIndex);
                    }

                    // If this is a light flat, import light prefab
                    if (archive == TextureReader.LightsTextureArchive && !isImported)
                    {
                        if (dfUnity.Option_CityLightPrefab == null)
                        {
                            return;
                        }

                        Vector2 size     = dfUnity.MeshReader.GetScaledBillboardSize(210, obj.TextureRecord);
                        Vector3 position = new Vector3(
                            obj.XPos,
                            -obj.YPos + size.y,
                            obj.ZPos + BlocksFile.RMBDimension) * MeshReader.GlobalScale;
                        position += subRecordPosition;

                        GameObjectHelper.InstantiatePrefab(dfUnity.Option_CityLightPrefab.gameObject, string.Empty, lightsParent, position);
                    }
                }
            }
        }