Merge remote-tracking branch 'origin/1.1' into linux-editor

# Conflicts:
#	Source/Engine/Core/Math/BoundingSphere.cs
#	Source/Engine/Debug/DebugDraw.cpp
#	Source/Engine/Platform/Win32/Win32Platform.cpp
#	Source/Engine/Platform/Win32/Win32Platform.h
This commit is contained in:
Wojtek Figat
2021-02-23 22:32:17 +01:00
129 changed files with 6085 additions and 1891 deletions

View File

@@ -213,6 +213,11 @@ void SceneAnimationPlayer::Tick(float dt)
_lastTime = _time = time;
}
void SceneAnimationPlayer::MapObject(const Guid& from, const Guid& to)
{
_objectsMapping[from] = to;
}
void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset)
{
// Restore all tracks
@@ -268,7 +273,9 @@ void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset)
case SceneAnimation::Track::Types::ObjectReferenceProperty:
{
value = &_restoreData[state.RestoreStateIndex];
auto obj = Scripting::FindObject<ScriptingObject>(*(Guid*)value);
Guid id = *(Guid*)value;
_objectsMapping.TryGet(id, id);
auto obj = Scripting::FindObject<ScriptingObject>(id);
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
break;
}
@@ -358,7 +365,9 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
void* value = (void*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (leftKey) + sizeof(float));
if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty)
{
auto obj = Scripting::FindObject<ScriptingObject>(*(Guid*)value);
Guid id = *(Guid*)value;
_objectsMapping.TryGet(id, id);
auto obj = Scripting::FindObject<ScriptingObject>(id);
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
*(void**)target = value;
}
@@ -680,10 +689,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
// Find actor
const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>();
Guid id = trackData->ID;
_objectsMapping.TryGet(id, id);
state.Object = Scripting::FindObject<Actor>(trackData->ID);
if (!state.Object)
{
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("actor"));
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor"));
break;
}
}
@@ -708,10 +719,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
break;
// Find script
state.Object = Scripting::FindObject<Script>(trackData->ID);
Guid id = trackData->ID;
_objectsMapping.TryGet(id, id);
state.Object = Scripting::FindObject<Script>(id);
if (!state.Object)
{
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("script"));
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("script"));
break;
}
@@ -940,10 +953,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
state.ManagedObject = nullptr;
// Find actor
state.Object = Scripting::FindObject<Camera>(trackData->ID);
Guid id = trackData->ID;
_objectsMapping.TryGet(id, id);
state.Object = Scripting::FindObject<Camera>(id);
if (!state.Object)
{
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("actor"));
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor"));
break;
}
}

View File

@@ -62,6 +62,7 @@ private:
Array<byte> _restoreData;
Camera* _cameraCutCam = nullptr;
bool _isUsingCameraCuts = false;
Dictionary<Guid, Guid> _objectsMapping;
// PostFx settings to use
struct
@@ -189,6 +190,13 @@ public:
/// <param name="dt">The update delta time (in seconds). It does not get scaled by player Speed parameter.</param>
API_FUNCTION() void Tick(float dt);
/// <summary>
/// Adds an object mapping. The object `from` represented by it's unique ID will be redirected to the specified `to`. Can be used to reuse the same animation for different objects.
/// </summary>
/// <param name="from">The source object from the scene animation asset to replace.</param>
/// <param name="to">The destination object to animate.</param>
API_FUNCTION() void MapObject(const Guid& from, const Guid& to);
private:
void Restore(SceneAnimation* anim, int32 stateIndexOffset);

View File

@@ -342,7 +342,7 @@ void AudioSource::Update()
{
// Update the velocity
const Vector3 pos = GetPosition();
const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), ZeroTolerance);
const auto prevVelocity = _velocity;
_velocity = (pos - _prevPos) / dt;
_prevPos = pos;

View File

@@ -22,6 +22,7 @@
#include "Engine/Utilities/StringConverter.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#endif
#if ENABLE_ASSETS_DISCOVERY
#include "Engine/Core/Collections/HashSet.h"

View File

@@ -200,3 +200,38 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(XboxScarlettPlatform);
DESERIALIZE(AndroidPlatform);
}
void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
const auto tags = stream.FindMember("Tags");
if (tags != stream.MemberEnd() && tags->value.IsArray())
{
auto& tagsArray = tags->value;
Tags.Clear();
Tags.EnsureCapacity(tagsArray.Size());
for (uint32 i = 0; i < tagsArray.Size(); i++)
{
auto& v = tagsArray[i];
if (v.IsString())
Tags.Add(v.GetText());
}
}
const auto layers = stream.FindMember("Layers");
if (layers != stream.MemberEnd() && layers->value.IsArray())
{
auto& layersArray = layers->value;
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
{
auto& v = layersArray[i];
if (v.IsString())
Layers[i] = v.GetText();
else
Layers[i].Clear();
}
for (uint32 i = layersArray.Size(); i < 32; i++)
{
Layers[i].Clear();
}
}
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Serialization/SerializationFwd.h"
/// <summary>
/// The objects layers selection mask (from layers and tags settings). Uses 1 bit per layer (up to 32 layers).
/// </summary>
API_STRUCT() struct FLAXENGINE_API LayersMask
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LayersMask);
/// <summary>
/// The layers selection mask.
/// </summary>
API_FIELD() uint32 Mask = MAX_uint32;
FORCE_INLINE bool HasLayer(int32 layerIndex) const
{
return (Mask & (1 << layerIndex)) != 0;
}
bool HasLayer(const StringView& layerName) const;
bool operator==(const LayersMask& other) const;
bool operator!=(const LayersMask& other) const;
};
// @formatter:off
namespace Serialization
{
inline bool ShouldSerialize(const LayersMask& v, const void* otherObj)
{
return !otherObj || v != *(LayersMask*)otherObj;
}
inline void Serialize(ISerializable::SerializeStream& stream, const LayersMask& v, const void* otherObj)
{
stream.Uint(v.Mask);
}
inline void Deserialize(ISerializable::DeserializeStream& stream, LayersMask& v, ISerializeModifier* modifier)
{
v.Mask = stream.GetUint();
}
}
// @formatter:on

View File

@@ -3,7 +3,6 @@
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/Json.h"
/// <summary>
/// Layers and objects tags settings.
@@ -14,12 +13,12 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings);
public:
/// <summary>
/// The tags names.
/// The tag names.
/// </summary>
Array<String> Tags;
/// <summary>
/// The layers names.
/// The layer names.
/// </summary>
String Layers[32];
@@ -31,44 +30,6 @@ public:
static LayersAndTagsSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
const auto tags = stream.FindMember("Tags");
if (tags != stream.MemberEnd())
{
auto& tagsArray = tags->value;
ASSERT(tagsArray.IsArray());
Tags.EnsureCapacity(tagsArray.Size());
// Note: we cannot remove tags at runtime so this should deserialize them in additive mode
// Tags are stored as tagIndex in actors so collection change would break the linkage
for (uint32 i = 0; i < tagsArray.Size(); i++)
{
auto& v = tagsArray[i];
if (v.IsString())
{
const String tag = v.GetText();
if (!Tags.Contains(tag))
Tags.Add(tag);
}
}
}
const auto layers = stream.FindMember("Layers");
if (layers != stream.MemberEnd())
{
auto& layersArray = layers->value;
ASSERT(layersArray.IsArray());
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
{
auto& v = layersArray[i];
if (v.IsString())
Layers[i] = v.GetText();
else
Layers[i].Clear();
}
}
}
void Apply() override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
};

View File

@@ -2,6 +2,7 @@
#include "BoundingSphere.h"
#include "BoundingBox.h"
#include "Matrix.h"
#include "Ray.h"
#include "../Types/String.h"
@@ -194,3 +195,9 @@ void BoundingSphere::Merge(const BoundingSphere& value1, const Vector3& value2,
result.Center = value1.Center + vector * (max + min);
result.Radius = max;
}
void BoundingSphere::Transform(const BoundingSphere& sphere, const Matrix& matrix, BoundingSphere& result)
{
Vector3::Transform(sphere.Center, matrix, result.Center);
result.Radius = sphere.Radius * matrix.GetScaleVector().GetAbsolute().MaxValue();
}

View File

