private TRColour4[] PopulateColourPalette16(byte[] palette) { TRColour4[] colourPalette = new TRColour4[MAX_PALETTE_SIZE]; int ci = 0; for (int i = 0; i < MAX_PALETTE_SIZE; i++) { TRColour4 col = new TRColour4(); col.Red = palette[ci]; ci++; col.Green = palette[ci]; ci++; col.Blue = palette[ci]; ci++; col.Unused = palette[ci]; ci++; colourPalette[i] = col; } return(colourPalette); }
// #159 Using high indices as we were doing previously appears not to work in TR2Main, // so instead we will check for the next available palette slot and use that. Each level // seems to only have around 30-40 used colours. We still use the Unused field to track // what we are making use of during imports. public static int GetNextPaletteIndex(TR2Level lvl) { int highestUsedPalette = -1; foreach (TRMesh mesh in lvl.Meshes) { foreach (TRFace4 t in mesh.ColouredRectangles) { highestUsedPalette = Math.Max(highestUsedPalette, BitConverter.GetBytes(t.Texture)[1]); } foreach (TRFace3 t in mesh.ColouredTriangles) { highestUsedPalette = Math.Max(highestUsedPalette, BitConverter.GetBytes(t.Texture)[1]); } } while (highestUsedPalette < lvl.Palette16.Length - 1) { TRColour4 palette = lvl.Palette16[++highestUsedPalette]; if (palette.Unused == 0) { return(highestUsedPalette); } } return(-1); }
public static int Import(TR2Level lvl, TRColour4 c) { int nextAvailableIndex = GetNextPaletteIndex(lvl); if (nextAvailableIndex == -1) { return(-1); } lvl.Palette16[nextAvailableIndex] = c; c.Unused = 255; // Avoid anything else trying to use this index return(nextAvailableIndex); }
public override void Import() { List <TRColour4> palette16 = Level.Palette16.ToList(); Dictionary <int, int> indexMap = new Dictionary <int, int>(); foreach (int paletteIndex in Definition.Colours.Keys) { TRColour4 newColour = Definition.Colours[paletteIndex]; int existingIndex = palette16.FindIndex ( e => e.Red == newColour.Red && e.Green == newColour.Green && e.Blue == newColour.Blue// && e.Unused == newColour.Unused ); if (existingIndex != -1) { indexMap[paletteIndex] = existingIndex; } else { indexMap[paletteIndex] = P16Importer.Import(Level, newColour); } } foreach (TRMesh mesh in Definition.Meshes) { foreach (TRFace4 rect in mesh.ColouredRectangles) { rect.Texture = ReindexTexture(rect.Texture, indexMap); } foreach (TRFace3 tri in mesh.ColouredTriangles) { tri.Texture = ReindexTexture(tri.Texture, indexMap); } } P16Importer.ResetPaletteTracking(Level); }
public void RedrawStaticTargets(StaticTextureSource source, string variant, Dictionary <TextureCategory, bool> options) { if (source.Categories != null) { // Exclude it if any of its categories are in the options and switched off foreach (TextureCategory category in source.Categories) { if (options.ContainsKey(category) && !options[category]) { return; } } } // For sprite sequence sources, the targets are mapped dynamically. if (source.IsSpriteSequence && (!StaticMapping.ContainsKey(source) || StaticMapping[source].Count == 0)) { GenerateSpriteSequenceTargets(source); } // This can happen if we have a source grouped for this level, // but the source is actually only in place on certain conditions // - an example is the flame in Venice, which is only added if // the Flamethrower has been imported. if (!StaticMapping.ContainsKey(source)) { return; } List <Rectangle> segments = source.VariantMap[variant]; foreach (StaticTextureTarget target in StaticMapping[source]) { if (target.Segment < 0 || target.Segment >= segments.Count) { throw new IndexOutOfRangeException(string.Format("Segment {0} is invalid for texture source {1}.", target.Segment, source.PNGPath)); } GetBitmapGraphics(target.Tile).ImportSegment(source, target, segments[target.Segment]); } if (source.EntityColourMap != null) { foreach (TR2Entities entity in source.EntityColourMap.Keys) { TRMesh[] meshes = TR2LevelUtilities.GetModelMeshes(_level, entity); ISet <int> colourIndices = new HashSet <int>(); foreach (TRMesh mesh in meshes) { foreach (TRFace4 t in mesh.ColouredRectangles) { colourIndices.Add(BitConverter.GetBytes(t.Texture)[1]); } foreach (TRFace3 t in mesh.ColouredTriangles) { colourIndices.Add(BitConverter.GetBytes(t.Texture)[1]); } } Dictionary <int, int> remapIndices = new Dictionary <int, int>(); foreach (Color targetColour in source.EntityColourMap[entity].Keys) { int matchedIndex = -1; foreach (int currentIndex in colourIndices) { TRColour4 currentColour = _level.Palette16[currentIndex]; if (currentColour.Red == targetColour.R && currentColour.Green == targetColour.G && currentColour.Blue == targetColour.B) { matchedIndex = currentIndex; } } if (matchedIndex == -1) { continue; } // Extract the colour from the top-left of the rectangle specified in the source, and import that into the level int sourceRectangle = source.EntityColourMap[entity][targetColour]; int newColourIndex = P16Importer.Import(_level, source.Bitmap.GetPixel(segments[sourceRectangle].X, segments[sourceRectangle].Y)); remapIndices.Add(matchedIndex, newColourIndex); } // Remap the affected mesh textures to the newly inserted colours foreach (TRMesh mesh in meshes) { foreach (TRFace4 t in mesh.ColouredRectangles) { t.Texture = ConvertMeshTexture(t.Texture, remapIndices); } foreach (TRFace3 t in mesh.ColouredTriangles) { t.Texture = ConvertMeshTexture(t.Texture, remapIndices); } } } // Reset the palette tracking P16Importer.ResetPaletteTracking(_level); } }
public void ExportAllTexturesToHtml(string filePath) { using (TexturePacker packer = new TexturePacker(_level)) { StringBuilder tiles = new StringBuilder(); foreach (TexturedTile tile in packer.Tiles) { tiles.Append(string.Format("<div class=\"tile\" id=\"tile_{0}\">", tile.Index)); foreach (TexturedTileSegment segment in tile.Rectangles) { using (MemoryStream ms = new MemoryStream()) { segment.Bitmap.Save(ms, ImageFormat.Png); List <int> objectTextures = GetObjectTextureList(segment); List <int> spriteTextures = GetSpriteTextureList(segment); tiles.Append(string.Format("<img src=\"data:image/png;base64, {0}\" ", Convert.ToBase64String(ms.ToArray()))); tiles.Append(string.Format("style=\"top:{0}px;left:{1}px;width:{2}px;height:{3}px\" ", segment.Bounds.Y, segment.Bounds.X, segment.Bounds.Width, segment.Bounds.Height)); tiles.Append(string.Format("data-tile=\"{0}\" ", tile.Index)); tiles.Append(string.Format("data-rect=\"{0}\" ", RectangleToString(segment.Bounds))); List <string> objectData = new List <string>(); List <string> spriteData = new List <string>(); foreach (AbstractIndexedTRTexture texture in segment.Textures) { if (texture is IndexedTRObjectTexture) { objectData.Add(texture.Index + ": " + RectangleToString(texture.Bounds)); } else { spriteData.Add(texture.Index + ": " + RectangleToString(texture.Bounds)); } } if (objectData.Count > 0) { tiles.Append(string.Format("data-objects=\"{0}\" ", string.Join(";", objectData))); } if (spriteData.Count > 0) { tiles.Append(string.Format("data-sprites=\"{0}\" ", string.Join(";", spriteData))); } tiles.Append("/>"); } } tiles.Append("</div>"); } StringBuilder levelSel = new StringBuilder(); foreach (string lvl in LevelNames.AsList) { levelSel.Append("<option"); if (lvl.ToUpper() == filePath.ToUpper()) { levelSel.Append(" selected=\"selected\""); } levelSel.Append(" value=\"").Append(lvl).Append(".html\">").Append(lvl).Append("</option>"); } StringBuilder skyboxInfo = new StringBuilder(); Dictionary <int, TRColour4> skyColours = GetSkyBoxColours(); if (skyColours.Count > 0) { skyboxInfo.Append("<div><span>Palette #</span><span>RGB</span><span>Swatch</span></div>"); string rgbTpl = "<span>[{0}, {1}, {2}]</span>"; string swatchTpl = "<span style=\"background:rgb({0},{1},{2})\"> </span>";; foreach (int index in skyColours.Keys) { TRColour4 c = skyColours[index]; skyboxInfo.Append("<div class=\"body-row\"><span>").Append(index).Append("</span>"); skyboxInfo.Append(string.Format(rgbTpl, c.Red, c.Green, c.Blue)); skyboxInfo.Append(string.Format(swatchTpl, c.Red, c.Green, c.Blue)); skyboxInfo.Append("</div>"); } } string tpl = File.ReadAllText(@"Resources\TileTemplate.html"); tpl = tpl.Replace("{Title}", filePath); tpl = tpl.Replace("{Levels}", levelSel.ToString()); tpl = tpl.Replace("{Tiles}", tiles.ToString()); tpl = tpl.Replace("{SkyBox}", skyboxInfo.ToString()); File.WriteAllText(filePath + ".html", tpl); } }