/// <summary> /// Get a new Material based on climate. /// </summary> /// <param name="key">Material key.</param> /// <param name="climate">New climate base.</param> /// <param name="season">New season.</param> /// <param name="windowStyle">New window style.</param> /// <returns>New material.</returns> public Material ChangeClimate(int key, ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { // Ready check if (!IsReady) { return(null); } // Reverse key and apply climate int archive, record, frame; ReverseTextureKey(key, out archive, out record, out frame); archive = ClimateSwaps.ApplyClimate(archive, record, climate, season); // Get new material Material material; CachedMaterial cm = GetMaterialFromCache(archive, record, out material); // Handle windows if (cm.isWindow) { ChangeWindowEmissionColor(material, windowStyle); } return(material); }
/// <summary> /// Gets Unity Material from Daggerfall terrain tilemap texture. /// </summary> /// <param name="archive">Archive index.</param> /// <returns>Material or null.</returns> public Material GetTerrainTilesetMaterial(int archive) { // Ready check if (!IsReady) { return(null); } // Return from cache if present int key = MakeTextureKey((short)archive, (byte)0, (byte)0, TileMapKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = GetMaterialFromCache(key); if (cm.filterMode == MainFilterMode) { // Properties are the same return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } //// TODO: Attempt to load prebuilt atlas asset, otherwise create one in memory //Texture2D texture = TerrainAtlasBuilder.LoadTerrainAtlasTextureResource(archive, CreateTextureAssetResourcesPath); //if (texture == null) // Generate atlas // Not currently generating normals as very slow on such a large texture // and results are not very noticeable GetTextureResults results = textureReader.GetTerrainTilesetTexture(archive); results.albedoMap.filterMode = MainFilterMode; Shader shader = Shader.Find(_DaggerfallTilemapShaderName); Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [Tilemap]", archive); material.SetTexture("_TileAtlasTex", results.albedoMap); CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = TileMapKeyGroup, material = material, filterMode = MainFilterMode, }; materialDict.Add(key, newcm); return(material); }
/// <summary> /// Gets CachedMaterial properties for a billboard with custom material. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="cachedMaterialOut">CachedMaterial out</param> /// <returns>True if CachedMaterial found.</returns> public bool GetCachedMaterialCustomBillboard(int archive, int record, int frame, out CachedMaterial cachedMaterialOut) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame, CustomBillboardKeyGroup); if (materialDict.ContainsKey(key)) { cachedMaterialOut = GetMaterialFromCache(key); return(true); } cachedMaterialOut = new CachedMaterial(); return(false); }
/// <summary> /// Gets CachedMaterial properties for an atlased material. /// Atlas material will not be loaded automatically if not found in cache. /// </summary> /// <param name="archive">Atlas archive index.</param> /// <param name="cachedMaterialOut">CachedMaterial out</param> /// <returns>True if CachedMaterial found.</returns> public bool GetCachedMaterialAtlas(int archive, out CachedMaterial cachedMaterialOut) { int key = MakeTextureKey((short)archive, (byte)0, (byte)0, AtlasKeyGroup); if (materialDict.ContainsKey(key)) { cachedMaterialOut = GetMaterialFromCache(key); return(true); } cachedMaterialOut = new CachedMaterial(); return(false); }
/// <summary> /// Gets Unity Material from Daggerfall terrain tilemap texture. /// </summary> /// <param name="archive">Archive index.</param> /// <returns>Material or null.</returns> public Material GetTerrainTilesetMaterial(int archive) { // Ready check if (!IsReady) { return(null); } // Return from cache if present int key = MakeTextureKey((short)archive, (byte)0, (byte)0, TileMapKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; if (cm.filterMode == MainFilterMode) { // Properties are the same return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } //// TODO: Attempt to load prebuilt atlas asset, otherwise create one in memory //Texture2D texture = TerrainAtlasBuilder.LoadTerrainAtlasTextureResource(archive, CreateTextureAssetResourcesPath); //if (texture == null) Texture2D texture = textureReader.GetTerrainTilesetTexture(archive); texture.filterMode = MainFilterMode; Shader shader = Shader.Find(_DaggerfallTilemapShaderName); Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [Tilemap]", archive); material.mainTexture = texture; CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = TileMapKeyGroup, material = material, filterMode = MainFilterMode, }; materialDict.Add(key, newcm); return(material); }
/// <summary> /// Sets CachedMaterial properties. /// existing Material will be updated in cache with cachedMaterialIn. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="frame">Frame index.</param> /// <param name="cachedMaterialIn">the CachedMaterial used to update the cache.</param> /// <returns>True if CachedMaterial was found and updated successfully.</returns> public bool SetCachedMaterial(int archive, int record, int frame, CachedMaterial cachedMaterialIn) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { materialDict[key] = cachedMaterialIn; return(true); } else { return(false); } }
private CachedMaterial GetMaterialFromCache(int key) { CachedMaterial cachedMaterial = materialDict[key]; // Update timestamp of last access, but only if difference is // significant to limit the number of reassignment to dictionary. float time = Time.realtimeSinceStartup; if (time - cachedMaterial.timeStamp > 59) { cachedMaterial.timeStamp = time; materialDict[key] = cachedMaterial; } return(cachedMaterial); }
/// <summary> /// Gets CachedMaterial properties. /// Material will be loaded into cache if not present already. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="frame">Frame index.</param> /// <param name="cachedMaterialOut">CachedMaterial out.</param> /// <param name="alphaIndex">Alpha index used if material needs to be loaded.</param> /// <returns>True if CachedMaterial found or loaded successfully.</returns> public bool GetCachedMaterial(int archive, int record, int frame, out CachedMaterial cachedMaterialOut, int alphaIndex = -1) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { cachedMaterialOut = GetMaterialFromCache(key); return(true); } else { // Not in cache - try to load material if (GetMaterial(archive, record, frame, alphaIndex) == null) { cachedMaterialOut = new CachedMaterial(); return(false); } } cachedMaterialOut = materialDict[key]; return(true); }
/// <summary> /// Gets Unity Material atlas from Daggerfall texture archive. /// </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 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 pixels to shrink UV rect.</param> /// <param name="copyToOppositeBorder">Copy texture edges to opposite border. Requires border, will overwrite dilate.</param> /// <param name="isBillboard">Set true when creating atlas material for simple billboards.</param> /// <returns>Material or null.</returns> public Material GetMaterialAtlas( int archive, int alphaIndex, int padding, int maxAtlasSize, out Rect[] rectsOut, out RecordIndex[] indicesOut, int border = 0, bool dilate = false, int shrinkUVs = 0, bool copyToOppositeBorder = false, bool isBillboard = false) { // Ready check if (!IsReady) { rectsOut = null; indicesOut = null; return(null); } int key = MakeTextureKey((short)archive, (byte)0, (byte)0, AtlasKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = GetMaterialFromCache(key); if (cm.filterMode == MainFilterMode) { // Properties are the same rectsOut = cm.atlasRects; indicesOut = cm.atlasIndices; return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } // Create material Material material; if (isBillboard) { material = CreateStandardMaterial(CustomBlendMode.Cutout); } else { material = CreateStandardMaterial(); } // Create settings GetTextureSettings settings = TextureReader.CreateTextureSettings(archive, 0, 0, alphaIndex, border, dilate); settings.createNormalMap = GenerateNormals; settings.autoEmission = true; settings.atlasShrinkUVs = shrinkUVs; settings.atlasPadding = padding; settings.atlasMaxSize = maxAtlasSize; settings.copyToOppositeBorder = copyToOppositeBorder; // Setup material material.name = string.Format("TEXTURE.{0:000} [Atlas]", archive); GetTextureResults results = textureReader.GetTexture2DAtlas(settings, AlphaTextureFormat); material.mainTexture = results.albedoMap; material.mainTexture.filterMode = MainFilterMode; // Setup normal map if (GenerateNormals && results.normalMap != null) { results.normalMap.filterMode = MainFilterMode; material.SetTexture(Uniforms.BumpMap, results.normalMap); material.EnableKeyword(KeyWords.NormalMap); } // Setup emission map if (results.isEmissive && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture(Uniforms.EmissionMap, results.emissionMap); material.SetColor(Uniforms.EmissionColor, Color.white); material.EnableKeyword(KeyWords.Emission); } // TEMP: Bridging between legacy material out params and GetTextureResults for now Vector2[] sizesOut, scalesOut, offsetsOut; sizesOut = results.atlasSizes.ToArray(); scalesOut = results.atlasScales.ToArray(); offsetsOut = results.atlasOffsets.ToArray(); rectsOut = results.atlasRects.ToArray(); indicesOut = results.atlasIndices.ToArray(); // Setup cached material CachedMaterial newcm = new CachedMaterial(); newcm.key = key; newcm.keyGroup = AtlasKeyGroup; newcm.atlasRects = rectsOut; newcm.atlasIndices = indicesOut; newcm.material = material; newcm.filterMode = MainFilterMode; newcm.recordSizes = sizesOut; newcm.recordScales = scalesOut; newcm.recordOffsets = offsetsOut; newcm.atlasFrameCounts = results.atlasFrameCounts.ToArray(); materialDict.Add(key, newcm); return(material); }
/// <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 = GetMaterialFromCache(key); rectOut = cm.singleRect; return(cm.material); } Material material; GetTextureResults results; // Try to import a material from mods, otherwise create a standard material // and import textures from Daggerfall files and loose files. if (!TextureReplacement.TextureExistsAmongLooseFiles(archive, record, frame) && TextureReplacement.TryImportMaterial(archive, record, frame, out material)) { results = TextureReplacement.MakeResults(material, archive, record); TextureReplacement.AssignFiltermode(material); } else { // 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 results = textureReader.GetTexture2D(settings, AlphaTextureFormat, TextureImport.LooseFiles); // Create 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.TextureExistsAmongLooseFiles(settings.archive, settings.record, settings.frame, TextureMap.Normal); if ((GenerateNormals || importedNormals) && results.normalMap != null) { results.normalMap.filterMode = MainFilterMode; material.SetTexture(Uniforms.BumpMap, results.normalMap); material.EnableKeyword(KeyWords.NormalMap); } // Setup emission map if (results.isEmissive && !results.isWindow && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture(Uniforms.EmissionMap, results.emissionMap); material.SetColor(Uniforms.EmissionColor, Color.white); material.EnableKeyword(KeyWords.Emission); } else if (results.isEmissive && results.isWindow && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture(Uniforms.EmissionMap, results.emissionMap); material.SetColor(Uniforms.EmissionColor, DayWindowColor * DayWindowIntensity); material.EnableKeyword(KeyWords.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 = results.singleRect, material = material, filterMode = MainFilterMode, isWindow = results.isWindow, recordSizes = recordSizes, recordScales = recordScales, recordOffsets = recordOffsets, singleFrameCount = results.textureFile.GetFrameCount(record), framesPerSecond = archive == FireWallsArchive ? 5 : 0, // Slow down fire walls }; materialDict.Add(key, newcm); return(material); }
/// <summary> /// Gets Unity Material from Daggerfall terrain tilemap texture. /// </summary> /// <param name="archive">Archive index.</param> /// <returns>Material or null.</returns> public Material GetTerrainTilesetMaterial(int archive) { // Ready check if (!IsReady) return null; // Return from cache if present int key = MakeTextureKey((short)archive, (byte)0, (byte)0, TileMapKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; if (cm.filterMode == MainFilterMode) { // Properties are the same return cm.material; } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } //// TODO: Attempt to load prebuilt atlas asset, otherwise create one in memory //Texture2D texture = TerrainAtlasBuilder.LoadTerrainAtlasTextureResource(archive, CreateTextureAssetResourcesPath); //if (texture == null) // Generate atlas // Not currently generating normals as very slow on such a large texture // and results are not very noticeable GetTextureResults results = textureReader.GetTerrainTilesetTexture(archive); results.albedoMap.filterMode = MainFilterMode; Shader shader = Shader.Find(_DaggerfallTilemapShaderName); Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [Tilemap]", archive); material.SetTexture("_TileAtlasTex", results.albedoMap); CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = TileMapKeyGroup, material = material, filterMode = MainFilterMode, }; materialDict.Add(key, newcm); return material; }
/// <summary> /// Gets Unity Material atlas from Daggerfall texture archive. /// </summary> /// <param name="archive">Archive index to create atlas from.</param> /// <param name="alphaIndex">Index to receive transparent alpha.</param> /// <param name="rectsOut">Array of rects, one for each record sub-texture and frame.</param> /// <param name="padding">Number of pixels 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 pixels to shrink UV rect.</param> /// <param name="copyToOppositeBorder">Copy texture edges to opposite border. Requires border, will overwrite dilate.</param> /// <param name="shader">Shader for material. If null, DefaultShaderName will be applied.</param> /// <returns>Material or null.</returns> public Material GetMaterialAtlas( int archive, int alphaIndex, int padding, int maxAtlasSize, out Rect[] rectsOut, out RecordIndex[] indicesOut, int border = 0, bool dilate = false, int shrinkUVs = 0, bool copyToOppositeBorder = false, Shader shader = null) { // Ready check if (!IsReady) { rectsOut = null; indicesOut = null; return(null); } int key = MakeTextureKey((short)archive, (byte)0, (byte)0, AtlasKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; if (cm.filterMode == MainFilterMode) { // Properties are the same rectsOut = cm.atlasRects; indicesOut = cm.indices; return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } if (shader == null) { shader = Shader.Find(DefaultShaderName); } Vector2[] sizesOut, scalesOut, offsetsOut; Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [Atlas]", archive); Texture2D texture = textureReader.GetTexture2DAtlas( archive, alphaIndex, padding, maxAtlasSize, out rectsOut, out indicesOut, out sizesOut, out scalesOut, out offsetsOut, border, dilate, shrinkUVs, copyToOppositeBorder); material.mainTexture = texture; material.mainTexture.filterMode = MainFilterMode; CachedMaterial newcm = new CachedMaterial(); newcm.key = key; newcm.keyGroup = AtlasKeyGroup; newcm.atlasRects = rectsOut; newcm.indices = indicesOut; newcm.material = material; newcm.filterMode = MainFilterMode; newcm.recordSizes = sizesOut; newcm.recordScales = scalesOut; newcm.recordOffsets = offsetsOut; materialDict.Add(key, newcm); return(material); }
/// <summary> /// Gets CachedMaterial properties. /// Material will be loaded into cache if not present already. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="frame">Frame index.</param> /// <param name="cachedMaterialOut">CachedMaterial out.</param> /// <param name="alphaIndex">Alpha index used if material needs to be loaded.</param> /// <returns>True if CachedMaterial found or loaded successfully.</returns> public bool GetCachedMaterial(int archive, int record, int frame, out CachedMaterial cachedMaterialOut, int alphaIndex = -1) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { cachedMaterialOut = materialDict[key]; return true; } else { // Not in cache - try to load material if (GetMaterial(archive, record, frame, alphaIndex) == null) { cachedMaterialOut = new CachedMaterial(); return false; } } cachedMaterialOut = materialDict[key]; return true; }
/// <summary> /// Gets Unity Mesh from previously combined model data. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleon for loading content.</param> /// <param name="combiner">ModelCombiner to build from.</param> /// <param name="cachedMaterialsOut">Array of cached materials in order of submesh.</param> /// <param name="textureKeysOut">Array of original texture keys in order of submesh.</param> /// <param name="hasAnimationsOut">True if one or more materials have animations.</param> /// <param name="solveTangents">Solve tangents for this mesh.</param> /// <param name="lightmapUVs">Add secondary lightmap UVs to this mesh.</param> /// <returns>Mesh object or null.</returns> public Mesh GetCombinedMesh( DaggerfallUnity dfUnity, ModelCombiner combiner, out CachedMaterial[] cachedMaterialsOut, out int[] textureKeysOut, out bool hasAnimationsOut, bool solveTangents = false, bool lightmapUVs = false) { cachedMaterialsOut = null; hasAnimationsOut = false; textureKeysOut = null; // Ready check if (!IsReady) { return(null); } // Get combined model ModelCombiner.CombinedModel combinedModel; if (!combiner.GetCombinedModel(out combinedModel)) { return(null); } // Load materials cachedMaterialsOut = new CachedMaterial[combinedModel.SubMeshes.Length]; textureKeysOut = new int[combinedModel.SubMeshes.Length]; for (int i = 0; i < combinedModel.SubMeshes.Length; i++) { int archive = combinedModel.SubMeshes[i].TextureArchive; int record = combinedModel.SubMeshes[i].TextureRecord; textureKeysOut[i] = MaterialReader.MakeTextureKey((short)archive, (byte)record, (byte)0); // Add material to array CachedMaterial cachedMaterial; dfUnity.MaterialReader.GetCachedMaterial(archive, record, 0, out cachedMaterial); cachedMaterialsOut[i] = cachedMaterial; // Set animation flag if (cachedMaterial.singleFrameCount > 1 && !hasAnimationsOut) { hasAnimationsOut = true; } } // Create mesh Mesh mesh = new Mesh(); mesh.name = "CombinedMesh"; mesh.vertices = combinedModel.Vertices; mesh.normals = combinedModel.Normals; mesh.uv = combinedModel.UVs; mesh.subMeshCount = combinedModel.SubMeshes.Length; // Set submesh triangles for (int s = 0; s < mesh.subMeshCount; s++) { var sub = combinedModel.SubMeshes[s]; int[] triangles = new int[sub.PrimitiveCount * 3]; for (int t = 0; t < sub.PrimitiveCount * 3; t++) { triangles[t] = combinedModel.Indices[sub.StartIndex + t]; } mesh.SetTriangles(triangles, s); } // Finalise mesh if (solveTangents) { TangentSolver(mesh); } if (lightmapUVs) { AddLightmapUVs(mesh); } mesh.RecalculateBounds(); return(mesh); }
/// <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> /// <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) { // 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; // Setup material Material material = CreateStandardMaterial(); material.name = FormatName(archive, record); material.mainTexture = results.albedoMap; material.mainTexture.filterMode = MainFilterMode; // Setup normal map if (GenerateNormals && 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"); } // 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); }
/// <summary> /// Gets Unity Mesh from Daggerfall model. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleon for loading content.</param> /// <param name="modelID">Daggerfall model ID to load..</param> /// <param name="cachedMaterialsOut">Array of cached materials in order of submesh.</param> /// <param name="textureKeysOut">Array of original texture keys in order of submesh.</param> /// <param name="hasAnimationsOut">True if one or more materials have animations.</param> /// <param name="solveTangents">Solve tangents for this mesh.</param> /// <param name="lightmapUVs">Add secondary lightmap UVs to this mesh.</param> /// <returns>Mesh object or null.</returns> public Mesh GetMesh( DaggerfallUnity dfUnity, uint modelID, out CachedMaterial[] cachedMaterialsOut, out int[] textureKeysOut, out bool hasAnimationsOut, bool solveTangents = false, bool lightmapUVs = false) { cachedMaterialsOut = null; hasAnimationsOut = false; textureKeysOut = null; // Ready check if (!IsReady) { return(null); } // Get model data ModelData model; if (!GetModelData(modelID, out model)) { DaggerfallUnity.LogMessage(string.Format("Unknown ModelID {0}.", modelID.ToString()), true); return(null); } // Load materials cachedMaterialsOut = new CachedMaterial[model.SubMeshes.Length]; textureKeysOut = new int[model.SubMeshes.Length]; for (int i = 0; i < model.SubMeshes.Length; i++) { int archive = model.DFMesh.SubMeshes[i].TextureArchive; int record = model.DFMesh.SubMeshes[i].TextureRecord; textureKeysOut[i] = MaterialReader.MakeTextureKey((short)archive, (byte)record, (byte)0); // Add material to array CachedMaterial cachedMaterial; dfUnity.MaterialReader.GetCachedMaterial(archive, record, 0, out cachedMaterial); cachedMaterialsOut[i] = cachedMaterial; // Set animation flag if (cachedMaterial.singleFrameCount > 1 && !hasAnimationsOut) { hasAnimationsOut = true; } } // Create mesh Mesh mesh = new Mesh(); mesh.name = modelID.ToString(); mesh.vertices = model.Vertices; mesh.normals = model.Normals; mesh.uv = model.UVs; mesh.subMeshCount = model.SubMeshes.Length; // Set submesh triangles for (int s = 0; s < mesh.subMeshCount; s++) { var sub = model.SubMeshes[s]; int[] triangles = new int[sub.PrimitiveCount * 3]; for (int t = 0; t < sub.PrimitiveCount * 3; t++) { triangles[t] = model.Indices[sub.StartIndex + t]; } mesh.SetTriangles(triangles, s); } // Finalise mesh if (solveTangents) { TangentSolver(mesh); } if (lightmapUVs) { AddLightmapUVs(mesh); } mesh.RecalculateBounds(); mesh.Optimize(); return(mesh); }
/// <summary> /// Gets Unity Material from Daggerfall terrain using texture arrays. /// </summary> /// <param name="archive">Archive index.</param> /// <returns>Material or null.</returns> public Material GetTerrainTextureArrayMaterial(int archive) { // Ready check if (!IsReady) { return(null); } // Return from cache if present int key = MakeTextureKey((short)archive, (byte)0, (byte)0, TileMapKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = GetMaterialFromCache(key); if (cm.filterMode == MainFilterMode) { // Properties are the same return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } // Generate texture array Texture2DArray textureArrayTerrainTiles = textureReader.GetTerrainTextureArray(archive, TextureMap.Albedo); Texture2DArray textureArrayTerrainTilesNormalMap = textureReader.GetTerrainTextureArray(archive, TextureMap.Normal); Texture2DArray textureArrayTerrainTilesParallaxMap = textureReader.GetTerrainTextureArray(archive, TextureMap.Height); Texture2DArray textureArrayTerrainTilesMetallicGloss = textureReader.GetTerrainTextureArray(archive, TextureMap.MetallicGloss); textureArrayTerrainTiles.filterMode = MainFilterMode; Shader shader = Shader.Find(_DaggerfallTilemapTextureArrayShaderName); Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [TilemapTextureArray]", archive); material.SetTexture(TileTexArrUniforms.TileTexArr, textureArrayTerrainTiles); if (textureArrayTerrainTilesNormalMap != null) { // If normal map texture array was loaded successfully enable _NORMALMAP in shader and set texture material.SetTexture(TileTexArrUniforms.TileNormalMapTexArr, textureArrayTerrainTilesNormalMap); material.EnableKeyword(KeyWords.NormalMap); //textureArrayTerrainTilesNormalMap.filterMode = MainFilterMode; } if (textureArrayTerrainTilesParallaxMap != null) { // If parallax map texture array was loaded successfully enable _PARALLAXMAP in shader and set texture material.SetTexture(TileTexArrUniforms.TileParallaxMapTexArr, textureArrayTerrainTilesParallaxMap); material.EnableKeyword(KeyWords.HeightMap); //textureArrayTerrainTilesParallaxMap.filterMode = MainFilterMode; } if (textureArrayTerrainTilesMetallicGloss != null) { // If metallic gloss map texture array was loaded successfully enable _METALLICGLOSSMAP in shader and set texture material.SetTexture(TileTexArrUniforms.TileMetallicGlossMapTexArr, textureArrayTerrainTilesMetallicGloss); material.EnableKeyword(KeyWords.MetallicGlossMap); material.SetFloat(Uniforms.Smoothness, 0.35f); //textureArrayTerrainTilesMetallicGloss.filterMode = MainFilterMode; } CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = TileMapKeyGroup, material = material, filterMode = MainFilterMode, }; materialDict.Add(key, newcm); return(material); }
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); }
/// <summary> /// Sets CachedMaterial properties. /// existing Material will be updated in cache with cachedMaterialIn. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> /// <param name="frame">Frame index.</param> /// <param name="cachedMaterialIn">the CachedMaterial used to update the cache.</param> /// <returns>True if CachedMaterial was found and updated successfully.</returns> public bool SetCachedMaterial(int archive, int record, int frame, CachedMaterial cachedMaterialIn) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { materialDict[key] = cachedMaterialIn; return true; } else { return false; } }
/// <summary> /// Gets Unity Material from Daggerfall terrain using texture arrays. /// </summary> /// <param name="archive">Archive index.</param> /// <returns>Material or null.</returns> public Material GetTerrainTextureArrayMaterial(int archive) { // Ready check if (!IsReady) { return(null); } // Return from cache if present int key = MakeTextureKey((short)archive, (byte)0, (byte)0, TileMapKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = GetMaterialFromCache(key); if (cm.filterMode == MainFilterMode) { // Properties are the same return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } // Generate texture array Texture2DArray textureArrayTerrainTiles = textureReader.GetTerrainAlbedoTextureArray(archive); Texture2DArray textureArrayTerrainTilesNormalMap = textureReader.GetTerrainNormalMapTextureArray(archive); Texture2DArray textureArrayTerrainTilesMetallicGloss = textureReader.GetTerrainMetallicGlossMapTextureArray(archive); textureArrayTerrainTiles.filterMode = MainFilterMode; Shader shader = Shader.Find(_DaggerfallTilemapTextureArrayShaderName); Material material = new Material(shader); material.name = string.Format("TEXTURE.{0:000} [TilemapTextureArray]", archive); material.SetTexture(TileTexArrUniforms.TileTexArr, textureArrayTerrainTiles); if (textureArrayTerrainTilesNormalMap != null) { // if normal map texture array was loaded successfully enable normalmap in shader and set texture material.SetTexture(TileTexArrUniforms.TileNormalMapTexArr, textureArrayTerrainTilesNormalMap); material.EnableKeyword(KeyWords.NormalMap); } if (textureArrayTerrainTilesMetallicGloss != null) { // if metallic gloss map texture array was loaded successfully set texture (should always contain a valid texture array - since it defaults to 1x1 textures) material.SetTexture(TileTexArrUniforms.TileMetallicGlossMapTexArr, textureArrayTerrainTilesMetallicGloss); } CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = TileMapKeyGroup, material = material, filterMode = MainFilterMode, }; materialDict.Add(key, newcm); return(material); }
/// <summary> /// Sets CachedMaterial properties for a billboard with custom material. /// </summary> /// <param name="archive">Archive index.</param> /// <param name="record">Record index.</param> public void SetCachedMaterialCustomBillboard(int archive, int record, int frame, CachedMaterial cachedMaterialIn) { int key = MakeTextureKey((short)archive, (byte)record, (byte)frame, CustomBillboardKeyGroup); cachedMaterialIn.key = key; cachedMaterialIn.keyGroup = CustomBillboardKeyGroup; materialDict.Add(key, cachedMaterialIn); }
/// <summary> /// Gets Unity Mesh from Daggerfall model. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleon for loading content.</param> /// <param name="modelID">Daggerfall model ID to load..</param> /// <param name="cachedMaterialsOut">Array of cached materials in order of submesh.</param> /// <param name="textureKeysOut">Array of original texture keys in order of submesh.</param> /// <param name="hasAnimationsOut">True if one or more materials have animations.</param> /// <param name="solveTangents">Solve tangents for this mesh.</param> /// <param name="lightmapUVs">Add secondary lightmap UVs to this mesh.</param> /// <returns>Mesh object or null.</returns> public Mesh GetMesh( DaggerfallUnity dfUnity, uint modelID, out CachedMaterial[] cachedMaterialsOut, out int[] textureKeysOut, out bool hasAnimationsOut, bool solveTangents = false, bool lightmapUVs = false) { cachedMaterialsOut = null; hasAnimationsOut = false; textureKeysOut = null; // Ready check if (!IsReady) return null; // Get model data ModelData model; if (!GetModelData(modelID, out model)) { DaggerfallUnity.LogMessage(string.Format("Unknown ModelID {0}.", modelID.ToString()), true); return null; } // Load materials cachedMaterialsOut = new CachedMaterial[model.SubMeshes.Length]; textureKeysOut = new int[model.SubMeshes.Length]; for (int i = 0; i < model.SubMeshes.Length; i++) { int archive = model.DFMesh.SubMeshes[i].TextureArchive; int record = model.DFMesh.SubMeshes[i].TextureRecord; textureKeysOut[i] = MaterialReader.MakeTextureKey((short)archive, (byte)record, (byte)0); // Add material to array CachedMaterial cachedMaterial; dfUnity.MaterialReader.GetCachedMaterial(archive, record, 0, out cachedMaterial); cachedMaterialsOut[i] = cachedMaterial; // Set animation flag if (cachedMaterial.singleFrameCount > 1 && !hasAnimationsOut) hasAnimationsOut = true; } // Create mesh Mesh mesh = new Mesh(); mesh.name = modelID.ToString(); mesh.vertices = model.Vertices; mesh.normals = model.Normals; mesh.uv = model.UVs; mesh.subMeshCount = model.SubMeshes.Length; // Set submesh triangles for (int s = 0; s < mesh.subMeshCount; s++) { var sub = model.SubMeshes[s]; int[] triangles = new int[sub.PrimitiveCount * 3]; for (int t = 0; t < sub.PrimitiveCount * 3; t++) { triangles[t] = model.Indices[sub.StartIndex + t]; } mesh.SetTriangles(triangles, s); } // Finalise mesh if (solveTangents) TangentSolver(mesh); if (lightmapUVs) AddLightmapUVs(mesh); mesh.RecalculateBounds(); mesh.Optimize(); return mesh; }
/// <summary> /// Gets Unity Material atlas from Daggerfall texture archive. /// </summary> /// <param name="archive">Archive index to create atlas from.</param> /// <param name="alphaIndex">Index to receive transparent alpha.</param> /// <param name="rectsOut">Array of rects, one for each record sub-texture and frame.</param> /// <param name="padding">Number of pixels 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 pixels to shrink UV rect.</param> /// <param name="copyToOppositeBorder">Copy texture edges to opposite border. Requires border, will overwrite dilate.</param> /// <param name="shader">Shader for material. If null, DefaultShaderName will be applied.</param> /// <returns>Material or null.</returns> public Material GetMaterialAtlas( int archive, int alphaIndex, int padding, int maxAtlasSize, out Rect[] rectsOut, out RecordIndex[] indicesOut, int border = 0, bool dilate = false, int shrinkUVs = 0, bool copyToOppositeBorder = false) { // Ready check if (!IsReady) { rectsOut = null; indicesOut = null; return null; } int key = MakeTextureKey((short)archive, (byte)0, (byte)0, AtlasKeyGroup); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; if (cm.filterMode == MainFilterMode) { // Properties are the same rectsOut = cm.atlasRects; indicesOut = cm.atlasIndices; return cm.material; } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } // Create material Material material = CreateStandardMaterial(); // Create settings GetTextureSettings settings = TextureReader.CreateTextureSettings(archive, 0, 0, alphaIndex, border, dilate); settings.createNormalMap = GenerateNormals; settings.autoEmission = true; settings.atlasShrinkUVs = shrinkUVs; settings.atlasPadding = padding; settings.atlasMaxSize = maxAtlasSize; settings.copyToOppositeBorder = copyToOppositeBorder; // Setup material material.name = string.Format("TEXTURE.{0:000} [Atlas]", archive); GetTextureResults results = textureReader.GetTexture2DAtlas(settings, AlphaTextureFormat, NonAlphaTextureFormat); material.mainTexture = results.albedoMap; material.mainTexture.filterMode = MainFilterMode; // Setup normal map if (GenerateNormals && results.normalMap != null) { results.normalMap.filterMode = MainFilterMode; material.SetTexture("_BumpMap", results.normalMap); material.EnableKeyword("_NORMALMAP"); } // Setup emission map if (results.isEmissive && results.emissionMap != null) { results.emissionMap.filterMode = MainFilterMode; material.SetTexture("_EmissionMap", results.emissionMap); material.SetColor("_EmissionColor", Color.white); material.EnableKeyword("_EMISSION"); } // TEMP: Bridging between legacy material out params and GetTextureResults for now Vector2[] sizesOut, scalesOut, offsetsOut; sizesOut = results.atlasSizes.ToArray(); scalesOut = results.atlasScales.ToArray(); offsetsOut = results.atlasOffsets.ToArray(); rectsOut = results.atlasRects.ToArray(); indicesOut = results.atlasIndices.ToArray(); // Setup cached material CachedMaterial newcm = new CachedMaterial(); newcm.key = key; newcm.keyGroup = AtlasKeyGroup; newcm.atlasRects = rectsOut; newcm.atlasIndices = indicesOut; newcm.material = material; newcm.filterMode = MainFilterMode; newcm.recordSizes = sizesOut; newcm.recordScales = scalesOut; newcm.recordOffsets = offsetsOut; newcm.atlasFrameCounts = results.atlasFrameCounts.ToArray(); materialDict.Add(key, newcm); return material; }
/// <summary> /// Gets CachedMaterial properties for an atlased material. /// Atlas material will not be loaded automatically if not found in cache. /// </summary> /// <param name="archive">Atlas archive index.</param> /// <param name="cachedMaterialOut">CachedMaterial out</param> /// <returns>True if CachedMaterial found.</returns> public bool GetCachedMaterialAtlas(int archive, out CachedMaterial cachedMaterialOut) { int key = MakeTextureKey((short)archive, (byte)0, (byte)0, AtlasKeyGroup); if (materialDict.ContainsKey(key)) { cachedMaterialOut = materialDict[key]; return true; } cachedMaterialOut = new CachedMaterial(); return false; }
/// <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="border">Number of pixels internal border around each texture.</param> /// <param name="dilate">Blend texture into surrounding empty pixels.</param> /// <param name="shader">Shader for material. If null, DefaultShaderName will be applied.</param> /// <returns>Material or null.</returns> public Material GetMaterial( int archive, int record, int frame, int alphaIndex, out Rect rectOut, int border = 0, bool dilate = false, Shader shader = null) { // Ready check if (!IsReady) { rectOut = new Rect(); return(null); } // HACK: Override shader for unlit textures // TODO: Find a better way to do this if (archive == 356 && (record == 0 || record == 2 || record == 3) || archive == 87 && record == 0) { // Only override if not specified if (shader == null) { shader = Shader.Find(dfUnity.MaterialReader.DefaultUnlitTextureShaderName); } } int key = MakeTextureKey((short)archive, (byte)record, (byte)frame); if (materialDict.ContainsKey(key)) { CachedMaterial cm = materialDict[key]; if (cm.filterMode == MainFilterMode) { // Properties are the same rectOut = cm.singleRect; return(cm.material); } else { // Properties don't match, remove material and reload materialDict.Remove(key); } } if (shader == null) { shader = Shader.Find(DefaultShaderName); } Material material = new Material(shader); material.name = FormatName(archive, record); Texture2D texture = textureReader.GetTexture2D(archive, record, frame, alphaIndex, out rectOut, border, dilate); material.mainTexture = texture; material.mainTexture.filterMode = MainFilterMode; DFSize size = textureReader.TextureFile.GetSize(record); DFSize scale = textureReader.TextureFile.GetScale(record); DFSize offset = textureReader.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.Width, offset.Height) }; CachedMaterial newcm = new CachedMaterial() { key = key, keyGroup = 0, singleRect = rectOut, material = material, filterMode = MainFilterMode, isWindow = ClimateSwaps.IsExteriorWindow(archive, record), recordSizes = recordSizes, recordScales = recordScales, recordOffsets = recordOffsets, recordFrameCount = textureReader.TextureFile.GetFrameCount(record), }; materialDict.Add(key, newcm); return(material); }
/// <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> /// <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) { // 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; // Setup material Material material = CreateStandardMaterial(); material.name = FormatName(archive, record); material.mainTexture = results.albedoMap; material.mainTexture.filterMode = MainFilterMode; // Setup normal map if (GenerateNormals && 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"); } // Setup cached material DFSize size = results.textureFile.GetSize(record); DFSize scale = results.textureFile.GetScale(record); DFSize 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.Width, offset.Height) }; 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; }
/// <summary> /// Gets Unity Mesh from previously combined model data. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleon for loading content.</param> /// <param name="combiner">ModelCombiner to build from.</param> /// <param name="cachedMaterialsOut">Array of cached materials in order of submesh.</param> /// <param name="textureKeysOut">Array of original texture keys in order of submesh.</param> /// <param name="hasAnimationsOut">True if one or more materials have animations.</param> /// <param name="solveTangents">Solve tangents for this mesh.</param> /// <param name="lightmapUVs">Add secondary lightmap UVs to this mesh.</param> /// <returns>Mesh object or null.</returns> public Mesh GetCombinedMesh( DaggerfallUnity dfUnity, ModelCombiner combiner, out CachedMaterial[] cachedMaterialsOut, out int[] textureKeysOut, out bool hasAnimationsOut, bool solveTangents = false, bool lightmapUVs = false) { cachedMaterialsOut = null; hasAnimationsOut = false; textureKeysOut = null; // Ready check if (!IsReady) return null; // Get combined model ModelCombiner.CombinedModel combinedModel; if (!combiner.GetCombinedModel(out combinedModel)) return null; // Load materials cachedMaterialsOut = new CachedMaterial[combinedModel.SubMeshes.Length]; textureKeysOut = new int[combinedModel.SubMeshes.Length]; for (int i = 0; i < combinedModel.SubMeshes.Length; i++) { int archive = combinedModel.SubMeshes[i].TextureArchive; int record = combinedModel.SubMeshes[i].TextureRecord; textureKeysOut[i] = MaterialReader.MakeTextureKey((short)archive, (byte)record, (byte)0); // Add material to array CachedMaterial cachedMaterial; dfUnity.MaterialReader.GetCachedMaterial(archive, record, 0, out cachedMaterial); cachedMaterialsOut[i] = cachedMaterial; // Set animation flag if (cachedMaterial.singleFrameCount > 1 && !hasAnimationsOut) hasAnimationsOut = true; } // Create mesh Mesh mesh = new Mesh(); mesh.name = "CombinedMesh"; mesh.vertices = combinedModel.Vertices; mesh.normals = combinedModel.Normals; mesh.uv = combinedModel.UVs; mesh.subMeshCount = combinedModel.SubMeshes.Length; // Set submesh triangles for (int s = 0; s < mesh.subMeshCount; s++) { var sub = combinedModel.SubMeshes[s]; int[] triangles = new int[sub.PrimitiveCount * 3]; for (int t = 0; t < sub.PrimitiveCount * 3; t++) { triangles[t] = combinedModel.Indices[sub.StartIndex + t]; } mesh.SetTriangles(triangles, s); } // Finalise mesh if (solveTangents) TangentSolver(mesh); if (lightmapUVs) AddLightmapUVs(mesh); mesh.RecalculateBounds(); mesh.Optimize(); return mesh; }