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

# Conflicts:
#	Source/Engine/Physics/PhysX/SimulationEventCallbackPhysX.cpp
This commit is contained in:
Wojtek Figat
2023-09-13 10:29:28 +02:00
22 changed files with 1328 additions and 136 deletions

View File

@@ -456,6 +456,19 @@ void Actor::SetLayerName(const StringView& value)
LOG(Warning, "Unknown layer name '{0}'", value);
}
void Actor::SetLayerNameRecursive(const StringView& value)
{
for (int32 i = 0; i < 32; i++)
{
if (Level::Layers[i] == value)
{
SetLayerRecursive(i);
return;
}
}
LOG(Warning, "Unknown layer name '{0}'", value);
}
bool Actor::HasTag() const
{
return Tags.Count() != 0;
@@ -476,6 +489,13 @@ void Actor::AddTag(const Tag& tag)
Tags.AddUnique(tag);
}
void Actor::AddTagRecursive(const Tag& tag)
{
for (const auto& child : Children)
child->AddTagRecursive(tag);
Tags.AddUnique(tag);
}
void Actor::RemoveTag(const Tag& tag)
{
Tags.Remove(tag);
@@ -505,6 +525,17 @@ void Actor::SetLayer(int32 layerIndex)
OnLayerChanged();
}
void Actor::SetLayerRecursive(int32 layerIndex)
{
layerIndex = Math::Clamp(layerIndex, 0, 31);
for (const auto& child : Children)
child->SetLayerRecursive(layerIndex);
if (layerIndex == _layer)
return;
_layer = layerIndex;
OnLayerChanged();
}
void Actor::SetName(const StringView& value)
{
if (_name == value)

View File

@@ -102,6 +102,12 @@ public:
/// <param name="layerIndex">The index of the layer.</param>
API_PROPERTY() void SetLayer(int32 layerIndex);
/// <summary>
/// Sets the layer recursively for all underlying children.
/// </summary>
/// <param name="layerIndex">The index of the layer.</param>
API_FUNCTION() void SetLayerRecursive(int32 layerIndex);
/// <summary>
/// Gets the name of the layer.
/// </summary>
@@ -113,6 +119,11 @@ public:
/// </summary>
API_PROPERTY() void SetLayerName(const StringView& value);
/// <summary>
/// Sets the name of the layer recursively for actor and for all underlying child actors.
/// </summary>
API_FUNCTION() void SetLayerNameRecursive(const StringView& value);
/// <summary>
/// Determines whether this actor has any tag assigned.
/// </summary>
@@ -136,6 +147,12 @@ public:
/// <param name="tag">The tag to add.</param>
API_FUNCTION() void AddTag(const Tag& tag);
/// <summary>
/// Adds a tag to the actor and for all underlying child actors.
/// </summary>
/// <param name="tag">The tag to add.</param>
API_FUNCTION() void AddTagRecursive(const Tag& tag);
/// <summary>
/// Removes a tag to the actor
/// </summary>

View File

@@ -484,8 +484,7 @@ namespace
void Spline::OnDebugDraw()
{
const Color color = GetSplineColor();
DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true);
DrawSpline(this, GetSplineColor().AlphaMultiplied(0.7f), _transform, true);
// Base
Actor::OnDebugDraw();
@@ -493,8 +492,7 @@ void Spline::OnDebugDraw()
void Spline::OnDebugDrawSelected()
{
const Color color = GetSplineColor();
DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false);
DrawSpline(this, Color::White, _transform, false);
// Base
Actor::OnDebugDrawSelected();

View File

@@ -354,6 +354,7 @@ public:
protected:
#if USE_EDITOR
// Spline color getter for debug drawing, can be overriden by custom spline types.
virtual Color GetSplineColor()
{
return Color::White;

View File

@@ -223,9 +223,10 @@ void WheeledVehicle::Setup()
return;
// Release previous
void* scene = GetPhysicsScene()->GetPhysicsScene();
if (_vehicle)
{
PhysicsBackend::RemoveVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
PhysicsBackend::RemoveVehicle(scene, this);
PhysicsBackend::DestroyVehicle(_vehicle, (int32)_driveTypeCurrent);
_vehicle = nullptr;
}
@@ -236,7 +237,7 @@ void WheeledVehicle::Setup()
if (!_vehicle)
return;
_driveTypeCurrent = _driveType;
PhysicsBackend::AddVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
PhysicsBackend::AddVehicle(scene, this);
PhysicsBackend::SetRigidDynamicActorSolverIterationCounts(_actor, 12, 4);
#else
LOG(Fatal, "Vehicles are not supported.");
@@ -358,8 +359,23 @@ void WheeledVehicle::OnColliderChanged(Collider* c)
{
RigidBody::OnColliderChanged(c);
// Rebuild vehicle when someone adds/removed wheels
Setup();
if (_useWheelsUpdates)
{
// Rebuild vehicle when someone adds/removed wheels
Setup();
}
}
void WheeledVehicle::OnActiveInTreeChanged()
{
// Skip rebuilds from per-wheel OnColliderChanged when whole vehicle is toggled
_useWheelsUpdates = false;
RigidBody::OnActiveInTreeChanged();
_useWheelsUpdates = true;
// Perform whole rebuild when it gets activated
if (IsActiveInHierarchy())
Setup();
}
void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene* previous)

View File

@@ -329,6 +329,7 @@ private:
DifferentialSettings _differential;
GearboxSettings _gearbox;
bool _fixInvalidForwardDir = false; // [Deprecated on 13.06.2023, expires on 13.06.2025]
bool _useWheelsUpdates = true;
public:
/// <summary>
@@ -484,6 +485,7 @@ public:
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnColliderChanged(Collider* c) override;
void OnActiveInTreeChanged() override;
protected:
void OnPhysicsSceneChanged(PhysicsScene* previous) override;

View File

@@ -1231,18 +1231,42 @@ void* PhysicsBackend::CreateScene(const PhysicsSettings& settings)
PxSceneDesc sceneDesc(ToleranceScale);
sceneDesc.gravity = C2P(settings.DefaultGravity);
sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS;
sceneDesc.flags |= PxSceneFlag::eENABLE_PCM;
if (!settings.DisableCCD)
sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
sceneDesc.simulationEventCallback = &scenePhysX->EventsCallback;
sceneDesc.filterShader = FilterShader;
sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
sceneDesc.solverType = PxSolverType::ePGS;
switch (settings.SolverType)
{
case PhysicsSolverType::ProjectedGaussSeidelIterativeSolver:
sceneDesc.solverType = PxSolverType::ePGS;
break;
case PhysicsSolverType::TemporalGaussSeidelSolver:
sceneDesc.solverType = PxSolverType::eTGS;
break;
}
if (sceneDesc.cpuDispatcher == nullptr)
{
scenePhysX->CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp<uint32>(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4));
CHECK_INIT(scenePhysX->CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
sceneDesc.cpuDispatcher = scenePhysX->CpuDispatcher;
}
switch (settings.BroadPhaseType)
{
case PhysicsBroadPhaseType::SweepAndPrune:
sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP;
break;
case PhysicsBroadPhaseType::MultiBoxPruning:
sceneDesc.broadPhaseType = PxBroadPhaseType::eMBP;
break;
case PhysicsBroadPhaseType::AutomaticBoxPruning:
sceneDesc.broadPhaseType = PxBroadPhaseType::eABP;
break;
case PhysicsBroadPhaseType::ParallelAutomaticBoxPruning:
sceneDesc.broadPhaseType = PxBroadPhaseType::ePABP;
break;
}
// Create scene
scenePhysX->Scene = PhysX->createScene(sceneDesc);

View File

@@ -11,8 +11,10 @@
namespace
{
void ClearColliderFromCollection(PhysicsColliderActor* collider, Array<SimulationEventCallback::CollidersPair>& collection)
void ClearColliderFromCollection(const PhysicsColliderActor* collider, Array<SimulationEventCallback::CollidersPair>& collection)
{
if (collection.IsEmpty())
return;
for (int32 i = 0; i < collection.Count(); i++)
{
if (collection[i].First == collider || collection[i].Second == collider)

View File

@@ -57,6 +57,8 @@ void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE(FrictionCombineMode);
DESERIALIZE(RestitutionCombineMode);
DESERIALIZE(DisableCCD);
DESERIALIZE(BroadPhaseType);
DESERIALIZE(SolverType);
DESERIALIZE(MaxDeltaTime);
DESERIALIZE(EnableSubstepping);
DESERIALIZE(SubstepDeltaTime);

View File

@@ -6,6 +6,50 @@
#include "Engine/Core/Math/Vector3.h"
#include "Types.h"
/// <summary>
/// Broad phase algorithm used in the simulation.
/// <see href="https://nvidia-omniverse.github.io/PhysX/physx/5.1.0/_build/physx/latest/struct_px_broad_phase_type.html"/>
/// </summary>
API_ENUM() enum class PhysicsBroadPhaseType
{
/// <summary>
/// 3-axes sweep-and-prune. Good generic choice with great performance when many objects are sleeping.
/// </summary>
SweepAndPrune = 0,
/// <summary>
/// Alternative broad phase algorithm that does not suffer from the same performance issues as SAP when all objects are moving or when inserting large numbers of objects.
/// </summary>
MultiBoxPruning = 1,
/// <summary>
/// Revisited implementation of MBP, which automatically manages broad-phase regions.
/// </summary>
AutomaticBoxPruning = 2,
/// <summary>
/// Parallel implementation of ABP. It can often be the fastest (CPU) broadphase, but it can use more memory than ABP.
/// </summary>
ParallelAutomaticBoxPruning = 3,
};
/// <summary>
/// The type of solver used in the simulation.
/// <see href="https://nvidia-omniverse.github.io/PhysX/physx/5.1.0/_build/physx/latest/struct_px_solver_type.html"/>
/// </summary>
API_ENUM() enum class PhysicsSolverType
{
/// <summary>
/// The iterative sequential impulse solver.
/// </summary>
ProjectedGaussSeidelIterativeSolver = 0,
/// <summary>
/// Non linear iterative solver. This kind of solver can lead to improved convergence and handle large mass ratios, long chains and jointed systems better. It is slightly more expensive than the default solver and can introduce more energy to correct joint and contact errors.
/// </summary>
TemporalGaussSeidelSolver = 1,
};
/// <summary>
/// Physics simulation settings container.
/// </summary>
@@ -43,6 +87,18 @@ public:
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Simulation\")")
bool DisableCCD = false;
/// <summary>
/// Broad phase algorithm to use in the simulation.
/// </summary>
API_FIELD(Attributes="EditorOrder(71), EditorDisplay(\"Simulation\")")
PhysicsBroadPhaseType BroadPhaseType = PhysicsBroadPhaseType::ParallelAutomaticBoxPruning;
/// <summary>
/// The solver type to use in the simulation.
/// </summary>
API_FIELD(Attributes="EditorOrder(72), EditorDisplay(\"Simulation\")")
PhysicsSolverType SolverType = PhysicsSolverType::ProjectedGaussSeidelIterativeSolver;
/// <summary>
/// The maximum allowed delta time (in seconds) for the physics simulation step.
/// </summary>

View File

@@ -383,6 +383,8 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
SERIALIZE(BaseLOD);
SERIALIZE(LODCount);
SERIALIZE(TriangleReduction);
SERIALIZE(SloppyOptimization);
SERIALIZE(LODTargetError);
SERIALIZE(ImportMaterials);
SERIALIZE(ImportTextures);
SERIALIZE(RestoreMaterialsOnReimport);
@@ -424,6 +426,8 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
DESERIALIZE(BaseLOD);
DESERIALIZE(LODCount);
DESERIALIZE(TriangleReduction);
DESERIALIZE(SloppyOptimization);
DESERIALIZE(LODTargetError);
DESERIALIZE(ImportMaterials);
DESERIALIZE(ImportTextures);
DESERIALIZE(RestoreMaterialsOnReimport);
@@ -1528,7 +1532,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
int32 dstMeshIndexCountTarget = int32(srcMeshIndexCount * triangleReduction) / 3 * 3;
Array<unsigned int> indices;
indices.Resize(dstMeshIndexCountTarget);
int32 dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget);
int32 dstMeshIndexCount = {};
if (options.SloppyOptimization)
dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget);
else
dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError);
indices.Resize(dstMeshIndexCount);
if (dstMeshIndexCount == 0)
continue;

View File

@@ -322,6 +322,12 @@ public:
// The target amount of triangles for the generated LOD (based on the higher LOD). Normalized to range 0-1. For instance 0.4 cuts the triangle count to 40%.
API_FIELD(Attributes="EditorOrder(1130), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry)), Limit(0, 1, 0.001f)")
float TriangleReduction = 0.5f;
// Whether to do a sloppy mesh optimization. This is faster but does not follow the topology of the original mesh.
API_FIELD(Attributes="EditorOrder(1140), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(ShowGeometry))")
bool SloppyOptimization = true;
// Only used if Sloppy is false. Target error is an approximate measure of the deviation from the original mesh using distance normalized to [0..1] range (e.g. 1e-2f means that simplifier will try to maintain the error to be below 1% of the mesh extents).
API_FIELD(Attributes="EditorOrder(1150), EditorDisplay(\"Level Of Detail\"), VisibleIf(nameof(SloppyOptimization), true), Limit(0.01f, 1, 0.001f)")
float LODTargetError = 0.1f;
public: // Materials

View File

@@ -199,11 +199,11 @@ namespace FlaxEngine.GUI
// UI navigation
if (_canvas.ReceivesEvents)
{
UpdateNavigation(deltaTime, _canvas.NavigationInputActionUp, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp);
UpdateNavigation(deltaTime, _canvas.NavigationInputActionDown, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown);
UpdateNavigation(deltaTime, _canvas.NavigationInputActionLeft, NavDirection.Left, ref _navigationHeldTimeLeft, ref _navigationRateTimeLeft);
UpdateNavigation(deltaTime, _canvas.NavigationInputActionRight, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight);
UpdateNavigation(deltaTime, _canvas.NavigationInputActionSubmit, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused);
UpdateNavigation(deltaTime, _canvas.NavigateUp.Name, NavDirection.Up, ref _navigationHeldTimeUp, ref _navigationRateTimeUp);
UpdateNavigation(deltaTime, _canvas.NavigateDown.Name, NavDirection.Down, ref _navigationHeldTimeDown, ref _navigationRateTimeDown);
UpdateNavigation(deltaTime, _canvas.NavigateLeft.Name, NavDirection.Left, ref _navigationHeldTimeLeft, ref _navigationRateTimeLeft);
UpdateNavigation(deltaTime, _canvas.NavigateRight.Name, NavDirection.Right, ref _navigationHeldTimeRight, ref _navigationRateTimeRight);
UpdateNavigation(deltaTime, _canvas.NavigateSubmit.Name, ref _navigationHeldTimeSubmit, ref _navigationRateTimeSubmit, SubmitFocused);
}
else
{

View File

@@ -263,39 +263,39 @@ namespace FlaxEngine
public float NavigationInputRepeatRate { get; set; } = 0.05f;
/// <summary>
/// The name of the input action for performing UI navigation Up (from Input Settings).
/// The input action for performing UI navigation Up (from Input Settings).
/// </summary>
[EditorOrder(510), EditorDisplay("Navigation", "Navigate Up")]
[Tooltip("The name of the input action for performing UI navigation Up (from Input Settings).")]
public string NavigationInputActionUp { get; set; } = "NavigateUp";
[Tooltip("The input action for performing UI navigation Up (from Input Settings).")]
public InputEvent NavigateUp { get; set; } = new InputEvent("NavigateUp");
/// <summary>
/// The name of the input action for performing UI navigation Down (from Input Settings).
/// The input action for performing UI navigation Down (from Input Settings).
/// </summary>
[EditorOrder(520), EditorDisplay("Navigation", "Navigate Down")]
[Tooltip("The name of the input action for performing UI navigation Down (from Input Settings).")]
public string NavigationInputActionDown { get; set; } = "NavigateDown";
[Tooltip("The input action for performing UI navigation Down (from Input Settings).")]
public InputEvent NavigateDown { get; set; } = new InputEvent("NavigateDown");
/// <summary>
/// The name of the input action for performing UI navigation Left (from Input Settings).
/// The input action for performing UI navigation Left (from Input Settings).
/// </summary>
[EditorOrder(530), EditorDisplay("Navigation", "Navigate Left")]
[Tooltip("The name of the input action for performing UI navigation Left (from Input Settings).")]
public string NavigationInputActionLeft { get; set; } = "NavigateLeft";
[Tooltip("The input action for performing UI navigation Left (from Input Settings).")]
public InputEvent NavigateLeft { get; set; } = new InputEvent("NavigateLeft");
/// <summary>
/// The name of the input action for performing UI navigation Right (from Input Settings).
/// The input action for performing UI navigation Right (from Input Settings).
/// </summary>
[EditorOrder(540), EditorDisplay("Navigation", "Navigate Right")]
[Tooltip("The name of the input action for performing UI navigation Right (from Input Settings).")]
public string NavigationInputActionRight { get; set; } = "NavigateRight";
[Tooltip("The input action for performing UI navigation Right (from Input Settings).")]
public InputEvent NavigateRight { get; set; } = new InputEvent("NavigateRight");
/// <summary>
/// The name of the input action for performing UI navigation Submit (from Input Settings).
/// The input action for performing UI navigation Submit (from Input Settings).
/// </summary>
[EditorOrder(540), EditorDisplay("Navigation", "Navigate Submit")]
[Tooltip("The name of the input action for performing UI navigation Submit (from Input Settings).")]
public string NavigationInputActionSubmit { get; set; } = "NavigateSubmit";
[EditorOrder(550), EditorDisplay("Navigation", "Navigate Submit")]
[Tooltip("The input action for performing UI navigation Submit (from Input Settings).")]
public InputEvent NavigateSubmit { get; set; } = new InputEvent("NavigateSubmit");
#endregion
@@ -620,14 +620,36 @@ namespace FlaxEngine
jsonWriter.WriteValue(NavigationInputRepeatDelay);
jsonWriter.WritePropertyName("NavigationInputRepeatRate");
jsonWriter.WriteValue(NavigationInputRepeatRate);
jsonWriter.WritePropertyName("NavigationInputActionUp");
jsonWriter.WriteValue(NavigationInputActionUp);
jsonWriter.WritePropertyName("NavigationInputActionDown");
jsonWriter.WriteValue(NavigationInputActionDown);
jsonWriter.WritePropertyName("NavigationInputActionLeft");
jsonWriter.WriteValue(NavigationInputActionLeft);
jsonWriter.WritePropertyName("NavigationInputActionRight");
jsonWriter.WriteValue(NavigationInputActionRight);
jsonWriter.WritePropertyName("NavigateUp");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateUp.Name);
jsonWriter.WriteEndObject();
jsonWriter.WritePropertyName("NavigateDown");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateDown.Name);
jsonWriter.WriteEndObject();
jsonWriter.WritePropertyName("NavigateLeft");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateLeft.Name);
jsonWriter.WriteEndObject();
jsonWriter.WritePropertyName("NavigateRight");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateRight.Name);
jsonWriter.WriteEndObject();
jsonWriter.WritePropertyName("NavigateSubmit");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateSubmit.Name);
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
@@ -713,25 +735,45 @@ namespace FlaxEngine
jsonWriter.WritePropertyName("NavigationInputRepeatRate");
jsonWriter.WriteValue(NavigationInputRepeatRate);
}
if (NavigationInputActionUp != other.NavigationInputActionUp)
if (NavigateUp.Name != other.NavigateUp.Name)
{
jsonWriter.WritePropertyName("NavigationInputActionUp");
jsonWriter.WriteValue(NavigationInputActionUp);
jsonWriter.WritePropertyName("NavigateUp");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateUp.Name);
jsonWriter.WriteEndObject();
}
if (NavigationInputActionDown != other.NavigationInputActionDown)
if (NavigateDown.Name != other.NavigateDown.Name)
{
jsonWriter.WritePropertyName("NavigationInputActionDown");
jsonWriter.WriteValue(NavigationInputActionDown);
jsonWriter.WritePropertyName("NavigateDown");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateDown.Name);
jsonWriter.WriteEndObject();
}
if (NavigationInputActionLeft != other.NavigationInputActionLeft)
if (NavigateLeft.Name != other.NavigateLeft.Name)
{
jsonWriter.WritePropertyName("NavigationInputActionLeft");
jsonWriter.WriteValue(NavigationInputActionLeft);
jsonWriter.WritePropertyName("NavigateLeft");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateLeft.Name);
jsonWriter.WriteEndObject();
}
if (NavigationInputActionRight != other.NavigationInputActionRight)
if (NavigateRight.Name != other.NavigateRight.Name)
{
jsonWriter.WritePropertyName("NavigationInputActionRight");
jsonWriter.WriteValue(NavigationInputActionRight);
jsonWriter.WritePropertyName("NavigateRight");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateRight.Name);
jsonWriter.WriteEndObject();
}
if (NavigateSubmit.Name != other.NavigateSubmit.Name)
{
jsonWriter.WritePropertyName("NavigateSubmit");
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("Name");
jsonWriter.WriteValue(NavigateSubmit.Name);
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndObject();