Merge remote-tracking branch 'origin/master' into 1.10

This commit is contained in:
Wojtek Figat
2024-12-10 11:07:31 +01:00
105 changed files with 2570 additions and 653 deletions

View File

@@ -66,27 +66,23 @@ namespace AnimationUtils
}
template<class T>
FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result)
FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result)
{
const float oneThird = 1.0f / 3.0f;
result = a + b * (length * oneThird);
result = value + tangent * tangentScale;
}
template<>
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result)
{
const float oneThird = 1.0f / 3.0f;
Quaternion::Slerp(a, b, oneThird, result);
Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result);
}
template<>
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
FORCE_INLINE void GetTangent<Transform>(const Transform& value, const Transform& tangent, float tangentScale, Transform& result)
{
const float oneThird = 1.0f / 3.0f;
const float oneThirdLength = length * oneThird;
result.Translation = a.Translation + b.Translation * oneThirdLength;
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength;
GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation);
GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation);
GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale);
}
template<class T>

View File

@@ -1,8 +1,10 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEngine.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace FlaxEngine
@@ -24,9 +26,9 @@ namespace FlaxEngine
/// </summary>
/// <param name="value">The value.</param>
/// <param name="tangent">The tangent.</param>
/// <param name="lengthThird">The length divided by 3.</param>
/// <param name="tangentScale">The tangent scale factor.</param>
/// <param name="result">The result.</param>
void GetTangent(ref U value, ref U tangent, float lengthThird, out U result);
void GetTangent(ref U value, ref U tangent, float tangentScale, out U result);
/// <summary>
/// Calculates the linear interpolation at the specified alpha.
@@ -67,7 +69,7 @@ namespace FlaxEngine
IKeyframeAccess<Color32>,
IKeyframeAccess<Color>
{
public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result)
public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result)
{
result = value;
}
@@ -82,9 +84,9 @@ namespace FlaxEngine
result = p0;
}
public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result)
public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result)
{
result = value + (int)(tangent * lengthThird);
result = value + (int)(tangent * tangentScale);
}
public void Linear(ref int a, ref int b, float alpha, out int result)
@@ -102,9 +104,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result)
public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref double a, ref double b, float alpha, out double result)
@@ -122,9 +124,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result)
public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref float a, ref float b, float alpha, out float result)
@@ -142,9 +144,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result)
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
@@ -162,9 +164,9 @@ namespace FlaxEngine
Vector2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result)
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
@@ -182,9 +184,9 @@ namespace FlaxEngine
Vector3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result)
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
@@ -202,9 +204,9 @@ namespace FlaxEngine
Vector4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result)
public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
@@ -222,9 +224,9 @@ namespace FlaxEngine
Float2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result)
public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
@@ -242,9 +244,9 @@ namespace FlaxEngine
Float3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result)
public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
@@ -262,9 +264,9 @@ namespace FlaxEngine
Float4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result)
public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
@@ -282,9 +284,9 @@ namespace FlaxEngine
Double2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result)
public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
@@ -302,9 +304,9 @@ namespace FlaxEngine
Double3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result)
public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
@@ -322,7 +324,7 @@ namespace FlaxEngine
Double4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result)
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result)
{
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
}
@@ -342,9 +344,9 @@ namespace FlaxEngine
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result)
public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
@@ -362,9 +364,9 @@ namespace FlaxEngine
Color32.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result)
public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
@@ -454,6 +456,40 @@ namespace FlaxEngine
time = end;
}
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="keyframes">The keyframes array.</param>
/// <returns>The raw keyframes data.</returns>
protected static unsafe byte[] MarshalKeyframes<Keyframe>(Keyframe[] keyframes)
{
if (keyframes == null || keyframes.Length == 0)
return null;
var keyframeSize = Unsafe.SizeOf<Keyframe>();
var result = new byte[keyframes.Length * keyframeSize];
fixed (byte* resultPtr = result)
{
var keyframesHandle = ManagedHandle.Alloc(keyframes, GCHandleType.Pinned);
var keyframesPtr = Marshal.UnsafeAddrOfPinnedArrayElement(keyframes, 0);
Buffer.MemoryCopy((void*)keyframesPtr, resultPtr, (uint)result.Length, (uint)result.Length);
keyframesHandle.Free();
}
return result;
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The keyframes array.</returns>
protected static unsafe Keyframe[] MarshalKeyframes<Keyframe>(byte[] raw) where Keyframe : struct
{
if (raw == null || raw.Length == 0)
return null;
fixed (byte* rawPtr = raw)
return MemoryMarshal.Cast<byte, Keyframe>(new Span<byte>(rawPtr, raw.Length)).ToArray();
}
}
/// <summary>
@@ -709,6 +745,30 @@ namespace FlaxEngine
leftKey = Mathf.Max(0, start - 1);
rightKey = Mathf.Min(start, Keyframes.Length - 1);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="curve">The curve to copy.</param>
/// <returns>The raw keyframes data.</returns>
public static unsafe implicit operator byte[](LinearCurve<T> curve)
{
if (curve == null)
return null;
return MarshalKeyframes<Keyframe>(curve.Keyframes);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The curve.</returns>
public static unsafe implicit operator LinearCurve<T>(byte[] raw)
{
if (raw == null || raw.Length == 0)
return null;
return new LinearCurve<T>(MarshalKeyframes<Keyframe>(raw));
}
}
/// <summary>
@@ -860,9 +920,9 @@ namespace FlaxEngine
// Evaluate the key at the curve
result.Time = leftKey.Time + length * t;
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
result.TangentIn = leftKey.TangentOut;
result.TangentOut = rightKey.TangentIn;
@@ -895,9 +955,9 @@ namespace FlaxEngine
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
// Evaluate the value at the curve
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
}
@@ -1000,5 +1060,29 @@ namespace FlaxEngine
leftKey = Mathf.Max(0, start - 1);
rightKey = Mathf.Min(start, Keyframes.Length - 1);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="curve">The curve to copy.</param>
/// <returns>The raw keyframes data.</returns>
public static unsafe implicit operator byte[](BezierCurve<T> curve)
{
if (curve == null)
return null;
return MarshalKeyframes<Keyframe>(curve.Keyframes);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The curve.</returns>
public static unsafe implicit operator BezierCurve<T>(byte[] raw)
{
if (raw == null || raw.Length == 0)
return null;
return new BezierCurve<T>(MarshalKeyframes<Keyframe>(raw));
}
}
}

