/// <summary> /// Apply dungeon texture table. /// </summary> /// <param name="dungeonTextureTable">Dungeon texture table changes to apply.</param> public void SetDungeonTextures(int[] dungeonTextureTable) { if (defaultTextures.Count == 0) { return; } DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Get new material array Material[] materials = new Material[defaultTextures.Count]; DFLocation.ClimateBaseType climateIndex = Game.GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType; for (int i = 0; i < defaultTextures.Count; i++) { MaterialReader.ReverseTextureKey(defaultTextures[i], out int archive, out int record, out _); archive = DungeonTextureTables.ApplyTextureTable(archive, dungeonTextureTable, climateIndex); materials[i] = dfUnity.MaterialReader.GetMaterial(archive, record); } // Assign material array if (materials != null) { GetComponent <MeshRenderer>().sharedMaterials = materials; } }
/// <summary> /// Apply dungeon texture table. /// </summary> /// <param name="dungeonTextureTable">Dungeon texture table changes to apply.</param> public void SetDungeonTextures(int[] dungeonTextureTable) { if (defaultTextures.Count == 0) { return; } DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return; } // Get new material array int archive, record, frame; Material[] materials = new Material[defaultTextures.Count]; for (int i = 0; i < defaultTextures.Count; i++) { MaterialReader.ReverseTextureKey(defaultTextures[i], out archive, out record, out frame); switch (archive) { case 119: archive = dungeonTextureTable[0]; break; case 120: archive = dungeonTextureTable[1]; break; case 122: archive = dungeonTextureTable[2]; break; case 123: archive = dungeonTextureTable[3]; break; case 124: archive = dungeonTextureTable[4]; break; case 168: archive = dungeonTextureTable[5]; break; } materials[i] = dfUnity.MaterialReader.GetMaterial(archive, record); } // Assign material array if (materials != null) { GetComponent <MeshRenderer>().sharedMaterials = materials; } }
/// <summary> /// Sets billboard material with a custom texture. /// </summary> /// <param name="texture">Texture2D to set on material.</param> /// <param name="size">Size of billboard quad in normal units (not Daggerfall units).</param> /// <returns>Material.</returns> public Material SetMaterial(Texture2D texture, Vector2 size, bool isLightArchive = false) { // Get DaggerfallUnity DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Get references meshRenderer = GetComponent <MeshRenderer>(); // Create material Material material = MaterialReader.CreateStandardMaterial(MaterialReader.CustomBlendMode.Cutout); material.mainTexture = texture; // Create mesh Mesh mesh = dfUnity.MeshReader.GetSimpleBillboardMesh(size); // Set summary summary.FlatType = FlatTypes.Decoration; summary.Size = size; // Assign mesh and material MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh oldMesh = meshFilter.sharedMesh; if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterial = material; } if (oldMesh) { // The old mesh is no longer required #if UNITY_EDITOR DestroyImmediate(oldMesh); #else Destroy(oldMesh); #endif } // General billboard shadows if enabled meshRenderer.shadowCastingMode = (DaggerfallUnity.Settings.GeneralBillboardShadows && !isLightArchive) ? ShadowCastingMode.TwoSided : ShadowCastingMode.Off; return(material); }
/// <summary> /// Removes from cache all assets that have not been accessed from the time in minutes defined by /// <see cref="SettingsManager.AssetCacheThreshold"/>. If equals to <c>0</c> assets are never removed from cache. /// </summary> internal void PruneCache() { if (Settings.AssetCacheThreshold == 0) { return; } float time = Time.realtimeSinceStartup; float threshold = Settings.AssetCacheThreshold * 60; MaterialReader.PruneCache(time, threshold); if (ModManager.Instance) { ModManager.Instance.PruneCache(time, threshold); } RaiseOnPruneCacheEvent(time, threshold); }
/// <summary> /// Rebuild materials back to default with no climate modifier. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> public void DisableClimate(DaggerfallUnity dfUnity) { if (defaultTextures.Count == 0) { return; } // Get new material array int archive, record, frame; Material[] materials = new Material[defaultTextures.Count]; for (int i = 0; i < defaultTextures.Count; i++) { MaterialReader.ReverseTextureKey(defaultTextures[i], out archive, out record, out frame); materials[i] = dfUnity.MaterialReader.GetMaterial(archive, record); } // Assign material array if (materials != null) { GetComponent <MeshRenderer>().sharedMaterials = materials; } }
/// <summary> /// Sets new Daggerfall material and recreates mesh. /// Will use an atlas if specified in DaggerfallUnity singleton. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="archive">Texture archive index.</param> /// <param name="record">Texture record index.</param> /// <param name="frame">Frame index.</param> /// <param name="dungeon">This is a dungeon billboard.</param> /// <returns>Material.</returns> public Material SetMaterial(DaggerfallUnity dfUnity, int archive, int record, int frame, bool dungeon) { Shader shader; //if (archive == 210 && dungeon) // Dungeon light billboards use unlit billboard shader if (archive == 210) // All light billboards use unlit billboard shader { shader = Shader.Find(dfUnity.MaterialReader.DefaultUnlitBillboardShaderName); } else { shader = Shader.Find(dfUnity.MaterialReader.DefaultBillboardShaderName); } // Get references meshRenderer = GetComponent <MeshRenderer>(); Vector2 size; Mesh mesh = null; Material material = null; if (dfUnity.MaterialReader.AtlasTextures) { material = dfUnity.MaterialReader.GetMaterialAtlas( archive, 0, 4, 2048, out summary.AtlasRects, out summary.AtlasIndices, 4, true, 0, false, shader); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.AtlasRects[summary.AtlasIndices[record].startIndex], archive, record, out size, dungeon); summary.AtlasedMaterial = true; if (summary.AtlasIndices[record].frameCount > 1) { summary.AnimatedMaterial = true; } else { summary.AnimatedMaterial = false; } } else { material = dfUnity.MaterialReader.GetMaterial( archive, record, frame, 0, out summary.Rect, 4, true, shader); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.Rect, archive, record, out size, dungeon); summary.AtlasedMaterial = false; summary.AnimatedMaterial = false; } // Set summary summary.InDungeon = dungeon; summary.FlatType = MaterialReader.GetFlatType(archive); summary.Archive = archive; summary.Record = record; summary.Size = size; // Set editor flat types if (summary.FlatType == FlatTypes.Editor) { summary.EditorFlatType = MaterialReader.GetEditorFlatType(summary.Record); } // Assign mesh and material MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh oldMesh = meshFilter.sharedMesh; if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterial = material; } if (oldMesh) { // The old mesh is no longer required DestroyImmediate(oldMesh); } return(material); }
/// <summary> /// Creates mesh and material for this enemy. /// </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 AssignMeshAndMaterial(DaggerfallUnity dfUnity, int archive) { // Get mesh filter if (meshFilter == null) { meshFilter = GetComponent <MeshFilter>(); } // Vertices for a 1x1 unit quad // This is scaled to correct size depending on facing and orientation float hx = 0.5f, hy = 0.5f; Vector3[] vertices = new Vector3[4]; vertices[0] = new Vector3(hx, hy, 0); vertices[1] = new Vector3(-hx, hy, 0); vertices[2] = new Vector3(hx, -hy, 0); vertices[3] = new Vector3(-hx, -hy, 0); // Indices int[] indices = new int[6] { 0, 1, 2, 3, 2, 1, }; // Normals Vector3 normal = Vector3.Normalize(Vector3.up + Vector3.forward); Vector3[] normals = new Vector3[4]; normals[0] = normal; normals[1] = normal; normals[2] = normal; normals[3] = normal; // Create mesh Mesh mesh = new Mesh(); mesh.name = string.Format("MobileEnemyMesh"); mesh.vertices = vertices; mesh.triangles = indices; mesh.normals = normals; // Assign mesh meshFilter.sharedMesh = mesh; // Seek textures from mods TextureReplacement.SetEnemyImportedTextures(archive, GetComponent <MeshFilter>(), ref summary.ImportedTextures); // Create material Material material; if (summary.ImportedTextures.HasImportedTextures) { material = MaterialReader.CreateStandardMaterial(MaterialReader.CustomBlendMode.Cutout); } else { material = dfUnity.MaterialReader.GetMaterialAtlas( archive, 0, 4, 1024, out summary.AtlasRects, out summary.AtlasIndices, 4, true, 0, false, true); } // Set new enemy material GetComponent <MeshRenderer>().sharedMaterial = material; }
/// <summary> /// Sets new Daggerfall material and recreates mesh. /// Will use an atlas if specified in DaggerfallUnity singleton. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="archive">Texture archive index.</param> /// <param name="record">Texture record index.</param> /// <param name="frame">Frame index.</param> /// <returns>Material.</returns> public Material SetMaterial(int archive, int record, int frame = 0) { // Get DaggerfallUnity DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Get references meshRenderer = GetComponent <MeshRenderer>(); Vector2 size; Vector2 scale; Mesh mesh = null; Material material = null; if (material = TextureReplacement.GetStaticBillboardMaterial(gameObject, archive, record, ref summary, out scale)) { mesh = dfUnity.MeshReader.GetBillboardMesh(summary.Rect, archive, record, out size); size *= scale; summary.AtlasedMaterial = false; summary.AnimatedMaterial = summary.ImportedTextures.FrameCount > 1; } else if (dfUnity.MaterialReader.AtlasTextures) { material = dfUnity.MaterialReader.GetMaterialAtlas( archive, 0, 4, 2048, out summary.AtlasRects, out summary.AtlasIndices, 4, true, 0, false, true); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.AtlasRects[summary.AtlasIndices[record].startIndex], archive, record, out size); summary.AtlasedMaterial = true; if (summary.AtlasIndices[record].frameCount > 1) { summary.AnimatedMaterial = true; } else { summary.AnimatedMaterial = false; } } else { material = dfUnity.MaterialReader.GetMaterial( archive, record, frame, 0, out summary.Rect, 4, true, true); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.Rect, archive, record, out size); summary.AtlasedMaterial = false; summary.AnimatedMaterial = false; } // Set summary summary.FlatType = MaterialReader.GetFlatType(archive); summary.Archive = archive; summary.Record = record; summary.Size = size; // Set editor flat types if (summary.FlatType == FlatTypes.Editor) { summary.EditorFlatType = MaterialReader.GetEditorFlatType(summary.Record); } // Set NPC flat type based on archive if (RDBLayout.IsNPCFlat(summary.Archive)) { summary.FlatType = FlatTypes.NPC; } // Assign mesh and material MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh oldMesh = meshFilter.sharedMesh; if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterial = material; } if (oldMesh) { // The old mesh is no longer required #if UNITY_EDITOR DestroyImmediate(oldMesh); #else Destroy(oldMesh); #endif } // General billboard shadows if enabled bool isLightArchive = (archive == TextureReader.LightsTextureArchive); meshRenderer.shadowCastingMode = (DaggerfallUnity.Settings.GeneralBillboardShadows && !isLightArchive) ? ShadowCastingMode.TwoSided : ShadowCastingMode.Off; // Add NPC trigger collider if (summary.FlatType == FlatTypes.NPC) { Collider col = gameObject.AddComponent <BoxCollider>(); col.isTrigger = true; } return(material); }
/// <summary> /// Sets new Daggerfall material and recreates mesh. /// Will use an atlas if specified in DaggerfallUnity singleton. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="archive">Texture archive index.</param> /// <param name="record">Texture record index.</param> /// <param name="frame">Frame index.</param> /// <param name="dungeon">This is a dungeon billboard.</param> /// <returns>Material.</returns> public Material SetMaterial(int archive, int record, int frame, bool dungeon) { // Get DaggerfallUnity DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Get references meshRenderer = GetComponent <MeshRenderer>(); Vector2 size; Mesh mesh = null; Material material = null; if (dfUnity.MaterialReader.AtlasTextures) { material = dfUnity.MaterialReader.GetMaterialAtlas( archive, 0, 4, 2048, out summary.AtlasRects, out summary.AtlasIndices, 4, true, 0, false); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.AtlasRects[summary.AtlasIndices[record].startIndex], archive, record, out size, dungeon); summary.AtlasedMaterial = true; if (summary.AtlasIndices[record].frameCount > 1) { summary.AnimatedMaterial = true; } else { summary.AnimatedMaterial = false; } } else { material = dfUnity.MaterialReader.GetMaterial( archive, record, frame, 0, out summary.Rect, 4, true); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.Rect, archive, record, out size, dungeon); summary.AtlasedMaterial = false; summary.AnimatedMaterial = false; } // Update material properties MaterialReader.SetBlendMode(material, MaterialReader.CustomBlendMode.Cutout); // Set summary summary.InDungeon = dungeon; summary.FlatType = MaterialReader.GetFlatType(archive); summary.Archive = archive; summary.Record = record; summary.Size = size; // Set editor flat types if (summary.FlatType == FlatTypes.Editor) { summary.EditorFlatType = MaterialReader.GetEditorFlatType(summary.Record); } // Assign mesh and material MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh oldMesh = meshFilter.sharedMesh; if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterial = material; } if (oldMesh) { // The old mesh is no longer required DestroyImmediate(oldMesh); } // Standalone billboards never cast shadows meshRenderer.shadowCastingMode = ShadowCastingMode.Off; 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(); return(mesh); }
/// <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> /// Sets new Daggerfall material and recreates mesh. /// Will use an atlas if specified in DaggerfallUnity singleton. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="archive">Texture archive index.</param> /// <param name="record">Texture record index.</param> /// <param name="frame">Frame index.</param> /// <returns>Material.</returns> public Material SetMaterial(int archive, int record, int frame = 0) { // Get DaggerfallUnity DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } // Get references meshRenderer = GetComponent <MeshRenderer>(); Vector2 size; Mesh mesh = null; Material material = null; if (dfUnity.MaterialReader.AtlasTextures) { material = dfUnity.MaterialReader.GetMaterialAtlas( archive, 0, 4, 2048, out summary.AtlasRects, out summary.AtlasIndices, 4, true, 0, false, true); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.AtlasRects[summary.AtlasIndices[record].startIndex], archive, record, out size); summary.AtlasedMaterial = true; if (summary.AtlasIndices[record].frameCount > 1) { summary.AnimatedMaterial = true; } else { summary.AnimatedMaterial = false; } } else { material = dfUnity.MaterialReader.GetMaterial( archive, record, frame, 0, out summary.Rect, 4, true, true); mesh = dfUnity.MeshReader.GetBillboardMesh( summary.Rect, archive, record, out size); summary.AtlasedMaterial = false; summary.AnimatedMaterial = false; } // Set summary summary.FlatType = MaterialReader.GetFlatType(archive); summary.Archive = archive; summary.Record = record; summary.Size = size; // Set editor flat types if (summary.FlatType == FlatTypes.Editor) { summary.EditorFlatType = MaterialReader.GetEditorFlatType(summary.Record); } // Set NPC flat type based on archive // This is just a hack for now while performing // more research into NPC names if (summary.Archive == 334 || // Daggerfall people summary.Archive == 346 || // Wayrest people summary.Archive == 357 || // Sentinel people summary.Archive >= 175 && summary.Archive <= 184) // Other people { summary.FlatType = FlatTypes.NPC; } // Assign mesh and material MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh oldMesh = meshFilter.sharedMesh; if (mesh) { meshFilter.sharedMesh = mesh; meshRenderer.sharedMaterial = material; } if (oldMesh) { // The old mesh is no longer required #if UNITY_EDITOR DestroyImmediate(oldMesh); #else Destroy(oldMesh); #endif } // Standalone billboards never cast shadows meshRenderer.shadowCastingMode = ShadowCastingMode.Off; // Add NPC trigger collider if (summary.FlatType == FlatTypes.NPC) { Collider col = gameObject.AddComponent <BoxCollider>(); col.isTrigger = true; } return(material); }