private void TidySegment(TexturedTileSegment segment, Dictionary <int, int> reindexMap) { for (int i = segment.Textures.Count - 1; i > 0; i--) //ignore the first = the largest { AbstractIndexedTRTexture texture = segment.Textures[i]; AbstractIndexedTRTexture candidateTexture = null; for (int j = 0; j < segment.Textures.Count; j++) { if (i == j) { continue; } AbstractIndexedTRTexture texture2 = segment.Textures[j]; if (texture.Equals(texture2)) { candidateTexture = texture2; break; } } if (candidateTexture != null) { reindexMap[texture.Index] = candidateTexture.Index; texture.Invalidate(); segment.Textures.RemoveAt(i); } } }
private List <int> GetSpriteTextureList(TexturedTileSegment segment) { List <int> indices = new List <int>(); foreach (AbstractIndexedTRTexture texture in segment.Textures) { if (texture is IndexedTRSpriteTexture) { indices.Add(texture.Index); } } return(indices); }
private int FindMatchingSegment(List <TexturedTileSegment> segments, TexturedTileSegment segment) { for (int i = 0; i < segments.Count; i++) { TexturedTileSegment otherSegment = segments[i]; if ( otherSegment.FirstTextureIndex == segment.FirstTextureIndex && otherSegment.FirstClassification == segment.FirstClassification ) { return(i); } } return(-1); }
public bool ShouldIgnoreSegment(TR2Entities entity, TexturedTileSegment segment) { if (IgnoreEntityTextures.ContainsKey(entity)) { if (IgnoreEntityTextures[entity].Count == 0) { return(true); } foreach (int textureIndex in IgnoreEntityTextures[entity]) { if (segment.IsObjectTextureFor(textureIndex)) { return(true); } } } return(false); }
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 Pack() { using (TexturePacker packer = new TexturePacker(Level)) { List <TR2Entities> removals = new List <TR2Entities>(); if (ClearUnusedSprites) { removals.Add(TR2Entities.Map_M_U); } if (EntitiesToRemove != null) { removals.AddRange(EntitiesToRemove); } packer.RemoveModelSegments(removals, TextureRemap); ApplyFlamePatch(); if (ClearUnusedSprites) { RemoveUnusedSprites(packer); } List <TexturedTileSegment> allSegments = new List <TexturedTileSegment>(); foreach (List <TexturedTileSegment> segmentList in _importSegments.Values) { // We only add unique segments, so if another segment already exists, // remap the definition's segment to that one. Example of when this is // needed is importing the dragon as DragonBack duplicates a lot of // DragonFront, so this will greatly reduce the import cost. for (int i = 0; i < segmentList.Count; i++) { TexturedTileSegment segment = segmentList[i]; int j = FindMatchingSegment(allSegments, segment); if (j == -1) { allSegments.Add(segment); } else { TexturedTileSegment otherSegment = allSegments[j]; segmentList[i] = allSegments[j]; foreach (IndexedTRObjectTexture texture in segment.Textures) { if (!otherSegment.IsObjectTextureFor(texture.Index)) { otherSegment.AddTexture(texture); } } } } } packer.AddRectangles(allSegments); packer.Pack(true); if (packer.OrphanedRectangles.Count > 0) { List <string> entityNames = new List <string>(); foreach (TRModelDefinition def in Definitions) { entityNames.Add(def.Entity.ToString()); } throw new PackingException(string.Format ( "Failed to pack {0} rectangles for model types [{1}].", packer.OrphanedRectangles.Count, string.Join(", ", entityNames) )); } } }
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); } } } } } } }