private void GenerateSpriteSequenceTargets(StaticTextureSource source) { if (!source.HasVariants) { throw new ArgumentException(string.Format("SpriteSequence {0} cannot be dynamically mapped without at least one source rectangle.", source.SpriteSequence)); } int i = _level.SpriteSequences.ToList().FindIndex(s => s.SpriteID == (int)source.SpriteSequence); if (i == -1) { return; } TRSpriteSequence sequence = _level.SpriteSequences[i]; StaticMapping[source] = new List <StaticTextureTarget>(); // An assumption is made here that each variant in the source will have the same number // of rectangles. We only want to define targets for the number of source rectangles, rather // than the total number of sprites. int numTargets = source.VariantMap[source.Variants[0]].Count; for (int j = 0; j < numTargets; j++) { TRSpriteTexture sprite = _level.SpriteTextures[sequence.Offset + j]; StaticMapping[source].Add(new StaticTextureTarget { Segment = j, Tile = sprite.Atlas, X = sprite.X, Y = sprite.Y }); } }
public Dictionary <TexturedTile, List <TexturedTileSegment> > GetSpriteSegments(TR2Entities entity) { Dictionary <TexturedTile, List <TexturedTileSegment> > segmentMap = new Dictionary <TexturedTile, List <TexturedTileSegment> >(); int i = Level.SpriteSequences.ToList().FindIndex(s => s.SpriteID == (int)entity); if (i != -1) { TRSpriteSequence sequence = Level.SpriteSequences[i]; List <int> indices = new List <int>(); for (int j = 0; j < sequence.NegativeLength * -1; j++) { indices.Add(sequence.Offset + j); } foreach (TexturedTile tile in _tiles) { List <TexturedTileSegment> segments = tile.GetSpriteTextureIndexSegments(indices); if (segments.Count > 0) { segmentMap[tile] = segments; } } } return(segmentMap); }
private void ApplyFlamePatch() { // TextureDeduplicator will have removed the extra flame blasts present in DA and Lair (Flamethrower and Dragon). // We need to ensure that if these models are present in any level, that the sprite sequences for the blasts point // to the same as the grenade blast instead. List <TR2Entities> flameEnemies = new List <TR2Entities> { TR2Entities.FlamethrowerGoon, TR2Entities.DragonExplosionEmitter_N }; if ( Definitions.ToList().FindIndex(d => flameEnemies.Contains(d.Entity)) != -1 || Level.Models.ToList().FindIndex(m => flameEnemies.Contains((TR2Entities)m.ID)) != -1 ) { List <TRSpriteSequence> sequences = Level.SpriteSequences.ToList(); int blastSequence = sequences.FindIndex(s => s.SpriteID == (int)TR2Entities.FireBlast_S_H); int grenadeSequence = sequences.FindIndex(s => s.SpriteID == (int)TR2Entities.Explosion_S_H); if (grenadeSequence != -1) { if (blastSequence == -1) { TRSpriteSequence grenadeBlast = sequences[grenadeSequence]; sequences.Add(new TRSpriteSequence { SpriteID = (int)TR2Entities.FireBlast_S_H, NegativeLength = grenadeBlast.NegativeLength, Offset = grenadeBlast.Offset }); Level.SpriteSequences = sequences.ToArray(); Level.NumSpriteSequences++; } else { Level.SpriteSequences[blastSequence].Offset = sequences[grenadeSequence].Offset; } } } }
private void MergeSpriteTextures() { List <TRSpriteTexture> levelSpriteTextures = Level.SpriteTextures.ToList(); List <TRSpriteSequence> levelSpriteSequences = Level.SpriteSequences.ToList(); foreach (TRModelDefinition definition in Definitions) { if (!_importSegments.ContainsKey(definition) || definition.SpriteSequences.Count == 0) { continue; } foreach (TR2Entities spriteEntity in definition.SpriteSequences.Keys) { TRSpriteSequence sequence = definition.SpriteSequences[spriteEntity]; sequence.Offset = -1; levelSpriteSequences.Add(sequence); foreach (int bitmapIndex in definition.SpriteTextures[spriteEntity].Keys) { List <IndexedTRSpriteTexture> textures = definition.SpriteTextures[spriteEntity][bitmapIndex]; for (int i = 0; i < textures.Count; i++) { if (sequence.Offset == -1) { // mark the position of the first sprite only sequence.Offset = (short)levelSpriteTextures.Count; } levelSpriteTextures.Add(textures[i].Texture); } } } } Level.SpriteTextures = levelSpriteTextures.ToArray(); Level.NumSpriteTextures = (uint)levelSpriteTextures.Count; Level.SpriteSequences = levelSpriteSequences.ToArray(); Level.NumSpriteSequences = (uint)levelSpriteSequences.Count; }
public override void Export() { List <TexturedTileSegment> allSegments = new List <TexturedTileSegment>(); using (TexturePacker levelPacker = new TexturePacker(Level, TextureClassifier)) { Dictionary <TexturedTile, List <TexturedTileSegment> > textureSegments = levelPacker.GetModelSegments(Definition.Entity); TRTextureDeduplicator deduplicator = new TRTextureDeduplicator { SegmentMap = textureSegments, UpdateGraphics = false }; deduplicator.Deduplicate(); Definition.ObjectTextures = new Dictionary <int, List <IndexedTRObjectTexture> >(); Definition.SpriteSequences = new Dictionary <TR2Entities, TRSpriteSequence>(); Definition.SpriteTextures = new Dictionary <TR2Entities, Dictionary <int, List <IndexedTRSpriteTexture> > >(); int bitmapIndex = 0; foreach (List <TexturedTileSegment> segments in textureSegments.Values) { for (int i = 0; i < segments.Count; i++) { TexturedTileSegment segment = segments[i]; if (!deduplicator.ShouldIgnoreSegment(Definition.Entity, segment)) { allSegments.Add(segment); Definition.ObjectTextures[bitmapIndex++] = new List <IndexedTRObjectTexture>(segment.Textures.Cast <IndexedTRObjectTexture>().ToArray()); } } } if (_entitySpriteDependencies.ContainsKey(Definition.Entity)) { foreach (TR2Entities spriteEntity in _entitySpriteDependencies[Definition.Entity]) { TRSpriteSequence sequence = Level.SpriteSequences.ToList().Find(s => s.SpriteID == (int)spriteEntity); if (sequence != null) { Definition.SpriteSequences[spriteEntity] = sequence; } Dictionary <TexturedTile, List <TexturedTileSegment> > spriteSegments = levelPacker.GetSpriteSegments(spriteEntity); Definition.SpriteTextures[spriteEntity] = new Dictionary <int, List <IndexedTRSpriteTexture> >(); foreach (List <TexturedTileSegment> segments in spriteSegments.Values) { for (int i = 0; i < segments.Count; i++) { TexturedTileSegment segment = segments[i]; allSegments.Add(segment); Definition.SpriteTextures[spriteEntity][bitmapIndex++] = new List <IndexedTRSpriteTexture>(segment.Textures.Cast <IndexedTRSpriteTexture>().ToArray()); } } } } if (allSegments.Count > 0) { using (TexturePacker segmentPacker = new TexturePacker()) { segmentPacker.AddRectangles(allSegments); segmentPacker.Options = new PackingOptions { FillMode = PackingFillMode.Horizontal, OrderMode = PackingOrderMode.Area, Order = PackingOrder.Descending, GroupMode = PackingGroupMode.Squares }; segmentPacker.TileWidth = _defaultBitmapWidth; segmentPacker.TileHeight = _defaultBitmapHeight; segmentPacker.MaximumTiles = 1; segmentPacker.Pack(); if (segmentPacker.OrphanedRectangles.Count > 0) { throw new PackingException(string.Format("Failed to export textures for {0}.", Definition.Entity)); } Definition.ObjectTextureCost = segmentPacker.TotalUsedSpace; TexturedTile tile = segmentPacker.Tiles[0]; List <Rectangle> rects = new List <Rectangle>(); foreach (TexturedTileSegment segment in allSegments) { rects.Add(segment.MappedBounds); } Definition.TextureSegments = rects.ToArray(); Rectangle region = tile.GetOccupiedRegion(); Definition.Bitmap = tile.BitmapGraphics.Extract(region); if (ExportIndividualSegments) { string dir = Path.Combine(SegmentsFolder, Definition.Alias.ToString()); if (Directory.Exists(dir)) { Directory.Delete(dir, true); } Directory.CreateDirectory(dir); foreach (TexturedTileSegment segment in allSegments) { segment.Bitmap.Save(Path.Combine(dir, segment.FirstTextureIndex + ".png"), ImageFormat.Png); } } } } else { Definition.ObjectTextureCost = 0; } } }
private void CollateSegments() { // Rebuild the segment list. We assume the list of IndexedTRObjectTextures has been // ordered by area descending to preserve the "master" texture for each segment. _importSegments = new Dictionary <TRModelDefinition, List <TexturedTileSegment> >(); // Track existing sprite sequences to avoid duplication List <TRSpriteSequence> spriteSequences = Level.SpriteSequences.ToList(); foreach (TRModelDefinition definition in Definitions) { if (!definition.HasGraphics || definition.IsDependencyOnly) { continue; } _importSegments[definition] = new List <TexturedTileSegment>(); using (BitmapGraphics bg = new BitmapGraphics(definition.Bitmap)) { foreach (int segmentIndex in definition.ObjectTextures.Keys) { Bitmap segmentClip = bg.Extract(definition.TextureSegments[segmentIndex]); TexturedTileSegment segment = null; foreach (IndexedTRObjectTexture texture in definition.ObjectTextures[segmentIndex]) { if (segment == null) { _importSegments[definition].Add(segment = new TexturedTileSegment(texture, segmentClip)); } else { segment.AddTexture(texture); } } } List <TR2Entities> spriteEntities = new List <TR2Entities>(definition.SpriteSequences.Keys); foreach (TR2Entities spriteEntity in spriteEntities) { TRSpriteSequence existingSequence = spriteSequences.Find(s => s.SpriteID == (int)spriteEntity); if (existingSequence != null) { definition.SpriteSequences.Remove(spriteEntity); continue; } else { // Add it to the tracking list in case we are importing 2 or more models // that share a sequence e.g. Dragon/Flamethrower and Flame_S_H spriteSequences.Add(new TRSpriteSequence { SpriteID = (int)spriteEntity }); } // The sequence will be merged later when we know the sprite texture offsets. // For now, add the segments we need for packing. Dictionary <int, List <IndexedTRSpriteTexture> > spriteTextures = definition.SpriteTextures[spriteEntity]; foreach (int segmentIndex in spriteTextures.Keys) { Bitmap segmentClip = bg.Extract(definition.TextureSegments[segmentIndex]); TexturedTileSegment segment = null; foreach (IndexedTRSpriteTexture texture in spriteTextures[segmentIndex]) { if (segment == null) { _importSegments[definition].Add(segment = new TexturedTileSegment(texture, segmentClip)); } else { segment.AddTexture(texture); } } } } } } }