Merge remote-tracking branch 'origin/master' into 1.12
# Conflicts: # Content/Editor/MaterialTemplates/Deformable.shader # Flax.flaxproj # Source/Engine/Content/Content.h # Source/Engine/Serialization/JsonTools.cpp
This commit is contained in:
@@ -84,6 +84,11 @@ bool BinaryAsset::Init(AssetInitData& initData)
|
||||
{
|
||||
asset->_dependantAssets.Add(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dependency is not yet loaded to keep track this link to act when it's loaded
|
||||
Content::onAssetDepend(this, e.First);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -336,9 +341,7 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool
|
||||
// Force-resolve storage (asset at that path could be not yet loaded into registry)
|
||||
storage = ContentStorageManager::GetStorage(filePath);
|
||||
}
|
||||
|
||||
// Check if can perform write operation to the asset container
|
||||
if (storage && !storage->AllowDataModifications())
|
||||
if (storage && storage->IsReadOnly())
|
||||
{
|
||||
LOG(Warning, "Cannot write to the asset storage container.");
|
||||
return true;
|
||||
@@ -635,7 +638,7 @@ void BinaryAsset::onRename(const StringView& newPath)
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// We don't support packages now
|
||||
ASSERT(!Storage->IsPackage() && Storage->AllowDataModifications() && Storage->GetEntriesCount() == 1);
|
||||
ASSERT(!Storage->IsPackage() && !Storage->IsReadOnly() && Storage->GetEntriesCount() == 1);
|
||||
|
||||
// Rename storage
|
||||
Storage->OnRename(newPath);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API BinaryAsset : public Asset
|
||||
{
|
||||
DECLARE_ASSET_HEADER(BinaryAsset);
|
||||
friend Content;
|
||||
protected:
|
||||
AssetHeader _header;
|
||||
FlaxStorageReference _storageRef; // Allow asset to have missing storage reference but only before asset is loaded or if it's virtual
|
||||
|
||||
@@ -415,9 +415,7 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
|
||||
// Check if need to resolve any collisions
|
||||
if (duplicatedEntries.HasItems())
|
||||
{
|
||||
// Check if cannot resolve collision for that container (it must allow to write to)
|
||||
// TODO: we could support packages as well but don't have to do it now, maybe in future
|
||||
if (storage->AllowDataModifications() == false)
|
||||
if (storage->IsReadOnly())
|
||||
{
|
||||
LOG(Error, "Cannot register \'{0}\'. Founded duplicated asset at \'{1}\' but storage container doesn't allow data modifications.", storagePath, info.Path);
|
||||
return;
|
||||
|
||||
@@ -98,6 +98,9 @@ namespace
|
||||
DateTime LastWorkspaceDiscovery;
|
||||
CriticalSection WorkspaceDiscoveryLocker;
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
Dictionary<Guid, HashSet<BinaryAsset*>> PendingDependencies;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
@@ -184,6 +187,9 @@ void ContentService::Update()
|
||||
{
|
||||
auto asset = LoadedAssetsToInvoke.Dequeue();
|
||||
asset->onLoaded_MainThread();
|
||||
#if USE_EDITOR
|
||||
Content::onAddDependencies(asset);
|
||||
#endif
|
||||
}
|
||||
LoadedAssetsToInvokeLocker.Unlock();
|
||||
}
|
||||
@@ -1091,10 +1097,17 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
|
||||
FileSystem::DeleteFile(tmpPath);
|
||||
|
||||
// Reload storage
|
||||
if (auto storage = ContentStorageManager::GetStorage(dstPath, false))
|
||||
auto storage = ContentStorageManager::GetStorage(dstPath, false);
|
||||
if (storage && storage->IsLoaded())
|
||||
{
|
||||
storage->Reload();
|
||||
}
|
||||
else if (auto dependencies = PendingDependencies.TryGet(dstId))
|
||||
{
|
||||
// Destination storage is not loaded but there are other assets that depend on it so update them
|
||||
for (const auto& e : *dependencies)
|
||||
e.Item->OnDependencyModified(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1311,6 +1324,9 @@ void Content::tryCallOnLoaded(Asset* asset)
|
||||
{
|
||||
LoadedAssetsToInvoke.RemoveAtKeepOrder(index);
|
||||
asset->onLoaded_MainThread();
|
||||
#if USE_EDITOR
|
||||
onAddDependencies(asset);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,6 +1344,10 @@ void Content::onAssetUnload(Asset* asset)
|
||||
Assets.Remove(asset->GetID());
|
||||
UnloadQueue.Remove(asset);
|
||||
LoadedAssetsToInvoke.Remove(asset);
|
||||
#if USE_EDITOR
|
||||
for (auto& e : PendingDependencies)
|
||||
e.Value.Remove(asset);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId)
|
||||
@@ -1335,8 +1355,42 @@ void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId
|
||||
ScopeLock locker(AssetsLocker);
|
||||
Assets.Remove(oldId);
|
||||
Assets.Add(newId, asset);
|
||||
#if USE_EDITOR
|
||||
if (PendingDependencies.ContainsKey(oldId))
|
||||
{
|
||||
auto deps = MoveTemp(PendingDependencies[oldId]);
|
||||
PendingDependencies.Remove(oldId);
|
||||
PendingDependencies.Add(newId, MoveTemp(deps));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Content::onAssetDepend(BinaryAsset* asset, const Guid& otherId)
|
||||
{
|
||||
ScopeLock locker(AssetsLocker);
|
||||
PendingDependencies[otherId].Add(asset);
|
||||
}
|
||||
|
||||
void Content::onAddDependencies(Asset* asset)
|
||||
{
|
||||
auto it = PendingDependencies.Find(asset->GetID());
|
||||
if (it.IsNotEnd())
|
||||
{
|
||||
auto& dependencies = it->Value;
|
||||
auto binaryAsset = Asset::Cast<BinaryAsset>(asset);
|
||||
if (binaryAsset)
|
||||
{
|
||||
for (const auto& e : dependencies)
|
||||
binaryAsset->_dependantAssets.Add(e.Item);
|
||||
}
|
||||
PendingDependencies.Remove(it);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Content::IsAssetTypeIdInvalid(const ScriptingTypeHandle& type, const ScriptingTypeHandle& assetType)
|
||||
{
|
||||
// Skip if no restrictions for the type
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
class Engine;
|
||||
class FlaxFile;
|
||||
class BinaryAsset;
|
||||
class IAssetFactory;
|
||||
class AssetsCache;
|
||||
|
||||
@@ -395,6 +396,12 @@ private:
|
||||
static void onAssetLoaded(Asset* asset);
|
||||
static void onAssetUnload(Asset* asset);
|
||||
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
|
||||
#if USE_EDITOR
|
||||
friend BinaryAsset;
|
||||
friend class ContentService;
|
||||
static void onAssetDepend(BinaryAsset* asset, const Guid& otherId);
|
||||
static void onAddDependencies(Asset* asset);
|
||||
#endif
|
||||
static void deleteFileSafety(const StringView& path, const Guid* id = nullptr);
|
||||
|
||||
// Internal bindings
|
||||
|
||||
@@ -28,7 +28,7 @@ bool BinaryAssetFactoryBase::Init(BinaryAsset* asset)
|
||||
#if USE_EDITOR
|
||||
// Check if need to perform data conversion to the newer version (only in Editor)
|
||||
const auto upgrader = GetUpgrader();
|
||||
if (storage->AllowDataModifications() && upgrader && upgrader->ShouldUpgrade(initData.SerializedVersion))
|
||||
if (!storage->IsReadOnly() && upgrader && upgrader->ShouldUpgrade(initData.SerializedVersion))
|
||||
{
|
||||
const auto startTime = DateTime::NowUTC();
|
||||
const AssetInfo info(asset->GetID(), asset->GetTypeName(), storage->GetPath());
|
||||
|
||||
@@ -80,7 +80,6 @@ private:
|
||||
auto asset = Asset.Get();
|
||||
if (asset)
|
||||
{
|
||||
asset->Locker.Lock();
|
||||
Task* task = (Task*)Platform::AtomicRead(&asset->_loadingTask);
|
||||
if (task)
|
||||
{
|
||||
@@ -99,7 +98,6 @@ private:
|
||||
task = task->GetContinueWithTask();
|
||||
} while (task);
|
||||
}
|
||||
asset->Locker.Unlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
// [FlaxStorage]
|
||||
String ToString() const override;
|
||||
bool IsPackage() const override;
|
||||
bool AllowDataModifications() const override;
|
||||
bool IsReadOnly() const override;
|
||||
bool HasAsset(const Guid& id) const override;
|
||||
bool HasAsset(const AssetInfo& info) const override;
|
||||
int32 GetEntriesCount() const override;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
// [FlaxStorage]
|
||||
String ToString() const override;
|
||||
bool IsPackage() const override;
|
||||
bool AllowDataModifications() const override;
|
||||
bool IsReadOnly() const override;
|
||||
bool HasAsset(const Guid& id) const override;
|
||||
bool HasAsset(const AssetInfo& info) const override;
|
||||
int32 GetEntriesCount() const override;
|
||||
|
||||
@@ -811,7 +811,7 @@ bool FlaxStorage::ChangeAssetID(Entry& e, const Guid& newId)
|
||||
|
||||
// TODO: validate entry
|
||||
ASSERT(newId.IsValid());
|
||||
ASSERT(AllowDataModifications());
|
||||
ASSERT(!IsReadOnly());
|
||||
|
||||
LOG(Info, "Changing asset \'{0}\' id to \'{1}\' (storage: \'{2}\')", e.ID, newId, _path);
|
||||
|
||||
@@ -885,7 +885,7 @@ bool FlaxStorage::ChangeAssetID(Entry& e, const Guid& newId)
|
||||
|
||||
FlaxChunk* FlaxStorage::AllocateChunk()
|
||||
{
|
||||
if (AllowDataModifications())
|
||||
if (!IsReadOnly())
|
||||
{
|
||||
PROFILE_MEM(ContentFiles);
|
||||
auto chunk = New<FlaxChunk>();
|
||||
@@ -1135,7 +1135,7 @@ bool FlaxStorage::Create(WriteStream* stream, Span<AssetInitData> assets, const
|
||||
bool FlaxStorage::Save(const AssetInitData& data, bool silentMode)
|
||||
{
|
||||
// Check if can modify the storage
|
||||
if (!AllowDataModifications())
|
||||
if (IsReadOnly())
|
||||
return true;
|
||||
|
||||
// Note: we support saving only single asset, to save more assets in single package use FlaxStorage::Create(..)
|
||||
@@ -1546,7 +1546,7 @@ void FlaxStorage::Tick(double time)
|
||||
|
||||
void FlaxStorage::OnRename(const StringView& newPath)
|
||||
{
|
||||
ASSERT(AllowDataModifications());
|
||||
ASSERT(!IsReadOnly());
|
||||
_path = newPath;
|
||||
}
|
||||
|
||||
@@ -1568,9 +1568,9 @@ bool FlaxFile::IsPackage() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlaxFile::AllowDataModifications() const
|
||||
bool FlaxFile::IsReadOnly() const
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlaxFile::HasAsset(const Guid& id) const
|
||||
@@ -1648,9 +1648,9 @@ bool FlaxPackage::IsPackage() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlaxPackage::AllowDataModifications() const
|
||||
bool FlaxPackage::IsReadOnly() const
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlaxPackage::HasAsset(const Guid& id) const
|
||||
|
||||
@@ -276,9 +276,9 @@ public:
|
||||
virtual bool IsPackage() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whenever storage container allows the data modifications.
|
||||
/// Checks whenever storage container doesn't allow modifications.
|
||||
/// </summary>
|
||||
virtual bool AllowDataModifications() const = 0;
|
||||
virtual bool IsReadOnly() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified asset exists in this container.
|
||||
|
||||
@@ -253,6 +253,16 @@ namespace FlaxEngine
|
||||
return new Rectangle(Location + new Float2(x, y), Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make offseted rectangle
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset (will be applied to X- and Y- axis).</param>
|
||||
/// <returns>Offseted rectangle.</returns>
|
||||
public Rectangle MakeOffsetted(float offset)
|
||||
{
|
||||
return new Rectangle(Location + offset, Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make offseted rectangle
|
||||
/// </summary>
|
||||
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE float GetTotalSeconds() const
|
||||
{
|
||||
return static_cast<float>(Ticks) / TicksPerSecond;
|
||||
return (float)((double)Ticks / TicksPerSecond);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -124,7 +124,7 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName, bool static
|
||||
VariantType::VariantType(Types type, const ScriptingType& sType)
|
||||
: VariantType(type)
|
||||
{
|
||||
SetTypeName(sType);
|
||||
SetTypeName(sType.Fullname, sType.Module->CanReload);
|
||||
}
|
||||
|
||||
VariantType::VariantType(Types type, const MClass* klass)
|
||||
@@ -171,7 +171,7 @@ VariantType::VariantType(const StringAnsiView& typeName)
|
||||
// Check case for array
|
||||
if (typeName.EndsWith(StringAnsiView("[]"), StringSearchCase::CaseSensitive))
|
||||
{
|
||||
new(this) VariantType(Array, StringAnsiView(typeName.Get(), typeName.Length() - 2));
|
||||
new(this) VariantType(Array, typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,10 +68,12 @@ API_STRUCT(InBuild) struct FLAXENGINE_API VariantType
|
||||
|
||||
MAX,
|
||||
#if USE_LARGE_WORLDS
|
||||
Real = Double,
|
||||
Vector2 = Double2,
|
||||
Vector3 = Double3,
|
||||
Vector4 = Double4,
|
||||
#else
|
||||
Real = Float,
|
||||
Vector2 = Float2,
|
||||
Vector3 = Float3,
|
||||
Vector4 = Float4,
|
||||
|
||||
@@ -1197,11 +1197,17 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static byte GetMethodParameterIsOut(ManagedHandle methodHandle, int parameterNum)
|
||||
internal static UInt64 GetMethodParameterIsOut(ManagedHandle methodHandle)
|
||||
{
|
||||
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
|
||||
ParameterInfo parameterInfo = methodHolder.method.GetParameters()[parameterNum];
|
||||
return (byte)(parameterInfo.IsOut ? 1 : 0);
|
||||
var parameters = methodHolder.method.GetParameters();
|
||||
UInt64 result = 0;
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameters[i].IsOut)
|
||||
result |= 1ul << i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
|
||||
@@ -198,7 +198,7 @@ void Foliage::DrawCluster(DrawContext& context, FoliageCluster* cluster, DrawCal
|
||||
sphere.Center -= context.ViewOrigin;
|
||||
if (Float3::Distance(context.LodView.Position, sphere.Center) - (float)sphere.Radius < instance.CullDistance &&
|
||||
context.RenderContext.View.CullingFrustum.Intersects(sphere) &&
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq)
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, (float)sphere.Radius, context.RenderContext.View) * context.ViewScreenSizeSq >= context.MinObjectPixelSizeSq)
|
||||
{
|
||||
const auto modelFrame = instance.DrawState.PrevFrame + 1;
|
||||
|
||||
|
||||
@@ -428,6 +428,20 @@ namespace FlaxEngine
|
||||
|
||||
partial struct VertexElement : IEquatable<VertexElement>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the vertex element description.
|
||||
/// </summary>
|
||||
/// <param name="type">Element type.</param>
|
||||
/// <param name="format">Data format.</param>
|
||||
public VertexElement(Types type, PixelFormat format)
|
||||
{
|
||||
Type = type;
|
||||
Slot = 0;
|
||||
Offset = 0;
|
||||
PerInstance = 0;
|
||||
Format = format;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the vertex element description.
|
||||
/// </summary>
|
||||
|
||||
@@ -52,13 +52,10 @@ IMaterial::BindParameters::BindParameters(::GPUContext* context, const ::RenderC
|
||||
}
|
||||
|
||||
IMaterial::BindParameters::BindParameters(::GPUContext* context, const ::RenderContext& renderContext, const ::DrawCall& drawCall, bool instanced)
|
||||
: GPUContext(context)
|
||||
, RenderContext(renderContext)
|
||||
, DrawCall(&drawCall)
|
||||
, Time(Time::Draw.UnscaledTime.GetTotalSeconds())
|
||||
, ScaledTime(Time::Draw.Time.GetTotalSeconds())
|
||||
, Instanced(instanced)
|
||||
: BindParameters(context, renderContext)
|
||||
{
|
||||
DrawCall = &drawCall;
|
||||
Instanced = instanced;
|
||||
}
|
||||
|
||||
GPUConstantBuffer* IMaterial::BindParameters::PerViewConstants = nullptr;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
@@ -17,13 +18,14 @@ namespace FlaxEngine
|
||||
{
|
||||
private Span<byte> _data;
|
||||
private PixelFormat _format;
|
||||
private int _stride;
|
||||
private int _stride, _count;
|
||||
private readonly PixelFormatSampler _sampler;
|
||||
|
||||
internal Stream(Span<byte> data, PixelFormat format, int stride)
|
||||
internal Stream(Span<byte> data, PixelFormat format, int stride, int count)
|
||||
{
|
||||
_data = data;
|
||||
_stride = stride;
|
||||
_count = count;
|
||||
if (PixelFormatSampler.Get(format, out _sampler))
|
||||
{
|
||||
_format = format;
|
||||
@@ -52,7 +54,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets the count of the items in the stride.
|
||||
/// </summary>
|
||||
public int Count => _data.Length / _stride;
|
||||
public int Count => _count;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if stream is valid.
|
||||
@@ -76,6 +78,10 @@ namespace FlaxEngine
|
||||
/// <returns>Loaded value.</returns>
|
||||
public int GetInt(int index)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
return (int)_sampler.Read(data + index * _stride).X;
|
||||
}
|
||||
@@ -87,6 +93,10 @@ namespace FlaxEngine
|
||||
/// <returns>Loaded value.</returns>
|
||||
public float GetFloat(int index)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
return _sampler.Read(data + index * _stride).X;
|
||||
}
|
||||
@@ -98,6 +108,10 @@ namespace FlaxEngine
|
||||
/// <returns>Loaded value.</returns>
|
||||
public Float2 GetFloat2(int index)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
return new Float2(_sampler.Read(data + index * _stride));
|
||||
}
|
||||
@@ -109,6 +123,10 @@ namespace FlaxEngine
|
||||
/// <returns>Loaded value.</returns>
|
||||
public Float3 GetFloat3(int index)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
return new Float3(_sampler.Read(data + index * _stride));
|
||||
}
|
||||
@@ -120,6 +138,10 @@ namespace FlaxEngine
|
||||
/// <returns>Loaded value.</returns>
|
||||
public Float4 GetFloat4(int index)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
return _sampler.Read(data + index * _stride);
|
||||
}
|
||||
@@ -131,9 +153,13 @@ namespace FlaxEngine
|
||||
/// <param name="value">Value to assign.</param>
|
||||
public void SetInt(int index, int value)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
var v = new Float4(value);
|
||||
fixed (byte* data = _data)
|
||||
_sampler.Write(data + index * _stride, ref v);
|
||||
_sampler.Write(data + index * _stride, &v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -143,9 +169,13 @@ namespace FlaxEngine
|
||||
/// <param name="value">Value to assign.</param>
|
||||
public void SetFloat(int index, float value)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
var v = new Float4(value);
|
||||
fixed (byte* data = _data)
|
||||
_sampler.Write(data + index * _stride, ref v);
|
||||
_sampler.Write(data + index * _stride, &v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -155,9 +185,13 @@ namespace FlaxEngine
|
||||
/// <param name="value">Value to assign.</param>
|
||||
public void SetFloat2(int index, Float2 value)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
var v = new Float4(value, 0.0f, 0.0f);
|
||||
fixed (byte* data = _data)
|
||||
_sampler.Write(data + index * _stride, ref v);
|
||||
_sampler.Write(data + index * _stride, &v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -167,9 +201,13 @@ namespace FlaxEngine
|
||||
/// <param name="value">Value to assign.</param>
|
||||
public void SetFloat3(int index, Float3 value)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
var v = new Float4(value, 0.0f);
|
||||
fixed (byte* data = _data)
|
||||
_sampler.Write(data + index * _stride, ref v);
|
||||
_sampler.Write(data + index * _stride, &v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,8 +217,12 @@ namespace FlaxEngine
|
||||
/// <param name="value">Value to assign.</param>
|
||||
public void SetFloat4(int index, Float4 value)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
fixed (byte* data = _data)
|
||||
_sampler.Write(data + index * _stride, ref value);
|
||||
_sampler.Write(data + index * _stride, &value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -190,6 +232,10 @@ namespace FlaxEngine
|
||||
/// <param name="data">Pointer to the source data.</param>
|
||||
public void SetLinear(IntPtr data)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
new Span<byte>(data.ToPointer(), _data.Length).CopyTo(_data);
|
||||
}
|
||||
|
||||
@@ -199,6 +245,10 @@ namespace FlaxEngine
|
||||
/// <param name="src">The source <see cref="T:System.Span`1"/>.</param>
|
||||
public void Set(Span<Float2> src)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32_Float))
|
||||
{
|
||||
src.CopyTo(MemoryMarshal.Cast<byte, Float2>(_data));
|
||||
@@ -211,7 +261,7 @@ namespace FlaxEngine
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var v = new Float4(src[i], 0.0f, 0.0f);
|
||||
_sampler.Write(data + i * _stride, ref v);
|
||||
_sampler.Write(data + i * _stride, &v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +273,10 @@ namespace FlaxEngine
|
||||
/// <param name="src">The source <see cref="T:System.Span`1"/>.</param>
|
||||
public void Set(Span<Float3> src)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32B32_Float))
|
||||
{
|
||||
src.CopyTo(MemoryMarshal.Cast<byte, Float3>(_data));
|
||||
@@ -235,7 +289,7 @@ namespace FlaxEngine
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var v = new Float4(src[i], 0.0f);
|
||||
_sampler.Write(data + i * _stride, ref v);
|
||||
_sampler.Write(data + i * _stride, &v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,6 +301,10 @@ namespace FlaxEngine
|
||||
/// <param name="src">The source <see cref="T:System.Span`1"/>.</param>
|
||||
public void Set(Span<Color> src)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32B32A32_Float))
|
||||
{
|
||||
src.CopyTo(MemoryMarshal.Cast<byte, Color>(_data));
|
||||
@@ -259,7 +317,7 @@ namespace FlaxEngine
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var v = (Float4)src[i];
|
||||
_sampler.Write(data + i * _stride, ref v);
|
||||
_sampler.Write(data + i * _stride, &v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,6 +329,10 @@ namespace FlaxEngine
|
||||
/// <param name="src">The source <see cref="T:System.Span`1"/>.</param>
|
||||
public void Set(Span<uint> src)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32_UInt))
|
||||
{
|
||||
src.CopyTo(MemoryMarshal.Cast<byte, uint>(_data));
|
||||
@@ -292,7 +354,7 @@ namespace FlaxEngine
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var v = new Float4(src[i]);
|
||||
_sampler.Write(data + i * _stride, ref v);
|
||||
_sampler.Write(data + i * _stride, &v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,6 +366,10 @@ namespace FlaxEngine
|
||||
/// <param name="dst">The destination <see cref="T:System.Span`1" />.</param>
|
||||
public void CopyTo(Span<Float2> dst)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32_Float))
|
||||
{
|
||||
_data.CopyTo(MemoryMarshal.Cast<Float2, byte>(dst));
|
||||
@@ -325,6 +391,10 @@ namespace FlaxEngine
|
||||
/// <param name="dst">The destination <see cref="T:System.Span`1" />.</param>
|
||||
public void CopyTo(Span<Float3> dst)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32B32_Float))
|
||||
{
|
||||
_data.CopyTo(MemoryMarshal.Cast<Float3, byte>(dst));
|
||||
@@ -346,6 +416,10 @@ namespace FlaxEngine
|
||||
/// <param name="dst">The destination <see cref="T:System.Span`1" />.</param>
|
||||
public void CopyTo(Span<Color> dst)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32G32B32A32_Float))
|
||||
{
|
||||
_data.CopyTo(MemoryMarshal.Cast<Color, byte>(dst));
|
||||
@@ -367,6 +441,10 @@ namespace FlaxEngine
|
||||
/// <param name="dst">The destination <see cref="T:System.Span`1" />.</param>
|
||||
public void CopyTo(Span<uint> dst)
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (_data == null)
|
||||
throw new NullReferenceException("Missing stream data. Ensure to allocate mesh buffer or check for its existence.");
|
||||
#endif
|
||||
if (IsLinear(PixelFormat.R32_UInt))
|
||||
{
|
||||
_data.CopyTo(MemoryMarshal.Cast<uint, byte>(dst));
|
||||
@@ -390,6 +468,17 @@ namespace FlaxEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if stream is valid.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to check.</param>
|
||||
/// <returns>True if stream is valid, otherwise false.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator bool(Stream stream)
|
||||
{
|
||||
return stream.IsValid;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[][] _data = new byte[(int)MeshBufferType.MAX][];
|
||||
@@ -510,19 +599,19 @@ namespace FlaxEngine
|
||||
bool use16BitIndexBuffer = false;
|
||||
IntPtr[] vbData = new IntPtr[3];
|
||||
GPUVertexLayout[] vbLayout = new GPUVertexLayout[3];
|
||||
if (_data[VB0] != null)
|
||||
if (_data[VB0] != null && _data[VB0].Length != 0)
|
||||
{
|
||||
vbData[0] = dataPtr[VB0];
|
||||
vbLayout[0] = _layouts[VB0];
|
||||
vertices = (uint)_data[VB0].Length / _layouts[VB0].Stride;
|
||||
}
|
||||
if (_data[VB1] != null)
|
||||
if (_data[VB1] != null && _data[VB1].Length != 0)
|
||||
{
|
||||
vbData[1] = dataPtr[VB1];
|
||||
vbLayout[1] = _layouts[VB1];
|
||||
vertices = (uint)_data[VB1].Length / _layouts[VB1].Stride;
|
||||
}
|
||||
if (_data[VB2] != null)
|
||||
if (_data[VB2] != null && _data[VB2].Length != 0)
|
||||
{
|
||||
vbData[2] = dataPtr[VB2];
|
||||
vbLayout[2] = _layouts[VB2];
|
||||
@@ -576,15 +665,16 @@ namespace FlaxEngine
|
||||
{
|
||||
Span<byte> data = new Span<byte>();
|
||||
PixelFormat format = PixelFormat.Unknown;
|
||||
int stride = 0;
|
||||
int stride = 0, count = 0;
|
||||
var ib = _data[(int)MeshBufferType.Index];
|
||||
if (ib != null)
|
||||
{
|
||||
data = ib;
|
||||
format = _formats[(int)MeshBufferType.Index];
|
||||
stride = PixelFormatExtensions.SizeInBytes(format);
|
||||
count = data.Length / stride;
|
||||
}
|
||||
return new Stream(data, format, stride);
|
||||
return new Stream(data, format, stride, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -596,7 +686,7 @@ namespace FlaxEngine
|
||||
{
|
||||
Span<byte> data = new Span<byte>();
|
||||
PixelFormat format = PixelFormat.Unknown;
|
||||
int stride = 0;
|
||||
int stride = 0, count = 0;
|
||||
for (int vbIndex = 0; vbIndex < 3 && format == PixelFormat.Unknown; vbIndex++)
|
||||
{
|
||||
int idx = vbIndex + 1;
|
||||
@@ -611,11 +701,12 @@ namespace FlaxEngine
|
||||
data = new Span<byte>(vb).Slice(e.Offset);
|
||||
format = e.Format;
|
||||
stride = (int)layout.Stride;
|
||||
count = vb.Length / stride;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Stream(data, format, stride);
|
||||
return new Stream(data, format, stride, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -722,6 +813,16 @@ namespace FlaxEngine
|
||||
set => SetStreamFloat3(VertexElement.Types.Normal, value, PackNormal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex tangent vectors (unpacked, normalized). Null if <see cref="VertexElement.Types.Tangent"/> does not exist in vertex buffers of the mesh.
|
||||
/// </summary>
|
||||
/// <remarks>Uses <see cref="Tangent"/> stream to read or write data to the vertex buffer.</remarks>
|
||||
public Float3[] Tangents
|
||||
{
|
||||
get => GetStreamFloat3(VertexElement.Types.Tangent, UnpackNormal);
|
||||
set => SetStreamFloat3(VertexElement.Types.Tangent, value, PackNormal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex UVs (texcoord channel 0). Null if <see cref="VertexElement.Types.TexCoord"/> does not exist in vertex buffers of the mesh.
|
||||
/// </summary>
|
||||
@@ -732,6 +833,70 @@ namespace FlaxEngine
|
||||
set => SetStreamFloat2(VertexElement.Types.TexCoord, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates normal vectors for all vertices.
|
||||
/// </summary>
|
||||
public void ComputeNormals()
|
||||
{
|
||||
var positions = Position();
|
||||
var indices = Index();
|
||||
if (!positions)
|
||||
throw new Exception("Cannot compute tangents without positions.");
|
||||
if (!indices)
|
||||
throw new Exception("Cannot compute tangents without indices.");
|
||||
if (!Normal())
|
||||
throw new Exception("Cannot compute tangents without Normal vertex element.");
|
||||
var vertexCount = positions.Count;
|
||||
if (vertexCount == 0)
|
||||
return;
|
||||
var indexCount = indices.Count;
|
||||
|
||||
// Compute per-face normals but store them per-vertex
|
||||
var normals = new Float3[vertexCount];
|
||||
for (int i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
var i1 = indices.GetInt(i + 0);
|
||||
var i2 = indices.GetInt(i + 1);
|
||||
var i3 = indices.GetInt(i + 2);
|
||||
Float3 v1 = positions.GetFloat3(i1);
|
||||
Float3 v2 = positions.GetFloat3(i2);
|
||||
Float3 v3 = positions.GetFloat3(i3);
|
||||
Float3 n = Float3.Cross((v2 - v1), (v3 - v1)).Normalized;
|
||||
|
||||
normals[i1] += n;
|
||||
normals[i2] += n;
|
||||
normals[i3] += n;
|
||||
}
|
||||
|
||||
// Average normals
|
||||
for (int i = 0; i < normals.Length; i++)
|
||||
normals[i] = normals[i].Normalized;
|
||||
|
||||
// Write back to the buffer
|
||||
Normals = normals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates tangent vectors for all vertices based on normals.
|
||||
/// </summary>
|
||||
public void ComputeTangents()
|
||||
{
|
||||
var normals = Normal();
|
||||
var tangents = Tangent();
|
||||
if (!normals)
|
||||
throw new Exception("Cannot compute tangents without normals.");
|
||||
if (!tangents)
|
||||
throw new Exception("Cannot compute tangents without Tangent vertex element.");
|
||||
var count = normals.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Float3 normal = normals.GetFloat3(i);
|
||||
UnpackNormal(ref normal);
|
||||
RenderTools.CalculateTangentFrame(out var n, out var t, ref normal);
|
||||
tangents.SetFloat4(i, t);
|
||||
}
|
||||
}
|
||||
|
||||
private uint[] GetStreamUInt(Stream stream)
|
||||
{
|
||||
uint[] result = null;
|
||||
|
||||
@@ -25,10 +25,10 @@ public:
|
||||
private:
|
||||
Span<byte> _data;
|
||||
PixelFormat _format;
|
||||
int32 _stride;
|
||||
int32 _stride, _count;
|
||||
PixelFormatSampler _sampler;
|
||||
|
||||
Stream(Span<byte> data, PixelFormat format, int32 stride);
|
||||
Stream(Span<byte> data, PixelFormat format, int32 stride, int32 count);
|
||||
|
||||
public:
|
||||
Span<byte> GetData() const;
|
||||
@@ -38,6 +38,11 @@ public:
|
||||
bool IsValid() const;
|
||||
bool IsLinear(PixelFormat expectedFormat) const;
|
||||
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
FORCE_INLINE int32 GetInt(int32 index) const
|
||||
{
|
||||
ASSERT_LOW_LAYER(index * _stride < _data.Length());
|
||||
|
||||
@@ -38,10 +38,11 @@ namespace
|
||||
#endif
|
||||
}
|
||||
|
||||
MeshAccessor::Stream::Stream(Span<byte> data, PixelFormat format, int32 stride)
|
||||
MeshAccessor::Stream::Stream(Span<byte> data, PixelFormat format, int32 stride, int32 count)
|
||||
: _data(data)
|
||||
, _format(PixelFormat::Unknown)
|
||||
, _stride(stride)
|
||||
, _count(count)
|
||||
{
|
||||
auto sampler = PixelFormatSampler::Get(format);
|
||||
if (sampler)
|
||||
@@ -72,7 +73,7 @@ int32 MeshAccessor::Stream::GetStride() const
|
||||
|
||||
int32 MeshAccessor::Stream::GetCount() const
|
||||
{
|
||||
return _data.Length() / _stride;
|
||||
return _count;
|
||||
}
|
||||
|
||||
bool MeshAccessor::Stream::IsValid() const
|
||||
@@ -368,22 +369,23 @@ MeshAccessor::Stream MeshAccessor::Index()
|
||||
{
|
||||
Span<byte> data;
|
||||
PixelFormat format = PixelFormat::Unknown;
|
||||
int32 stride = 0;
|
||||
int32 stride = 0, count = 0;
|
||||
auto& ib = _data[(int32)MeshBufferType::Index];
|
||||
if (ib.IsValid())
|
||||
{
|
||||
data = ib;
|
||||
format = _formats[(int32)MeshBufferType::Index];
|
||||
stride = PixelFormatExtensions::SizeInBytes(format);
|
||||
count = data.Length() / stride;
|
||||
}
|
||||
return Stream(data, format, stride);
|
||||
return Stream(data, format, stride, count);
|
||||
}
|
||||
|
||||
MeshAccessor::Stream MeshAccessor::Attribute(VertexElement::Types attribute)
|
||||
{
|
||||
Span<byte> data;
|
||||
PixelFormat format = PixelFormat::Unknown;
|
||||
int32 stride = 0;
|
||||
int32 stride = 0, count = 0;
|
||||
for (int32 vbIndex = 0; vbIndex < 3 && format == PixelFormat::Unknown; vbIndex++)
|
||||
{
|
||||
static_assert((int32)MeshBufferType::Vertex0 == 1, "Update code.");
|
||||
@@ -399,11 +401,12 @@ MeshAccessor::Stream MeshAccessor::Attribute(VertexElement::Types attribute)
|
||||
data = vb.Slice(e.Offset);
|
||||
format = e.Format;
|
||||
stride = layout->GetStride();
|
||||
count = vb.Length() / stride;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Stream(data, format, stride);
|
||||
return Stream(data, format, stride, count);
|
||||
}
|
||||
|
||||
MeshBase::~MeshBase()
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Write data function.
|
||||
/// </summary>
|
||||
public delegate* unmanaged<void*, ref Float4, void> Write;
|
||||
public delegate* unmanaged<void*, Float4*, void> Write;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a sampler tool for the specified format to read pixels.
|
||||
@@ -38,7 +38,7 @@ namespace FlaxEngine
|
||||
Format = format,
|
||||
PixelSize = pixelSize,
|
||||
Read = (delegate* unmanaged<void*, Float4>)read.ToPointer(),
|
||||
Write = (delegate* unmanaged<void*, ref Float4, void>)write.ToPointer(),
|
||||
Write = (delegate* unmanaged<void*, Float4*, void>)write.ToPointer(),
|
||||
};
|
||||
return pixelSize != 0;
|
||||
}
|
||||
|
||||
@@ -575,6 +575,13 @@ void RenderTools::ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascad
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderTools::ShouldUpdateCascade(int32 frameIndex, int32 cascadeIndex, int32 cascadeCount, int32 updateMaxCountPerFrame, bool updateForce)
|
||||
{
|
||||
int32 updateFrequency, updatePhrase;
|
||||
ComputeCascadeUpdateFrequency(cascadeIndex, cascadeCount, updateFrequency, updatePhrase, updateMaxCountPerFrame);
|
||||
return (frameIndex % updateFrequency == updatePhrase) || updateForce;
|
||||
}
|
||||
|
||||
float RenderTools::ComputeTemporalTime()
|
||||
{
|
||||
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
|
||||
|
||||
@@ -133,12 +133,7 @@ public:
|
||||
static void ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascadeCount, int32& updateFrequency, int32& updatePhrase, int32 updateMaxCountPerFrame = 1);
|
||||
|
||||
// Checks if cached data should be updated during the given frame.
|
||||
FORCE_INLINE static bool ShouldUpdateCascade(int32 frameIndex, int32 cascadeIndex, int32 cascadeCount, int32 updateMaxCountPerFrame = 1, bool updateForce = false)
|
||||
{
|
||||
int32 updateFrequency, updatePhrase;
|
||||
ComputeCascadeUpdateFrequency(cascadeIndex, cascadeCount, updateFrequency, updatePhrase, updateMaxCountPerFrame);
|
||||
return (frameIndex % updateFrequency == updatePhrase) || updateForce;
|
||||
}
|
||||
static bool ShouldUpdateCascade(int32 frameIndex, int32 cascadeIndex, int32 cascadeCount, int32 updateMaxCountPerFrame = 1, bool updateForce = false);
|
||||
|
||||
// Calculates temporal offset in the dithering factor that gets cleaned out by TAA.
|
||||
// Returns 0-1 value based on unscaled draw time for temporal effects to reduce artifacts from screen-space dithering when using Temporal Anti-Aliasing.
|
||||
@@ -150,8 +145,9 @@ public:
|
||||
DEPRECATED("Use CalculateTangentFrame with unpacked Float3/Float4.") static void CalculateTangentFrame(FloatR10G10B10A2& resultNormal, FloatR10G10B10A2& resultTangent, const Float3& normal, const Float3& tangent);
|
||||
|
||||
// Result normal/tangent are already packed into [0;1] range.
|
||||
static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal);
|
||||
static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal, const Float3& tangent);
|
||||
API_FUNCTION() static void CalculateTangentFrame(API_PARAM(Out) Float3& resultNormal, API_PARAM(Out) Float4& resultTangent, API_PARAM(Ref) const Float3& normal);
|
||||
// Result normal/tangent are already packed into [0;1] range.
|
||||
API_FUNCTION() static void CalculateTangentFrame(API_PARAM(Out) Float3& resultNormal, API_PARAM(Out) Float4& resultTangent, API_PARAM(Ref) const Float3& normal, API_PARAM(Ref) const Float3& tangent);
|
||||
|
||||
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
static void ComputeBoxModelDrawMatrix(const RenderView& view, const OrientedBoundingBox& box, Matrix& resultWorld, bool& resultIsViewInside);
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
#include "Engine/Core/Math/Double4x4.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Physics/Physics.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Serialization/ISerializeModifier.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
@@ -70,6 +71,44 @@ namespace
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void LinkPrefab(SceneObject* object, Prefab* linkPrefab)
|
||||
{
|
||||
// Find prefab object that is used by this object in a given prefab
|
||||
Guid currentPrefabId = object->GetPrefabID(), currentObjectId = object->GetPrefabObjectID();
|
||||
RETRY:
|
||||
auto prefab = Content::Load<Prefab>(currentPrefabId);
|
||||
Guid nestedPrefabId, nestedObjectId;
|
||||
if (prefab && prefab->GetNestedObject(currentObjectId, nestedPrefabId, nestedObjectId))
|
||||
{
|
||||
auto nestedPrefab = Content::Load<Prefab>(nestedPrefabId);
|
||||
if (nestedPrefab)
|
||||
{
|
||||
auto nestedObject = (Actor*)nestedPrefab->GetDefaultInstance(nestedObjectId);
|
||||
if (nestedObject && nestedPrefab == linkPrefab)
|
||||
{
|
||||
object->LinkPrefab(nestedPrefabId, nestedObjectId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try deeper
|
||||
currentPrefabId = nestedPrefabId;
|
||||
currentObjectId = nestedObjectId;
|
||||
goto RETRY;
|
||||
}
|
||||
|
||||
// Failed to resolve properly object from a given prefab
|
||||
object->BreakPrefabLink();
|
||||
}
|
||||
|
||||
void LinkPrefabRecursive(Actor* actor, Prefab* linkPrefab)
|
||||
{
|
||||
for (auto script : actor->Scripts)
|
||||
LinkPrefab(script, linkPrefab);
|
||||
for (auto child : actor->Children)
|
||||
LinkPrefab(child, linkPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
Actor::Actor(const SpawnParams& params)
|
||||
@@ -1185,11 +1224,15 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
}
|
||||
else if (!parent && parentId.IsValid())
|
||||
{
|
||||
// Skip warning if object was mapped to empty id (intentionally ignored)
|
||||
Guid tmpId;
|
||||
if (_prefabObjectID.IsValid())
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\', prefab object {2}", parentId, ToString(), _prefabObjectID);
|
||||
else if (!modifier->IdsMapping.TryGet(parentId, tmpId) || tmpId.IsValid()) // Skip warning if object was mapped to empty id (intentionally ignored)
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString());
|
||||
if (!modifier->IdsMapping.TryGet(parentId, tmpId) || tmpId.IsValid())
|
||||
{
|
||||
if (_prefabObjectID.IsValid())
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\', prefab object {2}", parentId, ToString(), _prefabObjectID);
|
||||
else
|
||||
LOG(Warning, "Missing parent actor {0} for \'{1}\'", parentId, ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1729,7 +1772,6 @@ void WriteObjectToBytes(SceneObject* obj, rapidjson_flax::StringBuffer& buffer,
|
||||
{
|
||||
// Create JSON
|
||||
CompactJsonWriter writer(buffer);
|
||||
|
||||
writer.SceneObject(obj);
|
||||
|
||||
// Write json to output
|
||||
@@ -1737,28 +1779,24 @@ void WriteObjectToBytes(SceneObject* obj, rapidjson_flax::StringBuffer& buffer,
|
||||
output.WriteInt32((int32)buffer.GetSize());
|
||||
output.WriteBytes((byte*)buffer.GetString(), (int32)buffer.GetSize());
|
||||
|
||||
// Store order in parent. Makes life easier for editor to sync objects order on undo/redo actions.
|
||||
output.WriteInt32(obj->GetOrderInParent());
|
||||
|
||||
// Reuse string buffer
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
bool Actor::ToBytes(const Array<Actor*>& actors, MemoryWriteStream& output)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
if (actors.IsEmpty())
|
||||
{
|
||||
// Cannot serialize empty list
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Level);
|
||||
|
||||
// Collect object ids that exist in the serialized data to allow references mapping later
|
||||
Array<Guid> ids(actors.Count());
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
// By default we collect actors and scripts (they are ManagedObjects recognized by the id)
|
||||
|
||||
auto actor = actors[i];
|
||||
if (!actor)
|
||||
continue;
|
||||
@@ -1766,7 +1804,8 @@ bool Actor::ToBytes(const Array<Actor*>& actors, MemoryWriteStream& output)
|
||||
for (int32 j = 0; j < actor->Scripts.Count(); j++)
|
||||
{
|
||||
const auto script = actor->Scripts[j];
|
||||
ids.Add(script->GetID());
|
||||
if (script)
|
||||
ids.Add(script->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1776,23 +1815,28 @@ bool Actor::ToBytes(const Array<Actor*>& actors, MemoryWriteStream& output)
|
||||
// Serialized objects ids (for references mapping)
|
||||
output.Write(ids);
|
||||
|
||||
// Objects data
|
||||
// Objects data (JSON)
|
||||
rapidjson_flax::StringBuffer buffer;
|
||||
CompactJsonWriter writer(buffer);
|
||||
writer.StartArray();
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
Actor* actor = actors[i];
|
||||
if (!actor)
|
||||
continue;
|
||||
|
||||
WriteObjectToBytes(actor, buffer, output);
|
||||
|
||||
writer.SceneObject(actor);
|
||||
for (int32 j = 0; j < actor->Scripts.Count(); j++)
|
||||
{
|
||||
Script* script = actor->Scripts[j];
|
||||
|
||||
WriteObjectToBytes(script, buffer, output);
|
||||
if (!script)
|
||||
continue;
|
||||
writer.SceneObject(script);
|
||||
}
|
||||
}
|
||||
writer.EndArray();
|
||||
// TODO: compress json (LZ4) if it's big enough
|
||||
output.WriteInt32((int32)buffer.GetSize());
|
||||
output.WriteBytes((byte*)buffer.GetString(), (int32)buffer.GetSize());
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1811,8 +1855,8 @@ Array<byte> Actor::ToBytes(const Array<Actor*>& actors)
|
||||
bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeModifier* modifier)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(Level);
|
||||
output.Clear();
|
||||
|
||||
ASSERT(modifier);
|
||||
if (data.Length() <= 0)
|
||||
return true;
|
||||
@@ -1828,50 +1872,71 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
}
|
||||
|
||||
// Serialized objects ids (for references mapping)
|
||||
#if 0
|
||||
Array<Guid> ids;
|
||||
stream.Read(ids);
|
||||
int32 objectsCount = ids.Count();
|
||||
#else
|
||||
int32 objectsCount;
|
||||
stream.ReadInt32(&objectsCount);
|
||||
stream.Move<Guid>(objectsCount);
|
||||
#endif
|
||||
if (objectsCount < 0)
|
||||
return true;
|
||||
|
||||
// Load objects data (JSON)
|
||||
int32 bufferSize;
|
||||
stream.ReadInt32(&bufferSize);
|
||||
const char* buffer = (const char*)stream.Move(bufferSize);
|
||||
rapidjson_flax::Document document;
|
||||
{
|
||||
PROFILE_CPU_NAMED("Json.Parse");
|
||||
document.Parse(buffer, bufferSize);
|
||||
}
|
||||
if (document.HasParseError())
|
||||
{
|
||||
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
Array<int32> order;
|
||||
order.Resize(objectsCount);
|
||||
modifier->EngineBuild = engineBuild;
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
sceneObjects->Resize(objectsCount);
|
||||
SceneObjectsFactory::Context context(modifier);
|
||||
|
||||
// Fix root linkage for prefab instances (eg. when user duplicates a sub-prefab actor but not a root one)
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, document, modifier);
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
for (auto& instance : context.Instances)
|
||||
{
|
||||
Guid prefabObjectId;
|
||||
if (!JsonTools::GetGuidIfValid(prefabObjectId, document[instance.RootIndex], "PrefabObjectID"))
|
||||
continue;
|
||||
|
||||
// Get the original object from prefab
|
||||
SceneObject* prefabObject = instance.Prefab->GetDefaultInstance(prefabObjectId);
|
||||
if (prefabObject && prefabObject->GetParent())
|
||||
{
|
||||
// Add empty mapping to parent object in prefab to prevent linking to it
|
||||
auto prefabObjectParentId = prefabObject->GetParent()->GetPrefabObjectID();
|
||||
instance.IdsMapping[prefabObjectParentId] = Guid::Empty;
|
||||
modifier->IdsMapping[prefabObjectParentId] = Guid::Empty;
|
||||
Guid nestedPrefabId, nestedPrefabObjectId;
|
||||
if (instance.Prefab->GetNestedObject(prefabObjectParentId, nestedPrefabId, nestedPrefabObjectId))
|
||||
{
|
||||
instance.IdsMapping[nestedPrefabObjectId] = Guid::Empty;
|
||||
modifier->IdsMapping[nestedPrefabObjectId] = Guid::Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize objects
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
auto startPos = stream.GetPosition();
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
// Buffer
|
||||
int32 bufferSize;
|
||||
stream.ReadInt32(&bufferSize);
|
||||
const char* buffer = (const char*)stream.GetPositionHandle();
|
||||
stream.Move(bufferSize);
|
||||
|
||||
// Order in parent
|
||||
int32 orderInParent;
|
||||
stream.ReadInt32(&orderInParent);
|
||||
order[i] = orderInParent;
|
||||
|
||||
// Load JSON
|
||||
rapidjson_flax::Document document;
|
||||
{
|
||||
PROFILE_CPU_NAMED("Json.Parse");
|
||||
document.Parse(buffer, bufferSize);
|
||||
}
|
||||
if (document.HasParseError())
|
||||
{
|
||||
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create object
|
||||
auto obj = SceneObjectsFactory::Spawn(context, document);
|
||||
auto& objData = document[i];
|
||||
auto obj = SceneObjectsFactory::Spawn(context, objData);
|
||||
sceneObjects->At(i) = obj;
|
||||
if (obj == nullptr)
|
||||
{
|
||||
@@ -1879,57 +1944,21 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
continue;
|
||||
}
|
||||
obj->RegisterObject();
|
||||
|
||||
// Add to results
|
||||
Actor* actor = dynamic_cast<Actor*>(obj);
|
||||
if (actor)
|
||||
{
|
||||
output.Add(actor);
|
||||
}
|
||||
}
|
||||
// TODO: optimize this to call json parsing only once per-object instead of twice (spawn + load)
|
||||
stream.SetPosition(startPos);
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
// Buffer
|
||||
int32 bufferSize;
|
||||
stream.ReadInt32(&bufferSize);
|
||||
const char* buffer = (const char*)stream.GetPositionHandle();
|
||||
stream.Move(bufferSize);
|
||||
|
||||
// Order in parent
|
||||
int32 orderInParent;
|
||||
stream.ReadInt32(&orderInParent);
|
||||
|
||||
// Load JSON
|
||||
rapidjson_flax::Document document;
|
||||
{
|
||||
PROFILE_CPU_NAMED("Json.Parse");
|
||||
document.Parse(buffer, bufferSize);
|
||||
}
|
||||
if (document.HasParseError())
|
||||
{
|
||||
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deserialize object
|
||||
auto& objData = document[i];
|
||||
auto obj = sceneObjects->At(i);
|
||||
if (obj)
|
||||
SceneObjectsFactory::Deserialize(context, obj, document);
|
||||
SceneObjectsFactory::Deserialize(context, obj, objData);
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(document);
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(objData);
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
|
||||
// Update objects order
|
||||
//for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
//SceneObject* obj = sceneObjects->At(i);
|
||||
// TODO: remove order from saved data?
|
||||
//obj->SetOrderInParent(order[i]);
|
||||
}
|
||||
|
||||
// Call events (only for parents because they will propagate events down the tree)
|
||||
CollectionPoolCache<ActorsCache::ActorsListType>::ScopeCache parents = ActorsCache::ActorsListCache.Get();
|
||||
parents->EnsureCapacity(output.Count());
|
||||
@@ -1940,6 +1969,32 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
Actor* actor = parents->At(i);
|
||||
if (actor->HasPrefabLink() && !actor->IsPrefabRoot())
|
||||
{
|
||||
// Find a prefab in which that object is a root to establish a new linkage
|
||||
Guid currentPrefabId = actor->GetPrefabID(), currentObjectId = actor->GetPrefabObjectID();
|
||||
RETRY:
|
||||
auto prefab = Content::Load<Prefab>(currentPrefabId);
|
||||
Guid nestedPrefabId, nestedObjectId;
|
||||
if (prefab && prefab->GetNestedObject(currentObjectId, nestedPrefabId, nestedObjectId))
|
||||
{
|
||||
auto nestedPrefab = Content::Load<Prefab>(nestedPrefabId);
|
||||
if (nestedPrefab)
|
||||
{
|
||||
auto nestedObject = (Actor*)nestedPrefab->GetDefaultInstance(nestedObjectId);
|
||||
if (nestedObject && nestedObject->IsPrefabRoot())
|
||||
{
|
||||
// Change link to the nested prefab
|
||||
actor->LinkPrefab(nestedPrefabId, nestedObjectId);
|
||||
LinkPrefabRecursive(actor, nestedPrefab);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Try deeper
|
||||
currentPrefabId = nestedPrefabId;
|
||||
currentObjectId = nestedObjectId;
|
||||
goto RETRY;
|
||||
}
|
||||
|
||||
actor->BreakPrefabLink();
|
||||
}
|
||||
}
|
||||
@@ -1949,8 +2004,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
}
|
||||
for (int32 i = 0; i < parents->Count(); i++)
|
||||
{
|
||||
Actor* actor = parents->At(i);
|
||||
actor->OnTransformChanged();
|
||||
parents->At(i)->OnTransformChanged();
|
||||
}
|
||||
|
||||
// Initialize actor that are spawned to scene or create managed instanced for others
|
||||
|
||||
@@ -507,7 +507,7 @@ namespace
|
||||
return;
|
||||
Spline::Keyframe* prev = spline->Curve.GetKeyframes().Get();
|
||||
Vector3 prevPos = transform.LocalToWorld(prev->Value.Translation);
|
||||
float distance = Vector3::Distance(prevPos, DebugDraw::GetViewPos());
|
||||
Real distance = Vector3::Distance(prevPos, DebugDraw::GetViewPos());
|
||||
if (distance < METERS_TO_UNITS(800)) // 800m
|
||||
{
|
||||
// Bezier curve
|
||||
|
||||
@@ -650,6 +650,11 @@ bool Prefab::ApplyAll(Actor* targetActor)
|
||||
LOG(Warning, "Failed to create default prefab instance for the prefab asset.");
|
||||
return true;
|
||||
}
|
||||
if (targetActor == _defaultInstance || targetActor->HasActorInHierarchy(_defaultInstance) || _defaultInstance->HasActorInHierarchy(targetActor))
|
||||
{
|
||||
LOG(Error, "Cannot apply changes to the prefab using default instance. Use manually spawned prefab instance instead.");
|
||||
return true;
|
||||
}
|
||||
if (targetActor->GetPrefabObjectID() != GetRootObjectId())
|
||||
{
|
||||
LOG(Warning, "Applying prefab changes with modified root object. Root object id: {0}, new root: {1} (prefab object id: {2})", GetRootObjectId().ToString(), targetActor->ToString(), targetActor->GetPrefabObjectID());
|
||||
|
||||
@@ -60,12 +60,14 @@ public:
|
||||
/// <summary>
|
||||
/// Requests the default prefab object instance. Deserializes the prefab objects from the asset. Skips if already done.
|
||||
/// </summary>
|
||||
/// <remarks>Default instances of the prefab are read-only and are used internally for objects serialization (prefab diff).</remarks>
|
||||
/// <returns>The root of the prefab object loaded from the prefab. Contains the default values. It's not added to gameplay but deserialized with postLoad and init event fired.</returns>
|
||||
API_FUNCTION() Actor* GetDefaultInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Requests the default prefab object instance. Deserializes the prefab objects from the asset. Skips if already done.
|
||||
/// </summary>
|
||||
/// <remarks>Default instances of the prefab are read-only and are used internally for objects serialization (prefab diff).</remarks>
|
||||
/// <param name="objectId">The ID of the object to get from prefab default object. It can be one of the child-actors or any script that exists in the prefab. Methods returns root if id is empty.</param>
|
||||
/// <returns>The object of the prefab loaded from the prefab. Contains the default values. It's not added to gameplay but deserialized with postLoad and init event fired.</returns>
|
||||
API_FUNCTION() SceneObject* GetDefaultInstance(API_PARAM(Ref) const Guid& objectId);
|
||||
|
||||
@@ -61,7 +61,7 @@ void SceneObject::BreakPrefabLink()
|
||||
|
||||
String SceneObject::GetNamePath(Char separatorChar) const
|
||||
{
|
||||
Array<StringView> names;
|
||||
Array<StringView, InlinedAllocation<8>> names;
|
||||
const Actor* a = dynamic_cast<const Actor*>(this);
|
||||
if (!a)
|
||||
a = GetParent();
|
||||
|
||||
@@ -1863,7 +1863,7 @@ void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||
{
|
||||
auto& item = it->Item;
|
||||
ScriptingObject* obj = item.Object.Get();
|
||||
if (!obj || !item.Spawned || item.Role != NetworkObjectRole::OwnedAuthoritative)
|
||||
if (!obj || item.Role != NetworkObjectRole::OwnedAuthoritative)
|
||||
continue;
|
||||
|
||||
// Mark this client as missing cached data
|
||||
|
||||
@@ -425,7 +425,7 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context)
|
||||
BoundingSphere frustumBounds;
|
||||
renderContext.View.CullingFrustum.GetSphere(frustumBounds);
|
||||
origin = frustumBounds.Center;
|
||||
size = frustumBounds.Radius;
|
||||
size = (float)frustumBounds.Radius;
|
||||
}
|
||||
Matrix::Scaling(size / ((float)box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum
|
||||
Matrix::CreateWorld(origin, Float3::Up, Float3::Backward, m2); // Rotate sphere model
|
||||
|
||||
@@ -747,7 +747,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
|
||||
if (drawModes != DrawPass::None &&
|
||||
(staticFlags & renderContext.View.StaticFlagsMask) == renderContext.View.StaticFlagsCompare &&
|
||||
renderContext.View.CullingFrustum.Intersects(bounds) &&
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(bounds.Center, bounds.Radius, renderContext.View) * (renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y) >= minObjectPixelSizeSq)
|
||||
RenderTools::ComputeBoundsScreenRadiusSquared(bounds.Center, (float)bounds.Radius, renderContext.View) * (renderContext.View.ScreenSize.X * renderContext.View.ScreenSize.Y) >= minObjectPixelSizeSq)
|
||||
{
|
||||
renderContext.List->ShadowDepthDrawCallsList.Indices.Add(index);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include "Scripting.h"
|
||||
#include "Events.h"
|
||||
#include "Internal/StdTypesContainer.h"
|
||||
|
||||
Dictionary<Pair<ScriptingTypeHandle, StringView>, void(*)(ScriptingObject*, void*, bool)> ScriptingEvents::EventsTable;
|
||||
Delegate<ScriptingObject*, Span<Variant>, ScriptingTypeHandle, StringView> ScriptingEvents::Event;
|
||||
@@ -32,6 +31,18 @@ ManagedBinaryModule* GetBinaryModuleCorlib()
|
||||
#endif
|
||||
}
|
||||
|
||||
MMethod* MClass::FindMethod(const char* name, int32 numParams, bool checkBaseClasses) const
|
||||
{
|
||||
MMethod* method = GetMethod(name, numParams);
|
||||
if (!method && checkBaseClasses)
|
||||
{
|
||||
MClass* base = GetBaseClass();
|
||||
if (base)
|
||||
method = base->FindMethod(name, numParams, true);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
ScriptingTypeHandle::ScriptingTypeHandle(const ScriptingTypeInitializer& initializer)
|
||||
: Module(initializer.Module)
|
||||
, TypeIndex(initializer.TypeIndex)
|
||||
@@ -835,61 +846,17 @@ namespace
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VariantTypeEquals(const VariantType& type, MType* mType, bool isOut = false)
|
||||
{
|
||||
MClass* mClass = MCore::Type::GetClass(mType);
|
||||
MClass* variantClass = MUtils::GetClass(type);
|
||||
if (variantClass != mClass)
|
||||
{
|
||||
// Hack for Vector2/3/4 which alias with Float2/3/4 or Double2/3/4 (depending on USE_LARGE_WORLDS)
|
||||
const auto& stdTypes = *StdTypesContainer::Instance();
|
||||
if (mClass == stdTypes.Vector2Class && (type.Type == VariantType::Float2 || type.Type == VariantType::Double2))
|
||||
return true;
|
||||
if (mClass == stdTypes.Vector3Class && (type.Type == VariantType::Float3 || type.Type == VariantType::Double3))
|
||||
return true;
|
||||
if (mClass == stdTypes.Vector4Class && (type.Type == VariantType::Float4 || type.Type == VariantType::Double4))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMethodSignature& signature)
|
||||
MMethod* ManagedBinaryModule::FindMethod(const MClass* mclass, const ScriptingTypeMethodSignature& signature)
|
||||
{
|
||||
#if USE_CSHARP
|
||||
if (!mclass)
|
||||
return nullptr;
|
||||
const auto& methods = mclass->GetMethods();
|
||||
for (MMethod* method : methods)
|
||||
{
|
||||
if (method->IsStatic() != signature.IsStatic)
|
||||
continue;
|
||||
if (method->GetName() != signature.Name)
|
||||
continue;
|
||||
if (method->GetParametersCount() != signature.Params.Count())
|
||||
continue;
|
||||
bool isValid = true;
|
||||
for (int32 paramIdx = 0; paramIdx < signature.Params.Count(); paramIdx++)
|
||||
{
|
||||
auto& param = signature.Params[paramIdx];
|
||||
MType* type = method->GetParameterType(paramIdx);
|
||||
if (param.IsOut != method->GetParameterIsOut(paramIdx) ||
|
||||
!VariantTypeEquals(param.Type, type, param.IsOut))
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid && VariantTypeEquals(signature.ReturnType, method->GetReturnType()))
|
||||
return method;
|
||||
}
|
||||
#endif
|
||||
return mclass ? mclass->GetMethod(signature) : nullptr;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_CSHARP
|
||||
|
||||
@@ -23,7 +23,7 @@ struct ScriptingTypeMethodSignature
|
||||
|
||||
StringAnsiView Name;
|
||||
VariantType ReturnType;
|
||||
bool IsStatic;
|
||||
bool IsStatic = false;
|
||||
Array<Param, InlinedAllocation<16>> Params;
|
||||
};
|
||||
|
||||
@@ -322,7 +322,7 @@ public:
|
||||
#endif
|
||||
|
||||
static ScriptingObject* ManagedObjectSpawn(const ScriptingObjectSpawnParams& params);
|
||||
static MMethod* FindMethod(MClass* mclass, const ScriptingTypeMethodSignature& signature);
|
||||
static MMethod* FindMethod(const MClass* mclass, const ScriptingTypeMethodSignature& signature);
|
||||
#if USE_CSHARP
|
||||
static ManagedBinaryModule* FindModule(const MClass* klass);
|
||||
static ScriptingTypeHandle FindType(const MClass* klass);
|
||||
|
||||
@@ -188,11 +188,11 @@ public:
|
||||
MClass* GetBaseClass() const;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this class is a sub class of the specified class (including any derived types).
|
||||
/// Checks if this class is a subclass of the specified class (including any derived types).
|
||||
/// </summary>
|
||||
/// <param name="klass">The class.</param>
|
||||
/// <param name="checkInterfaces">True if check interfaces, otherwise just base class.</param>
|
||||
/// <returns>True if this class is a sub class of the specified class.</returns>
|
||||
/// <returns>True if this class is a subclass of the specified class.</returns>
|
||||
bool IsSubClassOf(const MClass* klass, bool checkInterfaces = false) const;
|
||||
|
||||
/// <summary>
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
/// Checks is the provided object instance of this class' type.
|
||||
/// </summary>
|
||||
/// <param name="object">The object to check.</param>
|
||||
/// <returns>True if object is an instance the this class.</returns>
|
||||
/// <returns>True if object is an instance this class.</returns>
|
||||
bool IsInstanceOfType(MObject* object) const;
|
||||
|
||||
/// <summary>
|
||||
@@ -227,17 +227,7 @@ public:
|
||||
/// <param name="numParams">The method parameters count.</param>
|
||||
/// <param name="checkBaseClasses">True if check base classes when searching for the given method.</param>
|
||||
/// <returns>The method or null if failed to find it.</returns>
|
||||
MMethod* FindMethod(const char* name, int32 numParams, bool checkBaseClasses = true) const
|
||||
{
|
||||
MMethod* method = GetMethod(name, numParams);
|
||||
if (!method && checkBaseClasses)
|
||||
{
|
||||
MClass* base = GetBaseClass();
|
||||
if (base)
|
||||
method = base->FindMethod(name, numParams, true);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
MMethod* FindMethod(const char* name, int32 numParams, bool checkBaseClasses = true) const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object referencing a method with the specified name and number of parameters.
|
||||
@@ -248,6 +238,13 @@ public:
|
||||
/// <returns>The method or null if failed to get it.</returns>
|
||||
MMethod* GetMethod(const char* name, int32 numParams = 0) const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object referencing a method with the specified signature.
|
||||
/// </summary>
|
||||
/// <param name="signature">The method signature.</param>
|
||||
/// <returns>The method or null if failed to get it.</returns>
|
||||
MMethod* GetMethod(const struct ScriptingTypeMethodSignature& signature) const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns all methods belonging to this class.
|
||||
/// </summary>
|
||||
@@ -271,7 +268,7 @@ public:
|
||||
const Array<MField*, ArenaAllocation>& GetFields() const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object referencing a event with the specified name.
|
||||
/// Returns an object referencing an event with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The event name.</param>
|
||||
/// <returns>The event object.</returns>
|
||||
|
||||
@@ -29,6 +29,7 @@ protected:
|
||||
int32 _paramsCount;
|
||||
mutable void* _returnType;
|
||||
mutable Array<void*, InlinedAllocation<8>> _parameterTypes;
|
||||
mutable uint64 _parameterOuts = 0;
|
||||
void CacheSignature() const;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
|
||||
@@ -845,7 +845,6 @@ MClass* MUtils::GetClass(const VariantType& value)
|
||||
auto mclass = Scripting::FindClass(StringAnsiView(value.TypeName));
|
||||
if (mclass)
|
||||
return mclass;
|
||||
const auto& stdTypes = *StdTypesContainer::Instance();
|
||||
switch (value.Type)
|
||||
{
|
||||
case VariantType::Void:
|
||||
@@ -891,25 +890,25 @@ MClass* MUtils::GetClass(const VariantType& value)
|
||||
case VariantType::Double4:
|
||||
return Double4::TypeInitializer.GetClass();
|
||||
case VariantType::Color:
|
||||
return stdTypes.ColorClass;
|
||||
return Color::TypeInitializer.GetClass();
|
||||
case VariantType::Guid:
|
||||
return stdTypes.GuidClass;
|
||||
return GetBinaryModuleCorlib()->Assembly->GetClass("System.Guid");
|
||||
case VariantType::Typename:
|
||||
return stdTypes.TypeClass;
|
||||
return GetBinaryModuleCorlib()->Assembly->GetClass("System.Type");
|
||||
case VariantType::BoundingBox:
|
||||
return stdTypes.BoundingBoxClass;
|
||||
return BoundingBox::TypeInitializer.GetClass();
|
||||
case VariantType::BoundingSphere:
|
||||
return stdTypes.BoundingSphereClass;
|
||||
return BoundingSphere::TypeInitializer.GetClass();
|
||||
case VariantType::Quaternion:
|
||||
return stdTypes.QuaternionClass;
|
||||
return Quaternion::TypeInitializer.GetClass();
|
||||
case VariantType::Transform:
|
||||
return stdTypes.TransformClass;
|
||||
return Transform::TypeInitializer.GetClass();
|
||||
case VariantType::Rectangle:
|
||||
return stdTypes.RectangleClass;
|
||||
return Rectangle::TypeInitializer.GetClass();
|
||||
case VariantType::Ray:
|
||||
return stdTypes.RayClass;
|
||||
return Ray::TypeInitializer.GetClass();
|
||||
case VariantType::Matrix:
|
||||
return stdTypes.MatrixClass;
|
||||
return Matrix::TypeInitializer.GetClass();
|
||||
case VariantType::Array:
|
||||
if (value.TypeName)
|
||||
{
|
||||
@@ -1202,8 +1201,7 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
if (value.Type.Type != VariantType::Array)
|
||||
return nullptr;
|
||||
MObject* object = BoxVariant(value);
|
||||
auto typeStr = MCore::Type::ToString(type);
|
||||
if (object && !MCore::Object::GetClass(object)->IsSubClassOf(MCore::Type::GetClass(type)))
|
||||
if (object && MCore::Type::GetClass(type) != MCore::Array::GetArrayClass((MArray*)object))
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
@@ -1238,6 +1236,29 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool MUtils::VariantTypeEquals(const VariantType& type, MType* mType, bool isOut)
|
||||
{
|
||||
MClass* mClass = MCore::Type::GetClass(mType);
|
||||
MClass* variantClass = MUtils::GetClass(type);
|
||||
if (variantClass != mClass)
|
||||
{
|
||||
// Hack for Vector2/3/4 which alias with Float2/3/4 or Double2/3/4 (depending on USE_LARGE_WORLDS)
|
||||
if (mClass->GetFullName() == StringAnsiView("FlaxEngine.Vector2", 18) && (type.Type == VariantType::Float2 || type.Type == VariantType::Double2))
|
||||
return true;
|
||||
if (mClass->GetFullName() == StringAnsiView("FlaxEngine.Vector3", 18) && (type.Type == VariantType::Float3 || type.Type == VariantType::Double3))
|
||||
return true;
|
||||
if (mClass->GetFullName() == StringAnsiView("FlaxEngine.Vector4", 18) && (type.Type == VariantType::Float4 || type.Type == VariantType::Double4))
|
||||
return true;
|
||||
|
||||
// Arrays
|
||||
if (type == VariantType::Array && type.GetElementType() == VariantType::Object)
|
||||
return MCore::Type::GetType(mType) == MTypes::Array;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MObject* MUtils::ToManaged(const Version& value)
|
||||
{
|
||||
#if USE_NETCORE
|
||||
|
||||
@@ -621,6 +621,7 @@ namespace MUtils
|
||||
#endif
|
||||
|
||||
extern void* VariantToManagedArgPtr(Variant& value, MType* type, bool& failed);
|
||||
extern bool VariantTypeEquals(const VariantType& type, MType* mType, bool isOut = false);
|
||||
|
||||
extern MObject* ToManaged(const Version& value);
|
||||
extern Version ToNative(MObject* value);
|
||||
|
||||
@@ -1062,10 +1062,39 @@ MClass* MClass::GetElementClass() const
|
||||
MMethod* MClass::GetMethod(const char* name, int32 numParams) const
|
||||
{
|
||||
GetMethods();
|
||||
for (int32 i = 0; i < _methods.Count(); i++)
|
||||
for (MMethod* method : _methods)
|
||||
{
|
||||
if (_methods[i]->GetParametersCount() == numParams && _methods[i]->GetName() == name)
|
||||
return _methods[i];
|
||||
if (method->GetParametersCount() == numParams && method->GetName() == name)
|
||||
return method;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMethod* MClass::GetMethod(const ScriptingTypeMethodSignature& signature) const
|
||||
{
|
||||
GetMethods();
|
||||
for (MMethod* method : _methods)
|
||||
{
|
||||
if (method->IsStatic() != signature.IsStatic)
|
||||
continue;
|
||||
if (method->GetName() != signature.Name)
|
||||
continue;
|
||||
if (method->GetParametersCount() != signature.Params.Count())
|
||||
continue;
|
||||
bool isValid = true;
|
||||
for (int32 paramIdx = 0; paramIdx < signature.Params.Count(); paramIdx++)
|
||||
{
|
||||
auto& param = signature.Params[paramIdx];
|
||||
MType* type = method->GetParameterType(paramIdx);
|
||||
if (param.IsOut != method->GetParameterIsOut(paramIdx) ||
|
||||
!MUtils::VariantTypeEquals(param.Type, type, param.IsOut))
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid && (signature.ReturnType.Type == VariantType::Null || MUtils::VariantTypeEquals(signature.ReturnType, method->GetReturnType())))
|
||||
return method;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1485,13 +1514,16 @@ void MMethod::CacheSignature() const
|
||||
|
||||
static void* GetMethodReturnTypePtr = GetStaticMethodPointer(TEXT("GetMethodReturnType"));
|
||||
static void* GetMethodParameterTypesPtr = GetStaticMethodPointer(TEXT("GetMethodParameterTypes"));
|
||||
static void* GetMethodParameterIsOutPtr = GetStaticMethodPointer(TEXT("GetMethodParameterIsOut"));
|
||||
_returnType = CallStaticMethod<void*, void*>(GetMethodReturnTypePtr, _handle);
|
||||
_parameterOuts = 0;
|
||||
if (_paramsCount != 0)
|
||||
{
|
||||
void** parameterTypeHandles;
|
||||
CallStaticMethod<void, void*, void***>(GetMethodParameterTypesPtr, _handle, ¶meterTypeHandles);
|
||||
_parameterTypes.Set(parameterTypeHandles, _paramsCount);
|
||||
MCore::GC::FreeMemory(parameterTypeHandles);
|
||||
_parameterOuts = CallStaticMethod<uint64, void*>(GetMethodParameterIsOutPtr, _handle);
|
||||
}
|
||||
|
||||
_hasCachedSignature = true;
|
||||
@@ -1558,9 +1590,7 @@ bool MMethod::GetParameterIsOut(int32 paramIdx) const
|
||||
if (!_hasCachedSignature)
|
||||
CacheSignature();
|
||||
ASSERT_LOW_LAYER(paramIdx >= 0 && paramIdx < _paramsCount);
|
||||
// TODO: cache GetParameterIsOut maybe?
|
||||
static void* GetMethodParameterIsOutPtr = GetStaticMethodPointer(TEXT("GetMethodParameterIsOut"));
|
||||
return CallStaticMethod<bool, void*, int>(GetMethodParameterIsOutPtr, _handle, paramIdx);
|
||||
return _parameterOuts & (1ull << paramIdx);
|
||||
}
|
||||
|
||||
bool MMethod::HasAttribute(const MClass* klass) const
|
||||
|
||||
@@ -1357,6 +1357,11 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
|
||||
return method;
|
||||
}
|
||||
|
||||
MMethod* MClass::GetMethod(const ScriptingTypeMethodSignature& signature) const
|
||||
{
|
||||
return GetMethod(signature.Name.Get(), signature.Params.Count());
|
||||
}
|
||||
|
||||
const Array<MMethod*>& MClass::GetMethods() const
|
||||
{
|
||||
if (_hasCachedMethods)
|
||||
|
||||
@@ -363,6 +363,11 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMethod* MClass::GetMethod(const ScriptingTypeMethodSignature& signature) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Array<MMethod*, ArenaAllocation>& MClass::GetMethods() const
|
||||
{
|
||||
_hasCachedMethods = true;
|
||||
|
||||
@@ -72,6 +72,13 @@ void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, c
|
||||
}
|
||||
}
|
||||
|
||||
void JsonTools::MergeObjects(Value& target, Value& source, Value::AllocatorType& allocator)
|
||||
{
|
||||
ASSERT(target.IsObject() && source.IsObject());
|
||||
for (auto itr = source.MemberBegin(); itr != source.MemberEnd(); ++itr)
|
||||
target.AddMember(itr->name, itr->value, allocator);
|
||||
}
|
||||
|
||||
void JsonTools::ChangeIds(Document& doc, const Dictionary<Guid, Guid>& mapping)
|
||||
{
|
||||
if (mapping.IsEmpty())
|
||||
@@ -235,6 +242,14 @@ Plane JsonTools::GetPlane(const Value& value)
|
||||
return result;
|
||||
}
|
||||
|
||||
Rectangle JsonTools::GetRectangle(const Value& value)
|
||||
{
|
||||
return Rectangle(
|
||||
GetVector2(value, "Location", Vector2::Zero),
|
||||
GetVector2(value, "Size", Vector2::Zero)
|
||||
);
|
||||
}
|
||||
|
||||
BoundingSphere JsonTools::GetBoundingSphere(const Value& value)
|
||||
{
|
||||
BoundingSphere result;
|
||||
@@ -283,3 +298,14 @@ DateTime JsonTools::GetDateTime(const Value& value)
|
||||
{
|
||||
return DateTime(value.GetInt64());
|
||||
}
|
||||
|
||||
bool JsonTools::GetGuidIfValid(Guid& result, const Value& node, const char* name)
|
||||
{
|
||||
auto member = node.FindMember(name);
|
||||
if (member != node.MemberEnd())
|
||||
{
|
||||
result = GetGuid(member->value);
|
||||
return result.IsValid();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,13 +35,7 @@ public:
|
||||
MergeObjects(target, source, target.GetAllocator());
|
||||
}
|
||||
|
||||
static void MergeObjects(Value& target, Value& source, Value::AllocatorType& allocator)
|
||||
{
|
||||
ASSERT(target.IsObject() && source.IsObject());
|
||||
for (auto itr = source.MemberBegin(); itr != source.MemberEnd(); ++itr)
|
||||
target.AddMember(itr->name, itr->value, allocator);
|
||||
}
|
||||
|
||||
static void MergeObjects(Value& target, Value& source, Value::AllocatorType& allocator);
|
||||
static void ChangeIds(Document& doc, const Dictionary<Guid, Guid, HeapAllocation>& mapping);
|
||||
|
||||
public:
|
||||
@@ -75,11 +69,9 @@ public:
|
||||
static Float2 GetFloat2(const Value& value);
|
||||
static Float3 GetFloat3(const Value& value);
|
||||
static Float4 GetFloat4(const Value& value);
|
||||
|
||||
static Double2 GetDouble2(const Value& value);
|
||||
static Double3 GetDouble3(const Value& value);
|
||||
static Double4 GetDouble4(const Value& value);
|
||||
|
||||
static Color GetColor(const Value& value);
|
||||
static Quaternion GetQuaternion(const Value& value);
|
||||
static Ray GetRay(const Value& value);
|
||||
@@ -87,15 +79,7 @@ public:
|
||||
static Transform GetTransform(const Value& value);
|
||||
static void GetTransform(Transform& result, const Value& value);
|
||||
static Plane GetPlane(const Value& value);
|
||||
|
||||
static Rectangle GetRectangle(const Value& value)
|
||||
{
|
||||
return Rectangle(
|
||||
GetVector2(value, "Location", Vector2::Zero),
|
||||
GetVector2(value, "Size", Vector2::Zero)
|
||||
);
|
||||
}
|
||||
|
||||
static Rectangle GetRectangle(const Value& value);
|
||||
static BoundingSphere GetBoundingSphere(const Value& value);
|
||||
static BoundingBox GetBoundingBox(const Value& value);
|
||||
static Guid GetGuid(const Value& value);
|
||||
@@ -176,16 +160,7 @@ public:
|
||||
return member != node.MemberEnd() ? GetGuid(member->value) : Guid::Empty;
|
||||
}
|
||||
|
||||
FORCE_INLINE static bool GetGuidIfValid(Guid& result, const Value& node, const char* name)
|
||||
{
|
||||
auto member = node.FindMember(name);
|
||||
if (member != node.MemberEnd())
|
||||
{
|
||||
result = GetGuid(member->value);
|
||||
return result.IsValid();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool GetGuidIfValid(Guid& result, const Value& node, const char* name);
|
||||
|
||||
public:
|
||||
FORCE_INLINE static void GetBool(bool& result, const Value& node, const char* name)
|
||||
|
||||
@@ -439,7 +439,7 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
// Calculate normals for quad two vertices
|
||||
Float3 n0 = Float3::Normalize((v00 - v01) ^ (v01 - v10));
|
||||
Float3 n1 = Float3::Normalize((v11 - v10) ^ (v10 - v01));
|
||||
Float3 n2 = n0 + n1;
|
||||
Float3 n2 = Float3::Normalize(n0 + n1);
|
||||
|
||||
// Apply normal to each vertex using it
|
||||
normalsPerVertex[i00] += n1;
|
||||
@@ -2572,9 +2572,9 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<i
|
||||
const int32 vertexCount = rows * cols;
|
||||
_collisionVertices.Resize(vertexCount);
|
||||
Float3* vb = _collisionVertices.Get();
|
||||
for (int32 row = 0; row < rows; row++)
|
||||
for (int32 col = 0; col < cols; col++)
|
||||
{
|
||||
for (int32 col = 0; col < cols; col++)
|
||||
for (int32 row = 0; row < rows; row++)
|
||||
{
|
||||
Float3 v((float)row, PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row, col) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)col);
|
||||
Float3::Transform(v, world, v);
|
||||
@@ -2591,9 +2591,9 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Float3>& vertexBuffer, Array<i
|
||||
const int32 indexCount = (rows - 1) * (cols - 1) * 6;
|
||||
indexBuffer.Resize(indexCount);
|
||||
int32* ib = indexBuffer.Get();
|
||||
for (int32 row = 0; row < rows - 1; row++)
|
||||
for (int32 col = 0; col < cols - 1; col++)
|
||||
{
|
||||
for (int32 col = 0; col < cols - 1; col++)
|
||||
for (int32 row = 0; row < rows - 1; row++)
|
||||
{
|
||||
#define GET_INDEX(x, y) *ib++ = (col + (y)) + (row + (x)) * cols
|
||||
|
||||
|
||||
@@ -456,8 +456,8 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
||||
_writer.Write(TEXT("#define MATERIAL_REFLECTIONS_SSR_COLOR ({0})\n"), sceneColorTexture.ShaderName);
|
||||
}
|
||||
WRITE_FEATURES(Defines);
|
||||
inputs[In_Defines] = _writer.ToString();
|
||||
WriteCustomGlobalCode(customGlobalCodeNodes, In_Defines);
|
||||
inputs[In_Defines] = _writer.ToString();
|
||||
_writer.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData,
|
||||
minDistance *= -1; // Voxel is inside the geometry so turn it into negative distance to the surface
|
||||
|
||||
const int32 xAddress = x + yAddress;
|
||||
formatWrite(voxels.Get() + xAddress * formatStride, minDistance * encodeMAD.X + encodeMAD.Y);
|
||||
formatWrite(voxels.Get() + xAddress * formatStride, (float)minDistance * encodeMAD.X + encodeMAD.Y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace FlaxEngine.GUI
|
||||
'\\',
|
||||
'>',
|
||||
'<',
|
||||
'=',
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace FlaxEngine
|
||||
/// <inheritdoc />
|
||||
public override bool CanRender()
|
||||
{
|
||||
if (!Canvas)
|
||||
return false;
|
||||
|
||||
// Sync with canvas options
|
||||
Location = Canvas.RenderLocation;
|
||||
Order = Canvas.Order;
|
||||
|
||||
@@ -493,35 +493,26 @@ void ShaderGenerator::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
// Unpack
|
||||
case 30:
|
||||
{
|
||||
Box* b = node->GetBox(0);
|
||||
Value v = tryGetValue(b, Float2::Zero).AsFloat2();
|
||||
|
||||
int32 subIndex = box->ID - 1;
|
||||
ASSERT(subIndex >= 0 && subIndex < 2);
|
||||
|
||||
value = Value(ValueType::Float, v.Value + _subs[subIndex]);
|
||||
value = tryGetValue(node->GetBox(0), Float2::Zero).AsFloat2();
|
||||
const int32 subIndex = box->ID - 1;
|
||||
if (subIndex >= 0 && subIndex < 2)
|
||||
value = Value(ValueType::Float, value.Value + _subs[subIndex]);
|
||||
break;
|
||||
}
|
||||
case 31:
|
||||
{
|
||||
Box* b = node->GetBox(0);
|
||||
Value v = tryGetValue(b, Float3::Zero).AsFloat3();
|
||||
|
||||
int32 subIndex = box->ID - 1;
|
||||
ASSERT(subIndex >= 0 && subIndex < 3);
|
||||
|
||||
value = Value(ValueType::Float, v.Value + _subs[subIndex]);
|
||||
value = tryGetValue(node->GetBox(0), Float3::Zero).AsFloat3();
|
||||
const int32 subIndex = box->ID - 1;
|
||||
if (subIndex >= 0 && subIndex < 3)
|
||||
value = Value(ValueType::Float, value.Value + _subs[subIndex]);
|
||||
break;
|
||||
}
|
||||
case 32:
|
||||
{
|
||||
Box* b = node->GetBox(0);
|
||||
Value v = tryGetValue(b, Float4::Zero).AsFloat4();
|
||||
|
||||
int32 subIndex = box->ID - 1;
|
||||
ASSERT(subIndex >= 0 && subIndex < 4);
|
||||
|
||||
value = Value(ValueType::Float, v.Value + _subs[subIndex]);
|
||||
value = tryGetValue(node->GetBox(0), Float4::Zero).AsFloat4();
|
||||
const int32 subIndex = box->ID - 1;
|
||||
if (subIndex >= 0 && subIndex < 4)
|
||||
value = Value(ValueType::Float, value.Value + _subs[subIndex]);
|
||||
break;
|
||||
}
|
||||
case 33:
|
||||
|
||||
Reference in New Issue
Block a user