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

# Conflicts:
#	Source/Editor/Surface/SurfaceNode.cs
This commit is contained in:
Wojtek Figat
2021-01-27 10:39:44 +01:00
43 changed files with 1115 additions and 268 deletions

View File

@@ -15,7 +15,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
Vector2 n = p1 - p0;
const float length = n.Length();
if (length < 1e-10)
if (length < 1e-10f)
{
// Both points are the same, just give any
result = p0;
@@ -24,7 +24,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
n /= length;
const float dot = Vector2::Dot(n, p);
if (dot <= 0.0)
if (dot <= 0.0f)
{
// Before first point
result = p0;
@@ -41,6 +41,45 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2
}
}
Vector2 CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1)
{
Vector2 result;
ClosestPointPointLine(point, p0, p1, result);
return result;
}
void CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result)
{
const Vector3 p = point - p0;
Vector3 n = p1 - p0;
const float length = n.Length();
if (length < 1e-10f)
{
result = p0;
return;
}
n /= length;
const float dot = Vector3::Dot(n, p);
if (dot <= 0.0f)
{
result = p0;
return;
}
if (dot >= length)
{
result = p1;
return;
}
result = p0 + n * dot;
}
Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1)
{
Vector3 result;
ClosestPointPointLine(point, p0, p1, result);
return result;
}
void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result)
{
// Source: Real-Time Collision Detection by Christer Ericson
@@ -101,6 +140,13 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec
result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w
}
Vector3 CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3)
{
Vector3 result;
ClosestPointPointTriangle(point, vertex1, vertex2, vertex3, result);
return result;
}
void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result)
{
// Source: Real-Time Collision Detection by Christer Ericson
@@ -112,6 +158,13 @@ void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3&
result = point - t * plane.Normal;
}
Vector3 CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point)
{
Vector3 result;
ClosestPointPlanePoint(plane, point, result);
return result;
}
void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result)
{
// Source: Real-Time Collision Detection by Christer Ericson
@@ -122,6 +175,13 @@ void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector
Vector3::Min(temp, box.Maximum, result);
}
Vector3 CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point)
{
Vector3 result;
ClosestPointBoxPoint(box, point, result);
return result;
}
void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result)
{
Vector2 temp, end;
@@ -130,6 +190,13 @@ void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const V
Vector2::Min(temp, end, result);
}
Vector2 CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point)
{
Vector2 result;
ClosestPointRectanglePoint(rect, point, result);
return result;
}
void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result)
{
// Source: Jorgy343
@@ -147,6 +214,13 @@ void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, con
result += sphere.Center;
}
Vector3 CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point)
{
Vector3 result;
ClosestPointSpherePoint(sphere, point, result);
return result;
}
void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result)
{
// Source: Jorgy343
@@ -164,6 +238,13 @@ void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, c
result += sphere1.Center;
}
Vector3 CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
{
Vector3 result;
ClosestPointSphereSphere(sphere1, sphere2, result);
return result;
}
float CollisionsHelper::DistancePlanePoint(const Plane& plane, const Vector3& point)
{
// Source: Real-Time Collision Detection by Christer Ericson
@@ -821,7 +902,6 @@ bool CollisionsHelper::RayIntersectsBox(const Ray& ray, const BoundingBox& box,
d = Math::Abs(size.Z - Math::Abs(localPoint.Z));
if (d < dMin)
{
dMin = d;
normal = Vector3(0, 0, Math::Sign(localPoint.Z));
}
@@ -990,15 +1070,11 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsBox(const Plane& plane, c
min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z;
float distance = Vector3::Dot(plane.Normal, max);
if (distance + plane.D > Plane::DistanceEpsilon)
return PlaneIntersectionType::Front;
distance = Vector3::Dot(plane.Normal, min);
if (distance + plane.D < Plane::DistanceEpsilon)
return PlaneIntersectionType::Back;
return PlaneIntersectionType::Intersecting;
}
@@ -1012,10 +1088,8 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsSphere(const Plane& plane
if (distance > sphere.Radius)
return PlaneIntersectionType::Front;
if (distance < -sphere.Radius)
return PlaneIntersectionType::Back;
return PlaneIntersectionType::Intersecting;
}
@@ -1023,13 +1097,10 @@ bool CollisionsHelper::BoxIntersectsBox(const BoundingBox& box1, const BoundingB
{
if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X)
return false;
if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y)
return false;
if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z)
return false;
return true;
}