@@ -88,10 +88,7 @@ namespace FlaxEngine
/// Determines if there is an intersection between the current object and a <see cref="Ray" />.
/// </summary>
/// <param name="ray">The ray to test.</param>
/// <param name="distance">
/// When the method completes, contains the distance of the intersection,
/// or 0 if there was no intersection.
/// </param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
/// <returns>Whether the two objects intersected.</returns>
public bool Intersects(ref Ray ray, out float distance)
{
@@ -102,10 +99,7 @@ namespace FlaxEngine
/// Determines if there is an intersection between the current object and a <see cref="Ray" />.
/// </summary>
/// <param name="ray">The ray to test.</param>
/// <param name="point">
/// When the method completes, contains the point of intersection,
/// or <see cref="Vector3.Zero" /> if there was no intersection.
/// </param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
/// <returns>Whether the two objects intersected.</returns>
public bool Intersects(ref Ray ray, out Vector3 point)
{
@@ -224,11 +218,7 @@ namespace FlaxEngine
/// <param name="count">The count of points to process to compute the bounding sphere.</param>
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
/// <exception cref="System.ArgumentNullException">points</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// start
/// or
/// count
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">start or count</exception>
public static void FromPoints(Vector3[] points, int start, int count, out BoundingSphere result)
{
if (points == null)
@@ -244,30 +234,30 @@ namespace FlaxEngine
int upperEnd = start + count;
//Find the center of all points.
// Find the center of all points
Vector3 center = Vector3.Zero;
for (int i = start; i < upperEnd; ++i)
Vector3.Add(ref points[i], ref center, out center);
//This is the center of our sphere.
// This is the center of our sphere
center /= (float)count;
//Find the radius of the sphere
var radius = 0f;
for (int i = start; i < upperEnd; ++i)
{
//We are doing a relative distance comparison to find the maximum distance
//from the center of our sphere.
// We are doing a relative distance comparison to find the maximum distance from the center of our sphere
float distance;
Vector3.DistanceSquared(ref center, ref points[i], out float distance);
if (distance > radius)
radius = distance;
}
//Find the real distance from the DistanceSquared.
// Find the real distance from the DistanceSquared
radius = (float)Math.Sqrt(radius);
//Construct the sphere.
// Construct the sphere
result.Center = center;
result.Radius = radius;
}
@@ -281,7 +271,6 @@ namespace FlaxEngine
{
if (points == null)
throw new ArgumentNullException(nameof(points));
FromPoints(points, 0, points.Length, out result);
}
@@ -292,7 +281,7 @@ namespace FlaxEngine
/// <returns>The newly constructed bounding sphere.</returns>
public static BoundingSphere FromPoints(Vector3[] points)
{
FromPoints(points, out BoundingSphere result);
FromPoints(points, out var result);
return result;
}
@@ -320,7 +309,7 @@ namespace FlaxEngine
/// <returns>The newly constructed bounding sphere.</returns>
public static BoundingSphere FromBox(BoundingBox box)
{
FromBox(ref box, out BoundingSphere result);
FromBox(ref box, out var result);
return result;
}
@@ -383,19 +372,40 @@ namespace FlaxEngine
/// <returns>The newly constructed bounding sphere.</returns>
public static BoundingSphere Merge(BoundingSphere value1, BoundingSphere value2)
{
Merge(ref value1, ref value2, out BoundingSphere result);
Merge(ref value1, ref value2, out var result);
return result;
}
/// <summary>
/// Transforms the bounding sphere using the specified matrix.
/// </summary>
/// <param name="sphere">The sphere.</param>
/// <param name="matrix">The matrix.</param>
/// <remarks>The result transformed sphere.</remarks>
public static BoundingSphere Transform(BoundingSphere sphere, Matrix matrix)
{
Transform(ref sphere, ref matrix, out var result);
return result;
}
/// <summary>
/// Transforms the bounding sphere using the specified matrix.
/// </summary>
/// <param name="sphere">The sphere.</param>
/// <param name="matrix">The matrix.</param>
/// <param name="result">The result transformed sphere.</param>
public static void Transform(ref BoundingSphere sphere, ref Matrix matrix, out BoundingSphere result)
{
Vector3.Transform(ref sphere.Center, ref matrix, out result.Center);
result.Radius = sphere.Radius * matrix.ScaleVector.Absolute.MaxValue;
}
/// <summary>
/// Tests for equality between two objects.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(BoundingSphere left, BoundingSphere right)
{
@@ -407,10 +417,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(BoundingSphere left, BoundingSphere right)
{
@@ -420,9 +427,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture,
@@ -435,9 +440,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="format">The format.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format)
{
if (format == null)
@@ -453,9 +456,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(IFormatProvider formatProvider)
{
return string.Format(formatProvider,
@@ -469,9 +470,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="format">The format.</param>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null)
@@ -486,9 +485,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode()
{
unchecked
@@ -501,9 +498,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ref BoundingSphere value)
{
@@ -514,9 +509,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(BoundingSphere value)
{
@@ -527,14 +520,11 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object value)
{
if (!(value is BoundingSphere))
return false;
var strongValue = (BoundingSphere)value;
return Equals(ref strongValue);
}

View File

@@ -218,6 +218,14 @@ public:
/// <param name="value2">The point to merge.</param>
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
static void Merge(const BoundingSphere& value1, const Vector3& value2, BoundingSphere& result);
/// <summary>
/// Transforms the bounding sphere using the specified matrix.
/// </summary>
/// <param name="sphere">The sphere.</param>
/// <param name="matrix">The matrix.</param>
/// <param name="result">The result transformed sphere.</param>
static void Transform(const BoundingSphere& sphere, const Matrix& matrix, BoundingSphere& result);
};
template<>

View File

@@ -53,9 +53,7 @@ namespace FlaxEngine
/// Creates an <see cref="OrientedBoundingBox" /> from a BoundingBox.
/// </summary>
/// <param name="bb">The BoundingBox to create from.</param>
/// <remarks>
/// Initially, the OBB is axis-aligned box, but it can be rotated and transformed later.
/// </remarks>
/// <remarks>Initially, the OBB is axis-aligned box, but it can be rotated and transformed later.</remarks>
public OrientedBoundingBox(BoundingBox bb)
{
Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) / 2f;
@@ -79,9 +77,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="minimum">The minimum vertex of the bounding box.</param>
/// <param name="maximum">The maximum vertex of the bounding box.</param>
/// <remarks>
/// Initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.
/// </remarks>
/// <remarks>Initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.</remarks>
public OrientedBoundingBox(Vector3 minimum, Vector3 maximum)
{
Vector3 center = minimum + (maximum - minimum) / 2f;
@@ -93,10 +89,7 @@ namespace FlaxEngine
/// Creates an <see cref="OrientedBoundingBox" /> that fully contains the given points.
/// </summary>
/// <param name="points">The points that will be contained by the box.</param>
/// <remarks>
/// This method is not for computing the best tight-fitting OrientedBoundingBox.
/// And initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.
/// </remarks>
/// <remarks>This method is not for computing the best tight-fitting OrientedBoundingBox. And initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.</remarks>
public OrientedBoundingBox(Vector3[] points)
{
if ((points == null) || (points.Length == 0))
@@ -231,8 +224,7 @@ namespace FlaxEngine
/// <param name="mat">The transformation matrix.</param>
/// <remarks>
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection
/// accuracy.
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
/// </remarks>
public void Transform(ref Matrix mat)
{
@@ -245,8 +237,7 @@ namespace FlaxEngine
/// <param name="mat">The transformation matrix.</param>
/// <remarks>
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection
/// accuracy.
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
/// </remarks>
public void Transform(Matrix mat)
{
@@ -304,21 +295,15 @@ namespace FlaxEngine
/// <summary>
/// The size of the <see cref="OrientedBoundingBox" /> if no scaling is applied to the transformation matrix.
/// </summary>
/// <remarks>
/// The property will return the actual size even if the scaling is applied using Scale method,
/// but if the scaling is applied to transformation matrix, use GetSize Function instead.
/// <remarks>The property will return the actual size even if the scaling is applied using Scale method, but if the scaling is applied to transformation matrix, use GetSize Function instead.
/// </remarks>
public Vector3 Size => Extents * 2;
/// <summary>
/// Returns the size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the
/// transformation matrix.
/// Returns the size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the transformation matrix.
/// </summary>
/// <returns>The size of the consideration</returns>
/// <remarks>
/// This method is computationally expensive, so if no scale is applied to the transformation matrix
/// use <see cref="OrientedBoundingBox.Size" /> property instead.
/// </remarks>
/// <remarks>This method is computationally expensive, so if no scale is applied to the transformation matrix use <see cref="OrientedBoundingBox.Size" /> property instead.</remarks>
public Vector3 GetSize()
{
var xv = new Vector3(Extents.X * 2, 0, 0);
@@ -332,8 +317,7 @@ namespace FlaxEngine
}
/// <summary>
/// Returns the square size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to
/// the transformation matrix.
/// Returns the square size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the transformation matrix.
/// </summary>
/// <returns>The size of the consideration</returns>
public Vector3 GetSizeSquared()
@@ -432,16 +416,9 @@ namespace FlaxEngine
/// Determines whether a <see cref="OrientedBoundingBox" /> contains a <see cref="BoundingSphere" />.
/// </summary>
/// <param name="sphere">The sphere to test.</param>
/// <param name="ignoreScale">
/// Optimize the check operation by assuming that <see cref="OrientedBoundingBox" /> has no
/// scaling applied
/// </param>
/// <param name="ignoreScale">Optimize the check operation by assuming that <see cref="OrientedBoundingBox" /> has no scaling applied.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>
/// This method is not designed for <see cref="OrientedBoundingBox" /> which has a non-uniform scaling applied to its
/// transformation matrix.
/// But any type of scaling applied using Scale method will keep this method accurate.
/// </remarks>
/// <remarks>This method is not designed for <see cref="OrientedBoundingBox" /> which has a non-uniform scaling applied to its transformation matrix. But any type of scaling applied using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(BoundingSphere sphere, bool ignoreScale = false)
{
Matrix.Invert(ref Transformation, out Matrix invTrans);
@@ -489,11 +466,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="obb">The OrientedBoundingBox to test.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>
/// For accuracy, The transformation matrix for both <see cref="OrientedBoundingBox" /> must not have any scaling applied
/// to it.
/// Anyway, scaling using Scale method will keep this method accurate.
/// </remarks>
/// <remarks>For accuracy, The transformation matrix for both <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(ref OrientedBoundingBox obb)
{
ContainmentType cornersCheck = Contains(obb.GetCorners());
@@ -570,11 +543,7 @@ namespace FlaxEngine
/// <param name="L1">The first point in the line.</param>
/// <param name="L2">The second point in the line.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>
/// For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied
/// to it.
/// Anyway, scaling using Scale method will keep this method accurate.
/// </remarks>
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType ContainsLine(ref Vector3 L1, ref Vector3 L2)
{
ContainmentType cornersCheck = Contains(new[]
@@ -627,11 +596,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="box">The BoundingBox to test.</param>
/// <returns>The type of containment the two objects have.</returns>
/// <remarks>
/// For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied
/// to it.
/// Anyway, scaling using Scale method will keep this method accurate.
/// </remarks>
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
public ContainmentType Contains(ref BoundingBox box)
{
ContainmentType cornersCheck = Contains(box.GetCorners());
@@ -704,10 +669,7 @@ namespace FlaxEngine
/// Determines whether there is an intersection between a <see cref="Ray" /> and a <see cref="OrientedBoundingBox" />.
/// </summary>
/// <param name="ray">The ray to test.</param>
/// <param name="point">
/// When the method completes, contains the point of intersection,
/// or <see cref="Vector3.Zero" /> if there was no intersection.
/// </param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
/// <returns>Whether the two objects intersected.</returns>
public bool Intersects(ref Ray ray, out Vector3 point)
{
@@ -718,11 +680,11 @@ namespace FlaxEngine
Vector3.TransformNormal(ref ray.Direction, ref invTrans, out bRay.Direction);
Vector3.TransformCoordinate(ref ray.Position, ref invTrans, out bRay.Position);
//Perform a regular ray to BoundingBox check
// Perform a regular ray to BoundingBox check
var bb = new BoundingBox(-Extents, Extents);
bool intersects = CollisionsHelper.RayIntersectsBox(ref bRay, ref bb, out point);
//Put the result intersection back to world
// Put the result intersection back to world
if (intersects)
Vector3.TransformCoordinate(ref point, ref Transformation, out point);
@@ -733,10 +695,7 @@ namespace FlaxEngine
/// Determines whether there is an intersection between a <see cref="Ray" /> and a <see cref="OrientedBoundingBox" />.
/// </summary>
/// <param name="ray">The ray to test.</param>
/// <param name="distance">
/// When the method completes, contains the distance of intersection from the ray start,
/// or 0 if there was no intersection.
/// </param>
/// <param name="distance">When the method completes, contains the distance of intersection from the ray start, or 0 if there was no intersection.</param>
/// <returns>Whether the two objects intersected.</returns>
public bool Intersects(ref Ray ray, out float distance)
{
@@ -788,16 +747,12 @@ namespace FlaxEngine
}
/// <summary>
/// Calculates the matrix required to transfer any point from one <see cref="OrientedBoundingBox" /> local coordinates to
/// another.
/// Calculates the matrix required to transfer any point from one <see cref="OrientedBoundingBox" /> local coordinates to another.
/// </summary>
/// <param name="A">The source OrientedBoundingBox.</param>
/// <param name="B">The target OrientedBoundingBox.</param>
/// <param name="NoMatrixScaleApplied">
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
/// matrix of the OrientedBoundingBox.
/// </param>
/// <returns></returns>
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
/// <returns>The matrix.</returns>
public static Matrix GetBoxToBoxMatrix(ref OrientedBoundingBox A, ref OrientedBoundingBox B, bool NoMatrixScaleApplied = false)
{
Matrix AtoB_Matrix;
@@ -828,19 +783,12 @@ namespace FlaxEngine
}
/// <summary>
/// Merge an OrientedBoundingBox B into another OrientedBoundingBox A, by expanding A to contain B and keeping A
/// orientation.
/// Merge an OrientedBoundingBox B into another OrientedBoundingBox A, by expanding A to contain B and keeping A orientation.
/// </summary>
/// <param name="A">The <see cref="OrientedBoundingBox" /> to merge into it.</param>
/// <param name="B">The <see cref="OrientedBoundingBox" /> to be merged</param>
/// <param name="NoMatrixScaleApplied">
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
/// matrix of the OrientedBoundingBox.
/// </param>
/// <remarks>
/// Unlike merging axis aligned boxes, The operation is not interchangeable, because it keeps A orientation and merge B
/// into it.
/// </remarks>
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
/// <remarks>Unlike merging axis aligned boxes, The operation is not interchangeable, because it keeps A orientation and merge B into it.</remarks>
public static void Merge(ref OrientedBoundingBox A, ref OrientedBoundingBox B, bool NoMatrixScaleApplied = false)
{
Matrix AtoB_Matrix = GetBoxToBoxMatrix(ref A, ref B, NoMatrixScaleApplied);
@@ -869,10 +817,7 @@ namespace FlaxEngine
/// Merge this OrientedBoundingBox into another OrientedBoundingBox, keeping the other OrientedBoundingBox orientation.
/// </summary>
/// <param name="OBB">The other <see cref="OrientedBoundingBox" /> to merge into.</param>
/// <param name="NoMatrixScaleApplied">
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
/// matrix of the OrientedBoundingBox.
/// </param>
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
public void MergeInto(ref OrientedBoundingBox OBB, bool NoMatrixScaleApplied = false)
{
Merge(ref OBB, ref this, NoMatrixScaleApplied);
@@ -882,10 +827,7 @@ namespace FlaxEngine
/// Merge another OrientedBoundingBox into this OrientedBoundingBox.
/// </summary>
/// <param name="OBB">The other <see cref="OrientedBoundingBox" /> to merge into this OrientedBoundingBox.</param>
/// <param name="NoMatrixScaleApplied">
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
/// matrix of the OrientedBoundingBox.
/// </param>
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
public void Add(ref OrientedBoundingBox OBB, bool NoMatrixScaleApplied = false)
{
Merge(ref this, ref OBB, NoMatrixScaleApplied);
@@ -895,9 +837,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ref OrientedBoundingBox value)
{
@@ -908,9 +848,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(OrientedBoundingBox value)
{
@@ -921,9 +859,7 @@ namespace FlaxEngine
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
/// </summary>
/// <param name="value">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object value)
{
if (!(value is OrientedBoundingBox))
@@ -952,10 +888,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(OrientedBoundingBox left, OrientedBoundingBox right)
{
@@ -967,10 +900,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise,
/// <c>false</c>.
/// </returns>
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(OrientedBoundingBox left, OrientedBoundingBox right)
{
@@ -980,9 +910,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode()
{
return Extents.GetHashCode() + Transformation.GetHashCode();
@@ -991,9 +919,7 @@ namespace FlaxEngine
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "Center: {0}, Extents: {1}", Center, Extents);
@@ -1003,9 +929,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="format">The format.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format)
{
if (format == null)
@@ -1019,9 +943,7 @@ namespace FlaxEngine
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(IFormatProvider formatProvider)
{
return string.Format(formatProvider, "Center: {0}, Extents: {1}", Center.ToString(), Extents.ToString());
@@ -1032,9 +954,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="format">The format.</param>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null)

View File

@@ -250,6 +250,16 @@ namespace FlaxEngine
/// </summary>
public float ValuesSum => X + Y + Z;
/// <summary>
/// Gets a vector with values being absolute values of that vector.
/// </summary>
public Vector3 Absolute => new Vector3(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z));
/// <summary>
/// Gets a vector with values being opposite to values of that vector.
/// </summary>
public Vector3 Negative => new Vector3(-X, -Y, -Z);
/// <summary>
/// Gets or sets the component at the specified index.
/// </summary>
@@ -294,8 +304,7 @@ namespace FlaxEngine
/// </summary>
/// <returns>The length of the vector.</returns>
/// <remarks>
/// <see cref="Vector3.LengthSquared" /> may be preferred when only the relative length is needed
/// and speed is of the essence.
/// <see cref="Vector3.LengthSquared" /> may be preferred when only the relative length is needed and speed is of the essence.
/// </remarks>
public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z);
@@ -304,8 +313,7 @@ namespace FlaxEngine
/// </summary>
/// <returns>The squared length of the vector.</returns>
/// <remarks>
/// This method may be preferred to <see cref="Vector3.Length" /> when only a relative length is needed
/// and speed is of the essence.
/// This method may be preferred to <see cref="Vector3.Length" /> when only a relative length is needed and speed is of the essence.
/// </remarks>
public float LengthSquared => X * X + Y * Y + Z * Z;

View File

@@ -11,21 +11,21 @@ namespace Utilities
template<typename T>
FORCE_INLINE T RoundTo1DecimalPlace(T value)
{
return round(value * 10) / 10;
return (T)round((double)value * 10) / (T)10;
}
// Round floating point value up to 2 decimal places
template<typename T>
FORCE_INLINE T RoundTo2DecimalPlaces(T value)
{
return round(value * 100) / 100;
return (T)round((double)value * 100.0) / (T)100;
}
// Round floating point value up to 3 decimal places
template<typename T>
FORCE_INLINE T RoundTo3DecimalPlaces(T value)
{
return round(value * 1000) / 1000;
return (T)round((double)value * 1000.0) / (T)1000;
}
// Converts size of the file (in bytes) to the best fitting string

View File

@@ -20,6 +20,8 @@
#include "Engine/Animations/AnimationUtils.h"
#include "Engine/Profiler/Profiler.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Render2D/Render2D.h"
#include "Engine/Render2D/FontAsset.h"
// Debug draw service configuration
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
@@ -54,6 +56,25 @@ struct DebugTriangle
float TimeLeft;
};
struct DebugText2D
{
Array<Char, InlinedAllocation<64>> Text;
Vector2 Position;
int32 Size;
Color Color;
float TimeLeft;
};
struct DebugText3D
{
Array<Char, InlinedAllocation<64>> Text;
Transform Transform;
bool FaceCamera;
int32 Size;
Color Color;
float TimeLeft;
};
PACK_STRUCT(struct Vertex {
Vector3 Position;
Color32 Color;
@@ -133,10 +154,14 @@ struct DebugDrawData
Array<DebugTriangle> OneFrameTriangles;
Array<DebugTriangle> DefaultWireTriangles;
Array<DebugTriangle> OneFrameWireTriangles;
Array<DebugText2D> DefaultText2D;
Array<DebugText2D> OneFrameText2D;
Array<DebugText3D> DefaultText3D;
Array<DebugText3D> OneFrameText3D;
inline int32 Count() const
{
return LinesCount() + TrianglesCount();
return LinesCount() + TrianglesCount() + TextCount();
}
inline int32 LinesCount() const
@@ -149,6 +174,11 @@ struct DebugDrawData
return DefaultTriangles.Count() + OneFrameTriangles.Count() + DefaultWireTriangles.Count() + OneFrameWireTriangles.Count();
}
inline int32 TextCount() const
{
return DefaultText2D.Count() + OneFrameText2D.Count() + DefaultText3D.Count() + OneFrameText3D.Count();
}
inline void Add(const DebugLine& l)
{
if (l.TimeLeft > 0)
@@ -178,10 +208,14 @@ struct DebugDrawData
UpdateList(deltaTime, DefaultLines);
UpdateList(deltaTime, DefaultTriangles);
UpdateList(deltaTime, DefaultWireTriangles);
UpdateList(deltaTime, DefaultText2D);
UpdateList(deltaTime, DefaultText3D);
OneFrameLines.Clear();
OneFrameTriangles.Clear();
OneFrameWireTriangles.Clear();
OneFrameText2D.Clear();
OneFrameText3D.Clear();
}
inline void Clear()
@@ -192,6 +226,10 @@ struct DebugDrawData
OneFrameTriangles.Clear();
DefaultWireTriangles.Clear();
OneFrameWireTriangles.Clear();
DefaultText2D.Clear();
OneFrameText2D.Clear();
DefaultText3D.Clear();
OneFrameText3D.Clear();
}
inline void Release()
@@ -202,23 +240,38 @@ struct DebugDrawData
OneFrameTriangles.Resize(0);
DefaultWireTriangles.Resize(0);
OneFrameWireTriangles.Resize(0);
DefaultText2D.Resize(0);
OneFrameText2D.Resize(0);
DefaultText3D.Resize(0);
OneFrameText3D.Resize(0);
}
};
DebugDrawData DebugDrawDefault;
DebugDrawData DebugDrawDepthTest;
AssetReference<Shader> DebugDrawShader;
PsData DebugDrawPsLinesDefault;
PsData DebugDrawPsLinesDepthTest;
PsData DebugDrawPsWireTrianglesDefault;
PsData DebugDrawPsWireTrianglesDepthTest;
PsData DebugDrawPsTrianglesDefault;
PsData DebugDrawPsTrianglesDepthTest;
DynamicVertexBuffer* DebugDrawVB = nullptr;
Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES];
Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES];
Vector3 CylinderCache[DEBUG_DRAW_CYLINDER_VERTICES];
Array<Vector3> SphereTriangleCache;
struct DebugDrawContext
{
DebugDrawData DebugDrawDefault;
DebugDrawData DebugDrawDepthTest;
};
namespace
{
DebugDrawContext GlobalContext;
DebugDrawContext* Context;
AssetReference<Shader> DebugDrawShader;
AssetReference<FontAsset> DebugDrawFont;
PsData DebugDrawPsLinesDefault;
PsData DebugDrawPsLinesDepthTest;
PsData DebugDrawPsWireTrianglesDefault;
PsData DebugDrawPsWireTrianglesDepthTest;
PsData DebugDrawPsTrianglesDefault;
PsData DebugDrawPsTrianglesDepthTest;
DynamicVertexBuffer* DebugDrawVB = nullptr;
Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES];
Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES];
Vector3 CylinderCache[DEBUG_DRAW_CYLINDER_VERTICES];
Array<Vector3> SphereTriangleCache;
};
extern int32 BoxTrianglesIndicesCache[];
struct DebugDrawCall
@@ -289,6 +342,21 @@ DebugDrawCall WriteLists(int32& vertexCounter, const Array<T>& listA, const Arra
return drawCall;
}
inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext, const Vector3& viewUp, const Matrix& f, const Matrix& vp, const Viewport& viewport, GPUContext* context, GPUTextureView* target, GPUTextureView* depthBuffer)
{
Matrix w, fw, m;
if (t.FaceCamera)
Matrix::CreateWorld(t.Transform.Translation, renderContext.View.Direction, viewUp, w);
else
t.Transform.GetWorld(w);
Matrix::Multiply(f, w, fw);
Matrix::Multiply(fw, vp, m);
Render2D::Begin(context, target, depthBuffer, viewport, m);
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, Vector2::Zero);
Render2D::End();
}
class DebugDrawService : public EngineService
{
public:
@@ -307,16 +375,18 @@ DebugDrawService DebugDrawServiceInstance;
bool DebugDrawService::Init()
{
Context = &GlobalContext;
// Init wireframe sphere cache
int32 index = 0;
float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION;
for (float a = 0.0f; a < TWO_PI; a += step)
{
// Calculate sines and cosines
float sinA = sin(a);
float cosA = cos(a);
float sinB = sin(a + step);
float cosB = cos(a + step);
float sinA = Math::Sin(a);
float cosA = Math::Cos(a);
float sinB = Math::Sin(a + step);
float cosB = Math::Cos(a + step);
// XY loop
SphereCache[index++] = Vector3(cosA, sinA, 0.0f);
@@ -337,10 +407,10 @@ bool DebugDrawService::Init()
for (float a = 0.0f; a < TWO_PI; a += step)
{
// Calculate sines and cosines
float sinA = sin(a);
float cosA = cos(a);
float sinB = sin(a + step);
float cosB = cos(a + step);
float sinA = Math::Sin(a);
float cosA = Math::Cos(a);
float sinB = Math::Sin(a + step);
float cosB = Math::Cos(a + step);
CircleCache[index++] = Vector3(cosA, sinA, 0.0f);
CircleCache[index++] = Vector3(cosB, sinB, 0.0f);
@@ -436,8 +506,8 @@ void DebugDrawService::Update()
// Special case for Null renderer
if (GPUDevice::Instance->GetRendererType() == RendererType::Null)
{
DebugDrawDefault.Clear();
DebugDrawDepthTest.Clear();
GlobalContext.DebugDrawDefault.Clear();
GlobalContext.DebugDrawDepthTest.Clear();
return;
}
@@ -445,8 +515,8 @@ void DebugDrawService::Update()
// Update lists
const float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();
DebugDrawDefault.Update(deltaTime);
DebugDrawDepthTest.Update(deltaTime);
GlobalContext.DebugDrawDefault.Update(deltaTime);
GlobalContext.DebugDrawDepthTest.Update(deltaTime);
// Check if need to setup a resources
if (DebugDrawShader == nullptr)
@@ -501,8 +571,8 @@ void DebugDrawService::Update()
void DebugDrawService::Dispose()
{
// Clear lists
DebugDrawDefault.Release();
DebugDrawDepthTest.Release();
GlobalContext.DebugDrawDefault.Release();
GlobalContext.DebugDrawDepthTest.Release();
// Release resources
SphereTriangleCache.Resize(0);
@@ -516,20 +586,48 @@ void DebugDrawService::Dispose()
DebugDrawShader = nullptr;
}
#if USE_EDITOR
void* DebugDraw::AllocateContext()
{
auto context = (DebugDrawContext*)Allocator::Allocate(sizeof(DebugDrawContext));
Memory::ConstructItem(context);
return context;
}
void DebugDraw::FreeContext(void* context)
{
Memory::DestructItem((DebugDrawContext*)context);
Allocator::Free(context);
}
void DebugDraw::UpdateContext(void* context, float deltaTime)
{
((DebugDrawContext*)context)->DebugDrawDefault.Update(deltaTime);
((DebugDrawContext*)context)->DebugDrawDepthTest.Update(deltaTime);
}
void DebugDraw::SetContext(void* context)
{
Context = context ? (DebugDrawContext*)context : &GlobalContext;
}
#endif
void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest)
{
PROFILE_GPU_CPU("Debug Draw");
// Ensure to have shader loaded and any lines to render
const int32 debugDrawDepthTestCount = DebugDrawDepthTest.Count();
const int32 debugDrawDefaultCount = DebugDrawDefault.Count();
const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count();
const int32 debugDrawDefaultCount = Context->DebugDrawDefault.Count();
if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0)
return;
if (renderContext.Buffers == nullptr || !DebugDrawVB)
return;
auto context = GPUDevice::Instance->GetMainContext();
// Fallback to task backbuffer
// Fallback to task buffers
if (target == nullptr && renderContext.Task)
target = renderContext.Task->GetOutputView();
@@ -539,12 +637,12 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
#endif
DebugDrawVB->Clear();
int32 vertexCounter = 0;
const DebugDrawCall depthTestLines = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultLines, DebugDrawDepthTest.OneFrameLines);
const DebugDrawCall defaultLines = WriteLists(vertexCounter, DebugDrawDefault.DefaultLines, DebugDrawDefault.OneFrameLines);
const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultTriangles, DebugDrawDepthTest.OneFrameTriangles);
const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultTriangles, DebugDrawDefault.OneFrameTriangles);
const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultWireTriangles, DebugDrawDepthTest.OneFrameWireTriangles);
const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultWireTriangles, DebugDrawDefault.OneFrameWireTriangles);
const DebugDrawCall depthTestLines = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultLines, Context->DebugDrawDepthTest.OneFrameLines);
const DebugDrawCall defaultLines = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultLines, Context->DebugDrawDefault.OneFrameLines);
const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultTriangles, Context->DebugDrawDepthTest.OneFrameTriangles);
const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultTriangles, Context->DebugDrawDefault.OneFrameTriangles);
const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultWireTriangles, Context->DebugDrawDepthTest.OneFrameWireTriangles);
const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles);
DebugDrawVB->Flush(context);
#if COMPILE_WITH_PROFILER
ProfilerCPU::EndEvent(updateBufferProfileKey);
@@ -561,8 +659,6 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->BindCB(0, cb);
auto vb = DebugDrawVB->GetBuffer();
#define DRAW(drawCall) if (drawCall.VertexCount)
// Draw with depth test
if (depthTestLines.VertexCount + depthTestTriangles.VertexCount + depthTestWireTriangles.VertexCount > 0)
{
@@ -580,7 +676,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->BindVB(ToSpan(&vb, 1));
context->Draw(depthTestLines.StartVertex, depthTestLines.VertexCount);
}
// Wire Triangles
if (depthTestWireTriangles.VertexCount)
{
@@ -633,7 +729,50 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
}
}
#undef DRAW
// Text
if (Context->DebugDrawDefault.TextCount() + Context->DebugDrawDepthTest.TextCount())
{
PROFILE_GPU_CPU_NAMED("Text");
auto features = Render2D::Features;
Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping);
if (!DebugDrawFont)
DebugDrawFont = Content::LoadAsyncInternal<FontAsset>(TEXT("Editor/Fonts/Roboto-Regular"));
if (DebugDrawFont && DebugDrawFont->IsLoaded())
{
Viewport viewport = renderContext.Task->GetViewport();
if (Context->DebugDrawDefault.DefaultText2D.Count() + Context->DebugDrawDefault.OneFrameText2D.Count())
{
Render2D::Begin(context, target, nullptr, viewport);
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
{
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
}
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
{
const StringView text(t.Text.Get(), t.Text.Count() - 1);
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
}
Render2D::End();
}
if (Context->DebugDrawDefault.DefaultText3D.Count() + Context->DebugDrawDefault.OneFrameText3D.Count())
{
Matrix f;
Matrix::RotationZ(PI, f);
Vector3 viewUp;
Vector3::Transform(Vector3::Up, Quaternion::LookRotation(renderContext.View.Direction, Vector3::Up), viewUp);
for (auto& t : Context->DebugDrawDefault.DefaultText3D)
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
for (auto& t : Context->DebugDrawDefault.OneFrameText3D)
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
}
}
Render2D::Features = features;
}
}
namespace
@@ -649,9 +788,10 @@ namespace
}
}
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount)
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes)
{
PROFILE_CPU();
if (selectedActors)
{
for (int32 i = 0; i < selectedActorsCount; i++)
@@ -662,9 +802,19 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount)
}
}
Function<bool(Actor*)> function;
function.Bind(&DrawActorsTreeWalk);
SceneQuery::TreeExecute(function);
if (drawScenes)
{
Function<bool(Actor*)> function = [](Actor* actor)-> bool
{
if (actor->IsActiveInHierarchy())
{
actor->OnDebugDraw();
return true;
}
return false;
};
SceneQuery::TreeExecute(function);
}
}
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
@@ -674,9 +824,9 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color&
// Add line
if (depthTest)
DebugDrawDepthTest.Add(l);
Context->DebugDrawDepthTest.Add(l);
else
DebugDrawDefault.Add(l);
Context->DebugDrawDefault.Add(l);
}
void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
@@ -694,10 +844,10 @@ void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, c
const Vector3* p = lines.Get();
Array<DebugLine>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines;
if (depthTest)
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines;
else
list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines;
list->EnsureCapacity(list->Count() + lines.Length());
for (int32 i = 0; i < lines.Length(); i += 2)
@@ -713,9 +863,9 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3&
// Create draw call entry
Array<DebugLine>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines;
else
list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines;
DebugLine l = { p1, Vector3::Zero, Color32(color), duration };
// Find amount of segments to use
@@ -737,7 +887,7 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3&
}
}
#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; list.Add(l)
#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; Context->list.Add(l)
#define DRAW_WIRE_BOX(list) \
DRAW_WIRE_BOX_LINE(0, 1, list); \
DRAW_WIRE_BOX_LINE(0, 3, list); \
@@ -824,7 +974,7 @@ void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color,
{
l.Start = sphere.Center + SphereCache[i++] * sphere.Radius;
l.End = sphere.Center + SphereCache[i++] * sphere.Radius;
DebugDrawDepthTest.Add(l);
Context->DebugDrawDepthTest.Add(l);
}
}
else
@@ -833,7 +983,7 @@ void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color,
{
l.Start = sphere.Center + SphereCache[i++] * sphere.Radius;
l.End = sphere.Center + SphereCache[i++] * sphere.Radius;
DebugDrawDefault.Add(l);
Context->DebugDrawDefault.Add(l);
}
}
}
@@ -846,9 +996,9 @@ void DebugDraw::DrawSphere(const BoundingSphere& sphere, const Color& color, flo
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
list->EnsureCapacity(list->Count() + SphereTriangleCache.Count());
for (int32 i = 0; i < SphereTriangleCache.Count();)
@@ -905,11 +1055,11 @@ void DebugDraw::DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3
if (depthTest)
{
DebugDrawDepthTest.Add(t);
Context->DebugDrawDepthTest.Add(t);
}
else
{
DebugDrawDefault.Add(t);
Context->DebugDrawDefault.Add(t);
}
}
@@ -923,9 +1073,9 @@ void DebugDraw::DrawTriangles(const Span<Vector3>& vertices, const Color& color,
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
list->EnsureCapacity(list->Count() + vertices.Length() / 3);
for (int32 i = 0; i < vertices.Length();)
@@ -952,9 +1102,9 @@ void DebugDraw::DrawTriangles(const Span<Vector3>& vertices, const Span<int32>&
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
list->EnsureCapacity(list->Count() + indices.Length() / 3);
for (int32 i = 0; i < indices.Length();)
@@ -981,9 +1131,9 @@ void DebugDraw::DrawWireTriangles(const Span<Vector3>& vertices, const Color& co
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
list->EnsureCapacity(list->Count() + vertices.Length() / 3);
for (int32 i = 0; i < vertices.Length();)
@@ -1010,9 +1160,9 @@ void DebugDraw::DrawWireTriangles(const Span<Vector3>& vertices, const Span<int3
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
list->EnsureCapacity(list->Count() + indices.Length() / 3);
for (int32 i = 0; i < indices.Length();)
@@ -1055,10 +1205,10 @@ void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientat
{
// Calculate sines and cosines
// TODO: optimize this stuff
float sinA = sin(a) * radius;
float cosA = cos(a) * radius;
float sinB = sin(a + step) * radius;
float cosB = cos(a + step) * radius;
float sinA = Math::Sin(a) * radius;
float cosA = Math::Cos(a) * radius;
float sinB = Math::Sin(a + step) * radius;
float cosB = Math::Cos(a + step) * radius;
// First XY loop
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosB, sinB, -halfLength);
@@ -1104,8 +1254,8 @@ void DebugDraw::DrawWireCylinder(const Vector3& position, const Quaternion& orie
{
// Cache data
float theta = i * angleBetweenFacets;
float x = cos(theta) * radius;
float z = sin(theta) * radius;
float x = Math::Cos(theta) * radius;
float z = Math::Sin(theta) * radius;
// Top cap
CylinderCache[index++] = Vector3(x, verticalOffset, z);
@@ -1198,9 +1348,9 @@ void DebugDraw::DrawBox(const BoundingBox& box, const Color& color, float durati
t.TimeLeft = duration;
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
list->EnsureCapacity(list->Count() + 36);
for (int i0 = 0; i0 < 36;)
{
@@ -1224,9 +1374,9 @@ void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, floa
t.TimeLeft = duration;
Array<DebugTriangle>* list;
if (depthTest)
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
else
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
list->EnsureCapacity(list->Count() + 36);
for (int i0 = 0; i0 < 36;)
{
@@ -1238,4 +1388,51 @@ void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, floa
}
}
void DebugDraw::DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText2D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText2D : &Context->DebugDrawDefault.OneFrameText2D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Position = position;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
void DebugDraw::DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Transform = position;
t.FaceCamera = true;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
void DebugDraw::DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size, float duration)
{
if (text.Length() == 0 || size < 4)
return;
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
auto& t = list->AddOne();
t.Text.Resize(text.Length() + 1);
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
t.Text[text.Length()] = 0;
t.Transform = transform;
t.FaceCamera = false;
t.Size = size;
t.Color = color;
t.TimeLeft = duration;
}
#endif

View File

@@ -15,6 +15,7 @@ class GPUContext;
class RenderTask;
class SceneRenderTask;
class Actor;
struct Transform;
/// <summary>
/// The debug shapes rendering service. Not available in final game. For use only in the editor.
@@ -23,6 +24,35 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#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).
/// </summary>
/// <returns>The context object. Release it wil FreeContext. Returns null if failed.</returns>
API_FUNCTION() static void* AllocateContext();
/// <summary>
/// Frees the context for Debug Drawing.
/// </summary>
/// <param name="context">The context.</param>
API_FUNCTION() static void FreeContext(void* context);
/// <summary>
/// Updates the context for Debug Drawing.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="deltaTime">The update delta time (in seconds).</param>
API_FUNCTION() static void UpdateContext(void* context, float deltaTime);
/// <summary>
/// Sets the context for Debug Drawing to a custom or null to use global default.
/// </summary>
/// <param name="context">The context or null.</param>
API_FUNCTION() static void SetContext(void* context);
#endif
/// <summary>
/// Draws the collected debug shapes to the output.
/// </summary>
@@ -37,7 +67,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
/// </summary>
/// <param name="selectedActors">The list of actors to draw.</param>
/// <param name="selectedActorsCount">The size of the list of actors.</param>
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount);
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
/// <summary>
/// Draws the line.
@@ -277,6 +308,36 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
/// <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 DrawBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the text on a screen (2D).
/// </summary>
/// <param name="text">The text.</param>
/// <param name="position">The position of the text on the screen (in screen-space coordinates).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size = 20, float duration = 0.0f);
/// <summary>
/// Draws the text (3D) that automatically faces the camera.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="position">The position of the text (world-space).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size = 32, float duration = 0.0f);
/// <summary>
/// Draws the text (3D).
/// </summary>
/// <param name="text">The text.</param>
/// <param name="transform">The transformation of the text (world-space).</param>
/// <param name="color">The color.</param>
/// <param name="size">The font size.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f);
};
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest)
@@ -296,7 +357,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration)
#else
@@ -318,5 +380,6 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest)
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest)
#define DEBUG_DRAW_TEXT(text, position, color, size, duration)
#endif

