diff --git a/.vsconfig b/.vsconfig new file mode 100755 index 0000000..4b94783 --- /dev/null +++ b/.vsconfig @@ -0,0 +1,17 @@ +{ + "version": "1.0", + "components": [ + "Component.Unreal.Debugger", + "Component.Unreal.Ide", + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.Llvm.Clang", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.CoreEditor", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame" + ] +} diff --git a/Content/ImportedAssets/Rocks/SM_rock1.uasset b/Content/ImportedAssets/Rocks/SM_rock1.uasset new file mode 100755 index 0000000..a905747 Binary files /dev/null and b/Content/ImportedAssets/Rocks/SM_rock1.uasset differ diff --git a/Content/ImportedAssets/Trees/Material_001.uasset b/Content/ImportedAssets/Trees/Material_001.uasset new file mode 100755 index 0000000..4e18932 Binary files /dev/null and b/Content/ImportedAssets/Trees/Material_001.uasset differ diff --git a/Content/ImportedAssets/Trees/Material_002.uasset b/Content/ImportedAssets/Trees/Material_002.uasset new file mode 100755 index 0000000..6a995c8 Binary files /dev/null and b/Content/ImportedAssets/Trees/Material_002.uasset differ diff --git a/Content/ImportedAssets/Trees/Material_003.uasset b/Content/ImportedAssets/Trees/Material_003.uasset new file mode 100755 index 0000000..9074f2e Binary files /dev/null and b/Content/ImportedAssets/Trees/Material_003.uasset differ diff --git a/Content/ImportedAssets/Trees/Material_004.uasset b/Content/ImportedAssets/Trees/Material_004.uasset new file mode 100755 index 0000000..3701e3c Binary files /dev/null and b/Content/ImportedAssets/Trees/Material_004.uasset differ diff --git a/Content/ImportedAssets/Trees/Material_005.uasset b/Content/ImportedAssets/Trees/Material_005.uasset new file mode 100755 index 0000000..abd2222 Binary files /dev/null and b/Content/ImportedAssets/Trees/Material_005.uasset differ diff --git a/Content/ImportedAssets/Trees/SM_tree01.uasset b/Content/ImportedAssets/Trees/SM_tree01.uasset new file mode 100755 index 0000000..e01ae40 Binary files /dev/null and b/Content/ImportedAssets/Trees/SM_tree01.uasset differ diff --git a/Content/ImportedAssets/Trees/SM_tree02.uasset b/Content/ImportedAssets/Trees/SM_tree02.uasset new file mode 100755 index 0000000..a57f664 Binary files /dev/null and b/Content/ImportedAssets/Trees/SM_tree02.uasset differ diff --git a/Content/ImportedAssets/Trees/SM_tree03.uasset b/Content/ImportedAssets/Trees/SM_tree03.uasset new file mode 100755 index 0000000..bddce8e Binary files /dev/null and b/Content/ImportedAssets/Trees/SM_tree03.uasset differ diff --git a/Content/ImportedAssets/Trees/SM_tree04.uasset b/Content/ImportedAssets/Trees/SM_tree04.uasset new file mode 100755 index 0000000..88daca0 Binary files /dev/null and b/Content/ImportedAssets/Trees/SM_tree04.uasset differ diff --git a/Content/Maps/Testmap.umap b/Content/Maps/Testmap.umap index 4195435..2a6c520 100644 Binary files a/Content/Maps/Testmap.umap and b/Content/Maps/Testmap.umap differ diff --git a/Content/Maps/TurntableStage_BP_RobotPolice.umap b/Content/Maps/TurntableStage_BP_RobotPolice.umap new file mode 100755 index 0000000..4f240e8 Binary files /dev/null and b/Content/Maps/TurntableStage_BP_RobotPolice.umap differ diff --git a/Content/NewTextureRenderTarget2D.uasset b/Content/NewTextureRenderTarget2D.uasset new file mode 100755 index 0000000..49f8fb2 Binary files /dev/null and b/Content/NewTextureRenderTarget2D.uasset differ diff --git a/Content/Python/basic_scripting_demo/1.py b/Content/Python/basic_scripting_demo/1.py new file mode 100755 index 0000000..413fbec --- /dev/null +++ b/Content/Python/basic_scripting_demo/1.py @@ -0,0 +1,5 @@ +import unreal + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() +unreal.log(f"{len(actors)} actors in level") \ No newline at end of file diff --git a/Content/Python/basic_scripting_demo/2.py b/Content/Python/basic_scripting_demo/2.py new file mode 100755 index 0000000..7db0462 --- /dev/null +++ b/Content/Python/basic_scripting_demo/2.py @@ -0,0 +1,16 @@ +import unreal + +OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") +NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() + +old_mesh_count = 0 + +for actor in actors: + comp = actor.get_component_by_class(unreal.StaticMeshComponent) + if comp and comp.static_mesh == OLD: + old_mesh_count += 1 + +print(f"Found {old_mesh_count} instances of the old mesh") \ No newline at end of file diff --git a/Content/Python/basic_scripting_demo/3.py b/Content/Python/basic_scripting_demo/3.py new file mode 100755 index 0000000..6c6bb18 --- /dev/null +++ b/Content/Python/basic_scripting_demo/3.py @@ -0,0 +1,19 @@ +import unreal + +OLD = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree04.SM_tree04") +NEW = unreal.load_asset("/Game/ImportedAssets/Trees/SM_tree01.SM_tree01") + +sub = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = sub.get_all_level_actors() + +old_mesh_count = 0 + +with unreal.ScopedEditorTransaction("Swap meshes"): + for actor in actors: + comp = actor.get_component_by_class(unreal.StaticMeshComponent) + if comp and comp.static_mesh == OLD: + comp.set_editor_property("static_mesh", NEW) + # comp.set_static_mesh(NEW) -- tak tez mozna ale undo nie zadziala bo zmiana nie jest robiona przez edytor wiec jej on nie rejestruje + old_mesh_count += 1 + +unreal.log(f"Swapped {old_mesh_count} meshes") \ No newline at end of file diff --git a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset new file mode 100755 index 0000000..bd8a95c Binary files /dev/null and b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice.uasset differ diff --git a/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset new file mode 100755 index 0000000..4abf196 Binary files /dev/null and b/Content/Sequences/Turntables/BP_RobotPolice/LS_TT_BP_RobotPolice_RobotPolice_Anim.uasset differ diff --git a/ExampleProject.uproject b/ExampleProject.uproject index 757a1a1..cfcec1f 100644 --- a/ExampleProject.uproject +++ b/ExampleProject.uproject @@ -3,6 +3,13 @@ "EngineAssociation": "5.8", "Category": "", "Description": "", + "Modules": [ + { + "Name": "ExampleProject", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], "Plugins": [ { "Name": "Bridge", diff --git a/Plugins/DemoPlugin/DemoPlugin.uplugin b/Plugins/DemoPlugin/DemoPlugin.uplugin new file mode 100755 index 0000000..9684492 --- /dev/null +++ b/Plugins/DemoPlugin/DemoPlugin.uplugin @@ -0,0 +1,16 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0.0", + "FriendlyName": "Demo Render Bridge", + "Description": "Minimalny przyklad wywolania C++ z Unreal Python.", + "Category": "Examples", + "CanContainContent": false, + "Modules": [ + { + "Name": "DemoPlugin", + "Type": "Editor", + "LoadingPhase": "Default" + } + ] +} diff --git a/Plugins/DemoPlugin/README.md b/Plugins/DemoPlugin/README.md new file mode 100755 index 0000000..9495063 --- /dev/null +++ b/Plugins/DemoPlugin/README.md @@ -0,0 +1,48 @@ +# Demo Render Bridge: minimalny prawdziwy plugin UE C++ -> Python + +Plugin pokazuje rzeczywisty przeplyw: + +```text +TextureRenderTarget2D + -> Python wywoluje UFUNCTION + -> C++: FImageUtils::GetRenderTargetImage + -> C++: FImageUtils::CompressImage(".png") + -> TArray wraca do Pythona + -> Python zapisuje bytes na dysku +``` + +## Struktura + +```text +DemoPlugin.uplugin +Source/DemoPlugin/DemoPlugin.Build.cs +Source/DemoPlugin/Public/DemoRenderBridge.h +Source/DemoPlugin/Private/DemoRenderBridge.cpp +Source/DemoPlugin/Private/DemoPlugin.cpp +use_plugin.py +``` + +## Jak uruchomic + +1. Skopiuj caly katalog jako `/Plugins/DemoPlugin`. +2. Upewnij sie, ze projekt ma modul C++ i wlaczony `Python Editor Script Plugin`. +3. Otworz projekt i zaakceptuj kompilacje pluginu albo zbuduj projekt w IDE. +4. Utworz asset `TextureRenderTarget2D` pod `/Game/Demo/RT_Demo` lub zmien + sciezke `RENDER_TARGET_ASSET` w `use_plugin.py`. +5. W Unreal Editor wybierz `Tools -> Execute Python Script` i uruchom + `use_plugin.py`. +6. Wynik pojawi sie jako `Saved/demo_from_cpp.png`. + +## Co jest mostem do Pythona + +- `UCLASS()` rejestruje klase w refleksji. +- `UFUNCTION(BlueprintCallable)` eksportuje funkcje. +- `UDemoRenderBridge` staje sie `unreal.DemoRenderBridge`. +- `RenderTargetToPng` staje sie `render_target_to_png`. +- `UTextureRenderTarget2D*` przyjmuje obiekt zaladowany w Pythonie. +- `TArray` wraca jako kolekcja konwertowalna przez `bytes(...)`. + +To jest odchudzona wersja prawdziwego render bridge'a: pomija wybor formatu, +luminancje, specjalne kodowanie RGB, transport sieciowy i rozbudowana obsluge +bledow. Zachowuje jednak faktyczne wywolania API C++ Unreala i prawdziwy +kontrakt widoczny z Unreal Python. diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs b/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs new file mode 100755 index 0000000..f5b5329 --- /dev/null +++ b/Plugins/DemoPlugin/Source/DemoPlugin/DemoPlugin.Build.cs @@ -0,0 +1,17 @@ +using UnrealBuildTool; + +public class DemoPlugin : ModuleRules +{ + public DemoPlugin(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new[] + { + "Core", + "CoreUObject", + "Engine", + "ImageCore" + }); + } +} diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp new file mode 100755 index 0000000..ecec6c1 --- /dev/null +++ b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoPlugin.cpp @@ -0,0 +1,8 @@ +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FDefaultModuleImpl, DemoPlugin) + +//This registers that it is the implementation of DemoPlugin module +//that can be loaded by UE + +//Here could be startup scripts or sth diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp new file mode 100755 index 0000000..a017a70 --- /dev/null +++ b/Plugins/DemoPlugin/Source/DemoPlugin/Private/DemoRenderBridge.cpp @@ -0,0 +1,51 @@ +#include "DemoRenderBridge.h" + +#include "Engine/TextureRenderTarget2D.h" +#include "ImageCore.h" +#include "ImageUtils.h" + +bool UDemoRenderBridge::RenderTargetToPng( + UTextureRenderTarget2D* RenderTarget, + TArray& OutPngBytes, + FString& OutError) +{ + OutPngBytes.Reset(); + OutError.Reset(); + + if (!IsInGameThread()) + { + OutError = TEXT("Function must be ran on Game Thread"); + return false; + } + + if (!IsValid(RenderTarget)) + { + OutError = TEXT("Render Target reference is invalid"); + return false; + } + + // Faktyczne wywolanie API C++ Unreala: odczyt Render Targetu do FImage. + FImage Image; + if (!FImageUtils::GetRenderTargetImage(RenderTarget, Image)) + { + OutError = TEXT("Couldnt read Render Target"); + return false; + } + + // Faktyczne wywolanie API C++ Unreala: kompresja FImage do PNG w pamieci. + TArray64 Encoded; + if (!FImageUtils::CompressImage(Encoded, TEXT(".png"), Image, 0)) + { + OutError = TEXT("Couldnt encode png"); + return false; + } + + if (Encoded.Num() > MAX_int32) + { + OutError = TEXT("Result too large"); + return false; + } + + OutPngBytes.Append(Encoded.GetData(), static_cast(Encoded.Num())); + return true; +} diff --git a/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h b/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h new file mode 100755 index 0000000..3de6c44 --- /dev/null +++ b/Plugins/DemoPlugin/Source/DemoPlugin/Public/DemoRenderBridge.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "DemoRenderBridge.generated.h" + +class UTextureRenderTarget2D; + +/** Funkcje C++ dostepne w Blueprintach i Python API */ +UCLASS() +class DEMOPLUGIN_API UDemoRenderBridge : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + /** Odczytuje Render Target z GPU i zwraca zakodowany PNG w pamieci. */ + UFUNCTION(BlueprintCallable, Category = "Demo|C++ Bridge") + static bool RenderTargetToPng( + UTextureRenderTarget2D* RenderTarget, + TArray& OutPngBytes, + FString& OutError); +}; diff --git a/Plugins/DemoPlugin/use_plugin.py b/Plugins/DemoPlugin/use_plugin.py new file mode 100755 index 0000000..63801ae --- /dev/null +++ b/Plugins/DemoPlugin/use_plugin.py @@ -0,0 +1,19 @@ +from pathlib import Path + +import unreal + +RENDER_TARGET_ASSET = "/Game/NewTextureRenderTarget2D" +OUTPUT_FILE = Path(unreal.Paths.project_saved_dir()) / "demo_from_cpp.png" + +render_target = unreal.load_asset(RENDER_TARGET_ASSET) + +# Parametry Out funkcji UFUNCTION wracaja w Pythonie jako tuple. +png_bytes, error = unreal.DemoRenderBridge.render_target_to_png( + render_target +) + +if error: + raise RuntimeError(error) + +OUTPUT_FILE.write_bytes(bytes(png_bytes)) +unreal.log(f"preview saved as {OUTPUT_FILE}") diff --git a/Source/ExampleProject.Target.cs b/Source/ExampleProject.Target.cs new file mode 100755 index 0000000..c18e0cd --- /dev/null +++ b/Source/ExampleProject.Target.cs @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class ExampleProjectTarget : TargetRules +{ + public ExampleProjectTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.V7; + + ExtraModuleNames.AddRange( new string[] { "ExampleProject" } ); + } +} diff --git a/Source/ExampleProject/ExampleProject.Build.cs b/Source/ExampleProject/ExampleProject.Build.cs new file mode 100755 index 0000000..c25b174 --- /dev/null +++ b/Source/ExampleProject/ExampleProject.Build.cs @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; + +public class ExampleProject : ModuleRules +{ + public ExampleProject(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + + PrivateDependencyModuleNames.AddRange(new string[] { }); + + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + + // Uncomment if you are using online features + // PrivateDependencyModuleNames.Add("OnlineSubsystem"); + + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/Source/ExampleProject/ExampleProject.cpp b/Source/ExampleProject/ExampleProject.cpp new file mode 100755 index 0000000..881ecf4 --- /dev/null +++ b/Source/ExampleProject/ExampleProject.cpp @@ -0,0 +1,6 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "ExampleProject.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ExampleProject, "ExampleProject" ); diff --git a/Source/ExampleProject/ExampleProject.h b/Source/ExampleProject/ExampleProject.h new file mode 100755 index 0000000..c56eab6 --- /dev/null +++ b/Source/ExampleProject/ExampleProject.h @@ -0,0 +1,6 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + diff --git a/Source/ExampleProject/dummy.cpp b/Source/ExampleProject/dummy.cpp new file mode 100755 index 0000000..8496413 --- /dev/null +++ b/Source/ExampleProject/dummy.cpp @@ -0,0 +1,12 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "dummy.h" + +dummy::dummy() +{ +} + +dummy::~dummy() +{ +} diff --git a/Source/ExampleProject/dummy.h b/Source/ExampleProject/dummy.h new file mode 100755 index 0000000..33000dc --- /dev/null +++ b/Source/ExampleProject/dummy.h @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +/** + * + */ +class EXAMPLEPROJECT_API dummy +{ +public: + dummy(); + ~dummy(); +}; diff --git a/Source/ExampleProjectEditor.Target.cs b/Source/ExampleProjectEditor.Target.cs new file mode 100755 index 0000000..ef54f9c --- /dev/null +++ b/Source/ExampleProjectEditor.Target.cs @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class ExampleProjectEditorTarget : TargetRules +{ + public ExampleProjectEditorTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.V7; + + ExtraModuleNames.AddRange( new string[] { "ExampleProject" } ); + } +} diff --git a/open_in_unreal.sh b/open_in_unreal.sh new file mode 100755 index 0000000..7bd23a0 --- /dev/null +++ b/open_in_unreal.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Open ExampleProject.uproject in Unreal Editor on Linux. + +set -Eeuo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +PROJECT_PATH="$SCRIPT_DIR/ExampleProject.uproject" + +show_error() { + local message="$1" + + if command -v zenity >/dev/null 2>&1 && [[ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]]; then + zenity --error --title="ExampleProject - Unreal Editor" --width=520 --text="$message" + else + printf 'ERROR: %s\n' "$message" >&2 + if [[ -t 0 ]]; then + read -r -p "Press Enter to close..." _ + fi + fi +} + +if [[ ! -f "$PROJECT_PATH" ]]; then + show_error "Project file was not found:\n$PROJECT_PATH" + exit 1 +fi + +find_editor() { + local candidate + + # An explicit path takes priority. It may point to the UE root or the binary. + if [[ -n "${UE_ROOT:-}" ]]; then + for candidate in \ + "$UE_ROOT" \ + "$UE_ROOT/Engine/Binaries/Linux/UnrealEditor"; do + if [[ -x "$candidate" && ! -d "$candidate" ]]; then + printf '%s\n' "$candidate" + return 0 + fi + done + fi + + if command -v UnrealEditor >/dev/null 2>&1; then + command -v UnrealEditor + return 0 + fi + + for candidate in \ + "$HOME/Unreal/UE_5.8/Engine/Binaries/Linux/UnrealEditor" \ + "$HOME/Unreal/UE_5.7/Engine/Binaries/Linux/UnrealEditor" \ + "$HOME/UnrealEngine/Engine/Binaries/Linux/UnrealEditor" \ + /opt/UnrealEngine/Engine/Binaries/Linux/UnrealEditor \ + /mnt/UnrealEngine/Engine/Binaries/Linux/UnrealEditor; do + if [[ -x "$candidate" ]]; then + printf '%s\n' "$candidate" + return 0 + fi + done + + return 1 +} + +if ! UNREAL_EDITOR="$(find_editor)"; then + show_error "Unreal Editor was not found.\n\nSet UE_ROOT to the Unreal Engine directory, for example:\nexport UE_ROOT=/home/vysyr/Unreal/UE_5.7" + exit 1 +fi + +printf 'Opening project:\n %s\nwith:\n %s\n' "$PROJECT_PATH" "$UNREAL_EDITOR" +SDL_VIDEODRIVER=x11 exec "$UNREAL_EDITOR" "$PROJECT_PATH" "$@"