Ejemplo n.º 1
0
void FNiagaraEditor::DeleteSelectedNodes()
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = NodeGraphEditorPtr.Pin();
	if (!CurrentGraphEditor.IsValid())
	{
		return;
	}

	const FScopedTransaction Transaction(FGenericCommands::Get().Delete->GetDescription());
	CurrentGraphEditor->GetCurrentGraph()->Modify();

	const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes();
	CurrentGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		if (UEdGraphNode* Node = Cast<UEdGraphNode>(*NodeIt))
		{
			if (Node->CanUserDeleteNode())
			{
				Node->Modify();
				Node->DestroyNode();
			}
		}
	}
}
Ejemplo n.º 2
0
void FSoundCueEditor::SyncInBrowser()
{
	TArray<UObject*> ObjectsToSync;
	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		USoundCueGraphNode* SelectedNode = Cast<USoundCueGraphNode>(*NodeIt);

		if (SelectedNode)
		{
			USoundNodeWavePlayer* SelectedWave = Cast<USoundNodeWavePlayer>(SelectedNode->SoundNode);
			if (SelectedWave && SelectedWave->GetSoundWave())
			{
				ObjectsToSync.AddUnique(SelectedWave->GetSoundWave());
			}
			//ObjectsToSync.AddUnique(SelectedNode->SoundNode);
		}
	}

	if (ObjectsToSync.Num() > 0)
	{
		GEditor->SyncBrowserToObjects(ObjectsToSync);
	}
}
FBoxSphereBounds UNavMeshRenderingComponent::CalcBounds(const FTransform& LocalToWorld) const
{
	FBox BoundingBox(0);
#if WITH_RECAST
	ARecastNavMesh* NavMesh = Cast<ARecastNavMesh>(GetOwner());
	if (NavMesh)
	{
		BoundingBox = NavMesh->GetNavMeshBounds();
		if (NavMesh->bDrawOctree)
		{
			const UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
			const FNavigationOctree* NavOctree = NavSys ? NavSys->GetNavOctree() : NULL;
			if (NavOctree)
			{
				for (FNavigationOctree::TConstIterator<> NodeIt(*NavOctree); NodeIt.HasPendingNodes(); NodeIt.Advance())
				{
					const FOctreeNodeContext& CorrentContext = NodeIt.GetCurrentContext();
					BoundingBox += CorrentContext.Bounds.GetBox();
				}
			}
		}
	}
#endif
	return FBoxSphereBounds(BoundingBox);
}
Ejemplo n.º 4
0
void FSoundCueEditor::PlayNode()
{
	// already checked that only one node is selected
	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		PlaySingleNode(CastChecked<UEdGraphNode>(*NodeIt));
	}
}
Ejemplo n.º 5
0
void SGraphEditorImpl::ReconstructNodes()
{
	const UEdGraphSchema* Schema = this->EdGraphObj->GetSchema();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt( GraphPanel->SelectionManager.GetSelectedNodes() ); NodeIt; ++NodeIt)
	{
		if (UEdGraphNode* Node = Cast<UEdGraphNode>(*NodeIt))
		{
			Schema->ReconstructNode(*Node);
		}
	}
	NotifyGraphChanged();
}
Ejemplo n.º 6
0
void SGraphEditorImpl::BreakNodeLinks()
{
	const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links") );

	for (FGraphPanelSelectionSet::TConstIterator NodeIt( GraphPanel->SelectionManager.GetSelectedNodes() ); NodeIt; ++NodeIt)
	{
		if (UEdGraphNode* Node = Cast<UEdGraphNode>(*NodeIt))
		{
			const UEdGraphSchema* Schema = Node->GetSchema();
			Schema->BreakNodeLinks(*Node);
		}
	}
}
Ejemplo n.º 7
0
void SGraphPanel::UpdateSelectedNodesPositions (FVector2D PositionIncrement)
{
	for (FGraphPanelSelectionSet::TIterator NodeIt(SelectionManager.SelectedNodes); NodeIt; ++NodeIt)
	{
		TSharedRef<SNode>* pWidget = NodeToWidgetLookup.Find(*NodeIt);
		if (pWidget != NULL)
		{
			SNode& Widget = pWidget->Get();
			SNode::FNodeSet NodeFilter;
			Widget.MoveTo(Widget.GetPosition() + PositionIncrement, NodeFilter);
		}
	}
}
Ejemplo n.º 8
0
void UK2Node_MacroInstance::AllocateDefaultPins()
{
	UK2Node::AllocateDefaultPins();

	PreloadObject(MacroGraphReference.GetBlueprint());
	UEdGraph* MacroGraph = MacroGraphReference.GetGraph();
	
	if (MacroGraph != NULL)
	{
		PreloadObject(MacroGraph);

		// Preload the macro graph, if needed, so that we can get the proper pins
		if (MacroGraph->HasAnyFlags(RF_NeedLoad))
		{
			PreloadObject(MacroGraph);
			FBlueprintEditorUtils::PreloadMembers(MacroGraph);
		}

		for (TArray<UEdGraphNode*>::TIterator NodeIt(MacroGraph->Nodes); NodeIt; ++NodeIt)
		{
			if (UK2Node_Tunnel* TunnelNode = Cast<UK2Node_Tunnel>(*NodeIt))
			{
				// Only want exact tunnel nodes, more specific nodes like composites or macro instances shouldn't be grabbed.
				if (TunnelNode->GetClass() == UK2Node_Tunnel::StaticClass())
				{
					for (TArray<UEdGraphPin*>::TIterator PinIt(TunnelNode->Pins); PinIt; ++PinIt)
					{
						UEdGraphPin* PortPin = *PinIt;

						// We're not interested in any pins that have been expanded internally on the macro
						if (PortPin->ParentPin == NULL)
						{
							UEdGraphPin* NewLocalPin = CreatePin(
								UEdGraphPin::GetComplementaryDirection(PortPin->Direction),
								PortPin->PinType.PinCategory,
								PortPin->PinType.PinSubCategory,
								PortPin->PinType.PinSubCategoryObject.Get(),
								PortPin->PinType.bIsArray,
								PortPin->PinType.bIsReference,
								PortPin->PinName);
							NewLocalPin->DefaultValue = NewLocalPin->AutogeneratedDefaultValue = PortPin->DefaultValue;
						}
					}
				}
			}
		}
	}
}
Ejemplo n.º 9
0
void FSoundCueEditor::AddInput()
{
	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	// Iterator used but should only contain one node
	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		USoundCueGraphNode* SelectedNode = Cast<USoundCueGraphNode>(*NodeIt);

		if (SelectedNode)
		{
			SelectedNode->AddInputPin();
			break;
		}
	}
}
bool FSoundClassEditor::CanRemoveNodes() const
{
	const FGraphPanelSelectionSet SelectedNodes = GraphEditor->GetSelectedNodes();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		USoundClassGraphNode* Node = Cast<USoundClassGraphNode>(*NodeIt);

		if (Node && Node->CanUserDeleteNode())
		{
			return true;
		}
	}

	return false;
}
void USoundClassGraph::RecursivelyRemoveNodes(const TSet<class UObject*> NodesToRemove)
{
	Modify();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(NodesToRemove); NodeIt; ++NodeIt)
	{
		USoundClassGraphNode* Node = Cast<USoundClassGraphNode>(*NodeIt);

		if (Node && Node->CanUserDeleteNode())
		{
			RecursivelyRemoveNode(Node);
		}
	}

	LinkSoundClasses();
}
Ejemplo n.º 12
0
bool FSoundCueEditor::CanDeleteNodes() const
{
	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	if (SelectedNodes.Num() == 1)
	{
		for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
		{
			if (Cast<USoundCueGraphNode_Root>(*NodeIt))
			{
				// Return false if only root node is selected, as it can't be deleted
				return false;
			}
		}
	}

	return SelectedNodes.Num() > 0;
}
Ejemplo n.º 13
0
bool FSoundCueEditor::CanSyncInBrowser() const
{
	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		USoundCueGraphNode* SelectedNode = Cast<USoundCueGraphNode>(*NodeIt);

		if (SelectedNode)
		{
			USoundNodeWavePlayer* WavePlayer = Cast<USoundNodeWavePlayer>(SelectedNode->SoundNode);

			if (WavePlayer && WavePlayer->GetSoundWave())
			{
				return true;
			}
		}
	}
	return false;
}
Ejemplo n.º 14
0
void FNiagaraEditor::DeleteSelectedNodes()
{
	TSharedPtr<SGraphEditor> NodeGraphEditor = NodeGraphEditorPtr.Pin();
	if (!NodeGraphEditor.IsValid())
	{
		return;
	}

	const FGraphPanelSelectionSet SelectedNodes = NodeGraphEditor->GetSelectedNodes();
	NodeGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt( SelectedNodes ); NodeIt; ++NodeIt)
	{
		if (UEdGraphNode* Node = Cast<UEdGraphNode>(*NodeIt))
		{
			if (Node->CanUserDeleteNode())
			{
				Node->DestroyNode();
			}
		}
	}
}
Ejemplo n.º 15
0
// Exports a set of nodes to text
void FEdGraphUtilities::ExportNodesToText(TSet<UObject*> NodesToExport, /*out*/ FString& ExportedText)
{
	// Clear the mark state for saving.
	UnMarkAllObjects(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp));

	FStringOutputDevice Archive;
	const FExportObjectInnerContext Context;

	// Export each of the selected nodes
	UObject* LastOuter = NULL;
	for (TSet<UObject*>::TConstIterator NodeIt(NodesToExport); NodeIt; ++NodeIt)
	{
		UObject* Node = *NodeIt;

		// The nodes should all be from the same scope
		UObject* ThisOuter = Node->GetOuter();
		check((LastOuter == ThisOuter) || (LastOuter == NULL));
		LastOuter = ThisOuter;

		UExporter::ExportToOutputDevice(&Context, Node, NULL, Archive, TEXT("copy"), 0, PPF_ExportsNotFullyQualified|PPF_Copy|PPF_Delimited, false, ThisOuter);
	}

	ExportedText = Archive;
}
Ejemplo n.º 16
0
void FSoundCueEditor::DeleteSelectedNodes()
{
	const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "SoundCueEditorDeleteSelectedNode", "Delete Selected Sound Cue Node") );

	SoundCueGraphEditor->GetCurrentGraph()->Modify();

	const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

	SoundCueGraphEditor->ClearSelectionSet();

	for (FGraphPanelSelectionSet::TConstIterator NodeIt(SelectedNodes); NodeIt; ++NodeIt)
	{
		UEdGraphNode* Node = CastChecked<UEdGraphNode>(*NodeIt);

		if (Node->CanUserDeleteNode())
		{
			if (USoundCueGraphNode* SoundGraphNode = Cast<USoundCueGraphNode>(Node))
			{
				USoundNode* DelNode = SoundGraphNode->SoundNode;

				FBlueprintEditorUtils::RemoveNode(NULL, SoundGraphNode, true);

				// Make sure SoundCue is updated to match graph
				SoundCue->CompileSoundNodesFromGraphNodes();

				// Remove this node from the SoundCue's list of all SoundNodes
				SoundCue->AllNodes.Remove(DelNode);
				SoundCue->MarkPackageDirty();
			}
			else
			{
				FBlueprintEditorUtils::RemoveNode(NULL, Node, true);
			}
		}
	}
}
void UNavMeshRenderingComponent::GatherData(struct FNavMeshSceneProxyData* CurrentData) const
{
#if WITH_RECAST
	const ARecastNavMesh* NavMesh = Cast<ARecastNavMesh>(GetOwner());

	CurrentData->Reset();
	CurrentData->bEnableDrawing = NavMesh->bEnableDrawing;
	CurrentData->bNeedsNewData = false;

	if (CurrentData && NavMesh && NavMesh->bEnableDrawing)
	{
		FHitProxyId HitProxyId = FHitProxyId();
		CurrentData->bDrawPathCollidingGeometry = NavMesh->bDrawPathCollidingGeometry;

		CurrentData->NavMeshDrawOffset.Z = NavMesh->DrawOffset;
		CurrentData->NavMeshGeometry.bGatherPolyEdges = NavMesh->bDrawPolyEdges;
		CurrentData->NavMeshGeometry.bGatherNavMeshEdges = NavMesh->bDrawNavMeshEdges;

		const FNavDataConfig& NavConfig = NavMesh->GetConfig();
		CurrentData->NavMeshColors[RECAST_DEFAULT_AREA] = NavConfig.Color.DWColor() > 0 ? NavConfig.Color : NavMeshRenderColor_RecastMesh;
		for (uint8 i = 0; i < RECAST_DEFAULT_AREA; ++i)
		{
			CurrentData->NavMeshColors[i] = NavMesh->GetAreaIDColor(i);
		}

		// just a little trick to make sure navmeshes with different sized are not drawn with same offset
		CurrentData->NavMeshDrawOffset.Z += NavMesh->GetConfig().AgentRadius / 10.f;

		NavMesh->BeginBatchQuery();

		if (NavMesh->bDrawOctree)
		{
			const UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
			const FNavigationOctree* NavOctree = NavSys ? NavSys->GetNavOctree() : NULL;
			if (NavOctree)
			{
				for (FNavigationOctree::TConstIterator<> NodeIt(*NavOctree); NodeIt.HasPendingNodes(); NodeIt.Advance())
				{
					const FNavigationOctree::FNode& CurrentNode = NodeIt.GetCurrentNode();
					const FOctreeNodeContext& CorrentContext = NodeIt.GetCurrentContext();
					CurrentData->OctreeBounds.Add(CorrentContext.Bounds);

					FOREACH_OCTREE_CHILD_NODE(ChildRef)
					{
						if (CurrentNode.HasChild(ChildRef))
						{
							NodeIt.PushChild(ChildRef);
						}
					}
				}
			}
		}

		NavMesh->GetDebugGeometry(CurrentData->NavMeshGeometry);
		const TArray<FVector>& MeshVerts = CurrentData->NavMeshGeometry.MeshVerts;

		// @fixme, this is going to double up on lots of interior lines
		if (NavMesh->bDrawTriangleEdges)
		{
			for (int32 AreaIdx = 0; AreaIdx < RECAST_MAX_AREAS; ++AreaIdx)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.AreaIndices[AreaIdx];
				for (int32 Idx=0; Idx<MeshIndices.Num(); Idx += 3)
				{
					CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(MeshVerts[MeshIndices[Idx + 0]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx + 1]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges, DefaultEdges_LineThickness));
					CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(MeshVerts[MeshIndices[Idx + 1]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx + 2]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges, DefaultEdges_LineThickness));
					CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(MeshVerts[MeshIndices[Idx + 2]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx + 0]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges, DefaultEdges_LineThickness));
				}
			}
		}

		// make lines for tile edges
		if (NavMesh->bDrawPolyEdges)
		{
			const TArray<FVector>& TileEdgeVerts = CurrentData->NavMeshGeometry.PolyEdges;
			for (int32 Idx=0; Idx < TileEdgeVerts.Num(); Idx += 2)
			{
				CurrentData->TileEdgeLines.Add( FDebugRenderSceneProxy::FDebugLine(TileEdgeVerts[Idx] + CurrentData->NavMeshDrawOffset, TileEdgeVerts[Idx+1] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
			}
		}

		// make lines for navmesh edges
		if (NavMesh->bDrawNavMeshEdges)
		{
			const FColor EdgesColor = DarkenColor(CurrentData->NavMeshColors[RECAST_DEFAULT_AREA]);
			const TArray<FVector>& NavMeshEdgeVerts = CurrentData->NavMeshGeometry.NavMeshEdges;
			for (int32 Idx=0; Idx < NavMeshEdgeVerts.Num(); Idx += 2)
			{
				CurrentData->NavMeshEdgeLines.Add( FDebugRenderSceneProxy::FDebugLine(NavMeshEdgeVerts[Idx] + CurrentData->NavMeshDrawOffset, NavMeshEdgeVerts[Idx+1] + CurrentData->NavMeshDrawOffset, EdgesColor));
			}
		}

		// offset all navigation-link positions
		if (!NavMesh->bDrawClusters)
		{
			for (int32 OffMeshLineIndex = 0; OffMeshLineIndex < CurrentData->NavMeshGeometry.OffMeshLinks.Num(); ++OffMeshLineIndex)
			{
				FRecastDebugGeometry::FOffMeshLink& Link = CurrentData->NavMeshGeometry.OffMeshLinks[OffMeshLineIndex];
				const bool bLinkValid = (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left) && (Link.ValidEnds & FRecastDebugGeometry::OMLE_Right);

				if (NavMesh->bDrawFailedNavLinks || (NavMesh->bDrawNavLinks && bLinkValid))
				{
					const FVector V0 = Link.Left + CurrentData->NavMeshDrawOffset;
					const FVector V1 = Link.Right + CurrentData->NavMeshDrawOffset;
					const FColor LinkColor = ((Link.Direction && Link.ValidEnds) || (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left)) ? DarkenColor(CurrentData->NavMeshColors[Link.AreaID]) : NavMeshRenderColor_OffMeshConnectionInvalid;

					CacheArc(CurrentData->NavLinkLines, V0, V1, 0.4f, 4, LinkColor, LinkLines_LineThickness);

					const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
					CacheArrowHead(CurrentData->NavLinkLines, V1, V0+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
					if (Link.Direction)
					{
						CacheArrowHead(CurrentData->NavLinkLines, V0, V1+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
					}

					// if the connection as a whole is valid check if there are any of ends is invalid
					if (LinkColor != NavMeshRenderColor_OffMeshConnectionInvalid)
					{
						if (Link.Direction && (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left) == 0)
						{
							// left end invalid - mark it
							DrawWireCylinder(CurrentData->NavLinkLines, V0, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), NavMeshRenderColor_OffMeshConnectionInvalid, Link.Radius, NavMesh->AgentMaxStepHeight, 16, 0, DefaultEdges_LineThickness);
						}
						if ((Link.ValidEnds & FRecastDebugGeometry::OMLE_Right) == 0)
						{
							DrawWireCylinder(CurrentData->NavLinkLines, V1, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), NavMeshRenderColor_OffMeshConnectionInvalid, Link.Radius, NavMesh->AgentMaxStepHeight, 16, 0, DefaultEdges_LineThickness);
						}
					}
				}					
			}
		}

		if (NavMesh->bDrawTileLabels || NavMesh->bDrawPolygonLabels || NavMesh->bDrawDefaultPolygonCost || NavMesh->bDrawTileBounds)
		{
			// calculate appropriate points for displaying debug labels
			const int32 TilesCount = NavMesh->GetNavMeshTilesCount();
			CurrentData->DebugLabels.Reserve(TilesCount);

			for (int32 TileIndex = 0; TileIndex < TilesCount; ++TileIndex)
			{
				int32 X, Y, Layer;
				if (NavMesh->GetNavMeshTileXY(TileIndex, X, Y, Layer))
				{
					const FBox TileBoundingBox = NavMesh->GetNavMeshTileBounds(TileIndex);
					FVector TileLabelLocation = TileBoundingBox.GetCenter();
					TileLabelLocation.Z = TileBoundingBox.Max.Z;

					FNavLocation NavLocation(TileLabelLocation);
					if (!NavMesh->ProjectPoint(TileLabelLocation, NavLocation, FVector(NavMesh->TileSizeUU/100, NavMesh->TileSizeUU/100, TileBoundingBox.Max.Z-TileBoundingBox.Min.Z)))
					{
						NavMesh->ProjectPoint(TileLabelLocation, NavLocation, FVector(NavMesh->TileSizeUU/2, NavMesh->TileSizeUU/2, TileBoundingBox.Max.Z-TileBoundingBox.Min.Z));
					}

					if (NavMesh->bDrawTileLabels)
					{
						CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
							/*Location*/NavLocation.Location + CurrentData->NavMeshDrawOffset
							, /*Text*/FString::Printf(TEXT("(%d,%d:%d)"), X, Y, Layer)
							));
					}

					if (NavMesh->bDrawPolygonLabels || NavMesh->bDrawDefaultPolygonCost)
					{
						TArray<FNavPoly> Polys;
						NavMesh->GetPolysInTile(TileIndex, Polys);

						if (NavMesh->bDrawDefaultPolygonCost)
						{
							float DefaultCosts[RECAST_MAX_AREAS];
							float FixedCosts[RECAST_MAX_AREAS];

							NavMesh->GetDefaultQueryFilter()->GetAllAreaCosts(DefaultCosts, FixedCosts, RECAST_MAX_AREAS);

							for(int k = 0; k < Polys.Num(); ++k)
							{
								uint32 AreaID = NavMesh->GetPolyAreaID(Polys[k].Ref);

								CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
									/*Location*/Polys[k].Center + CurrentData->NavMeshDrawOffset
									, /*Text*/FString::Printf(TEXT("\\%.3f; %.3f\\"), DefaultCosts[AreaID], FixedCosts[AreaID])
									));
							}
						}
						else
						{
							for(int k = 0; k < Polys.Num(); ++k)
							{
								uint32 NavPolyIndex = 0;
								uint32 NavTileIndex = 0;
								NavMesh->GetPolyTileIndex(Polys[k].Ref, NavPolyIndex, NavTileIndex);

								CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
									/*Location*/Polys[k].Center + CurrentData->NavMeshDrawOffset
									, /*Text*/FString::Printf(TEXT("[%X:%X]"), NavTileIndex, NavPolyIndex)
									));
							}
						}
					}							

					if (NavMesh->bDrawTileBounds)
					{
						FBox TileBox = NavMesh->GetNavMeshTileBounds(TileIndex);

						float DrawZ = (TileBox.Min.Z + TileBox.Max.Z) * 0.5f;		// @hack average
						FVector LL(TileBox.Min.X, TileBox.Min.Y, DrawZ);
						FVector UR(TileBox.Max.X, TileBox.Max.Y, DrawZ);
						FVector UL(LL.X, UR.Y, DrawZ);
						FVector LR(UR.X, LL.Y, DrawZ);
						CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(LL, UL, NavMeshRenderColor_TileBounds, DefaultEdges_LineThickness));
						CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(UL, UR, NavMeshRenderColor_TileBounds, DefaultEdges_LineThickness));
						CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(UR, LR, NavMeshRenderColor_TileBounds, DefaultEdges_LineThickness));
						CurrentData->ThickLineItems.Add(FNavMeshSceneProxyData::FDebugThickLine(LR, LL, NavMeshRenderColor_TileBounds, DefaultEdges_LineThickness));
					}
				}
			}
		}

		CurrentData->bSkipDistanceCheck = GIsEditor && (GEngine->GetDebugLocalPlayer() == NULL);
		CurrentData->bDrawClusters = NavMesh->bDrawClusters;
		NavMesh->FinishBatchQuery();

		// Draw Mesh
		if (NavMesh->bDrawClusters)
		{
			for (int32 Idx = 0; Idx < CurrentData->NavMeshGeometry.Clusters.Num(); ++Idx)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.Clusters[Idx].MeshIndices;

				if (MeshIndices.Num() == 0)
				{
					continue;
				}

				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				DebugMeshData.ClusterColor = GetClusterColor(Idx);
				for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
				{
					AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset, DebugMeshData.ClusterColor);
				}
				for (int32 TriIdx=0; TriIdx < MeshIndices.Num(); TriIdx+=3)
				{
					AddTriangleHelper(DebugMeshData, MeshIndices[TriIdx], MeshIndices[TriIdx+1], MeshIndices[TriIdx+2]);
				}

				CurrentData->MeshBuilders.Add(DebugMeshData);
			}
		}
		else if (NavMesh->bDrawNavMesh)
		{			
			for (int32 AreaType = 0; AreaType < RECAST_MAX_AREAS; ++AreaType)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.AreaIndices[AreaType];

				if (MeshIndices.Num() == 0)
				{
					continue;
				}

				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
				{
					AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset, CurrentData->NavMeshColors[AreaType]);
				}
				for (int32 TriIdx=0; TriIdx < MeshIndices.Num(); TriIdx+=3)
				{
					AddTriangleHelper(DebugMeshData, MeshIndices[TriIdx], MeshIndices[TriIdx+1], MeshIndices[TriIdx+2]);
				}

				DebugMeshData.ClusterColor = CurrentData->NavMeshColors[AreaType];
				CurrentData->MeshBuilders.Add(DebugMeshData);
			}
		}

		if (NavMesh->bDrawPathCollidingGeometry)
		{
			// draw all geometry gathered in navoctree
			const FNavigationOctree* NavOctree = NavMesh->GetWorld()->GetNavigationSystem()->GetNavOctree();

			TArray<FVector> PathCollidingGeomVerts;
			TArray <int32> PathCollidingGeomIndices;
			for (FNavigationOctree::TConstIterator<> It(*NavOctree); It.HasPendingNodes(); It.Advance())
			{
				const FNavigationOctree::FNode& Node = It.GetCurrentNode();
				for (FNavigationOctree::ElementConstIt ElementIt(Node.GetElementIt()); ElementIt; ElementIt++)
				{
					const FNavigationOctreeElement& Element = *ElementIt;
					if (Element.ShouldUseGeometry(&NavMesh->NavDataConfig) && Element.Data.CollisionData.Num())
					{
						const FRecastGeometryCache CachedGeometry(Element.Data.CollisionData.GetData());
						AppendGeometry(PathCollidingGeomVerts, PathCollidingGeomIndices, CachedGeometry.Verts, CachedGeometry.Header.NumVerts, CachedGeometry.Indices, CachedGeometry.Header.NumFaces);
					}
				}
				FOREACH_OCTREE_CHILD_NODE(ChildRef)
				{
					if (Node.HasChild(ChildRef))
					{
						It.PushChild(ChildRef);
					}
				}
			}
			CurrentData->PathCollidingGeomIndices = PathCollidingGeomIndices;
			for (const auto& Vertex : PathCollidingGeomVerts)
			{
				CurrentData->PathCollidingGeomVerts.Add(FDynamicMeshVertex(Vertex));
			}

		}

		if (CurrentData->NavMeshGeometry.BuiltMeshIndices.Num() > 0)
		{
			FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
			for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
			{
				AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_RecastTileBeingRebuilt);
			}
			DebugMeshData.Indices.Append(CurrentData->NavMeshGeometry.BuiltMeshIndices);
			DebugMeshData.ClusterColor = NavMeshRenderColor_RecastTileBeingRebuilt;
			CurrentData->MeshBuilders.Add(DebugMeshData);

			// updates should be requested by FRecastNavMeshGenerator::TickAsyncBuild after tiles were refreshed
		}

		if (NavMesh->bDrawClusters)
		{
			for (int i = 0; i < CurrentData->NavMeshGeometry.ClusterLinks.Num(); i++)
			{
				const FRecastDebugGeometry::FClusterLink& CLink = CurrentData->NavMeshGeometry.ClusterLinks[i];
				const FVector V0 = CLink.FromCluster + CurrentData->NavMeshDrawOffset;
				const FVector V1 = CLink.ToCluster + CurrentData->NavMeshDrawOffset + FVector(0,0,20.0f);

				CacheArc(CurrentData->ClusterLinkLines, V0, V1, 0.4f, 4, FColor::Black, ClusterLinkLines_LineThickness);
				const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
				CacheArrowHead(CurrentData->ClusterLinkLines, V1, V0+VOffset, 30.f, FColor::Black, ClusterLinkLines_LineThickness);
			}
		}

		// cache segment links
		if (NavMesh->bDrawNavLinks)
		{
			for (int32 iArea = 0; iArea < RECAST_MAX_AREAS; iArea++)
			{
				const TArray<int32>& Indices = CurrentData->NavMeshGeometry.OffMeshSegmentAreas[iArea];
				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				int32 VertBase = 0;

				for (int32 i = 0; i < Indices.Num(); i++)
				{
					FRecastDebugGeometry::FOffMeshSegment& SegInfo = CurrentData->NavMeshGeometry.OffMeshSegments[Indices[i]];
					const FVector A0 = SegInfo.LeftStart + CurrentData->NavMeshDrawOffset;
					const FVector A1 = SegInfo.LeftEnd + CurrentData->NavMeshDrawOffset;
					const FVector B0 = SegInfo.RightStart + CurrentData->NavMeshDrawOffset;
					const FVector B1 = SegInfo.RightEnd + CurrentData->NavMeshDrawOffset;
					const FVector Edge0 = B0 - A0;
					const FVector Edge1 = B1 - A1;
					const float Len0 = Edge0.Size();
					const float Len1 = Edge1.Size();
					const FColor SegColor = DarkenColor(CurrentData->NavMeshColors[SegInfo.AreaID]);
					const FColor ColA = (SegInfo.ValidEnds & FRecastDebugGeometry::OMLE_Left) ? FColor::White : FColor::Black;
					const FColor ColB = (SegInfo.ValidEnds & FRecastDebugGeometry::OMLE_Right) ? FColor::White : FColor::Black;

					const int32 NumArcPoints = 8;
					const float ArcPtsScale = 1.0f / NumArcPoints;

					FVector Prev0 = EvalArc(A0, Edge0, Len0*0.25f, 0);
					FVector Prev1 = EvalArc(A1, Edge1, Len1*0.25f, 0);
					AddVertexHelper(DebugMeshData, Prev0, ColA);
					AddVertexHelper(DebugMeshData, Prev1, ColA);
					for (int32 j = 1; j <= NumArcPoints; j++)
					{
						const float u = j * ArcPtsScale;
						FVector Pt0 = EvalArc(A0, Edge0, Len0*0.25f, u);
						FVector Pt1 = EvalArc(A1, Edge1, Len1*0.25f, u);

						AddVertexHelper(DebugMeshData, Pt0, (j == NumArcPoints) ? ColB : FColor::White);
						AddVertexHelper(DebugMeshData, Pt1, (j == NumArcPoints) ? ColB : FColor::White);

						AddTriangleHelper(DebugMeshData, VertBase+0, VertBase+2, VertBase+1);
						AddTriangleHelper(DebugMeshData, VertBase+2, VertBase+3, VertBase+1);
						AddTriangleHelper(DebugMeshData, VertBase+0, VertBase+1, VertBase+2);
						AddTriangleHelper(DebugMeshData, VertBase+2, VertBase+1, VertBase+3);

						VertBase += 2;
						Prev0 = Pt0;
						Prev1 = Pt1;
					}
					VertBase += 2;

					DebugMeshData.ClusterColor = SegColor;
				}

				if (DebugMeshData.Indices.Num())
				{
					CurrentData->MeshBuilders.Add(DebugMeshData);
				}
			}
		}

		CurrentData->NavMeshGeometry.PolyEdges.Empty();
		CurrentData->NavMeshGeometry.NavMeshEdges.Empty();
	}
