/// <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); }
/// <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 { 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); }
/// <summary> /// Gets Unity Material from Daggerfall texture with more options. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="frame">Frame index.</param> /// <param name="rectOut">Receives UV rect for texture inside border.</param> /// <param name="borderSize">Number of pixels internal border around each texture.</param> /// <param name="dilate">Blend texture into surrounding empty pixels.</param> /// <param name="isBillboard">Set true when creating atlas material for simple billboards.</param> /// <returns>Material or null.</returns> public Material GetMaterial( int archive, int record, int frame, int alphaIndex, out Rect rectOut, int borderSize = 0, bool dilate = false, bool isBillboard = false) { // Ready check if (!IsReady) { rectOut = new Rect(); return(null); } // Try to retrieve from cache int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; rectOut = cm.singleRect; return(cm.material); } // Create new texture settings GetTextureSettings settings = TextureReader.CreateTextureSettings(archive, record, frame, alphaIndex, borderSize, dilate); settings.autoEmissionForWindows = true; settings.sharpen = Sharpen; if (GenerateNormals) { settings.createNormalMap = true; settings.normalStrength = NormalTextureStrength; } // Set emissive for self-illuminated textures if (textureReader.IsEmissive(archive, record)) { settings.createEmissionMap = true; settings.emissionIndex = -1; } // Get texture GetTextureResults results = textureReader.GetTexture2D(settings, AlphaTextureFormat, NonAlphaTextureFormat); rectOut = results.singleRect; // Create material Material material; if (isBillboard) { material = CreateStandardMaterial(CustomBlendMode.Cutout); } else { material = CreateStandardMaterial(); } // Setup material material.name = FormatName(archive, record); material.mainTexture = results.albedoMap; material.mainTexture.filterMode = MainFilterMode; // Setup normal map bool importedNormals = TextureReplacement.CustomNormalExist(settings.archive, settings.record, settings.frame); if ((GenerateNormals || importedNormals) && results.normalMap != null) { results.normalMap.filterMode = MainFilterMode; material.SetTexture("_BumpMap", results.normalMap); material.EnableKeyword("_NORMALMAP"); } // Setup emission map if (results.isEmissive && !results.isWindow && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture("_EmissionMap", results.emissionMap); material.SetColor("_EmissionColor", Color.white); material.EnableKeyword("_EMISSION"); } else if (results.isEmissive && results.isWindow && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture("_EmissionMap", results.emissionMap); material.SetColor("_EmissionColor", DayWindowColor * DayWindowIntensity); material.EnableKeyword("_EMISSION"); } // Import additional custom components of material TextureReplacement.CustomizeMaterial(archive, record, frame, material); // Setup cached material DFSize size = results.textureFile.GetSize(record); DFSize scale = results.textureFile.GetScale(record); DFPosition offset = results.textureFile.GetOffset(record); Vector2[] recordSizes = new Vector2[1] { new Vector2(size.Width, size.Height) }; Vector2[] recordScales = new Vector2[1] { new Vector2(scale.Width, scale.Height) }; Vector2[] recordOffsets = new Vector2[1] { new Vector2(offset.X, offset.Y) }; CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = 0, albedoMap = results.albedoMap, normalMap = results.normalMap, emissionMap = results.emissionMap, singleRect = rectOut, material = material, filterMode = MainFilterMode, isWindow = results.isWindow, recordSizes = recordSizes, recordScales = recordScales, recordOffsets = recordOffsets, singleFrameCount = results.textureFile.GetFrameCount(record), }; materialDict.Add(key, newcm); return(material); }