Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
85df185
Add OoT3D FBX texture fixes and animation-only FBX export
oosh-designs Apr 6, 2026
adaad6e
Keep geometry in animation FBX exports
oosh-designs Apr 6, 2026
df80df6
Add Blender headless FBX exporter with default Blender path
oosh-designs Apr 6, 2026
de17ca4
Route FBX export through Blender when configured
oosh-designs Apr 6, 2026
ebc57b4
Fix Blender CLI args
oosh-designs Apr 6, 2026
8c7e556
Fix Blender exporter source file encoding breakage
oosh-designs Apr 6, 2026
7671d08
Use indexed UV coords in Blender temp GLB export
oosh-designs Apr 6, 2026
7538504
Fix Blender exporter syntax error
oosh-designs Apr 6, 2026
d1ddace
Make Blender temp GLB UV indices configurable
oosh-designs Apr 6, 2026
ba614ca
Fix UV remapping in glTF export and robust old UV fixer
oosh-designs Apr 7, 2026
31ee118
Add Blender intermediate package exporter
oosh-designs Apr 7, 2026
0dc87dc
Switch Blender FBX export to manifest intermediate path
oosh-designs Apr 7, 2026
d3a69ee
Fix intermediate exporter missing imports
oosh-designs Apr 7, 2026
b5a9004
Serialize Blender manifest with camelCase keys
oosh-designs Apr 7, 2026
aa7770f
Fix model-pose deformation and flip Blender UV V axis
oosh-designs Apr 7, 2026
859bdb2
Convert animation tracks from absolute local TRS to pose deltas
oosh-designs Apr 7, 2026
2c22cb6
Make FBX animation export include built actions and frame range
oosh-designs Apr 7, 2026
0697c26
Skip default GLB export when Blender FBX path is configured
oosh-designs Apr 7, 2026
5300364
Filter GLB from explicit export formats when Blender FBX exporter is …
oosh-designs Apr 7, 2026
83f230d
Make Blender FBX exporter public for cross-assembly format filtering
oosh-designs Apr 7, 2026
95189d7
added scale correction and texture selection improvments
oOsh89 Apr 9, 2026
a026a7d
added animation only to blenderheadless fbx
oOsh89 Apr 10, 2026
55950ec
added scale fixes, and first step of material overhal
oOsh89 Apr 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion FinModelUtility/Fin/Fin/src/image/Images.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public static IImageEncoder ConvertFinImageFormatToImageSharpEncoder(
LocalImageFormat.BMP => new BmpEncoder(),
LocalImageFormat.PNG => new PngEncoder {
SkipMetadata = true,
TransparentColorMode = PngTransparentColorMode.Clear,
TransparentColorMode = PngTransparentColorMode.Preserve,
},
LocalImageFormat.JPEG => new JpegEncoder() {
SkipMetadata = true,
Expand Down
309 changes: 309 additions & 0 deletions FinModelUtility/Fin/Fin/src/image/Images.cs.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using FastBitmapLib;

using fin.color;
using fin.image.formats;
using fin.io;
using fin.util.asserts;

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats;

using Color = System.Drawing.Color;
using Image = SixLabors.ImageSharp.Image;
using Rectangle = System.Drawing.Rectangle;

namespace fin.image;

public static class FinImage {
public static bool IsSupportedFileType(IReadOnlyTreeFile file) {
var extension = file.FileType.ToLower()[1..];
return ImageSharpConfig.ImageFormats.Any(format
=> format.FileExtensions
.Any(otherExtension =>
extension ==
otherExtension));
}

public static IImage FromFile(IReadOnlyGenericFile file) {
try {
using var stream = file.OpenRead();
return FromStream(stream);
} catch (Exception e) {
throw new Exception($"Failed to load image \"{file}\"!", e);
}
}

public static async Task<IImage> FromFileAsync(IReadOnlyGenericFile file) {
await using var stream = file.OpenRead();
return await FromStreamAsync(stream);
}

public static IImage[] FromGifFile(IReadOnlyGenericFile file) {
try {
using var stream = file.OpenRead();
return FromGifStream(stream);
} catch (Exception e) {
throw new Exception($"Failed to load image \"{file}\"!", e);
}
}

public static async Task<IImage[]>
FromGifFileAsync(IReadOnlyGenericFile file) {
await using var stream = file.OpenRead();
return await FromGifStreamAsync(stream);
}

public static Configuration ImageSharpConfig { get; }

static FinImage() {
ImageSharpConfig = Configuration.Default.Clone();
ImageSharpConfig.PreferContiguousImageBuffers = true;
}

public static IImage FromStream(Stream stream) {
var imageTask = FromStreamAsync(stream);
imageTask.Wait();
return imageTask.Result;
}

public static async Task<IImage> FromStreamAsync(Stream stream) {
var decoderOptions =
new DecoderOptions { Configuration = ImageSharpConfig };
var image = Image.Load(decoderOptions, stream);
return FromImageSharpImage_(image);
}

public static IImage[] FromGifStream(Stream stream) {
var imageTask = FromGifStreamAsync(stream);
imageTask.Wait();
return imageTask.Result;
}

public static async Task<IImage[]> FromGifStreamAsync(Stream stream) {
var decoderOptions =
new DecoderOptions { Configuration = ImageSharpConfig };
var mergedFramesImage = Image.Load(decoderOptions, stream);
var mergedFrames = mergedFramesImage.Frames;

var separateImages = new IImage[mergedFrames.Count];
for (var i = 0; i < separateImages.Length; ++i) {
var frameImage = i < separateImages.Length - 1
? mergedFrames.ExportFrame(0)
: mergedFramesImage;

separateImages[i] = FromImageSharpImage_(frameImage);
}

return separateImages;
}

private static IImage FromImageSharpImage_(Image image) {
var pixelFormat = image.GetType().GenericTypeArguments[0];
if (pixelFormat == typeof(Rgba32)) {
return new Rgba32Image(PixelFormat.RGBA8888,
Asserts.CastNonnull(image as Image<Rgba32>));
}

if (pixelFormat == typeof(Rgb24)) {
return new Rgb24Image(PixelFormat.RGB888,
Asserts.CastNonnull(image as Image<Rgb24>));
}

if (pixelFormat == typeof(L8)) {
return new L8Image(PixelFormat.L8,
Asserts.CastNonnull(image as Image<L8>));
}

if (pixelFormat == typeof(La16)) {
return new La16Image(PixelFormat.LA88,
Asserts.CastNonnull(image as Image<La16>));
}

throw new ArgumentOutOfRangeException(
nameof(pixelFormat),
pixelFormat,
null);
}

public static IImage Create1x1FromColor(Color color)
=> CreateFromColor(color, 1, 1);

public static unsafe IImage CreateFromColor(
Color color,
int width,
int height) {
var bmp = new Rgba32Image(PixelFormat.RGBA8888, width, height);

using var imageLock = bmp.Lock();
imageLock.Pixels.Fill(new Rgba32(color.R, color.G, color.B, color.A));

return bmp;
}

public static IImageEncoder ConvertFinImageFormatToImageSharpEncoder(
LocalImageFormat imageFormat)
=> imageFormat switch {
LocalImageFormat.BMP => new BmpEncoder(),
LocalImageFormat.PNG => new PngEncoder {
SkipMetadata = true,
TransparentColorMode = PngTransparentColorMode.Clear,
},
LocalImageFormat.JPEG => new JpegEncoder() {
SkipMetadata = true,
},
LocalImageFormat.GIF => new GifEncoder(),
LocalImageFormat.TGA => new TgaEncoder(),
LocalImageFormat.WEBP => new WebpEncoder {
SkipMetadata = true,
FileFormat = WebpFileFormatType.Lossless,
},
_ => throw new ArgumentOutOfRangeException(
nameof(imageFormat),
imageFormat,
null)
};

public static unsafe Bitmap ConvertToBitmap(IImage image) {
var width = image.Width;
var height = image.Height;

var bitmap = new Bitmap(width,
height,
System.Drawing.Imaging.PixelFormat
.Format32bppArgb);
using var fastBitmap = bitmap.FastLock();
var dstPtr = (int*) fastBitmap.Scan0;

switch (image) {
case Rgba32Image rgba32Image: {
using var imageLock = rgba32Image.Lock();
var srcPtr = imageLock.Pixels;
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var index = y * width + x;
var rgba = srcPtr[index];
dstPtr[index] =
FinColor.MergeBgra(rgba.R, rgba.G, rgba.B, rgba.A);
}
}

break;
}
case Rgb24Image rgb24Image: {
using var imageLock = rgb24Image.Lock();
var srcPtr = imageLock.Pixels;
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var index = y * width + x;
var rgb = srcPtr[index];
dstPtr[index] = FinColor.MergeBgra(rgb.R, rgb.G, rgb.B, 255);
}
}

break;
}
case L8Image i8Image: {
using var imageLock = i8Image.Lock();
var srcPtr = imageLock.Pixels;
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var index = y * width + x;
var i = srcPtr[index].PackedValue;
dstPtr[index] = FinColor.MergeBgra(i, i, i, 255);
}
}

