mirror of
https://github.com/LazyDuchess/OpenTS2.git
synced 2025-01-23 00:31:47 -05:00
Abstract Unity Reia frame reading.
This commit is contained in:
parent
5eba4383cc
commit
c44b8139cf
12 changed files with 353 additions and 199 deletions
8
Assets/Scripts/OpenTS2/Engine/Files.meta
Normal file
8
Assets/Scripts/OpenTS2/Engine/Files.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c117ec6c60008f744b962efd1780388c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Scripts/OpenTS2/Engine/Files/Formats.meta
Normal file
8
Assets/Scripts/OpenTS2/Engine/Files/Formats.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58087d4f971645047a3271e1ed9f6013
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Scripts/OpenTS2/Engine/Files/Formats/Reia.meta
Normal file
8
Assets/Scripts/OpenTS2/Engine/Files/Formats/Reia.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 017ce228a1793614eb51163720fbc28e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e5fbcd77a94fb6b41958097193fd6417
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07b91bc412d0dea4cbefd9de86962095
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3d26a920e5f2aab409f38c3f7d45730e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in a new issue