@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// Represents single blend point.
|
/// Represents single blend point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||||
protected class BlendPoint : Control
|
internal class BlendPoint : Control
|
||||||
{
|
{
|
||||||
private readonly BlendPointsEditor _editor;
|
private readonly BlendPointsEditor _editor;
|
||||||
private readonly int _index;
|
private readonly int _index;
|
||||||
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Index => _index;
|
public int Index => _index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag that indicates that user is moving this point with a mouse.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMouseDown => _isMouseDown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BLend points array.
|
||||||
|
/// </summary>
|
||||||
|
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <returns>The blend point control position.</returns>
|
/// <returns>The blend point control position.</returns>
|
||||||
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
||||||
{
|
{
|
||||||
|
if (_rangeX.IsZero)
|
||||||
|
{
|
||||||
|
var data0 = (Float4)_node.Values[0];
|
||||||
|
_rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||||
|
}
|
||||||
GetPointsArea(out var pointsArea);
|
GetPointsArea(out var pointsArea);
|
||||||
if (_is2D)
|
if (_is2D)
|
||||||
{
|
{
|
||||||
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
pointsArea.Center.Y
|
pointsArea.Center.Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
SetAsset((int)b.Tag, Guid.Empty);
|
SetAsset((int)b.Tag, Guid.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
|
||||||
{
|
|
||||||
// Draw line
|
|
||||||
Render2D.DrawLine(start, end, gridColor);
|
|
||||||
|
|
||||||
// Draw label
|
|
||||||
var labelWidth = 50.0f;
|
|
||||||
var labelHeight = 10.0f;
|
|
||||||
var labelMargin = 2.0f;
|
|
||||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
|
||||||
var hAlign = TextAlignment.Near;
|
|
||||||
Rectangle labelRect;
|
|
||||||
if (vertical)
|
|
||||||
{
|
|
||||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
|
||||||
if (isLast)
|
|
||||||
return; // Don't overlap with the first horizontal label
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
|
||||||
if (isLast)
|
|
||||||
{
|
|
||||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
|
||||||
hAlign = TextAlignment.Far;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var rect = new Rectangle(Float2.Zero, Size);
|
var rect = new Rectangle(Float2.Zero, Size);
|
||||||
var containsFocus = ContainsFocus;
|
var containsFocus = ContainsFocus;
|
||||||
GetPointsArea(out var pointsArea);
|
|
||||||
var data0 = (Float4)_node.Values[0];
|
|
||||||
var rangeX = new Float2(data0.X, data0.Y);
|
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
_node.DrawEditorBackground(ref rect);
|
||||||
//Render2D.DrawRectangle(pointsArea, Color.Red);
|
|
||||||
|
|
||||||
// Grid
|
// Grid
|
||||||
int splits = 10;
|
_node.DrawEditorGrid(ref rect);
|
||||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
|
||||||
var labelColor = style.ForegroundDisabled;
|
|
||||||
var labelFont = style.FontSmall;
|
|
||||||
//var blendArea = BlendAreaRect;
|
|
||||||
var blendArea = pointsArea;
|
|
||||||
|
|
||||||
for (int i = 0; i <= splits; i++)
|
|
||||||
{
|
|
||||||
float alpha = (float)i / splits;
|
|
||||||
float x = blendArea.Left + blendArea.Width * alpha;
|
|
||||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
|
||||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
|
||||||
}
|
|
||||||
if (_is2D)
|
|
||||||
{
|
|
||||||
var rangeY = new Float2(data0.Z, data0.W);
|
|
||||||
for (int i = 0; i <= splits; i++)
|
|
||||||
{
|
|
||||||
float alpha = (float)i / splits;
|
|
||||||
float y = blendArea.Top + blendArea.Height * alpha;
|
|
||||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
|
||||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float y = blendArea.Center.Y;
|
|
||||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||||
|
{
|
||||||
|
// Draw line
|
||||||
|
Render2D.DrawLine(start, end, gridColor);
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
var labelWidth = 50.0f;
|
||||||
|
var labelHeight = 10.0f;
|
||||||
|
var labelMargin = 2.0f;
|
||||||
|
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
var hAlign = TextAlignment.Near;
|
||||||
|
Rectangle labelRect;
|
||||||
|
if (vertical)
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
return; // Don't overlap with the first horizontal label
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
{
|
||||||
|
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||||
|
hAlign = TextAlignment.Far;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom drawing logic for blend space background.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void DrawEditorBackground(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
|
||||||
|
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom drawing logic for blend space grid.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void DrawEditorGrid(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
_editor.GetPointsArea(out var pointsArea);
|
||||||
|
var data0 = (Float4)Values[0];
|
||||||
|
var rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
int splits = 10;
|
||||||
|
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||||
|
var labelColor = style.ForegroundDisabled;
|
||||||
|
var labelFont = style.FontSmall;
|
||||||
|
//var blendArea = BlendAreaRect;
|
||||||
|
var blendArea = pointsArea;
|
||||||
|
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
|
{
|
||||||
|
float alpha = (float)i / splits;
|
||||||
|
float x = blendArea.Left + blendArea.Width * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||||
|
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
|
}
|
||||||
|
if (_editor.Is2D)
|
||||||
|
{
|
||||||
|
var rangeY = new Float2(data0.Z, data0.W);
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
|
{
|
||||||
|
float alpha = (float)i / splits;
|
||||||
|
float y = blendArea.Top + blendArea.Height * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
||||||
|
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float y = blendArea.Center.Y;
|
||||||
|
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the editor UI.
|
/// Updates the editor UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -972,6 +1006,10 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private readonly FloatValueBox _animationX;
|
private readonly FloatValueBox _animationX;
|
||||||
private readonly Label _animationYLabel;
|
private readonly Label _animationYLabel;
|
||||||
private readonly FloatValueBox _animationY;
|
private readonly FloatValueBox _animationY;
|
||||||
|
private Float2[] _triangles;
|
||||||
|
private Color[] _triangleColors;
|
||||||
|
private Float2[] _selectedTriangles;
|
||||||
|
private Color[] _selectedColors;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
@@ -1040,6 +1078,143 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ClearTriangles()
|
||||||
|
{
|
||||||
|
// Remove cache
|
||||||
|
_triangles = null;
|
||||||
|
_triangleColors = null;
|
||||||
|
_selectedTriangles = null;
|
||||||
|
_selectedColors = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheTriangles()
|
||||||
|
{
|
||||||
|
// Get locations of blend point vertices
|
||||||
|
int pointsCount = _editor.PointsCount;
|
||||||
|
int count = 0, j = 0;
|
||||||
|
for (int i = 0; i < pointsCount; i++)
|
||||||
|
{
|
||||||
|
var animId = (Guid)Values[5 + i * 2];
|
||||||
|
if (animId != Guid.Empty)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
var vertices = new Float2[count];
|
||||||
|
for (int i = 0; i < pointsCount; i++)
|
||||||
|
{
|
||||||
|
var animId = (Guid)Values[5 + i * 2];
|
||||||
|
if (animId != Guid.Empty)
|
||||||
|
{
|
||||||
|
var dataA = (Float4)Values[4 + i * 2];
|
||||||
|
vertices[j++] = new Float2(dataA.X, dataA.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulate
|
||||||
|
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
|
||||||
|
_triangleColors = null;
|
||||||
|
|
||||||
|
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
|
||||||
|
if (_triangles == null || _triangles.Length == 0)
|
||||||
|
{
|
||||||
|
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
|
||||||
|
var triangles = new List<Float2>();
|
||||||
|
int verticesLeft = vertices.Length;
|
||||||
|
while (verticesLeft >= 3)
|
||||||
|
{
|
||||||
|
verticesLeft -= 3;
|
||||||
|
triangles.Add(vertices[verticesLeft + 0]);
|
||||||
|
triangles.Add(vertices[verticesLeft + 1]);
|
||||||
|
triangles.Add(vertices[verticesLeft + 2]);
|
||||||
|
}
|
||||||
|
if (verticesLeft == 1)
|
||||||
|
{
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
}
|
||||||
|
else if (verticesLeft == 2)
|
||||||
|
{
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[1]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
}
|
||||||
|
_triangles = triangles.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project to the blend space for drawing
|
||||||
|
for (int i = 0; i < _triangles.Length; i++)
|
||||||
|
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
|
||||||
|
|
||||||
|
// Check if anything is selected
|
||||||
|
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||||
|
if (selectedIndex != -1)
|
||||||
|
{
|
||||||
|
// Find triangles that contain selected point
|
||||||
|
var dataA = (Float4)Values[4 + selectedIndex * 2];
|
||||||
|
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
|
||||||
|
var selectedTriangles = new List<Float2>();
|
||||||
|
var selectedColors = new List<Color>();
|
||||||
|
var style = Style.Current;
|
||||||
|
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
|
||||||
|
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
|
||||||
|
_triangleColors = new Color[_triangles.Length];
|
||||||
|
for (int i = 0; i < _triangles.Length; i += 3)
|
||||||
|
{
|
||||||
|
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
|
||||||
|
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
|
||||||
|
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
|
||||||
|
if (is0 || is1 || is2)
|
||||||
|
{
|
||||||
|
selectedTriangles.Add(_triangles[i + 0]);
|
||||||
|
selectedTriangles.Add(_triangles[i + 1]);
|
||||||
|
selectedTriangles.Add(_triangles[i + 2]);
|
||||||
|
selectedColors.Add(is0 ? Color.White : Color.Transparent);
|
||||||
|
selectedColors.Add(is1 ? Color.White : Color.Transparent);
|
||||||
|
selectedColors.Add(is2 ? Color.White : Color.Transparent);
|
||||||
|
}
|
||||||
|
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
|
||||||
|
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
|
||||||
|
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
|
||||||
|
}
|
||||||
|
_selectedTriangles = selectedTriangles.ToArray();
|
||||||
|
_selectedColors = selectedColors.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void DrawEditorBackground(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
base.DrawEditorBackground(ref rect);
|
||||||
|
|
||||||
|
// Draw triangulated multi blend space
|
||||||
|
var style = Style.Current;
|
||||||
|
if (_triangles == null)
|
||||||
|
CacheTriangles();
|
||||||
|
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
|
||||||
|
Render2D.FillTriangles(_triangles, _triangleColors);
|
||||||
|
else
|
||||||
|
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
|
||||||
|
Render2D.DrawTriangles(_triangles, style.Foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void DrawEditorGrid(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
base.DrawEditorGrid(ref rect);
|
||||||
|
|
||||||
|
// Highlight selected blend point
|
||||||
|
var style = Style.Current;
|
||||||
|
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||||
|
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
|
||||||
|
{
|
||||||
|
var point = _editor.BlendPoints[selectedIndex];
|
||||||
|
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
|
||||||
|
Render2D.PushTint(ref highlightColor);
|
||||||
|
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
|
||||||
|
Render2D.PopTint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||||
{
|
{
|
||||||
@@ -1064,6 +1239,23 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_animationX.Enabled = isValid;
|
_animationX.Enabled = isValid;
|
||||||
_animationYLabel.Enabled = isValid;
|
_animationYLabel.Enabled = isValid;
|
||||||
_animationY.Enabled = isValid;
|
_animationY.Enabled = isValid;
|
||||||
|
ClearTriangles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnValuesChanged()
|
||||||
|
{
|
||||||
|
base.OnValuesChanged();
|
||||||
|
|
||||||
|
ClearTriangles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
|
{
|
||||||
|
base.OnLoaded(action);
|
||||||
|
|
||||||
|
ClearTriangles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,12 +301,12 @@ void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2
|
|||||||
IBIndex += 3;
|
IBIndex += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2)
|
FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2)
|
||||||
{
|
{
|
||||||
WriteTri(p0, p1, p2, Float2::Zero, Float2::Zero, Float2::Zero, color0, color1, color2);
|
WriteTri(p0, p1, p2, Float2::Zero, Float2::Zero, Float2::Zero, color0, color1, color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2)
|
FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2)
|
||||||
{
|
{
|
||||||
WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black);
|
WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black);
|
||||||
}
|
}
|
||||||
@@ -1816,8 +1816,8 @@ void DrawLines(const Float2* points, int32 pointsCount, const Color& color1, con
|
|||||||
|
|
||||||
// Ending cap
|
// Ending cap
|
||||||
{
|
{
|
||||||
ApplyTransform(points[0], p1t);
|
ApplyTransform(points[pointsCount - 2], p1t);
|
||||||
ApplyTransform(points[1], p2t);
|
//ApplyTransform(points[pointsCount - 1], p2t);
|
||||||
|
|
||||||
const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t);
|
const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t);
|
||||||
|
|
||||||
@@ -1962,9 +1962,56 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
|
|||||||
WriteRect(rect, Color::White);
|
WriteRect(rect, Color::White);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render2D::DrawTriangles(const Span<Float2>& vertices, const Color& color, float thickness)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
|
|
||||||
|
Float2 points[2];
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// TODO: fix this
|
||||||
|
DrawLines(&vertices.Get()[i], 3, color, color, thickness);
|
||||||
|
#else
|
||||||
|
points[0] = vertices.Get()[i + 0];
|
||||||
|
points[1] = vertices.Get()[i + 1];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
points[0] = vertices.Get()[i + 2];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
points[1] = vertices.Get()[i + 0];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render2D::DrawTriangles(const Span<Float2>& vertices, const Span<Color>& colors, float thickness)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
|
|
||||||
|
Float2 points[2];
|
||||||
|
Color cols[2];
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
{
|
||||||
|
points[0] = vertices.Get()[i + 0];
|
||||||
|
points[1] = vertices.Get()[i + 1];
|
||||||
|
cols[0] = colors.Get()[i + 0];
|
||||||
|
cols[1] = colors.Get()[i + 1];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
points[0] = vertices.Get()[i + 2];
|
||||||
|
cols[0] = colors.Get()[i + 2];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
points[1] = vertices.Get()[i + 0];
|
||||||
|
cols[1] = colors.Get()[i + 0];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
|
||||||
{
|
{
|
||||||
RENDER2D_CHECK_RENDERING_STATE;
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
CHECK(vertices.Length() == uvs.Length());
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
|
|
||||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
@@ -1979,14 +2026,24 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices
|
|||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Color& color)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Color& color)
|
||||||
{
|
{
|
||||||
Color colors[3] = { (Color)color, (Color)color, (Color)color };
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
Span<Color> spancolor(colors, 3);
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
DrawTexturedTriangles(t, vertices, uvs, spancolor);
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
|
|
||||||
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
|
drawCall.Type = DrawCallType::FillTexture;
|
||||||
|
drawCall.StartIB = IBIndex;
|
||||||
|
drawCall.CountIB = vertices.Length();
|
||||||
|
drawCall.AsTexture.Ptr = t;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], uvs[i], uvs[i + 1], uvs[i + 2], color, color, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors)
|
||||||
{
|
{
|
||||||
RENDER2D_CHECK_RENDERING_STATE;
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
CHECK(vertices.Length() == uvs.Length());
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
CHECK(vertices.Length() == colors.Length());
|
CHECK(vertices.Length() == colors.Length());
|
||||||
|
|
||||||
@@ -2021,6 +2078,19 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render2D::FillTriangles(const Span<Float2>& vertices, const Color& color)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
|
||||||
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
|
drawCall.Type = NeedAlphaWithTint(color) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||||
|
drawCall.StartIB = IBIndex;
|
||||||
|
drawCall.CountIB = vertices.Length();
|
||||||
|
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], color, color, color);
|
||||||
|
}
|
||||||
|
|
||||||
void Render2D::FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha)
|
void Render2D::FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha)
|
||||||
{
|
{
|
||||||
CHECK(vertices.Length() == colors.Length());
|
CHECK(vertices.Length() == colors.Length());
|
||||||
|
|||||||
@@ -415,6 +415,22 @@ public:
|
|||||||
/// <param name="blurStrength">The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.</param>
|
/// <param name="blurStrength">The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.</param>
|
||||||
API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength);
|
API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
/// <param name="thickness">The line thickness.</param>
|
||||||
|
API_FUNCTION() static void DrawTriangles(const Span<Float2>& vertices, const Color& color, float thickness = 1.0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="colors">The colors array.</param>
|
||||||
|
/// <param name="thickness">The line thickness.</param>
|
||||||
|
API_FUNCTION() static void DrawTriangles(const Span<Float2>& vertices, const Span<Color>& colors, float thickness = 1.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws vertices array.
|
/// Draws vertices array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -451,13 +467,20 @@ public:
|
|||||||
/// <param name="colors">The colors array.</param>
|
/// <param name="colors">The colors array.</param>
|
||||||
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors);
|
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Color& color);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws vertices array.
|
/// Draws vertices array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="vertices">The vertices array.</param>
|
/// <param name="vertices">The vertices array.</param>
|
||||||
/// <param name="colors">The colors array.</param>
|
/// <param name="colors">The colors array.</param>
|
||||||
/// <param name="useAlpha">If true alpha blending will be enabled.</param>
|
/// <param name="useAlpha">If true alpha blending will be enabled.</param>
|
||||||
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha);
|
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills a triangular area.
|
/// Fills a triangular area.
|
||||||
|
|||||||
@@ -7,10 +7,11 @@
|
|||||||
#include "Engine/Core/Collections/Array.h"
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper class with Delaunay triangulation algorithm implementation,
|
/// Helper class with Delaunay triangulation algorithm implementation (2D space).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class Delaunay2D
|
API_CLASS(Internal, Static, Namespace="FlaxEngine.Utilities") class Delaunay2D
|
||||||
{
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(Delaunay2D);
|
||||||
public:
|
public:
|
||||||
struct Triangle
|
struct Triangle
|
||||||
{
|
{
|
||||||
@@ -31,14 +32,35 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TVertexArray, typename TTrianglesArray>
|
/// <summary>
|
||||||
static void Triangulate(const TVertexArray& vertices, TTrianglesArray& triangles)
|
/// Triangulates input vertices array into the list of triangle vertices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">Input list of vertices.</param>
|
||||||
|
/// <returns>Result list of triangles. Each triangle is made out of sequence of 3 vertices. Empty if no valid triangle built.</returns>
|
||||||
|
API_FUNCTION() static Array<Float2> Triangulate(const Array<Float2>& vertices)
|
||||||
|
{
|
||||||
|
Array<Triangle> triangles;
|
||||||
|
Triangulate(vertices, triangles);
|
||||||
|
Array<Float2> result;
|
||||||
|
result.Resize(triangles.Count() * 3);
|
||||||
|
int32 c = 0;
|
||||||
|
for (const Triangle& t : triangles)
|
||||||
|
{
|
||||||
|
result.Get()[c++] = vertices[t.Indices[0]];
|
||||||
|
result.Get()[c++] = vertices[t.Indices[1]];
|
||||||
|
result.Get()[c++] = vertices[t.Indices[2]];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TrianglesArray = Triangle>
|
||||||
|
static void Triangulate(const Array<Float2>& vertices, TrianglesArray& triangles)
|
||||||
{
|
{
|
||||||
// Skip if no change to produce any triangles
|
// Skip if no change to produce any triangles
|
||||||
if (vertices.Count() < 3)
|
if (vertices.Count() < 3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TVertexArray points = vertices;
|
Array<Float2> points = vertices;
|
||||||
Array<Edge> polygon;
|
Array<Edge> polygon;
|
||||||
|
|
||||||
Rectangle rect;
|
Rectangle rect;
|
||||||
@@ -142,8 +164,7 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TVertexArray>
|
static bool CircumCircleContains(const Array<Float2>& vertices, const Triangle& triangle, int32 vertexIndex)
|
||||||
static bool CircumCircleContains(const TVertexArray& vertices, const Triangle& triangle, int vertexIndex)
|
|
||||||
{
|
{
|
||||||
Float2 p1 = vertices[triangle.Indices[0]];
|
Float2 p1 = vertices[triangle.Indices[0]];
|
||||||
Float2 p2 = vertices[triangle.Indices[1]];
|
Float2 p2 = vertices[triangle.Indices[1]];
|
||||||
@@ -157,25 +178,20 @@ private:
|
|||||||
(ab * (p3.Y - p2.Y) + cd * (p1.Y - p3.Y) + ef * (p2.Y - p1.Y)) / (p1.X * (p3.Y - p2.Y) + p2.X * (p1.Y - p3.Y) + p3.X * (p2.Y - p1.Y)),
|
(ab * (p3.Y - p2.Y) + cd * (p1.Y - p3.Y) + ef * (p2.Y - p1.Y)) / (p1.X * (p3.Y - p2.Y) + p2.X * (p1.Y - p3.Y) + p3.X * (p2.Y - p1.Y)),
|
||||||
(ab * (p3.X - p2.X) + cd * (p1.X - p3.X) + ef * (p2.X - p1.X)) / (p1.Y * (p3.X - p2.X) + p2.Y * (p1.X - p3.X) + p3.Y * (p2.X - p1.X)));
|
(ab * (p3.X - p2.X) + cd * (p1.X - p3.X) + ef * (p2.X - p1.X)) / (p1.Y * (p3.X - p2.X) + p2.Y * (p1.X - p3.X) + p3.Y * (p2.X - p1.X)));
|
||||||
|
|
||||||
circum *= 0.5;
|
circum *= 0.5f;
|
||||||
float r = Float2::DistanceSquared(p1, circum);
|
float r = Float2::DistanceSquared(p1, circum);
|
||||||
float d = Float2::DistanceSquared(vertices[vertexIndex], circum);
|
float d = Float2::DistanceSquared(vertices[vertexIndex], circum);
|
||||||
return d <= r;
|
return d <= r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TVertexArray>
|
static bool EdgeCompare(const Array<Float2>& vertices, const Edge& a, const Edge& b)
|
||||||
static bool EdgeCompare(const TVertexArray& vertices, const Edge& a, const Edge& b)
|
|
||||||
{
|
{
|
||||||
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance)
|
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance &&
|
||||||
{
|
Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance)
|
||||||
return true;
|
return true;
|
||||||
}
|
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance &&
|
||||||
|
Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
|
||||||
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user