View File

@@ -72,6 +72,33 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <returns>The closest point between the two objects.</result>
static Vector2 ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result);
/// <summary>
/// Determines the closest point between a point and a line.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="p0">The line first point.</param>
/// <param name="p1">The line second point.</param>
/// <returns>The closest point between the two objects.</result>
static Vector3 ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1);
/// <summary>
/// Determines the closest point between a point and a triangle.
/// </summary>
@@ -82,6 +109,16 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result);
/// <summary>
/// Determines the closest point between a point and a triangle.
/// </summary>
/// <param name="point">The point to test.</param>
/// <param name="vertex1">The first vertex to test.</param>
/// <param name="vertex2">The second vertex to test.</param>
/// <param name="vertex3">The third vertex to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3);
/// <summary>
/// Determines the closest point between a <see cref="Plane" /> and a point.
/// </summary>
@@ -90,6 +127,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="Plane" /> and a point.
/// </summary>
/// <param name="plane">The plane to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointPlanePoint(const Plane& plane, const Vector3& point);
/// <summary>
/// Determines the closest point between a <see cref="BoundingBox" /> and a point.
/// </summary>
@@ -98,6 +143,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingBox" /> and a point.
/// </summary>
/// <param name="box">The box to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector3 ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point);
/// <summary>
/// Determines the closest point between a <see cref="Rectangle" /> and a point.
/// </summary>
@@ -106,6 +159,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects.</param>
static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result);
/// <summary>
/// Determines the closest point between a <see cref="Rectangle" /> and a point.
/// </summary>
/// <param name="rect">The rectangle to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects.</returns>
static Vector2 ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a point.
/// </summary>
@@ -114,6 +175,14 @@ public:
/// <param name="result">When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</param>
static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a point.
/// </summary>
/// <param name="sphere">The sphere to test.</param>
/// <param name="point">The point to test.</param>
/// <returns>The closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</return>
static Vector3 ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a <see cref="BoundingSphere" />.
/// </summary>
@@ -127,6 +196,19 @@ public:
/// </remarks>
static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result);
/// <summary>
/// Determines the closest point between a <see cref="BoundingSphere" /> and a <see cref="BoundingSphere" />.
/// </summary>
/// <param name="sphere1">The first sphere to test.</param>
/// <param name="sphere2">The second sphere to test.</param>
/// <returns>The closest point between the two objects; or, if the point is directly in the center of the sphere, contains <see cref="Vector3.Zero" />.</returns>
/// <remarks>
/// If the two spheres are overlapping, but not directly on top of each other, the closest point
/// is the 'closest' point of intersection. This can also be considered is the deepest point of
/// intersection.
/// </remarks>
static Vector3 ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2);
/// <summary>
/// Determines the distance between a <see cref="Plane" /> and a point.
/// </summary>

View File

@@ -117,7 +117,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param>
@@ -163,7 +163,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param>
@@ -210,7 +210,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param>
@@ -257,7 +257,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param>

View File

