using System; using System.Collections.Generic; using Godot; namespace Cthangover.Live2D.Cubism.Framework { /// /// 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: /// /// and MULTIPLY the /// current scale by target / canvas-size (they do not assign it), /// exactly like the C++ original. /// The (width, height) constructor performs /// SetHeight(2.0f) like the SDK, i.e. the model is initially /// scaled to occupy 2 logical units of height. /// 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). /// CenterX/CenterY/Bottom/Right compute their offset from the full /// scaled size (canvas size × current scale). /// /// /// reads positioning keys from a Cubism /// .model3.json "Layout" section; writes the final /// transform to a Godot . /// public class CubismModelMatrix { private float _width; private float _height; private float _scaleX = 1f; private float _scaleY = 1f; private float _translateX; private float _translateY; /// Creates an identity matrix with no known canvas size. public CubismModelMatrix() { } /// /// Creates a matrix referenced to the given canvas dimensions. /// Like the SDK constructor, this calls (2.0f), /// so the model initially occupies 2 logical units of height. /// public CubismModelMatrix(float width, float height) { _width = width; _height = height; SetHeight(2.0f); } /// Current scale on the X axis. public float ScaleX => _scaleX; /// Current scale on the Y axis. public float ScaleY => _scaleY; /// Current X translation offset. public float TranslateX => _translateX; /// Current Y translation offset. public float TranslateY => _translateY; /// SDK CubismModelMatrix::Scale — multiplies the current scale. public void Scale(float scaleX, float scaleY) { _scaleX *= scaleX; _scaleY *= scaleY; } /// /// 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). /// public void SetWidth(float w) { if (_width <= 0f) return; var scaleX = w / _width; var scaleY = scaleX; Scale(scaleX, scaleY); } /// /// 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). /// public void SetHeight(float h) { if (_height <= 0f) return; var scaleX = h / _height; var scaleY = scaleX; Scale(scaleX, scaleY); } /// 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). public void Translate(float x, float y) { _translateX += x; _translateY += y; } /// SDK SetPosition — incremental (adds to the current translation). public void SetPosition(float x, float y) => Translate(x, y); /// SDK SetX — incremental (adds to the current X translation). public void SetX(float x) => _translateX += x; /// SDK SetY — incremental (adds to the current Y translation). public void SetY(float y) => _translateY += y; /// /// Centers the model horizontally and vertically at the given point. /// Equivalent to calling then . /// public void SetCenterPosition(float x, float y) { CenterX(x); CenterY(y); } /// SDK CenterX — adds x - (width × scaleX) / 2 to the X translation. public void CenterX(float x) { var w = _width * _scaleX; _translateX += x - (w / 2f); } /// SDK CenterY — adds y - (height × scaleY) / 2 to the Y translation. public void CenterY(float y) { var h = _height * _scaleY; _translateY += y - (h / 2f); } /// SDK Top — adds y to the Y translation (via SetY). public void Top(float y) => SetY(y); /// SDK Bottom — adds y - (height × scaleY) to the Y translation. public void Bottom(float y) { var h = _height * _scaleY; _translateY += y - h; } /// SDK Left — adds x to the X translation (via SetX). public void Left(float x) => SetX(x); /// SDK Right — adds x - (width × scaleX) to the X translation. public void Right(float x) { var w = _width * _scaleX; _translateX += x - w; } /// /// 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". /// public void SetupFromLayout(Dictionary 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; } } } /// /// Writes the current scale and translation to a Godot . /// Sets both Scale and Position properties. /// public void ApplyToNode(Node2D node) { node.Scale = new Vector2(_scaleX, _scaleY); node.Position = new Vector2(_translateX, _translateY); } } }