// Called every frame
void ASpacePartioner::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

	//check(bInitialized);
	//check(bDrawDebugInfo);

	if (bInitialized && bDrawDebugInfo)
	{

		int level = 0;
		float max;
		float offsetMax;
		float offset;
		FVector maxExtent;
		FVector center;
		FVector tempForCoercion;
		FBoxCenterAndExtent OldBounds = FBoxCenterAndExtent();

		int nodeCount = 0;
		int elementCount = 0;

		// Go through the nodes of the octree
		for (FSimpleOctree::TConstIterator<> NodeIt(*OctreeData); NodeIt.HasPendingNodes(); NodeIt.Advance())
		{
			const FSimpleOctree::FNode& CurrentNode = NodeIt.GetCurrentNode();
			const FOctreeNodeContext& CurrentContext = NodeIt.GetCurrentContext();
			const FBoxCenterAndExtent& CurrentBounds = CurrentContext.Bounds;

			nodeCount++;

			FOREACH_OCTREE_CHILD_NODE(ChildRef)
			{
				if (CurrentNode.HasChild(ChildRef))
				{
					NodeIt.PushChild(ChildRef);
				}
			}

			// If the extents have changed then we have moved a level.
			if (!OldBounds.Extent.Equals(CurrentBounds.Extent))
			{
				level++;
			}
			OldBounds = CurrentBounds;

			// UE_LOG(LogTemp, Log, TEXT("Level: %d"), level);

			// Draw Node Bounds
			tempForCoercion = CurrentBounds.Extent;
			max = tempForCoercion.GetMax();
			center = CurrentBounds.Center;

			// UE_LOG(LogTemp, Log, TEXT("center before: %s"), *center.ToString());
			
			// To understand the math here check out the constructors in FOctreeNodeContext
			// Offset nodes that are not the root bounds
			if (!OctreeData->GetRootBounds().Extent.Equals(CurrentBounds.Extent))
			{
				for (int i = 1; i < level; i++)
				{
					// Calculate offset
					offsetMax = max / (1.0f + (1.0f / FOctreeNodeContext::LoosenessDenominator));
					offset = max - offsetMax;
					max = offsetMax;
				
					// Calculate Center Offset
					if (center.X > 0)
					{
						center.X = center.X + offset;
					}
					else
					{
						center.X = center.X - offset;
					}

					if (center.Y > 0)
					{
						center.Y = center.Y + offset;
					}
					else
					{
						center.Y = center.Y - offset;
					}

					if (center.Z > 0)
					{
						center.Z = center.Z + offset;
					}
					else
					{
						center.Z = center.Z - offset;
					}
				}
			}

			UE_LOG(LogTemp, Log, TEXT("max: %f"), max);
			// UE_LOG(LogTemp, Log, TEXT("center of nodes: %s"), *center.ToString());

			maxExtent = FVector(max, max, max);
			

			// UE_LOG(LogTemp, Log, TEXT("Extent of nodes: %s"), *tempForCoercion.ToString());

			DrawDebugBox(GetWorld(), center, maxExtent, FColor().Blue, false, 0.0f);
			DrawDebugSphere(GetWorld(), center + maxExtent, 4.0f, 12, FColor().Green, false, 0.0f);
			DrawDebugSphere(GetWorld(), center - maxExtent, 4.0f, 12, FColor().Red, false, 0.0f);

			for (FSimpleOctree::ElementConstIt ElementIt(CurrentNode.GetElementIt()); ElementIt; ++ElementIt)
			{
				const FOctreeElement& Sample = *ElementIt;

				// Draw debug boxes around elements
				max = Sample.BoxSphereBounds.BoxExtent.GetMax();
				maxExtent = FVector(max, max, max);
				center = Sample.MyActor->GetActorLocation();

				DrawDebugBox(GetWorld(), center, maxExtent, FColor().Blue, false, 0.0f);
				DrawDebugSphere(GetWorld(), center + maxExtent, 4.0f, 12, FColor().White, false, 0.0f);
				DrawDebugSphere(GetWorld(), center - maxExtent, 4.0f, 12, FColor().White, false, 0.0f);
				elementCount++;
			}
		}
		// UE_LOG(LogTemp, Log, TEXT("Node Count: %d, Element Count: %d"), nodeCount, elementCount);
	}
FArchive& operator<<(FArchive& Ar,FPrecomputedLightVolume& Volume)
{
	if (Ar.IsCountingMemory())
	{
		const int32 AllocatedBytes = Volume.GetAllocatedBytes();
		Ar.CountBytes(AllocatedBytes, AllocatedBytes);
	}
	else if (Ar.IsLoading())
	{
		Ar << Volume.bInitialized;
		if (Volume.bInitialized)
		{
			FBox Bounds;
			Ar << Bounds;
			float SampleSpacing = 0.0f;
			Ar << SampleSpacing;
			Volume.Initialize(Bounds);
			TArray<FVolumeLightingSample> HighQualitySamples;
			// Deserialize samples as an array, and add them to the octree
			Ar << HighQualitySamples;

			if (FPlatformProperties::SupportsHighQualityLightmaps() 
				&& (GIsEditor || AllowHighQualityLightmaps(GMaxRHIFeatureLevel)))
			{
				for(int32 SampleIndex = 0; SampleIndex < HighQualitySamples.Num(); SampleIndex++)
				{
					Volume.AddHighQualityLightingSample(HighQualitySamples[SampleIndex]);
				}
			}

			TArray<FVolumeLightingSample> LowQualitySamples;

			if (Ar.UE4Ver() >= VER_UE4_VOLUME_SAMPLE_LOW_QUALITY_SUPPORT)
			{
				Ar << LowQualitySamples;
			}

			if (FPlatformProperties::SupportsLowQualityLightmaps() 
				&& (GIsEditor || !AllowHighQualityLightmaps(GMaxRHIFeatureLevel)))
			{
				for(int32 SampleIndex = 0; SampleIndex < LowQualitySamples.Num(); SampleIndex++)
				{
					Volume.AddLowQualityLightingSample(LowQualitySamples[SampleIndex]);
				}
			}

			Volume.FinalizeSamples();
		}
	}
	else if (Ar.IsSaving())
	{
		Ar << Volume.bInitialized;
		if (Volume.bInitialized)
		{
			Ar << Volume.Bounds;
			float SampleSpacing = 0.0f;
			Ar << SampleSpacing;

			TArray<FVolumeLightingSample> HighQualitySamples;

			if (!Ar.IsCooking() || Ar.CookingTarget()->SupportsFeature(ETargetPlatformFeatures::HighQualityLightmaps))
			{
				// Gather an array of samples from the octree
				for(FLightVolumeOctree::TConstIterator<> NodeIt(Volume.HighQualityLightmapOctree); NodeIt.HasPendingNodes(); NodeIt.Advance())
				{
					const FLightVolumeOctree::FNode& CurrentNode = NodeIt.GetCurrentNode();

					FOREACH_OCTREE_CHILD_NODE(ChildRef)
					{
						if(CurrentNode.HasChild(ChildRef))
						{
							NodeIt.PushChild(ChildRef);
						}
					}

					for (FLightVolumeOctree::ElementConstIt ElementIt(CurrentNode.GetElementIt()); ElementIt; ++ElementIt)
					{
						const FVolumeLightingSample& Sample = *ElementIt;
						HighQualitySamples.Add(Sample);
					}
				}
			}
			
			Ar << HighQualitySamples;

			TArray<FVolumeLightingSample> LowQualitySamples;

			if (!Ar.IsCooking() || Ar.CookingTarget()->SupportsFeature(ETargetPlatformFeatures::LowQualityLightmaps))
			{
				// Gather an array of samples from the octree
				for(FLightVolumeOctree::TConstIterator<> NodeIt(Volume.LowQualityLightmapOctree); NodeIt.HasPendingNodes(); NodeIt.Advance())
				{
					const FLightVolumeOctree::FNode& CurrentNode = NodeIt.GetCurrentNode();

					FOREACH_OCTREE_CHILD_NODE(ChildRef)
					{
						if(CurrentNode.HasChild(ChildRef))
						{
							NodeIt.PushChild(ChildRef);
						}
					}

					for (FLightVolumeOctree::ElementConstIt ElementIt(CurrentNode.GetElementIt()); ElementIt; ++ElementIt)
					{
						const FVolumeLightingSample& Sample = *ElementIt;
						LowQualitySamples.Add(Sample);
					}
				}
			}

			Ar << LowQualitySamples;
		}