View File

@@ -36,6 +36,7 @@
#include "Engine/Profiler/Profiler.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#include "Editor/Managed/ManagedEditor.h"
#else
#include "Engine/Utilities/Encryption.h"

View File

@@ -7,7 +7,7 @@ namespace FlaxEngine
/// <summary>
/// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene.
/// </summary>
public struct SceneReference
public struct SceneReference : IComparable, IComparable<Guid>, IComparable<SceneReference>
{
/// <summary>
/// The identifier of the scene asset (and the scene object).
@@ -22,5 +22,93 @@ namespace FlaxEngine
{
ID = id;
}
/// <summary>
/// Compares two values and returns true if are equal or false if unequal.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>True if values are equal, otherwise false.</returns>
public static bool operator ==(SceneReference left, SceneReference right)
{
return left.ID == right.ID;
}
/// <summary>
/// Compares two values and returns false if are equal or true if unequal.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>True if values are not equal, otherwise false.</returns>
public static bool operator !=(SceneReference left, SceneReference right)
{
return left.ID != right.ID;
}
/// <summary>
/// Compares two values and returns true if are equal or false if unequal.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>True if values are equal, otherwise false.</returns>
public static bool operator ==(SceneReference left, Guid right)
{
return left.ID == right;
}
/// <summary>
/// Compares two values and returns false if are equal or true if unequal.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>True if values are not equal, otherwise false.</returns>
public static bool operator !=(SceneReference left, Guid right)
{
return left.ID != right;
}
/// <inheritdoc />
public int CompareTo(object obj)
{
if (obj is Guid id)
return CompareTo(id);
if (obj is SceneReference other)
return CompareTo(other);
return 0;
}
/// <inheritdoc />
public int CompareTo(Guid other)
{
return ID.CompareTo(other);
}
/// <inheritdoc />
public int CompareTo(SceneReference other)
{
return ID.CompareTo(other.ID);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj is Guid id)
return ID == id;
if (obj is SceneReference other)
return ID == other.ID;
return false;
}
/// <inheritdoc />
public override int GetHashCode()
{
return ID.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return ID.ToString();
}
}
}

