Abstract Unity Reia frame reading.

This commit is contained in:
LazyDuchess 2022-12-15 04:26:42 -03:00
parent 5eba4383cc
commit c44b8139cf
12 changed files with 353 additions and 199 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c117ec6c60008f744b962efd1780388c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 58087d4f971645047a3271e1ed9f6013
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 017ce228a1793614eb51163720fbc28e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,224 @@
using OpenTS2.Engine;
using OpenTS2.Files.Utils;
using System;
using System.Collections.Generic;
using UnityEngine;
using OpenTS2.Files.Formats.Reia;
namespace OpenTS2.Engine.Files.Formats.Reia
{
/// <summary>
/// Unity Reia reader.
/// </summary>
public class UnityReiaFrame : ReiaFrame
{
public Texture2D Image => _image;
private readonly Texture2D _image;
public UnityReiaFrame(Texture2D Image)
{
_image = Image;
}
static IEnumerable<ReiaFrame> ReadFrameEnumerableInternal(IoBuffer io, int width, int height, bool disposePreviousFrames)
{
ReiaFrame previousFrame = null;
var frameMagic = io.ReadCString(4);
while (io.HasMore && frameMagic != "")
{
if (frameMagic != "frme")
{
throw new Exception("Magic in start of frame doesn't equal frme");
}
var frameSize = io.ReadUInt32();
var currentFrame = (previousFrame as UnityReiaFrame)?.Image;
if (!disposePreviousFrames || currentFrame == null)
currentFrame = new Texture2D(width, height);
var frame = ReadSingleFrame(io, width, height, (previousFrame as UnityReiaFrame)?.Image, currentFrame);
/*
if (disposePreviousFrames)
previousFrame?.Dispose();*/
previousFrame = frame;
yield return frame;
var padding = frameSize % 2;
io.Skip(padding);
frameMagic = io.ReadCString(4);
}
}
public static IEnumerable<ReiaFrame> UnityReadFrameEnumerable(IoBuffer io, int width, int height)
{
return ReadFrameEnumerableInternal(io, width, height, true);
}
public static List<ReiaFrame> UnityReadFrames(IoBuffer io, int width, int height)
{
var frameStream = ReadFrameEnumerableInternal(io, width, height, false);
var frameList = new List<ReiaFrame>(frameStream);
return frameList;
}
static Color32 ReadSinglePixel(IoBuffer io)
{
var bytes = io.ReadBytes(3);
return new Color32(bytes[2], bytes[1], bytes[0], 255);
}
static Texture2D ReadBlock(IoBuffer io, int blockWidth, int blockHeight)
{
var numPixels = 32 * 32;
// 3 bytes for the RGB channels per pixel.
var imgData = new Color32[numPixels];
var i = 0;
while (i < numPixels)
{
var rleValue = io.ReadSByte();
if (rleValue < 0)
{
// Negative RLE value means we are going to be repeating the next
// color -n times.
var numRepeats = -rleValue;
var pixel = ReadSinglePixel(io);
for (var j = 0; j <= numRepeats; j++)
{
imgData[i] = pixel;
i++;
}
}
else
{
// Positive RLE value means we are going to be getting n unique
// pixels.
var numUniquePixels = rleValue;
for (var j = 0; j <= numUniquePixels; j++)
{
var pixel = ReadSinglePixel(io);
imgData[i] = pixel;
i++;
}
}
}
if (blockWidth != 32 || blockHeight != 32)
{
var texTemp = new Texture2D(32, 32);
texTemp.SetPixels32(imgData);
texTemp.Apply();
var trimmedImgData = texTemp.GetPixels(0, 0, blockWidth, blockHeight);
texTemp.Free();
var tex = new Texture2D(blockWidth, blockHeight);
tex.SetPixels(trimmedImgData);
tex.Apply();
return tex;
}
else
{
var tex = new Texture2D(blockWidth, blockHeight);
tex.SetPixels32(0, 0, blockWidth, blockHeight, imgData);
tex.Apply();
return tex;
}
}
static void AddTextures(Texture2D source, Texture2D destination)
{
var destinationPixels = destination.GetPixels32();
var sourcePixels = source.GetPixels32();
for (var i = 0; i < destinationPixels.Length; i++)
{
var newPixel = destinationPixels[i];
newPixel.r += sourcePixels[i].r;
newPixel.g += sourcePixels[i].g;
newPixel.b += sourcePixels[i].b;
destinationPixels[i] = newPixel;
}
destination.SetPixels32(destinationPixels);
destination.Apply();
}
static ReiaFrame ReadSingleFrame(IoBuffer io, int width, int height, Texture2D previousFrame, Texture2D image)
{
var maxJ = (int)Mathf.Ceil((float)width / 32);
var maxI = (int)Mathf.Ceil((float)height / 32);
Texture2D smallBlock = null;
image.wrapMode = TextureWrapMode.Clamp;
for (var i = 0; i < maxI; i++)
{
for (var j = 0; j < maxJ; j++)
{
var x = (j * 32);
var y = (i * 32);
var xBound = x + 32;
var yBound = y + 32;
var blockWidth = 32;
var blockHeight = 32;
if (xBound > image.width)
blockWidth -= xBound - image.width;
if (yBound > image.height)
blockHeight -= yBound - image.height;
smallBlock = new Texture2D(blockWidth, blockHeight);
// First byte tells us if we should expect a new 32x32 pixel block or re-use
// the one from the previous frame.
var newFrame = io.ReadByte() != 0;
if (newFrame)
{
var block = ReadBlock(io, blockWidth, blockHeight);
if (previousFrame != null)
{
var previousPixels = previousFrame.GetPixels(x, y, blockWidth, blockHeight);
smallBlock.SetPixels(previousPixels);
smallBlock.Apply();
AddTextures(smallBlock, block);
}
image.SetPixels32(x, y, blockWidth, blockHeight, block.GetPixels32());
block.Free();
}
else
{
if (previousFrame == null)
{
throw new Exception("Tried to re-use previous 32x32 block but it's null");
}
var previousPixels = previousFrame.GetPixels(x, y, blockWidth, blockHeight);
smallBlock.SetPixels(previousPixels);
smallBlock.Apply();
image.SetPixels32(x, y, blockWidth, blockHeight, smallBlock.GetPixels32());
}
if (smallBlock != null)
smallBlock.Free();
}
}
image.Apply();
return new UnityReiaFrame(image);
}
public override void Dispose()
{
if (_image != null)
{
_image.Free();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5fbcd77a94fb6b41958097193fd6417
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTS2.Files.Formats.Reia;
using OpenTS2.Files.Utils;
namespace OpenTS2.Engine.Files.Formats.Reia
{
/// <summary>
/// Unity implementation of Reia video files.
/// </summary>
class UnityReiaImplementation : ReiaImplementation
{
public override IEnumerable<ReiaFrame> ReadFrameEnumerable(IoBuffer io, int width, int height)
{
return UnityReiaFrame.UnityReadFrameEnumerable(io, width, height);
}
public override List<ReiaFrame> ReadFrames(IoBuffer io, int width, int height)
{
return UnityReiaFrame.UnityReadFrames(io, width, height);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07b91bc412d0dea4cbefd9de86962095
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,6 +1,7 @@
using OpenTS2.Assemblies;
using OpenTS2.Client;
using OpenTS2.Content;
using OpenTS2.Engine.Files.Formats.Reia;
using OpenTS2.Files;
using OpenTS2.Files.Formats.DBPF;
using UnityEngine;
@ -26,6 +27,7 @@ namespace OpenTS2.Engine
var contentManager = new ContentManager();
var objectManager = new ObjectManager(contentManager.Provider);
var logger = new UnityLogger();
var reiaImplementation = new UnityReiaImplementation();
Filesystem.Initialize(new JSONPathProvider(), epManager);
Factories.TextureFactory = new TextureFactory();
CodecAttribute.Initialize();

View file

@ -8,6 +8,7 @@ using UnityEngine.UI;
using OpenTS2.Files;
using OpenTS2.Files.Formats.Reia;
using System.Diagnostics;
using OpenTS2.Engine.Files.Formats.Reia;
namespace OpenTS2.Engine.Tests
{
@ -61,7 +62,7 @@ namespace OpenTS2.Engine.Tests
{
reia.MoveNextFrame();
}
image.texture = reia.GetCurrentFrame().Image;
image.texture = (reia.GetCurrentFrame() as UnityReiaFrame).Image;
}
}
}

View file

@ -8,213 +8,19 @@ namespace OpenTS2.Files.Formats.Reia
{
public class ReiaFrame : IDisposable
{
public Texture2D Image => _image;
private readonly Texture2D _image;
public ReiaFrame(Texture2D Image)
public virtual void Dispose()
{
_image = Image;
}
static IEnumerable<ReiaFrame> ReadFrameEnumerableInternal(IoBuffer io, int width, int height, bool disposePreviousFrames)
{
ReiaFrame previousFrame = null;
var frameMagic = io.ReadCString(4);
while (io.HasMore && frameMagic != "")
{
if (frameMagic != "frme")
{
throw new Exception("Magic in start of frame doesn't equal frme");
}
var frameSize = io.ReadUInt32();
var currentFrame = previousFrame?.Image;
if (!disposePreviousFrames || currentFrame == null)
currentFrame = new Texture2D(width, height);
var frame = ReadSingleFrame(io, width, height, previousFrame?.Image, currentFrame);
/*
if (disposePreviousFrames)
previousFrame?.Dispose();*/
previousFrame = frame;
yield return frame;
var padding = frameSize % 2;
io.Skip(padding);
frameMagic = io.ReadCString(4);
}
}
public static IEnumerable<ReiaFrame> ReadFrameEnumerable(IoBuffer io, int width, int height)
{
return ReadFrameEnumerableInternal(io, width, height, true);
return ReiaImplementation.Get().ReadFrameEnumerable(io, width, height);
}
public static List<ReiaFrame> ReadFrames(IoBuffer io, int width, int height)
{
var frameStream = ReadFrameEnumerableInternal(io, width, height, false);
var frameList = new List<ReiaFrame>(frameStream);
return frameList;
}
static Color32 ReadSinglePixel(IoBuffer io)
{
var bytes = io.ReadBytes(3);
return new Color32(bytes[2], bytes[1], bytes[0], 255);
}
static Texture2D ReadBlock(IoBuffer io, int blockWidth, int blockHeight)
{
var numPixels = 32 * 32;
// 3 bytes for the RGB channels per pixel.
var imgData = new Color32[numPixels];
var i = 0;
while (i < numPixels)
{
var rleValue = io.ReadSByte();
if (rleValue < 0)
{
// Negative RLE value means we are going to be repeating the next
// color -n times.
var numRepeats = -rleValue;
var pixel = ReadSinglePixel(io);
for (var j = 0; j <= numRepeats; j++)
{
imgData[i] = pixel;
i++;
}
}
else
{
// Positive RLE value means we are going to be getting n unique
// pixels.
var numUniquePixels = rleValue;
for (var j = 0; j <= numUniquePixels; j++)
{
var pixel = ReadSinglePixel(io);
imgData[i] = pixel;
i++;
}
}
}
if (blockWidth != 32 || blockHeight != 32)
{
var texTemp = new Texture2D(32, 32);
texTemp.SetPixels32(imgData);
texTemp.Apply();
var trimmedImgData = texTemp.GetPixels(0, 0, blockWidth, blockHeight);
texTemp.Free();
var tex = new Texture2D(blockWidth, blockHeight);
tex.SetPixels(trimmedImgData);
tex.Apply();
return tex;
}
else
{
var tex = new Texture2D(blockWidth, blockHeight);
tex.SetPixels32(0, 0, blockWidth, blockHeight, imgData);
tex.Apply();
return tex;
}
}
static void AddTextures(Texture2D source, Texture2D destination)
{
var destinationPixels = destination.GetPixels32();
var sourcePixels = source.GetPixels32();
for (var i = 0; i < destinationPixels.Length; i++)
{
var newPixel = destinationPixels[i];
newPixel.r += sourcePixels[i].r;
newPixel.g += sourcePixels[i].g;
newPixel.b += sourcePixels[i].b;
destinationPixels[i] = newPixel;
}
destination.SetPixels32(destinationPixels);
destination.Apply();
}
static ReiaFrame ReadSingleFrame(IoBuffer io, int width, int height, Texture2D previousFrame, Texture2D image)
{
var maxJ = (int)Mathf.Ceil((float)width / 32);
var maxI = (int)Mathf.Ceil((float)height / 32);
Texture2D smallBlock = null;
image.wrapMode = TextureWrapMode.Clamp;
for (var i = 0; i < maxI; i++)
{
for (var j = 0; j < maxJ; j++)
{
var x = (j * 32);
var y = (i * 32);
var xBound = x + 32;
var yBound = y + 32;
var blockWidth = 32;
var blockHeight = 32;
if (xBound > image.width)
blockWidth -= xBound - image.width;
if (yBound > image.height)
blockHeight -= yBound - image.height;
smallBlock = new Texture2D(blockWidth, blockHeight);
// First byte tells us if we should expect a new 32x32 pixel block or re-use
// the one from the previous frame.
var newFrame = io.ReadByte() != 0;
if (newFrame)
{
var block = ReadBlock(io, blockWidth, blockHeight);
if (previousFrame != null)
{
var previousPixels = previousFrame.GetPixels(x, y, blockWidth, blockHeight);
smallBlock.SetPixels(previousPixels);
smallBlock.Apply();
AddTextures(smallBlock, block);
}
image.SetPixels32(x, y, blockWidth, blockHeight, block.GetPixels32());
block.Free();
}
else
{
if (previousFrame == null)
{
throw new Exception("Tried to re-use previous 32x32 block but it's null");
}
var previousPixels = previousFrame.GetPixels(x, y, blockWidth, blockHeight);
smallBlock.SetPixels(previousPixels);
smallBlock.Apply();
image.SetPixels32(x, y, blockWidth, blockHeight, smallBlock.GetPixels32());
}
if (smallBlock != null)
smallBlock.Free();
}
}
image.Apply();
return new ReiaFrame(image);
}
public void Dispose()
{
if (_image != null)
{
_image.Free();
}
return ReiaImplementation.Get().ReadFrames(io, width, height);
}
}
}

View file

@ -0,0 +1,38 @@
using OpenTS2.Files.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenTS2.Files.Formats.Reia
{
public abstract class ReiaImplementation
{
/// <summary>
/// Get current ReiaImplementation Singleton.
/// </summary>
/// <returns>ReiaImplementation Singleton.</returns>
public static ReiaImplementation Get()
{
return s_instance;
}
/// <summary>
/// Static ReiaImplementation Singleton instance.
/// </summary>
static ReiaImplementation s_instance;
/// <summary>
/// Construct a new ReiaImplementation, make it the new Singleton.
/// </summary>
public ReiaImplementation()
{
s_instance = this;
}
public abstract IEnumerable<ReiaFrame> ReadFrameEnumerable(IoBuffer io, int width, int height);
public abstract List<ReiaFrame> ReadFrames(IoBuffer io, int width, int height);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d26a920e5f2aab409f38c3f7d45730e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: