mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-06-06 23:34:50 +00:00
790 lines
31 KiB
C#
790 lines
31 KiB
C#
/* Copyright (c) 2023-present miku-666
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1.The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
**/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using OpenTK;
|
|
using PckStudio.Internal;
|
|
using PckStudio.Extensions;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using System.Windows.Forms;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using PckStudio.Properties;
|
|
using PckStudio.Forms.Editor;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using PckStudio.Rendering.Camera;
|
|
using PckStudio.Rendering.Texture;
|
|
using PckStudio.Rendering.Shader;
|
|
|
|
namespace PckStudio.Rendering
|
|
{
|
|
internal partial class SkinRenderer : Renderer3D
|
|
{
|
|
/// <summary>
|
|
/// The visible Texture on the renderer
|
|
/// </summary>
|
|
/// <returns>The visible Texture</returns>
|
|
[Description("The current Texture")]
|
|
[Category("Appearance")]
|
|
public Image Texture
|
|
{
|
|
get => _texture;
|
|
set
|
|
{
|
|
var args = new TextureChangingEventArgs(value);
|
|
Events[nameof(TextureChanging)]?.DynamicInvoke(this, args);
|
|
if (!args.Cancel)
|
|
{
|
|
RenderTexture = _texture = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ClampModel { get; set; } = false;
|
|
|
|
[Description("Event that gets fired when the Texture is changing")]
|
|
[Category("Property Chnaged")]
|
|
[Browsable(true)]
|
|
public event EventHandler<TextureChangingEventArgs> TextureChanging
|
|
{
|
|
add => Events.AddHandler(nameof(TextureChanging), value);
|
|
remove => Events.RemoveHandler(nameof(TextureChanging), value);
|
|
}
|
|
|
|
public SkinANIM ANIM
|
|
{
|
|
get => _anim;
|
|
set
|
|
{
|
|
_anim = value;
|
|
OnANIMUpdate();
|
|
}
|
|
}
|
|
|
|
public ObservableCollection<SkinBOX> ModelData { get; }
|
|
public ReadOnlyDictionary<string, float> PartOffsets => new ReadOnlyDictionary<string, float>(partOffset);
|
|
|
|
/// <summary>
|
|
/// Captures the currently displayed frame
|
|
/// </summary>
|
|
/// <returns>Image of the cameras current view</returns>
|
|
public Image GetThumbnail()
|
|
{
|
|
Bitmap bmp = new Bitmap(Width, Height);
|
|
BitmapData data = bmp.LockBits(ClientRectangle, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
|
|
|
MakeCurrent();
|
|
GL.Finish();
|
|
GL.ReadPixels(0, 0, Width, Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
|
|
bmp.UnlockBits(data);
|
|
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
|
return bmp;
|
|
}
|
|
|
|
private Vector2 CameraTarget
|
|
{
|
|
get => Camera.Position;
|
|
set
|
|
{
|
|
if (ClampModel)
|
|
value = Vector2.Clamp(value, new Vector2(Camera.Distance / 2f * -1), new Vector2(Camera.Distance / 2f));
|
|
Camera.LookAt(value);
|
|
}
|
|
}
|
|
|
|
private Vector2 _globalModelRotation;
|
|
private Vector2 GlobalModelRotation
|
|
{
|
|
get => _globalModelRotation;
|
|
set
|
|
{
|
|
_globalModelRotation.X = MathHelper.Clamp(value.X, -30f, 30f);
|
|
_globalModelRotation.Y = value.Y % 360f;
|
|
}
|
|
}
|
|
|
|
public Size TextureSize { get; private set; } = new Size(64, 64);
|
|
private const float OverlayScale = 1.12f;
|
|
|
|
private bool _isLeftMouseDown;
|
|
private bool _isRightMouseDown;
|
|
|
|
private Image RenderTexture
|
|
{
|
|
set
|
|
{
|
|
if (value is not null && HasValidContext && _skinShader is not null)
|
|
{
|
|
MakeCurrent();
|
|
TextureSize = value.Width == value.Height ? new Size(64, 64) : new Size(64, 32);
|
|
var texture = new Texture2D(value);
|
|
_skinShader.Bind();
|
|
_skinShader.SetUniform1("u_Texture", 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsMouseHidden
|
|
{
|
|
get => !Cursor.IsVisible();
|
|
set
|
|
{
|
|
if (value)
|
|
{
|
|
Cursor.Hide();
|
|
return;
|
|
}
|
|
Cursor.Show();
|
|
}
|
|
}
|
|
private Point PreviousMouseLocation;
|
|
private Point CurrentMouseLocation;
|
|
|
|
private ShaderProgram _skinShader;
|
|
private SkinANIM _anim;
|
|
private Image _texture;
|
|
|
|
private Shader _skyboxShader;
|
|
private ShaderProgram _skyboxShader;
|
|
private RenderBuffer _skyboxRenderBuffer;
|
|
private CubeTexture _skyboxTexture;
|
|
private float skyboxRotation = 0f;
|
|
private float skyboxRotationStep = 0.5f;
|
|
|
|
private Dictionary<string, CubeRenderGroup> additionalModelRenderGroups;
|
|
private Dictionary<string, float> partOffset;
|
|
|
|
private CubeRenderGroup head;
|
|
private CubeRenderGroup body;
|
|
private CubeRenderGroup rightArm;
|
|
private CubeRenderGroup leftArm;
|
|
private CubeRenderGroup rightLeg;
|
|
private CubeRenderGroup leftLeg;
|
|
|
|
private CubeRenderGroup headOverlay;
|
|
private CubeRenderGroup bodyOverlay;
|
|
private CubeRenderGroup rightArmOverlay;
|
|
private CubeRenderGroup leftArmOverlay;
|
|
private CubeRenderGroup rightLegOverlay;
|
|
private CubeRenderGroup leftLegOverlay;
|
|
|
|
private float animationCurrentRotationAngle;
|
|
private float animationRotationStep = 0.5f;
|
|
private float animationMaxAngleInDegrees = 5f;
|
|
|
|
private bool showWireFrame = false;
|
|
|
|
private Matrix4 HeadMatrix { get; set; } = Matrix4.Identity;
|
|
private Matrix4 BodyMatrix { get; set; } = Matrix4.Identity;
|
|
private Matrix4 RightArmMatrix { get; set; } = Matrix4.CreateFromAxisAngle(Vector3.UnitZ, 25f);
|
|
private Matrix4 LeftArmMatrix { get; set; } = Matrix4.CreateFromAxisAngle(Vector3.UnitZ, -25f);
|
|
private Matrix4 RightLegMatrix { get; set; } = Matrix4.Identity;
|
|
private Matrix4 LeftLegMatrix { get; set; } = Matrix4.Identity;
|
|
|
|
private static Vector3[] cubeVertices = new Vector3[]
|
|
{
|
|
// front
|
|
new Vector3(-1.0f, -1.0f, 1.0f),
|
|
new Vector3( 1.0f, -1.0f, 1.0f),
|
|
new Vector3( 1.0f, 1.0f, 1.0f),
|
|
new Vector3(-1.0f, 1.0f, 1.0f),
|
|
// back
|
|
new Vector3(-1.0f, -1.0f, -1.0f),
|
|
new Vector3( 1.0f, -1.0f, -1.0f),
|
|
new Vector3( 1.0f, 1.0f, -1.0f),
|
|
new Vector3(-1.0f, 1.0f, -1.0f)
|
|
};
|
|
public SkinRenderer() : base()
|
|
{
|
|
InitializeCamera();
|
|
InitializeSkinData();
|
|
InitializeShaders();
|
|
InitializeComponent();
|
|
|
|
ANIM ??= new SkinANIM(SkinAnimMask.RESOLUTION_64x64);
|
|
OnTimerTick = AnimationTick;
|
|
ModelData = new ObservableCollection<SkinBOX>();
|
|
ModelData.CollectionChanged += ModelData_CollectionChanged;
|
|
additionalModelRenderGroups = new Dictionary<string, CubeRenderGroup>(6)
|
|
{
|
|
{ "HEAD", head },
|
|
{ "BODY", body },
|
|
{ "ARM0", rightArm },
|
|
{ "ARM1", leftArm },
|
|
{ "LEG0", rightLeg },
|
|
{ "LEG1", leftLeg },
|
|
|
|
{ "HEADWEAR", headOverlay },
|
|
{ "JACKET" , bodyOverlay },
|
|
{ "SLEEVE0" , rightArmOverlay },
|
|
{ "SLEEVE1" , leftArmOverlay },
|
|
{ "PANTS0" , rightLegOverlay },
|
|
{ "PANTS1" , leftLegOverlay },
|
|
|
|
{ "BODYARMOR", new CubeRenderGroup("BODYARMOR") },
|
|
{ "BELT", new CubeRenderGroup("BELT") },
|
|
{ "ARMARMOR0", new CubeRenderGroup("ARMARMOR0") },
|
|
{ "ARMARMOR1", new CubeRenderGroup("ARMARMOR1") },
|
|
};
|
|
|
|
partOffset = new Dictionary<string, float>()
|
|
{
|
|
{ "HEAD", 0f },
|
|
{ "BODY", 0f },
|
|
{ "ARM0", 0f },
|
|
{ "ARM1", 0f },
|
|
{ "LEG0", 0f },
|
|
{ "LEG1", 0f },
|
|
|
|
{ "HEADWEAR" , 0f },
|
|
{ "JACKET" , 0f },
|
|
{ "SLEEVE0" , 0f },
|
|
{ "SLEEVE1" , 0f },
|
|
{ "PANTS0" , 0f },
|
|
{ "PANTS1" , 0f },
|
|
|
|
{ "BODYARMOR", 0f },
|
|
{ "BELT" , 0f },
|
|
|
|
{ "HELMET" , 0f },
|
|
|
|
{ "BOOT0" , 0f },
|
|
{ "BOOT1" , 0f },
|
|
|
|
{ "TOOL0" , 0f },
|
|
{ "TOOL1" , 0f },
|
|
};
|
|
}
|
|
|
|
// TODO: calculate CameraDistance based on model size
|
|
private const float DefaultCameraDistance = 64f;
|
|
private void InitializeCamera()
|
|
{
|
|
Camera = new PerspectiveCamera(60f, new Vector3(0f, 0f, 0f))
|
|
{
|
|
MinimumFov = 30f,
|
|
MaximumFov = 120f,
|
|
Distance = DefaultCameraDistance,
|
|
};
|
|
}
|
|
|
|
private void InitializeSkinData()
|
|
{
|
|
head ??= new CubeRenderGroup("Head");
|
|
head.AddCube(new(-4, -8, -4), new(8, 8, 8), new(0, 0), flipZMapping: true);
|
|
head.Submit();
|
|
|
|
headOverlay ??= new CubeRenderGroup("Head Overlay", OverlayScale);
|
|
headOverlay.AddCube(new(-4, -8, -4), new(8, 8, 8), new(32, 0), flipZMapping: true, scale: OverlayScale);
|
|
headOverlay.Submit();
|
|
|
|
body ??= new CubeRenderGroup("Body");
|
|
body.AddCube(new(-4, 0, -2), new(8, 12, 4), new(16, 16));
|
|
body.Submit();
|
|
|
|
bodyOverlay ??= new CubeRenderGroup("Body Overlay", OverlayScale);
|
|
bodyOverlay.AddCube(new(-4, 0, -2), new(8, 12, 4), new(16, 32), scale: OverlayScale);
|
|
bodyOverlay.Submit();
|
|
|
|
rightArm ??= new CubeRenderGroup("Right Arm");
|
|
rightArm.AddCube(new(-3, -2, -2), new(4, 12, 4), new(40, 16));
|
|
rightArm.Submit();
|
|
|
|
rightArmOverlay ??= new CubeRenderGroup("Right Arm Overlay", OverlayScale);
|
|
rightArmOverlay.AddCube(new(-3, -2, -2), new(4, 12, 4), new(40, 32), scale: OverlayScale);
|
|
rightArmOverlay.Submit();
|
|
|
|
leftArm ??= new CubeRenderGroup("Left Arm");
|
|
leftArm.AddCube(new(-1, -2, -2), new(4, 12, 4), new(32, 48));
|
|
leftArm.Submit();
|
|
|
|
leftArmOverlay ??= new CubeRenderGroup("Left Arm Overlay", OverlayScale);
|
|
leftArmOverlay.AddCube(new(-1, -2, -2), new(4, 12, 4), new(48, 48), scale: OverlayScale);
|
|
leftArmOverlay.Submit();
|
|
|
|
rightLeg ??= new CubeRenderGroup("Right Leg");
|
|
rightLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 16));
|
|
rightLeg.Submit();
|
|
|
|
rightLegOverlay ??= new CubeRenderGroup("Right Leg Overlay", OverlayScale);
|
|
rightLegOverlay.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 32), scale: OverlayScale);
|
|
rightLegOverlay.Submit();
|
|
|
|
leftLeg ??= new CubeRenderGroup("Left Leg");
|
|
leftLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(16, 48));
|
|
leftLeg.Submit();
|
|
|
|
leftLegOverlay ??= new CubeRenderGroup("Left Leg Overlay", OverlayScale);
|
|
leftLegOverlay.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 48), scale: OverlayScale);
|
|
leftLegOverlay.Submit();
|
|
}
|
|
|
|
private void InitializeShaders()
|
|
{
|
|
if (DesignMode)
|
|
return;
|
|
|
|
MakeCurrent();
|
|
|
|
Trace.TraceInformation(GL.GetString(StringName.Version));
|
|
|
|
// Skin shader
|
|
{
|
|
_skinShader = ShaderProgram.Create(
|
|
new ShaderSource(ShaderType.VertexShader, Resources.skinVertexShader),
|
|
new ShaderSource(ShaderType.FragmentShader, Resources.skinFragmentShader),
|
|
new ShaderSource(ShaderType.GeometryShader, Resources.skinGeometryShader)
|
|
);
|
|
_skinShader.Bind();
|
|
_skinShader.SetUniform1("u_Texture", 0);
|
|
_skinShader.Validate();
|
|
GLErrorCheck();
|
|
|
|
skinTexture = new Texture2D(0);
|
|
skinTexture.PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat.Bgra;
|
|
skinTexture.InternalPixelFormat = PixelInternalFormat.Rgba8;
|
|
skinTexture.MinFilter = TextureMinFilter.Nearest;
|
|
skinTexture.MagFilter = TextureMagFilter.Nearest;
|
|
skinTexture.WrapS = TextureWrapMode.Repeat;
|
|
skinTexture.WrapT = TextureWrapMode.Repeat;
|
|
|
|
Texture ??= Resources.classic_template;
|
|
|
|
_skinShader.Unbind();
|
|
GLErrorCheck();
|
|
}
|
|
|
|
// Skybox shader
|
|
{
|
|
var skyboxVAO = new VertexArray();
|
|
var skyboxVBO = new VertexBuffer<Vector3>(cubeVertices, cubeVertices.Length * Vector3.SizeInBytes);
|
|
var vboLayout = new VertexBufferLayout();
|
|
vboLayout.Add<float>(3);
|
|
skyboxVAO.AddBuffer(skyboxVBO, vboLayout);
|
|
var skybocIBO = IndexBuffer.Create(
|
|
// front
|
|
0, 1, 2,
|
|
2, 3, 0,
|
|
// right
|
|
1, 5, 6,
|
|
6, 2, 1,
|
|
// back
|
|
7, 6, 5,
|
|
5, 4, 7,
|
|
// left
|
|
4, 0, 3,
|
|
3, 7, 4,
|
|
// bottom
|
|
4, 5, 1,
|
|
1, 0, 4,
|
|
// top
|
|
3, 2, 6,
|
|
6, 7, 3);
|
|
|
|
_skyboxRenderBuffer = new RenderBuffer(skyboxVAO, skybocIBO, PrimitiveType.Triangles);
|
|
|
|
skyboxVAO.Unbind();
|
|
skybocIBO.Unbind();
|
|
|
|
_skyboxShader = ShaderProgram.Create(Resources.skyboxVertexShader, Resources.skyboxFragmentShader);
|
|
_skyboxShader.Bind();
|
|
_skyboxShader.SetUniform1("skybox", 1);
|
|
_skyboxShader.SetUniform1("brightness", 1f);
|
|
_skyboxShader.Validate();
|
|
|
|
string customSkyboxFilepath = Path.Combine(Program.AppData, "cubemap.png");
|
|
Image skyboxImage = File.Exists(customSkyboxFilepath)
|
|
? Image.FromFile(customSkyboxFilepath)
|
|
: Resources.DefaultSkyTexture;
|
|
|
|
_skyboxTexture = new CubeTexture(skyboxImage);
|
|
|
|
_skyboxShader = Shader.Create(Resources.skyboxVertexShader, Resources.skyboxFragmentShader);
|
|
_skyboxShader.Bind();
|
|
_skyboxShader.SetUniform1("skybox", 1);
|
|
_skyboxShader.Validate();
|
|
|
|
GLErrorCheck();
|
|
}
|
|
|
|
// Initialize skin shader
|
|
{
|
|
_skinShader = Shader.Create(
|
|
new ShaderSource(ShaderType.VertexShader, Resources.skinVertexShader),
|
|
new ShaderSource(ShaderType.FragmentShader, Resources.skinFragmentShader),
|
|
new ShaderSource(ShaderType.GeometryShader, Resources.skinGeometryShader)
|
|
);
|
|
_skinShader.Bind();
|
|
_skinShader.Validate();
|
|
|
|
Texture ??= Resources.classic_template;
|
|
RenderTexture = Texture;
|
|
|
|
GLErrorCheck();
|
|
}
|
|
}
|
|
|
|
public void SetPartOffset(SkinPartOffset offset)
|
|
{
|
|
SetPartOffset(offset.Type, offset.Value);
|
|
}
|
|
|
|
public void SetPartOffset(string name, float value)
|
|
{
|
|
if (!partOffset.ContainsKey(name))
|
|
{
|
|
Debug.WriteLine($"'{name}' is not inside {nameof(partOffset)}");
|
|
return;
|
|
}
|
|
partOffset[name] = value;
|
|
}
|
|
|
|
private float GetOffset(string name)
|
|
{
|
|
return partOffset.ContainsKey(name) ? partOffset[name] : 0f;
|
|
}
|
|
|
|
private void ModelData_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e.Action != NotifyCollectionChangedAction.Move &&
|
|
e.Action != NotifyCollectionChangedAction.Reset)
|
|
{
|
|
UpdateModelData();
|
|
}
|
|
}
|
|
|
|
public void UpdateModelData()
|
|
{
|
|
ReInitialzeSkinData();
|
|
}
|
|
|
|
private void AddCustomModelPart(SkinBOX skinBox)
|
|
{
|
|
if (!additionalModelRenderGroups.ContainsKey(skinBox.Type))
|
|
throw new KeyNotFoundException(skinBox.Type);
|
|
|
|
CubeRenderGroup group = additionalModelRenderGroups[skinBox.Type];
|
|
group.AddSkinBox(skinBox);
|
|
group.Submit();
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private void GLErrorCheck()
|
|
{
|
|
var error = GL.GetError();
|
|
Debug.Assert(error == ErrorCode.NoError, error.ToString());
|
|
}
|
|
|
|
protected override bool ProcessDialogKey(Keys keyData)
|
|
{
|
|
switch (keyData)
|
|
{
|
|
case Keys.Escape:
|
|
ReleaseMouse();
|
|
var point = new Point(Parent.Location.X + Location.X, Parent.Location.Y + Location.Y);
|
|
contextMenuStrip1.Show(point);
|
|
return true;
|
|
case Keys.F3:
|
|
showWireFrame = !showWireFrame;
|
|
return true;
|
|
case Keys.R:
|
|
GlobalModelRotation = Vector2.Zero;
|
|
Camera.Distance = DefaultCameraDistance;
|
|
return true;
|
|
case Keys.A:
|
|
ReleaseMouse();
|
|
{
|
|
using var animeditor = new ANIMEditor(ANIM);
|
|
if (animeditor.ShowDialog() == DialogResult.OK)
|
|
{
|
|
ANIM = animeditor.ResultAnim;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return base.ProcessDialogKey(keyData);
|
|
}
|
|
|
|
private void ReleaseMouse()
|
|
{
|
|
if (IsMouseHidden || _isLeftMouseDown || _isRightMouseDown)
|
|
{
|
|
IsMouseHidden = _isRightMouseDown = _isLeftMouseDown = false;
|
|
Cursor.Position = PreviousMouseLocation;
|
|
}
|
|
}
|
|
|
|
private void OnANIMUpdate()
|
|
{
|
|
head.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.HEAD_DISABLED));
|
|
headOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.HEAD_OVERLAY_DISABLED));
|
|
|
|
body.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.BODY_DISABLED));
|
|
rightArm.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.RIGHT_ARM_DISABLED));
|
|
leftArm.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.LEFT_ARM_DISABLED));
|
|
rightLeg.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.RIGHT_LEG_DISABLED));
|
|
leftLeg.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.LEFT_LEG_DISABLED));
|
|
|
|
bool slim = ANIM.GetFlag(SkinAnimFlag.SLIM_MODEL);
|
|
if (slim || ANIM.GetFlag(SkinAnimFlag.RESOLUTION_64x64))
|
|
{
|
|
TextureSize = new Size(64, 64);
|
|
bodyOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.BODY_OVERLAY_DISABLED));
|
|
rightArmOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.RIGHT_ARM_OVERLAY_DISABLED));
|
|
leftArmOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.LEFT_ARM_OVERLAY_DISABLED));
|
|
rightLegOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.RIGHT_LEG_OVERLAY_DISABLED));
|
|
leftLegOverlay.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.LEFT_LEG_OVERLAY_DISABLED));
|
|
|
|
int slimValue = slim ? 3 : 4;
|
|
rightArm.ReplaceCube(0, new(-3, -2, -2), new(slimValue, 12, 4), new(40, 16));
|
|
rightArmOverlay.ReplaceCube(0, new(-3, -2, -2), new(slimValue, 12, 4), new(40, 32), scale: OverlayScale);
|
|
leftArm.ReplaceCube(0, new(-1, -2, -2), new(slimValue, 12, 4), new(32, 48));
|
|
leftArmOverlay.ReplaceCube(0, new(-1, -2, -2), new(slimValue, 12, 4), new(48, 48), scale: OverlayScale);
|
|
|
|
rightLeg.ReplaceCube(0, new(-2, 0, -2), new(4, 12, 4), new(0, 16));
|
|
leftLeg.ReplaceCube(0, new(-2, 0, -2), new(4, 12, 4), new(16, 48));
|
|
return;
|
|
}
|
|
|
|
TextureSize = new Size(64, 32);
|
|
|
|
rightArm.ReplaceCube(0, new(-3, -2, -2), new(4, 12, 4), new(40, 16));
|
|
rightArmOverlay.SetEnabled(0, false);
|
|
|
|
leftArm.ReplaceCube(0, new(-1, -2, -2), new(4, 12, 4), new(40, 16), mirrorTexture: true);
|
|
leftArmOverlay.SetEnabled(0, false);
|
|
|
|
rightLeg.ReplaceCube(0, new(-2, 0, -2), new(4, 12, 4), new(0, 16));
|
|
leftLeg.ReplaceCube(0, new(-2, 0, -2), new(4, 12, 4), new(0, 16), mirrorTexture: true);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
base.OnPaint(e);
|
|
if (DesignMode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MakeCurrent();
|
|
|
|
GL.Viewport(Size);
|
|
|
|
GL.ClearColor(BackColor);
|
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
GL.Enable(EnableCap.DepthTest); // Enable correct Z Drawings
|
|
|
|
// Render (custom) skin
|
|
{
|
|
var viewProjection = Camera.GetViewProjection();
|
|
_skinShader.Bind();
|
|
_skinShader.SetUniformMat4("u_ViewProjection", ref viewProjection);
|
|
_skinShader.SetUniform2("u_TexSize", new Vector2(TextureSize.Width, TextureSize.Height));
|
|
|
|
skinTexture.Bind();
|
|
|
|
GL.Enable(EnableCap.Texture2D); // Enable textures
|
|
|
|
GL.DepthFunc(DepthFunction.Lequal); // Enable correct Z Drawings
|
|
GL.DepthMask(true);
|
|
|
|
GL.Enable(EnableCap.Blend);
|
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
|
|
GL.Enable(EnableCap.AlphaTest); // Enable transparent
|
|
GL.AlphaFunc(AlphaFunction.Greater, 0.4f);
|
|
|
|
GL.PolygonMode(MaterialFace.FrontAndBack, showWireFrame ? PolygonMode.Line : PolygonMode.Fill);
|
|
|
|
Matrix4 modelMatrix = Matrix4.CreateTranslation(0f, 4f, 0f) * // <- model rotation pivot point
|
|
Matrix4.CreateFromAxisAngle(-Vector3.UnitX, MathHelper.DegreesToRadians(GlobalModelRotation.X)) *
|
|
Matrix4.CreateFromAxisAngle( Vector3.UnitY, MathHelper.DegreesToRadians(GlobalModelRotation.Y));
|
|
|
|
bool slimModel = ANIM.GetFlag(SkinAnimFlag.SLIM_MODEL);
|
|
|
|
var legRightMatrix = Matrix4.Identity;
|
|
var legLeftMatrix = Matrix4.Identity;
|
|
var armRightMatrix = Matrix4.Identity;
|
|
var armLeftMatrix = Matrix4.Identity;
|
|
|
|
if (!ANIM.GetFlag(SkinAnimFlag.STATIC_ARMS))
|
|
{
|
|
armRightMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(animationCurrentRotationAngle));
|
|
armLeftMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(-animationCurrentRotationAngle));
|
|
}
|
|
|
|
if (!ANIM.GetFlag(SkinAnimFlag.STATIC_LEGS))
|
|
{
|
|
legRightMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(-animationCurrentRotationAngle));
|
|
legLeftMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(animationCurrentRotationAngle));
|
|
}
|
|
|
|
if (ANIM.GetFlag(SkinAnimFlag.ZOMBIE_ARMS))
|
|
{
|
|
var rotation = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(-90f));
|
|
armRightMatrix = rotation;
|
|
armLeftMatrix = rotation;
|
|
}
|
|
|
|
if (ANIM.GetFlag(SkinAnimFlag.STATUE_OF_LIBERTY))
|
|
{
|
|
armRightMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(-180f));
|
|
armLeftMatrix = Matrix4.CreateRotationX(0f);
|
|
}
|
|
|
|
RenderBodyPart(new Vector3(0f, 0f, 0f), Vector3.Zero, HeadMatrix, modelMatrix, "HEAD", "HEADWEAR");
|
|
RenderBodyPart(new Vector3(0f, 0f, 0f), Vector3.Zero, BodyMatrix, modelMatrix, "BODY", "JACKET");
|
|
RenderBodyPart(new Vector3( 4f, 2f, 0f), new Vector3(slimModel ? -4f : -5f, -2f, 0f), RightArmMatrix * armRightMatrix, modelMatrix, "ARM0", "SLEEVE0");
|
|
RenderBodyPart(new Vector3(-4f, 2f, 0f), new Vector3( 5f, -2f, 0f), LeftArmMatrix * armLeftMatrix , modelMatrix, "ARM1", "SLEEVE1");
|
|
RenderBodyPart(new Vector3(0f, 12f, 0f), new Vector3(-2f, -12f, 0f), RightLegMatrix * legRightMatrix, modelMatrix, "LEG0", "PANTS0");
|
|
RenderBodyPart(new Vector3(0f, 12f, 0f), new Vector3(2f, -12f, 0f), LeftLegMatrix * legLeftMatrix, modelMatrix, "LEG1", "PANTS1");
|
|
}
|
|
|
|
// Render Skybox
|
|
{
|
|
GL.DepthFunc(DepthFunction.Lequal);
|
|
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
|
|
_skyboxShader.Bind();
|
|
_skyboxTexture.Bind(1);
|
|
|
|
var view = new Matrix4(new Matrix3(Matrix4.LookAt(Camera.WorldPosition, Camera.WorldPosition + Camera.Orientation, Camera.Up)))
|
|
* Matrix4.CreateRotationY(MathHelper.DegreesToRadians(skyboxRotation));
|
|
var proj = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(Camera.Fov), AspectRatio, 1f, 1000f);
|
|
var viewproj = view * proj;
|
|
_skyboxShader.SetUniformMat4("viewProjection", ref viewproj);
|
|
Renderer.Draw(_skyboxShader, _skyboxRenderBuffer);
|
|
GL.DepthFunc(DepthFunction.Less);
|
|
}
|
|
|
|
SwapBuffers();
|
|
}
|
|
|
|
private void RenderBodyPart(Vector3 pivot, Vector3 translation, Matrix4 partMatrix, Matrix4 globalMatrix, params string[] additionalData)
|
|
{
|
|
foreach (var data in additionalData)
|
|
{
|
|
RenderPart(data, pivot, translation, partMatrix, globalMatrix);
|
|
}
|
|
}
|
|
|
|
private void RenderPart(string name, Vector3 pivot, Vector3 translation, Matrix4 partMatrix, Matrix4 globalMatrix)
|
|
{
|
|
CubeRenderGroup renderGroup = additionalModelRenderGroups[name];
|
|
float yOffset = GetOffset(name);
|
|
translation.Y -= yOffset;
|
|
pivot.Y += yOffset;
|
|
renderGroup.Submit();
|
|
RenderBuffer buffer = renderGroup.GetRenderBuffer();
|
|
Matrix4 model = Pivot(translation, pivot, partMatrix);
|
|
model *= globalMatrix;
|
|
_skinShader.SetUniformMat4("u_Model", ref model);
|
|
Renderer.Draw(_skinShader, buffer);
|
|
}
|
|
|
|
private static Matrix4 Pivot(Vector3 translation, Vector3 pivot, Matrix4 target)
|
|
{
|
|
var model = Matrix4.CreateTranslation(translation);
|
|
model *= Matrix4.CreateTranslation(pivot);
|
|
model *= target;
|
|
model *= Matrix4.CreateTranslation(pivot).Inverted();
|
|
return model;
|
|
}
|
|
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
base.OnMouseMove(e);
|
|
// Rotate the model
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
float deltaX = (Cursor.Position.X - CurrentMouseLocation.X) * 0.5f;
|
|
float deltaY = (Cursor.Position.Y - CurrentMouseLocation.Y) * 0.5f;
|
|
GlobalModelRotation += new Vector2(-deltaY, deltaX) * Camera.Distance * 0.015f;
|
|
Cursor.Position = new Point((int)Math.Round(Screen.PrimaryScreen.Bounds.Width / 2d), (int)Math.Round(Screen.PrimaryScreen.Bounds.Height / 2d));
|
|
CurrentMouseLocation = Cursor.Position;
|
|
return;
|
|
}
|
|
// Move the model
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
float deltaX = (Cursor.Position.X - CurrentMouseLocation.X) * 0.05f;
|
|
float deltaY = (Cursor.Position.Y - CurrentMouseLocation.Y) * 0.05f;
|
|
Camera.Pan(deltaX, deltaY);
|
|
Cursor.Position = new Point((int)Math.Round(Screen.PrimaryScreen.Bounds.Width / 2d), (int)Math.Round(Screen.PrimaryScreen.Bounds.Height / 2d));
|
|
CurrentMouseLocation = Cursor.Position;
|
|
}
|
|
}
|
|
|
|
protected override void OnMouseWheel(MouseEventArgs e)
|
|
{
|
|
Camera.Distance -= e.Delta / System.Windows.Input.Mouse.MouseWheelDeltaForOneLine;
|
|
}
|
|
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
base.OnMouseDown(e);
|
|
if (e.Button == MouseButtons.Right || e.Button == MouseButtons.Left)
|
|
{
|
|
if (!IsMouseHidden)
|
|
{
|
|
IsMouseHidden = true;
|
|
}
|
|
PreviousMouseLocation = Cursor.Position;
|
|
CurrentMouseLocation = Cursor.Position;
|
|
}
|
|
}
|
|
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
ReleaseMouse();
|
|
base.OnMouseUp(e);
|
|
}
|
|
|
|
private void AnimationTick(object sender, EventArgs e)
|
|
{
|
|
skyboxRotation += skyboxRotationStep;
|
|
skyboxRotation %= 360f;
|
|
animationCurrentRotationAngle += animationRotationStep;
|
|
if (animationCurrentRotationAngle >= animationMaxAngleInDegrees || animationCurrentRotationAngle <= -animationMaxAngleInDegrees)
|
|
animationRotationStep = -animationRotationStep;
|
|
}
|
|
|
|
private void ReInitialzeSkinData()
|
|
{
|
|
foreach (var renderGroup in additionalModelRenderGroups.Values)
|
|
{
|
|
renderGroup.Clear();
|
|
}
|
|
|
|
InitializeSkinData();
|
|
foreach (var item in ModelData)
|
|
{
|
|
AddCustomModelPart(item);
|
|
}
|
|
OnANIMUpdate();
|
|
}
|
|
|
|
private void reInitToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ReInitialzeSkinData();
|
|
}
|
|
}
|
|
} |