Merge remote-tracking branch 'origin/master' into 1.9
This commit is contained in:
@@ -83,13 +83,13 @@ public:
|
||||
/// Checks if knowledge has a given goal (exact type match without base class check).
|
||||
/// </summary>
|
||||
/// <param name="type">The goal type.</param>
|
||||
/// <returns>True if has a given goal, otherwise false.</returns>
|
||||
/// <returns>True if knowledge has a given goal, otherwise false.</returns>
|
||||
API_FUNCTION() bool HasGoal(ScriptingTypeHandle type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if knowledge has a given goal (exact type match without base class check).
|
||||
/// </summary>
|
||||
/// <returns>True if has a given goal, otherwise false.</returns>
|
||||
/// <returns>True if knowledge has a given goal, otherwise false.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE bool HasGoal()
|
||||
{
|
||||
|
||||
@@ -140,7 +140,7 @@ void BehaviorTreeNode::Deserialize(DeserializeStream& stream, ISerializeModifier
|
||||
{
|
||||
SerializableScriptingObject::Deserialize(stream, modifier);
|
||||
|
||||
Name.Clear(); // Missing Name is assumes as unnamed node
|
||||
Name.Clear(); // Missing Name is assumed as unnamed node
|
||||
DESERIALIZE(Name);
|
||||
}
|
||||
|
||||
@@ -197,7 +197,6 @@ BehaviorUpdateResult BehaviorTreeSequenceNode::Update(const BehaviorUpdateContex
|
||||
return BehaviorUpdateResult::Failed;
|
||||
|
||||
auto result = Children[state->CurrentChildIndex]->InvokeUpdate(context);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case BehaviorUpdateResult::Success:
|
||||
@@ -232,7 +231,6 @@ BehaviorUpdateResult BehaviorTreeSelectorNode::Update(const BehaviorUpdateContex
|
||||
return BehaviorUpdateResult::Failed;
|
||||
|
||||
auto result = Children[state->CurrentChildIndex]->InvokeUpdate(context);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case BehaviorUpdateResult::Success:
|
||||
|
||||
@@ -1522,12 +1522,16 @@ void VisualScript::unload(bool isReloading)
|
||||
if (_scriptingTypeHandle)
|
||||
{
|
||||
VisualScriptingBinaryModule::Locker.Lock();
|
||||
auto& type = VisualScriptingModule.Types[_scriptingTypeHandle.TypeIndex];
|
||||
ScriptingType& type = VisualScriptingModule.Types[_scriptingTypeHandle.TypeIndex];
|
||||
if (type.Script.DefaultInstance)
|
||||
{
|
||||
Delete(type.Script.DefaultInstance);
|
||||
type.Script.DefaultInstance = nullptr;
|
||||
}
|
||||
char* typeName = (char*)Allocator::Allocate(sizeof(_typenameChars));
|
||||
Platform::MemoryCopy(typeName, _typenameChars, sizeof(_typenameChars));
|
||||
((StringAnsiView&)type.Fullname) = StringAnsiView(typeName, 32);
|
||||
VisualScriptingModule._unloadedScriptTypeNames.Add(typeName);
|
||||
VisualScriptingModule.TypeNameToTypeIndex.RemoveValue(_scriptingTypeHandle.TypeIndex);
|
||||
VisualScriptingModule.Scripts[_scriptingTypeHandle.TypeIndex] = nullptr;
|
||||
_scriptingTypeHandleCached = _scriptingTypeHandle;
|
||||
@@ -1653,6 +1657,8 @@ VisualScriptingBinaryModule::VisualScriptingBinaryModule()
|
||||
ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const ScriptingObjectSpawnParams& params)
|
||||
{
|
||||
// Create native object (base type can be C++ or C#)
|
||||
if (params.Type.Module == nullptr)
|
||||
return nullptr;
|
||||
ScriptingType& visualScriptType = (ScriptingType&)params.Type.GetType();
|
||||
ScriptingTypeHandle baseTypeHandle = visualScriptType.GetBaseType();
|
||||
const ScriptingType* baseTypePtr = &baseTypeHandle.GetType();
|
||||
@@ -1663,9 +1669,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
|
||||
}
|
||||
ScriptingObject* object = baseTypePtr->Script.Spawn(params);
|
||||
if (!object)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Beware! Hacking vtables incoming! Undefined behaviors exploits! Low-level programming!
|
||||
visualScriptType.HackObjectVTable(object, baseTypeHandle, 1);
|
||||
@@ -2060,6 +2064,11 @@ void VisualScriptingBinaryModule::Destroy(bool isReloading)
|
||||
return;
|
||||
|
||||
BinaryModule::Destroy(isReloading);
|
||||
|
||||
// Free cached script typenames table
|
||||
for (char* str : _unloadedScriptTypeNames)
|
||||
Allocator::Free(str);
|
||||
_unloadedScriptTypeNames.Clear();
|
||||
}
|
||||
|
||||
ScriptingTypeHandle VisualScript::GetScriptingType()
|
||||
|
||||
@@ -303,6 +303,7 @@ class FLAXENGINE_API VisualScriptingBinaryModule : public BinaryModule
|
||||
friend VisualScript;
|
||||
private:
|
||||
StringAnsi _name;
|
||||
Array<char*> _unloadedScriptTypeNames;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace FlaxEngine
|
||||
#if FLAX_EDITOR
|
||||
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
|
||||
#endif
|
||||
[Newtonsoft.Json.JsonConverter(typeof(Json.JsonAssetReferenceConverter))]
|
||||
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -579,7 +579,7 @@ public:
|
||||
/// Insert the given item at specified index with keeping items order.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which item should be inserted.</param>
|
||||
/// <param name="item">The item to insert.</param>
|
||||
/// <param name="item">The item to be inserted by copying.</param>
|
||||
void Insert(int32 index, const T& item)
|
||||
{
|
||||
ASSERT(index >= 0 && index <= _count);
|
||||
@@ -592,6 +592,23 @@ public:
|
||||
data[index] = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert the given item at specified index with keeping items order.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which item should be inserted.</param>
|
||||
/// <param name="item">The item to inserted by moving.</param>
|
||||
void Insert(int32 index, T&& item)
|
||||
{
|
||||
ASSERT(index >= 0 && index <= _count);
|
||||
EnsureCapacity(_count + 1);
|
||||
T* data = _allocation.Get();
|
||||
Memory::ConstructItems(data + _count, 1);
|
||||
for (int32 i = _count - 1; i >= index; i--)
|
||||
data[i + 1] = MoveTemp(data[i]);
|
||||
_count++;
|
||||
data[index] = MoveTemp(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert the given item at specified index with keeping items order.
|
||||
/// </summary>
|
||||
@@ -772,9 +789,9 @@ public:
|
||||
/// <summary>
|
||||
/// Performs pop from stack operation (stack grows at the end of the collection).
|
||||
/// </summary>
|
||||
T Pop()
|
||||
FORCE_INLINE T Pop()
|
||||
{
|
||||
T item(Last());
|
||||
T item = MoveTemp(Last());
|
||||
RemoveLast();
|
||||
return item;
|
||||
}
|
||||
@@ -807,6 +824,15 @@ public:
|
||||
Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs enqueue to queue operation (queue head is in the beginning of queue).
|
||||
/// </summary>
|
||||
/// <param name="item">The item to append.</param>
|
||||
void Enqueue(T&& item)
|
||||
{
|
||||
Add(MoveTemp(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs dequeue from queue operation (queue head is in the beginning of queue).
|
||||
/// </summary>
|
||||
@@ -814,7 +840,7 @@ public:
|
||||
T Dequeue()
|
||||
{
|
||||
ASSERT(HasItems());
|
||||
T item(First());
|
||||
T item = MoveTemp(_allocation.Get()[0]);
|
||||
RemoveAtKeepOrder(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace FlaxEngine
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct FloatR10G10B10A2
|
||||
{
|
||||
private uint value;
|
||||
private uint rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref = "T:FlaxEngine.FloatR10G10B10A2" /> structure.
|
||||
@@ -23,7 +23,7 @@ namespace FlaxEngine
|
||||
/// <param name="w">The floating point value that should be stored in A component (2 bit format).</param>
|
||||
public FloatR10G10B10A2(float x, float y, float z, float w)
|
||||
{
|
||||
value = Pack(x, y, z, w);
|
||||
rawValue = Pack(x, y, z, w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -33,7 +33,7 @@ namespace FlaxEngine
|
||||
/// <param name="w">The floating point value that should be stored in alpha component (2 bit format).</param>
|
||||
public FloatR10G10B10A2(Float3 value, float w = 0)
|
||||
{
|
||||
this.value = Pack(value.X, value.Y, value.Z, w);
|
||||
rawValue = Pack(value.X, value.Y, value.Z, w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,37 +42,33 @@ namespace FlaxEngine
|
||||
/// <param name = "value">The floating point value that should be stored in 10 bit format.</param>
|
||||
public FloatR10G10B10A2(Float4 value)
|
||||
{
|
||||
this.value = Pack(value.X, value.Y, value.Z, value.W);
|
||||
rawValue = Pack(value.X, value.Y, value.Z, value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw 32 bit value used to back this vector.
|
||||
/// </summary>
|
||||
public uint RawValue
|
||||
{
|
||||
get => value;
|
||||
set => this.value = value;
|
||||
}
|
||||
public uint RawValue => rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the R component.
|
||||
/// </summary>
|
||||
public float R => (value & 0x3FF) / 1023.0f;
|
||||
public float R => (rawValue & 0x3FF) / 1023.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the G component.
|
||||
/// </summary>
|
||||
public float G => ((value >> 10) & 0x3FF) / 1023.0f;
|
||||
public float G => ((rawValue >> 10) & 0x3FF) / 1023.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the B component.
|
||||
/// </summary>
|
||||
public float B => ((value >> 20) & 0x3FF) / 1023.0f;
|
||||
public float B => ((rawValue >> 20) & 0x3FF) / 1023.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the A component.
|
||||
/// </summary>
|
||||
public float A => (value >> 30) / 3.0f;
|
||||
public float A => (rawValue >> 30) / 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Performs an explicit conversion from <see cref = "T:FlaxEngine.Float4" /> to <see cref = "T:FlaxEngine.FloatR10G10B10A2" />.
|
||||
@@ -102,7 +98,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(FloatR10G10B10A2 left, FloatR10G10B10A2 right)
|
||||
{
|
||||
return left.value == right.value;
|
||||
return left.rawValue == right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +109,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(FloatR10G10B10A2 left, FloatR10G10B10A2 right)
|
||||
{
|
||||
return left.value != right.value;
|
||||
return left.rawValue != right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -131,7 +127,7 @@ namespace FlaxEngine
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
return rawValue.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -142,7 +138,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name = "value1" /> is the same instance as <paramref name = "value2" /> or if both are <c>null</c> references or if <c>value1.Equals(value2)</c> returns <c>true</c>; otherwise, <c>false</c>.</returns>
|
||||
public static bool Equals(ref FloatR10G10B10A2 value1, ref FloatR10G10B10A2 value2)
|
||||
{
|
||||
return value1.value == value2.value;
|
||||
return value1.rawValue == value2.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,7 +148,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if the current instance is equal to the specified object; <c>false</c> otherwise.</returns>
|
||||
public bool Equals(FloatR10G10B10A2 other)
|
||||
{
|
||||
return other.value == value;
|
||||
return other.rawValue == rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -162,7 +158,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if the current instance is equal to the specified object; <c>false</c> otherwise.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is FloatR10G10B10A2 other && value == other.value;
|
||||
return obj is FloatR10G10B10A2 other && rawValue == other.rawValue;
|
||||
}
|
||||
|
||||
private static uint Pack(float x, float y, float z, float w)
|
||||
@@ -191,11 +187,11 @@ namespace FlaxEngine
|
||||
{
|
||||
Float3 vectorOut;
|
||||
|
||||
uint tmp = value & 0x3FF;
|
||||
uint tmp = rawValue & 0x3FF;
|
||||
vectorOut.X = tmp / 1023.0f;
|
||||
tmp = (value >> 10) & 0x3FF;
|
||||
tmp = (rawValue >> 10) & 0x3FF;
|
||||
vectorOut.Y = tmp / 1023.0f;
|
||||
tmp = (value >> 20) & 0x3FF;
|
||||
tmp = (rawValue >> 20) & 0x3FF;
|
||||
vectorOut.Z = tmp / 1023.0f;
|
||||
|
||||
return vectorOut;
|
||||
@@ -209,13 +205,13 @@ namespace FlaxEngine
|
||||
{
|
||||
Float4 vectorOut;
|
||||
|
||||
uint tmp = value & 0x3FF;
|
||||
uint tmp = rawValue & 0x3FF;
|
||||
vectorOut.X = tmp / 1023.0f;
|
||||
tmp = (value >> 10) & 0x3FF;
|
||||
tmp = (rawValue >> 10) & 0x3FF;
|
||||
vectorOut.Y = tmp / 1023.0f;
|
||||
tmp = (value >> 20) & 0x3FF;
|
||||
tmp = (rawValue >> 20) & 0x3FF;
|
||||
vectorOut.Z = tmp / 1023.0f;
|
||||
vectorOut.W = (value >> 30) / 3.0f;
|
||||
vectorOut.W = (rawValue >> 30) / 3.0f;
|
||||
|
||||
return vectorOut;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace FlaxEngine
|
||||
{
|
||||
// Reference: [https://github.com/Microsoft/DirectXMath/blob/master/Inc/DirectXPackedVector.h]
|
||||
|
||||
private uint value;
|
||||
private uint rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref = "T:FlaxEngine.FloatR11G11B10" /> structure.
|
||||
@@ -31,7 +31,7 @@ namespace FlaxEngine
|
||||
/// <param name="z">The floating point value that should be stored in B component (10 bits format).</param>
|
||||
public FloatR11G11B10(float x, float y, float z)
|
||||
{
|
||||
value = Pack(x, y, z);
|
||||
rawValue = Pack(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,17 +40,13 @@ namespace FlaxEngine
|
||||
/// <param name="value">The floating point value that should be stored in compressed format.</param>
|
||||
public FloatR11G11B10(Float3 value)
|
||||
{
|
||||
this.value = Pack(value.X, value.Y, value.Z);
|
||||
rawValue = Pack(value.X, value.Y, value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw 32 bit value used to back this vector.
|
||||
/// </summary>
|
||||
public uint RawValue
|
||||
{
|
||||
get => value;
|
||||
set => this.value = value;
|
||||
}
|
||||
public uint RawValue => rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Performs an explicit conversion from <see cref = "T:FlaxEngine.Float3" /> to <see cref = "T:FlaxEngine.FloatR11G11B10" />.
|
||||
@@ -80,7 +76,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(FloatR11G11B10 left, FloatR11G11B10 right)
|
||||
{
|
||||
return left.value == right.value;
|
||||
return left.rawValue == right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,7 +87,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(FloatR11G11B10 left, FloatR11G11B10 right)
|
||||
{
|
||||
return left.value != right.value;
|
||||
return left.rawValue != right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -109,7 +105,7 @@ namespace FlaxEngine
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return value.GetHashCode();
|
||||
return rawValue.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +116,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name="value1" /> is the same instance as <paramref name="value2" /> or if both are <c>null</c> references or if <c>value1.Equals(value2)</c> returns <c>true</c>; otherwise, <c>false</c>.</returns>
|
||||
public static bool Equals(ref FloatR11G11B10 value1, ref FloatR11G11B10 value2)
|
||||
{
|
||||
return value1.value == value2.value;
|
||||
return value1.rawValue == value2.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -130,7 +126,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if the current instance is equal to the specified object; <c>false</c> otherwise.</returns>
|
||||
public bool Equals(FloatR11G11B10 other)
|
||||
{
|
||||
return other.value == value;
|
||||
return other.rawValue == rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,7 +136,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if the current instance is equal to the specified object; <c>false</c> otherwise.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is FloatR11G11B10 other && value == other.value;
|
||||
return obj is FloatR11G11B10 other && rawValue == other.rawValue;
|
||||
}
|
||||
|
||||
private static unsafe uint Pack(float x, float y, float z)
|
||||
@@ -288,7 +284,7 @@ namespace FlaxEngine
|
||||
{
|
||||
int zeroExponent = -112;
|
||||
|
||||
Packed packed = new Packed(value);
|
||||
Packed packed = new Packed(rawValue);
|
||||
uint* result = stackalloc uint[4];
|
||||
uint exponent;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace FlaxEngine
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct Half
|
||||
{
|
||||
private ushort value;
|
||||
private ushort rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Number of decimal digits of precision.
|
||||
@@ -111,17 +111,13 @@ namespace FlaxEngine
|
||||
/// <param name = "value">The floating point value that should be stored in 16 bit format.</param>
|
||||
public Half(float value)
|
||||
{
|
||||
this.value = HalfUtils.Pack(value);
|
||||
rawValue = HalfUtils.Pack(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the raw 16 bit value used to back this half-float.
|
||||
/// </summary>
|
||||
public ushort RawValue
|
||||
{
|
||||
get => value;
|
||||
set => this.value = value;
|
||||
}
|
||||
public ushort RawValue => rawValue;
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of half precision values into full precision values.
|
||||
@@ -166,7 +162,7 @@ namespace FlaxEngine
|
||||
/// <returns>The converted value.</returns>
|
||||
public static implicit operator float(Half value)
|
||||
{
|
||||
return HalfUtils.Unpack(value.value);
|
||||
return HalfUtils.Unpack(value.rawValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -177,7 +173,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name = "left" /> has the same value as <paramref name = "right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator ==(Half left, Half right)
|
||||
{
|
||||
return left.value == right.value;
|
||||
return left.rawValue == right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,7 +184,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name = "left" /> has a different value than <paramref name = "right" />; otherwise, <c>false</c>.</returns>
|
||||
public static bool operator !=(Half left, Half right)
|
||||
{
|
||||
return left.value != right.value;
|
||||
return left.rawValue != right.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,7 +203,7 @@ namespace FlaxEngine
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
ushort num = value;
|
||||
ushort num = rawValue;
|
||||
return (((num * 3) / 2) ^ num);
|
||||
}
|
||||
|
||||
@@ -219,7 +215,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if <paramref name = "value1" /> is the same instance as <paramref name = "value2" /> or if both are <c>null</c> references or if <c>value1.Equals(value2)</c> returns <c>true</c>; otherwise, <c>false</c>.</returns>
|
||||
public static bool Equals(ref Half value1, ref Half value2)
|
||||
{
|
||||
return value1.value == value2.value;
|
||||
return value1.rawValue == value2.rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,7 +225,7 @@ namespace FlaxEngine
|
||||
/// <returns><c>true</c> if the current instance is equal to the specified object; <c>false</c> otherwise.</returns>
|
||||
public bool Equals(Half other)
|
||||
{
|
||||
return other.value == value;
|
||||
return other.rawValue == rawValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -248,7 +244,7 @@ namespace FlaxEngine
|
||||
return false;
|
||||
}
|
||||
Half half = (Half)obj;
|
||||
return half.value == value;
|
||||
return half.rawValue == rawValue;
|
||||
}
|
||||
|
||||
static Half()
|
||||
|
||||
@@ -289,6 +289,7 @@ void Quaternion::Billboard(const Float3& objectPosition, const Float3& cameraPos
|
||||
|
||||
Quaternion Quaternion::FromDirection(const Float3& direction)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
Quaternion orientation;
|
||||
if (Float3::Dot(direction, Float3::Up) >= 0.999f)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
: Position(position)
|
||||
, Direction(direction)
|
||||
{
|
||||
ASSERT(Direction.IsNormalized());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -946,7 +946,8 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo
|
||||
|
||||
void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest)
|
||||
{
|
||||
const auto rot = Quaternion::FromDirection(direction.GetNormalized());
|
||||
ASSERT(direction.IsNormalized());
|
||||
const auto rot = Quaternion::FromDirection(direction);
|
||||
const Vector3 up = (rot * Vector3::Up);
|
||||
const Vector3 forward = (rot * Vector3::Forward);
|
||||
const Vector3 right = (rot * Vector3::Right);
|
||||
@@ -971,16 +972,17 @@ void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const C
|
||||
|
||||
void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float length, float duration, bool depthTest)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (isnan(length) || isinf(length))
|
||||
return;
|
||||
DrawLine(origin, origin + (direction.GetNormalized() * length), color, duration, depthTest);
|
||||
DrawLine(origin, origin + (direction * length), color, duration, depthTest);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawRay(const Ray& ray, const Color& color, float length, float duration, bool depthTest)
|
||||
{
|
||||
if (isnan(length) || isinf(length))
|
||||
return;
|
||||
DrawLine(ray.Position, ray.Position + (ray.Direction.GetNormalized() * length), color, duration, depthTest);
|
||||
DrawLine(ray.Position, ray.Position + (ray.Direction * length), color, duration, depthTest);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
|
||||
|
||||
@@ -850,11 +850,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
object fieldOwner = fieldOwnerHandle.Target;
|
||||
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||
object value = null;
|
||||
if (field.field.FieldType.IsValueType)
|
||||
value = Marshal.PtrToStructure(valuePtr, field.field.FieldType);
|
||||
else if (valuePtr != IntPtr.Zero)
|
||||
value = ManagedHandle.FromIntPtr(valuePtr).Target;
|
||||
object value = MarshalToManaged(valuePtr, field.field.FieldType);
|
||||
field.field.SetValue(fieldOwner, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
#define USE_MIKKTSPACE 1
|
||||
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
||||
#if USE_ASSIMP
|
||||
#define USE_SPARIAL_SORT 1
|
||||
#define USE_SPATIAL_SORT 1
|
||||
#define ASSIMP_BUILD_NO_EXPORT
|
||||
#include "Engine/Tools/ModelTool/SpatialSort.h"
|
||||
//#include <ThirdParty/assimp/SpatialSort.h>
|
||||
#else
|
||||
#define USE_SPARIAL_SORT 0
|
||||
#define USE_SPATIAL_SORT 0
|
||||
#endif
|
||||
#include <stack>
|
||||
|
||||
@@ -155,18 +155,18 @@ bool MeshData::GenerateLightmapUVs()
|
||||
}
|
||||
|
||||
int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int32 searchRange, const Array<int32>& mapping
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
, const Assimp::SpatialSort& spatialSort
|
||||
, std::vector<unsigned int>& sparialSortCache
|
||||
, std::vector<unsigned int>& spatialSortCache
|
||||
#endif
|
||||
)
|
||||
{
|
||||
const float uvEpsSqr = (1.0f / 250.0f) * (1.0f / 250.0f);
|
||||
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
const Float3 vPosition = mesh.Positions[vertexIndex];
|
||||
spatialSort.FindPositions(*(aiVector3D*)&vPosition, 1e-4f, sparialSortCache);
|
||||
if (sparialSortCache.empty())
|
||||
spatialSort.FindPositions(*(aiVector3D*)&vPosition, 1e-5f, spatialSortCache);
|
||||
if (spatialSortCache.empty())
|
||||
return INVALID_INDEX;
|
||||
|
||||
const Float2 vUV = mesh.UVs.HasItems() ? mesh.UVs[vertexIndex] : Float2::Zero;
|
||||
@@ -177,9 +177,9 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
|
||||
|
||||
const int32 end = startIndex + searchRange;
|
||||
|
||||
for (size_t i = 0; i < sparialSortCache.size(); i++)
|
||||
for (size_t i = 0; i < spatialSortCache.size(); i++)
|
||||
{
|
||||
const int32 v = sparialSortCache[i];
|
||||
const int32 v = spatialSortCache[i];
|
||||
if (v < startIndex || v >= end)
|
||||
continue;
|
||||
#else
|
||||
@@ -247,11 +247,11 @@ void MeshData::BuildIndexBuffer()
|
||||
mapping.Resize(vertexCount);
|
||||
int32 newVertexCounter = 0;
|
||||
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
// Set up a SpatialSort to quickly find all vertices close to a given position
|
||||
Assimp::SpatialSort vertexFinder;
|
||||
vertexFinder.Fill((const aiVector3D*)Positions.Get(), vertexCount, sizeof(Float3));
|
||||
std::vector<unsigned int> sparialSortCache;
|
||||
std::vector<unsigned int> spatialSortCache;
|
||||
#endif
|
||||
|
||||
// Build index buffer
|
||||
@@ -259,8 +259,8 @@ void MeshData::BuildIndexBuffer()
|
||||
{
|
||||
// Find duplicated vertex before the current one
|
||||
const int32 reuseVertexIndex = FindVertex(*this, vertexIndex, 0, vertexIndex, mapping
|
||||
#if USE_SPARIAL_SORT
|
||||
, vertexFinder, sparialSortCache
|
||||
#if USE_SPATIAL_SORT
|
||||
, vertexFinder, spatialSortCache
|
||||
#endif
|
||||
);
|
||||
if (reuseVertexIndex == INVALID_INDEX)
|
||||
@@ -304,18 +304,15 @@ void MeshData::BuildIndexBuffer()
|
||||
|
||||
dstBlendShape.Name = srcBlendShape.Name;
|
||||
dstBlendShape.Weight = srcBlendShape.Weight;
|
||||
dstBlendShape.Vertices.Resize(newVertexCounter);
|
||||
for (int32 i = 0, j = 0; i < srcBlendShape.Vertices.Count(); i++)
|
||||
dstBlendShape.Vertices.EnsureCapacity(srcBlendShape.Vertices.Count());
|
||||
for (int32 i = 0; i < srcBlendShape.Vertices.Count(); i++)
|
||||
{
|
||||
const auto idx = mapping[i];
|
||||
if (idx != INVALID_INDEX)
|
||||
auto& v = srcBlendShape.Vertices[i];
|
||||
int32 newVertexIndex = v.VertexIndex < (uint32)vertexCount ? mapping[v.VertexIndex] : INVALID_INDEX;
|
||||
if (newVertexIndex != INVALID_INDEX)
|
||||
{
|
||||
auto& v = srcBlendShape.Vertices[i];
|
||||
ASSERT_LOW_LAYER(v.VertexIndex < (uint32)vertexCount);
|
||||
ASSERT_LOW_LAYER(mapping[v.VertexIndex] != INVALID_INDEX);
|
||||
v.VertexIndex = mapping[v.VertexIndex];
|
||||
ASSERT_LOW_LAYER(v.VertexIndex < (uint32)newVertexCounter);
|
||||
dstBlendShape.Vertices[j++] = v;
|
||||
v.VertexIndex = newVertexIndex;
|
||||
dstBlendShape.Vertices.Add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,7 +373,7 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
Float3::Max(max, v3, max);
|
||||
}
|
||||
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
// Set up a SpatialSort to quickly find all vertices close to a given position
|
||||
Assimp::SpatialSort vertexFinder;
|
||||
vertexFinder.Fill((const aiVector3D*)Positions.Get(), vertexCount, sizeof(Float3));
|
||||
@@ -399,7 +396,7 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
continue;
|
||||
|
||||
// Get all vertices that share this one
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
vertexFinder.FindPositions(*(aiVector3D*)&Positions[i], posEpsilon, verticesFound);
|
||||
const int32 verticesFoundCount = (int32)verticesFound.size();
|
||||
#else
|
||||
@@ -429,7 +426,7 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
for (int32 i = 0; i < vertexCount; i++)
|
||||
{
|
||||
// Get all vertices that share this one
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
vertexFinder.FindPositions(*(aiVector3D*)&Positions[i], posEpsilon, verticesFound);
|
||||
const int32 verticesFoundCount = (int32)verticesFound.size();
|
||||
#else
|
||||
@@ -623,7 +620,7 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
// Set up a SpatialSort to quickly find all vertices close to a given position
|
||||
Assimp::SpatialSort vertexFinder;
|
||||
vertexFinder.Fill((const aiVector3D*)Positions.Get(), vertexCount, sizeof(Float3));
|
||||
@@ -648,7 +645,7 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
closeVertices.Clear();
|
||||
|
||||
// Find all vertices close to that position
|
||||
#if USE_SPARIAL_SORT
|
||||
#if USE_SPATIAL_SORT
|
||||
vertexFinder.FindPositions(*(aiVector3D*)&origPos, posEpsilon, verticesFound);
|
||||
const int32 verticesFoundCount = (int32)verticesFound.size();
|
||||
#else
|
||||
|
||||
@@ -543,6 +543,15 @@ void Actor::SetLayerRecursive(int32 layerIndex)
|
||||
OnLayerChanged();
|
||||
}
|
||||
|
||||
void Actor::SetName(String&& value)
|
||||
{
|
||||
if (_name == value)
|
||||
return;
|
||||
_name = MoveTemp(value);
|
||||
if (GetScene())
|
||||
Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, this, nullptr);
|
||||
}
|
||||
|
||||
void Actor::SetName(const StringView& value)
|
||||
{
|
||||
if (_name == value)
|
||||
|
||||
@@ -185,7 +185,13 @@ public:
|
||||
/// Sets the actor name.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
API_PROPERTY() void SetName(const StringView& value);
|
||||
API_PROPERTY() void SetName(String&& value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the actor name.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set.</param>
|
||||
void SetName(const StringView& value);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -930,6 +930,9 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
// Fire event
|
||||
CallSceneEvent(SceneEventType::OnSceneLoading, scene, sceneId);
|
||||
|
||||
// Get any injected children of the scene.
|
||||
Array<Actor*> injectedSceneChildren = scene->Children;
|
||||
|
||||
// Loaded scene objects list
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
const int32 dataCount = (int32)data.Size();
|
||||
@@ -1031,6 +1034,20 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
// /\ all above this has to be done on multiple threads at once
|
||||
// \/ all below this has to be done on an any thread
|
||||
|
||||
// Add injected children of scene (via OnSceneLoading) into sceneObjects to be initialized
|
||||
for (auto child : injectedSceneChildren)
|
||||
{
|
||||
Array<SceneObject*> injectedSceneObjects;
|
||||
injectedSceneObjects.Add(child);
|
||||
SceneQuery::GetAllSceneObjects(child, injectedSceneObjects);
|
||||
for (auto o : injectedSceneObjects)
|
||||
{
|
||||
if (!o->IsRegistered())
|
||||
o->RegisterObject();
|
||||
sceneObjects->Add(o);
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize prefab instances (prefab may have objects removed or reordered so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
|
||||
@@ -1047,7 +1064,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
PROFILE_CPU_NAMED("Initialize");
|
||||
|
||||
SceneObject** objects = sceneObjects->Get();
|
||||
for (int32 i = 0; i < dataCount; i++)
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = objects[i];
|
||||
if (obj)
|
||||
|
||||
@@ -180,7 +180,7 @@ void Scene::CreateCsgCollider()
|
||||
// Create collider
|
||||
auto result = New<MeshCollider>();
|
||||
result->SetStaticFlags(StaticFlags::FullyStatic);
|
||||
result->SetName(CSG_COLLIDER_NAME);
|
||||
result->SetName(String(CSG_COLLIDER_NAME));
|
||||
result->CollisionData = CSGData.CollisionData;
|
||||
result->HideFlags |= HideFlags::DontSelect;
|
||||
|
||||
@@ -203,7 +203,7 @@ void Scene::CreateCsgModel()
|
||||
// Create model
|
||||
auto result = New<StaticModel>();
|
||||
result->SetStaticFlags(StaticFlags::FullyStatic);
|
||||
result->SetName(CSG_MODEL_NAME);
|
||||
result->SetName(String(CSG_MODEL_NAME));
|
||||
result->Model = CSGData.Model;
|
||||
result->HideFlags |= HideFlags::DontSelect;
|
||||
|
||||
|
||||
@@ -27,6 +27,11 @@ Cloth::Cloth(const SpawnParams& params)
|
||||
_drawCategory = SceneRendering::SceneDrawAsync;
|
||||
}
|
||||
|
||||
void* Cloth::GetPhysicsCloth() const
|
||||
{
|
||||
return _cloth;
|
||||
}
|
||||
|
||||
ModelInstanceActor::MeshReference Cloth::GetMesh() const
|
||||
{
|
||||
auto value = _mesh;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
/// </summary>
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Cloth\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API Cloth : public Actor
|
||||
{
|
||||
friend class PhysicsBackend;
|
||||
DECLARE_SCENE_OBJECT(Cloth);
|
||||
|
||||
/// <summary>
|
||||
@@ -231,6 +230,11 @@ private:
|
||||
Array<float> _paint;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the native physics backend object.
|
||||
/// </summary>
|
||||
void* GetPhysicsCloth() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mesh to use for the cloth simulation (single mesh from specific LOD). Always from the parent static or animated model actor.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Physics/Wheeled Vehicle\"), ActorToolbox(\"Physics\")") class FLAXENGINE_API WheeledVehicle : public RigidBody
|
||||
{
|
||||
friend class PhysicsBackend;
|
||||
friend struct ScenePhysX;
|
||||
DECLARE_SCENE_OBJECT(WheeledVehicle);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -71,6 +71,7 @@ void Collider::SetContactOffset(float value)
|
||||
|
||||
bool Collider::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
resultHitDistance = MAX_float;
|
||||
if (_shape == nullptr)
|
||||
return false;
|
||||
@@ -79,6 +80,7 @@ bool Collider::RayCast(const Vector3& origin, const Vector3& direction, float& r
|
||||
|
||||
bool Collider::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (_shape == nullptr)
|
||||
return false;
|
||||
return PhysicsBackend::RayCastShape(_shape, _transform.Translation, _transform.Orientation, origin, direction, hitInfo, maxDistance);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ public:
|
||||
static PxCooking* GetCooking();
|
||||
#endif
|
||||
static PxMaterial* GetDefaultMaterial();
|
||||
static void SimulationStepDone(PxScene* scene, float dt);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#if COMPILE_WITH_PHYSX
|
||||
|
||||
#include "PhysicsStepperPhysX.h"
|
||||
#include "Engine/Physics/Physics.h"
|
||||
#include "PhysicsBackendPhysX.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include <ThirdParty/PhysX/foundation/PxMath.h>
|
||||
#include <ThirdParty/PhysX/PxSceneLock.h>
|
||||
@@ -66,7 +66,7 @@ void MultiThreadStepper::substepDone(StepperTask* ownerTask)
|
||||
}
|
||||
|
||||
// -> OnSubstep
|
||||
//Physics::OnSubstep();
|
||||
PhysicsBackendPhysX::SimulationStepDone(mScene, mSubStepSize);
|
||||
|
||||
if (mCurrentSubStep >= mNbSubSteps)
|
||||
{
|
||||
|
||||
@@ -235,76 +235,91 @@ bool Physics::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCas
|
||||
|
||||
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->RayCast(origin, direction, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->RayCast(origin, direction, hitInfo, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::RayCastAll(const Vector3& origin, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->RayCastAll(origin, direction, results, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->BoxCast(center, halfExtents, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->BoxCast(center, halfExtents, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->BoxCastAll(center, halfExtents, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::SphereCast(const Vector3& center, const float radius, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->SphereCast(center, radius, direction, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::SphereCast(const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->SphereCast(center, radius, direction, hitInfo, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::SphereCastAll(const Vector3& center, const float radius, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->SphereCastAll(center, radius, direction, results, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->CapsuleCast(center, radius, height, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->CapsuleCast(center, radius, height, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->CapsuleCastAll(center, radius, height, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->ConvexCast(center, convexMesh, scale, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->ConvexCast(center, convexMesh, scale, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool Physics::ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return DefaultScene->ConvexCastAll(center, convexMesh, scale, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
@@ -505,76 +520,91 @@ bool PhysicsScene::LineCastAll(const Vector3& start, const Vector3& end, Array<R
|
||||
|
||||
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::RayCast(_scene, origin, direction, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::RayCast(_scene, origin, direction, hitInfo, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::RayCastAll(const Vector3& origin, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::RayCastAll(_scene, origin, direction, results, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::BoxCast(_scene, center, halfExtents, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::BoxCast(_scene, center, halfExtents, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::BoxCastAll(_scene, center, halfExtents, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::SphereCast(_scene, center, radius, direction, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::SphereCast(_scene, center, radius, direction, hitInfo, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::SphereCastAll(const Vector3& center, const float radius, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::SphereCastAll(_scene, center, radius, direction, results, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::CapsuleCast(_scene, center, radius, height, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::CapsuleCast(_scene, center, radius, height, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::CapsuleCastAll(_scene, center, radius, height, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::ConvexCast(_scene, center, convexMesh, scale, direction, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::ConvexCast(_scene, center, convexMesh, scale, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
bool PhysicsScene::ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
return PhysicsBackend::ConvexCastAll(_scene, center, convexMesh, scale, direction, results, rotation, maxDistance, layerMask, hitTriggers);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
/// Performs a line between two points in the scene, returns all hitpoints infos.
|
||||
/// </summary>
|
||||
/// <param name="start">The origin of the ray.</param>
|
||||
/// <param name="end">The normalized direction of the ray.</param>
|
||||
/// <param name="end">The end position of the line.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
/// <summary>
|
||||
/// Tries to find a method in a given scripting type by the method name and parameters count.
|
||||
/// </summary>
|
||||
/// <remarks>If the the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods).</remarks>
|
||||
/// <remarks>If the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods).</remarks>
|
||||
/// <param name="typeHandle">The type to find method inside it.</param>
|
||||
/// <param name="name">The method name.</param>
|
||||
/// <param name="numParams">The method parameters count.</param>
|
||||
@@ -182,7 +182,7 @@ public:
|
||||
/// Gets the value of a given scripting field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field.</param>
|
||||
/// <param name="instance">The object instance to get it's member field. Unused for static fields.</param>
|
||||
/// <param name="instance">The object instance to get its member field. Unused for static fields.</param>
|
||||
/// <param name="result">The output field value.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
virtual bool GetFieldValue(void* field, const Variant& instance, Variant& result)
|
||||
@@ -194,7 +194,7 @@ public:
|
||||
/// Sets the value of a given scripting field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field.</param>
|
||||
/// <param name="instance">The object instance to set it's member field. Unused for static fields.</param>
|
||||
/// <param name="instance">The object instance to set its member field. Unused for static fields.</param>
|
||||
/// <param name="value">The field value to assign.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
virtual bool SetFieldValue(void* field, const Variant& instance, Variant& value)
|
||||
@@ -242,7 +242,7 @@ public:
|
||||
/// <summary>
|
||||
/// Unloads the module (native library and C# assembly and any other scripting data). Unregisters the module.
|
||||
/// </summary>
|
||||
/// <param name="isReloading">If true module is during reloading and should force release the runtime data. Used for C# assembly to cleanup it's runtime data in Mono (or other scripting runtime).</param>
|
||||
/// <param name="isReloading">If true module is during reloading and should force release the runtime data. Used for C# assembly to clean up it's runtime data in Mono (or other scripting runtime).</param>
|
||||
virtual void Destroy(bool isReloading);
|
||||
};
|
||||
|
||||
|
||||
@@ -429,6 +429,60 @@ namespace FlaxEngine.Json
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class JsonAssetReferenceConverter : JsonConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override unsafe void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var asset = (JsonAsset)value.GetType().GetField("Asset").GetValue(value);
|
||||
var id = asset?.ID ?? Guid.Empty;
|
||||
writer.WriteValue(JsonSerializer.GetStringID(&id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
var result = Activator.CreateInstance(objectType);
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
JsonSerializer.ParseID((string)reader.Value, out var id);
|
||||
var asset = Content.LoadAsync<JsonAsset>(id);
|
||||
objectType.GetField("Asset").SetValue(result, asset);
|
||||
}
|
||||
else if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
// [Deprecated on 26.07.2024, expires on 26.07.2026]
|
||||
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
{
|
||||
var propertyName = (string)reader.Value;
|
||||
reader.Read();
|
||||
if (propertyName == "Asset" && reader.TokenType == JsonToken.String)
|
||||
{
|
||||
JsonSerializer.ParseID((string)reader.Value, out var id);
|
||||
var asset = Content.LoadAsync<JsonAsset>(id);
|
||||
objectType.GetField("Asset").SetValue(result, asset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.Name.StartsWith("JsonAssetReference");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize Guid values using `N` format
|
||||
|
||||
@@ -1963,6 +1963,7 @@ bool TerrainPatch::UpdateCollision()
|
||||
|
||||
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (_physicsShape == nullptr)
|
||||
return false;
|
||||
Vector3 shapePos;
|
||||
@@ -1973,6 +1974,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
|
||||
|
||||
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, Vector3& resultHitNormal, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (_physicsShape == nullptr)
|
||||
return false;
|
||||
Vector3 shapePos;
|
||||
@@ -1990,6 +1992,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
|
||||
|
||||
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, TerrainChunk*& resultChunk, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (_physicsShape == nullptr)
|
||||
return false;
|
||||
Vector3 shapePos;
|
||||
@@ -2027,6 +2030,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
|
||||
|
||||
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance) const
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
if (_physicsShape == nullptr)
|
||||
return false;
|
||||
Vector3 shapePos;
|
||||
|
||||
@@ -189,7 +189,7 @@ TEST_CASE("Prefabs")
|
||||
Guid newChildId;
|
||||
Guid::Parse(TEXT("123456a04cc60d56a2f024bfeef57723"), newChildId);
|
||||
auto newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer));
|
||||
newChild->SetName(TEXT("Prefab B.Child"));
|
||||
newChild->SetName(String(TEXT("Prefab B.Child")));
|
||||
newChild->SetParent(instanceB);
|
||||
|
||||
// Apply nested prefab changes
|
||||
@@ -213,7 +213,7 @@ TEST_CASE("Prefabs")
|
||||
// Add another child
|
||||
Guid::Parse(TEXT("678906a04cc60d56a2f024bfeef57723"), newChildId);
|
||||
newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer));
|
||||
newChild->SetName(TEXT("Prefab B.Child 2"));
|
||||
newChild->SetName(String(TEXT("Prefab B.Child 2")));
|
||||
newChild->SetParent(instanceB);
|
||||
|
||||
// Apply nested prefab changes
|
||||
|
||||
@@ -735,6 +735,25 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
_writer.Write(*triplanarTexture);
|
||||
value = result;
|
||||
}
|
||||
// Get Lightmap UV
|
||||
case 18:
|
||||
{
|
||||
auto output = writeLocal(Value::InitForZero(ValueType::Float2), node);
|
||||
auto lightmapUV = String::Format(TEXT(
|
||||
"{{\n"
|
||||
"#if USE_LIGHTMAP\n"
|
||||
"\t {0} = input.LightmapUV;\n"
|
||||
"#else\n"
|
||||
"\t {0} = float2(0,0);\n"
|
||||
"#endif\n"
|
||||
"}}\n"
|
||||
), output.Value);
|
||||
|
||||
_writer.Write(*lightmapUV);
|
||||
|
||||
value = output;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Mathd.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Math/Plane.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||
@@ -13,6 +14,11 @@
|
||||
#include "Engine/Platform/File.h"
|
||||
|
||||
#define OPEN_FBX_CONVERT_SPACE 1
|
||||
#if BUILD_DEBUG
|
||||
#define OPEN_FBX_GET_CACHE_LIST(arrayName, varName, size) data.arrayName.Resize(size, false); auto& varName = data.arrayName
|
||||
#else
|
||||
#define OPEN_FBX_GET_CACHE_LIST(arrayName, varName, size) data.arrayName.Resize(size, false); auto* varName = data.arrayName.Get()
|
||||
#endif
|
||||
|
||||
// Import OpenFBX library
|
||||
// Source: https://github.com/nem0/OpenFBX
|
||||
@@ -49,7 +55,7 @@ Quaternion ToQuaternion(const ofbx::Quat& v)
|
||||
return Quaternion((float)v.x, (float)v.y, (float)v.z, (float)v.w);
|
||||
}
|
||||
|
||||
Matrix ToMatrix(const ofbx::Matrix& mat)
|
||||
Matrix ToMatrix(const ofbx::DMatrix& mat)
|
||||
{
|
||||
Matrix result;
|
||||
for (int32 i = 0; i < 16; i++)
|
||||
@@ -103,6 +109,13 @@ struct OpenFbxImporterData
|
||||
Array<const ofbx::Material*> Materials;
|
||||
Array<MaterialSlotEntry> ImportedMaterials;
|
||||
|
||||
Array<int> TriangulatedIndicesCache;
|
||||
Array<Int4> BlendIndicesCache;
|
||||
Array<Float4> BlendWeightsCache;
|
||||
Array<Float2> TriangulatePointsCache;
|
||||
Array<int> TriangulateIndicesCache;
|
||||
Array<int> TriangulateEarIndicesCache;
|
||||
|
||||
OpenFbxImporterData(const String& path, const ModelTool::Options& options, ofbx::IScene* scene)
|
||||
: Scene(scene)
|
||||
, ScenePtr(scene)
|
||||
@@ -416,7 +429,7 @@ void ProcessNodes(OpenFbxImporterData& data, const ofbx::Object* aNode, int32 pa
|
||||
Matrix GetOffsetMatrix(OpenFbxImporterData& data, const ofbx::Mesh* mesh, const ofbx::Object* node)
|
||||
{
|
||||
#if 1
|
||||
auto* skin = mesh ? mesh->getGeometry()->getSkin() : nullptr;
|
||||
auto* skin = mesh ? mesh->getSkin() : nullptr;
|
||||
if (skin)
|
||||
{
|
||||
for (int i = 0, c = skin->getClusterCount(); i < c; i++)
|
||||
@@ -445,7 +458,7 @@ Matrix GetOffsetMatrix(OpenFbxImporterData& data, const ofbx::Mesh* mesh, const
|
||||
|
||||
bool IsMeshInvalid(const ofbx::Mesh* aMesh)
|
||||
{
|
||||
return aMesh->getGeometry()->getVertexCount() == 0;
|
||||
return aMesh->getGeometryData().getPositions().count == 0;
|
||||
}
|
||||
|
||||
bool ImportBones(OpenFbxImporterData& data, String& errorMsg)
|
||||
@@ -455,8 +468,7 @@ bool ImportBones(OpenFbxImporterData& data, String& errorMsg)
|
||||
for (int i = 0; i < meshCount; i++)
|
||||
{
|
||||
const auto aMesh = data.Scene->getMesh(i);
|
||||
const auto aGeometry = aMesh->getGeometry();
|
||||
const ofbx::Skin* skin = aGeometry->getSkin();
|
||||
const ofbx::Skin* skin = aMesh->getSkin();
|
||||
if (skin == nullptr || IsMeshInvalid(aMesh))
|
||||
continue;
|
||||
|
||||
@@ -524,56 +536,198 @@ bool ImportBones(OpenFbxImporterData& data, String& errorMsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* aMesh, MeshData& mesh, String& errorMsg, int32 triangleStart, int32 triangleEnd)
|
||||
int Triangulate(OpenFbxImporterData& data, const ofbx::GeometryData& geom, const ofbx::GeometryPartition::Polygon& polygon, int* triangulatedIndices)
|
||||
{
|
||||
if (polygon.vertex_count < 3)
|
||||
return 0;
|
||||
else if (polygon.vertex_count == 3)
|
||||
{
|
||||
triangulatedIndices[0] = polygon.from_vertex;
|
||||
triangulatedIndices[1] = polygon.from_vertex + 1;
|
||||
triangulatedIndices[2] = polygon.from_vertex + 2;
|
||||
return 3;
|
||||
}
|
||||
else if (polygon.vertex_count == 4)
|
||||
{
|
||||
triangulatedIndices[0] = polygon.from_vertex + 0;
|
||||
triangulatedIndices[1] = polygon.from_vertex + 1;
|
||||
triangulatedIndices[2] = polygon.from_vertex + 2;
|
||||
triangulatedIndices[3] = polygon.from_vertex + 0;
|
||||
triangulatedIndices[4] = polygon.from_vertex + 2;
|
||||
triangulatedIndices[5] = polygon.from_vertex + 3;
|
||||
return 6;
|
||||
}
|
||||
|
||||
const ofbx::Vec3Attributes& positions = geom.getPositions();
|
||||
Float3 normal = ToFloat3(geom.getNormals().get(polygon.from_vertex));
|
||||
|
||||
// Check if the polygon is convex
|
||||
int lastSign = 0;
|
||||
bool isConvex = true;
|
||||
for (int i = 0; i < polygon.vertex_count; i++)
|
||||
{
|
||||
Float3 v1 = ToFloat3(positions.get(polygon.from_vertex + i));
|
||||
Float3 v2 = ToFloat3(positions.get(polygon.from_vertex + (i + 1) % polygon.vertex_count));
|
||||
Float3 v3 = ToFloat3(positions.get(polygon.from_vertex + (i + 2) % polygon.vertex_count));
|
||||
|
||||
// The winding order of all triangles must be same for polygon to be considered convex
|
||||
int sign;
|
||||
Float3 c = Float3::Cross(v1 - v2, v3 - v2);
|
||||
if (c.LengthSquared() == 0.0f)
|
||||
continue;
|
||||
else if (Math::NotSameSign(c.X, normal.X) || Math::NotSameSign(c.Y, normal.Y) || Math::NotSameSign(c.Z, normal.Z))
|
||||
sign = 1;
|
||||
else
|
||||
sign = -1;
|
||||
if ((sign < 0 && lastSign > 0) || (sign > 0 && lastSign < 0))
|
||||
{
|
||||
isConvex = false;
|
||||
break;
|
||||
}
|
||||
lastSign += sign;
|
||||
}
|
||||
|
||||
// Fast-path for convex case
|
||||
if (isConvex)
|
||||
{
|
||||
for (int i = 0; i < polygon.vertex_count - 2; i++)
|
||||
{
|
||||
triangulatedIndices[i * 3 + 0] = polygon.from_vertex;
|
||||
triangulatedIndices[i * 3 + 1] = polygon.from_vertex + (i + 1) % polygon.vertex_count;
|
||||
triangulatedIndices[i * 3 + 2] = polygon.from_vertex + (i + 2) % polygon.vertex_count;
|
||||
}
|
||||
return 3 * (polygon.vertex_count - 2);
|
||||
}
|
||||
|
||||
// Setup arrays for temporary data (TODO: maybe double-linked list is more optimal?)
|
||||
auto& points = data.TriangulatePointsCache;
|
||||
auto& indices = data.TriangulateIndicesCache;
|
||||
auto& earIndices = data.TriangulateEarIndicesCache;
|
||||
points.Clear();
|
||||
indices.Clear();
|
||||
earIndices.Clear();
|
||||
points.EnsureCapacity(polygon.vertex_count, false);
|
||||
indices.EnsureCapacity(polygon.vertex_count, false);
|
||||
earIndices.EnsureCapacity(3 * (polygon.vertex_count - 2), false);
|
||||
|
||||
// Project points to a plane, choose two arbitrary axises
|
||||
const Float3 u = Float3::Cross(normal, Math::Abs(normal.X) > Math::Abs(normal.Y) ? Float3::Up : Float3::Right).GetNormalized();
|
||||
const Float3 v = Float3::Cross(normal, u).GetNormalized();
|
||||
for (int i = 0; i < polygon.vertex_count; i++)
|
||||
{
|
||||
const Float3 point = ToFloat3(positions.get(polygon.from_vertex + i));
|
||||
const Float3 projectedPoint = Float3::ProjectOnPlane(point, normal);
|
||||
const Float2 pointOnPlane = Float2(
|
||||
projectedPoint.X * u.X + projectedPoint.Y * u.Y + projectedPoint.Z * u.Z,
|
||||
projectedPoint.X * v.X + projectedPoint.Y * v.Y + projectedPoint.Z * v.Z);
|
||||
|
||||
points.Add(pointOnPlane);
|
||||
indices.Add(i);
|
||||
}
|
||||
|
||||
// Triangulate non-convex polygons using simple ear-clipping algorithm (https://nils-olovsson.se/articles/ear_clipping_triangulation/)
|
||||
const int maxIterations = indices.Count() * 10; // Safe guard to prevent infinite loop
|
||||
int index = 0;
|
||||
while (indices.Count() > 3 && index < maxIterations)
|
||||
{
|
||||
const int i1 = index % indices.Count();
|
||||
const int i2 = (index + 1) % indices.Count();
|
||||
const int i3 = (index + 2) % indices.Count();
|
||||
const Float2 p1 = points[indices[i1]];
|
||||
const Float2 p2 = points[indices[i2]];
|
||||
const Float2 p3 = points[indices[i3]];
|
||||
|
||||
// TODO: Skip triangles with very sharp angles?
|
||||
|
||||
// Skip reflex vertices
|
||||
if (Float2::Cross(p2 - p1, p3 - p1) < 0.0f)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The triangle is considered to be an "ear" when no other points reside inside the triangle
|
||||
bool isEar = true;
|
||||
for (int j = 0; j < indices.Count(); j++)
|
||||
{
|
||||
if (j == i1 || j == i2 || j == i3)
|
||||
continue;
|
||||
const Float2 candidate = points[indices[j]];
|
||||
if (CollisionsHelper::IsPointInTriangle(candidate, p1, p2, p3))
|
||||
{
|
||||
isEar = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isEar)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add an ear and remove the tip point from evaluation
|
||||
earIndices.Add(indices[i1]);
|
||||
earIndices.Add(indices[i2]);
|
||||
earIndices.Add(indices[i3]);
|
||||
indices.RemoveAtKeepOrder(i2);
|
||||
}
|
||||
|
||||
for (int i = 0; i < earIndices.Count(); i++)
|
||||
triangulatedIndices[i] = polygon.from_vertex + (earIndices[i] % polygon.vertex_count);
|
||||
triangulatedIndices[earIndices.Count() + 0] = polygon.from_vertex + (indices[0] % polygon.vertex_count);
|
||||
triangulatedIndices[earIndices.Count() + 1] = polygon.from_vertex + (indices[1] % polygon.vertex_count);
|
||||
triangulatedIndices[earIndices.Count() + 2] = polygon.from_vertex + (indices[2] % polygon.vertex_count);
|
||||
|
||||
return 3 * (polygon.vertex_count - 2);
|
||||
}
|
||||
|
||||
bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* aMesh, MeshData& mesh, String& errorMsg, int partitionIndex)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
mesh.Name = aMesh->name;
|
||||
ZoneText(*mesh.Name, mesh.Name.Length());
|
||||
const int32 firstVertexOffset = triangleStart * 3;
|
||||
const int32 lastVertexOffset = triangleEnd * 3;
|
||||
const ofbx::Geometry* aGeometry = aMesh->getGeometry();
|
||||
const int vertexCount = lastVertexOffset - firstVertexOffset + 3;
|
||||
ASSERT(firstVertexOffset + vertexCount <= aGeometry->getVertexCount());
|
||||
const ofbx::Vec3* vertices = aGeometry->getVertices();
|
||||
const ofbx::Vec3* normals = aGeometry->getNormals();
|
||||
const ofbx::Vec3* tangents = aGeometry->getTangents();
|
||||
const ofbx::Vec4* colors = aGeometry->getColors();
|
||||
const ofbx::Vec2* uvs = aGeometry->getUVs();
|
||||
const ofbx::Skin* skin = aGeometry->getSkin();
|
||||
const ofbx::BlendShape* blendShape = aGeometry->getBlendShape();
|
||||
const ofbx::GeometryData& geometryData = aMesh->getGeometryData();
|
||||
const ofbx::GeometryPartition& partition = geometryData.getPartition(partitionIndex);
|
||||
const int vertexCount = partition.triangles_count * 3;
|
||||
const ofbx::Vec3Attributes& positions = geometryData.getPositions();
|
||||
const ofbx::Vec2Attributes& uvs = geometryData.getUVs();
|
||||
const ofbx::Vec3Attributes& normals = geometryData.getNormals();
|
||||
const ofbx::Vec3Attributes& tangents = geometryData.getTangents();
|
||||
const ofbx::Vec4Attributes& colors = geometryData.getColors();
|
||||
const ofbx::Skin* skin = aMesh->getSkin();
|
||||
const ofbx::BlendShape* blendShape = aMesh->getBlendShape();
|
||||
OPEN_FBX_GET_CACHE_LIST(TriangulatedIndicesCache, triangulatedIndices, vertexCount);
|
||||
|
||||
// Properties
|
||||
const ofbx::Material* aMaterial = nullptr;
|
||||
if (aMesh->getMaterialCount() > 0)
|
||||
{
|
||||
if (aGeometry->getMaterials())
|
||||
aMaterial = aMesh->getMaterial(aGeometry->getMaterials()[triangleStart]);
|
||||
else
|
||||
aMaterial = aMesh->getMaterial(0);
|
||||
}
|
||||
aMaterial = aMesh->getMaterial(partitionIndex);
|
||||
mesh.MaterialSlotIndex = data.AddMaterial(result, aMaterial);
|
||||
|
||||
// Vertex positions
|
||||
mesh.Positions.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.Positions.Get()[i] = ToFloat3(vertices[i + firstVertexOffset]);
|
||||
{
|
||||
int numIndicesTotal = 0;
|
||||
for (int i = 0; i < partition.polygon_count; i++)
|
||||
{
|
||||
int numIndices = Triangulate(data, geometryData, partition.polygons[i], &triangulatedIndices[numIndicesTotal]);
|
||||
for (int j = numIndicesTotal; j < numIndicesTotal + numIndices; j++)
|
||||
mesh.Positions.Get()[j] = ToFloat3(positions.get(triangulatedIndices[j]));
|
||||
numIndicesTotal += numIndices;
|
||||
}
|
||||
}
|
||||
|
||||
// Indices (dummy index buffer)
|
||||
if (vertexCount % 3 != 0)
|
||||
{
|
||||
errorMsg = TEXT("Invalid vertex count. It must be multiple of 3.");
|
||||
return true;
|
||||
}
|
||||
mesh.Indices.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.Indices.Get()[i] = i;
|
||||
|
||||
// Texture coordinates
|
||||
if (uvs)
|
||||
if (uvs.values)
|
||||
{
|
||||
mesh.UVs.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.UVs.Get()[i] = ToFloat2(uvs[i + firstVertexOffset]);
|
||||
mesh.UVs.Get()[i] = ToFloat2(uvs.get(triangulatedIndices[i]));
|
||||
if (data.ConvertRH)
|
||||
{
|
||||
for (int32 v = 0; v < vertexCount; v++)
|
||||
@@ -582,7 +736,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
}
|
||||
|
||||
// Normals
|
||||
if (data.Options.CalculateNormals || !normals)
|
||||
if (data.Options.CalculateNormals || !normals.values)
|
||||
{
|
||||
if (mesh.GenerateNormals(data.Options.SmoothingNormalsAngle))
|
||||
{
|
||||
@@ -590,11 +744,11 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (normals)
|
||||
else if (normals.values)
|
||||
{
|
||||
mesh.Normals.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.Normals.Get()[i] = ToFloat3(normals[i + firstVertexOffset]);
|
||||
mesh.Normals.Get()[i] = ToFloat3(normals.get(triangulatedIndices[i]));
|
||||
if (data.ConvertRH)
|
||||
{
|
||||
// Mirror normals along the Z axis
|
||||
@@ -604,15 +758,15 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
}
|
||||
|
||||
// Tangents
|
||||
if ((data.Options.CalculateTangents || !tangents) && mesh.UVs.HasItems())
|
||||
if ((data.Options.CalculateTangents || !tangents.values) && mesh.UVs.HasItems())
|
||||
{
|
||||
// Generated after full mesh data conversion
|
||||
}
|
||||
else if (tangents)
|
||||
else if (tangents.values)
|
||||
{
|
||||
mesh.Tangents.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.Tangents.Get()[i] = ToFloat3(tangents[i + firstVertexOffset]);
|
||||
mesh.Tangents.Get()[i] = ToFloat3(tangents.get(triangulatedIndices[i]));
|
||||
if (data.ConvertRH)
|
||||
{
|
||||
// Mirror tangents along the Z axis
|
||||
@@ -658,12 +812,12 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
}
|
||||
|
||||
// Check if has that channel texcoords
|
||||
const auto lightmapUVs = aGeometry->getUVs(inputChannelIndex);
|
||||
if (lightmapUVs)
|
||||
const auto lightmapUVs = geometryData.getUVs(inputChannelIndex);
|
||||
if (lightmapUVs.values)
|
||||
{
|
||||
mesh.LightmapUVs.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.LightmapUVs.Get()[i] = ToFloat2(lightmapUVs[i + firstVertexOffset]);
|
||||
mesh.LightmapUVs.Get()[i] = ToFloat2(lightmapUVs.get(triangulatedIndices[i]));
|
||||
if (data.ConvertRH)
|
||||
{
|
||||
for (int32 v = 0; v < vertexCount; v++)
|
||||
@@ -677,20 +831,20 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
}
|
||||
|
||||
// Vertex Colors
|
||||
if (data.Options.ImportVertexColors && colors)
|
||||
if (data.Options.ImportVertexColors && colors.values)
|
||||
{
|
||||
mesh.Colors.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
mesh.Colors.Get()[i] = ToColor(colors[i + firstVertexOffset]);
|
||||
mesh.Colors.Get()[i] = ToColor(colors.get(triangulatedIndices[i]));
|
||||
}
|
||||
|
||||
// Blend Indices and Blend Weights
|
||||
if (skin && skin->getClusterCount() > 0 && EnumHasAnyFlags(data.Options.ImportTypes, ImportDataTypes::Skeleton))
|
||||
{
|
||||
mesh.BlendIndices.Resize(vertexCount);
|
||||
mesh.BlendWeights.Resize(vertexCount);
|
||||
mesh.BlendIndices.SetAll(Int4::Zero);
|
||||
mesh.BlendWeights.SetAll(Float4::Zero);
|
||||
OPEN_FBX_GET_CACHE_LIST(BlendIndicesCache, blendIndices, positions.values_count);
|
||||
OPEN_FBX_GET_CACHE_LIST(BlendWeightsCache, blendWeights, positions.values_count);
|
||||
data.BlendIndicesCache.SetAll(Int4::Zero);
|
||||
data.BlendWeightsCache.SetAll(Float4::Zero);
|
||||
|
||||
for (int clusterIndex = 0, clusterCount = skin->getClusterCount(); clusterIndex < clusterCount; clusterIndex++)
|
||||
{
|
||||
@@ -718,12 +872,12 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
const double* clusterWeights = cluster->getWeights();
|
||||
for (int j = 0; j < cluster->getIndicesCount(); j++)
|
||||
{
|
||||
int vtxIndex = clusterIndices[j] - firstVertexOffset;
|
||||
int vtxIndex = clusterIndices[j];
|
||||
float vtxWeight = (float)clusterWeights[j];
|
||||
if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= vertexCount)
|
||||
if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= positions.values_count)
|
||||
continue;
|
||||
Int4& indices = mesh.BlendIndices.Get()[vtxIndex];
|
||||
Float4& weights = mesh.BlendWeights.Get()[vtxIndex];
|
||||
Int4& indices = blendIndices[vtxIndex];
|
||||
Float4& weights = blendWeights[vtxIndex];
|
||||
|
||||
for (int32 k = 0; k < 4; k++)
|
||||
{
|
||||
@@ -745,6 +899,16 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
}
|
||||
}
|
||||
|
||||
// Remap blend values to triangulated data
|
||||
mesh.BlendIndices.Resize(vertexCount, false);
|
||||
mesh.BlendWeights.Resize(vertexCount, false);
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
{
|
||||
const int idx = positions.indices[triangulatedIndices[i]];
|
||||
mesh.BlendIndices.Get()[i] = blendIndices[idx];
|
||||
mesh.BlendWeights.Get()[i] = blendWeights[idx];
|
||||
}
|
||||
|
||||
mesh.NormalizeBlendWeights();
|
||||
}
|
||||
|
||||
@@ -756,44 +920,43 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
{
|
||||
const ofbx::BlendShapeChannel* channel = blendShape->getBlendShapeChannel(channelIndex);
|
||||
|
||||
// Use last shape
|
||||
// Use the last shape
|
||||
const int targetShapeCount = channel->getShapeCount();
|
||||
if (targetShapeCount == 0)
|
||||
continue;
|
||||
const ofbx::Shape* shape = channel->getShape(targetShapeCount - 1);
|
||||
|
||||
if (shape->getVertexCount() != aGeometry->getVertexCount())
|
||||
const ofbx::Vec3* shapeVertices = shape->getVertices();
|
||||
const ofbx::Vec3* shapeNormals = shape->getNormals();
|
||||
const int* shapeIndices = shape->getIndices();
|
||||
const int shapeVertexCount = shape->getVertexCount();
|
||||
const int shapeIndexCount = shape->getIndexCount();
|
||||
if (shapeVertexCount != shapeIndexCount)
|
||||
{
|
||||
LOG(Error, "Blend shape '{0}' in mesh '{1}' has different amount of vertices ({2}) than mesh ({3})", String(shape->name), mesh.Name, shape->getVertexCount(), aGeometry->getVertexCount());
|
||||
LOG(Error, "Blend shape '{0}' in mesh '{1}' has different amount of vertices ({2}) and indices ({3})", String(shape->name), mesh.Name, shapeVertexCount, shapeIndexCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
BlendShape& blendShapeData = mesh.BlendShapes.AddOne();
|
||||
blendShapeData.Name = shape->name;
|
||||
blendShapeData.Weight = channel->getShapeCount() > 1 ? (float)(channel->getDeformPercent() / 100.0) : 1.0f;
|
||||
blendShapeData.Vertices.EnsureCapacity(shapeIndexCount);
|
||||
|
||||
blendShapeData.Vertices.Resize(vertexCount);
|
||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||
blendShapeData.Vertices.Get()[i].VertexIndex = i;
|
||||
|
||||
auto shapeVertices = shape->getVertices();
|
||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||
for (int32 i = 0; i < shapeIndexCount; i++)
|
||||
{
|
||||
auto delta = ToFloat3(shapeVertices[i + firstVertexOffset]) - mesh.Positions.Get()[i];
|
||||
blendShapeData.Vertices.Get()[i].PositionDelta = delta;
|
||||
}
|
||||
|
||||
auto shapeNormals = shape->getNormals();
|
||||
for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++)
|
||||
{
|
||||
auto delta = ToFloat3(shapeNormals[i + firstVertexOffset]);
|
||||
if (data.ConvertRH)
|
||||
int shapeIndex = shapeIndices[i];
|
||||
BlendShapeVertex v;
|
||||
v.PositionDelta = ToFloat3(shapeVertices[i]);
|
||||
v.NormalDelta = shapeNormals ? ToFloat3(shapeNormals[i]) : Float3::Zero;
|
||||
for (int32 vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
|
||||
{
|
||||
// Mirror normals along the Z axis
|
||||
delta.Z *= -1.0f;
|
||||
int sourceIndex = positions.indices[triangulatedIndices[vertexIndex]];
|
||||
if (sourceIndex == shapeIndex)
|
||||
{
|
||||
// Add blend shape vertex
|
||||
v.VertexIndex = vertexIndex;
|
||||
blendShapeData.Vertices.Add(v);
|
||||
}
|
||||
}
|
||||
delta = delta - mesh.Normals.Get()[i];
|
||||
blendShapeData.Vertices.Get()[i].NormalDelta = delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -806,7 +969,10 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
for (auto& blendShapeData : mesh.BlendShapes)
|
||||
{
|
||||
for (auto& v : blendShapeData.Vertices)
|
||||
{
|
||||
v.PositionDelta.Z *= -1.0f;
|
||||
v.NormalDelta.Z *= -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,7 +986,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
Swap(mesh.Indices.Get()[i], mesh.Indices.Get()[i + 2]);
|
||||
}
|
||||
|
||||
if ((data.Options.CalculateTangents || !tangents) && mesh.UVs.HasItems())
|
||||
if ((data.Options.CalculateTangents || !tangents.values) && mesh.UVs.HasItems())
|
||||
{
|
||||
if (mesh.GenerateTangents(data.Options.SmoothingTangentsAngle))
|
||||
{
|
||||
@@ -858,7 +1024,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImportMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* aMesh, String& errorMsg, int32 triangleStart, int32 triangleEnd)
|
||||
bool ImportMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* aMesh, String& errorMsg, int partitionIndex)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
@@ -899,7 +1065,7 @@ bool ImportMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
|
||||
// Import mesh data
|
||||
MeshData* meshData = New<MeshData>();
|
||||
if (ProcessMesh(result, data, aMesh, *meshData, errorMsg, triangleStart, triangleEnd))
|
||||
if (ProcessMesh(result, data, aMesh, *meshData, errorMsg, partitionIndex))
|
||||
return true;
|
||||
|
||||
// Link mesh
|
||||
@@ -916,36 +1082,17 @@ bool ImportMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
||||
bool ImportMesh(int32 index, ModelData& result, OpenFbxImporterData& data, String& errorMsg)
|
||||
{
|
||||
const auto aMesh = data.Scene->getMesh(index);
|
||||
const auto aGeometry = aMesh->getGeometry();
|
||||
const auto trianglesCount = aGeometry->getVertexCount() / 3;
|
||||
if (IsMeshInvalid(aMesh))
|
||||
return false;
|
||||
|
||||
if (aMesh->getMaterialCount() < 2 || !aGeometry->getMaterials())
|
||||
const auto& geomData = aMesh->getGeometryData();
|
||||
for (int i = 0; i < geomData.getPartitionCount(); i++)
|
||||
{
|
||||
// Fast path if mesh is using single material for all triangles
|
||||
if (ImportMesh(result, data, aMesh, errorMsg, 0, trianglesCount - 1))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create mesh for each sequence of triangles that share the same material
|
||||
const auto materials = aGeometry->getMaterials();
|
||||
int32 rangeStart = 0;
|
||||
int32 rangeStartVal = materials[rangeStart];
|
||||
for (int32 triangleIndex = 1; triangleIndex < trianglesCount; triangleIndex++)
|
||||
{
|
||||
if (rangeStartVal != materials[triangleIndex])
|
||||
{
|
||||
if (ImportMesh(result, data, aMesh, errorMsg, rangeStart, triangleIndex - 1))
|
||||
return true;
|
||||
const auto& partition = geomData.getPartition(i);
|
||||
if (partition.polygon_count == 0)
|
||||
continue;
|
||||
|
||||
// Start a new range
|
||||
rangeStart = triangleIndex;
|
||||
rangeStartVal = materials[triangleIndex];
|
||||
}
|
||||
}
|
||||
if (ImportMesh(result, data, aMesh, errorMsg, rangeStart, trianglesCount - 1))
|
||||
if (ImportMesh(result, data, aMesh, errorMsg, i))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -962,35 +1109,35 @@ struct AnimInfo
|
||||
|
||||
struct Frame
|
||||
{
|
||||
ofbx::Vec3 Translation;
|
||||
ofbx::Vec3 Rotation;
|
||||
ofbx::Vec3 Scaling;
|
||||
ofbx::DVec3 Translation;
|
||||
ofbx::DVec3 Rotation;
|
||||
ofbx::DVec3 Scaling;
|
||||
};
|
||||
|
||||
void ExtractKeyframePosition(const ofbx::Object* bone, ofbx::Vec3& trans, const Frame& localFrame, Float3& keyframe)
|
||||
void ExtractKeyframePosition(const ofbx::Object* bone, ofbx::DVec3& trans, const Frame& localFrame, Float3& keyframe)
|
||||
{
|
||||
const Matrix frameTrans = ToMatrix(bone->evalLocal(trans, localFrame.Rotation, localFrame.Scaling));
|
||||
keyframe = frameTrans.GetTranslation();
|
||||
}
|
||||
|
||||
void ExtractKeyframeRotation(const ofbx::Object* bone, ofbx::Vec3& trans, const Frame& localFrame, Quaternion& keyframe)
|
||||
void ExtractKeyframeRotation(const ofbx::Object* bone, ofbx::DVec3& trans, const Frame& localFrame, Quaternion& keyframe)
|
||||
{
|
||||
const Matrix frameTrans = ToMatrix(bone->evalLocal(localFrame.Translation, trans, { 1.0, 1.0, 1.0 }));
|
||||
Quaternion::RotationMatrix(frameTrans, keyframe);
|
||||
}
|
||||
|
||||
void ExtractKeyframeScale(const ofbx::Object* bone, ofbx::Vec3& trans, const Frame& localFrame, Float3& keyframe)
|
||||
void ExtractKeyframeScale(const ofbx::Object* bone, ofbx::DVec3& trans, const Frame& localFrame, Float3& keyframe)
|
||||
{
|
||||
// Fix empty scale case
|
||||
if (Math::IsZero(trans.x) && Math::IsZero(trans.y) && Math::IsZero(trans.z))
|
||||
trans = { 1.0, 1.0, 1.0 };
|
||||
|
||||
const Matrix frameTrans = ToMatrix(bone->evalLocal(localFrame.Translation, localFrame.Rotation, trans));
|
||||
const Matrix frameTrans = ToMatrix(bone->evalLocal(localFrame.Translation, { 0.0, 0.0, 0.0 }, trans));
|
||||
keyframe = frameTrans.GetScaleVector();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curve, AnimInfo& info, void (*ExtractKeyframe)(const ofbx::Object*, ofbx::Vec3&, const Frame&, T&))
|
||||
void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curve, AnimInfo& info, void (*ExtractKeyframe)(const ofbx::Object*, ofbx::DVec3&, const Frame&, T&))
|
||||
{
|
||||
if (curveNode == nullptr)
|
||||
return;
|
||||
@@ -1008,7 +1155,7 @@ void ImportCurve(const ofbx::AnimationCurveNode* curveNode, LinearCurve<T>& curv
|
||||
|
||||
key.Time = (float)i;
|
||||
|
||||
ofbx::Vec3 trans = curveNode->getNodeLocalTransform(t);
|
||||
ofbx::DVec3 trans = curveNode->getNodeLocalTransform(t);
|
||||
ExtractKeyframe(bone, trans, localFrame, key.Value);
|
||||
}
|
||||
}
|
||||
@@ -1125,21 +1272,26 @@ bool ModelTool::ImportDataOpenFBX(const String& path, ModelData& data, Options&
|
||||
errorMsg = TEXT("Cannot load file.");
|
||||
return true;
|
||||
}
|
||||
ofbx::u64 loadFlags = 0;
|
||||
ofbx::LoadFlags loadFlags = ofbx::LoadFlags::NONE;
|
||||
if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry))
|
||||
{
|
||||
loadFlags |= (ofbx::u64)ofbx::LoadFlags::TRIANGULATE;
|
||||
if (!options.ImportBlendShapes)
|
||||
loadFlags |= (ofbx::u64)ofbx::LoadFlags::IGNORE_BLEND_SHAPES;
|
||||
loadFlags |= ofbx::LoadFlags::IGNORE_BLEND_SHAPES;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadFlags |= (ofbx::u64)ofbx::LoadFlags::IGNORE_GEOMETRY | (ofbx::u64)ofbx::LoadFlags::IGNORE_BLEND_SHAPES;
|
||||
loadFlags |= ofbx::LoadFlags::IGNORE_GEOMETRY | ofbx::LoadFlags::IGNORE_BLEND_SHAPES;
|
||||
}
|
||||
if (EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Materials))
|
||||
loadFlags |= ofbx::LoadFlags::IGNORE_MATERIALS;
|
||||
if (EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Textures))
|
||||
loadFlags |= ofbx::LoadFlags::IGNORE_TEXTURES;
|
||||
if (EnumHasNoneFlags(options.ImportTypes, ImportDataTypes::Animations))
|
||||
loadFlags |= ofbx::LoadFlags::IGNORE_ANIMATIONS;
|
||||
ofbx::IScene* scene;
|
||||
{
|
||||
PROFILE_CPU_NAMED("ofbx::load");
|
||||
scene = ofbx::load(fileData.Get(), fileData.Count(), loadFlags);
|
||||
scene = ofbx::load(fileData.Get(), fileData.Count(), (ofbx::u16)loadFlags);
|
||||
}
|
||||
if (!scene)
|
||||
{
|
||||
|
||||
@@ -1349,31 +1349,31 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
||||
mesh->BlendIndices.SetAll(indices);
|
||||
mesh->BlendWeights.SetAll(weights);
|
||||
}
|
||||
#if BUILD_DEBUG
|
||||
else
|
||||
{
|
||||
auto& indices = mesh->BlendIndices;
|
||||
for (int32 j = 0; j < indices.Count(); j++)
|
||||
{
|
||||
const int32 min = indices[j].MinValue();
|
||||
const int32 max = indices[j].MaxValue();
|
||||
const Int4 ij = indices.Get()[j];
|
||||
const int32 min = ij.MinValue();
|
||||
const int32 max = ij.MaxValue();
|
||||
if (min < 0 || max >= data.Skeleton.Bones.Count())
|
||||
{
|
||||
LOG(Warning, "Imported mesh \'{0}\' has invalid blend indices. It may result in invalid rendering.", mesh->Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto& weights = mesh->BlendWeights;
|
||||
for (int32 j = 0; j < weights.Count(); j++)
|
||||
{
|
||||
const float sum = weights[j].SumValues();
|
||||
const float sum = weights.Get()[j].SumValues();
|
||||
if (Math::Abs(sum - 1.0f) > ZeroTolerance)
|
||||
{
|
||||
LOG(Warning, "Imported mesh \'{0}\' has invalid blend weights. It may result in invalid rendering.", mesh->Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Animations))
|
||||
|
||||
Reference in New Issue
Block a user