View File

@@ -372,7 +372,7 @@ void Mesh::Render(GPUContext* context) const
context->DrawIndexedInstanced(_triangles * 3, 1, 0, 0, 0);
}
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals) const
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom) const
{
if (!material || !material->IsSurface())
return;
@@ -399,8 +399,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.Surface.Skinning = nullptr;
drawCall.Surface.LODDitherFactor = 0.0f;
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = 0.0f;
renderContext.List->AddDrawCall(DrawPass::Default, flags, drawCall, receiveDecals);
drawCall.PerInstanceRandom = perInstanceRandom;
renderContext.List->AddDrawCall(drawModes, flags, drawCall, receiveDecals);
}
void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const

View File

@@ -459,7 +459,9 @@ public:
/// <param name="world">The world transformation of the model.</param>
/// <param name="flags">The object static flags.</param>
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true) const;
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const;
/// <summary>
/// Draws the mesh.

View File

@@ -122,11 +122,13 @@ public:
/// <param name="world">The world transformation of the model.</param>
/// <param name="flags">The object static flags.</param>
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true) const
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Draw(renderContext, material, world, flags, receiveDecals);
Meshes[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom);
}
}

View File

@@ -222,6 +222,11 @@ SceneRenderTask::~SceneRenderTask()
Buffers->DeleteObjectNow();
}
void SceneRenderTask::CameraCut()
{
IsCameraCut = true;
}
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
{
if ((ActorsSource & ActorsSources::Scenes) != 0)

View File

@@ -236,10 +236,7 @@ public:
/// <summary>
/// Marks the next rendered frame as camera cut. Used to clear the temporal effects history and prevent visual artifacts blended from the previous frames.
/// </summary>
API_FUNCTION() void CameraCut()
{
IsCameraCut = true;
}
API_FUNCTION() void CameraCut();
/// <summary>
/// The output texture (can be null if using rendering to window swap chain). Can be sued to redirect the default scene rendering output to a texture.

View File

@@ -115,6 +115,7 @@ void RenderView::CopyFrom(Camera* camera)
Matrix::Invert(Projection, IP);
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
RenderLayersMask = camera->RenderLayersMask;
}
void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
@@ -131,6 +132,7 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
Matrix::Invert(Projection, IP);
Frustum.GetInvMatrix(IVP);
CullingFrustum = Frustum;
RenderLayersMask = camera->RenderLayersMask;
}
DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const