@@ -10,6 +10,127 @@
#include "Engine/Serialization/MemoryReadStream.h"
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
namespace
{
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Vector3* vertices, IndexType* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
auto model = mesh->GetModel();
CHECK_RETURN(model && model->IsVirtual(), true);
CHECK_RETURN(triangles && vertices, true);
// Pack mesh data into vertex buffers
Array<VB1ElementType> vb1;
Array<VB2ElementType> vb2;
vb1.Resize(vertexCount);
if (normals)
{
if (tangents)
{
for (uint32 i = 0; i < vertexCount; i++)
{
const Vector3 normal = normals[i];
const Vector3 tangent = tangents[i];
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
else
{
for (uint32 i = 0; i < vertexCount; i++)
{
const Vector3 normal = normals[i];
// Calculate tangent
Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ);
Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY);
Vector3 tangent;
if (c1.LengthSquared() > c2.LengthSquared())
tangent = c1;
else
tangent = c2;
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
}
else
{
// Set default tangent frame
const auto n = Float1010102(Vector3::UnitZ);
const auto t = Float1010102(Vector3::UnitX);
for (uint32 i = 0; i < vertexCount; i++)
{
vb1[i].Normal = n;
vb1[i].Tangent = t;
}
}
if (uvs)
{
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = Half2(uvs[i]);
}
else
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = v;
}
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].LightmapUVs = v;
}
if (colors)
{
vb2.Resize(vertexCount);
for (uint32 i = 0; i < vertexCount; i++)
vb2[i].Color = colors[i];
}
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles);
}
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount);
ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto vertices = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0);
auto triangles = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
const auto normals = normalsObj ? (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0) : nullptr;
const auto tangents = tangentsObj ? (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0) : nullptr;
const auto uvs = uvObj ? (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0) : nullptr;
const auto colors = colorsObj ? (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0) : nullptr;
return UpdateMesh<IndexType>(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
template<typename IndexType>
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj)
{
const auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && trianglesObj);
// Get buffer data
ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
return mesh->UpdateTriangles(triangleCount, ib);
}
}
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices)
{
Unload();
@@ -31,6 +152,16 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType*
return failed;
}
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
return ::UpdateMesh<uint16>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors)
{
return ::UpdateMesh<uint32>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
}
bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices)
{
// Cache data
@@ -384,108 +515,9 @@ ScriptingObject* Mesh::GetParentModel()
return _model;
}
template<typename IndexType>
bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj);
// Get buffers data
ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount);
ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto vb0 = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
Array<VB1ElementType> vb1;
Array<VB2ElementType> vb2;
vb1.Resize(vertexCount);
if (normalsObj)
{
const auto normals = (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0);
if (tangentsObj)
{
const auto tangents = (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0);
for (uint32 i = 0; i < vertexCount; i++)
{
// Peek normal and tangent
const Vector3 normal = normals[i];
const Vector3 tangent = tangents[i];
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
else
{
for (uint32 i = 0; i < vertexCount; i++)
{
// Peek normal
const Vector3 normal = normals[i];
// Calculate tangent
Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ);
Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY);
Vector3 tangent;
if (c1.LengthSquared() > c2.LengthSquared())
tangent = c1;
else
tangent = c2;
// Calculate bitangent sign
Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent));
byte sign = static_cast<byte>(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0);
// Set tangent frame
vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign);
vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0);
}
}
}
else
{
const auto n = Float1010102(Vector3::UnitZ);
const auto t = Float1010102(Vector3::UnitX);
for (uint32 i = 0; i < vertexCount; i++)
{
vb1[i].Normal = n;
vb1[i].Tangent = t;
}
}
if (uvObj)
{
const auto uvs = (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = Half2(uvs[i]);
}
else
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].TexCoord = v;
}
{
auto v = Half2(0, 0);
for (uint32 i = 0; i < vertexCount; i++)
vb1[i].LightmapUVs = v;
}
if (colorsObj)
{
vb2.Resize(vertexCount);
const auto colors = (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0);
for (uint32 i = 0; i < vertexCount; i++)
vb2[i].Color = colors[i];
}
return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vb0, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, ib);
}
bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
{
return ::UpdateMesh<int32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
return ::UpdateMesh<uint32>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
}
bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj)
@@ -493,22 +525,9 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* v
return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
}
template<typename IndexType>
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj)
{
auto model = mesh->GetModel();
ASSERT(model && model->IsVirtual() && trianglesObj);
// Get buffer data
ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount);
auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0);
return mesh->UpdateTriangles(triangleCount, ib);
}
bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj)
{
return ::UpdateTriangles<int32>(this, triangleCount, trianglesObj);
return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
}
bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj)

