mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-06-06 23:14:49 +00:00
AnimationEditor - Added Interpolation to animation
This commit is contained in:
@@ -10,12 +10,12 @@ namespace PckStudio.Extensions
|
||||
/// Normalizes the Color between 0.0 - 1.0
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Vector3 Normalize(this Color color)
|
||||
public static Vector4 Normalize(this Color color)
|
||||
{
|
||||
return new Vector3(color.R / 255f, color.G / 255f, color.B / 255f);
|
||||
return new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
|
||||
}
|
||||
|
||||
private static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
|
||||
public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
|
||||
{
|
||||
if (value.CompareTo(min) < 0) return min;
|
||||
if (value.CompareTo(max) > 0) return max;
|
||||
@@ -26,7 +26,6 @@ namespace PckStudio.Extensions
|
||||
{
|
||||
source = Clamp(source, 0.0f, 1.0f);
|
||||
overlay = Clamp(overlay, 0.0f, 1.0f);
|
||||
|
||||
float resultValue = blendType switch
|
||||
{
|
||||
BlendMode.Add => source + overlay,
|
||||
@@ -41,5 +40,19 @@ namespace PckStudio.Extensions
|
||||
return (byte)Clamp(resultValue * 255, 0, 255);
|
||||
}
|
||||
|
||||
public static byte Mix(double ratio, byte val1, byte val2)
|
||||
{
|
||||
return (byte)(ratio * val1 + (1.0 - ratio) * val2);
|
||||
}
|
||||
|
||||
public static Color Mix(this Color c1, Color c2, double ratio)
|
||||
{
|
||||
ratio = Clamp(ratio, 0.0, 1.0);
|
||||
return Color.FromArgb(c1.A,
|
||||
Mix(ratio, c1.R, c2.R),
|
||||
Mix(ratio, c1.G, c2.G),
|
||||
Mix(ratio, c1.B, c2.B)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,5 +229,43 @@ namespace PckStudio.Extensions
|
||||
overlayImage.UnlockBits(overlayImageData);
|
||||
return bitmapResult;
|
||||
}
|
||||
|
||||
public static Image Interpolate(this Image image1, Image image2, double delta)
|
||||
{
|
||||
delta = ColorExtensions.Clamp(delta, 0.0, 1.0);
|
||||
if (image1 is not Bitmap baseImage || image2 is not Bitmap overlayImage ||
|
||||
image1.Width != image2.Width || image1.Height != image2.Height)
|
||||
return image1;
|
||||
|
||||
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];
|
||||
|
||||
Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);
|
||||
|
||||
BitmapData overlayImageData = overlayImage.LockBits(new Rectangle(Point.Empty, overlayImage.Size),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
byte[] overlayImageBuffer = new byte[overlayImageData.Stride * overlayImageData.Height];
|
||||
|
||||
Marshal.Copy(overlayImageData.Scan0, overlayImageBuffer, 0, overlayImageBuffer.Length);
|
||||
|
||||
for (int k = 0; k < baseImageBuffer.Length && k < overlayImageBuffer.Length; k += 4)
|
||||
{
|
||||
baseImageBuffer[k + 0] = ColorExtensions.Mix(delta, baseImageBuffer[k + 0], overlayImageBuffer[k + 0]);
|
||||
baseImageBuffer[k + 1] = ColorExtensions.Mix(delta, baseImageBuffer[k + 1], overlayImageBuffer[k + 1]);
|
||||
baseImageBuffer[k + 2] = ColorExtensions.Mix(delta, baseImageBuffer[k + 2], overlayImageBuffer[k + 2]);
|
||||
}
|
||||
|
||||
Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
|
||||
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
Marshal.Copy(baseImageBuffer, 0, resultImageData.Scan0, baseImageBuffer.Length);
|
||||
|
||||
bitmapResult.UnlockBits(resultImageData);
|
||||
baseImage.UnlockBits(baseImageData);
|
||||
overlayImage.UnlockBits(overlayImageData);
|
||||
return bitmapResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,20 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
public int FrameCount => frames.Count;
|
||||
|
||||
public int TextureCount => frameTextures.Count;
|
||||
public int TextureCount => textures.Count;
|
||||
|
||||
public Frame this[int frameIndex] => frames[frameIndex];
|
||||
|
||||
// TODO: implement this
|
||||
public bool Interpolate { get; set; } = false;
|
||||
|
||||
private readonly List<Image> frameTextures;
|
||||
private readonly List<Image> textures;
|
||||
|
||||
private readonly List<Frame> frames = new List<Frame>();
|
||||
|
||||
|
||||
public Animation(IEnumerable<Image> image)
|
||||
{
|
||||
frameTextures = new List<Image>(image);
|
||||
textures = new List<Image>(image);
|
||||
}
|
||||
|
||||
public Animation(IEnumerable<Image> frameTextures, string ANIM) : this(frameTextures)
|
||||
@@ -67,8 +66,6 @@ namespace PckStudio.Forms.Editor
|
||||
foreach (string frameInfo in animData)
|
||||
{
|
||||
string[] frameData = frameInfo.Split('*');
|
||||
//if (frameData.Length < 2)
|
||||
// continue; // shouldn't happen
|
||||
int currentFrameIndex = 0;
|
||||
int.TryParse(frameData[0], out currentFrameIndex);
|
||||
|
||||
@@ -77,7 +74,7 @@ namespace PckStudio.Forms.Editor
|
||||
// This will detect that and place the last frame time in its place.
|
||||
// This is accurate to console edition behavior.
|
||||
// - MattNL
|
||||
int currentFrameTime = string.IsNullOrEmpty(frameData[1]) ? lastFrameTime : int.Parse(frameData[1]);
|
||||
int currentFrameTime = frameData.Length < 2 || string.IsNullOrEmpty(frameData[1]) ? lastFrameTime : int.Parse(frameData[1]);
|
||||
AddFrame(currentFrameIndex, currentFrameTime);
|
||||
lastFrameTime = currentFrameTime;
|
||||
}
|
||||
@@ -86,9 +83,9 @@ namespace PckStudio.Forms.Editor
|
||||
public Frame AddFrame(int frameTextureIndex) => AddFrame(frameTextureIndex, MinimumFrameTime);
|
||||
public Frame AddFrame(int frameTextureIndex, int frameTime)
|
||||
{
|
||||
if (frameTextureIndex < 0 || frameTextureIndex >= frameTextures.Count)
|
||||
if (frameTextureIndex < 0 || frameTextureIndex >= textures.Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(frameTextureIndex));
|
||||
Frame f = new Frame(frameTextures[frameTextureIndex], frameTime);
|
||||
Frame f = new Frame(textures[frameTextureIndex], frameTime);
|
||||
frames.Add(f);
|
||||
return f;
|
||||
}
|
||||
@@ -108,20 +105,20 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
public List<Image> GetFrameTextures()
|
||||
{
|
||||
return frameTextures;
|
||||
return textures;
|
||||
}
|
||||
|
||||
public int GetTextureIndex(Image frameTexture)
|
||||
{
|
||||
_ = frameTexture ?? throw new ArgumentNullException(nameof(frameTexture));
|
||||
return frameTextures.IndexOf(frameTexture);
|
||||
return textures.IndexOf(frameTexture);
|
||||
}
|
||||
|
||||
public void SetFrame(Frame frame, int frameTextureIndex, int frameTime = MinimumFrameTime)
|
||||
=> SetFrame(frames.IndexOf(frame), frameTextureIndex, frameTime);
|
||||
public void SetFrame(int frameIndex, int frameTextureIndex, int frameTime = MinimumFrameTime)
|
||||
{
|
||||
frames[frameIndex] = new Frame(frameTextures[frameTextureIndex], frameTime);
|
||||
frames[frameIndex] = new Frame(textures[frameTextureIndex], frameTime);
|
||||
}
|
||||
|
||||
public string BuildAnim()
|
||||
@@ -134,13 +131,11 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
public Image BuildTexture(bool isClockOrCompass, List<Image> linearImages = default!)
|
||||
{
|
||||
int width = frameTextures[0].Width;
|
||||
int height = frameTextures[0].Height;
|
||||
if (width != height)
|
||||
var textures = isClockOrCompass ? linearImages : this.textures;
|
||||
|
||||
if (textures[0].Width != textures[0].Height)
|
||||
throw new Exception("Invalid size");
|
||||
|
||||
var textures = isClockOrCompass ? linearImages : frameTextures;
|
||||
|
||||
return ImageExtensions.CombineImages(textures, ImageLayoutDirection.Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
27
PCK-Studio/Forms/Editor/AnimationEditor.Designer.cs
generated
27
PCK-Studio/Forms/Editor/AnimationEditor.Designer.cs
generated
@@ -53,11 +53,11 @@
|
||||
this.AnimationStopBtn = new MetroFramework.Controls.MetroButton();
|
||||
this.tileLabel = new MetroFramework.Controls.MetroLabel();
|
||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
this.pictureBoxWithInterpolationMode1 = new PckStudio.PictureBoxWithInterpolationMode();
|
||||
this.animationPictureBox = new PckStudio.Forms.Editor.AnimationPictureBox();
|
||||
this.contextMenuStrip1.SuspendLayout();
|
||||
this.menuStrip.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBoxWithInterpolationMode1)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationPictureBox)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// frameTreeView
|
||||
@@ -282,18 +282,17 @@
|
||||
this.pictureBox1.TabIndex = 21;
|
||||
this.pictureBox1.TabStop = false;
|
||||
//
|
||||
// pictureBoxWithInterpolationMode1
|
||||
// animationPictureBox
|
||||
//
|
||||
this.pictureBoxWithInterpolationMode1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
this.animationPictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.pictureBoxWithInterpolationMode1.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
||||
this.pictureBoxWithInterpolationMode1.Location = new System.Drawing.Point(157, 88);
|
||||
this.pictureBoxWithInterpolationMode1.Name = "pictureBoxWithInterpolationMode1";
|
||||
this.pictureBoxWithInterpolationMode1.Size = new System.Drawing.Size(235, 223);
|
||||
this.pictureBoxWithInterpolationMode1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pictureBoxWithInterpolationMode1.TabIndex = 16;
|
||||
this.pictureBoxWithInterpolationMode1.TabStop = false;
|
||||
this.animationPictureBox.Location = new System.Drawing.Point(157, 88);
|
||||
this.animationPictureBox.Name = "animationPictureBox";
|
||||
this.animationPictureBox.Size = new System.Drawing.Size(235, 223);
|
||||
this.animationPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.animationPictureBox.TabIndex = 16;
|
||||
this.animationPictureBox.TabStop = false;
|
||||
//
|
||||
// AnimationEditor
|
||||
//
|
||||
@@ -305,7 +304,7 @@
|
||||
this.Controls.Add(this.AnimationStopBtn);
|
||||
this.Controls.Add(this.AnimationPlayBtn);
|
||||
this.Controls.Add(this.tileLabel);
|
||||
this.Controls.Add(this.pictureBoxWithInterpolationMode1);
|
||||
this.Controls.Add(this.animationPictureBox);
|
||||
this.Controls.Add(this.frameTreeView);
|
||||
this.Controls.Add(this.menuStrip);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
@@ -319,7 +318,7 @@
|
||||
this.menuStrip.ResumeLayout(false);
|
||||
this.menuStrip.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBoxWithInterpolationMode1)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationPictureBox)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -331,7 +330,7 @@
|
||||
private System.Windows.Forms.MenuStrip menuStrip;
|
||||
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem1;
|
||||
private PictureBoxWithInterpolationMode pictureBoxWithInterpolationMode1;
|
||||
private PckStudio.Forms.Editor.AnimationPictureBox animationPictureBox;
|
||||
private MetroFramework.Controls.MetroCheckBox InterpolationCheckbox;
|
||||
private MetroFramework.Controls.MetroButton AnimationPlayBtn;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace PckStudio.Forms.Editor
|
||||
{
|
||||
PckFile.FileData animationFile;
|
||||
Animation currentAnimation;
|
||||
AnimationPlayer player;
|
||||
|
||||
bool isItem = false;
|
||||
string animationSection => AnimationResources.GetAnimationSection(isItem);
|
||||
@@ -54,7 +53,6 @@ namespace PckStudio.Forms.Editor
|
||||
currentAnimation = animationFile.Properties.HasProperty("ANIM")
|
||||
? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM"))
|
||||
: new Animation(frameTextures);
|
||||
player = new AnimationPlayer(pictureBoxWithInterpolationMode1);
|
||||
|
||||
foreach (JObject content in AnimationResources.tileData[animationSection].Children())
|
||||
{
|
||||
@@ -84,14 +82,14 @@ namespace PckStudio.Forms.Editor
|
||||
SelectedImageIndex = imageIndex,
|
||||
});
|
||||
}
|
||||
player.SelectFrame(currentAnimation, 0);
|
||||
animationPictureBox.SelectFrame(currentAnimation, 0);
|
||||
}
|
||||
|
||||
private void frameTreeView_AfterSelect(object sender, TreeViewEventArgs e)
|
||||
{
|
||||
if (player.IsPlaying && !AnimationPlayBtn.Enabled)
|
||||
if (animationPictureBox.IsPlaying && !AnimationPlayBtn.Enabled)
|
||||
AnimationPlayBtn.Enabled = !(AnimationStopBtn.Enabled = !AnimationStopBtn.Enabled);
|
||||
player.SelectFrame(currentAnimation, frameTreeView.SelectedNode.Index);
|
||||
animationPictureBox.SelectFrame(currentAnimation, frameTreeView.SelectedNode.Index);
|
||||
}
|
||||
|
||||
private int mix(double ratio, int val1, int val2) // Ported from Java Edition code
|
||||
@@ -101,20 +99,19 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void StartAnimationBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
// prevent player from crashing
|
||||
player.Stop();
|
||||
// prevent player from crashing
|
||||
animationPictureBox.Stop();
|
||||
AnimationPlayBtn.Enabled = !(AnimationStopBtn.Enabled = !AnimationStopBtn.Enabled);
|
||||
if (currentAnimation.FrameCount > 1)
|
||||
{
|
||||
player.SetContext(pictureBoxWithInterpolationMode1);
|
||||
player.Start(currentAnimation);
|
||||
animationPictureBox.Start(currentAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void StopAnimationBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
AnimationPlayBtn.Enabled = !(AnimationStopBtn.Enabled = !AnimationStopBtn.Enabled);
|
||||
player.Stop();
|
||||
animationPictureBox.Stop();
|
||||
}
|
||||
|
||||
private void frameTreeView_KeyDown(object sender, KeyEventArgs e)
|
||||
@@ -436,9 +433,9 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void AnimationEditor_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (player.IsPlaying)
|
||||
if (animationPictureBox.IsPlaying)
|
||||
{
|
||||
player.Stop();
|
||||
animationPictureBox.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,76 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading.Tasks;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using PckStudio.Extensions;
|
||||
|
||||
namespace PckStudio.Forms.Editor
|
||||
{
|
||||
// TODO: write as a UI control ??
|
||||
sealed class AnimationPlayer
|
||||
{
|
||||
internal class AnimationPictureBox : PictureBox
|
||||
{
|
||||
private const int TickInMillisecond = 50; // 1 InGame tick
|
||||
public bool IsPlaying { get; private set; } = false;
|
||||
|
||||
private int currentAnimationFrameIndex = 0;
|
||||
private PictureBox display;
|
||||
private Animation.Frame currentFrame;
|
||||
private Animation _animation;
|
||||
private CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
public AnimationPlayer(PictureBox display)
|
||||
{
|
||||
SetContext(display);
|
||||
}
|
||||
protected override void OnPaint(PaintEventArgs pe)
|
||||
{
|
||||
pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
base.OnPaint(pe);
|
||||
}
|
||||
|
||||
private async void DoAnimate()
|
||||
private async void DoAnimate()
|
||||
{
|
||||
_ = display ?? throw new ArgumentNullException(nameof(display));
|
||||
_ = _animation ?? throw new ArgumentNullException(nameof(_animation));
|
||||
IsPlaying = true;
|
||||
Animation.Frame nextFrame;
|
||||
while (!cts.IsCancellationRequested)
|
||||
{
|
||||
if (currentAnimationFrameIndex >= _animation.FrameCount)
|
||||
{
|
||||
currentAnimationFrameIndex = 0;
|
||||
Animation.Frame frame = SetDisplayFrame(currentAnimationFrameIndex++);
|
||||
await Task.Delay(TickInMillisecond * frame.Ticks);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAnimationFrameIndex + 1 >= _animation.FrameCount)
|
||||
{
|
||||
nextFrame = _animation[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
nextFrame = _animation[currentAnimationFrameIndex + 1];
|
||||
}
|
||||
|
||||
currentFrame = _animation[currentAnimationFrameIndex++];
|
||||
if (_animation.Interpolate)
|
||||
{
|
||||
await InterpolateFrame(currentFrame, nextFrame);
|
||||
continue;
|
||||
}
|
||||
SetAnimationFrame(currentFrame);
|
||||
await Task.Delay(TickInMillisecond * currentFrame.Ticks);
|
||||
}
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
public void Start(Animation animation)
|
||||
private async Task InterpolateFrame(Animation.Frame currentFrame, Animation.Frame nextFrame)
|
||||
{
|
||||
for (int i = 0; i < currentFrame.Ticks; i++)
|
||||
{
|
||||
double delta = 1.0f - i / (double)currentFrame.Ticks;
|
||||
Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta);
|
||||
await Task.Delay(TickInMillisecond);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start(Animation animation)
|
||||
{
|
||||
_animation = animation;
|
||||
cts = new CancellationTokenSource();
|
||||
@@ -47,30 +79,31 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
public void Stop([CallerMemberName] string callerName = default!)
|
||||
{
|
||||
Debug.WriteLine($"{nameof(AnimationPlayer.Stop)} called from {callerName}!");
|
||||
Debug.WriteLine($"{nameof(AnimationPictureBox.Stop)} called from {callerName}!");
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
public Animation.Frame GetCurrentFrame() => _animation[currentAnimationFrameIndex];
|
||||
|
||||
public void SetContext(PictureBox display) => this.display = display;
|
||||
|
||||
public void SelectFrame(Animation animation, int index)
|
||||
{
|
||||
_animation = animation;
|
||||
if (IsPlaying)
|
||||
Stop();
|
||||
SetDisplayFrame(index);
|
||||
_animation = animation;
|
||||
currentAnimationFrameIndex = index;
|
||||
currentFrame = SetAnimationFrame(index);
|
||||
}
|
||||
|
||||
private Animation.Frame SetDisplayFrame(int frameIndex)
|
||||
private Animation.Frame SetAnimationFrame(int frameIndex)
|
||||
{
|
||||
Monitor.Enter(_animation);
|
||||
Animation.Frame frame = _animation[frameIndex];
|
||||
display.Image = frame.Texture;
|
||||
Monitor.Exit(_animation);
|
||||
var frame = _animation[frameIndex];
|
||||
SetAnimationFrame(frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private void SetAnimationFrame(Animation.Frame frame)
|
||||
{
|
||||
Image = frame.Texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,9 @@
|
||||
<DependentUpon>TextPrompt.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\Editor\Animation.cs" />
|
||||
<Compile Include="Forms\Editor\AnimationPlayer.cs" />
|
||||
<Compile Include="Forms\Editor\AnimationPlayer.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\Editor\MaterialsEditor.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
||||
Reference in New Issue
Block a user