View File

@@ -89,6 +89,7 @@ namespace FlaxEngine
Projection = camera.Projection;
NonJitteredProjection = Projection;
TemporalAAJitter = Vector4.Zero;
RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
}
@@ -107,6 +108,7 @@ namespace FlaxEngine
camera.GetMatrices(out View, out Projection, ref customViewport);
NonJitteredProjection = Projection;
TemporalAAJitter = Vector4.Zero;
RenderLayersMask = camera.RenderLayersMask;
UpdateCachedData();
}

View File

@@ -5,6 +5,7 @@
#include "Engine/Core/Math/BoundingFrustum.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Config/LayersMask.h"
#include "Engine/Level/Types.h"
#include "Enums.h"
@@ -141,6 +142,11 @@ public:
/// </summary>
API_FIELD() int32 TaaFrameIndex = 0;
/// <summary>
/// The rendering mask for layers. Used to exclude objects from rendering.
/// </summary>
API_FIELD() LayersMask RenderLayersMask;
public:
/// <summary>

View File

@@ -1825,7 +1825,9 @@ bool GPUDeviceVulkan::Init()
#endif
#undef INIT_FUNC
VmaAllocatorCreateInfo allocatorInfo = {};
allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
allocatorInfo.physicalDevice = gpu;
allocatorInfo.instance = Instance;
allocatorInfo.device = Device;
allocatorInfo.pVulkanFunctions = &vulkanFunctions;
VALIDATE_VULKAN_RESULT(vmaCreateAllocator(&allocatorInfo, &Allocator));

View File

@@ -198,7 +198,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
// Check if value won't change
if (_parent == value)
return;
if (!IsInMainThread())
if (IsDuringPlay() && !IsInMainThread())
{
LOG(Error, "Editing scene hierarchy is only allowed on a main thread.");
return;
@@ -406,7 +406,7 @@ const String& Actor::GetTag() const
void Actor::SetLayer(int32 layerIndex)
{
layerIndex = Math::Min<int32>(layerIndex, 31);
layerIndex = Math::Clamp(layerIndex, 0, 31);
if (layerIndex == _layer)
return;

View File

@@ -330,6 +330,7 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(Near, _near);
SERIALIZE_MEMBER(Far, _far);
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
SERIALIZE(RenderLayersMask);
}
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -343,6 +344,7 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
DESERIALIZE_MEMBER(Near, _near);
DESERIALIZE_MEMBER(Far, _far);
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
DESERIALIZE(RenderLayersMask);
}
void Camera::OnEnable()

View File

@@ -3,13 +3,16 @@
#pragma once
#include "../Actor.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/BoundingFrustum.h"
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Config/LayersMask.h"
#if USE_EDITOR
#include "Engine/Content/AssetReference.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
#include "Engine/Content/Assets/Model.h"
#endif
/// <summary>
/// Describes the camera projection and view. Provides information about how to render scene (viewport location and direction, etc.).
@@ -21,6 +24,7 @@ DECLARE_SCENE_OBJECT(Camera);
// List with all created cameras actors on the scene
static Array<Camera*> Cameras;
// The current cut-scene camera. Set by the Scene Animation Player to the current shot camera.
static Camera* CutSceneCamera;
// The overriden main camera.
@@ -161,6 +165,12 @@ public:
/// </summary>
API_PROPERTY() void SetOrthographicScale(float value);
/// <summary>
/// The layers mask used for rendering using this camera. Can be used to include or exclude specific actor layers from the drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
LayersMask RenderLayersMask;
public:
/// <summary>

View File

@@ -198,8 +198,8 @@ void SpotLight::OnDebugDrawSelected()
Vector3 up = _transform.GetUp();
Vector3 forward = GetDirection();
float radius = GetScaledRadius();
float discRadius = radius * tan(_outerConeAngle * DegreesToRadians);
float falloffDiscRadius = radius * tan(_innerConeAngle * DegreesToRadians);
float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
Vector3 position = GetPosition();
DEBUG_DRAW_LINE(position, position + forward * radius + up * discRadius, color, 0, true);

View File

@@ -37,6 +37,21 @@
#include "Engine/Engine/CommandLine.h"
#endif
bool LayersMask::HasLayer(const StringView& layerName) const
{
return HasLayer(Level::GetLayerIndex(layerName));
}
bool LayersMask::operator==(const LayersMask& other) const
{
return Mask == other.Mask;
}
bool LayersMask::operator!=(const LayersMask& other) const
{
return Mask != other.Mask;
}
enum class SceneEventType
{
OnSceneSaving = 0,
@@ -98,7 +113,6 @@ public:
{
}
bool Init() override;
void Update() override;
void LateUpdate() override;
void FixedUpdate() override;
@@ -162,13 +176,22 @@ bool LevelImpl::deleteActor(Actor* actor)
return false;
}
bool LevelService::Init()
void LayersAndTagsSettings::Apply()
{
auto& settings = *LayersAndTagsSettings::Get();
Level::Tags = settings.Tags;
// Note: we cannot remove tags/layers at runtime so this should deserialize them in additive mode
// Tags/Layers are stored as index in actors so collection change would break the linkage
for (auto& tag : Tags)
{
if (!Level::Tags.Contains(tag))
Level::Tags.Add(tag);
}
for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++)
Level::Layers[i] = settings.Layers[i];
return false;
{
const auto& src = Layers[i];
auto& dst = Level::Layers[i];
if (dst.IsEmpty() || !src.IsEmpty())
dst = src;
}
}
void LevelService::Update()
@@ -194,7 +217,7 @@ void LevelService::Update()
for (int32 i = 0; i < scenes.Count(); i++)
{
if (scenes[i]->GetIsActive())
scenes[i]->Ticking.Update.TickEditorScripts();
scenes[i]->Ticking.Update.TickExecuteInEditor();
}
}
#endif
@@ -223,7 +246,7 @@ void LevelService::LateUpdate()
for (int32 i = 0; i < scenes.Count(); i++)
{
if (scenes[i]->GetIsActive())
scenes[i]->Ticking.LateUpdate.TickEditorScripts();
scenes[i]->Ticking.LateUpdate.TickExecuteInEditor();
}
}
#endif
@@ -255,7 +278,7 @@ void LevelService::FixedUpdate()
for (int32 i = 0; i < scenes.Count(); i++)
{
if (scenes[i]->GetIsActive())
scenes[i]->Ticking.FixedUpdate.TickEditorScripts();
scenes[i]->Ticking.FixedUpdate.TickExecuteInEditor();
}
}
#endif
@@ -674,6 +697,20 @@ int32 Level::GetNonEmptyLayerNamesCount()
return result + 1;
}
int32 Level::GetLayerIndex(const StringView& layer)
{
int32 result = -1;
for (int32 i = 0; i < 32; i++)
{
if (Layers[i] == layer)
{
result = i;
break;
}
}
return result;
}
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
{
PROFILE_CPU();

View File

@@ -445,7 +445,7 @@ public:
/// The layers names.
/// </summary>
static String Layers[32];
/// <summary>
/// Gets or adds the tag (returns the tag index).
/// </summary>
@@ -459,6 +459,11 @@ public:
/// <returns>The layers count.</returns>
static int32 GetNonEmptyLayerNamesCount();
/// <summary>
/// Gets the zero-based index of the layer.
/// </summary>
static int32 GetLayerIndex(const StringView& layer);
private:
// Actor API

View File

@@ -2,7 +2,7 @@
namespace FlaxEngine
{
public sealed partial class Scene
partial class Scene
{
/// <summary>
/// The scene asset typename. Type of the serialized scene asset data. Hidden class for the scene assets. Actors deserialization rules are strictly controlled under the hood by the C++ core parts. Mostly because scene asset has the same ID as scene root actor so loading both managed objects for scene asset and scene will crash (due to object ids conflict).

View File

@@ -19,7 +19,7 @@ class NavMesh;
/// <summary>
/// The scene root object that contains a hierarchy of actors.
/// </summary>
API_CLASS() class FLAXENGINE_API Scene final : public Actor
API_CLASS() class FLAXENGINE_API Scene : public Actor
{
DECLARE_SCENE_OBJECT(Scene);
friend Level;

View File

@@ -28,8 +28,8 @@ SceneRendering::SceneRendering(::Scene* scene)
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
{
#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
#if SCENE_RENDERING_USE_SIMD
CullDataSIMD cullData;
{
// Near
@@ -126,7 +126,7 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
for (int32 i = 0; i < actors.Count(); i++)
{
auto actor = actors[i];
if (frustum.Intersects(actor->GetSphere()))
if (view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
@@ -134,8 +134,8 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
{
#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
#if SCENE_RENDERING_USE_SIMD
CullDataSIMD cullData;
{
// Near
@@ -233,7 +233,7 @@ void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderCon
for (int32 i = 0; i < actors.Count(); i++)
{
auto actor = actors[i];
if (actor->GetStaticFlags() & renderContext.View.StaticFlagsMask && frustum.Intersects(actor->GetSphere()))
if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
@@ -257,7 +257,7 @@ void SceneRendering::Draw(RenderContext& renderContext)
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
if (actor->GetStaticFlags() & view.StaticFlagsMask)
if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()))
actor->Draw(renderContext);
}
}
@@ -271,7 +271,8 @@ void SceneRendering::Draw(RenderContext& renderContext)
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
actor->Draw(renderContext);
if (view.RenderLayersMask.HasLayer(actor->GetLayer()))
actor->Draw(renderContext);
}
}
}

View File

@@ -17,15 +17,9 @@ public:
/// <summary>
/// Tick function type.
/// </summary>
class Tick
struct Tick
{
public:
/// <summary>
/// Signature of the function to call
/// </summary>
typedef void (*Signature)();
typedef void (*SignatureObj)(void*);
template<class T, void(T::*Method)()>
@@ -51,8 +45,7 @@ public:
/// <summary>
/// Calls the binded function.
/// </summary>
/// <returns>Function result</returns>
void Call() const
FORCE_INLINE void Call() const
{
(*FunctionObj)(Callee);
}
@@ -63,10 +56,11 @@ public:
public:
Array<Script*> Scripts;
Array<Tick> Ticks;
#if USE_EDITOR
Array<Script*> ScriptsExecuteInEditor;
Array<Tick> TicksExecuteInEditor;
#endif
Array<Tick> Ticks;
TickData(int32 capacity)
: Scripts(capacity)
@@ -81,18 +75,17 @@ public:
TickScripts(Scripts);
for (int32 i = 0; i < Ticks.Count(); i++)
{
Ticks[i].Call();
}
}
#if USE_EDITOR
void TickEditorScripts()
void TickExecuteInEditor()
{
TickScripts(ScriptsExecuteInEditor);
}
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
TicksExecuteInEditor[i].Call();
}
#endif
void AddScript(Script* script);
@@ -118,12 +111,35 @@ public:
}
}
#if USE_EDITOR
template<class T, void(T::*Method)()>
void AddTickExecuteInEditor(T* callee)
{
SceneTicking::Tick tick;
tick.Bind<T, Method>(callee);
TicksExecuteInEditor.Add(tick);
}
void RemoveTickExecuteInEditor(void* callee)
{
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
{
if (TicksExecuteInEditor[i].Callee == callee)
{
TicksExecuteInEditor.RemoveAt(i);
break;
}
}
}
#endif
void Clear()
{
Scripts.Clear();
Ticks.Clear();
#if USE_EDITOR
ScriptsExecuteInEditor.Clear();
TicksExecuteInEditor.Clear();
#endif
}
};