View File

@@ -216,9 +216,9 @@ public:
/// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, int32* ib)
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib)
{
return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false);
}
@@ -231,7 +231,7 @@ public:
/// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib)
{
@@ -240,17 +240,51 @@ public:
/// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb0">The first vertex buffer data.</param>
/// <param name="vb1">The second vertex buffer data.</param>
/// <param name="vb2">The third vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer in clockwise order.</param>
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices);
/// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uvs">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr);
/// <summary>
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uvs">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr);
public:
/// <summary>
@@ -259,7 +293,7 @@ public:
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="ib">The index buffer.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, int32* ib)
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib)
{
return UpdateTriangles(triangleCount, ib, false);
}

View File

@@ -163,7 +163,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer in clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib)
{
@@ -176,7 +176,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer, clockwise order.</param>
/// <returns>True if failed, otherwise false.</returns>
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib)
{
@@ -189,7 +189,7 @@ public:
/// <param name="vertexCount">The amount of vertices in the vertex buffer.</param>
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
/// <param name="vb">The vertex buffer data.</param>
/// <param name="ib">The index buffer.</param>
/// <param name="ib">The index buffer in clockwise order.</param>
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
/// <returns>True if failed, otherwise false.</returns>
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices);

View File

@@ -104,7 +104,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null.</param>
/// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param>
/// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>
@@ -140,7 +140,7 @@ namespace FlaxEngine
/// Mesh data will be cached and uploaded to the GPU with a delay.
/// </summary>
/// <param name="vertices">The mesh vertices positions. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="triangles">The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null.</param>
/// <param name="blendIndices">The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null.</param>
/// <param name="blendWeights">The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null.</param>
/// <param name="normals">The normal vectors (per vertex).</param>

View File

@@ -55,7 +55,7 @@ void CharacterController::SetHeight(const float value)
void CharacterController::SetSlopeLimit(float value)
{
value = Math::Clamp(value, 0.0f, 90.0f);
value = Math::Clamp(value, 0.0f, 89.0f);
if (Math::NearEqual(value, _slopeLimit))
return;
@@ -177,7 +177,7 @@ void CharacterController::CreateActor()
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
desc.height = Math::Max(Math::Abs(_height) * scaling, minSize);
desc.radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize);
// Create controller
_controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc);
@@ -202,7 +202,7 @@ void CharacterController::UpdateSize() const
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
_controller->setRadius(radius);

View File

@@ -5,6 +5,7 @@
#include "WindowsFileSystem.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/Window.h"
#include "Engine/Platform/Windows/ComPtr.h"
#include "Engine/Core/Types/StringView.h"
#include "../Win32/IncludeWindowsHeaders.h"
@@ -293,41 +294,39 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin
{
bool result = true;
// Allocate memory for the filenames
int32 maxPathSize = 2 * MAX_PATH;
Array<Char> pathBuffer;
pathBuffer.Resize(maxPathSize);
pathBuffer[0] = 0;
// Randomly generated GUID used for storing the last location of this dialog
const Guid folderGuid(0x53890ed9, 0xa55e47ba, 0xa970bdae, 0x72acedff);
// Setup description
BROWSEINFOW bi;
ZeroMemory(&bi, sizeof(bi));
if (parentWindow)
bi.hwndOwner = static_cast<HWND>(parentWindow->GetNativePtr());
bi.lpszTitle = title.HasChars() ? title.Get() : nullptr;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)(initialDirectory.HasChars() ? initialDirectory.Get() : nullptr);
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
if (pidl != nullptr)
ComPtr<IFileOpenDialog> fd;
if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd))))
{
// Get the name of the folder and put it in path
SHGetPathFromIDList(pidl, pathBuffer.Get());
DWORD options;
fd->GetOptions(&options);
fd->SetOptions(options | FOS_PICKFOLDERS | FOS_NOCHANGEDIR);
if (pathBuffer[0] != 0)
{
path = pathBuffer.Get();
result = false;
}
if (title.HasChars())
fd->SetTitle(title.Get());
// Free memory used
IMalloc* imalloc = 0;
if (SUCCEEDED(SHGetMalloc(&imalloc)))
// Associate the last selected folder with this GUID instead of overwriting the global one
fd->SetClientGuid(*reinterpret_cast<const GUID*>(&folderGuid));
ComPtr<IShellItem> defaultFolder;
if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder))))
fd->SetFolder(defaultFolder);
if (SUCCEEDED(fd->Show(parentWindow->GetHWND())))
{
imalloc->Free(pidl);
imalloc->Release();
ComPtr<IShellItem> si;
if (SUCCEEDED(fd->GetResult(&si)))
{
LPWSTR resultPath;
if (SUCCEEDED(si->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &resultPath)))
{
path = resultPath;
CoTaskMemFree(resultPath);
result = false;
}
}
}
}

