public byte[] TextureToPngBytes(Texture2DObject texture) { if (texture.TextureFormat == Texture2DObject.TextureFormatType.RGB24) { //what's the quality setting here? using (MemoryStream msPng = new MemoryStream()) { RGBBytesToBitmap(texture.ImageData, texture.Width, texture.Height).Compress(Bitmap.CompressFormat.Png, 90, msPng); return(msPng.ToArray()); } } else if (texture.TextureFormat == Texture2DObject.TextureFormatType.ETC_RGB4 || texture.TextureFormat == Texture2DObject.TextureFormatType.ETC2_RGB) { using (MemoryStream msPng = new MemoryStream()) { ETCBytesToBitmap(texture.ImageData, texture.Width, texture.Height).Compress(Bitmap.CompressFormat.Png, 90, msPng); return(msPng.ToArray()); } } else { Log.LogErr($"{texture?.Name} is in an unsupported format for decoding: {texture?.TextureFormat}"); return(null); } }
public void AssignImageToTexture(byte[] imageData, Texture2DObject targetTexture, int targetWidth, int targetHeight, int targetMips = Int32.MaxValue, TextureConversionFormat format = TextureConversionFormat.Auto) { //right now "Auto" is always RGB24. Probably will automatically decide based on image size, but doing ETC2 sucks at the moment Bitmap sourceImage = null; using (var ms = new MemoryStream(imageData)) { sourceImage = new Bitmap(ms); } int mips; byte[] textureBytes; if (format == TextureConversionFormat.ETC2_RGB) { throw new NotImplementedException(); //textureBytes = ConvertToETC2AndMipmap(sourceImage, targetWidth, targetHeight, targetMips, out mips); targetTexture.TextureFormat = Texture2DObject.TextureFormatType.ETC2_RGB; } else //always fall back to RGB24 { textureBytes = ConvertToRGBAndMipmap(sourceImage, targetWidth, targetHeight, targetMips, out mips); targetTexture.TextureFormat = Texture2DObject.TextureFormatType.RGB24; } targetTexture.ForcedFallbackFormat = 4; targetTexture.DownscaleFallback = false; targetTexture.Width = targetWidth; targetTexture.Height = targetHeight; targetTexture.CompleteImageSize = textureBytes.Length; targetTexture.MipCount = mips; targetTexture.IsReadable = false; targetTexture.StreamingMipmaps = false; targetTexture.StreamingMipmapsPriority = 0; targetTexture.ImageCount = 1; targetTexture.TextureDimension = 2; targetTexture.TextureSettings = new GLTextureSettings() { FilterMode = 2, Aniso = 1, MipBias = -1, WrapU = 1, WrapV = 1, WrapW = 0 }; targetTexture.LightmapFormat = 6; targetTexture.ColorSpace = 1; targetTexture.ImageData = textureBytes; targetTexture.StreamData = new StreamingInfo() { offset = 0, size = 0, path = "" }; }
private static void SetFallbackCoverTexture(Texture2DObject texture) { byte[] imageBytes; var assembly = Assembly.GetExecutingAssembly(); var resourceStream = assembly.GetManifestResourceStream("QuestomAssets.CustomSongsCover.ETC_RGB4"); using (var reader = new BinaryReader(resourceStream, Encoding.UTF8)) { imageBytes = reader.ReadBytes((int)resourceStream.Length); } //Resource File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "CustomSongsCover.ETC_RGB4")); int mips = 11; texture.ForcedFallbackFormat = 4; texture.DownscaleFallback = false; texture.Width = 1024; texture.Height = 1024; texture.CompleteImageSize = imageBytes.Length; texture.TextureFormat = AssetsChanger.Texture2DObject.TextureFormatType.ETC2_RGB; texture.MipCount = mips; texture.IsReadable = false; texture.StreamingMipmaps = false; texture.StreamingMipmapsPriority = 0; texture.ImageCount = 1; texture.TextureDimension = 2; texture.TextureSettings = new GLTextureSettings() { FilterMode = 2, Aniso = 1, MipBias = -1, WrapU = 1, WrapV = 1, WrapW = 0 }; texture.LightmapFormat = 6; texture.ColorSpace = 1; texture.ImageData = imageBytes; texture.StreamData = new StreamingInfo() { offset = 0, size = 0, path = "" }; }
public SpriteObject LoadPackCover(string assetName, byte[] coverImageBytes) { Texture2DObject packCover = null; if (coverImageBytes != null && coverImageBytes.Length > 0) { try { var loadedCover = new Texture2DObject(_assetsFile) { Name = assetName }; ImageUtils.Instance.AssignImageToTexture(coverImageBytes, loadedCover, 1024, 1024); packCover = loadedCover; } catch (Exception ex) { Log.LogErr($"Failed to convert to texture, falling back to default cover image", ex); } } if (packCover == null) { Log.LogMsg($"Using default cover image for asset name {assetName}"); packCover = new Texture2DObject(_assetsFile) { Name = assetName }; SetFallbackCoverTexture(packCover); } _assetsFile.AddObject(packCover, true); //slightly less hacky than before, but only a little var extrasCover = _assetsFile.Manager.MassFirstAsset <SpriteObject>(x => x.Object.Name == "ExtrasCover"); SpriteObject coverAsset = extrasCover.Clone(); coverAsset.Name = assetName; coverAsset.RenderData.Texture = packCover.PtrFrom(coverAsset); _assetsFile.AddObject(coverAsset, true); return(coverAsset); }
public SpriteObject LoadPackCover(string assetName, string packCoverImage) { Texture2DObject packCover = null; if (!string.IsNullOrWhiteSpace(packCoverImage)) { try { var loadedCover = new Texture2DObject(_assetsFile) { Name = assetName }; byte[] coverImageBytes = _config.SongFileProvider.Read(_config.PlaylistArtPath.CombineFwdSlash(packCoverImage)); ImageUtils.Instance.AssignImageToTexture(coverImageBytes, loadedCover, 1024, 1024); packCover = loadedCover; } catch (Exception ex) { Log.LogErr($"Failed to convert to texture, falling back to default cover image", ex); } } if (packCover == null) { packCover = new Texture2DObject(_assetsFile) { Name = assetName }; SetFallbackCoverTexture(packCover); } _assetsFile.AddObject(packCover, true); //slightly less hacky than before, but only a little var extrasCover = _assetsFile.Manager.MassFirstAsset <SpriteObject>(x => x.Object.Name == "ExtrasCover"); SpriteObject coverAsset = extrasCover.Clone(); coverAsset.Name = assetName; coverAsset.Texture = packCover.PtrFrom(coverAsset); _assetsFile.AddObject(coverAsset, true); return(coverAsset); }
public void AssignImageToTexture(byte[] imageData, Texture2DObject targetTexture, int targetWidth, int targetHeight, int targetMips = int.MaxValue, TextureConversionFormat format = TextureConversionFormat.Auto) { int actualMips; Bitmap bitmap = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length); byte[] textureBytes = MipmapToRGBBytes(bitmap, targetWidth, targetHeight, targetMips, out actualMips); targetTexture.TextureFormat = Texture2DObject.TextureFormatType.RGB24; targetTexture.ForcedFallbackFormat = 4; targetTexture.DownscaleFallback = false; targetTexture.Width = targetWidth; targetTexture.Height = targetHeight; targetTexture.CompleteImageSize = textureBytes.Length; targetTexture.MipCount = actualMips; targetTexture.IsReadable = false; targetTexture.StreamingMipmaps = false; targetTexture.StreamingMipmapsPriority = 0; targetTexture.ImageCount = 1; targetTexture.TextureDimension = 2; targetTexture.TextureSettings = new GLTextureSettings() { FilterMode = 2, Aniso = 1, MipBias = -1, WrapU = 1, WrapV = 1, WrapW = 0 }; targetTexture.LightmapFormat = 6; targetTexture.ColorSpace = 1; targetTexture.ImageData = textureBytes; targetTexture.StreamData = new StreamingInfo() { offset = 0, size = 0, path = "" }; }
public Texture2DObject LoadSongCover(string songPath, BeatmapLevelDataObject levelData) { if (!string.IsNullOrWhiteSpace(levelData.CoverImageFilename) && _config.SongFileProvider.FileExists(songPath.CombineFwdSlash(levelData.CoverImageFilename))) { try { string coverFile = songPath.CombineFwdSlash(levelData.CoverImageFilename); var coverAsset = new Texture2DObject(_assetsFile) { Name = levelData.LevelID + "Cover" }; byte[] imageBytes = _config.SongFileProvider.Read(coverFile); ImageUtils.Instance.AssignImageToTexture(imageBytes, coverAsset, 256, 256); return(coverAsset); } catch (Exception ex) { Log.LogErr("Error loading cover art asset", ex); } } return(null); }
public byte[] TextureToPngBytes(Texture2DObject texture) { //does unity store mips of compressed textures individually, or does it compress the entire thing? switch (texture.TextureFormat) { case Texture2DObject.TextureFormatType.RGB24: using (var ms = new MemoryStream()) { ConvertFromRGB24ToBitmap(texture.ImageData, texture.Width, texture.Height, RotateFlipType.RotateNoneFlipXY).Save(ms, System.Drawing.Imaging.ImageFormat.Png); return(ms.ToArray()); } //not really sure if it can do all these or not case Texture2DObject.TextureFormatType.ETC2_RGB: case Texture2DObject.TextureFormatType.ETC_RGB4: case Texture2DObject.TextureFormatType.ETC2_RGBA8: case Texture2DObject.TextureFormatType.ETC2_RGBA1: return(null); // return ConvertETC2ToBitmap(texture.ImageData, texture.Width, texture.Height); default: throw new NotImplementedException($"Texture type {texture.ToString()} isn't currently supported."); } }
private static void SetFallbackCoverTexture(Texture2DObject texture) { byte[] imageBytes = File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "CustomSongsCover.ETC_RGB4")); int mips = 11; texture.ForcedFallbackFormat = 4; texture.DownscaleFallback = false; texture.Width = 1024; texture.Height = 1024; texture.CompleteImageSize = imageBytes.Length; texture.TextureFormat = AssetsChanger.Texture2DObject.TextureFormatType.ETC2_RGB; texture.MipCount = mips; texture.IsReadable = false; texture.StreamingMipmaps = false; texture.StreamingMipmapsPriority = 0; texture.ImageCount = 1; texture.TextureDimension = 2; texture.TextureSettings = new GLTextureSettings() { FilterMode = 2, Aniso = 1, MipBias = -1, WrapU = 1, WrapV = 1, WrapW = 0 }; texture.LightmapFormat = 6; texture.ColorSpace = 1; texture.ImageData = imageBytes; texture.StreamData = new StreamingInfo() { offset = 0, size = 0, path = "" }; }
public Texture2DObject LoadSongCover(string songPath, BeatmapLevelDataObject levelData) { if (!string.IsNullOrWhiteSpace(levelData.CoverImageFilename) && File.Exists(Path.Combine(songPath, levelData.CoverImageFilename))) { try { string coverFile = Path.Combine(songPath, levelData.CoverImageFilename); Bitmap coverImage = (Bitmap)Bitmap.FromFile(coverFile); var coverAsset = new Texture2DObject(_assetsFile) { Name = levelData.LevelID + "Cover" }; ImageUtils.AssignImageToTexture(coverImage, coverAsset, 256, 256); return(coverAsset); } catch (Exception ex) { Log.LogErr("Error loading cover art asset", ex); } } return(null); }
public static byte[] ToPngBytes(this Texture2DObject texture) { return(ImageUtils.Instance.TextureToPngBytes(texture)); }
public BeatmapLevelDataObject LoadSongToAsset(BeatmapLevelDataObject beatmapLevel, string songPath, bool includeCovers = true) { try { beatmapLevel.Name = $"{beatmapLevel.LevelID}Level"; //cover art Texture2DObject coverImage = null; if (includeCovers) { coverImage = LoadSongCover(songPath, beatmapLevel); } //audio var audioAsset = LoadSongAudioAsset(songPath, beatmapLevel); if (audioAsset == null) { Log.LogErr($"failed to get audio for song at path {songPath}"); return(null); } var toRemoveSet = new List <DifficultyBeatmapSet>(); foreach (var difficultySet in beatmapLevel.DifficultyBeatmapSets) { var characteristic = GetCharacteristicAsset(difficultySet.BeatmapCharacteristicName)?.PtrFrom(beatmapLevel); if (characteristic == null) { Log.LogErr($"Characteristic {difficultySet.BeatmapCharacteristicName} couldn't be found. Set will be removed."); toRemoveSet.Add(difficultySet); continue; } difficultySet.BeatmapCharacteristic = characteristic; List <DifficultyBeatmap> toRemove = new List <DifficultyBeatmap>(); foreach (var difficultyBeatmap in difficultySet.DifficultyBeatmaps) { string dataFile = null; if (!string.IsNullOrWhiteSpace(difficultyBeatmap.BeatmapFilename)) { dataFile = songPath.CombineFwdSlash(difficultyBeatmap.BeatmapFilename); if (!_config.SongFileProvider.FileExists(dataFile)) { Log.LogErr($"BeatmapFilename was set to {dataFile} but the file didn't exist, will try to fall back to difficulty name."); dataFile = null; } } if (dataFile == null) { dataFile = songPath.CombineFwdSlash($"{difficultyBeatmap.Difficulty.ToString()}.dat"); } if (!_config.SongFileProvider.FileExists(dataFile)) { Log.LogErr(dataFile + " is missing, skipping this difficulty"); toRemove.Add(difficultyBeatmap); continue; } string jsonData = _config.SongFileProvider.ReadToString(dataFile); if (_assetsFile != null) { difficultyBeatmap.BeatmapData = new BeatmapDataObject(_assetsFile); } difficultyBeatmap.BeatmapData.Name = beatmapLevel.LevelID + ((difficultySet.BeatmapCharacteristicName == Characteristic.Standard) ? "" : difficultySet.BeatmapCharacteristicName.ToString()) + difficultyBeatmap.Difficulty.ToString() + "BeatmapData"; difficultyBeatmap.BeatmapData.BeatsPerMinute = beatmapLevel.BeatsPerMinute; difficultyBeatmap.BeatmapData.Shuffle = beatmapLevel.Shuffle; difficultyBeatmap.BeatmapData.ShufflePeriod = beatmapLevel.ShufflePeriod; difficultyBeatmap.BeatmapData.JsonData = jsonData; _assetsFile.AddObject(difficultyBeatmap.BeatmapData, true); difficultyBeatmap.BeatmapDataPtr = difficultyBeatmap.BeatmapData.PtrFrom(beatmapLevel); } toRemove.ForEach(x => difficultySet.DifficultyBeatmaps.Remove(x)); if (difficultySet.DifficultyBeatmaps.Count < 1) { Log.LogErr($"Song at path {songPath} has no valid beatmaps for any difficulty on set {difficultySet.BeatmapCharacteristicName}, removing it"); toRemoveSet.Add(difficultySet); continue; } } toRemoveSet.ForEach(x => beatmapLevel.DifficultyBeatmapSets.Remove(x)); if (beatmapLevel.DifficultyBeatmapSets.Count < 1) { Log.LogErr($"Song at path {songPath} has no valid characterstics, it will not be imported"); return(null); } _assetsFile.AddObject(audioAsset, true); if (coverImage != null) { _assetsFile.AddObject(coverImage); } beatmapLevel.AudioClip = audioAsset.PtrFrom(beatmapLevel); if (coverImage == null) { var bsCover = _assetsFile.FindAsset <Texture2DObject>(x => x.Object.Name == "BeatSaberCover"); if (bsCover == null) { Log.LogErr("Unable to find BeatSaberCover in assets! How is that gone?"); throw new Exception("Could not find beat saber cover in assets! That should never be missing."); } var cover = bsCover.Clone(); _assetsFile.AddObject(cover, true); beatmapLevel.CoverImageTexture2D = cover.PtrFrom(beatmapLevel); } else { beatmapLevel.CoverImageTexture2D = coverImage.PtrFrom(beatmapLevel); } var environment = GetEnvironment(beatmapLevel.EnvironmentName); if (environment == null) { Log.LogMsg($"Unknown environment name '{beatmapLevel.EnvironmentName}' on '{beatmapLevel.SongName}', falling back to default."); environment = GetEnvironment("DefaultEnvironment"); if (environment == null) { throw new Exception("Unable to find the default environment!"); } } beatmapLevel.EnvironmentInfo = environment.PtrFrom(beatmapLevel); _assetsFile.AddObject(beatmapLevel, true); return(beatmapLevel); } catch (Exception ex) { Log.LogErr($"Error loading song from path {songPath}", ex); return(null); } }
private BeatmapCharacteristicObject CreateCharacteristic(Characteristic characteristic) { if (characteristic == Characteristic.Standard) { throw new Exception("Tried to create standard beatmap characteristic which means it's missing. Assets are broken."); } BeatmapCharacteristicObject standardCharacteristic = GetCharacteristicAsset(Characteristic.Standard); if (standardCharacteristic == null) { Log.LogErr($"Unable to locate the standard beatmap characteristic while verifying characteristics!"); throw new Exception("Could not locate standard beatmap characteristic!"); } int count = _assetsFile.Manager.MassFindAssets <BeatmapCharacteristicObject>(x => true, false).Count(); try { string characteristicName = $"LEVEL_{characteristic.ToString().ToUpper()}"; string hintText = $"{characteristicName}_HINT"; string assetName = MiscUtils.GetCharacteristicAssetName(characteristic); var lightshowAsset = (BeatmapCharacteristicObject)standardCharacteristic.ObjectInfo.Clone(standardCharacteristic.ObjectInfo.ParentFile); lightshowAsset.Name = assetName; lightshowAsset.SerializedName = characteristic.ToString(); lightshowAsset.SortingOrder = count; lightshowAsset.CompoundIdPartName = characteristic.ToString(); //todo: text translation stuff //lightshowAsset.CharacteristicName = characteristicName; //lightshowAsset.HintText = hintText; var allChar = _assetsFile.Manager.MassFirstOrDefaultAsset <BeatmapCharacteristicCollectionObject>(x => true); if (allChar == null) { throw new Exception("Unable to find AllBeatmapCharacteristics object!"); } if (!allChar.Object.BeatmapCharacteristics.Any(x => x.Object.Name == lightshowAsset.Name)) { allChar.Object.BeatmapCharacteristics.Add(lightshowAsset.PtrFrom(allChar.Object)); } try { byte[] lightshowIcon = _config.EmbeddedResourcesFileProvider.Read($"{characteristic}.png"); if (lightshowIcon == null || lightshowIcon.Length < 1) { throw new Exception($"{characteristic}.png read was null or empty!"); } var clonedSprite = (SpriteObject)standardCharacteristic.Icon.Object.ObjectInfo.Clone(standardCharacteristic.ObjectInfo.ParentFile); var newTexture = new Texture2DObject(standardCharacteristic.ObjectInfo.ParentFile) { Name = assetName }; clonedSprite.RenderData.AtlasRectOffset.X = -1; clonedSprite.RenderData.AtlasRectOffset.Y = -1; clonedSprite.RenderData.TextureRect.X = 0; clonedSprite.RenderData.TextureRect.Y = 0; clonedSprite.RenderData.Texture = newTexture.PtrFrom(clonedSprite); clonedSprite.Name = assetName + "Icon"; ImageUtils.Instance.AssignImageToTexture(lightshowIcon, newTexture, 128, 128, Int32.MaxValue, TextureConversionFormat.Auto); lightshowAsset.Icon = clonedSprite.PtrFrom(lightshowAsset); standardCharacteristic.ObjectInfo.ParentFile.AddObject(clonedSprite); standardCharacteristic.ObjectInfo.ParentFile.AddObject(newTexture); standardCharacteristic.ObjectInfo.ParentFile.AddObject(lightshowAsset); } catch (Exception ex) { Log.LogErr($"Failed to load {characteristic}'s png icon!", ex); throw; } return(lightshowAsset); } catch (Exception ex) { Log.LogErr($"Exception trying to create {characteristic} characteristic!", ex); throw new Exception($"Error trying to create characteristic {characteristic}!", ex); } }
public BeatmapLevelDataObject LoadSongToAsset(BeatmapLevelDataObject beatmapLevel, string songPath, out string oggFileName, bool includeCovers = true) { oggFileName = null; try { beatmapLevel.Name = $"{beatmapLevel.LevelID}Level"; //cover art Texture2DObject coverImage = null; if (includeCovers) { coverImage = LoadSongCover(songPath, beatmapLevel); } //audio var audioAsset = LoadSongAudioAsset(songPath, beatmapLevel); if (audioAsset == null) { Log.LogErr($"failed to get audio for song at path {songPath}"); return(null); } oggFileName = Path.Combine(songPath, beatmapLevel.SongFilename);; foreach (var difficultySet in beatmapLevel.DifficultyBeatmapSets) { difficultySet.BeatmapCharacteristic = GetCharacteristicAsset(difficultySet.BeatmapCharacteristicName).PtrFrom(beatmapLevel); List <DifficultyBeatmap> toRemove = new List <DifficultyBeatmap>(); foreach (var difficultyBeatmap in difficultySet.DifficultyBeatmaps) { var dataFile = Path.Combine(songPath, $"{difficultyBeatmap.Difficulty.ToString()}.dat"); if (!File.Exists(dataFile)) { Log.LogErr(dataFile + " is missing, skipping this difficulty"); toRemove.Add(difficultyBeatmap); continue; } string jsonData; using (var sr = new StreamReader(dataFile)) { jsonData = sr.ReadToEnd(); } if (_assetsFile != null) { difficultyBeatmap.BeatmapData = new BeatmapDataObject(_assetsFile); } difficultyBeatmap.BeatmapData.Name = beatmapLevel.LevelID + ((difficultySet.BeatmapCharacteristicName == Characteristic.Standard) ? "" : difficultySet.BeatmapCharacteristicName.ToString()) + difficultyBeatmap.Difficulty.ToString() + "BeatmapData"; difficultyBeatmap.BeatmapData.BeatsPerMinute = beatmapLevel.BeatsPerMinute; difficultyBeatmap.BeatmapData.Shuffle = beatmapLevel.Shuffle; difficultyBeatmap.BeatmapData.ShufflePeriod = beatmapLevel.ShufflePeriod; difficultyBeatmap.BeatmapData.JsonData = jsonData; difficultyBeatmap.BeatmapData.TransformToProjectedData(); difficultyBeatmap.BeatmapData.JsonData = null; _assetsFile.AddObject(difficultyBeatmap.BeatmapData, true); difficultyBeatmap.BeatmapDataPtr = difficultyBeatmap.BeatmapData.PtrFrom(beatmapLevel); } toRemove.ForEach(x => difficultySet.DifficultyBeatmaps.Remove(x)); if (difficultySet.DifficultyBeatmaps.Count < 1) { Log.LogErr($"Song at path {songPath} has no valid beatmaps for any difficulty, skipping song"); return(null); } } _assetsFile.AddObject(audioAsset, true); if (coverImage != null) { _assetsFile.AddObject(coverImage); } beatmapLevel.AudioClip = audioAsset.PtrFrom(beatmapLevel); if (coverImage == null) { beatmapLevel.CoverImageTexture2D = _assetsFile.FindAsset <Texture2DObject>(x => x.Object.Name == "BeatSaberCover").PtrFrom(beatmapLevel); } else { beatmapLevel.CoverImageTexture2D = coverImage.PtrFrom(beatmapLevel); } var environment = _assetsFile.Manager.MassFirstOrDefaultAsset <MonoBehaviourObject>(x => x.Object.Name == $"{beatmapLevel.EnvironmentName}SceneInfo"); if (environment == null) { Log.LogMsg($"Unknown environment name '{beatmapLevel.EnvironmentName}' on '{beatmapLevel.SongName}', falling back to default."); environment = _assetsFile.Manager.MassFirstAsset <MonoBehaviourObject>(x => x.Object.Name == "DefaultEnvironmentSceneInfo"); } beatmapLevel.EnvironmentSceneInfo = environment.PtrFrom(beatmapLevel); _assetsFile.AddObject(beatmapLevel, true); return(beatmapLevel); } catch (Exception ex) { Log.LogErr($"Error loading song from path {songPath}", ex); return(null); } }
public static Bitmap ToBitmap(this Texture2DObject texture) { return(ImageUtils.TextureToBitmap(texture)); }