View File

@@ -247,16 +247,18 @@ public:
static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
{
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
}
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
{
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
}
@@ -264,8 +266,9 @@ public:
{
result.Time = a.Time + length * alpha;
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
result.TangentIn = a.TangentOut;
result.TangentOut = b.TangentIn;
@@ -498,7 +501,7 @@ protected:
/// An animation spline represented by a set of keyframes, each representing an endpoint of a curve.
/// </summary>
template<class T, typename KeyFrame = LinearCurveKeyframe<T>>
class Curve : public CurveBase<T, KeyFrame>
API_CLASS(InBuild, Template, MarshalAs=Span<byte>) class Curve : public CurveBase<T, KeyFrame>
{
public:
typedef CurveBase<T, KeyFrame> Base;
@@ -760,28 +763,42 @@ public:
}
return true;
}
// Raw memory copy (used by scripting bindings - see MarshalAs tag).
Curve& operator=(const Span<byte>& raw)
{
ASSERT((raw.Length() % sizeof(KeyFrame)) == 0);
const int32 count = raw.Length() / sizeof(KeyFrame);
_keyframes.Resize(count, false);
Platform::MemoryCopy(_keyframes.Get(), raw.Get(), sizeof(KeyFrame) * count);
return *this;
}
operator Span<byte>()
{
return Span<byte>((const byte*)_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame));
}
};
/// <summary>
/// An animation spline represented by a set of keyframes, each representing a value point.
/// </summary>
template<typename T>
using StepCurve = Curve<T, StepCurveKeyframe<T>>;
API_TYPEDEF() using StepCurve = Curve<T, StepCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
/// </summary>
template<typename T>
using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
API_TYPEDEF() using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve.
/// </summary>
template<typename T>
using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
API_TYPEDEF() using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve.
/// </summary>
template<typename T>
using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
API_TYPEDEF() using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;

View File

@@ -2263,6 +2263,14 @@ void VisualScript::GetMethodSignature(int32 index, String& name, byte& flags, St
}
}
Variant VisualScript::InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const
{
auto& method = _methods[index];
Variant result;
VisualScriptingModule.InvokeMethod((void*)&method, instance, parameters, result);
return result;
}
Span<byte> VisualScript::GetMetaData(int32 typeID)
{
auto meta = Graph.Meta.GetEntry(typeID);

View File

@@ -267,6 +267,9 @@ public:
// Gets the signature data of the method.
API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array<String>& paramNames, API_PARAM(Out) Array<String>& paramTypeNames, API_PARAM(Out) Array<bool>& paramOuts);
// Invokes the method.
API_FUNCTION() Variant InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const;
// Gets the metadata of the script surface.
API_FUNCTION() Span<byte> GetMetaData(int32 typeID);

View File

@@ -21,8 +21,8 @@ namespace FlaxEditor.Content.Settings
{
new BuildTarget
{
Name = "Windows 64bit",
Output = "Output\\Win64",
Name = "Windows",
Output = "Output\\Windows",
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Development,
},
@@ -35,8 +35,8 @@ namespace FlaxEditor.Content.Settings
{
new BuildTarget
{
Name = "Windows 64bit",
Output = "Output\\Win64",
Name = "Windows",
Output = "Output\\Windows",
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Release,
},

View File

@@ -16,7 +16,7 @@ namespace FlaxEditor.Content.Settings
public List<string> Tags = new List<string>();
/// <summary>
/// The layers names.
/// The layer names.
/// </summary>
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
public string[] Layers = new string[32];
@@ -30,6 +30,31 @@ namespace FlaxEditor.Content.Settings
return GetCurrentLayers(out int _);
}
/// <summary>
/// The layer names.
/// </summary>
[EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
public string[] TerrainLayers = new string[8];
/// <summary>
/// Gets the current terrain layer names. Returns "Layer" + index for layers without a name.
/// </summary>
/// <returns>The layer names.</returns>
public static string[] GetCurrentTerrainLayers()
{
#if FLAX_TESTS
return System.Array.Empty<string>();
#else
string[] layerNames = GameSettings.Load<LayersAndTagsSettings>().TerrainLayers;
for (int i = 0; i < layerNames.Length; i++)
{
if (string.IsNullOrEmpty(layerNames[i]))
layerNames[i] = $"Layer {i}";
}
return layerNames;
#endif
}
[LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "layerCount")]
internal static partial string[] GetCurrentLayers(out int layerCount);

View File

@@ -16,10 +16,14 @@
#include <iostream>
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
namespace
{
bool LogAfterInit = false, IsDuringLog = false;
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
bool IsWindowsSingleNewLineChar = false;
#endif
int LogTotalErrorsCnt = 0;
FileWriteStream* LogFile = nullptr;
CriticalSection LogLocker;
@@ -86,6 +90,11 @@ bool Log::Logger::Init()
}
LogTotalErrorsCnt = 0;
LogAfterInit = true;
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
String envVar;
Platform::GetEnvironmentVariable(TEXT("GITHUB_ACTION"), envVar);
IsWindowsSingleNewLineChar = envVar.HasChars();
#endif
// Write BOM (UTF-16 (LE); BOM: FF FE)
byte bom[] = { 0xFF, 0xFE };
@@ -127,6 +136,11 @@ void Log::Logger::Write(const StringView& msg)
printf("%s", ansi.Get());
#else
std::wcout.write(ptr, length);
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
if (IsWindowsSingleNewLineChar)
std::wcout.write(TEXT("\n"), 1); // Github Actions show logs with duplicated new-line characters so skip \r
else
#endif
std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1);
#endif
}

View File