View File

@@ -274,8 +274,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
// Compute depth difference
auto depthDiff = writeLocal(VariantType::Float, String::Format(TEXT("{0} * ViewFar - {1}"), sceneDepth.Value, posVS.Value), node);
auto fadeDistance = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat();
// Apply smoothing factor and clamp the result
value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, node->Values[0].AsFloat), node);
value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, fadeDistance.Value), node);
break;
}
// Material Function
@@ -337,6 +339,59 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
case 25:
value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)"));
break;
// Blend Normals
case 26:
{
const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3();
const auto additionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3();
const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value);
value = writeLocal(ValueType::Vector3, text, node);
break;
}
// Rotator
case 27:
{
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2();
auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat();
const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node);
const auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node);
const auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node);
const auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node);
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node);
break;
}
// Sphere Mask
case 28:
{
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type);
Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat();
Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat();
Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool();
// Get distance and apply radius
auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node);
// Apply hardness, use 0.991 as max since any value above will result in harsh aliasing
auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node);
value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node);
break;
}
// Tiling & Offset
case 29:
{
auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2();
auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2();
auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2();
value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node);
break;
}
default:
break;
}

View File

@@ -190,6 +190,16 @@ bool TextureTool::ExportTextureDirectXTex(ImageType type, const StringView& path
}
img = tmp.GetImage(0, 0, 0);
}
else if (image.format == DXGI_FORMAT_R10G10B10A2_UNORM || image.format == DXGI_FORMAT_R11G11B10_FLOAT)
{
result = DirectX::Convert(image, DXGI_FORMAT_R8G8B8A8_UNORM, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, tmp);
if (FAILED(result))
{
LOG(Error, "Cannot convert texture, error: {0:x}", static_cast<uint32>(result));
return true;
}
img = tmp.GetImage(0, 0, 0);
}
DirectX::WICCodecs codec;
switch (type)

View File

@@ -401,6 +401,18 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
{
value = writeFunction1(node, tryGetValue(node->GetBox(0), Value::Zero), TEXT("radians"));
break;
}
// Remap
case 48:
{
const auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat);
const auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2());
const auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2());
const auto clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool();
const auto mapFunc = String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value);
value = writeLocal(ValueType::Float, String::Format(TEXT("{2} ? clamp({0}, {1}.x, {1}.y) : {0}"), mapFunc, rangeB.Value, clamp.Value), node);
break;
}
default:
break;

View File

@@ -371,6 +371,20 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value)
if (value.Type.Type == VariantType::Enum)
value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero);
break;
// Remap
case 48:
{
const float inVal = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat;
const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2();
const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2();
const bool clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool;
auto mapFunc = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X);
// Clamp value?
value = clamp ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc;
break;
}
default:
break;
}