diff --git a/PCK-Studio/Extensions/System.Numerics.cs b/PCK-Studio/Extensions/System.Numerics.cs index d76ff76d..74de865d 100644 --- a/PCK-Studio/Extensions/System.Numerics.cs +++ b/PCK-Studio/Extensions/System.Numerics.cs @@ -11,5 +11,15 @@ { return new OpenTK.Vector2(vector2.X, vector2.Y); } + + internal static System.Numerics.Vector3 ToNumericsVector(this OpenTK.Vector3 vector3) + { + return new System.Numerics.Vector3(vector3.X, vector3.Y, vector3.Z); + } + + internal static System.Numerics.Vector2 ToNumericsVector(this OpenTK.Vector2 vector2) + { + return new System.Numerics.Vector2(vector2.X, vector2.Y); + } } } diff --git a/PCK-Studio/External/Format/BlockBenchModel.cs b/PCK-Studio/External/Format/BlockBenchModel.cs new file mode 100644 index 00000000..613c407c --- /dev/null +++ b/PCK-Studio/External/Format/BlockBenchModel.cs @@ -0,0 +1,155 @@ +using System; +using System.Buffers.Text; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace PckStudio.External.Format +{ + internal class Meta + { + [JsonProperty("format_version")] + internal string FormatVersion; + + [JsonProperty("model_format")] + internal string ModelFormat; + + [JsonProperty("box_uv")] + internal bool UseBoxUv; + } + + internal class Element + { + [JsonProperty("name")] + internal string Name; + + [JsonProperty("box_uv")] + internal bool UseBoxUv; + + [JsonProperty("visibility")] + internal bool Visibility { get; set; } = true; + + [JsonProperty("rescale")] + internal bool Rescale; + + [JsonProperty("locked")] + internal bool Locked; + + [JsonProperty("inflate")] + internal float Inflate; + + [JsonProperty("origin")] + private float[] origin; + + [JsonProperty("from")] + private float[] from; + + [JsonProperty("to")] + private float[] to; + + [JsonProperty("uv_offset")] + private int[] uv_offset; + + [JsonIgnore()] + internal Vector3 Origin => new Vector3(origin?[0] ?? 0, origin?[1] ?? 0, origin?[2] ?? 0); + + [JsonIgnore()] + internal Vector3 From => new Vector3(from?[0] ?? 0, from?[1] ?? 0, from?[2] ?? 0); + + [JsonIgnore()] + internal Vector3 To => new Vector3(to?[0] ?? 0, to?[1] ?? 0, to?[2] ?? 0); + + [JsonIgnore()] + internal Vector2 UvOffset => new Vector2(uv_offset?[0] ?? 0, uv_offset?[1] ?? 0); + + [JsonProperty("type")] + internal string Type; + + [JsonProperty("uuid")] + internal Guid Uuid; + } + + internal class Texture + { + [JsonProperty("name")] + internal string Name; + + [JsonProperty("source")] + internal Uri TextureSource; + + internal Image GetTexture() + { + string data = TextureSource.AbsolutePath; + const string dataHead = "image/png;base64,"; + if (data.StartsWith(dataHead)) + { + byte[] encodedData = Convert.FromBase64String(data.Substring(dataHead.Length)); + using (var ms = new MemoryStream(encodedData)) + { + return Image.FromStream(ms); + } + } + return null; + } + } + + internal class Outline + { + [JsonProperty("name")] + internal string Name; + + [JsonProperty("origin")] + internal float[] Origin; + + [JsonProperty("uuid")] + internal Guid Uuid; + + [JsonProperty("children")] + internal Guid[] Children; + } + + internal class TextureRes + { + [JsonProperty("width")] + internal int Width; + + [JsonProperty("height")] + internal int Height; + + public static implicit operator Size(TextureRes res) => new Size(res.Width, res.Height); + } + + internal class BlockBenchModel + { + [JsonProperty("name")] + internal string Name; + + [JsonProperty("meta")] + internal Meta Metadata; + + [JsonProperty("model_identifier")] + internal string ModelIdentifier; + + [JsonProperty("visible_box")] + internal int[] VisibleBox; + + [JsonProperty("resolution")] + internal TextureRes TextureResolution; + + [JsonProperty("elements")] + internal Element[] Elements; + + [JsonProperty("outliner")] + internal Outline[] Outliner; + + [JsonProperty("textures")] + internal Texture[] Textures; + + } +} diff --git a/PCK-Studio/Forms/Editor/CustomSkinEditor.cs b/PCK-Studio/Forms/Editor/CustomSkinEditor.cs index 2d594ff0..58bb1398 100644 --- a/PCK-Studio/Forms/Editor/CustomSkinEditor.cs +++ b/PCK-Studio/Forms/Editor/CustomSkinEditor.cs @@ -13,6 +13,11 @@ using PckStudio.IO.PSM; using PckStudio.Internal.FileFormats; using System.Linq; using PckStudio.Forms.Additional_Popups; +using PckStudio.External.Format; +using Newtonsoft.Json; +using System.Numerics; +using PckStudio.Rendering; +using System.Diagnostics; namespace PckStudio.Forms.Editor { @@ -48,7 +53,8 @@ namespace PckStudio.Forms.Editor private readonly FileDialogOption[] fileFilters = [ - new ("Pck skin model(*.psm)", "*.psm") + new ("Pck skin model(*.psm)", "*.psm"), + new ("Block bench model(*.bbmodel)", "*.bbmodel") ]; private string skinModelFileFilters => string.Join("|", fileFilters); @@ -220,6 +226,66 @@ namespace PckStudio.Forms.Editor _skin.PartOffsets.AddRange(csmbFile.Offsets); LoadModelData(_skin); break; + case ".bbmodel": + BlockBenchModel blockBenchModel = JsonConvert.DeserializeObject(File.ReadAllText(openFileDialog.FileName)); + _skin.AdditionalBoxes.Clear(); + _skin.PartOffsets.Clear(); + // TODO: clean this up -miku + _skin.ANIM.SetFlag(SkinAnimFlag.RESOLUTION_64x64, true); + _skin.ANIM.SetFlag(SkinAnimFlag.SLIM_MODEL, false); + _skin.ANIM.SetFlag(SkinAnimFlag.HEAD_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.HEAD_OVERLAY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.BODY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.BODY_OVERLAY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.RIGHT_ARM_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.RIGHT_ARM_OVERLAY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.LEFT_ARM_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.LEFT_ARM_OVERLAY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.RIGHT_LEG_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.RIGHT_LEG_OVERLAY_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.LEFT_LEG_DISABLED, true); + _skin.ANIM.SetFlag(SkinAnimFlag.LEFT_LEG_OVERLAY_DISABLED, true); + + + // IMPROVMENT: detect default body parts and toggle anim flag instead of adding box data -miku + foreach (Outline outline in blockBenchModel.Outliner) + { + foreach (Element element in blockBenchModel.Elements.Where(e => outline.Children.Contains(e.Uuid))) + { + if (!element.UseBoxUv || !element.Visibility) + continue; + + BoundingBox boundingBox = new BoundingBox(element.From.ToOpenTKVector(), element.To.ToOpenTKVector()); + Vector3 pos = boundingBox.Start.ToNumericsVector(); + Vector3 size = boundingBox.Volume.ToNumericsVector(); + + //Debug.WriteLine($"{outline.Name} {element.Name}({element.Uuid})"); + //Debug.WriteLine($"boundingBox.Start({boundingBox.Start})"); + //Debug.WriteLine($"boundingBox.End({boundingBox.End})"); + //Debug.WriteLine($"size({size})"); + + Vector3 transformUnit = new Vector3(-1, -1, 1); + Vector3 coordinateUnit = new Vector3(1, 1, 0); + + pos *= transformUnit; + pos -= size * coordinateUnit; + pos.Y += 24f; + + Vector3 translation = renderer3D1.GetTranslation(outline.Name).ToNumericsVector(); + Vector3 pivot = renderer3D1.GetPivot(outline.Name).ToNumericsVector(); + + pos += translation * -Vector3.UnitX - pivot * Vector3.UnitY; + //Debug.WriteLine(translation); + //Debug.WriteLine(pivot); + //Debug.WriteLine(pos); + + _skin.AdditionalBoxes.Add(new SkinBOX(outline.Name, pos, size, element.UvOffset)); + } + } + + _skin.Texture = blockBenchModel.Textures[0].GetTexture(); + LoadModelData(_skin); + break; default: break; } diff --git a/PCK-Studio/PckStudio.csproj b/PCK-Studio/PckStudio.csproj index 46ceb7c1..8baea77e 100644 --- a/PCK-Studio/PckStudio.csproj +++ b/PCK-Studio/PckStudio.csproj @@ -151,6 +151,7 @@ + diff --git a/PCK-Studio/Rendering/BoundingBox.cs b/PCK-Studio/Rendering/BoundingBox.cs index 617676b7..f85b5cb5 100644 --- a/PCK-Studio/Rendering/BoundingBox.cs +++ b/PCK-Studio/Rendering/BoundingBox.cs @@ -11,11 +11,14 @@ namespace PckStudio.Rendering { public readonly Vector3 Start; public readonly Vector3 End; + public readonly Vector3 Volume; public BoundingBox(Vector3 start, Vector3 end) { Start = start; End = end; + Vector3 size = End - Start; + Volume = new Vector3(Math.Abs(size.X), Math.Abs(size.Y), Math.Abs(size.Z)); } public ColorVertex[] GetVertices() diff --git a/PCK-Studio/Rendering/SkinRenderer.cs b/PCK-Studio/Rendering/SkinRenderer.cs index 4dac3cb2..0852fd3d 100644 --- a/PCK-Studio/Rendering/SkinRenderer.cs +++ b/PCK-Studio/Rendering/SkinRenderer.cs @@ -101,7 +101,7 @@ namespace PckStudio.Rendering } } - [Description("Event that gets fired when the Texture is changing")] + [Description("Event that gets fired when the skin texture is changing")] [Category("Property Chnaged")] [Browsable(true)] public event EventHandler TextureChanging @@ -110,7 +110,7 @@ namespace PckStudio.Rendering remove => Events.RemoveHandler(nameof(TextureChanging), value); } - [Description("Event that gets fired when the Texture is changing")] + [Description("Event that gets fired when the cape texture is changing")] [Category("Property Chnaged")] [Browsable(true)] public event EventHandler CapeTextureChanging @@ -345,13 +345,13 @@ namespace PckStudio.Rendering leftArm.AddCube(new(-1, -2, -2), new(4, 12, 4), new(48, 48), inflate: OverlayScale); rightLeg ??= new CubeGroupMesh("Right Leg"); - rightLeg.Pivot = new Vector3(0f, 12f, 0f); + rightLeg.Pivot = new Vector3(-2f, 12f, 0f); rightLeg.Translation = new Vector3(-2f, -12f, 0f); rightLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 16)); rightLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 32), OverlayScale); leftLeg ??= new CubeGroupMesh("Left Leg"); - leftLeg.Pivot = new Vector3(0f, 12f, 0f); + leftLeg.Pivot = new Vector3(2f, 12f, 0f); leftLeg.Translation = new Vector3(2f, -12f, 0f); leftLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(16, 48)); leftLeg.AddCube(new(-2, 0, -2), new(4, 12, 4), new(0, 48), OverlayScale); @@ -642,22 +642,7 @@ namespace PckStudio.Rendering GLErrorCheck(); } -#if DEBUG - // Debug render - { - ColorVertex[] vertices = [ - new ColorVertex(Vector3.Zero, Color.White) - ]; - VertexArray vao = new VertexArray(); - var debugVBO = new VertexBuffer(); - debugVBO.SetData(vertices); - VertexBufferLayout layout = new VertexBufferLayout(); - layout.Add(ShaderDataType.Float3); - layout.Add(ShaderDataType.Float4); - vao.AddBuffer(debugVBO, layout); - d_debugDrawContext = new DrawContext(vao, debugVBO.GenIndexBuffer(), PrimitiveType.Points); - } -#endif + InitializeDebugShaders(); } private DrawContext GetGuidelineDrawContext() @@ -741,6 +726,16 @@ namespace PckStudio.Rendering } } + public Vector3 GetTranslation(string name) + { + return meshStorage.ContainsKey(name) ? meshStorage[name].Translation : Vector3.Zero; + } + + public Vector3 GetPivot(string name) + { + return meshStorage.ContainsKey(name) ? meshStorage[name].Pivot : Vector3.Zero; + } + public void SetPartOffset(SkinPartOffset offset) { SetPartOffset(offset.Type, offset.Value); @@ -988,9 +983,9 @@ namespace PckStudio.Rendering GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); GL.Enable(EnableCap.AlphaTest); // Enable transparent - GL.AlphaFunc(AlphaFunction.Greater, 0.4f); + GL.AlphaFunc(AlphaFunction.Greater, 0.0f); GL.DepthFunc(DepthFunction.Lequal); - + if (showWireFrame) GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); @@ -1305,6 +1300,42 @@ namespace PckStudio.Rendering } } + [Conditional("DEBUG")] + private void InitializeDebugShaders() + { +#if DEBUG + // Debug point render + { + ColorVertex[] vertices = [ + new ColorVertex(Vector3.Zero, Color.White) + ]; + VertexArray vao = new VertexArray(); + var debugVBO = new VertexBuffer(); + debugVBO.SetData(vertices); + VertexBufferLayout layout = new VertexBufferLayout(); + layout.Add(ShaderDataType.Float3); + layout.Add(ShaderDataType.Float4); + vao.AddBuffer(debugVBO, layout); + d_debugPointDrawContext = new DrawContext(vao, debugVBO.GenIndexBuffer(), PrimitiveType.Points); + } + // Debug point render + { + ColorVertex[] vertices = [ + new ColorVertex(Vector3.Zero), + new ColorVertex(Vector3.One) + ]; + VertexArray vao = new VertexArray(); + var debugVBO = new VertexBuffer(); + debugVBO.SetData(vertices); + VertexBufferLayout layout = new VertexBufferLayout(); + layout.Add(ShaderDataType.Float3); + layout.Add(ShaderDataType.Float4); + vao.AddBuffer(debugVBO, layout); + d_debugLineDrawContext = new DrawContext(vao, debugVBO.GenIndexBuffer(), PrimitiveType.Lines); + } +#endif + } + [Conditional("DEBUG")] private void RenderDebug() { @@ -1324,12 +1355,53 @@ namespace PckStudio.Rendering colorShader.SetUniform1("intensity", 0.75f); colorShader.SetUniform4("baseColor", Color.DeepPink); GL.PointSize(5f); - Renderer.Draw(colorShader, d_debugDrawContext); + Renderer.Draw(colorShader, d_debugPointDrawContext); GL.PointSize(1f); GL.DepthMask(true); GL.DepthFunc(DepthFunction.Less); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); } + if (d_showDirectionArrows) + { + GL.BlendFunc(BlendingFactor.DstAlpha, BlendingFactor.OneMinusSrcAlpha); + GL.DepthFunc(DepthFunction.Always); + GL.DepthMask(false); + GL.Enable(EnableCap.LineSmooth); + colorShader.Bind(); + + var viewProjection = Camera.GetViewProjection(); + var transform = Matrix4.Identity; + transform *= Matrix4.CreateTranslation(Vector3.Zero); + transform *= Matrix4.CreateScale(Camera.Distance / 4f).Inverted(); + transform.Invert(); + colorShader.SetUniformMat4("Transform", ref transform); + colorShader.SetUniformMat4("ViewProjection", ref viewProjection); + colorShader.SetUniform1("intensity", 0.75f); + + + Renderer.SetLineWidth(2f); + + colorShader.SetUniform4("baseColor", Color.Red); + ColorVertex[] line = [new ColorVertex(Vector3.Zero), new ColorVertex(Vector3.UnitX)]; + d_debugLineDrawContext.VertexArray.GetBuffer(0).SetData(line); + Renderer.Draw(colorShader, d_debugLineDrawContext); + + colorShader.SetUniform4("baseColor", Color.Green); + line = [new ColorVertex(Vector3.Zero), new ColorVertex(Vector3.UnitY)]; + d_debugLineDrawContext.VertexArray.GetBuffer(0).SetData(line); + Renderer.Draw(colorShader, d_debugLineDrawContext); + + colorShader.SetUniform4("baseColor", Color.Blue); + line = [new ColorVertex(Vector3.Zero), new ColorVertex(Vector3.UnitZ)]; + d_debugLineDrawContext.VertexArray.GetBuffer(0).SetData(line); + Renderer.Draw(colorShader, d_debugLineDrawContext); + + Renderer.SetLineWidth(1f); + + GL.DepthMask(true); + GL.DepthFunc(DepthFunction.Less); + GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); + } d_debugLabel.Text = Camera.ToString(); #endif } @@ -1388,6 +1460,11 @@ namespace PckStudio.Rendering debugShowFocalPointToolStripMenuItem.Click += (s, e) => d_showFocalPoint = debugShowFocalPointToolStripMenuItem.Checked; contextMenuStrip1.Items.Add(debugShowFocalPointToolStripMenuItem); + var debugShowDirectionArrows = new ToolStripMenuItem("Show Direction Arrows"); + debugShowDirectionArrows.CheckOnClick = true; + debugShowDirectionArrows.Click += (s, e) => d_showDirectionArrows = debugShowDirectionArrows.Checked; + contextMenuStrip1.Items.Add(debugShowDirectionArrows); + Controls.Add(d_debugLabel); this.contextMenuStrip1.ResumeLayout(false); @@ -1396,7 +1473,9 @@ namespace PckStudio.Rendering #if DEBUG private bool d_showFocalPoint; - private DrawContext d_debugDrawContext; + private bool d_showDirectionArrows; + private DrawContext d_debugPointDrawContext; + private DrawContext d_debugLineDrawContext; private Label d_debugLabel; private ToolStripMenuItem reToolStripMenuItem; private ContextMenuStrip contextMenuStrip1;