@@ -124,9 +124,9 @@ void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, Boundi
const auto ya = up * box.Minimum.Y;
const auto yb = up * box.Maximum.Y;
const auto backward = matrix.GetBackward();
const auto za = backward * box.Minimum.Z;
const auto zb = backward * box.Maximum.Z;
const auto forward = matrix.GetForward();
const auto za = forward * box.Minimum.Z;
const auto zb = forward * box.Maximum.Z;
const auto translation = matrix.GetTranslation();
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation;
@@ -146,9 +146,9 @@ void BoundingBox::Transform(const BoundingBox& box, const ::Transform& transform
const auto ya = up * box.Minimum.Y;
const auto yb = up * box.Maximum.Y;
const auto backward = Float3::Transform(Float3::Backward, transform.Orientation);
const auto za = backward * box.Minimum.Z;
const auto zb = backward * box.Maximum.Z;
const auto forward = Float3::Transform(Float3::Forward, transform.Orientation);
const auto za = forward * box.Minimum.Z;
const auto zb = forward * box.Maximum.Z;
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation;
const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation;

View File

@@ -474,9 +474,9 @@ namespace FlaxEngine
var ya = up * box.Minimum.Y;
var yb = up * box.Maximum.Y;
Double3 backward = transform.Backward;
var za = backward * box.Minimum.Z;
var zb = backward * box.Maximum.Z;
Double3 forward = transform.Forward;
var za = forward * box.Minimum.Z;
var zb = forward * box.Maximum.Z;
var translation = transform.TranslationVector;
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation;
@@ -514,9 +514,9 @@ namespace FlaxEngine
var ya = up * box.Minimum.Y;
var yb = up * box.Maximum.Y;
Double3 backward = transform.Backward;
var za = backward * box.Minimum.Z;
var zb = backward * box.Maximum.Z;
Double3 forward = transform.Forward;
var za = forward * box.Minimum.Z;
var zb = forward * box.Maximum.Z;
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation;
var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation;

View File

@@ -136,12 +136,12 @@ void Matrix::Decompose(Float3& scale, Matrix3x3& rotation, Float3& translation)
const auto right = Float3::Cross(up, at);
rotation.SetRight(right);
rotation.SetUp(up);
rotation.SetBackward(at);
rotation.SetForward(at);
// In case of reflexions
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z;
scale.Z = Float3::Dot(at, GetForward()) > 0.0f ? scale.Z : -scale.Z;
}
void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const

View File

@@ -215,23 +215,9 @@ namespace FlaxEngine
}
/// <summary>
/// Gets or sets the forward <see cref="Float3" /> of the matrix; that is -M31, -M32, and -M33.
/// Gets or sets the forward <see cref="Float3" /> of the matrix; that is M31, M32, and M33.
/// </summary>
public Float3 Forward
{
get => new Float3(-M31, -M32, -M33);
set
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
}
/// <summary>
/// Gets or sets the backward <see cref="Float3" /> of the matrix; that is M31, M32, and M33.
/// </summary>
public Float3 Backward
{
get => new Float3(M31, M32, M33);
set
@@ -242,6 +228,20 @@ namespace FlaxEngine
}
}
/// <summary>
/// Gets or sets the backward <see cref="Float3" /> of the matrix; that is -M31, -M32, and -M33.
/// </summary>
public Float3 Backward
{
get => new Float3(-M31, -M32, -M33);
set
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Matrix" /> struct.
/// </summary>

View File

@@ -210,31 +210,31 @@ public:
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
Float3 GetForward() const
{
return -Float3(M31, M32, M33);
return Float3(M31, M32, M33);
}
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
void SetForward(const Float3& value)
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
// Gets the backward Float3 of the matrix; that is M31, M32, and M33.
Float3 GetBackward() const
{
return Float3(M31, M32, M33);
}
// Sets the backward Float3 of the matrix; that is M31, M32, and M33.
void SetBackward(const Float3& value)
{
M31 = value.X;
M32 = value.Y;
M33 = value.Z;
}
// Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
Float3 GetBackward() const
{
return Float3(-M31, -M32, -M33);
}
// Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
void SetBackward(const Float3& value)
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
// Gets the first row in the matrix; that is M11, M12, M13, and M14.
Float4 GetRow1() const
{

View File

@@ -225,7 +225,7 @@ void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const
const auto right = Float3::Cross(up, at);
rotation.SetRight(right);
rotation.SetUp(up);
rotation.SetBackward(at);
rotation.SetForward(at);
// In case of reflexions
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;

View File

@@ -303,9 +303,6 @@ namespace FlaxEngine
/// <summary>
/// Gets a value indicating whether this instance is an identity Matrix3x3.
/// </summary>
/// <value>
/// <c>true</c> if this instance is an identity Matrix3x3; otherwise, <c>false</c>.
/// </value>
public bool IsIdentity => Equals(Identity);
/// <summary>
@@ -566,19 +563,19 @@ namespace FlaxEngine
/// </remarks>
public bool DecomposeUniformScale(out float scale, out Quaternion rotation)
{
//Scaling is the length of the rows. ( just take one row since this is a uniform matrix)
// Scaling is the length of the rows. ( just take one row since this is a uniform matrix)
scale = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13));
var invScale = 1f / scale;
//If any of the scaling factors are zero, then the rotation matrix can not exist.
// If any of the scaling factors are zero, then the rotation matrix can not exist
if (Math.Abs(scale) < Mathf.Epsilon)
{
rotation = Quaternion.Identity;
return false;
}
//The rotation is the left over matrix after dividing out the scaling.
Matrix3x3 rotationmatrix = new Matrix3x3
// The rotation is the leftover matrix after dividing out the scaling
var rotationMatrix = new Matrix3x3
{
M11 = M11 * invScale,
M12 = M12 * invScale,
@@ -590,8 +587,7 @@ namespace FlaxEngine
M32 = M32 * invScale,
M33 = M33 * invScale
};
Quaternion.RotationMatrix(ref rotationmatrix, out rotation);
Quaternion.RotationMatrix(ref rotationMatrix, out rotation);
return true;
}

View File

