public GenericGraphicResource RequestGraphicResource(string path, bool async = false) { GenericGraphicResource resource; if (!cachedGraphics.TryGetValue(path, out resource)) { #if UNCOMPRESSED_CONTENT string pathAbsolute = PathOp.Combine(DualityApp.DataDirectory, "Animations", path); #else string pathAbsolute = PathOp.Combine(DualityApp.DataDirectory, "Main.dz", "Animations", path); #endif SpriteJson json; using (Stream s = FileOp.Open(pathAbsolute + ".res", FileAccessMode.Read)) { lock (jsonParser) { json = jsonParser.Parse<SpriteJson>(s); } } resource = new GenericGraphicResource { FrameDimensions = new Point2(json.FrameSize[0], json.FrameSize[1]), FrameConfiguration = new Point2(json.FrameConfiguration[0], json.FrameConfiguration[1]), FrameDuration = (json.FrameRate <= 0 ? -1 : (1f / json.FrameRate) * 5), FrameCount = json.FrameCount }; if (json.Hotspot != null) { resource.Hotspot = new Point2(json.Hotspot[0], json.Hotspot[1]); } if (json.Coldspot != null) { resource.Coldspot = new Point2(json.Coldspot[0], json.Coldspot[1]); resource.HasColdspot = true; } if (json.Gunspot != null) { resource.Gunspot = new Point2(json.Gunspot[0], json.Gunspot[1]); } PixelData pixelData; using (Stream s = FileOp.Open(pathAbsolute, FileAccessMode.Read)) { pixelData = new Png(s).GetPixelData(); } // Use external palette if ((json.Flags & 0x01) != 0x00) { ColorRgba[] palette = paletteTexture.Res.BasePixmap.Res.MainLayer.Data; ColorRgba[] data = pixelData.Data; #if !DISABLE_ASYNC Parallel.ForEach(Partitioner.Create(0, data.Length), range => { for (int i = range.Item1; i < range.Item2; i++) { #else for (int i = 0; i < data.Length; i++) { #endif int colorIdx = data[i].R; data[i] = palette[colorIdx].WithAlpha(palette[colorIdx].A * data[i].A / (255f * 255f)); // ToDo: Pinball sprites have strange palette (1-3 indices down), CandionV looks bad, other levels look different } #if !DISABLE_ASYNC }); #endif } bool linearSampling = (json.Flags & 0x02) != 0x00; Pixmap map = new Pixmap(pixelData); map.GenerateAnimAtlas(resource.FrameConfiguration.X, resource.FrameConfiguration.Y, 0); if (async) { GenericGraphicResourceAsyncFinalize asyncFinalize = new GenericGraphicResourceAsyncFinalize(); asyncFinalize.TextureMap = map; #if !DISABLE_NORMAL_MAPPING string filenameNormal = pathAbsolute.Replace(".png", ".n.png"); if (FileOp.Exists(filenameNormal)) { using (Stream s = FileOp.Open(filenameNormal, FileAccessMode.Read)) { asyncFinalize.TextureNormalMap = new Pixmap(new Png(s).GetPixelData()); } } else { resource.TextureNormal = defaultNormalMap; } #else resource.TextureNormal = defaultNormalMap; #endif asyncFinalize.LinearSampling = linearSampling; resource.AsyncFinalize = asyncFinalize; } else { TextureMagFilter magFilter; TextureMinFilter minFilter; if (linearSampling) { magFilter = TextureMagFilter.Linear; minFilter = TextureMinFilter.LinearMipmapLinear; } else { magFilter = TextureMagFilter.Nearest; minFilter = TextureMinFilter.Nearest; } resource.Texture = new Texture(map, TextureSizeMode.NonPowerOfTwo, magFilter, minFilter, json.TextureWrap, json.TextureWrap); #if !DISABLE_NORMAL_MAPPING string filenameNormal = pathAbsolute.Replace(".png", ".n.png"); if (FileOp.Exists(filenameNormal)) { using (Stream s = FileOp.Open(filenameNormal, FileAccessMode.Read)) { pixelData = new Png(s).GetPixelData(); } resource.TextureNormal = new Texture(new Pixmap(pixelData), TextureSizeMode.NonPowerOfTwo, magFilter, minFilter, json.TextureWrap, json.TextureWrap); resource.TextureNormal.Res.DetachSource(); } else { resource.TextureNormal = defaultNormalMap; } #else resource.TextureNormal = defaultNormalMap; #endif } cachedGraphics[path] = resource; } resource.Referenced = true; return resource; }