mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-04-03 11:37:57 -06:00
Texpack is a C# tool to build textures from the game art repository. Texpack takes a json as input. The json tells Texpack where the art is, what art to pack, and how to pack it. See texpack/texjson.py for input json generation, json structure, and Texpack customization options. Texpack outputs a json that maps each image filename to information such as what texture the image was packed in where it is within the texture.
220 lines
9.5 KiB
C#
220 lines
9.5 KiB
C#
using System;
|
|
using SpiffCode;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using SpiffLib;
|
|
using System.Linq;
|
|
|
|
namespace SpiffCode {
|
|
|
|
class MainClass {
|
|
|
|
public static void Main(string[] args) {
|
|
|
|
if (args.Count() < 1 || args.Count() > 2) {
|
|
Console.WriteLine("Usage: <in json> <out dir>");
|
|
return;
|
|
}
|
|
|
|
string outdir = args[1];
|
|
string infile = System.IO.File.ReadAllText(args[0]);
|
|
InputJsonObject dict = (InputJsonObject)Json.DeserializeJson<InputJsonObject>(infile);
|
|
|
|
// outtrcs is used later to generate an output json that
|
|
// includes info on all the images in all the packers
|
|
|
|
List<TexRect> outtrcs = new List<TexRect>();
|
|
|
|
foreach (InputJsonObject.Packer packerinfo in dict.packers) {
|
|
Console.WriteLine("Processing: {0}", packerinfo.name);
|
|
|
|
Packer packer = new Packer(
|
|
packerinfo.name,
|
|
packerinfo.width,
|
|
packerinfo.height,
|
|
packerinfo.search_resolution,
|
|
packerinfo.sort_type
|
|
);
|
|
|
|
// When a trc is added to packer, a black_ variant will
|
|
// be added to packerBlack. Since packerBlack is compiled
|
|
// with the same specs and same number of trcs with the same
|
|
// size, each image's black_ variant should be packed in the
|
|
// same location. This allows the result of the packerBlack to
|
|
// essentially be a black_ for the entire result of packer
|
|
// NOTE: if this process fails, fcanPackBlackPacker should get set to false
|
|
|
|
Packer packerBlack = new Packer(
|
|
String.Format("black_{0}", packerinfo.name),
|
|
packerinfo.width,
|
|
packerinfo.height,
|
|
packerinfo.search_resolution,
|
|
packerinfo.sort_type
|
|
);
|
|
bool fcanPackBlackPacker = true;
|
|
|
|
foreach (InputJsonObject.Packer.Image imageinfo in packerinfo.images) {
|
|
|
|
// Paths should be relative to the art dir
|
|
|
|
string path = string.Format("{0}/{1}", dict.art_dir, imageinfo.path);
|
|
string pathBlack = null;
|
|
if (imageinfo.black != null)
|
|
pathBlack = string.Format("{0}/{1}", dict.art_dir, imageinfo.black);
|
|
|
|
// Load the bitmaps
|
|
|
|
if (!File.Exists(path)) {
|
|
Console.WriteLine("{0} does not exist!", path);
|
|
throw new Exception("Requested bitmap does not exist");
|
|
}
|
|
Bitmap bm = new Bitmap(path);
|
|
Bitmap bmBlack = null;
|
|
if (File.Exists(pathBlack)) {
|
|
bmBlack = new Bitmap(pathBlack);
|
|
}
|
|
|
|
// Scale BEFORE mapping transparency
|
|
|
|
if (imageinfo.scale != 1.0) {
|
|
|
|
// NOTE: I think this scaling method was designed for 824 art
|
|
|
|
bm = TBitmapTools.ScaleBitmap(bm, imageinfo.scale, null);
|
|
if (bmBlack != null)
|
|
bmBlack = TBitmapTools.ScaleBitmap(bmBlack, imageinfo.scale, null);
|
|
}
|
|
|
|
// Old 8 bit art needs some processing
|
|
|
|
if (imageinfo.black == "8bit") {
|
|
bm = BitmapTools.MapBitmapTransparency(bm);
|
|
bm = BitmapTools.MapBitmapShadow(bm, dict.shadow_8bit);
|
|
bmBlack = BitmapTools.CreateBlackBitmap(bm);
|
|
|
|
// HACK: Shift the hue up by 18 so that it
|
|
// "matches" the 2432 hue and can be hued with
|
|
// along with the 2432 art
|
|
|
|
Bitmap bmSub = SideMap.Subtract(bm, bmBlack);
|
|
Bitmap bmHew = SideMap.ShiftHue(bmSub, 18);
|
|
bm = SideMap.Add(bmHew, bmBlack);
|
|
}
|
|
|
|
if (bmBlack != null) {
|
|
// It's okay to not have a black_
|
|
// But if we do, it better be the right size
|
|
if (bm.Width != bmBlack.Width || bm.Height != bmBlack.Height)
|
|
throw new Exception("Bitmap black_ must be the same size as the bitmap");
|
|
} else {
|
|
// A missing black means that a black_ variant
|
|
// of the packer's result can't be created
|
|
fcanPackBlackPacker = false;
|
|
}
|
|
|
|
// Caulcaute atlases
|
|
|
|
List<int> atlases = new List<int>();
|
|
if (packerinfo.hue_variants != null)
|
|
foreach (InputJsonObject.Packer.HueVariant hv in packerinfo.hue_variants)
|
|
atlases.Add(hv.index);
|
|
|
|
if (atlases.Count != 0 && atlases.Count != 3) {
|
|
Console.WriteLine("{0} hue variations found for {1}", atlases.Count, packerinfo.name);
|
|
Console.WriteLine("Atlas should have exacly 0 variations or exactly 3 hue variations");
|
|
throw new Exception("Incorrect number of hue variations for atlas");
|
|
}
|
|
|
|
if (atlases.Count == 3 && packerinfo.grayscale.name == null) {
|
|
Console.WriteLine("{0} must have grayscale (for side neutral)", packerinfo.name);
|
|
throw new Exception("Missing grayscale entry for sidemapped packer");
|
|
}
|
|
|
|
// 0 means the "base index" is used for all sides
|
|
|
|
if (atlases.Count == 0) {
|
|
for (int i = 0; i < 5; i++)
|
|
atlases.Add(packerinfo.index);
|
|
} else { // if 3
|
|
atlases.Add(packerinfo.index);
|
|
atlases.Add(packerinfo.grayscale.index);
|
|
}
|
|
|
|
// Sort atlases assuming that the index values for side0 to side4 are correspondingly smallest to largest
|
|
|
|
atlases.Sort();
|
|
|
|
// Create trc
|
|
|
|
TexRect trc = new TexRect(imageinfo.name, bm, atlases.ToArray());
|
|
Rectangle rcClip = trc.CropTransparent();
|
|
packer.AddRect(trc);
|
|
|
|
// Force the black to be cropped with the same rect
|
|
// This keeps the black from getting a different size
|
|
// in the event that it has different sized margins
|
|
|
|
// Only bother to do this if we can still pack the black packer
|
|
|
|
if (bmBlack != null & fcanPackBlackPacker) {
|
|
TexRect trcBlack = new TexRect(String.Format("black_{0}", imageinfo.name), bmBlack, atlases.ToArray());
|
|
trcBlack.CropTransparent(rcClip);
|
|
packerBlack.AddRect(trcBlack);
|
|
}
|
|
|
|
// Queue trc for the out json
|
|
|
|
outtrcs.Add(trc);
|
|
}
|
|
|
|
// Compile the packer and output stats
|
|
|
|
int rectsFailed = packer.Compile();
|
|
Console.WriteLine("{0} rects not packed: {1}", packer.Name, rectsFailed);
|
|
Console.WriteLine("{0} search steps spent: {1}", packer.Name, packer.SearchSteps);
|
|
Bitmap bmp = packer.Image();
|
|
Bitmap bmpBlack = null;
|
|
|
|
// Go ahead and save
|
|
|
|
bmp.Save(String.Format("{0}/{1}", outdir, packer.Name), ImageFormat.Png);
|
|
|
|
// Only build the black packer if needed
|
|
|
|
if (fcanPackBlackPacker && packerinfo.hue_variants != null) {
|
|
packerBlack.Compile();
|
|
bmpBlack = packerBlack.Image();
|
|
|
|
// hue shifts: subtract black, shift hue, add black, save
|
|
|
|
Bitmap bmpSub = SideMap.Subtract(bmp, bmpBlack);
|
|
foreach (InputJsonObject.Packer.HueVariant hv in packerinfo.hue_variants) {
|
|
Bitmap bmpNewHue = SideMap.ShiftHue(bmpSub, hv.hue);
|
|
Bitmap bmpResult = SideMap.Add(bmpNewHue, bmpBlack);
|
|
bmpResult.Save(String.Format("{0}/{1}", outdir, hv.name), ImageFormat.Png);
|
|
}
|
|
|
|
// grayscale
|
|
|
|
if (packerinfo.grayscale.name != null) {
|
|
Bitmap bmpGray = BitmapTools.CreateGrayscale(bmpSub);
|
|
Bitmap bmpResult = SideMap.Add(bmpGray, bmpBlack);
|
|
bmpResult.Save(String.Format("{0}/{1}", outdir, packerinfo.grayscale.name), ImageFormat.Png);
|
|
}
|
|
|
|
} else if (packerinfo.hue_variants != null) {
|
|
Console.WriteLine("Can't create variant(s) for {0} becuase a black_ is missing", packerinfo.name);
|
|
throw new Exception("Missing black_ in packer that has hue variants");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("Saving atlasmap.json");
|
|
string json = Json.SerializeObject<Dictionary<string, TexRectJsonEntry>>(Json.CreateEntries(outtrcs));
|
|
File.WriteAllText(String.Format("{0}/atlasmap.json", outdir), json);
|
|
}
|
|
|
|
} // class MainClass
|
|
} // namespace SpiffCode
|