@@ -175,34 +175,34 @@ public:
M13 = -value.Z;
}
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
// Gets the forward Float3 of the matrix; that is M31, M32, and M33.
Float3 GetForward() const
{
return -Float3(M31, M32, M33);
}
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
// Sets the forward Float3 of the matrix; that is M31, M32, and M33.
void SetForward(const Float3& value)
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
// Gets the backward Float3 of the matrix; that is M31, M32, and M33.
Float3 GetBackward() const
{
return Float3(M31, M32, M33);
}
// Sets the backward Float3 of the matrix; that is M31, M32, and M33.
void SetBackward(const Float3& value)
{
M31 = value.X;
M32 = value.Y;
M33 = value.Z;
}
// Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
Float3 GetBackward() const
{
return Float3(-M31, -M32, -M33);
}
// Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
void SetBackward(const Float3& value)
{
M31 = -value.X;
M32 = -value.Y;
M33 = -value.Z;
}
// Gets the first row in the matrix; that is M11, M12 and M13.
Float3 GetRow1() const
{

View File

@@ -1827,6 +1827,7 @@ Variant::operator Float4() const
return Float4(*(Float3*)AsData, 0.0f);
case VariantType::Float4:
case VariantType::Color:
case VariantType::Quaternion:
return *(Float4*)AsData;
case VariantType::Double2:
return Float4(AsDouble2(), 0.0f, 0.0f);

View File

@@ -94,6 +94,13 @@ struct DebugLine
float TimeLeft;
};
struct DebugGeometryBuffer
{
GPUBuffer* Buffer;
float TimeLeft;
Matrix Transform;
};
struct DebugTriangle
{
Float3 V0;
@@ -122,12 +129,9 @@ struct DebugText3D
float TimeLeft;
};
PACK_STRUCT(struct Vertex {
Float3 Position;
Color32 Color;
});
typedef DebugDraw::Vertex Vertex;
GPU_CB_STRUCT(Data {
GPU_CB_STRUCT(ShaderData {
Matrix ViewProjection;
Float2 Padding;
float ClipPosZBias;
@@ -231,6 +235,7 @@ void TeleportList(const Float3& delta, Array<DebugText3D>& list)
struct DebugDrawData
{
Array<DebugGeometryBuffer> GeometryBuffers;
Array<DebugLine> DefaultLines;
Array<Vertex> OneFrameLines;
Array<DebugTriangle> DefaultTriangles;
@@ -244,7 +249,7 @@ struct DebugDrawData
inline int32 Count() const
{
return LinesCount() + TrianglesCount() + TextCount();
return LinesCount() + TrianglesCount() + TextCount() + GeometryBuffers.Count();
}
inline int32 LinesCount() const
@@ -280,6 +285,7 @@ struct DebugDrawData
inline void Update(float deltaTime)
{
UpdateList(deltaTime, GeometryBuffers);
UpdateList(deltaTime, DefaultLines);
UpdateList(deltaTime, DefaultTriangles);
UpdateList(deltaTime, DefaultWireTriangles);
@@ -784,7 +790,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
// Update constant buffer
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
Data data;
ShaderData data;
Matrix vp;
Matrix::Multiply(view.View, view.Projection, vp);
Matrix::Transpose(vp, data.ViewProjection);
@@ -830,6 +836,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
}
// Geometries
for (auto& geometry : Context->DebugDrawDepthTest.GeometryBuffers)
{
auto tmp = data;
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
context->UpdateCB(cb, &tmp);
auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
context->SetState(state->Get(enableDepthWrite, true));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
}
if (Context->DebugDrawDepthTest.GeometryBuffers.HasItems())
context->UpdateCB(cb, &data);
if (data.EnableDepthTest)
context->UnBindSR(0);
}
@@ -862,6 +884,19 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->BindVB(ToSpan(&vb, 1));
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
}
// Geometries
for (auto& geometry : Context->DebugDrawDefault.GeometryBuffers)
{
auto tmp = data;
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
context->UpdateCB(cb, &tmp);
context->SetState(DebugDrawPsLinesDefault.Get(false, false));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
}
}
// Text
@@ -1088,6 +1123,24 @@ void DebugDraw::DrawLines(const Span<Float3>& lines, const Matrix& transform, co
}
}
void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float duration, bool depthTest)
{
if (lines == nullptr || lines->GetSize() == 0)
return;
if (lines->GetSize() % (sizeof(Vertex) * 2) != 0)
{
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
return;
}
// Draw lines
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
auto& geometry = debugDrawData.GeometryBuffers.AddOne();
geometry.Buffer = lines;
geometry.TimeLeft = duration;
geometry.Transform = transform * Matrix::Translation(-Context->Origin);
}
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
{
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
@@ -2147,6 +2200,7 @@ void DebugDraw::DrawText(const StringView& text, const Transform& transform, con
void DebugDraw::Clear(void* context)
{
DebugDraw::UpdateContext(context, MAX_float);
UpdateContext(context, MAX_float);
}
#endif

View File

@@ -6,6 +6,8 @@
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Math/Color.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Types/Span.h"
struct RenderView;
@@ -14,6 +16,7 @@ class Light;
struct RenderContext;
class GPUTextureView;
class GPUContext;
class GPUBuffer;
class RenderTask;
class SceneRenderTask;
class Actor;
@@ -26,6 +29,14 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
/// <summary>
/// Vertex data for debug shapes.
/// </summary>
PACK_STRUCT(struct Vertex {
Float3 Position;
Color32 Color;
});
#if USE_EDITOR
/// <summary>
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
@@ -175,6 +186,15 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines using the provided vertex buffer that contains pairs of Vertex elements. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
/// </summary>
/// <param name="lines">The GPU buffer with vertices for lines (must have multiple of 2 elements).</param>
/// <param name="transform">The custom matrix used to transform all line vertices.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(GPUBuffer* lines, const Matrix& transform, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
/// </summary>
@@ -691,9 +711,9 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
/// <summary>
/// Clear all debug draw displayed on sceen.
/// Clears all debug shapes displayed on screen.
/// </summary>
/// <returns></returns>
/// <param name="context">The context.</param>
API_FUNCTION() static void Clear(void* context = nullptr);
};

View File

@@ -154,6 +154,7 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_ARG_SWITCH("-build ", Build);
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug);
PARSE_BOOL_SWITCH("-exit ", Exit);
PARSE_ARG_OPT_SWITCH("-play ", Play);
#endif
#if USE_EDITOR || !BUILD_RELEASE

View File

@@ -168,6 +168,11 @@ public:
/// </summary>
Nullable<bool> ShaderDebug;
/// <summary>
/// -exit (exits the editor after startup and performing all queued actions). Usefull when invoking editor from CL/CD.
/// </summary>
Nullable<bool> Exit;
/// <summary>
/// -play !guid! ( Scene to play, can be empty to use default )
/// </summary>

View File

@@ -5,6 +5,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h"
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
@@ -36,7 +37,8 @@ GPUTasksContext::~GPUTasksContext()
auto task = tasks[i];
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
{
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
if (!Globals::IsRequestingExit)
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
task->CancelSync();
}
}
@@ -60,7 +62,8 @@ void GPUTasksContext::OnCancelSync(GPUTask* task)
_tasksDone.Remove(task);
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
if (!Globals::IsRequestingExit)
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
}
void GPUTasksContext::OnFrameBegin()

View File