View File

@@ -245,7 +245,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
_emitter = emitter;
_effect = effect;
_deltaTime = 0.0f;
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
_viewTask = effect->GetRenderTask();
_callStack.Clear();
// Find the maximum radius of the particle light
@@ -351,7 +351,7 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff
_emitter = emitter;
_effect = effect;
_deltaTime = 0.0f;
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
_viewTask = effect->GetRenderTask();
_callStack.Clear();
// Draw lights
@@ -411,7 +411,7 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE
_effect = effect;
_particleIndex = 0;
_deltaTime = dt;
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
_viewTask = effect->GetRenderTask();
_callStack.Clear();
auto& cpu = data.Buffer->CPU;
@@ -557,7 +557,7 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par
_effect = effect;
_particleIndex = 0;
_deltaTime = dt;
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
_viewTask = effect->GetRenderTask();
_callStack.Clear();
// Spawn particles

View File

@@ -27,11 +27,6 @@ class ParticleEmitterGraphCPUExecutor;
#define PARTICLE_EMITTER_MAX_CALL_STACK 100
// Gets the render task for the given effect update
#define PARTICLE_EMITTER_GET_VIEW_TASK(effect) \
(SceneRenderTask*)((effect)->CustomViewRenderTask && (effect)->CustomViewRenderTask->LastUsedFrame != 0 ? (effect)->CustomViewRenderTask.Get() : \
(MainRenderTask::Instance && MainRenderTask::Instance->LastUsedFrame != 0 ? MainRenderTask::Instance : nullptr))
class ParticleEmitterGraphCPUBox : public VisjectGraphBox
{
public:

View File

@@ -144,7 +144,7 @@ void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, Partic
}
// Skip if can
SceneRenderTask* viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
SceneRenderTask* viewTask = effect->GetRenderTask();
const int32 threads = data.Buffer->GPU.ParticlesCountMax + data.GPU.SpawnCount;
if (data.GPU.DeltaTime <= 0.0f || threads == 0 || !_mainCS)
return;

View File

@@ -339,6 +339,39 @@ void ParticleEffect::Sync()
}
}
SceneRenderTask* ParticleEffect::GetRenderTask() const
{
const uint64 minFrame = Engine::FrameCount - 2;
// Custom task
const auto customViewRenderTask = CustomViewRenderTask.Get();
if (customViewRenderTask && customViewRenderTask->Enabled && customViewRenderTask->LastUsedFrame >= minFrame)
return customViewRenderTask;
// Main task
const auto mainRenderTask = MainRenderTask::Instance;
if (mainRenderTask && mainRenderTask->Enabled && mainRenderTask->LastUsedFrame >= minFrame)
return mainRenderTask;
// Editor viewport
#if USE_EDITOR
for (auto task : RenderTask::Tasks)
{
if (task->LastUsedFrame >= minFrame && task->Enabled)
{
if (const auto sceneRenderTask = Cast<SceneRenderTask>(task))
{
if (sceneRenderTask->ActorsSource == ActorsSources::Scenes)
{
return sceneRenderTask;
}
}
}
}
#endif
return nullptr;
}
#if USE_EDITOR
Array<ParticleEffect::ParameterOverride> ParticleEffect::GetParametersOverrides()
@@ -373,6 +406,18 @@ void ParticleEffect::Update()
UpdateSimulation();
}
#if USE_EDITOR
#include "Editor/Editor.h"
void ParticleEffect::UpdateExecuteInEditor()
{
if (!Editor::IsPlayMode)
Update();
}
#endif
void ParticleEffect::CacheModifiedParameters()
{
if (_parameters.IsEmpty())
@@ -649,6 +694,7 @@ void ParticleEffect::OnEnable()
GetSceneRendering()->AddGeometry(this);
#if USE_EDITOR
GetSceneRendering()->AddViewportIcon(this);
GetScene()->Ticking.Update.AddTickExecuteInEditor<ParticleEffect, &ParticleEffect::UpdateExecuteInEditor>(this);
#endif
// Base
@@ -658,6 +704,7 @@ void ParticleEffect::OnEnable()
void ParticleEffect::OnDisable()
{
#if USE_EDITOR
GetScene()->Ticking.Update.RemoveTickExecuteInEditor(this);
GetSceneRendering()->RemoveViewportIcon(this);
#endif
GetSceneRendering()->RemoveGeometry(this);

View File

@@ -356,6 +356,11 @@ public:
/// </summary>
void Sync();
/// <summary>
/// Gets the render task to use for particles simulation (eg. depth buffer collisions or view information).
/// </summary>
SceneRenderTask* GetRenderTask() const;
#if USE_EDITOR
protected:
// Exposed parameters overrides for Editor Undo.
@@ -366,6 +371,9 @@ protected:
private:
void Update();
#if USE_EDITOR
void UpdateExecuteInEditor();
#endif
void CacheModifiedParameters();
void ApplyModifiedParameters();
void OnParticleSystemModified();

View File

@@ -17,6 +17,9 @@
#include "Engine/Profiler/ProfilerGPU.h"
#include "Engine/Renderer/Utils/BitonicSort.h"
#endif
#if USE_EDITOR
#include "Editor/Editor.h"
#endif
struct SpriteParticleVertex
{
@@ -1155,6 +1158,13 @@ void ParticleManagerService::Update()
// Simulation delta time can be based on a time since last update or the current delta time
float dt = effect->UseTimeScale ? deltaTime : deltaTimeUnscaled;
float t = effect->UseTimeScale ? time : timeUnscaled;
#if USE_EDITOR
if (!Editor::IsPlayMode)
{
dt = deltaTimeUnscaled;
t = timeUnscaled;
}
#endif
const float lastUpdateTime = instance.LastUpdateTime;
if (lastUpdateTime > 0 && t > lastUpdateTime)
{

View File

@@ -316,6 +316,20 @@ public:
/// <param name="ptr">A pointer to the memory block to deallocate.</param>
static void Free(void* ptr) = delete;
/// <summary>
/// Allocates pages memory block.
/// </summary>
/// <param name="numPages">The number of pages to allocate.</param>
/// <param name="pageSize">The size of single page. Use Platform::GetCPUInfo().PageSize or provide compatible, custom size.</param>
/// <returns>The pointer to the allocated pages in memory.</returns>
static void* AllocatePages(uint64 numPages, uint64 pageSize) = delete;
/// <summary>
/// Frees allocated pages memory block.
/// </summary>
/// <param name="ptr">The pointer to the pages to deallocate.</param>
static void FreePages(void* ptr) = delete;
public:
/// <summary>

View File

@@ -51,6 +51,20 @@ void UnixPlatform::Free(void* ptr)
}
}
void* UnixPlatform::AllocatePages(uint64 numPages, uint64 pageSize)
{
const uint64 numBytes = numPages * pageSize;
// Fallback to malloc
return malloc(numBytes);
}
void UnixPlatform::FreePages(void* ptr)
{
// Fallback to free
free(ptr);
}
uint64 UnixPlatform::GetCurrentProcessId()
{
return getpid();

View File

@@ -16,6 +16,8 @@ public:
// [PlatformBase]
static void* Allocate(uint64 size, uint64 alignment);
static void Free(void* ptr);
static void* AllocatePages(uint64 numPages, uint64 pageSize);
static void FreePages(void* ptr);
static uint64 GetCurrentProcessId();
};

View File

@@ -316,6 +316,20 @@ void Win32Platform::Free(void* ptr)
_aligned_free(ptr);
}
void* Win32Platform::AllocatePages(uint64 numPages, uint64 pageSize)
{
const uint64 numBytes = numPages * pageSize;
// Use VirtualAlloc to allocate page-aligned memory
return VirtualAlloc(nullptr, numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
void Win32Platform::FreePages(void* ptr)
{
// Free page-aligned memory
VirtualFree(ptr, 0, MEM_RELEASE);
}
bool Win32Platform::Is64BitPlatform()
{
#ifdef PLATFORM_64BITS

View File

@@ -30,6 +30,8 @@ public:
static void Prefetch(void const* ptr);
static void* Allocate(uint64 size, uint64 alignment);
static void Free(void* ptr);
static void* AllocatePages(uint64 numPages, uint64 pageSize);
static void FreePages(void* ptr);
static bool Is64BitPlatform();
static BatteryInfo GetBatteryInfo();
static CPUInfo GetCPUInfo();

View File

@@ -307,13 +307,14 @@ int32 Font::HitTestText(const StringView& text, const Vector2& location, const T
ProcessText(text, lines, layout);
ASSERT(lines.HasItems());
float scale = layout.Scale / FontManager::FontScale;
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
// Offset position to match lines origin space
Vector2 rootOffset = layout.Bounds.Location + lines.First().Location;
Vector2 testPoint = location - rootOffset;
// Get line which may intersect with the position (it's possible because lines have fixed height)
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / GetHeight()), 0, lines.Count() - 1);
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / baseLinesDistance), 0, lines.Count() - 1);
const FontLineCache& line = lines[lineIndex];
float x = line.Location.X;
@@ -380,6 +381,7 @@ Vector2 Font::GetCharPosition(const StringView& text, int32 index, const TextLay
ProcessText(text, lines, layout);
ASSERT(lines.HasItems());
float scale = layout.Scale / FontManager::FontScale;
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
Vector2 rootOffset = layout.Bounds.Location + lines.First().Location;
// Find line with that position
@@ -414,12 +416,12 @@ Vector2 Font::GetCharPosition(const StringView& text, int32 index, const TextLay
}
// Upper left corner of the character
return rootOffset + Vector2(x, static_cast<float>(lineIndex * GetHeight()));
return rootOffset + Vector2(x, static_cast<float>(lineIndex * baseLinesDistance));
}
}
// Position after last character in the last line
return rootOffset + Vector2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * GetHeight()));
return rootOffset + Vector2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * baseLinesDistance));
}
void Font::FlushFaceSize() const