break;
}
default: {
image.Access(getHandler => {
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
getHandler(
x,
y,
out var r,
out var g,
out var b,
out var a);

var index = y * width + x;
dstPtr[index] = FinColor.MergeBgra(r, g, b, a);
}
}
});
break;
}
}

return bitmap;
}

public delegate void GetHandler<TPixel>(int x,
int y,
out TPixel pixel)
where TPixel : unmanaged, IPixel<TPixel>;

public delegate void AccessHandler<TPixel>(GetHandler<TPixel> getHandler)
where TPixel : unmanaged, IPixel<TPixel>;

public static unsafe void Access<TPixel>(Image<TPixel> image,
AccessHandler<TPixel>
accessHandler)
where TPixel : unmanaged, IPixel<TPixel> {
var frame = Asserts.CastNonnull(image.Frames[0]);
Asserts.True(frame.DangerousTryGetSinglePixelMemory(out var memory));

using var memoryHandle = memory.Pin();

var ptr = (TPixel*) memoryHandle.Pointer;

void GetHandler(int x, int y, out TPixel pixel)
=> pixel = ptr[y * frame.Width + x];

accessHandler(GetHandler);
}


public delegate void SetHandler<TPixel>(int x,
int y,
TPixel pixel)
where TPixel : unmanaged, IPixel<TPixel>;

public delegate void MutateHandler<TPixel>(GetHandler<TPixel> getHandler,
SetHandler<TPixel> setHandler)
where TPixel : unmanaged, IPixel<TPixel>;

public static unsafe void Mutate<TPixel>(Image<TPixel> image,
MutateHandler<TPixel>
mutateHandler)
where TPixel : unmanaged, IPixel<TPixel> {
var frame = Asserts.CastNonnull(image.Frames[0]);
Asserts.True(frame.DangerousTryGetSinglePixelMemory(out var memory));

using var memoryHandle = memory.Pin();

var ptr = (TPixel*) memoryHandle.Pointer;

void GetHandler(int x, int y, out TPixel pixel)
=> pixel = ptr[y * frame.Width + x];

void SetHandler(int x, int y, TPixel pixel)
=> ptr[y * frame.Width + x] = pixel;

mutateHandler(GetHandler, SetHandler);
}
}
Loading