Merge remote-tracking branch 'origin/master' into 1.9
# Conflicts: # Source/Editor/Modules/ContentDatabaseModule.cs # Source/Editor/Surface/SurfaceUtils.cs # Source/Editor/Windows/Assets/MaterialInstanceWindow.cs # Source/Engine/Foliage/Foliage.cpp # Source/Engine/Graphics/Models/MeshBase.h # Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp
This commit is contained in:
@@ -1421,6 +1421,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
const auto cData = node->Values[4 + c * 2].AsFloat4();
|
||||
|
||||
// Get triangle coords
|
||||
byte anims[3] = { a, b, c };
|
||||
Float2 points[3] = {
|
||||
Float2(aData.X, aData.Y),
|
||||
Float2(bData.X, bData.Y),
|
||||
@@ -1534,18 +1535,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
bestPoint = closest;
|
||||
hasBest = true;
|
||||
|
||||
float d = Float2::Distance(s[0], s[1]);
|
||||
if (Math::IsZero(d))
|
||||
{
|
||||
bestWeight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bestWeight = Float2::Distance(s[0], closest) / d;
|
||||
}
|
||||
|
||||
bestAnims[0] = j;
|
||||
bestAnims[1] = (j + 1) % 3;
|
||||
const float d = Float2::Distance(s[0], s[1]);
|
||||
bestWeight = d < ANIM_GRAPH_BLEND_THRESHOLD ? 0 : Float2::Distance(s[0], closest) / d;
|
||||
|
||||
bestAnims[0] = anims[j];
|
||||
bestAnims[1] = anims[(j + 1) % 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,7 +1471,8 @@ Asset::LoadResult VisualScript::load()
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
const int32 oldIndex = _oldParamsLayout.Find(Graph.Parameters[i].Identifier);
|
||||
instanceParams[i] = oldIndex != -1 ? valuesCache[oldIndex] : Graph.Parameters[i].Value;
|
||||
const bool useOldValue = oldIndex != -1 && valuesCache[oldIndex] != _oldParamsValues[i];
|
||||
instanceParams[i] = useOldValue ? valuesCache[oldIndex] : Graph.Parameters[i].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1486,6 +1487,8 @@ Asset::LoadResult VisualScript::load()
|
||||
instanceParams[i] = Graph.Parameters[i].Value;
|
||||
}
|
||||
}
|
||||
_oldParamsLayout.Clear();
|
||||
_oldParamsValues.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1499,15 +1502,18 @@ void VisualScript::unload(bool isReloading)
|
||||
{
|
||||
// Cache existing instanced parameters IDs to restore values after asset reload (params order might be changed but the IDs are stable)
|
||||
_oldParamsLayout.Resize(Graph.Parameters.Count());
|
||||
_oldParamsValues.Resize(Graph.Parameters.Count());
|
||||
for (int32 i = 0; i < Graph.Parameters.Count(); i++)
|
||||
{
|
||||
auto& param = Graph.Parameters[i];
|
||||
_oldParamsLayout[i] = param.Identifier;
|
||||
_oldParamsValues[i] = param.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldParamsLayout.Clear();
|
||||
_oldParamsValues.Clear();
|
||||
}
|
||||
#else
|
||||
_instances.Clear();
|
||||
|
||||
@@ -154,6 +154,7 @@ private:
|
||||
Array<Field, InlinedAllocation<32>> _fields;
|
||||
#if USE_EDITOR
|
||||
Array<Guid> _oldParamsLayout;
|
||||
Array<Variant> _oldParamsValues;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
#if FLAX_EDITOR
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
#endif
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
@@ -11,6 +15,7 @@ namespace FlaxEngine
|
||||
/// <typeparam name="T">Type of the asset instance type.</typeparam>
|
||||
#if FLAX_EDITOR
|
||||
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
|
||||
[TypeConverter(typeof(TypeConverters.JsonAssetReferenceConverter))]
|
||||
#endif
|
||||
[Newtonsoft.Json.JsonConverter(typeof(Json.JsonAssetReferenceConverter))]
|
||||
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
|
||||
@@ -125,7 +130,7 @@ namespace FlaxEngine
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Asset?.ToString();
|
||||
return Asset?.ToString() ?? "null";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -141,3 +146,33 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class JsonAssetReferenceConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (value is string valueStr)
|
||||
{
|
||||
var result = Activator.CreateInstance(destinationType);
|
||||
Json.JsonSerializer.ParseID(valueStr, out var id);
|
||||
var asset = Content.LoadAsync<JsonAsset>(id);
|
||||
destinationType.GetField("Asset").SetValue(result, asset);
|
||||
return result;
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType.Name.StartsWith("JsonAssetReference", StringComparison.Ordinal))
|
||||
return true;
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -639,7 +639,17 @@ CreateAssetResult ImportModel::CreateAnimation(CreateAssetContext& context, Mode
|
||||
|
||||
// Save animation data
|
||||
MemoryWriteStream stream(8182);
|
||||
const int32 animIndex = options && options->ObjectIndex != -1 ? options->ObjectIndex : 0; // Single animation per asset
|
||||
int32 animIndex = options ? options->ObjectIndex : -1; // Single animation per asset
|
||||
if (animIndex == -1)
|
||||
{
|
||||
// Pick the longest animation by default (eg. to skip ref pose anim if exported as the first one)
|
||||
animIndex = 0;
|
||||
for (int32 i = 1; i < modelData.Animations.Count(); i++)
|
||||
{
|
||||
if (modelData.Animations[i].GetLength() > modelData.Animations[animIndex].GetLength())
|
||||
animIndex = i;
|
||||
}
|
||||
}
|
||||
if (modelData.Pack2AnimationHeader(&stream, animIndex))
|
||||
return CreateAssetResult::Error;
|
||||
if (context.AllocateChunk(0))
|
||||
|
||||
@@ -865,6 +865,16 @@ namespace FlaxEngine
|
||||
return new Float2(Mathf.Ceil(v.X), Mathf.Ceil(v.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the vector with components containing the smallest integer smaller to or equal to the original value.
|
||||
/// </summary>
|
||||
/// <param name="v">The value.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public static Float2 Floor(Float2 v)
|
||||
{
|
||||
return new Float2(Mathf.Floor(v.X), Mathf.Floor(v.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Breaks the components of the vector into an integral and a fractional part. Returns vector made of fractional parts.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Matrix3x3.h"
|
||||
#include "Math.h"
|
||||
#include "../Types/String.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
|
||||
Quaternion Quaternion::Zero(0, 0, 0, 0);
|
||||
Quaternion Quaternion::One(1, 1, 1, 1);
|
||||
@@ -537,3 +538,14 @@ void Quaternion::RotationYawPitchRoll(float yaw, float pitch, float roll, Quater
|
||||
result.Y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
|
||||
result.Z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
|
||||
}
|
||||
|
||||
Quaternion Quaternion::GetRotationFromNormal(const Vector3& normal, const Transform& reference)
|
||||
{
|
||||
Float3 up = reference.GetUp();
|
||||
const float dot = Vector3::Dot(normal, up);
|
||||
if (Math::NearEqual(Math::Abs(dot), 1))
|
||||
{
|
||||
up = reference.GetRight();
|
||||
}
|
||||
return Quaternion::LookRotation(normal, up);
|
||||
}
|
||||
|
||||
@@ -1483,6 +1483,45 @@ namespace FlaxEngine
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation from a normal in relation to a transform.<br/>
|
||||
/// This function is especially useful for axis aligned faces,
|
||||
/// and with <seealso cref="Physics.RayCast(Vector3, Vector3, out RayCastHit, float, uint, bool)"/>.
|
||||
///
|
||||
/// <example><para><b>Example code:</b></para>
|
||||
/// <code>
|
||||
/// <see langword="public" /> <see langword="class" /> GetRotationFromNormalExample : <see cref="Script"/><br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||
/// {<br/>
|
||||
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||
/// {<br/>
|
||||
/// <see cref="Vector3"/> position = hit.Collider.Position;
|
||||
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||
/// <see cref="Vector3"/> point = hit.Point;
|
||||
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||
/// <see cref="Quaternion"/> rot = <see cref="Quaternion"/>.GetRotationFromNormal(normal,transform);
|
||||
/// SomeObject.Position = point;
|
||||
/// SomeObject.Orientation = rot;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="reference">The reference transform.</param>
|
||||
/// <returns>The rotation from the normal vector.</returns>
|
||||
public static Quaternion GetRotationFromNormal(Vector3 normal, Transform reference)
|
||||
{
|
||||
Float3 up = reference.Up;
|
||||
var dot = Vector3.Dot(normal, up);
|
||||
if (Mathf.NearEqual(Math.Abs(dot), 1))
|
||||
up = reference.Right;
|
||||
return LookRotation(normal, up);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two quaternions.
|
||||
/// </summary>
|
||||
|
||||
@@ -660,6 +660,14 @@ public:
|
||||
// @param roll The roll of rotation (in radians)
|
||||
// @param result When the method completes, contains the newly created quaternion
|
||||
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
|
||||
|
||||
/// <summary>
|
||||
/// Gets rotation from a normal in relation to a transform. This function is especially useful for axis aligned faces, and with <seealso cref="Physics::RayCast"/>.
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="reference">The reference transform.</param>
|
||||
/// <returns>The rotation from the normal vector.</returns>
|
||||
static Quaternion GetRotationFromNormal(const Vector3& normal, const Transform& reference);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -252,3 +252,9 @@ void Transform::Lerp(const Transform& t1, const Transform& t2, float amount, Tra
|
||||
Quaternion::Slerp(t1.Orientation, t2.Orientation, amount, result.Orientation);
|
||||
Float3::Lerp(t1.Scale, t2.Scale, amount, result.Scale);
|
||||
}
|
||||
|
||||
Transform Transform::AlignRotationToNormalAndSnapToGrid(const Vector3& point, const Vector3& normal, const Vector3& normalOffset, const Transform& relativeTo, const Vector3& gridSize, const Float3& scale)
|
||||
{
|
||||
Quaternion rot = Quaternion::GetRotationFromNormal(normal, relativeTo);
|
||||
return Transform(Vector3::SnapToGrid(point, gridSize, rot, relativeTo.Translation, normalOffset), rot, scale);
|
||||
}
|
||||
|
||||
@@ -478,6 +478,96 @@ namespace FlaxEngine
|
||||
Float3.Lerp(ref start.Scale, ref end.Scale, amount, out result.Scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines the functions:<br/>
|
||||
/// <see cref="Vector3.SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/>,<br/>
|
||||
/// <see cref="Quaternion.GetRotationFromNormal"/>.
|
||||
/// <example><para><b>Example code:</b></para>
|
||||
/// <code>
|
||||
/// <see langword="public" /> <see langword="class" /> AlignRotationToObjectAndSnapToGridExample : <see cref="Script"/><br/>
|
||||
/// <see langword="public" /> <see cref="Vector3"/> Offset = new Vector3(0, 0, 50f);<br/>
|
||||
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||
/// {<br/>
|
||||
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||
/// {<br/>
|
||||
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||
/// <see cref="Vector3"/> point = hit.Point;
|
||||
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||
/// SomeObject.Transform = <see cref="Transform"/>.AlignRotationToNormalAndSnapToGrid
|
||||
/// (
|
||||
/// point,
|
||||
/// normal,
|
||||
/// Offset,
|
||||
/// transform,
|
||||
/// SomeObject.Scale,
|
||||
/// GridSize,
|
||||
/// Float3.One
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="point">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="relativeTo">The relative transform.</param>
|
||||
/// <param name="scale">The scale to apply to the transform.</param>
|
||||
/// <returns>The rotated and snapped transform.</returns>
|
||||
public static Transform AlignRotationToNormalAndSnapToGrid(Vector3 point, Vector3 normal, Vector3 normalOffset, Transform relativeTo, Vector3 gridSize, Float3 scale)
|
||||
{
|
||||
Quaternion rot = Quaternion.GetRotationFromNormal(normal, relativeTo);
|
||||
return new Transform(Vector3.SnapToGrid(point, gridSize, rot, relativeTo.Translation, normalOffset), rot, scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines the functions:<br/>
|
||||
/// <see cref="Vector3.SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/>,<br/>
|
||||
/// <see cref="Quaternion.GetRotationFromNormal"/>.
|
||||
/// <example><para><b>Example code:</b></para>
|
||||
/// <code>
|
||||
/// <see langword="public" /> <see langword="class" /> AlignRotationToObjectAndSnapToGridExample : <see cref="Script"/><br/>
|
||||
/// <see langword="public" /> <see cref="Vector3"/> Offset = new Vector3(0, 0, 50f);<br/>
|
||||
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||
/// {<br/>
|
||||
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||
/// {<br/>
|
||||
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||
/// <see cref="Vector3"/> point = hit.Point;
|
||||
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||
/// SomeObject.Transform = <see cref="Transform"/>.AlignRotationToNormalAndSnapToGrid
|
||||
/// (
|
||||
/// point,
|
||||
/// normal,
|
||||
/// Offset,
|
||||
/// transform,
|
||||
/// GridSize
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="point">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="relativeTo">The relative transform.</param>
|
||||
/// <returns>The rotated and snapped transform with scale <see cref="Float3.One"/>.</returns>
|
||||
public static Transform AlignRotationToNormalAndSnapToGrid(Vector3 point, Vector3 normal, Vector3 normalOffset, Transform relativeTo, Vector3 gridSize)
|
||||
{
|
||||
return AlignRotationToNormalAndSnapToGrid(point, normal, normalOffset, relativeTo, gridSize, Float3.One);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
|
||||
@@ -291,6 +291,20 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines the functions: <br/>
|
||||
/// <see cref="SnapToGrid"/>,<br/>
|
||||
/// <see cref="GetRotationFromNormal"/>.
|
||||
/// </summary>
|
||||
/// <param name="point">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="relativeTo">The relative transform.</param>
|
||||
/// <param name="scale">The scale to apply to the transform.</param>
|
||||
/// <returns>The rotated and snapped transform.</returns>
|
||||
static Transform AlignRotationToNormalAndSnapToGrid(const Vector3& point, const Vector3& normal, const Vector3& normalOffset, const Transform& relativeTo, const Vector3& gridSize, const Float3& scale = Float3::One);
|
||||
|
||||
public:
|
||||
FORCE_INLINE Transform operator*(const Transform& other) const
|
||||
{
|
||||
|
||||
@@ -324,6 +324,20 @@ float Float3::Angle(const Float3& from, const Float3& to)
|
||||
return Math::Acos(dot);
|
||||
}
|
||||
|
||||
template<>
|
||||
Float3 Float3::SnapToGrid(const Float3& pos, const Float3& gridSize)
|
||||
{
|
||||
return Float3(Math::Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.X) * gridSize.X,
|
||||
Math::Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.Y) * gridSize.Y,
|
||||
Math::Ceil((pos.Z - (gridSize.Z * 0.5f)) / gridSize.Z) * gridSize.Z);
|
||||
}
|
||||
|
||||
template<>
|
||||
Float3 Float3::SnapToGrid(const Float3& point, const Float3& gridSize, const Quaternion& gridOrientation, const Float3& gridOrigin, const Float3& offset)
|
||||
{
|
||||
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||
}
|
||||
|
||||
// Double
|
||||
|
||||
static_assert(sizeof(Double3) == 24, "Invalid Double3 type size.");
|
||||
@@ -638,6 +652,20 @@ double Double3::Angle(const Double3& from, const Double3& to)
|
||||
return Math::Acos(dot);
|
||||
}
|
||||
|
||||
template<>
|
||||
Double3 Double3::SnapToGrid(const Double3& pos, const Double3& gridSize)
|
||||
{
|
||||
return Double3(Math::Ceil((pos.X - (gridSize.X * 0.5)) / gridSize.X) * gridSize.X,
|
||||
Math::Ceil((pos.Y - (gridSize.Y * 0.5)) / gridSize.Y) * gridSize.Y,
|
||||
Math::Ceil((pos.Z - (gridSize.Z * 0.5)) / gridSize.Z) * gridSize.Z);
|
||||
}
|
||||
|
||||
template<>
|
||||
Double3 Double3::SnapToGrid(const Double3& point, const Double3& gridSize, const Quaternion& gridOrientation, const Double3& gridOrigin, const Double3& offset)
|
||||
{
|
||||
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||
}
|
||||
|
||||
// Int
|
||||
|
||||
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
|
||||
@@ -852,3 +880,17 @@ int32 Int3::Angle(const Int3& from, const Int3& to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<>
|
||||
Int3 Int3::SnapToGrid(const Int3& pos, const Int3& gridSize)
|
||||
{
|
||||
return Int3(((pos.X - (gridSize.X / 2)) / gridSize.X) * gridSize.X,
|
||||
((pos.Y - (gridSize.Y / 2)) / gridSize.Y) * gridSize.Y,
|
||||
((pos.Z - (gridSize.Z / 2)) / gridSize.Z) * gridSize.Z);
|
||||
}
|
||||
|
||||
template<>
|
||||
Int3 Int3::SnapToGrid(const Int3& point, const Int3& gridSize, const Quaternion& gridOrientation, const Int3& gridOrigin, const Int3& offset)
|
||||
{
|
||||
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||
}
|
||||
|
||||
@@ -1672,7 +1672,7 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the input position into the grid.
|
||||
/// Snaps the input position onto the grid.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
@@ -1685,6 +1685,44 @@ namespace FlaxEngine
|
||||
return pos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the <paramref name="point"/> onto the rotated grid.<br/>
|
||||
/// For world aligned grid snapping use <b><see cref="SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/></b> instead.
|
||||
/// <example><para><b>Example code:</b></para>
|
||||
/// <code>
|
||||
/// <see langword="public" /> <see langword="class" /> SnapToGridExample : <see cref="Script"/><br/>
|
||||
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||
/// {<br/>
|
||||
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||
/// {<br/>
|
||||
/// <see cref="Vector3"/> position = hit.Collider.Position;
|
||||
/// <see cref="FlaxEngine.Transform"/> transform = hit.Collider.Transform;
|
||||
/// <see cref="Vector3"/> point = hit.Point;
|
||||
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||
/// //Get rotation from normal relative to collider transform
|
||||
/// <see cref="Quaternion"/> rot = <see cref="Quaternion"/>.GetRotationFromNormal(normal, transform);
|
||||
/// point = <see cref="Vector3"/>.SnapToGrid(point, GridSize, rot, position);
|
||||
/// SomeObject.Position = point;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <param name="point">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <param name="gridOrientation">The rotation of the grid.</param>
|
||||
/// <param name="gridOrigin">The center point of the grid.</param>
|
||||
/// <param name="offset">The local position offset applied to the snapped position before grid rotation.</param>
|
||||
/// <returns>The position snapped to the grid.</returns>
|
||||
public static Vector3 SnapToGrid(Vector3 point, Vector3 gridSize, Quaternion gridOrientation, Vector3 gridOrigin, Vector3 offset)
|
||||
{
|
||||
return ((SnapToGrid(point - gridOrigin, gridSize) * gridOrientation.Conjugated() + offset) * gridOrientation) + gridOrigin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
|
||||
@@ -927,6 +927,25 @@ public:
|
||||
/// <param name="to">The second vector.</param>
|
||||
/// <returns>The angle (in radians).</returns>
|
||||
static FLAXENGINE_API T Angle(const Vector3Base& from, const Vector3Base& to);
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the input position onto the grid.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <returns>The position snapped to the grid.</returns>
|
||||
static FLAXENGINE_API Vector3Base SnapToGrid(const Vector3Base& pos, const Vector3Base& gridSize);
|
||||
|
||||
/// <summary>
|
||||
/// Snaps the <paramref name="point"/> onto the rotated grid. For world aligned grid snapping use <b><see cref="SnapToGrid"/></b> instead.
|
||||
/// </summary>
|
||||
/// <param name="point">The position to snap.</param>
|
||||
/// <param name="gridSize">The size of the grid.</param>
|
||||
/// <param name="gridOrigin">The center point of the grid.</param>
|
||||
/// <param name="gridOrientation">The rotation of the grid.</param>
|
||||
/// <param name="offset">The local position offset applied to the snapped position before grid rotation.</param>
|
||||
/// <returns>The position snapped to the grid.</returns>
|
||||
static FLAXENGINE_API Vector3Base SnapToGrid(const Vector3Base& point, const Vector3Base& gridSize, const Quaternion& gridOrientation, const Vector3Base& gridOrigin = Zero, const Vector3Base& offset = Zero);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -479,6 +479,10 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
||||
batch.DrawCall.InstanceCount = 1;
|
||||
auto& firstInstance = batch.Instances[0];
|
||||
firstInstance.Load(batch.DrawCall);
|
||||
#if USE_EDITOR
|
||||
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||
batch.DrawCall.Surface.LODDitherFactor = type.ScaleInLightmap; // See LightmapUVsDensityMaterialShader
|
||||
#endif
|
||||
|
||||
if (EnumHasAnyFlags(drawModes, DrawPass::Forward))
|
||||
{
|
||||
|
||||
@@ -39,11 +39,13 @@ public:
|
||||
|
||||
FORCE_INLINE GPUPipelineState* Get(int index) const
|
||||
{
|
||||
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||
return States[index];
|
||||
}
|
||||
|
||||
FORCE_INLINE GPUPipelineState*& operator[](int32 index)
|
||||
{
|
||||
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||
return States[index];
|
||||
}
|
||||
|
||||
@@ -129,6 +131,7 @@ public:
|
||||
public:
|
||||
FORCE_INLINE GPUShaderProgramCS* Get(const int index) const
|
||||
{
|
||||
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||
return Shaders[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -480,6 +480,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
|
||||
const ViewMode viewMode = renderContext.View.Mode;
|
||||
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
||||
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
||||
if (viewMode == ViewMode::LightmapUVsDensity)
|
||||
drawCall.Surface.LODDitherFactor = info.LightmapScale; // See LightmapUVsDensityMaterialShader
|
||||
#endif
|
||||
|
||||
// Push draw call to the render list
|
||||
@@ -541,6 +543,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
|
||||
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
|
||||
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
||||
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
||||
if (viewMode == ViewMode::LightmapUVsDensity)
|
||||
drawCall.Surface.LODDitherFactor = info.LightmapScale; // See LightmapUVsDensityMaterialShader
|
||||
#endif
|
||||
|
||||
// Push draw call to the render lists
|
||||
|
||||
@@ -241,5 +241,9 @@ public:
|
||||
/// The object sorting key.
|
||||
/// </summary>
|
||||
int8 SortOrder;
|
||||
|
||||
#if USE_EDITOR
|
||||
float LightmapScale = -1.0f;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
@@ -52,6 +52,11 @@ API_ENUM() enum class ToneMappingMode
|
||||
/// The ACES Filmic reference tonemapper (approximation).
|
||||
/// </summary>
|
||||
ACES = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The AGX tonemapper.
|
||||
/// </summary>
|
||||
AGX = 3,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -213,13 +213,13 @@ protected:
|
||||
const int32 dstMips = dstTexture->MipLevels();
|
||||
GPUTexture* srcTexture = _streamingTexture->GetTexture();
|
||||
const int32 srcMips = srcTexture->MipLevels();
|
||||
const int32 srcMissingMips = srcMips - srcTexture->ResidentMipLevels();
|
||||
const int32 mipCount = Math::Min(dstMips, srcMips);
|
||||
ASSERT(mipCount > 0);
|
||||
for (int32 mipIndex = 0; mipIndex < mipCount; mipIndex++)
|
||||
for (int32 mipIndex = srcMissingMips; mipIndex < mipCount; mipIndex++)
|
||||
{
|
||||
context->GPU->CopySubresource(dstTexture, dstMips - mipIndex - 1, srcTexture, srcMips - mipIndex - 1);
|
||||
}
|
||||
_uploadedMipCount = mipCount;
|
||||
_uploadedMipCount = mipCount - srcMissingMips;
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
@@ -238,10 +238,10 @@ protected:
|
||||
|
||||
void OnSync() override
|
||||
{
|
||||
_newTexture->SetResidentMipLevels(_uploadedMipCount);
|
||||
Swap(_streamingTexture->_texture, _newTexture);
|
||||
_streamingTexture->GetTexture()->SetResidentMipLevels(_uploadedMipCount);
|
||||
_streamingTexture->ResidencyChanged();
|
||||
SAFE_DELETE_GPU_RESOURCE(_newTexture);
|
||||
_streamingTexture->ResidencyChanged();
|
||||
|
||||
// Base
|
||||
GPUTask::OnSync();
|
||||
|
||||
@@ -27,32 +27,18 @@ struct AxisEvaluation
|
||||
|
||||
struct ActionData
|
||||
{
|
||||
bool Active;
|
||||
uint64 FrameIndex;
|
||||
InputActionState State;
|
||||
|
||||
ActionData()
|
||||
{
|
||||
Active = false;
|
||||
FrameIndex = 0;
|
||||
State = InputActionState::Waiting;
|
||||
}
|
||||
bool Active = false;
|
||||
uint64 FrameIndex = 0;
|
||||
InputActionState State = InputActionState::Waiting;
|
||||
};
|
||||
|
||||
struct AxisData
|
||||
{
|
||||
float Value;
|
||||
float ValueRaw;
|
||||
float PrevKeyValue;
|
||||
uint64 FrameIndex;
|
||||
|
||||
AxisData()
|
||||
{
|
||||
Value = 0.0f;
|
||||
ValueRaw = 0.0f;
|
||||
PrevKeyValue = 0.0f;
|
||||
FrameIndex = 0;
|
||||
}
|
||||
float Value = 0.0f;
|
||||
float ValueRaw = 0.0f;
|
||||
float PrevValue = 0.0f;
|
||||
float PrevKeyValue = 0.0f;
|
||||
uint64 FrameIndex = 0;
|
||||
};
|
||||
|
||||
namespace InputImpl
|
||||
@@ -990,6 +976,7 @@ void InputService::Update()
|
||||
|
||||
// Setup axis data
|
||||
data.PrevKeyValue = e.PrevKeyValue;
|
||||
data.PrevValue = data.Value;
|
||||
data.ValueRaw = e.RawValue;
|
||||
data.Value = e.Value;
|
||||
|
||||
@@ -1025,7 +1012,7 @@ void InputService::Update()
|
||||
{
|
||||
for (auto i = Axes.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (Math::NotNearEqual(i->Value.Value, i->Value.PrevKeyValue))
|
||||
if (Math::NotNearEqual(i->Value.Value, i->Value.PrevValue))
|
||||
{
|
||||
Input::AxisValueChanged(i->Key);
|
||||
}
|
||||
|
||||
@@ -237,7 +237,10 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewp
|
||||
viewport.Unproject(nearPoint, ivp, nearPoint);
|
||||
viewport.Unproject(farPoint, ivp, farPoint);
|
||||
|
||||
return Ray(nearPoint, Vector3::Normalize(farPoint - nearPoint));
|
||||
Vector3 dir = Vector3::Normalize(farPoint - nearPoint);
|
||||
if (dir.IsZero())
|
||||
return Ray::Identity;
|
||||
return Ray(nearPoint, dir);
|
||||
}
|
||||
|
||||
Viewport Camera::GetViewport() const
|
||||
@@ -303,6 +306,8 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp
|
||||
void Camera::OnPreviewModelLoaded()
|
||||
{
|
||||
_previewModelBuffer.Setup(_previewModel.Get());
|
||||
if (_previewModelBuffer.Count() > 0)
|
||||
_previewModelBuffer.At(0).ReceiveDecals = false;
|
||||
|
||||
UpdateCache();
|
||||
}
|
||||
|
||||
@@ -150,35 +150,30 @@ float Spline::GetSplineLength() const
|
||||
{
|
||||
float sum = 0.0f;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
Vector3 prevPoint = Vector3::Zero;
|
||||
if (Curve.GetKeyframes().Count() != 0)
|
||||
{
|
||||
const auto& a = Curve[0];
|
||||
prevPoint = a.Value.Translation * _transform.Scale;
|
||||
}
|
||||
constexpr float step = 1.0f / (float)(slices - 1);
|
||||
const Vector3 scale = _transform.Scale;
|
||||
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
||||
{
|
||||
const auto& a = Curve[i - 1];
|
||||
const auto& b = Curve[i];
|
||||
Vector3 prevPoint = a.Value.Translation * scale;
|
||||
|
||||
const float length = Math::Abs(b.Time - a.Time);
|
||||
Vector3 leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
for (int32 slice = 0; slice < slices; slice++)
|
||||
for (int32 slice = 1; slice < slices; slice++)
|
||||
{
|
||||
const float t = (float)slice * step;
|
||||
Vector3 pos;
|
||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||
pos *= _transform.Scale;
|
||||
sum += (float)Vector3::DistanceSquared(pos, prevPoint);
|
||||
pos *= scale;
|
||||
sum += (float)Vector3::Distance(pos, prevPoint);
|
||||
prevPoint = pos;
|
||||
}
|
||||
}
|
||||
return Math::Sqrt(sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
float Spline::GetSplineSegmentLength(int32 index) const
|
||||
@@ -188,28 +183,28 @@ float Spline::GetSplineSegmentLength(int32 index) const
|
||||
CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f);
|
||||
float sum = 0.0f;
|
||||
constexpr int32 slices = 20;
|
||||
constexpr float step = 1.0f / (float)slices;
|
||||
constexpr float step = 1.0f / (float)(slices - 1);
|
||||
const auto& a = Curve[index - 1];
|
||||
const auto& b = Curve[index];
|
||||
Vector3 startPoint = a.Value.Translation * _transform.Scale;
|
||||
const Vector3 scale = _transform.Scale;
|
||||
Vector3 prevPoint = a.Value.Translation * scale;
|
||||
{
|
||||
const float length = Math::Abs(b.Time - a.Time);
|
||||
Vector3 leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
for (int32 slice = 0; slice < slices; slice++)
|
||||
for (int32 slice = 1; slice < slices; slice++)
|
||||
{
|
||||
const float t = (float)slice * step;
|
||||
Vector3 pos;
|
||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||
pos *= _transform.Scale;
|
||||
sum += (float)Vector3::DistanceSquared(pos, startPoint);
|
||||
startPoint = pos;
|
||||
pos *= scale;
|
||||
sum += (float)Vector3::Distance(pos, prevPoint);
|
||||
prevPoint = pos;
|
||||
}
|
||||
}
|
||||
return Math::Sqrt(sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
float Spline::GetSplineTime(int32 index) const
|
||||
|
||||
@@ -357,6 +357,10 @@ void StaticModel::Draw(RenderContext& renderContext)
|
||||
draw.ForcedLOD = _forcedLod;
|
||||
draw.SortOrder = _sortOrder;
|
||||
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
||||
#if USE_EDITOR
|
||||
if (HasStaticFlag(StaticFlags::Lightmap))
|
||||
draw.LightmapScale = _scaleInLightmap;
|
||||
#endif
|
||||
|
||||
Model->Draw(renderContext, draw);
|
||||
|
||||
@@ -391,6 +395,10 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
|
||||
draw.ForcedLOD = _forcedLod;
|
||||
draw.SortOrder = _sortOrder;
|
||||
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
||||
#if USE_EDITOR
|
||||
if (HasStaticFlag(StaticFlags::Lightmap))
|
||||
draw.LightmapScale = _scaleInLightmap;
|
||||
#endif
|
||||
|
||||
Model->Draw(renderContextBatch, draw);
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
[TypeConverter(typeof(TypeConverters.TagConverter))]
|
||||
#endif
|
||||
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -254,7 +252,6 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class TagConverter : TypeConverter
|
||||
@@ -291,4 +288,3 @@ namespace FlaxEngine.TypeConverters
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1503,6 +1503,8 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
||||
|
||||
bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
||||
{
|
||||
if (targetIds.IsValid() && targetIds.Length() == 0)
|
||||
return true; // Target list is provided, but it's empty so nobody will get this RPC
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||
if (!info || !obj || NetworkManager::IsOffline())
|
||||
|
||||
@@ -66,12 +66,14 @@ bool GPUParticles::Init(ParticleEmitter* owner, MemoryReadStream& shaderCacheStr
|
||||
LOG(Warning, "Missing valid GPU particles constant buffer.");
|
||||
return true;
|
||||
}
|
||||
if (cb0->GetSize() < sizeof(GPUParticlesData))
|
||||
const int32 cbSize = cb0->GetSize();
|
||||
if (cbSize < sizeof(GPUParticlesData))
|
||||
{
|
||||
LOG(Warning, "Invalid size GPU particles constant buffer. required {0} bytes but got {1}", sizeof(GPUParticlesData), cb0->GetSize());
|
||||
LOG(Warning, "Invalid size GPU particles constant buffer. required {0} bytes but got {1}", sizeof(GPUParticlesData), cbSize);
|
||||
return true;
|
||||
}
|
||||
_cbData.Resize(cb0->GetSize());
|
||||
_cbData.Resize(cbSize);
|
||||
Platform::MemoryClear(_cbData.Get(), cbSize);
|
||||
|
||||
// Load material parameters
|
||||
if (_params.Load(materialParamsStream))
|
||||
|
||||
@@ -15,7 +15,7 @@ private:
|
||||
bool _useVolumeTexture;
|
||||
PixelFormat _lutFormat;
|
||||
AssetReference<Shader> _shader;
|
||||
GPUPipelineStatePermutationsPs<3> _psLut;
|
||||
GPUPipelineStatePermutationsPs<4> _psLut;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Foliage/Foliage.h"
|
||||
#include "Engine/ShadowsOfMordor/Builder.Config.h"
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Level/Actors/StaticModel.h"
|
||||
|
||||
@@ -70,40 +69,6 @@ DrawPass LightmapUVsDensityMaterialShader::GetDrawModes() const
|
||||
return DrawPass::GBuffer;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
Actor* FindActorByDrawCall(Actor* actor, const DrawCall& drawCall, float& scaleInLightmap)
|
||||
{
|
||||
// TODO: large-worlds
|
||||
const auto asStaticModel = ScriptingObject::Cast<StaticModel>(actor);
|
||||
if (asStaticModel && asStaticModel->GetPerInstanceRandom() == drawCall.PerInstanceRandom && asStaticModel->GetPosition() == drawCall.ObjectPosition)
|
||||
{
|
||||
scaleInLightmap = asStaticModel->GetScaleInLightmap();
|
||||
return asStaticModel;
|
||||
}
|
||||
const auto asFoliage = ScriptingObject::Cast<Foliage>(actor);
|
||||
if (asFoliage)
|
||||
{
|
||||
for (auto i = asFoliage->Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto& instance = *i;
|
||||
if (instance.Random == drawCall.PerInstanceRandom && instance.Transform.Translation == drawCall.ObjectPosition)
|
||||
{
|
||||
scaleInLightmap = asFoliage->FoliageTypes[instance.Type].ScaleInLightmap;
|
||||
return asFoliage;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Actor* child : actor->Children)
|
||||
{
|
||||
const auto other = FindActorByDrawCall(child, drawCall, scaleInLightmap);
|
||||
if (other)
|
||||
return other;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
||||
{
|
||||
// Prepare
|
||||
@@ -121,33 +86,6 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
||||
_ps->Init(psDesc);
|
||||
}
|
||||
|
||||
// Find the static model that produced this draw call
|
||||
const Actor* drawCallActor = nullptr;
|
||||
float scaleInLightmap = 1.0f;
|
||||
if (params.RenderContext.Task)
|
||||
{
|
||||
// Skip this lookup as it's too slow
|
||||
|
||||
/*if (params.RenderContext.Task->ActorsSource & ActorsSources::CustomActors)
|
||||
{
|
||||
for (auto actor : params.RenderContext.Task->CustomActors)
|
||||
{
|
||||
drawCallActor = FindActorByDrawCall(actor, drawCall, scaleInLightmap);
|
||||
if (drawCallActor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!drawCallActor && params.RenderContext.Task->ActorsSource & ActorsSources::Scenes)
|
||||
{
|
||||
for (auto& scene : Level::Scenes)
|
||||
{
|
||||
drawCallActor = FindActorByDrawCall(scene, drawCall, scaleInLightmap);
|
||||
if (drawCallActor)
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Bind constants
|
||||
if (cb && cb->GetSize())
|
||||
{
|
||||
@@ -166,19 +104,15 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
||||
data.LightmapSize = 1024.0f;
|
||||
data.LightmapArea = drawCall.Surface.LightmapUVsArea;
|
||||
const ModelLOD* drawCallModelLod;
|
||||
if (GBufferPass::IndexBufferToModelLOD.TryGet(drawCall.Geometry.IndexBuffer, drawCallModelLod))
|
||||
float scaleInLightmap = drawCall.Surface.LODDitherFactor; // Reuse field
|
||||
if (scaleInLightmap < 0.0f)
|
||||
data.LightmapSize = -1.0f; // Not using lightmap
|
||||
else if (GBufferPass::IndexBufferToModelLOD.TryGet(drawCall.Geometry.IndexBuffer, drawCallModelLod))
|
||||
{
|
||||
// Calculate current lightmap slot size for the object (matches the ShadowsOfMordor calculations when baking the lighting)
|
||||
float globalObjectsScale = 1.0f;
|
||||
int32 atlasSize = 1024;
|
||||
int32 chartsPadding = 3;
|
||||
const Scene* drawCallScene = drawCallActor ? drawCallActor->GetScene() : (Level::Scenes.Count() != 0 ? Level::Scenes[0] : nullptr);
|
||||
if (drawCallScene)
|
||||
{
|
||||
globalObjectsScale = drawCallScene->Info.LightmapSettings.GlobalObjectsScale;
|
||||
atlasSize = (int32)drawCallScene->Info.LightmapSettings.AtlasSize;
|
||||
chartsPadding = drawCallScene->Info.LightmapSettings.ChartsPadding;
|
||||
}
|
||||
BoundingBox box = drawCallModelLod->GetBox(drawCall.World);
|
||||
Float3 size = box.GetSize();
|
||||
float dimensionsCoeff = size.AverageArithmetic();
|
||||
|
||||
@@ -479,7 +479,7 @@ namespace FlaxEngine.Json
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.Name.StartsWith("JsonAssetReference");
|
||||
return objectType.Name.StartsWith("JsonAssetReference", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the list with physical materials used to define the terrain collider physical properties - each for terrain layer (layer index matches index in this array).
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(520), EditorDisplay(\"Collision\"), Collection(MinCount = 8, MaxCount = 8)")
|
||||
API_PROPERTY(Attributes="EditorOrder(520), EditorDisplay(\"Collision\"), Collection(MinCount=8, MaxCount=8)")
|
||||
FORCE_INLINE const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& GetPhysicalMaterials() const
|
||||
{
|
||||
return _physicalMaterials;
|
||||
@@ -199,6 +199,27 @@ public:
|
||||
API_PROPERTY()
|
||||
void SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the physical material used to define the terrain collider physical properties.
|
||||
/// [Deprecated on 16.02.2024, expires on 16.02.2026]
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
|
||||
DEPRECATED("Use PhysicalMaterials instead.") FORCE_INLINE JsonAssetReference<PhysicalMaterial>& GetPhysicalMaterial()
|
||||
{
|
||||
return _physicalMaterials[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the physical materials used to define the terrain collider physical properties.
|
||||
/// [Deprecated on 16.02.2024, expires on 16.02.2026]
|
||||
/// </summary>
|
||||
DEPRECATED("Use PhysicalMaterials instead.") API_PROPERTY()
|
||||
void SetPhysicalMaterial(const JsonAssetReference<PhysicalMaterial>& value)
|
||||
{
|
||||
for (auto& e : _physicalMaterials)
|
||||
e = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the terrain Level Of Detail count.
|
||||
/// </summary>
|
||||
|
||||
@@ -124,6 +124,10 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
|
||||
}
|
||||
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
||||
drawCall.PerInstanceRandom = _perInstanceRandom;
|
||||
#if USE_EDITOR
|
||||
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
|
||||
#endif
|
||||
|
||||
// Add half-texel offset for heightmap sampling in vertex shader
|
||||
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
||||
@@ -181,6 +185,10 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
|
||||
}
|
||||
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
||||
drawCall.PerInstanceRandom = _perInstanceRandom;
|
||||
#if USE_EDITOR
|
||||
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
|
||||
#endif
|
||||
|
||||
// Add half-texel offset for heightmap sampling in vertex shader
|
||||
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
Foo::Foo(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
, FooInterface(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TestNesting::TestNesting(const SpawnParams& params)
|
||||
: SerializableScriptingObject(params)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,21 @@
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||
|
||||
// Test interface (name conflict with namespace)
|
||||
API_INTERFACE(Namespace="Foo") class FLAXENGINE_API IFoo
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(IFoo);
|
||||
};
|
||||
|
||||
// Test class (name conflict with namespace)
|
||||
API_CLASS(Namespace="Foo") class FLAXENGINE_API Foo : public ScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(Foo);
|
||||
|
||||
// Test field.
|
||||
API_FIELD() IFoo* FooInterface;
|
||||
};
|
||||
|
||||
// Test compilation with nested types.
|
||||
API_CLASS() class TestNesting : public SerializableScriptingObject
|
||||
{
|
||||
|
||||
@@ -192,15 +192,10 @@ void MaterialGenerator::prepareLayer(MaterialLayer* layer, bool allowVisiblePara
|
||||
|
||||
// For all not root layers (sub-layers) we won't to change theirs ID in order to prevent duplicated ID)
|
||||
m.SrcId = param->Identifier;
|
||||
if (isRooLayer)
|
||||
m.DstId = param->Identifier;
|
||||
if (!isRooLayer)
|
||||
{
|
||||
// Use the same ID (so we can edit it)
|
||||
m.DstId = param->Identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate new ID
|
||||
m.DstId = param->Identifier;
|
||||
// Generate new ID (stable permutation based on the original ID)
|
||||
m.DstId.A += _parameters.Count() * 17 + 13;
|
||||
}
|
||||
layer->ParamIdsMappings.Add(m);
|
||||
|
||||
@@ -644,20 +644,12 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
// Flipbook
|
||||
case 10:
|
||||
{
|
||||
// Get input values
|
||||
auto uv = Value::Cast(tryGetValue(node->GetBox(0), getUVs), VariantType::Float2);
|
||||
auto frame = Value::Cast(tryGetValue(node->GetBox(1), node->Values[0]), VariantType::Float);
|
||||
auto framesXY = Value::Cast(tryGetValue(node->GetBox(2), node->Values[1]), VariantType::Float2);
|
||||
auto invertX = Value::Cast(tryGetValue(node->GetBox(3), node->Values[2]), VariantType::Float);
|
||||
auto invertY = Value::Cast(tryGetValue(node->GetBox(4), node->Values[3]), VariantType::Float);
|
||||
|
||||
// Write operations
|
||||
auto framesCount = writeLocal(VariantType::Float, String::Format(TEXT("{0}.x * {1}.y"), framesXY.Value, framesXY.Value), node);
|
||||
frame = writeLocal(VariantType::Float, String::Format(TEXT("fmod(floor({0}), {1})"), frame.Value, framesCount.Value), node);
|
||||
auto framesXYInv = writeOperation2(node, Value::One.AsFloat2(), framesXY, '/');
|
||||
auto frameY = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.y - (floor({2} * {3}.x) + {0} * 1))"), invertY.Value, framesXY.Value, frame.Value, framesXYInv.Value), node);
|
||||
auto frameX = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.x - (({2} - {1}.x * floor({2} * {3}.x)) + {0} * 1))"), invertX.Value, framesXY.Value, frame.Value, framesXYInv.Value), node);
|
||||
value = writeLocal(VariantType::Float2, String::Format(TEXT("({3} + float2({0}, {1})) * {2}"), frameX.Value, frameY.Value, framesXYInv.Value, uv.Value), node);
|
||||
value = writeLocal(VariantType::Float2, String::Format(TEXT("Flipbook({0}, {1}, {2}, float2({3}, {4}))"), uv.Value, frame.Value, framesXY.Value, invertX.Value, invertY.Value), node);
|
||||
break;
|
||||
}
|
||||
// Sample Global SDF
|
||||
|
||||
@@ -1379,14 +1379,16 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
||||
}
|
||||
if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Animations))
|
||||
{
|
||||
int32 index = 0;
|
||||
for (auto& animation : data.Animations)
|
||||
{
|
||||
LOG(Info, "Imported animation '{}' has {} channels, duration: {} frames, frames per second: {}", animation.Name, animation.Channels.Count(), animation.Duration, animation.FramesPerSecond);
|
||||
LOG(Info, "Imported animation '{}' at index {} has {} channels, duration: {} frames ({} seconds), frames per second: {}", animation.Name, index, animation.Channels.Count(), animation.Duration, animation.GetLength(), animation.FramesPerSecond);
|
||||
if (animation.Duration <= ZeroTolerance || animation.FramesPerSecond <= ZeroTolerance)
|
||||
{
|
||||
errorMsg = TEXT("Invalid animation duration.");
|
||||
return true;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
switch (options.Type)
|
||||
|
||||
@@ -924,6 +924,19 @@ namespace FlaxEngine.GUI
|
||||
return newLineLoc;
|
||||
}
|
||||
|
||||
private int FindNextLineBegin()
|
||||
{
|
||||
int caretPos = CaretPosition;
|
||||
if (caretPos + 2 > TextLength)
|
||||
return TextLength;
|
||||
int newLineLoc = _text.IndexOf('\n', caretPos + 2);
|
||||
if (newLineLoc == -1)
|
||||
newLineLoc = TextLength;
|
||||
else
|
||||
newLineLoc++;
|
||||
return newLineLoc;
|
||||
}
|
||||
|
||||
private int FindLineDownChar(int index)
|
||||
{
|
||||
if (!IsMultiline)
|
||||
@@ -1423,6 +1436,30 @@ namespace FlaxEngine.GUI
|
||||
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.PageDown:
|
||||
{
|
||||
if (IsScrollable && IsMultiline)
|
||||
{
|
||||
var location = GetCharPosition(_selectionStart, out var height);
|
||||
var sizeHeight = Size.Y / height;
|
||||
location.Y += height * (int)sizeHeight;
|
||||
TargetViewOffset = Vector2.Clamp(new Float2(0, location.Y), Float2.Zero, TextSize - new Float2(0, Size.Y));
|
||||
SetSelection(HitTestText(location));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.PageUp:
|
||||
{
|
||||
if (IsScrollable && IsMultiline)
|
||||
{
|
||||
var location = GetCharPosition(_selectionStart, out var height);
|
||||
var sizeHeight = Size.Y / height;
|
||||
location.Y -= height * (int)sizeHeight;
|
||||
TargetViewOffset = Vector2.Clamp(new Float2(0, location.Y), Float2.Zero, TextSize - new Float2(0, Size.Y));
|
||||
SetSelection(HitTestText(location));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.Delete:
|
||||
{
|
||||
if (IsReadOnly)
|
||||
@@ -1491,8 +1528,13 @@ namespace FlaxEngine.GUI
|
||||
return true;
|
||||
case KeyboardKeys.End:
|
||||
{
|
||||
// Select text from the current cursor point to the beginning of a new line
|
||||
if (shiftDown && _selectionStart != -1)
|
||||
SetSelection(_selectionStart, FindNextLineBegin());
|
||||
// Move caret after last character
|
||||
SetSelection(TextLength);
|
||||
else
|
||||
SetSelection(TextLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
case KeyboardKeys.Tab:
|
||||
|
||||
@@ -750,7 +750,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
if (base.IsTouchOver)
|
||||
return true;
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i].IsTouchOver)
|
||||
return true;
|
||||
@@ -960,7 +960,7 @@ namespace FlaxEngine.GUI
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled && child.IsMouseOver)
|
||||
@@ -1063,7 +1063,7 @@ namespace FlaxEngine.GUI
|
||||
if (base.IsTouchPointerOver(pointerId))
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
if (_children[i].IsTouchPointerOver(pointerId))
|
||||
return true;
|
||||
@@ -1168,7 +1168,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnTouchLeave(int pointerId)
|
||||
{
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Visible && child.Enabled && child.IsTouchPointerOver(pointerId))
|
||||
@@ -1183,7 +1183,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnCharInput(char c)
|
||||
{
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Enabled && child.ContainsFocus)
|
||||
@@ -1197,7 +1197,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Enabled && child.ContainsFocus)
|
||||
@@ -1211,7 +1211,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnKeyUp(KeyboardKeys key)
|
||||
{
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.Enabled && child.ContainsFocus)
|
||||
@@ -1294,7 +1294,7 @@ namespace FlaxEngine.GUI
|
||||
base.OnDragLeave();
|
||||
|
||||
// Check all children collisions with mouse and fire events for them
|
||||
for (int i = 0; i < _children.Count && _children.Count > 0; i++)
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
if (child.IsDragOver)
|
||||
|
||||
@@ -409,9 +409,24 @@ void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, Variant& b, MathO
|
||||
case VariantType::Uint:
|
||||
v.AsUint = (uint32)op((float)a.AsUint, (float)b.AsUint);
|
||||
break;
|
||||
case VariantType::Int64:
|
||||
v.AsUint = (int64)op((float)a.AsInt64, (float)b.AsInt64);
|
||||
break;
|
||||
case VariantType::Uint64:
|
||||
v.AsUint = (uint64)op((float)a.AsUint64, (float)b.AsUint64);
|
||||
break;
|
||||
case VariantType::Int16:
|
||||
v.AsUint = (int16)op((float)a.AsInt16, (float)b.AsInt16);
|
||||
break;
|
||||
case VariantType::Uint16:
|
||||
v.AsUint = (uint16)op((float)a.AsUint16, (float)b.AsUint16);
|
||||
break;
|
||||
case VariantType::Float:
|
||||
v.AsFloat = op(a.AsFloat, b.AsFloat);
|
||||
break;
|
||||
case VariantType::Double:
|
||||
v.AsDouble = op((float)a.AsDouble, (float)b.AsDouble);
|
||||
break;
|
||||
case VariantType::Float2:
|
||||
{
|
||||
Float2& vv = *(Float2*)v.AsData;
|
||||
|
||||
Reference in New Issue
Block a user