View File

@@ -21,13 +21,12 @@ bool SpriteHandle::GetSprite(Sprite* result) const
*result = Atlas->Sprites[Index];
return true;
}
return false;
}
bool SpriteHandle::IsValid() const
{
return Atlas && Index != INVALID_INDEX && Atlas->Sprites.Count() > Index;
return Atlas && Index >= 0 && Atlas->Sprites.Count() > Index;
}
GPUTexture* SpriteHandle::GetAtlasTexture() const

View File

@@ -2,9 +2,11 @@
#pragma once
#include "Engine/Core/ISerializable.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Math/Rectangle.h"
#include "Engine/Content/BinaryAsset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Graphics/Textures/TextureBase.h"
class SpriteAtlas;
@@ -31,8 +33,9 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(Sprite);
/// <summary>
/// Handle to sprite atlas slot with a single sprite texture.
/// </summary>
API_STRUCT() struct FLAXENGINE_API SpriteHandle
API_STRUCT() struct FLAXENGINE_API SpriteHandle : ISerializable
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(SpriteHandle);
/// <summary>
@@ -43,7 +46,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(SpriteHandle);
/// <summary>
/// The parent atlas.
/// </summary>
API_FIELD() SpriteAtlas* Atlas;
API_FIELD() AssetReference<SpriteAtlas> Atlas;
/// <summary>
/// The atlas sprites array index.
@@ -55,7 +58,6 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(SpriteHandle);
/// </summary>
SpriteHandle()
{
Atlas = nullptr;
Index = -1;
}
@@ -65,8 +67,8 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(SpriteHandle);
/// <param name="atlas">The sprite atlas.</param>
/// <param name="index">The sprite slot index.</param>
SpriteHandle(SpriteAtlas* atlas, int32 index)
: Atlas(atlas)
{
Atlas = atlas;
Index = index;
}

View File

@@ -516,6 +516,7 @@ namespace
Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.InstanceCount != 0 &&
b.InstanceCount != 0 &&
handler.CanBatch(a, b) &&
a.WorldDeterminantSign == b.WorldDeterminantSign;
}
}

View File

@@ -168,6 +168,7 @@ MClass* MAssembly::GetClass(const StringAnsiView& fullname) const
const auto& classes = GetClasses();
MClass* result = nullptr;
classes.TryGet(key, result);
#if 0
if (!result)
{
@@ -183,25 +184,30 @@ MClass* MAssembly::GetClass(const StringAnsiView& fullname) const
MClass* MAssembly::GetClass(MonoClass* monoClass) const
{
// Check input
if (monoClass == nullptr)
if (monoClass == nullptr || !IsLoaded() || mono_class_get_image(monoClass) != _monoImage)
return nullptr;
// Check state
if (!IsLoaded())
{
LOG(Fatal, "Trying to use unloaded assembly {0}! Source: {1}", String(_name), TEXT("MAssembly::GetClass()"));
return nullptr;
}
// Find class by native pointer
const auto& classes = GetClasses();
const auto typeToken = mono_class_get_type_token(monoClass);
for (auto i = classes.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value->GetNative() == monoClass)
MonoClass* e = i->Value->GetNative();
if (e == monoClass || mono_class_get_type_token(e) == typeToken)
{
return i->Value;
}
}
#if 0
{
LOG(Warning, "Failed to find class {0}.{1} in assembly {2}. Classes:", String(mono_class_get_namespace(monoClass)), String(mono_class_get_name(monoClass)), ToString());
for (auto i = classes.Begin(); i.IsNotEnd(); ++i)
{
LOG(Warning, " - {0}", String(i->Key));
}
}
#endif
return nullptr;
}

View File