@@ -5,6 +5,7 @@
#include "GPUResourceProperty.h"
#include "GPUBufferDescription.h"
#include "PixelFormatExtensions.h"
#include "RenderTask.h"
#include "Async/Tasks/GPUCopyResourceTask.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/String.h"
@@ -358,6 +359,16 @@ void GPUBuffer::SetData(const void* data, uint32 size)
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
return;
}
if (_desc.Usage == GPUResourceUsage::Default && GPUDevice::Instance->IsRendering())
{
// Upload using the context (will use internal staging buffer inside command buffer)
RenderContext::GPULocker.Lock();
GPUDevice::Instance->GetMainContext()->UpdateBuffer(this, data, size);
RenderContext::GPULocker.Unlock();
return;
}
void* mapped = Map(GPUResourceMapMode::Write);
if (!mapped)
return;

View File

@@ -74,8 +74,7 @@ public:
/// </summary>
API_PROPERTY() FORCE_INLINE uint32 GetElementsCount() const
{
ASSERT(_desc.Stride > 0);
return _desc.Size / _desc.Stride;
return _desc.Stride > 0 ? _desc.Size / _desc.Stride : 0;
}
/// <summary>

View File

@@ -282,35 +282,35 @@ void MaterialParameter::Bind(BindMeta& meta) const
switch (_type)
{
case MaterialParameterType::Bool:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool)));
*((int32*)(meta.Constants.Get() + _offset)) = _asBool;
break;
case MaterialParameterType::Integer:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32)));
*((int32*)(meta.Constants.Get() + _offset)) = _asInteger;
break;
case MaterialParameterType::Float:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float)));
*((float*)(meta.Constants.Get() + _offset)) = _asFloat;
break;
case MaterialParameterType::Vector2:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
*((Float2*)(meta.Constants.Get() + _offset)) = _asVector2;
break;
case MaterialParameterType::Vector3:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
*((Float3*)(meta.Constants.Get() + _offset)) = _asVector3;
break;
case MaterialParameterType::Vector4:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
*((Float4*)(meta.Constants.Get() + _offset)) = *(Float4*)&AsData;
break;
case MaterialParameterType::Color:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
*((Color*)(meta.Constants.Get() + _offset)) = _asColor;
break;
case MaterialParameterType::Matrix:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Matrix));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Matrix)));
Matrix::Transpose(*(Matrix*)&AsData, *(Matrix*)(meta.Constants.Get() + _offset));
break;
case MaterialParameterType::NormalMap:
@@ -409,44 +409,44 @@ void MaterialParameter::Bind(BindMeta& meta) const
switch (e->Value.Type.Type)
{
case VariantType::Bool:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool)));
*((bool*)(meta.Constants.Get() + _offset)) = e->Value.AsBool;
break;
case VariantType::Int:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32)));
*((int32*)(meta.Constants.Get() + _offset)) = e->Value.AsInt;
break;
case VariantType::Uint:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(uint32));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(uint32)));
*((uint32*)(meta.Constants.Get() + _offset)) = e->Value.AsUint;
break;
case VariantType::Float:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float)));
*((float*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat;
break;
case VariantType::Float2:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
*((Float2*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat2();
break;
case VariantType::Float3:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
*((Float3*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat3();
break;
case VariantType::Float4:
case VariantType::Color:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
*((Float4*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat4();
break;
case VariantType::Double2:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
*((Float2*)(meta.Constants.Get() + _offset)) = (Float2)e->Value.AsDouble2();
break;
case VariantType::Double3:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
*((Float3*)(meta.Constants.Get() + _offset)) = (Float3)e->Value.AsDouble3();
break;
case VariantType::Double4:
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
*((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4();
break;
default: ;

View File

@@ -85,7 +85,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
{
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32));
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32)));
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
}
}

View File

@@ -68,7 +68,7 @@ void VolumeParticleMaterialShader::Bind(BindParameters& params)
{
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32));
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32)));
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
}
}

View File

@@ -285,7 +285,10 @@ void GPUContextDX11::SetBlendFactor(const Float4& value)
void GPUContextDX11::SetStencilRef(uint32 value)
{
if (CurrentStencilRef != value)
{
CurrentStencilRef = value;
_context->OMSetDepthStencilState(CurrentDepthStencilState, CurrentStencilRef);
}
}
void GPUContextDX11::ResetSR()

View File

@@ -158,10 +158,10 @@ float Spline::GetSplineLength() const
const auto& b = Curve[i];
Vector3 prevPoint = a.Value.Translation * scale;
const float length = Math::Abs(b.Time - a.Time);
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
Vector3 leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
for (int32 slice = 1; slice < slices; slice++)
{
@@ -189,10 +189,10 @@ float Spline::GetSplineSegmentLength(int32 index) const
const Vector3 scale = _transform.Scale;
Vector3 prevPoint = a.Value.Translation * scale;
{
const float length = Math::Abs(b.Time - a.Time);
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
Vector3 leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
for (int32 slice = 1; slice < slices; slice++)
{
@@ -478,9 +478,7 @@ void Spline::GetKeyframes(MArray* data)
void Spline::SetKeyframes(MArray* data)
{
const int32 count = MCore::Array::GetLength(data);
Curve.GetKeyframes().Resize(count, false);
Platform::MemoryCopy(Curve.GetKeyframes().Get(), MCore::Array::GetAddress(data), sizeof(Keyframe) * count);
Curve = Span<byte>((const byte*)MCore::Array::GetAddress(data), MCore::Array::GetLength(data));
UpdateSpline();
}

View File

@@ -184,9 +184,9 @@ void SplineModel::OnSplineUpdated()
auto& instance = _instances[segment];
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
// Find maximum scale over the segment spline and collect the segment positions for bounds
segmentPoints.Clear();
@@ -256,9 +256,9 @@ void SplineModel::UpdateDeformationBuffer()
auto& instance = _instances[segment];
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
for (int32 chunk = 0; chunk < chunksPerSegment; chunk++)
{
const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv);
@@ -291,10 +291,10 @@ void SplineModel::UpdateDeformationBuffer()
{
const auto& start = keyframes[segments - 1];
const auto& end = keyframes[segments];
const float length = end.Time - start.Time;
const float tangentScale = (end.Time - start.Time) / 3.0f;
const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform);
Vector3 direction;
AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction);

View File

@@ -90,6 +90,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
// Draw physics shapes
if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders)
{
PROFILE_CPU_NAMED("PhysicsDebug");
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
{
@@ -100,6 +101,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
// Draw light shapes
if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug))
{
PROFILE_CPU_NAMED("LightsDebug");
const LightsDebugCallback* lightsDebugData = LightsDebug.Get();
for (int32 i = 0; i < LightsDebug.Count(); i++)
{

View File

@@ -456,6 +456,7 @@ void ParticleEmitterGPUGenerator::PrepareGraph(ParticleEmitterGraphGPU* graph)
mp.AsFloat3 = param->Value.AsFloat3();
break;
case VariantType::Float4:
case VariantType::Quaternion:
mp.Type = MaterialParameterType::Vector4;
*(Float4*)&mp.AsData = param->Value.AsFloat4();
break;

View File

@@ -214,9 +214,9 @@ void SplineCollider::GetGeometry(CollisionShape& collision)
auto offsetIndices = segment * collisionIndices.Count();
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
// Vertex buffer is deformed along the spline
auto srcVertices = collisionVertices.Get();

View File

@@ -468,12 +468,12 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
if (settings.WaitForEnd)
{
id<NSObject> outputObserver = nil;
id<NSObject> outputObserverError = nil;
if (captureStdOut)
{
NSPipe *stdoutPipe = [NSPipe pipe];
NSPipe* stdoutPipe = [NSPipe pipe];
[task setStandardOutput:stdoutPipe];
outputObserver = [[NSNotificationCenter defaultCenter]
addObserverForName: NSFileHandleDataAvailableNotification
object: [stdoutPipe fileHandleForReading]
@@ -497,8 +497,34 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
}
}
];
[[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
NSPipe *stderrPipe = [NSPipe pipe];
[task setStandardError:stderrPipe];
outputObserverError = [[NSNotificationCenter defaultCenter]
addObserverForName: NSFileHandleDataAvailableNotification
object: [stderrPipe fileHandleForReading]
queue: nil
usingBlock:^(NSNotification* notification)
{
NSData* data = [stderrPipe fileHandleForReading].availableData;
if (data.length)
{
String line((const char*)data.bytes, data.length);
if (settings.SaveOutput)
settings.Output.Add(line.Get(), line.Length());
if (settings.LogOutput)
{
StringView lineView(line);
if (line[line.Length() - 1] == '\n')
lineView = StringView(line.Get(), line.Length() - 1);
Log::Logger::Write(LogType::Error, lineView);
}
[[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
}
}
];
[[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
}
String exception;

View File

@@ -301,12 +301,12 @@ void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2
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);
}
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);
}
@@ -1816,8 +1816,8 @@ void DrawLines(const Float2* points, int32 pointsCount, const Color& color1, con
// Ending cap
{
ApplyTransform(points[0], p1t);
ApplyTransform(points[1], p2t);
ApplyTransform(points[pointsCount - 2], p1t);
//ApplyTransform(points[pointsCount - 1], p2t);
const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t);
@@ -1882,19 +1882,46 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3,
const Float2 d3 = p4 - p3;
const float len = d1.Length() + d2.Length() + d3.Length();
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
const float segmentCountInv = 1.0f / segmentCount;
const float segmentCountInv = 1.0f / (float)segmentCount;
// Draw segmented curve
Float2 p;
AnimationUtils::Bezier(p1, p2, p3, p4, 0, p);
Lines2.Clear();
Lines2.Add(p);
for (int32 i = 1; i <= segmentCount; i++)
Lines2.Add(p1);
for (int32 i = 1; i < segmentCount; i++)
{
const float t = i * segmentCountInv;
const float t = (float)i * segmentCountInv;
Float2 p;
AnimationUtils::Bezier(p1, p2, p3, p4, t, p);
Lines2.Add(p);
}
Lines2.Add(p4);
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
}
void Render2D::DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness)
{
RENDER2D_CHECK_RENDERING_STATE;
// Find amount of segments to use
const Float2 d1 = p2 - p1;
const Float2 d2 = p3 - p2;
const Float2 d3 = p4 - p3;
const float len = d1.Length() + d2.Length() + d3.Length();
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
const float segmentCountInv = 1.0f / (float)segmentCount;
// Draw segmented curve
Lines2.Clear();
Lines2.Add(p1);
for (int32 i = 1; i < segmentCount; i++)
{
const float t = (float)i * segmentCountInv;
Float2 p;
p.X = Math::Lerp(p1.X, p4.X, t);
AnimationUtils::Bezier(p1.Y, p2.Y, p3.Y, p4.Y, t, p.Y);
Lines2.Add(p);
}
Lines2.Add(p4);
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
}
@@ -1935,9 +1962,56 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
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)
{
RENDER2D_CHECK_RENDERING_STATE;
CHECK(vertices.Length() % 3 == 0);
CHECK(vertices.Length() == uvs.Length());
Render2DDrawCall& drawCall = DrawCalls.AddOne();
@@ -1952,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)
{
Color colors[3] = { (Color)color, (Color)color, (Color)color };
Span<Color> spancolor(colors, 3);
DrawTexturedTriangles(t, vertices, uvs, spancolor);
RENDER2D_CHECK_RENDERING_STATE;
CHECK(vertices.Length() % 3 == 0);
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)
{
RENDER2D_CHECK_RENDERING_STATE;
CHECK(vertices.Length() % 3 == 0);
CHECK(vertices.Length() == uvs.Length());
CHECK(vertices.Length() == colors.Length());
@@ -1994,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)
{
CHECK(vertices.Length() == colors.Length());

View File

@@ -389,6 +389,17 @@ public:
/// <param name="thickness">The line thickness.</param>
API_FUNCTION() static void DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
/// <summary>
/// Draws a spline curve (Bezier but X axis represents uniform time).
/// </summary>
/// <param name="p1">The start point.</param>
/// <param name="p2">The first control point.</param>
/// <param name="p3">The second control point.</param>
/// <param name="p4">The end point.</param>
/// <param name="color">The line color</param>
/// <param name="thickness">The line thickness.</param>
API_FUNCTION() static void DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
/// <summary>
/// Draws the GUI material.
/// </summary>
@@ -404,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>
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>
/// Draws vertices array.
/// </summary>
@@ -440,13 +467,20 @@ public:
/// <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);
/// <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>
/// Draws vertices array.
/// </summary>
/// <param name="vertices">The vertices array.</param>
/// <param name="colors">The colors array.</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>
/// Fills a triangular area.

View File

@@ -271,7 +271,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
// Calculate star texture rotation matrix
Float3 camX = renderContext.View.View.GetRight();
Float3 camZ = renderContext.View.View.GetForward();
Float3 camZ = renderContext.View.View.GetBackward();
float camRot = Float3::Dot(camX, Float3::Forward) + Float3::Dot(camZ, Float3::Up);
float camRotCos = Math::Cos(camRot) * 0.8f;
float camRotSin = Math::Sin(camRot) * 0.8f;

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine
{
/// <summary>
/// Displays the method in the properties panel where user can click and invoke this method.
/// </summary>
/// <remarks>Supported on both static and member methods that are parameterless.</remarks>
[AttributeUsage(AttributeTargets.Method)]
public sealed class ButtonAttribute : Attribute
{
/// <summary>
/// The button text. Empty value will use method name (auto-formatted).
/// </summary>
public string Text;
/// <summary>
/// The button tooltip text. Empty value will use method documentation.
/// </summary>
public string Tooltip;
/// <summary>
/// Initializes a new instance of the <see cref="ButtonAttribute"/> class.
/// </summary>
public ButtonAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ButtonAttribute"/> class.
/// </summary>
/// <param name="text">The button text.</param>
/// <param name="tooltip">The button tooltip.</param>
public ButtonAttribute(string text, string tooltip = null)
{
Text = text;
Tooltip = tooltip;
}
}
}

View File

@@ -33,6 +33,11 @@
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
#endif
#if TERRAIN_USE_PHYSICS_DEBUG
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/DynamicBuffer.h"
#include "Engine/Engine/Units.h"
#endif
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/RawDataAsset.h"
@@ -94,7 +99,8 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
}
#endif
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLines.Resize(0);
SAFE_DELETE(_debugLines);
_debugLinesDirty = true;
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
@@ -1822,7 +1828,7 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod
// Invalidate cache
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLines.Resize(0);
_debugLinesDirty = true;
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
@@ -1940,7 +1946,7 @@ bool TerrainPatch::UpdateCollision()
{
// Invalidate cache
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLines.Resize(0);
_debugLinesDirty = true;
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
@@ -2082,7 +2088,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
{
// Invalidate cache
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLines.Resize(0);
_debugLinesDirty = true;
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
@@ -2211,7 +2217,8 @@ void TerrainPatch::DestroyCollision()
_physicsShape = nullptr;
_physicsHeightField = nullptr;
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLines.Resize(0);
_debugLinesDirty = true;
SAFE_DELETE(_debugLines);
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
@@ -2224,15 +2231,26 @@ void TerrainPatch::DestroyCollision()
void TerrainPatch::CacheDebugLines()
{
PROFILE_CPU();
ASSERT(_debugLines.IsEmpty() && _physicsHeightField);
ASSERT(_physicsHeightField);
_debugLinesDirty = false;
if (!_debugLines)
_debugLines = GPUDevice::Instance->CreateBuffer(TEXT("Terrain.DebugLines"));
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
const int32 count = (rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2;
typedef DebugDraw::Vertex Vertex;
if (_debugLines->GetElementsCount() != count)
{
if (_debugLines->Init(GPUBufferDescription::Vertex(sizeof(Vertex), count)))
return;
}
Array<Vertex> debugLines;
debugLines.Resize(count);
auto* data = debugLines.Get();
const Color32 color(Color::GreenYellow * 0.8f);
_debugLines.Resize((rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2);
Vector3* data = _debugLines.Get();
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
#define GET_VERTEX(x, y) const Vertex v##x##y = { Float3((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))), color }
for (int32 row = 0; row < rows - 1; row++)
{
@@ -2243,7 +2261,7 @@ void TerrainPatch::CacheDebugLines()
if (sample.MaterialIndex0 == (uint8)PhysicsBackend::HeightFieldMaterial::Hole)
{
for (int32 i = 0; i < 6; i++)
*data++ = Vector3::Zero;
*data++ = Vertex { Float3::Zero, Color32::Black };
continue;
}
@@ -2284,18 +2302,16 @@ void TerrainPatch::CacheDebugLines()
}
#undef GET_VERTEX
_debugLines->SetData(debugLines.Get(), _debugLines->GetSize());
}
void TerrainPatch::DrawPhysicsDebug(RenderView& view)
{
#if COMPILE_WITH_DEBUG_DRAW
const BoundingBox bounds(_bounds.Minimum - view.Origin, _bounds.Maximum - view.Origin);
if (!_physicsShape || !view.CullingFrustum.Intersects(bounds))
return;
const Transform terrainTransform = _terrain->_transform;
const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
if (view.Mode == ViewMode::PhysicsColliders)
{
DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true);
@@ -2304,13 +2320,17 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view)
{
BoundingSphere sphere;
BoundingSphere::FromBox(bounds, sphere);
if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < 4000.0f)
if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < METERS_TO_UNITS(500))
{
if (_debugLines.IsEmpty())
if (!_debugLines || _debugLinesDirty)
CacheDebugLines();
DEBUG_DRAW_LINES(_debugLines, world, Color::GreenYellow * 0.8f, 0, true);
const Transform terrainTransform = _terrain->_transform;
const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
DebugDraw::DrawLines(_debugLines, world);
}
}
#endif
}
#endif

View File

@@ -40,12 +40,13 @@ private:
Array<Color32> _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT];
bool _wasHeightModified;
bool _wasSplatmapModified[TERRAIN_MAX_SPLATMAPS_COUNT];
#if TERRAIN_USE_PHYSICS_DEBUG
bool _debugLinesDirty = true;
class GPUBuffer* _debugLines = nullptr;
#endif
TextureBase::InitData* _dataHeightmap = nullptr;
TextureBase::InitData* _dataSplatmap[TERRAIN_MAX_SPLATMAPS_COUNT] = {};
#endif
#if TERRAIN_USE_PHYSICS_DEBUG
Array<Vector3> _debugLines; // TODO: large-worlds
#endif
#if USE_EDITOR
Array<Vector3> _collisionTriangles; // TODO: large-worlds
#endif

View File

@@ -263,16 +263,21 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
protected override void SetViewOffset(ref Float2 value)
{
// Update scroll bars but with locked layout
bool wasLocked = _isLayoutLocked;
int layoutUpdateLock = _layoutUpdateLock;
_isLayoutLocked = true;
_layoutUpdateLock = 999;
if (HScrollBar != null)
HScrollBar.Value = -value.X;
HScrollBar.TargetValue = -value.X;
if (VScrollBar != null)
VScrollBar.Value = -value.Y;
VScrollBar.TargetValue = -value.Y;
_layoutUpdateLock = layoutUpdateLock;
_isLayoutLocked = wasLocked;
base.SetViewOffset(ref value);
PerformLayout();
}
/// <summary>
@@ -553,7 +558,12 @@ namespace FlaxEngine.GUI
if (vScrollEnabled)
{
VScrollBar.SetScrollRange(scrollBounds.Top, Mathf.Max(Mathf.Max(0, scrollBounds.Top), scrollBounds.Height - height));
float max;
if (scrollBounds.Top < 0)
max = Mathf.Max(scrollBounds.Bottom, scrollBounds.Top + scrollBounds.Height - height);
else
max = Mathf.Max(scrollBounds.Top, scrollBounds.Height - height);
VScrollBar.SetScrollRange(scrollBounds.Top, max);
}
VScrollBar.Bounds = new Rectangle(Width - _scrollBarsSize, 0, _scrollBarsSize, Height);
}
@@ -580,7 +590,12 @@ namespace FlaxEngine.GUI
if (hScrollEnabled)
{
HScrollBar.SetScrollRange(scrollBounds.Left, Mathf.Max(Mathf.Max(0, scrollBounds.Left), scrollBounds.Width - width));
float max;
if (scrollBounds.Left < 0)
max = Mathf.Max(scrollBounds.Right, scrollBounds.Left + scrollBounds.Width - width);
else
max = Mathf.Max(scrollBounds.Left, scrollBounds.Width - width);
HScrollBar.SetScrollRange(scrollBounds.Left, max);
}
HScrollBar.Bounds = new Rectangle(0, Height - _scrollBarsSize, Width - (VScrollBar != null && VScrollBar.Visible ? VScrollBar.Width : 0), _scrollBarsSize);
}
@@ -596,17 +611,29 @@ namespace FlaxEngine.GUI
// Calculate scroll area bounds
var totalMin = Float2.Zero;
var totalMax = Float2.Zero;
var hasTotal = false;
for (int i = 0; i < _children.Count; i++)
{
var c = _children[i];
if (c.Visible && c.IsScrollable)
{
var min = Float2.Zero;
var max = c.Size;
Matrix3x3.Transform2D(ref min, ref c._cachedTransform, out min);
Matrix3x3.Transform2D(ref max, ref c._cachedTransform, out max);
Float2.Min(ref min, ref totalMin, out totalMin);
Float2.Max(ref max, ref totalMax, out totalMax);
var upperLeft = Float2.Zero;
var bottomRight = c.Size;
Matrix3x3.Transform2D(ref upperLeft, ref c._cachedTransform, out upperLeft);
Matrix3x3.Transform2D(ref bottomRight, ref c._cachedTransform, out bottomRight);
Float2.Min(ref upperLeft, ref bottomRight, out var min);
Float2.Max(ref upperLeft, ref bottomRight, out var max);
if (hasTotal)
{
Float2.Min(ref min, ref totalMin, out totalMin);
Float2.Max(ref max, ref totalMax, out totalMax);
}
else
{
totalMin = min;
totalMax = max;
hasTotal = true;
}
}
}

View File

@@ -348,6 +348,8 @@ namespace FlaxEngine.GUI
// https://easings.net/#easeOutSine
var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2);
if (progress >= 1.0f)
easedProgress = 1.0f;
value = Mathf.Lerp(_startValue, _targetValue, easedProgress);
_scrollAnimationProgress = progress;

View File

@@ -7,10 +7,11 @@
#include "Engine/Core/Collections/Array.h"
/// <summary>
/// Helper class with Delaunay triangulation algorithm implementation,
/// Helper class with Delaunay triangulation algorithm implementation (2D space).
/// </summary>
class Delaunay2D
API_CLASS(Internal, Static, Namespace="FlaxEngine.Utilities") class Delaunay2D
{
DECLARE_SCRIPTING_TYPE_MINIMAL(Delaunay2D);
public:
struct Triangle
{
@@ -31,14 +32,35 @@ public:
}
};
template<typename TVertexArray, typename TTrianglesArray>
static void Triangulate(const TVertexArray& vertices, TTrianglesArray& triangles)
/// <summary>
/// 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
if (vertices.Count() < 3)
if (vertices.Count() < 3)
return;
TVertexArray points = vertices;
Array<Float2> points = vertices;
Array<Edge> polygon;
Rectangle rect;
@@ -142,8 +164,7 @@ private:
}
};
template<typename TVertexArray>
static bool CircumCircleContains(const TVertexArray& vertices, const Triangle& triangle, int vertexIndex)
static bool CircumCircleContains(const Array<Float2>& vertices, const Triangle& triangle, int32 vertexIndex)
{
Float2 p1 = vertices[triangle.Indices[0]];
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.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 d = Float2::DistanceSquared(vertices[vertexIndex], circum);
return d <= r;
}
template<typename TVertexArray>
static bool EdgeCompare(const TVertexArray& vertices, const Edge& a, const Edge& b)
static bool EdgeCompare(const Array<Float2>& 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;
}
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
{
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance &&
Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
return true;
}
return false;
}
};

View File

@@ -92,9 +92,13 @@ void ShaderGenerator::ProcessGroupConstants(Box* box, Node* node, Value& value)
value = Value(cv.W);
break;
}
// Rotation
case 8:
{
value = Value::Zero;
const float pitch = (float)node->Values[0];
const float yaw = (float)node->Values[1];
const float roll = (float)node->Values[2];
value = Value(Quaternion::Euler(pitch, yaw, roll));
break;
}
// PI

View File

@@ -460,6 +460,7 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType::
case VariantType::Types::Float4:
case VariantType::Types::Double4:
case VariantType::Types::Color:
case VariantType::Types::Quaternion:
switch (v.Type)
{
case VariantType::Types::Bool:
@@ -485,16 +486,6 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType::
break;
}
break;
case VariantType::Types::Quaternion:
switch (v.Type)
{
case VariantType::Types::Color:
case VariantType::Types::Float4:
case VariantType::Types::Double4:
format = TEXT("{}");
break;
}
break;
}
if (format == nullptr)
{

View File

@@ -79,6 +79,7 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
value = cv.W;
break;
}
// Rotation
case 8:
{
const float pitch = (float)node->Values[0];