Skip to content

Commit

Permalink
Support for creating VTF files
Browse files Browse the repository at this point in the history
  • Loading branch information
LogicAndTrick committed Apr 14, 2024
1 parent b9f7ac6 commit 975b676
Show file tree
Hide file tree
Showing 26 changed files with 1,384 additions and 78 deletions.
5 changes: 4 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ These C# libraries parse the formats you'll find in your Half-Life install direc
[![Nuget](https://img.shields.io/nuget/v/Sledge.Formats.Map?color=277ACE&label=Sledge.Formats.Map&logo=nuget)](https://www.nuget.org/packages/Sledge.Formats.Map/)
[![Nuget](https://img.shields.io/nuget/v/Sledge.Formats.Packages?color=9C23D3&label=Sledge.Formats.Packages&logo=nuget)](https://www.nuget.org/packages/Sledge.Formats.Packages/)
[![Nuget](https://img.shields.io/nuget/v/Sledge.Formats.Texture?color=06CCBB&label=Sledge.Formats.Texture&logo=nuget)](https://www.nuget.org/packages/Sledge.Formats.Texture/)
[![Nuget](https://img.shields.io/nuget/v/Sledge.Formats.Texture.ImageSharp?color=037F73&label=Sledge.Formats.Texture.ImageSharp&logo=nuget)](https://www.nuget.org/packages/Sledge.Formats.Texture.ImageSharp/)
[![Nuget](https://img.shields.io/nuget/v/Sledge.Formats.GameData?color=FF42D9&label=Sledge.Formats.GameData&logo=nuget)](https://www.nuget.org/packages/Sledge.Formats.GameData/)


Expand Down Expand Up @@ -51,7 +52,9 @@ These C# libraries parse the formats you'll find in your Half-Life install direc
- Quake 1's gfx.wad contains a lump called "CONCHARS", which has an invalid type. There's special logic to handle this lump.
- Vtf
- **VtfFile** - The VTF format used by the Source engine.
- Currently supports all formats that VtfLib supports (read only, not write)
- Currently supports all formats that VtfLib supports
- Sledge.Formats.Texture.ImageSharp - ImageSharp extensions to allow creation of textures
- Currently only supports VTF formats
- Sledge.Formats.GameData - Game data formats used by level editors
- Fgd - The FGD format used by Worldcraft, Valve Hammer Editor, JACK, TrenchBroom, Sledge, and other editors
- Source 1 & 2 FGDs will load as long as they are valid, but their use is not fully tested.
Expand Down
91 changes: 91 additions & 0 deletions Sledge.Formats.Texture.ImageSharp/PixelFormats/Rg88.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;

namespace Sledge.Formats.Texture.ImageSharp.PixelFormats;

public struct Rg88 : IPixel<Rg88>, IPackedVector<ushort>
{
public ushort PackedValue { get; set; }

public Rg88(Vector3 vector)
{
PackedValue = Pack(vector);
}

private static ushort Pack(Vector3 vector)
{
vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);

var r = (int)(vector.X * byte.MaxValue);
var g = (int)(vector.Y * byte.MaxValue);

return (ushort)(g << 8 | r);
}

public readonly PixelOperations<Rg88> CreatePixelOperations()
{
return new PixelOperations<Rg88>();
}

public void FromScaledVector4(Vector4 vector)
{
FromVector4(vector);
}

public readonly Vector4 ToScaledVector4()
{
return ToVector4();
}

public void FromVector4(Vector4 vector)
{
PackedValue = Pack(new Vector3(vector.X, vector.Y, vector.Z));
}

public readonly Vector4 ToVector4()
{
var r = (PackedValue & 0xFF);
var g = (PackedValue & 0xFF00) >> 8;
return new Vector4(r / (float) byte.MaxValue, g / (float) byte.MaxValue, 1, 1);
}

public readonly bool Equals(Rg88 other)
{
return PackedValue == other.PackedValue;
}

public readonly override bool Equals(object? obj)
{
return obj is Rg88 other && Equals(other);
}

public readonly override int GetHashCode()
{
return PackedValue.GetHashCode();
}

public static bool operator ==(Rg88 left, Rg88 right)
{
return left.Equals(right);
}

public static bool operator !=(Rg88 left, Rg88 right)
{
return !left.Equals(right);
}

public void FromArgb32(Argb32 source) => throw new NotImplementedException();
public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException();
public void FromBgr24(Bgr24 source) => throw new NotImplementedException();
public void FromBgra32(Bgra32 source) => throw new NotImplementedException();
public void FromAbgr32(Abgr32 source) => throw new NotImplementedException();
public void FromL8(L8 source) => throw new NotImplementedException();
public void FromL16(L16 source) => throw new NotImplementedException();
public void FromLa16(La16 source) => throw new NotImplementedException();
public void FromLa32(La32 source) => throw new NotImplementedException();
public void FromRgb24(Rgb24 source) => throw new NotImplementedException();
public void FromRgba32(Rgba32 source) => throw new NotImplementedException();
public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException();
public void FromRgb48(Rgb48 source) => throw new NotImplementedException();
public void FromRgba64(Rgba64 source) => throw new NotImplementedException();
}
96 changes: 96 additions & 0 deletions Sledge.Formats.Texture.ImageSharp/PixelFormats/Rgb565.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;

namespace Sledge.Formats.Texture.ImageSharp.PixelFormats;

public struct Rgb565 : IPixel<Rgb565>, IPackedVector<ushort>
{
public ushort PackedValue { get; set; }

public Rgb565(Vector3 vector)
{
PackedValue = Pack(vector);
}

private static ushort Pack(Vector3 vector)
{
vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One);

return (ushort)(
(((int)Math.Round(vector.Z * 31F) & 0x1F) << 11)
| (((int)Math.Round(vector.Y * 63F) & 0x3F) << 5)
| ((int)Math.Round(vector.X * 31F) & 0x1F)
);
}

public readonly Vector3 ToVector3() => new(
(PackedValue & 0x1F) * (1F / 31F),
((PackedValue >> 5) & 0x3F) * (1F / 63F),
((PackedValue >> 11) & 0x1F) * (1F / 31F)
);

public readonly PixelOperations<Rgb565> CreatePixelOperations()
{
return new PixelOperations<Rgb565>();
}

public void FromScaledVector4(Vector4 vector)
{
FromVector4(vector);
}

public readonly Vector4 ToScaledVector4()
{
return ToVector4();
}

public void FromVector4(Vector4 vector)
{
PackedValue = Pack(new Vector3(vector.X, vector.Y, vector.Z));
}

public readonly Vector4 ToVector4()
{
return new Vector4(ToVector3(), 1);
}

public readonly bool Equals(Rgb565 other)
{
return PackedValue == other.PackedValue;
}

public readonly override bool Equals(object? obj)
{
return obj is Rgb565 other && Equals(other);
}

public readonly override int GetHashCode()
{
return PackedValue.GetHashCode();
}

public static bool operator ==(Rgb565 left, Rgb565 right)
{
return left.Equals(right);
}

public static bool operator !=(Rgb565 left, Rgb565 right)
{
return !left.Equals(right);
}

public void FromArgb32(Argb32 source) => throw new NotImplementedException();
public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException();
public void FromBgr24(Bgr24 source) => throw new NotImplementedException();
public void FromBgra32(Bgra32 source) => throw new NotImplementedException();
public void FromAbgr32(Abgr32 source) => throw new NotImplementedException();
public void FromL8(L8 source) => throw new NotImplementedException();
public void FromL16(L16 source) => throw new NotImplementedException();
public void FromLa16(La16 source) => throw new NotImplementedException();
public void FromLa32(La32 source) => throw new NotImplementedException();
public void FromRgb24(Rgb24 source) => throw new NotImplementedException();
public void FromRgba32(Rgba32 source) => throw new NotImplementedException();
public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException();
public void FromRgb48(Rgb48 source) => throw new NotImplementedException();
public void FromRgba64(Rgba64 source) => throw new NotImplementedException();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>LogicAndTrick</Authors>
<Description>ImageSharp extensions for texture formats used by Quake, Half-Life, and the Source engine. Currently supported format is Source's VTF.</Description>
<Copyright>2024 LogicAndTrick</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/LogicAndTrick/sledge-formats</PackageProjectUrl>
<PackageIcon>sledge-logo.png</PackageIcon>
<RepositoryUrl>https://github.com/LogicAndTrick/sledge-formats</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>half-life quake source valve wad vtf</PackageTags>
<PackageReleaseNotes>Initial release</PackageReleaseNotes>
<Version>1.0.0</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BCnEncoder.Net.ImageSharp" Version="1.1.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Sledge.Formats.Texture\Sledge.Formats.Texture.csproj" />
</ItemGroup>

<ItemGroup>
<None Include="..\sledge-logo.png" Link="sledge-logo.png">
<PackagePath></PackagePath>
<Pack>True</Pack>
</None>
</ItemGroup>

</Project>
45 changes: 45 additions & 0 deletions Sledge.Formats.Texture.ImageSharp/VtfExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Sledge.Formats.Texture.Vtf;

namespace Sledge.Formats.Texture.ImageSharp;

public static class VtfExtensions
{
/// <summary>
/// Convert a vtf image to an ImageSharp image.
/// </summary>
public static Image<T> ToImage<T>(this VtfImage vtfImage) where T : unmanaged, IPixel<T>
{
using var conv = Image.LoadPixelData<Bgra32>(vtfImage.GetBgra32Data(), vtfImage.Width, vtfImage.Height);
return conv.CloneAs<T>();
}

/// <summary>
/// Convert a vtf file to an ImageSharp image. The largest image will be used, for the first face, and the first frame.
/// </summary>
public static Image<T> ToImage<T>(this VtfFile vtfFile) where T : unmanaged, IPixel<T>
{
return ToImage<T>(vtfFile.Images.OrderByDescending(x => x.Width).ThenBy(x => x.Frame).ThenBy(x => x.Face).First());
}

/// <summary>
/// Convert an ImageSharp image to a vtf image.
/// </summary>
public static VtfImage ToVtfImage(this Image image, VtfImageBuilderOptions? options = null)
{
var builder = new VtfImageBuilder(options ?? new VtfImageBuilderOptions());
return builder.CreateImage(image);
}

/// <summary>
/// Convert an ImageSharp image to a vtf file.
/// </summary>
public static VtfFile ToVtfFile(this Image image, VtfImageBuilderOptions? options = null)
{
var vtf = new VtfFile();
var builder = new VtfImageBuilder(options ?? new VtfImageBuilderOptions());
foreach (var img in builder.CreateImages(image)) vtf.AddImage(img);
return vtf;
}
}
Loading

0 comments on commit 975b676

Please sign in to comment.