@@ -2,10 +2,7 @@
#pragma once
#include "ISerializable.h"
#include "ISerializeModifier.h"
#include "Json.h"
#include "JsonWriter.h"
#include "SerializationFwd.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
@@ -13,74 +10,6 @@
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/WeakAssetReference.h"
// The floating-point values serialization epsilon for equality checks precision
#define SERIALIZE_EPSILON 0.0000001f
// Helper macro to cast object on diff serialization
#define SERIALIZE_GET_OTHER_OBJ(type) const auto other = static_cast<const type*>(otherObj)
// Helper macro to use find member in the stream by name (skips strlen call if string is constant)
#define SERIALIZE_FIND_MEMBER(stream, name) stream.FindMember(rapidjson_flax::Value(name, ARRAY_COUNT(name) - 1))
// Serialization helper macro
#define SERIALIZE(name) \
if (Serialization::ShouldSerialize(name, other ? &other->name : nullptr)) \
{ \
stream.JKEY(#name); \
Serialization::Serialize(stream, name, other ? &other->name : nullptr); \
}
// Serialization helper macro (for private members, or with custom member name)
#define SERIALIZE_MEMBER(name, member) \
if (Serialization::ShouldSerialize(member, other ? &other->member : nullptr)) \
{ \
stream.JKEY(#name); \
Serialization::Serialize(stream, member, other ? &other->member : nullptr); \
}
// Deserialization helper macro
#define DESERIALIZE(name) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd()) \
Serialization::Deserialize(e->value, name, modifier); \
}
// Deserialization helper macro (for private members, or with custom member name)
#define DESERIALIZE_MEMBER(name, member) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd()) \
Serialization::Deserialize(e->value, member, modifier); \
}
// Helper macros for bit fields
#define SERIALIZE_BIT(name) \
if (!other || name != other->name) \
{ \
stream.JKEY(#name); \
stream.Bool(name != 0); \
}
#define SERIALIZE_BIT_MEMBER(name, member) \
if (!other || member != other->member) \
{ \
stream.JKEY(#name); \
stream.Bool(member != 0); \
}
#define DESERIALIZE_BIT(name) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd() && e->value.IsBool()) \
name = e->value.GetBool() ? 1 : 0; \
}
#define DESERIALIZE_BIT_MEMBER(name, member) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd() && e->value.IsBool()) \
member = e->value.GetBool() ? 1 : 0; \
}
struct Version;
struct VariantType;

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/ISerializable.h"
#include "ISerializeModifier.h"
#include "Json.h"
#include "JsonWriter.h"
// The floating-point values serialization epsilon for equality checks precision
#define SERIALIZE_EPSILON 0.0000001f
// Helper macro to cast object on diff serialization
#define SERIALIZE_GET_OTHER_OBJ(type) const auto other = static_cast<const type*>(otherObj)
// Helper macro to use find member in the stream by name (skips strlen call if string is constant)
#define SERIALIZE_FIND_MEMBER(stream, name) stream.FindMember(rapidjson_flax::Value(name, ARRAY_COUNT(name) - 1))
// Serialization helper macro
#define SERIALIZE(name) \
if (Serialization::ShouldSerialize(name, other ? &other->name : nullptr)) \
{ \
stream.JKEY(#name); \
Serialization::Serialize(stream, name, other ? &other->name : nullptr); \
}
// Serialization helper macro (for private members, or with custom member name)
#define SERIALIZE_MEMBER(name, member) \
if (Serialization::ShouldSerialize(member, other ? &other->member : nullptr)) \
{ \
stream.JKEY(#name); \
Serialization::Serialize(stream, member, other ? &other->member : nullptr); \
}
// Deserialization helper macro
#define DESERIALIZE(name) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd()) \
Serialization::Deserialize(e->value, name, modifier); \
}
// Deserialization helper macro (for private members, or with custom member name)
#define DESERIALIZE_MEMBER(name, member) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd()) \
Serialization::Deserialize(e->value, member, modifier); \
}
// Helper macros for bit fields
#define SERIALIZE_BIT(name) \
if (!other || name != other->name) \
{ \
stream.JKEY(#name); \
stream.Bool(name != 0); \
}
#define SERIALIZE_BIT_MEMBER(name, member) \
if (!other || member != other->member) \
{ \
stream.JKEY(#name); \
stream.Bool(member != 0); \
}
#define DESERIALIZE_BIT(name) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd() && e->value.IsBool()) \
name = e->value.GetBool() ? 1 : 0; \
}
#define DESERIALIZE_BIT_MEMBER(name, member) \
{ \
const auto e = SERIALIZE_FIND_MEMBER(stream, #name); \
if (e != stream.MemberEnd() && e->value.IsBool()) \
member = e->value.GetBool() ? 1 : 0; \
}

View File

@@ -14,6 +14,7 @@
#include "Engine/Utilities/StringConverter.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#endif
namespace IncludedFiles

View File

@@ -21,6 +21,7 @@
#include "Engine/Platform/FileSystemWatcher.h"
#include "Engine/Platform/FileSystem.h"
#include "Editor/Editor.h"
#include "Editor/ProjectInfo.h"
#endif
#if COMPILE_WITH_D3D_SHADER_COMPILER

View File

@@ -10,7 +10,8 @@
void Task::Start()
{
ASSERT(GetState() == TaskState::Created);
if (_state != TaskState::Created)
return;
OnStart();

View File

@@ -61,7 +61,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Updates the text blocks.
/// </summary>
protected void UpdateTextBlocks()
protected virtual void UpdateTextBlocks()
{
Profiler.BeginEvent("RichTextBoxBase.UpdateTextBlocks");

View File

@@ -28,6 +28,10 @@ namespace FlaxEngine.GUI
'\"',
')',
'(',
'/',
'\\',
'>',
'<',
};
/// <summary>
@@ -422,7 +426,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Clears all text from the text box control.
/// </summary>
public void Clear()
public virtual void Clear()
{
Text = string.Empty;
}
@@ -430,7 +434,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Clear selection range
/// </summary>
public void ClearSelection()
public virtual void ClearSelection()
{
OnSelectingEnd();
SetSelection(-1);
@@ -439,7 +443,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Resets the view offset (text scroll view).
/// </summary>
public void ResetViewOffset()
public virtual void ResetViewOffset()
{
TargetViewOffset = Vector2.Zero;
}
@@ -455,7 +459,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Copies the current selection in the text box to the Clipboard.
/// </summary>
public void Copy()
public virtual void Copy()
{
var selectedText = SelectedText;
if (selectedText.Length > 0)
@@ -468,7 +472,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Moves the current selection in the text box to the Clipboard.
/// </summary>
public void Cut()
public virtual void Cut()
{
var selectedText = SelectedText;
if (selectedText.Length > 0)
@@ -490,7 +494,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Replaces the current selection in the text box with the contents of the Clipboard.
/// </summary>
public void Paste()
public virtual void Paste()
{
if (IsReadOnly)
return;
@@ -508,7 +512,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Duplicates the current selection in the text box.
/// </summary>
public void Duplicate()
public virtual void Duplicate()
{
if (IsReadOnly)
return;
@@ -526,7 +530,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Ensures that the caret is visible in the TextBox window, by scrolling the TextBox control surface if necessary.
/// </summary>
public void ScrollToCaret()
public virtual void ScrollToCaret()
{
// If it's empty
if (_text.Length == 0)
@@ -547,7 +551,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Selects all text in the text box.
/// </summary>
public void SelectAll()
public virtual void SelectAll()
{
if (TextLength > 0)
{
@@ -558,7 +562,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Sets the selection to empty value.
/// </summary>
public void Deselect()
public virtual void Deselect()
{
SetSelection(-1);
}
@@ -568,7 +572,7 @@ namespace FlaxEngine.GUI
/// </summary>
/// <param name="location">The location (in control-space).</param>
/// <returns>The character index under the location</returns>
public int CharIndexAtPoint(ref Vector2 location)
public virtual int CharIndexAtPoint(ref Vector2 location)
{
return HitTestText(location + _viewOffset);
}
@@ -577,7 +581,7 @@ namespace FlaxEngine.GUI
/// Inserts the specified character (at the current selection).
/// </summary>
/// <param name="c">The character.</param>
public void Insert(char c)
public virtual void Insert(char c)
{
Insert(c.ToString());
}
@@ -586,7 +590,7 @@ namespace FlaxEngine.GUI
/// Inserts the specified text (at the current selection).
/// </summary>
/// <param name="str">The string.</param>
public void Insert(string str)
public virtual void Insert(string str)
{
if (IsReadOnly)
return;
@@ -622,7 +626,12 @@ namespace FlaxEngine.GUI
OnTextChanged();
}
private void MoveRight(bool shift, bool ctrl)
/// <summary>
/// Moves the caret right.
/// </summary>
/// <param name="shift">Shift is held.</param>
/// <param name="ctrl">Control is held.</param>
protected virtual void MoveRight(bool shift, bool ctrl)
{
if (HasSelection && !shift)
{
@@ -647,7 +656,12 @@ namespace FlaxEngine.GUI
}
}
private void MoveLeft(bool shift, bool ctrl)
/// <summary>
/// Moves the caret left.
/// </summary>
/// <param name="shift">Shift is held.</param>
/// <param name="ctrl">Control is held.</param>
protected virtual void MoveLeft(bool shift, bool ctrl)
{
if (HasSelection && !shift)
{
@@ -672,7 +686,12 @@ namespace FlaxEngine.GUI
}
}
private void MoveDown(bool shift, bool ctrl)
/// <summary>
/// Moves the caret down.
/// </summary>
/// <param name="shift">Shift is held.</param>
/// <param name="ctrl">Control is held.</param>
protected virtual void MoveDown(bool shift, bool ctrl)
{
if (HasSelection && !shift)
{
@@ -693,7 +712,12 @@ namespace FlaxEngine.GUI
}
}
private void MoveUp(bool shift, bool ctrl)
/// <summary>
/// Moves the caret up.
/// </summary>
/// <param name="shift">Shift is held.</param>
/// <param name="ctrl">Control is held.</param>
protected virtual void MoveUp(bool shift, bool ctrl)
{
if (HasSelection && !shift)
{
@@ -719,7 +743,7 @@ namespace FlaxEngine.GUI
/// </summary>
/// <param name="caret">The caret position.</param>
/// <param name="withScroll">If set to <c>true</c> with auto-scroll.</param>
protected void SetSelection(int caret, bool withScroll = true)
protected virtual void SetSelection(int caret, bool withScroll = true)
{
SetSelection(caret, caret);
}
@@ -730,7 +754,7 @@ namespace FlaxEngine.GUI
/// <param name="start">The selection start character.</param>
/// <param name="end">The selection end character.</param>
/// <param name="withScroll">If set to <c>true</c> with auto-scroll.</param>
protected void SetSelection(int start, int end, bool withScroll = true)
protected virtual void SetSelection(int start, int end, bool withScroll = true)
{
// Update parameters
int textLength = _text.Length;

View File

@@ -0,0 +1,213 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "SpriteRender.h"
#include "Engine/Core/Types/Variant.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Level/Scene/SceneRendering.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/MaterialInstance.h"
#include "Engine/Level/Actors/Camera.h"
#include "Engine/Serialization/Serialization.h"
SpriteRender::SpriteRender(const SpawnParams& params)
: Actor(params)
, _color(Color::White)
, _size(100.0f)
{
_quadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
Material.Loaded.Bind<SpriteRender, &SpriteRender::OnMaterialLoaded>(this);
Image.Changed.Bind<SpriteRender, &SpriteRender::SetImage>(this);
}
Vector2 SpriteRender::GetSize() const
{
return _size;
}
void SpriteRender::SetSize(const Vector2& value)
{
if (_size == value)
return;
_size = value;
OnTransformChanged();
}
Color SpriteRender::GetColor() const
{
return _color;
}
void SpriteRender::SetColor(const Color& value)
{
_color = value;
if (_paramColor)
_paramColor->SetValue(value);
}
SpriteHandle SpriteRender::GetSprite() const
{
return _sprite;
}
void SpriteRender::SetSprite(const SpriteHandle& value)
{
_sprite = value;
SetImage();
}
void SpriteRender::OnMaterialLoaded()
{
// Setup material instance
if (!_materialInstance)
{
_materialInstance = Content::CreateVirtualAsset<MaterialInstance>();
_materialInstance->AddReference();
}
_materialInstance->SetBaseMaterial(Material);
// Cache parameters
_paramImageMAD = _materialInstance->GetParameter(TEXT("ImageMAD"));
if (_paramImageMAD && _paramImageMAD->GetParameterType() != MaterialParameterType::Vector4)
_paramImageMAD = nullptr;
_paramImage = _materialInstance->GetParameter(TEXT("Image"));
if (_paramImage && _paramImage->GetParameterType() != MaterialParameterType::Texture)
_paramImage = nullptr;
else if (_paramImage)
SetImage();
_paramColor = _materialInstance->GetParameter(TEXT("Color"));
if (_paramColor && _paramColor->GetParameterType() != MaterialParameterType::Color && _paramColor->GetParameterType() != MaterialParameterType::Vector4 && _paramColor->GetParameterType() != MaterialParameterType::Vector3)
_paramColor = nullptr;
else if (_paramColor)
_paramColor->SetValue(_color);
}
void SpriteRender::SetImage()
{
TextureBase* image = Image.Get();
Vector4 imageMAD(Vector2::One, Vector2::Zero);
if (!image && _sprite.IsValid())
{
image = _sprite.Atlas.Get();
Sprite* sprite = &_sprite.Atlas->Sprites.At(_sprite.Index);
imageMAD = Vector4(sprite->Area.Size, sprite->Area.Location);
}
if (_paramImage)
_paramImage->SetValue(image);
if (_paramImageMAD)
_paramImageMAD->SetValue(imageMAD);
}
bool SpriteRender::HasContentLoaded() const
{
return (Material == nullptr || Material->IsLoaded()) && (Image == nullptr || Image->IsLoaded());
}
void SpriteRender::Draw(RenderContext& renderContext)
{
if (!Material || !Material->IsLoaded() || !_quadModel || !_quadModel->IsLoaded())
return;
auto model = _quadModel.As<Model>();
if (model->GetLoadedLODs() == 0)
return;
auto& view = renderContext.View;
Matrix m1, m2, m3, world;
Matrix::Scaling(_size.X, _size.Y, 1.0f, m2);
Matrix::RotationY(PI, m3);
Matrix::Multiply(m2, m3, m1);
if (FaceCamera)
{
Matrix::Billboard(_transform.Translation, view.Position, Vector3::Up, view.Direction, m2);
Matrix::Multiply(m1, m2, m3);
Matrix::Scaling(_transform.Scale, m1);
Matrix::Multiply(m1, m3, world);
}
else
{
_transform.GetWorld(m2);
Matrix::Multiply(m1, m2, world);
}
model->LODs[0].Draw(renderContext, _materialInstance, world, GetStaticFlags(), false, DrawModes, GetPerInstanceRandom());
}
void SpriteRender::DrawGeneric(RenderContext& renderContext)
{
Draw(renderContext);
}
void SpriteRender::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
Actor::Serialize(stream, otherObj);
SERIALIZE_GET_OTHER_OBJ(SpriteRender);
SERIALIZE_MEMBER(Size, _size);
SERIALIZE_MEMBER(Color, _color);
SERIALIZE_MEMBER(Sprite, _sprite);
SERIALIZE(Image);
SERIALIZE(Material);
SERIALIZE(FaceCamera);
SERIALIZE(DrawModes);
}
void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
// Base
Actor::Deserialize(stream, modifier);
DESERIALIZE_MEMBER(Size, _size);
DESERIALIZE_MEMBER(Color, _color);
DESERIALIZE_MEMBER(Sprite, _sprite);
DESERIALIZE(Image);
DESERIALIZE(Material);
DESERIALIZE(FaceCamera);
DESERIALIZE(DrawModes);
SetImage();
if (_paramColor)
_paramColor->SetValue(_color);
}
void SpriteRender::OnEndPlay()
{
// Base
Actor::OnEndPlay();
// Release material instance
if (_materialInstance)
{
_materialInstance->SetBaseMaterial(nullptr);
_materialInstance->Params.Resize(0);
_materialInstance->RemoveReference();
_materialInstance = nullptr;
}
}
void SpriteRender::OnEnable()
{
GetSceneRendering()->AddGeometry(this);
// Base
Actor::OnEnable();
}
void SpriteRender::OnDisable()
{
GetSceneRendering()->RemoveGeometry(this);
// Base
Actor::OnDisable();
}
void SpriteRender::OnTransformChanged()
{
// Base
Actor::OnTransformChanged();
const BoundingSphere localSphere(Vector3::Zero, _size.Length());
Matrix world;
_transform.GetWorld(world);
BoundingSphere::Transform(localSphere, world, _sphere);
BoundingBox::FromSphere(_sphere, _box);
}

View File

@@ -0,0 +1,108 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Level/Actor.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Render2D/SpriteAtlas.h"
/// <summary>
/// Sprite rendering object.
/// </summary>
API_CLASS() class FLAXENGINE_API SpriteRender : public Actor
{
DECLARE_SCENE_OBJECT(SpriteRender);
private:
Color _color;
Vector2 _size;
SpriteHandle _sprite;
MaterialInstance* _materialInstance = nullptr;
MaterialParameter* _paramImage = nullptr;
MaterialParameter* _paramImageMAD = nullptr;
MaterialParameter* _paramColor = nullptr;
AssetReference<Asset> _quadModel;
public:
/// <summary>
/// Gets the size of the sprite.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Sprite\")")
Vector2 GetSize() const;
/// <summary>
/// Sets the size of the sprite.
/// </summary>
API_PROPERTY() void SetSize(const Vector2& value);
/// <summary>
/// Gets the color of the sprite. Passed to the sprite material in parameter named `Color`.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Color), \"1,1,1,1\"), EditorDisplay(\"Sprite\")")
Color GetColor() const;
/// <summary>
/// Sets the color of the sprite. Passed to the sprite material in parameter named `Color`.
/// </summary>
API_PROPERTY() void SetColor(const Color& value);
/// <summary>
/// The sprite texture to draw.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Sprite\")")
AssetReference<Texture> Image;
/// <summary>
/// Gets the sprite to draw. Used only if Image is unset.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(25), EditorDisplay(\"Sprite\")")
SpriteHandle GetSprite() const;
/// <summary>
/// Sets the sprite to draw. Used only if Image is unset.
/// </summary>
API_PROPERTY()
void SetSprite(const SpriteHandle& value);
/// <summary>
/// The material used for the sprite rendering. It should contain texture parameter named Image and color parameter named Color. For showing sprites from sprite atlas ensure to add Vector4 param ImageMAD for UVs transformation.
/// </summary>
API_FIELD(Attributes="EditorOrder(30), DefaultValue(null), EditorDisplay(\"Sprite\")")
AssetReference<MaterialBase> Material;
/// <summary>
/// If checked, the sprite will automatically face the view camera, otherwise it will be oriented as an actor.
/// </summary>
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"Sprite\")")
bool FaceCamera = true;
/// <summary>
/// The draw passes to use for rendering this object. Uncheck `Depth` to disable sprite casting shadows.
/// </summary>
API_FIELD(Attributes="EditorOrder(50), DefaultValue(DrawPass.Default), EditorDisplay(\"Sprite\")")
DrawPass DrawModes = DrawPass::Default;
private:
void OnMaterialLoaded();
void SetImage();
public:
// [Actor]
bool HasContentLoaded() const override;
void Draw(RenderContext& renderContext) override;
void DrawGeneric(RenderContext& renderContext) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnEndPlay() override;
protected:
// [Actor]
void OnEnable() override;
void OnDisable() override;
void OnTransformChanged() override;
};

View File

@@ -534,6 +534,12 @@ void ShaderGenerator::ProcessGroupPacking(Box* box, Node* node, Value& value)
Value v = tryGetValue(node->GetBox(0), Vector4::Zero).AsVector4();
value = Value(ValueType::Vector2, v.Value + TEXT(".yz"));
break;
}
case 47:
{
Value v = tryGetValue(node->GetBox(0), Vector4::Zero).AsVector4();
value = Value(ValueType::Vector2, v.Value + TEXT(".zw"));
break;
}
// Mask XYZ
case 70:

View File

@@ -533,6 +533,12 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
value = Vector2(v.Y, v.Z);
break;
}
case 47:
{
const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
value = Vector2(v.Z, v.W);
break;
}
// Mask XYZ
case 70: