#include "FLESHViewportClient.h" #include "FLESHEditor.h" #include "CanvasItem.h" #include "CanvasTypes.h" #include "Engine/SkeletalMesh.h" #include "Components/SkeletalMeshComponent.h" #include "Components/DirectionalLightComponent.h" #include "Components/SkyLightComponent.h" #include "Slate/SceneViewport.h" #include "PreviewScene.h" #include "EditorViewportClient.h" #include "EngineGlobals.h" #include "Engine/Engine.h" #include "Engine.h" #include "CanvasTypes.h" #include "SceneView.h" #include "InputCoreTypes.h" #include "Components/SplineComponent.h" #include "EditorModeManager.h" #include "UnrealWidget.h" FFLESHViewportClient::FFLESHViewportClient(FFLESHEditor* InEditor) : FEditorViewportClient(nullptr) , Viewport(nullptr) , Editor(InEditor) , bShowWireframe(false) , bShowBones(false) { // Create a valid preview scene PreviewScene = MakeShareable(new FPreviewScene(FPreviewScene::ConstructionValues())); // Set the scene for FEditorViewportClient - use constructor instead of SetPreviewScene FEditorViewportClient::PreviewScene = PreviewScene.Get(); // Set viewport type SetViewportType(LVT_Perspective); // Enable real-time rendering SetRealtime(true); // Set initial view position and rotation SetInitialViewTransform( LVT_Perspective, FVector(0.0f, 100.0f, 0.0f), // Camera position FRotator(0.0f, -90.0f, 0.0f), // Camera rotation 200.0f // Camera distance ); // Enable camera controls EngineShowFlags.SetPostProcessing(true); EngineShowFlags.SetLighting(true); EngineShowFlags.SetIndirectLightingCache(true); EngineShowFlags.SetTemporalAA(true); // Set default render mode SetViewMode(VMI_Lit); // Set camera control options similar to asset editor SetRealtime(true); bSetListenerPosition = false; // Enable standard editor camera controls if (Widget) { Widget->SetSnapEnabled(true); } // Load and display objects from NodeTree LoadNodesFromNodeTree(); } FFLESHViewportClient::~FFLESHViewportClient() { // Clean up resources } void FFLESHViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) { FEditorViewportClient::Draw(View, PDI); // Draw navigation widget (coordinate axes) if (PDI) { // Draw coordinate system axes const float AxisLength = 50.0f; const float AxisThickness = 1.0f; // Draw X axis (red) PDI->DrawLine(FVector::ZeroVector, FVector(AxisLength, 0.0f, 0.0f), FLinearColor::Red, SDPG_Foreground, AxisThickness); // Draw Y axis (green) PDI->DrawLine(FVector::ZeroVector, FVector(0.0f, AxisLength, 0.0f), FLinearColor::Green, SDPG_Foreground, AxisThickness); // Draw Z axis (blue) PDI->DrawLine(FVector::ZeroVector, FVector(0.0f, 0.0f, AxisLength), FLinearColor::Blue, SDPG_Foreground, AxisThickness); } // Draw custom UI elements if (Viewport) { FCanvas* Canvas = Viewport->GetDebugCanvas(); if (Canvas) { // Draw mode information FString ModeText; if (bShowWireframe) { ModeText += TEXT("Wireframe Mode | "); } if (bShowBones) { ModeText += TEXT("Show Bones | "); } if (!ModeText.IsEmpty()) { // Remove last separator ModeText.RemoveAt(ModeText.Len() - 3, 3); // Draw text FCanvasTextItem TextItem(FVector2D(10, 10), FText::FromString(ModeText), GEngine->GetSmallFont(), FLinearColor::White); Canvas->DrawItem(TextItem); } } } } bool FFLESHViewportClient::InputKey(const FInputKeyEventArgs& EventArgs) { // Handle keyboard and mouse input bool bHandled = false; // Add custom shortcuts if (EventArgs.Event == IE_Pressed) { if (EventArgs.Key == EKeys::W) { // W key toggles wireframe mode ToggleWireframe(); bHandled = true; } else if (EventArgs.Key == EKeys::B) { // B key toggles bone display ToggleBones(); bHandled = true; } else if (EventArgs.Key == EKeys::R) { // R key resets camera ResetCamera(); bHandled = true; } else if (EventArgs.Key == EKeys::F) { // F key focuses on selected object FocusOnSelectedNode(); bHandled = true; } } // If not handled, pass to base class if (!bHandled) { bHandled = FEditorViewportClient::InputKey(EventArgs); } return bHandled; } bool FFLESHViewportClient::InputKey(FViewport* InViewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool bGamepad) { // Create new parameter structure and call the new version of InputKey FInputKeyEventArgs Args(InViewport, ControllerId, Key, Event, AmountDepressed, false); // Forward to the new API version return InputKey(Args); } // This version of InputAxis is deprecated, but kept for compatibility bool FFLESHViewportClient::InputAxis(FViewport* InViewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad) { // Create InputKeyEventArgs for the new API FInputKeyEventArgs Args(InViewport, ControllerId, Key, IE_Axis); Args.AmountDepressed = Delta; return InputKey(Args); } void FFLESHViewportClient::ResetCamera() { // Reset camera position and rotation SetViewLocation(FVector(0.0f, 100.0f, 0.0f)); SetViewRotation(FRotator(0.0f, -90.0f, 0.0f)); // Invalidate view Invalidate(); } void FFLESHViewportClient::ToggleWireframe() { // Toggle wireframe mode bShowWireframe = !bShowWireframe; if (bShowWireframe) { SetViewMode(VMI_Wireframe); } else { SetViewMode(VMI_Lit); } // Invalidate view Invalidate(); } void FFLESHViewportClient::ToggleBones() { // Toggle bone display bShowBones = !bShowBones; // Update engine flags EngineShowFlags.SetBones(bShowBones); // Invalidate view Invalidate(); } void FFLESHViewportClient::LoadNodesFromNodeTree() { if (!PreviewScene.IsValid() || !Editor) { return; } // Clear current preview scene components by creating a new one PreviewScene = MakeShareable(new FPreviewScene(FPreviewScene::ConstructionValues())); FEditorViewportClient::PreviewScene = PreviewScene.Get(); // Add a default light UDirectionalLightComponent* DirectionalLight = NewObject(); DirectionalLight->SetMobility(EComponentMobility::Movable); DirectionalLight->SetIntensity(1.0f); PreviewScene->AddComponent(DirectionalLight, FTransform(FRotator(-45.0f, 45.0f, 0.0f))); // Add a skylight for ambient lighting USkyLightComponent* SkyLight = NewObject(); SkyLight->SetMobility(EComponentMobility::Movable); SkyLight->SourceType = ESkyLightSourceType::SLS_CapturedScene; PreviewScene->AddComponent(SkyLight, FTransform::Identity); // Add a second directional light from another angle UDirectionalLightComponent* BackLight = NewObject(); BackLight->SetMobility(EComponentMobility::Movable); BackLight->SetIntensity(0.6f); BackLight->SetLightColor(FLinearColor(0.8f, 0.8f, 1.0f)); // Slightly blue backlight PreviewScene->AddComponent(BackLight, FTransform(FRotator(45.0f, -135.0f, 0.0f))); // Get all nodes from NodeTree const TArray>& NodeRoots = Editor->GetNodeTreeRoots(); // Recursively load all nodes for (const TSharedPtr& RootNode : NodeRoots) { if (RootNode.IsValid()) { LoadNodeRecursive(RootNode, nullptr); } } // Update viewport Invalidate(); } void FFLESHViewportClient::LoadNodeRecursive(TSharedPtr Node, USceneComponent* ParentComponent) { if (!Node.IsValid() || !PreviewScene.IsValid()) { return; } USceneComponent* NodeComponent = nullptr; // Simplified node loading logic, use static mesh for all node types UStaticMeshComponent* MeshComponent = NewObject(); // Choose different mesh and color based on node type FString MeshPath; FLinearColor Color; if (Node->NodeType.Equals(TEXT("SoftBody"))) { MeshPath = TEXT("/Engine/BasicShapes/Sphere.Sphere"); Color = FLinearColor(1.0f, 0.5f, 0.5f, 0.7f); // Translucent red } else if (Node->NodeType.Equals(TEXT("LineChain"))) { MeshPath = TEXT("/Engine/BasicShapes/Cylinder.Cylinder"); Color = FLinearColor(0.5f, 0.5f, 1.0f, 0.7f); // Translucent blue } else if (Node->NodeType.Equals(TEXT("Plane"))) { MeshPath = TEXT("/Engine/BasicShapes/Plane.Plane"); Color = FLinearColor(0.5f, 0.5f, 1.0f, 0.5f); // Translucent blue } else if (Node->NodeType.Equals(TEXT("Matrix"))) { MeshPath = TEXT("/Engine/BasicShapes/Cube.Cube"); Color = FLinearColor(0.5f, 1.0f, 0.5f, 0.7f); // Translucent green MeshComponent->SetWorldScale3D(FVector(0.25f, 0.25f, 0.25f)); // Scale down cube } else // Default to sphere { MeshPath = TEXT("/Engine/BasicShapes/Sphere.Sphere"); Color = FLinearColor(1.0f, 1.0f, 1.0f, 0.7f); // Translucent white } // Load static mesh UStaticMesh* StaticMesh = LoadObject(nullptr, *MeshPath); if (StaticMesh) { MeshComponent->SetStaticMesh(StaticMesh); // Set translucent material UMaterial* Material = LoadObject(nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial")); if (Material) { UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(Material, MeshComponent); if (DynamicMaterial) { DynamicMaterial->SetVectorParameterValue(TEXT("Color"), Color); MeshComponent->SetMaterial(0, DynamicMaterial); } } } // Add to preview scene PreviewScene->AddComponent(MeshComponent, FTransform::Identity); NodeComponent = MeshComponent; // If parent component exists, set parent-child relationship if (NodeComponent && ParentComponent) { NodeComponent->AttachToComponent(ParentComponent, FAttachmentTransformRules::KeepRelativeTransform); } // Recursively load child nodes for (const TSharedPtr& ChildNode : Node->Children) { if (ChildNode.IsValid() && NodeComponent) { LoadNodeRecursive(ChildNode, NodeComponent); } } } void FFLESHViewportClient::UpdateVisibleNodes() { // Reload all nodes LoadNodesFromNodeTree(); } void FFLESHViewportClient::FocusOnSelectedNode() { if (!Editor) { return; } // Get currently selected node TSharedPtr SelectedNode = Editor->GetSelectedNodeItem(); if (!SelectedNode.IsValid()) { return; } // Position camera at the selected node's location // Note: Here we use a default position since we don't have actual position information for the node // In a real implementation, we should find the component for this node and get its position FVector FocusLocation = FVector::ZeroVector; // Set camera position and rotation SetViewLocation(FocusLocation + FVector(0.0f, 100.0f, 0.0f)); SetViewRotation(FRotator(0.0f, -90.0f, 0.0f)); // Update view Invalidate(); }