Files
mod_live2d_base/source_code/Cubism/Framework/CubismModelMatrix.cs
T
2026-10-16 16:18:18 +00:00

208 lines
7.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using Godot;
namespace Cthangover.Live2D.Cubism.Framework
{
/// <summary>
/// Port of CubismModelMatrix.cpp (Cubism SDK for Native 5-r.5).
/// Manages the 2D transformation (scale, translation, alignment) of a Cubism
/// model inside its logical coordinate space.
///
/// SDK semantics to be aware of:
/// <list type="bullet">
/// <item><see cref="SetWidth"/> and <see cref="SetHeight"/> MULTIPLY the
/// current scale by target / canvas-size (they do not assign it),
/// exactly like the C++ original.</item>
/// <item>The (width, height) constructor performs
/// <c>SetHeight(2.0f)</c> like the SDK, i.e. the model is initially
/// scaled to occupy 2 logical units of height.</item>
/// <item>Translation methods are INCREMENTAL in the SDK: SetX/SetY,
/// SetPosition, Top, Left (via TranslateX/Y) add to the current
/// translation. SetupFromLayout is unaffected because it starts from a
/// fresh matrix (translation 0).</item>
/// <item>CenterX/CenterY/Bottom/Right compute their offset from the full
/// scaled size (canvas size × current scale).</item>
/// </list>
///
/// <see cref="SetupFromLayout"/> reads positioning keys from a Cubism
/// .model3.json "Layout" section; <see cref="ApplyToNode"/> writes the final
/// transform to a Godot <see cref="Node2D"/>.
/// </summary>
public class CubismModelMatrix
{
private float _width;
private float _height;
private float _scaleX = 1f;
private float _scaleY = 1f;
private float _translateX;
private float _translateY;
/// <summary>Creates an identity matrix with no known canvas size.</summary>
public CubismModelMatrix() { }
/// <summary>
/// Creates a matrix referenced to the given canvas dimensions.
/// Like the SDK constructor, this calls <see cref="SetHeight"/>(2.0f),
/// so the model initially occupies 2 logical units of height.
/// </summary>
public CubismModelMatrix(float width, float height)
{
_width = width;
_height = height;
SetHeight(2.0f);
}
/// <summary>Current scale on the X axis.</summary>
public float ScaleX => _scaleX;
/// <summary>Current scale on the Y axis.</summary>
public float ScaleY => _scaleY;
/// <summary>Current X translation offset.</summary>
public float TranslateX => _translateX;
/// <summary>Current Y translation offset.</summary>
public float TranslateY => _translateY;
/// <summary>SDK CubismModelMatrix::Scale — multiplies the current scale.</summary>
public void Scale(float scaleX, float scaleY)
{
_scaleX *= scaleX;
_scaleY *= scaleY;
}
/// <summary>
/// SDK SetWidth: multiplies the current uniform scale by w / canvasWidth.
/// Ignored when the canvas width is zero or unknown (the SDK would divide by zero).
/// </summary>
public void SetWidth(float w)
{
if (_width <= 0f) return;
var scaleX = w / _width;
var scaleY = scaleX;
Scale(scaleX, scaleY);
}
/// <summary>
/// SDK SetHeight: multiplies the current uniform scale by h / canvasHeight.
/// Ignored when the canvas height is zero or unknown (the SDK would divide by zero).
/// </summary>
public void SetHeight(float h)
{
if (_height <= 0f) return;
var scaleX = h / _height;
var scaleY = scaleX;
Scale(scaleX, scaleY);
}
/// <summary>SDK Translate — adds x and y to the current translation
/// (the SDK's TranslateX/TranslateY helpers are folded in here; the
/// TranslateX/TranslateY properties above are the read accessors).</summary>
public void Translate(float x, float y)
{
_translateX += x;
_translateY += y;
}
/// <summary>SDK SetPosition — incremental (adds to the current translation).</summary>
public void SetPosition(float x, float y) => Translate(x, y);
/// <summary>SDK SetX — incremental (adds to the current X translation).</summary>
public void SetX(float x) => _translateX += x;
/// <summary>SDK SetY — incremental (adds to the current Y translation).</summary>
public void SetY(float y) => _translateY += y;
/// <summary>
/// Centers the model horizontally and vertically at the given point.
/// Equivalent to calling <see cref="CenterX"/> then <see cref="CenterY"/>.
/// </summary>
public void SetCenterPosition(float x, float y)
{
CenterX(x);
CenterY(y);
}
/// <summary>SDK CenterX — adds x - (width × scaleX) / 2 to the X translation.</summary>
public void CenterX(float x)
{
var w = _width * _scaleX;
_translateX += x - (w / 2f);
}
/// <summary>SDK CenterY — adds y - (height × scaleY) / 2 to the Y translation.</summary>
public void CenterY(float y)
{
var h = _height * _scaleY;
_translateY += y - (h / 2f);
}
/// <summary>SDK Top — adds y to the Y translation (via SetY).</summary>
public void Top(float y) => SetY(y);
/// <summary>SDK Bottom — adds y - (height × scaleY) to the Y translation.</summary>
public void Bottom(float y)
{
var h = _height * _scaleY;
_translateY += y - h;
}
/// <summary>SDK Left — adds x to the X translation (via SetX).</summary>
public void Left(float x) => SetX(x);
/// <summary>SDK Right — adds x - (width × scaleX) to the X translation.</summary>
public void Right(float x)
{
var w = _width * _scaleX;
_translateX += x - w;
}
/// <summary>
/// SDK SetupFromLayout — configures scale and position from a layout
/// dictionary (lowercased keys). Width/height are applied first (they
/// affect scale), then the position keys: "x", "y", "center_x",
/// "center_y", "top", "bottom", "left", "right".
/// </summary>
public void SetupFromLayout(Dictionary<string, float> layout)
{
foreach (var kvp in layout)
{
switch (kvp.Key)
{
case "width": SetWidth(kvp.Value); break;
case "height": SetHeight(kvp.Value); break;
}
}
foreach (var kvp in layout)
{
switch (kvp.Key)
{
case "x": SetX(kvp.Value); break;
case "y": SetY(kvp.Value); break;
case "center_x": CenterX(kvp.Value); break;
case "center_y": CenterY(kvp.Value); break;
case "top": Top(kvp.Value); break;
case "bottom": Bottom(kvp.Value); break;
case "left": Left(kvp.Value); break;
case "right": Right(kvp.Value); break;
}
}
}
/// <summary>
/// Writes the current scale and translation to a Godot <see cref="Node2D"/>.
/// Sets both <c>Scale</c> and <c>Position</c> properties.
/// </summary>
public void ApplyToNode(Node2D node)
{
node.Scale = new Vector2(_scaleX, _scaleY);
node.Position = new Vector2(_translateX, _translateY);
}
}
}