diff --git a/README.md b/README.md index 1318c566f..972c7c0a7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # UnrealEnginePython Embed Python in Unreal Engine 4 @@ -5,6 +6,9 @@ Embed Python in Unreal Engine 4 This fork is meant to encapsulate python + pip + scripts fully in the plugin and to allow dependency plugins to be built on top of the python plugin. Specifically it means adding automatic pip dependency resolution and automatic sys.path additions such that the resulting two plugins can be fully drag and dropped into a new project. + +Teaser: https://twitter.com/KNLstudio/status/932657812466843648 + # How and Why ? This is a plugin embedding a whole Python VM (versions 3.x [the default and suggested one] and 2.7) In Unreal Engine 4 (both the editor and runtime). @@ -25,7 +29,7 @@ Once the plugin is installed and enabled, you get access to the 'PythonConsole' All of the exposed engine features are under the 'unreal_engine' virtual module (it is completely coded in c into the plugin, so do not expect to run 'import unreal_engine' from a standard python shell) -The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17 and 4.18. +The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18 and 4.19 We support official python.org releases as well as IntelPython and Anaconda distributions. @@ -488,6 +492,30 @@ To access the fields of a struct just call the fields() method. A good example of struct usage is available here: https://github.com/20tab/UnrealEnginePython/blob/master/docs/Settings.md +As structs are passed by value, you need to pay attention when manipulating structs fields that are structs by themselves: + +```python +from unreal_engine.structs import TerrificStruct, DumbStruct + +ts = TerrificStruct() +ts.dumb = DumbStruct(Foo=17, Bar=22) + +# will not modify the original DumbStruct but a copy of it !!! +ts.dumb.Foo = 22 +``` + +You can eventually force structs to be passed by ref (extremely dangerous as the internal C pointer could be a dangling one) using the ref() function: + +```python +from unreal_engine.structs import TerrificStruct, DumbStruct + +ts = TerrificStruct() +ts.dumb = DumbStruct(Foo=17, Bar=22) + +# ref() will return a pointer to a struct +ts.ref().dumb.foo().Foo = 22 +``` + The ue_site.py file ------------------- @@ -809,15 +837,9 @@ Memory management Dealing with 2 different GC's is really challenging. -PyActor, PyPawn and PythonComponent automatically DECREF's the mapped classes. This should be enough unless you hold references -in the python modules themselves. As this would be a bad programming practice, the current approach should be safe enough. - -In addition to this, every time a uobject has to return its UObject mapping, it checks for its validity and safety: +Starting from release 20180226 a new memory management system has been added (FUnrealEnginePythonHouseKeeper, available here https://github.com/20tab/UnrealEnginePython/blob/master/Source/UnrealEnginePython/Public/PythonHouseKeeper.h). This new system is completely integrated with the Unreal Engine reflection-based GC and will hold track of each ue_PyUObject abd the related UObject to understand when a python object can be safely destroyed. -```c -#define ue_py_check(py_u) if (!py_u->ue_object || !py_u->ue_object->IsValidLowLevel() || py_u->ue_object->IsPendingKillOrUnreachable())\ - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") -``` +The same system works for delegates, as well as Slate. Unit Testing diff --git a/Source/PythonConsole/Private/PythonConsoleModule.cpp b/Source/PythonConsole/Private/PythonConsoleModule.cpp index a3a8470f6..dd86deb36 100644 --- a/Source/PythonConsole/Private/PythonConsoleModule.cpp +++ b/Source/PythonConsole/Private/PythonConsoleModule.cpp @@ -8,7 +8,7 @@ #include "SDockTab.h" #include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructure.h" -IMPLEMENT_MODULE( FPythonConsoleModule, PythonLog ); +IMPLEMENT_MODULE( FPythonConsoleModule, PythonConsole ); namespace PythonConsoleModule { @@ -94,71 +94,4 @@ TSharedRef< SWidget > FPythonConsoleModule::MakeConsoleInputBox( TSharedPtr< SEd TSharedRef< SPythonConsoleInputBox > NewConsoleInputBox = SNew( SPythonConsoleInputBox ); OutExposedEditableTextBox = NewConsoleInputBox->GetEditableTextBox(); return NewConsoleInputBox; -} - - -void FPythonConsoleModule::TogglePythonConsoleForWindow( const TSharedRef< SWindow >& Window, const EPythonConsoleStyle::Type InStyle, const FPythonConsoleDelegates& PythonConsoleDelegates ) -{ - bool bShouldOpen = true; - // Close an existing console box, if there is one - TSharedPtr< SWidget > PinnedPythonConsole( PythonConsole.Pin() ); - if( PinnedPythonConsole.IsValid() ) - { - // If the console is already open close it unless it is in a different window. In that case reopen it on that window - bShouldOpen = false; - TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedPythonConsole.ToSharedRef()); - if (WindowForExistingConsole.IsValid()) - { - WindowForExistingConsole->RemoveOverlaySlot(PinnedPythonConsole.ToSharedRef()); - PythonConsole.Reset(); - } - - if( WindowForExistingConsole != Window ) - { - // Console is being opened on another window - bShouldOpen = true; - } - } - - TSharedPtr ActiveTab = FGlobalTabmanager::Get()->GetActiveTab(); - if (ActiveTab.IsValid() && ActiveTab->GetLayoutIdentifier() == FTabId(PythonConsoleModule::PythonLogTabName)) - { - FGlobalTabmanager::Get()->DrawAttention(ActiveTab.ToSharedRef()); - bShouldOpen = false; - } - - if( bShouldOpen ) - { - const EPythonConsoleStyle::Type PythonConsoleStyle = InStyle; - TSharedRef< SPythonConsole > PythonConsoleRef = SNew( SPythonConsole, PythonConsoleStyle, this, &PythonConsoleDelegates ); - PythonConsole = PythonConsoleRef; - - const int32 MaximumZOrder = MAX_int32; - Window->AddOverlaySlot( MaximumZOrder ) - .VAlign(VAlign_Bottom) - .HAlign(HAlign_Center) - .Padding( 10.0f ) - [ - PythonConsoleRef - ]; - - // Force keyboard focus - PythonConsoleRef->SetFocusToEditableText(); - } -} - - -void FPythonConsoleModule::ClosePythonConsole() -{ - TSharedPtr< SWidget > PinnedPythonConsole( PythonConsole.Pin() ); - - if( PinnedPythonConsole.IsValid() ) - { - TSharedPtr< SWindow > WindowForExistingConsole = FSlateApplication::Get().FindWidgetWindow(PinnedPythonConsole.ToSharedRef()); - if (WindowForExistingConsole.IsValid()) - { - WindowForExistingConsole->RemoveOverlaySlot( PinnedPythonConsole.ToSharedRef() ); - PythonConsole.Reset(); - } - } } \ No newline at end of file diff --git a/Source/PythonConsole/Public/PythonConsoleModule.h b/Source/PythonConsole/Public/PythonConsoleModule.h index 7d5189b48..607fd61b2 100644 --- a/Source/PythonConsole/Public/PythonConsoleModule.h +++ b/Source/PythonConsole/Public/PythonConsoleModule.h @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2018 20Tab S.r.l. All Rights Reserved. #pragma once @@ -33,12 +33,6 @@ class FPythonConsoleModule : public IModuleInterface output log DLL is unloaded on the fly. */ virtual TSharedRef< SWidget > MakeConsoleInputBox( TSharedPtr< SEditableTextBox >& OutExposedEditableTextBox ) const; - /** Opens a debug console in the specified window, if not already open */ - virtual void TogglePythonConsoleForWindow( const TSharedRef< SWindow >& Window, const EPythonConsoleStyle::Type InStyle, const FPythonConsoleDelegates& PythonConsoleDelegates ); - - /** Closes the debug console for the specified window */ - virtual void ClosePythonConsole(); - private: diff --git a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp index 8a62710a4..53417bf6b 100644 --- a/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp +++ b/Source/PythonEditor/Private/PYRichTextSyntaxHighlighterTextLayoutMarshaller.cpp @@ -311,7 +311,11 @@ void FPYRichTextSyntaxHighlighterTextLayoutMarshaller::ParseTokens(const FString FRunInfo RunInfo(TEXT("SyntaxHighlight.PY.Normal")); FTextBlockStyle TextBlockStyle = SyntaxTextStyle.NormalTextStyle; +#if ENGINE_MINOR_VERSION >= 18 const bool bIsWhitespace = FString(TokenText).TrimEnd().IsEmpty(); +#else + const bool bIsWhitespace = FString(TokenText).TrimTrailing().IsEmpty(); +#endif if(!bIsWhitespace) { bool bHasMatchedSyntax = false; diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index ac33dcb83..1e82e45d7 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp @@ -13,18 +13,21 @@ #include "Editor/AIGraph/Classes/AIGraph.h" #include "Editor/AIGraph/Classes/AIGraphNode.h" -PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_function = NULL; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "O|ii:graph_add_node_call_function", &py_function, &x, &y)) { + if (!PyArg_ParseTuple(args, "O|ii:graph_add_node_call_function", &py_function, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -32,16 +35,20 @@ PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * arg UFunction *function = nullptr; - if (ue_is_pyuobject(py_function)) { + if (ue_is_pyuobject(py_function)) + { ue_PyUObject *py_function_obj = (ue_PyUObject *)py_function; - if (py_function_obj->ue_object->IsA()) { + if (py_function_obj->ue_object->IsA()) + { function = (UFunction *)py_function_obj->ue_object; } - else { + else + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } } - else if (py_ue_is_callable(py_function)) { + else if (py_ue_is_callable(py_function)) + { ue_PyCallable *py_callable = (ue_PyCallable *)py_function; function = py_callable->u_function; } @@ -61,30 +68,29 @@ PyObject *py_ue_graph_add_node_call_function(ue_PyUObject * self, PyObject * arg UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|ii:graph_add_node_custom_event", &name, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|ii:graph_add_node_custom_event", &name, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -102,23 +108,21 @@ PyObject *py_ue_graph_add_node_custom_event(ue_PyUObject * self, PyObject * args UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } @@ -131,7 +135,8 @@ PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject * self, PyObject return ret; } -PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -140,22 +145,26 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "Os|ii:graph_add_node_event", &py_class, &name, &x, &y)) { + if (!PyArg_ParseTuple(args, "Os|ii:graph_add_node_event", &py_class, &name, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; - if (!ue_is_pyuobject(py_class)) { + if (!ue_is_pyuobject(py_class)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)py_class; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass"); } @@ -163,7 +172,8 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { UBlueprint *bp = (UBlueprint *)graph->GetOuter(); UK2Node_Event *node = FBlueprintEditorUtils::FindOverrideForFunction(bp, u_class, UTF8_TO_TCHAR(name)); - if (!node) { + if (!node) + { node = NewObject(graph); UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); node->EventReference.SetExternalMember(UTF8_TO_TCHAR(name), u_class); @@ -172,15 +182,11 @@ PyObject *py_ue_graph_add_node_event(ue_PyUObject * self, PyObject * args) { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -188,23 +194,28 @@ PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args PyObject *py_struct = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_get", &name, &py_struct, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_get", &name, &py_struct, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; UStruct *u_struct = nullptr; - if (py_struct && py_struct != Py_None) { - if (!ue_is_pyuobject(py_struct)) { + if (py_struct && py_struct != Py_None) + { + if (!ue_is_pyuobject(py_struct)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *ue_py_struct = (ue_PyUObject *)py_struct; - if (!ue_py_struct->ue_object->IsA()) { + if (!ue_py_struct->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UStruct"); } u_struct = (UStruct *)ue_py_struct->ue_object; @@ -216,18 +227,14 @@ PyObject *py_ue_graph_add_node_variable_get(ue_PyUObject * self, PyObject * args UEdGraphSchema_K2::ConfigureVarNode(node, FName(UTF8_TO_TCHAR(name)), u_struct, bp); node = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate(graph, node, FVector2D(x, y)); - - FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -235,23 +242,28 @@ PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args PyObject *py_struct = nullptr; int x = 0; int y = 0; - if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_set", &name, &py_struct, &x, &y)) { + if (!PyArg_ParseTuple(args, "s|Oii:graph_add_node_variable_set", &name, &py_struct, &x, &y)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; UStruct *u_struct = nullptr; - if (py_struct && py_struct != Py_None) { - if (!ue_is_pyuobject(py_struct)) { + if (py_struct && py_struct != Py_None) + { + if (!ue_is_pyuobject(py_struct)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *ue_py_struct = (ue_PyUObject *)py_struct; - if (!ue_py_struct->ue_object->IsA()) { + if (!ue_py_struct->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UStruct"); } u_struct = (UStruct *)ue_py_struct->ue_object; @@ -266,15 +278,11 @@ PyObject *py_ue_graph_add_node_variable_set(ue_PyUObject * self, PyObject * args FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -283,39 +291,47 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { int y = 0; PyObject *py_data = nullptr; char *metadata = nullptr; - if (!PyArg_ParseTuple(args, "O|iisO:graph_add_node", &py_node_class, &x, &y, &metadata, &py_data)) { + if (!PyArg_ParseTuple(args, "O|iisO:graph_add_node", &py_node_class, &x, &y, &metadata, &py_data)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); } UEdGraph *graph = (UEdGraph *)self->ue_object; - if (!ue_is_pyuobject(py_node_class)) { + if (!ue_is_pyuobject(py_node_class)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } UEdGraphNode *node = nullptr; ue_PyUObject *py_obj = (ue_PyUObject *)py_node_class; - if (py_obj->ue_object->IsA()) { + if (py_obj->ue_object->IsA()) + { UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a child of UEdGraphNode"); } node = (UEdGraphNode *)NewObject(graph, u_class); node->PostLoad(); } - else if (py_obj->ue_object->IsA()) { + else if (py_obj->ue_object->IsA()) + { node = (UEdGraphNode *)py_obj->ue_object; - if (node->GetOuter() != graph) { + if (node->GetOuter() != graph) + { node->Rename(*node->GetName(), graph); } } - if (!node) { + if (!node) + { return PyErr_Format(PyExc_Exception, "argument is not a supported type"); } @@ -327,63 +343,71 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { node->NodePosY = y; // do something with data, based on the node type - if (node->IsA()) { + if (node->IsA()) + { UAIGraphNode *ai_node = (UAIGraphNode *)node; - if (py_data) { + if (py_data) + { FGraphNodeClassData *class_data = ue_py_check_struct(py_data); - if (class_data == nullptr) { + if (class_data == nullptr) + { UE_LOG(LogPython, Warning, TEXT("Unable to manage data argument for UAIGraphNode")); } - else { + else + { ai_node->ClassData = *class_data; } } } - if (metadata == nullptr || strlen(metadata) == 0) { + if (metadata == nullptr || strlen(metadata) == 0) + { UEdGraphSchema_K2::SetNodeMetaData(node, FNodeMetadata::DefaultGraphNode); } - else { + else + { UEdGraphSchema_K2::SetNodeMetaData(node, FName(UTF8_TO_TCHAR(metadata))); } graph->AddNode(node); - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(node); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(node); } -PyObject *py_ue_node_pins(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_pins(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); } UEdGraphNode *node = (UEdGraphNode *)self->ue_object; PyObject *pins_list = PyList_New(0); - for (UEdGraphPin *pin : node->Pins) { + for (UEdGraphPin *pin : node->Pins) + { PyList_Append(pins_list, (PyObject *)py_ue_new_edgraphpin(pin)); } return pins_list; } -PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); int title_type = ENodeTitleType::FullTitle; - if (!PyArg_ParseTuple(args, "|i:node_get_title", &title_type)) { + if (!PyArg_ParseTuple(args, "|i:node_get_title", &title_type)) + { return NULL; } @@ -392,11 +416,12 @@ PyObject *py_ue_node_get_title(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); FText title = node->GetNodeTitle((ENodeTitleType::Type)title_type); - + return PyUnicode_FromString(TCHAR_TO_UTF8(*(title.ToString()))); } -PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -410,7 +435,8 @@ PyObject *py_ue_node_allocate_default_pins(ue_PyUObject * self, PyObject * args) return Py_None; } -PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -424,23 +450,27 @@ PyObject *py_ue_node_reconstruct(ue_PyUObject * self, PyObject * args) { return Py_None; } -PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name = nullptr; - if (!PyArg_ParseTuple(args, "s:node_find_pin", &name)) { + if (!PyArg_ParseTuple(args, "s:node_find_pin", &name)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraphNode"); } UEdGraphNode *node = (UEdGraphNode *)self->ue_object; UEdGraphPin *pin = node->FindPin(UTF8_TO_TCHAR(name)); - if (!pin) { + if (!pin) + { return PyErr_Format(PyExc_Exception, "unable to find pin \"%s\"", name); } @@ -449,7 +479,8 @@ PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) { return ret; } -PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -457,7 +488,8 @@ PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { PyObject *pin_type; char *name = nullptr; int index = 0; - if (!PyArg_ParseTuple(args, "iOs|i:node_create_pin", &pin_direction, &pin_type, &name, &index)) { + if (!PyArg_ParseTuple(args, "iOs|i:node_create_pin", &pin_direction, &pin_type, &name, &index)) + { return nullptr; } @@ -471,18 +503,22 @@ PyObject *py_ue_node_create_pin(ue_PyUObject * self, PyObject * args) { UEdGraphPin *pin = nullptr; - if (node->IsA()) { + if (node->IsA()) + { UK2Node_EditablePinBase *node_base = (UK2Node_EditablePinBase *)node; pin = node_base->CreateUserDefinedPin(UTF8_TO_TCHAR(name), *pin_struct, (EEdGraphPinDirection)pin_direction); } - else { + else + { pin = node->CreatePin((EEdGraphPinDirection)pin_direction, *pin_struct, UTF8_TO_TCHAR(name), index); } - if (!pin) { + if (!pin) + { return PyErr_Format(PyExc_Exception, "unable to create pin \"%s\"", name); } - if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) { + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp index d93476d6e..c9fc2441c 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp @@ -79,15 +79,27 @@ static PyMethodDef ue_PyEdGraphPin_methods[] = { }; static PyObject *py_ue_edgraphpin_get_name(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinName.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinName))); +#endif } static PyObject *py_ue_edgraphpin_get_category(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinCategory.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinCategory))); +#endif } static PyObject *py_ue_edgraphpin_get_sub_category(ue_PyEdGraphPin *self, void *closure) { +#if ENGINE_MINOR_VERSION > 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinSubCategory.ToString()))); +#else return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->pin->PinType.PinSubCategory))); +#endif } static PyObject *py_ue_edgraphpin_get_default_value(ue_PyEdGraphPin *self, void *closure) { @@ -107,14 +119,9 @@ static int py_ue_edgraphpin_set_default_value(ue_PyEdGraphPin *self, PyObject *v static PyObject *py_ue_edgraphpin_get_default_object(ue_PyEdGraphPin *self, void *closure) { UObject *u_object = self->pin->DefaultObject; if (!u_object) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - PyObject *ret = (PyObject *)ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(u_object); } static int py_ue_edgraphpin_set_default_object(ue_PyEdGraphPin *self, PyObject *value, void *closure) { @@ -139,7 +146,11 @@ static PyGetSetDef ue_PyEdGraphPin_getseters[] = { static PyObject *ue_PyEdGraphPin_str(ue_PyEdGraphPin *self) { return PyUnicode_FromFormat("", +#if ENGINE_MINOR_VERSION > 18 + TCHAR_TO_UTF8(*self->pin->PinName.ToString()), TCHAR_TO_UTF8(*self->pin->PinType.PinCategory.ToString())); +#else TCHAR_TO_UTF8(*self->pin->PinName), TCHAR_TO_UTF8(*self->pin->PinType.PinCategory)); +#endif } static PyTypeObject ue_PyEdGraphPinType = { diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp index bf911ff12..31af0269e 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.cpp @@ -12,7 +12,11 @@ static PyObject *py_ue_iconsole_manager_add_history_entry(PyObject *cls, PyObjec return nullptr; } +#if ENGINE_MINOR_VERSION > 18 + IConsoleManager::Get().AddConsoleHistoryEntry(TEXT(""), UTF8_TO_TCHAR(entry)); +#else IConsoleManager::Get().AddConsoleHistoryEntry(UTF8_TO_TCHAR(entry)); +#endif Py_RETURN_NONE; } @@ -20,7 +24,11 @@ static PyObject *py_ue_iconsole_manager_add_history_entry(PyObject *cls, PyObjec static PyObject *py_ue_iconsole_manager_get_history(PyObject *cls, PyObject * args) { TArray history; +#if ENGINE_MINOR_VERSION > 18 + IConsoleManager::Get().GetConsoleHistory(TEXT(""), history); +#else IConsoleManager::Get().GetConsoleHistory(history); +#endif PyObject *py_history = PyList_New(0); for (FString item : history) { @@ -132,11 +140,11 @@ static PyObject *py_ue_iconsole_manager_get_string(PyObject *cls, PyObject * arg return PyErr_Format(PyExc_Exception, "console object \"%s\" is not a variable", key); } - TConsoleVariableData *c_string = c_object->AsVariableString(); + /*TConsoleVariableData *c_string = c_object->AsVariableString(); if (!c_string) { return PyErr_Format(PyExc_Exception, "console object \"%s\" is not a string", key); - } + }*/ return PyUnicode_FromString(TCHAR_TO_UTF8(*var->GetString())); } @@ -507,7 +515,7 @@ static PyObject *py_ue_iconsole_manager_register_variable_float(PyObject *cls, P Py_RETURN_NONE; } -void UPythonConsoleDelegate::OnConsoleCommand(const TArray < FString > & InArgs) +void FPythonSmartConsoleDelegate::OnConsoleCommand(const TArray < FString > & InArgs) { FScopePythonGIL gil; @@ -555,11 +563,10 @@ static PyObject *py_ue_iconsole_manager_register_command(PyObject *cls, PyObject return PyErr_Format(PyExc_Exception, "console object \"%s\" already exists", key); } - UPythonConsoleDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartConsoleDelegate); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); FConsoleCommandWithArgsDelegate console_delegate; - console_delegate.BindUObject(py_delegate, &UPythonConsoleDelegate::OnConsoleCommand); + console_delegate.BindSP(py_delegate, &FPythonSmartConsoleDelegate::OnConsoleCommand); if (!IConsoleManager::Get().RegisterConsoleCommand(UTF8_TO_TCHAR(key), help ? UTF8_TO_TCHAR(help) : UTF8_TO_TCHAR(key), console_delegate, 0)) { diff --git a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h index b147fdf7e..01815ccfd 100644 --- a/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h +++ b/Source/UnrealEnginePython/Private/ConsoleManager/UEPyIConsoleManager.h @@ -3,18 +3,15 @@ #include "UnrealEnginePython.h" #include "Runtime/Core/Public/HAL/IConsoleManager.h" -#include "UEPyIConsoleManager.generated.h" typedef struct { PyObject_HEAD - /* Type-specific fields go here. */ + /* Type-specific fields go here. */ } ue_PyIConsoleManager; -UCLASS() -class UPythonConsoleDelegate : public UPythonDelegate +class FPythonSmartConsoleDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: void OnConsoleCommand(const TArray < FString > &InArgs); diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp index eb3ea1ed1..96f003948 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.cpp @@ -1,7 +1,6 @@ - -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#if ENGINE_MINOR_VERSION > 12 #if WITH_EDITOR #include "UEPyFbx.h" diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp index 15fa7d7a8..cf367203e 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.cpp @@ -1,9 +1,8 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#include "UEPyFbx.h" #if WITH_EDITOR - -#include "UEPyFbx.h" +#if ENGINE_MINOR_VERSION > 12 static PyObject *py_ue_fbx_mesh_get_polygon_count(ue_PyFbxMesh *self, PyObject *args) { diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h index 370698a10..be525201f 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxMesh.h @@ -2,6 +2,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 12 #include @@ -15,6 +16,7 @@ struct ue_PyFbxMesh void ue_python_init_fbx_mesh(PyObject *); -PyObject *py_ue_new_fbx_mesh(FbxNode *); +PyObject *py_ue_new_fbx_mesh(FbxMesh *); #endif +#endif diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp index 9f9f62871..15dfa0170 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.cpp @@ -1,10 +1,8 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" - -#if WITH_EDITOR - #include "UEPyFbx.h" +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 12 static PyObject *py_ue_fbx_node_get_child_count(ue_PyFbxNode *self, PyObject *args) { return PyLong_FromLong(self->fbx_node->GetChildCount()); } diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h index 7e07f089f..2fd7d6f84 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxNode.h @@ -2,7 +2,7 @@ #include "UnrealEnginePython.h" #if WITH_EDITOR - +#if ENGINE_MINOR_VERSION > 12 #include struct ue_PyFbxNode { @@ -19,3 +19,4 @@ PyObject *py_ue_new_fbx_node(FbxNode *); ue_PyFbxNode *py_ue_is_fbx_node(PyObject *); #endif +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp index 414d0a545..b9d902a32 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxObject.cpp @@ -138,6 +138,108 @@ static PyObject *py_ue_fbx_object_key_get_seconds(ue_PyFbxObject *self, PyObject return PyFloat_FromDouble(fbx_anim_curve->KeyGetTime(index).GetSecondDouble()); } +static PyObject *py_ue_fbx_object_key_get_left_tangent(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + return PyFloat_FromDouble(fbx_anim_curve->KeyGetLeftDerivative(index)); +} + +static PyObject *py_ue_fbx_object_key_get_right_tangent(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + return PyFloat_FromDouble(fbx_anim_curve->KeyGetRightDerivative(index)); +} + +static PyObject *py_ue_fbx_object_key_get_interp_mode(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + + ERichCurveInterpMode Mode = RCIM_Linear; + // Convert the interpolation type from FBX to Unreal. + switch (fbx_anim_curve->KeyGetInterpolation(index)) + { + case FbxAnimCurveDef::eInterpolationCubic: + Mode = RCIM_Cubic; + break; + + case FbxAnimCurveDef::eInterpolationConstant: + if (fbx_anim_curve->KeyGetTangentMode(index) != (FbxAnimCurveDef::ETangentMode)FbxAnimCurveDef::eConstantStandard) + { + // warning not support + ; + } + Mode = RCIM_Constant; + break; + + case FbxAnimCurveDef::eInterpolationLinear: + Mode = RCIM_Linear; + break; + } + + return PyLong_FromUnsignedLong(uint64(Mode)); +} + +static PyObject *py_ue_fbx_object_key_get_tangent_mode(ue_PyFbxObject *self, PyObject *args) { + int index; + if (!PyArg_ParseTuple(args, "i", &index)) { + return nullptr; + } + FbxAnimCurve *fbx_anim_curve = FbxCast(self->fbx_object); + if (!fbx_anim_curve) + return PyErr_Format(PyExc_Exception, "object is not a FbxAnimCurve"); + + ERichCurveTangentMode Mode = RCTM_Auto; + // Convert the interpolation type from FBX to Unreal. + if ( fbx_anim_curve->KeyGetInterpolation(index) == + FbxAnimCurveDef::eInterpolationCubic ) + { + switch (fbx_anim_curve->KeyGetTangentMode(index)) + { + // Auto tangents will now be imported as user tangents to allow the + // user to modify them without inadvertently resetting other tangents + // case KFbxAnimCurveDef::eTANGENT_AUTO: + // if ((KFbxAnimCurveDef::eTANGENT_GENERIC_CLAMP & FbxKey.GetTangentMode(true))) + // { + // Mode = CIM_CurveAutoClamped; + // } + // else + // { + // Mode = CIM_CurveAuto; + // } + // break; + case FbxAnimCurveDef::eTangentBreak: + Mode = RCTM_Break; + break; + case FbxAnimCurveDef::eTangentAuto: + Mode = RCTM_Auto; + break; + case FbxAnimCurveDef::eTangentUser: + case FbxAnimCurveDef::eTangentTCB: + Mode = RCTM_User; + break; + default: + break; + } + } + + return PyLong_FromUnsignedLong(uint64(Mode)); +} + static PyMethodDef ue_PyFbxObject_methods[] = { { "get_member_count", (PyCFunction)py_ue_fbx_object_get_member_count, METH_VARARGS, "" }, { "get_member", (PyCFunction)py_ue_fbx_object_get_member, METH_VARARGS, "" }, @@ -153,6 +255,10 @@ static PyMethodDef ue_PyFbxObject_methods[] = { { "key_get_count", (PyCFunction)py_ue_fbx_object_key_get_count, METH_VARARGS, "" }, { "key_get_value", (PyCFunction)py_ue_fbx_object_key_get_value, METH_VARARGS, "" }, { "key_get_seconds", (PyCFunction)py_ue_fbx_object_key_get_seconds, METH_VARARGS, "" }, + { "key_get_left_tangent", (PyCFunction)py_ue_fbx_object_key_get_left_tangent, METH_VARARGS, "" }, + { "key_get_right_tangent", (PyCFunction)py_ue_fbx_object_key_get_right_tangent, METH_VARARGS, "" }, + { "key_get_interp_mode", (PyCFunction)py_ue_fbx_object_key_get_interp_mode, METH_VARARGS, "" }, + { "key_get_tangent_mode", (PyCFunction)py_ue_fbx_object_key_get_tangent_mode, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp index eb4fd6dbc..62a076746 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxProperty.cpp @@ -1,6 +1,6 @@ -#if ENGINE_MINOR_VERSION > 12 #include "UnrealEnginePythonPrivatePCH.h" +#if ENGINE_MINOR_VERSION > 12 #if WITH_EDITOR #include "UEPyFbx.h" @@ -14,6 +14,10 @@ static PyObject *py_ue_fbx_property_get_double3(ue_PyFbxProperty *self, PyObject return Py_BuildValue((char *)"(fff)", value[0], value[1], value[2]); } +static PyObject *py_ue_fbx_property_get_string(ue_PyFbxProperty *self, PyObject *args) { + return PyUnicode_FromString(self->fbx_property.Get()); +} + static PyObject *py_ue_fbx_property_is_valid(ue_PyFbxProperty *self, PyObject *args) { if (self->fbx_property.IsValid()) { Py_RETURN_TRUE; @@ -44,6 +48,7 @@ static PyObject *py_ue_fbx_property_get_curve_node(ue_PyFbxProperty *self, PyObj static PyMethodDef ue_PyFbxProperty_methods[] = { { "get_name", (PyCFunction)py_ue_fbx_property_get_name, METH_VARARGS, "" }, { "get_double3", (PyCFunction)py_ue_fbx_property_get_double3, METH_VARARGS, "" }, + { "get_string", (PyCFunction)py_ue_fbx_property_get_string, METH_VARARGS, "" }, { "is_valid", (PyCFunction)py_ue_fbx_property_is_valid, METH_VARARGS, "" }, { "get_curve_node", (PyCFunction)py_ue_fbx_property_get_curve_node, METH_VARARGS, "" }, { NULL } /* Sentinel */ diff --git a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp index a69bd690e..5147c25c9 100644 --- a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp +++ b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.cpp @@ -3,10 +3,12 @@ #include "Runtime/Online/HTTP/Public/HttpManager.h" -static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject * args) +{ char *verb; - if (!PyArg_ParseTuple(args, "s:set_verb", &verb)) { + if (!PyArg_ParseTuple(args, "s:set_verb", &verb)) + { return NULL; } @@ -16,10 +18,12 @@ static PyObject *py_ue_ihttp_request_set_verb(ue_PyIHttpRequest *self, PyObject return Py_None; } -static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * args) +{ char *url; - if (!PyArg_ParseTuple(args, "s:set_url", &url)) { + if (!PyArg_ParseTuple(args, "s:set_url", &url)) + { return NULL; } @@ -29,11 +33,13 @@ static PyObject *py_ue_ihttp_request_set_url(ue_PyIHttpRequest *self, PyObject * return Py_None; } -static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObject * args) +{ char *key; char *value; - if (!PyArg_ParseTuple(args, "ss:set_header", &key, &value)) { + if (!PyArg_ParseTuple(args, "ss:set_header", &key, &value)) + { return NULL; } @@ -43,11 +49,13 @@ static PyObject *py_ue_ihttp_request_set_header(ue_PyIHttpRequest *self, PyObjec return Py_None; } -static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, PyObject * args) +{ char *key; char *value; - if (!PyArg_ParseTuple(args, "ss:append_to_header", &key, &value)) { + if (!PyArg_ParseTuple(args, "ss:append_to_header", &key, &value)) + { return NULL; } @@ -57,17 +65,21 @@ static PyObject *py_ue_ihttp_request_append_to_header(ue_PyIHttpRequest *self, P return Py_None; } -static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:set_content", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:set_content", &py_obj)) + { return NULL; } - if (PyUnicode_Check(py_obj)) { + if (PyUnicode_Check(py_obj)) + { self->http_request->SetContentAsString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_obj))); } - else if (PyBytes_Check(py_obj)) { + else if (PyBytes_Check(py_obj)) + { char *buf = nullptr; Py_ssize_t len = 0; PyBytes_AsStringAndSize(py_obj, &buf, &len); @@ -80,10 +92,12 @@ static PyObject *py_ue_ihttp_request_set_content(ue_PyIHttpRequest *self, PyObje return Py_None; } -static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * args) +{ float delta_seconds; - if (!PyArg_ParseTuple(args, "f:tick", &delta_seconds)) { + if (!PyArg_ParseTuple(args, "f:tick", &delta_seconds)) + { return NULL; } @@ -95,112 +109,129 @@ static PyObject *py_ue_ihttp_request_tick(ue_PyIHttpRequest *self, PyObject * ar return Py_None; } -static PyObject *py_ue_ihttp_request_process_request(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_process_request(ue_PyIHttpRequest *self, PyObject * args) +{ self->http_request->ProcessRequest(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ihttp_request_cancel_request(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_cancel_request(ue_PyIHttpRequest *self, PyObject * args) +{ self->http_request->CancelRequest(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ihttp_request_get_status(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_status(ue_PyIHttpRequest *self, PyObject * args) +{ return PyLong_FromLong((int)self->http_request->GetStatus()); } -static PyObject *py_ue_ihttp_request_get_verb(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_verb(ue_PyIHttpRequest *self, PyObject * args) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->http_request->GetVerb())); } -static PyObject *py_ue_ihttp_request_get_elapsed_time(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_elapsed_time(ue_PyIHttpRequest *self, PyObject * args) +{ return PyFloat_FromDouble(self->http_request->GetElapsedTime()); } -static PyObject *py_ue_ihttp_request_get_response(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_get_response(ue_PyIHttpRequest *self, PyObject * args) +{ FHttpResponsePtr response = self->http_request->GetResponse(); - if (!response.IsValid()) { + if (!response.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to retrieve IHttpResponse"); } return py_ue_new_ihttp_response(response.Get()); } -void UPythonHttpDelegate::OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful) { +void FPythonSmartHttpDelegate::OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful) +{ FScopePythonGIL gil; - if (!request.IsValid() || !response.IsValid()) { + if (!request.IsValid() || !response.IsValid()) + { UE_LOG(LogPython, Error, TEXT("Unable to retrieve HTTP infos")); return; } PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OOO", py_http_request, py_ue_new_ihttp_response(response.Get()), successful ? Py_True : Py_False); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -void UPythonHttpDelegate::OnRequestProgress(FHttpRequestPtr request, int32 sent, int32 received) { +void FPythonSmartHttpDelegate::OnRequestProgress(FHttpRequestPtr request, int32 sent, int32 received) +{ FScopePythonGIL gil; - if (!request.IsValid()) { + if (!request.IsValid()) + { UE_LOG(LogPython, Error, TEXT("Unable to retrieve HTTP infos")); return; } PyObject *ret = PyObject_CallFunction(py_callable, (char *)"Oii", py_http_request, sent, received); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -static PyObject *py_ue_ihttp_request_bind_on_process_request_complete(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_bind_on_process_request_complete(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_process_request_complete", &py_callable)) { - return NULL; + if (!PyArg_ParseTuple(args, "O:bind_on_process_request_complete", &py_callable)) + { + return nullptr; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not a callable"); } - UPythonHttpDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartHttpDelegate); py_delegate->SetPyCallable(py_callable); // this trick avoids generating a new python object py_delegate->SetPyHttpRequest(self); - self->http_request->OnProcessRequestComplete().BindUObject(py_delegate, &UPythonHttpDelegate::OnRequestComplete); + self->http_request->OnProcessRequestComplete().BindSP(py_delegate, &FPythonSmartHttpDelegate::OnRequestComplete); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -static PyObject *py_ue_ihttp_request_bind_on_request_progress(ue_PyIHttpRequest *self, PyObject * args) { +static PyObject *py_ue_ihttp_request_bind_on_request_progress(ue_PyIHttpRequest *self, PyObject * args) +{ PyObject *py_callable; - if (!PyArg_ParseTuple(args, "O:bind_on_request_progress", &py_callable)) { - return NULL; + if (!PyArg_ParseTuple(args, "O:bind_on_request_progress", &py_callable)) + { + return nullptr; } - if (!PyCallable_Check(py_callable)) { + if (!PyCallable_Check(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not a callable"); } - UPythonHttpDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartHttpDelegate); py_delegate->SetPyCallable(py_callable); // this trick avoids generating a new python object py_delegate->SetPyHttpRequest(self); - self->http_request->OnRequestProgress().BindUObject(py_delegate, &UPythonHttpDelegate::OnRequestProgress); + self->http_request->OnRequestProgress().BindSP(py_delegate, &FPythonSmartHttpDelegate::OnRequestProgress); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef ue_PyIHttpRequest_methods[] = { @@ -228,12 +259,13 @@ static PyObject *ue_PyIHttpRequest_str(ue_PyIHttpRequest *self) &self->http_request.Get()); } -static void ue_PyIHttpRequest_dealloc(ue_PyIHttpRequest *self) { +static void ue_PyIHttpRequest_dealloc(ue_PyIHttpRequest *self) +{ #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIHttpRequest %p mapped to IHttpRequest %p"), self, &self->http_request.Get()); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIHttpRequest %p mapped to IHttpRequest %p"), self, &self->http_request.Get()); #endif Py_DECREF(self->py_dict); - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyTypeObject ue_PyIHttpRequestType = { @@ -269,19 +301,23 @@ static PyTypeObject ue_PyIHttpRequestType = { 0, }; -static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyObject *kwargs) { +static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyObject *kwargs) +{ char *verb = nullptr; char* url = nullptr; - if (!PyArg_ParseTuple(args, "|ss:__init__", &verb, &url)) { + if (!PyArg_ParseTuple(args, "|ss:__init__", &verb, &url)) + { return -1; } new(&self->http_request) TSharedRef(FHttpModule::Get().CreateRequest()); self->py_dict = PyDict_New(); - if (verb) { + if (verb) + { self->http_request->SetVerb(UTF8_TO_TCHAR(verb)); } - if (url) { + if (url) + { self->http_request->SetURL(UTF8_TO_TCHAR(url)); } @@ -289,7 +325,8 @@ static int ue_py_ihttp_request_init(ue_PyIHttpRequest *self, PyObject *args, PyO return 0; } -void ue_python_init_ihttp_request(PyObject *ue_module) { +void ue_python_init_ihttp_request(PyObject *ue_module) +{ ue_PyIHttpRequestType.tp_new = PyType_GenericNew; ue_PyIHttpRequestType.tp_init = (initproc)ue_py_ihttp_request_init; @@ -297,8 +334,8 @@ void ue_python_init_ihttp_request(PyObject *ue_module) { ue_PyIHttpRequestType.tp_base = &ue_PyIHttpBaseType; ue_PyIHttpRequestType.tp_getattro = PyObject_GenericGetAttr; - ue_PyIHttpRequestType.tp_setattro = PyObject_GenericSetAttr; - ue_PyIHttpRequestType.tp_dictoffset = offsetof(ue_PyIHttpRequest, py_dict); + ue_PyIHttpRequestType.tp_setattro = PyObject_GenericSetAttr; + ue_PyIHttpRequestType.tp_dictoffset = offsetof(ue_PyIHttpRequest, py_dict); if (PyType_Ready(&ue_PyIHttpRequestType) < 0) return; diff --git a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h index b527012d9..4873942f6 100644 --- a/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h +++ b/Source/UnrealEnginePython/Private/Http/UEPyIHttpRequest.h @@ -5,8 +5,6 @@ #include "Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h" #include "Runtime/Online/HTTP/Public/HttpModule.h" -#include "UEPyIHttpRequest.generated.h" - extern PyTypeObject ue_PyIHttpBaseType; @@ -20,10 +18,8 @@ typedef struct { void ue_python_init_ihttp_request(PyObject *); -UCLASS() -class UPythonHttpDelegate : public UPythonDelegate +class FPythonSmartHttpDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: void OnRequestComplete(FHttpRequestPtr request, FHttpResponsePtr response, bool successful); @@ -34,7 +30,7 @@ class UPythonHttpDelegate : public UPythonDelegate Py_INCREF(py_http_request); } - ~UPythonHttpDelegate() { + ~FPythonSmartHttpDelegate() { Py_XDECREF(py_http_request); } protected: diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp new file mode 100644 index 000000000..661105f14 --- /dev/null +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -0,0 +1,134 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR + +#include "UEPyFMaterialEditorUtilities.h" + + + + +static PyObject *py_ue_paste_nodes_here(PyObject *cls, PyObject * args) +{ + PyObject *py_graph; + float x; + float y; + + if (!PyArg_ParseTuple(args, "O(ff):paste_nodes_here", &py_graph, &x, &y)) + return nullptr; + + UEdGraph *Graph = ue_py_check_type(py_graph); + if (!Graph) + { + return PyErr_Format(PyExc_Exception, "argument is not a UEdGraph"); + } + + FMaterialEditorUtilities::PasteNodesHere(Graph, FVector2D(x, y)); + Py_RETURN_NONE; +} + +static PyObject *py_ue_update_material_after_graph_change(PyObject *cls, PyObject * args) +{ + PyObject *py_graph; + + if (!PyArg_ParseTuple(args, "O:update_material_after_graph_change", &py_graph)) + return nullptr; + + UEdGraph *Graph = ue_py_check_type(py_graph); + if (!Graph) + { + return PyErr_Format(PyExc_Exception, "argument is not a UEdGraph"); + } + + FMaterialEditorUtilities::UpdateMaterialAfterGraphChange(Graph); + Py_RETURN_NONE; +} + +static PyObject *py_ue_command_apply(PyObject *cls, PyObject * args) +{ + PyObject *py_material; + + if (!PyArg_ParseTuple(args, "O:command_apply", &py_material)) + return nullptr; + + UMaterial *Material = ue_py_check_type(py_material); + if (!Material) + { + return PyErr_Format(PyExc_Exception, "argument is not a UMaterial"); + } + + IAssetEditorInstance *Instance = FAssetEditorManager::Get().FindEditorForAsset(Material, false); + if (!Instance) + { + return PyErr_Format(PyExc_Exception, "unable to retrieve editor for UMaterial"); + } + + IMaterialEditor *MaterialEditor = (IMaterialEditor *)Instance; + + MaterialEditor->GetToolkitCommands()->ExecuteAction(FMaterialEditorCommands::Get().Apply.ToSharedRef()); + + Py_RETURN_NONE; + +} + + +static PyMethodDef ue_PyFMaterialEditorUtilities_methods[] = { + { "paste_nodes_here", (PyCFunction)py_ue_paste_nodes_here, METH_VARARGS | METH_CLASS, "" }, + { "update_material_after_graph_change", (PyCFunction)py_ue_update_material_after_graph_change, METH_VARARGS | METH_CLASS, "" }, + { "command_apply", (PyCFunction)py_ue_command_apply, METH_VARARGS | METH_CLASS, "" }, + { NULL } /* Sentinel */ +}; + + +static PyTypeObject ue_PyFMaterialEditorUtilitiesType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FMaterialEditorUtilities", /* tp_name */ + sizeof(ue_PyFMaterialEditorUtilities), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine MaterialEditorUtilities", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFMaterialEditorUtilities_methods, /* tp_methods */ + 0, + 0, +}; + +static int py_ue_fmaterial_editor_utilities_init(ue_PyFMaterialEditorUtilities *self, PyObject * args) +{ + PyErr_SetString(PyExc_Exception, "FMaterialEditorUtilities is a singleton"); + return -1; +} + +void ue_python_init_fmaterial_editor_utilities(PyObject *ue_module) +{ + ue_PyFMaterialEditorUtilitiesType.tp_new = PyType_GenericNew; + ue_PyFMaterialEditorUtilitiesType.tp_init = (initproc)py_ue_fmaterial_editor_utilities_init; + + if (PyType_Ready(&ue_PyFMaterialEditorUtilitiesType) < 0) + return; + + Py_INCREF(&ue_PyFMaterialEditorUtilitiesType); + PyModule_AddObject(ue_module, "FMaterialEditorUtilities", (PyObject *)&ue_PyFMaterialEditorUtilitiesType); +} + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h new file mode 100644 index 000000000..7e6b7d5d3 --- /dev/null +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h @@ -0,0 +1,19 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR + +#include "Editor/MaterialEditor/Public/MaterialEditorUtilities.h" +#include "Editor/MaterialEditor/Public/MaterialEditorActions.h" +#include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" +#include "Editor/MaterialEditor/Public/IMaterialEditor.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ +} ue_PyFMaterialEditorUtilities; + +void ue_python_init_fmaterial_editor_utilities(PyObject *); +#endif diff --git a/Source/UnrealEnginePython/Private/PyActor.cpp b/Source/UnrealEnginePython/Private/PyActor.cpp index 767b6670d..a7fa60170 100644 --- a/Source/UnrealEnginePython/Private/PyActor.cpp +++ b/Source/UnrealEnginePython/Private/PyActor.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyActor.h" @@ -21,7 +23,7 @@ void APyActor::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); @@ -68,6 +70,7 @@ void APyActor::PreInitializeComponents() PyObject_SetAttrString(py_actor_instance, (char*)"uobject", (PyObject *)py_uobject); + if (!PyObject_HasAttrString(py_actor_instance, (char *)"tick") || PythonTickForceDisabled) { SetActorTickEnabled(false); @@ -78,6 +81,7 @@ void APyActor::PreInitializeComponents() ue_bind_events_for_py_class_by_attribute(this, py_actor_instance); + if (!PyObject_HasAttrString(py_actor_instance, (char *)"pre_initialize_components")) return; @@ -132,6 +136,7 @@ void APyActor::BeginPlay() return; } Py_DECREF(bp_ret); + } @@ -177,6 +182,8 @@ void APyActor::EndPlay(const EEndPlayReason::Type EndPlayReason) Py_XDECREF(ep_ret); } + + Super::EndPlay(EndPlayReason); // ... @@ -282,20 +289,14 @@ APyActor::~APyActor() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - -#if defined(UEPY_MEMORY_DEBUG) - if (py_actor_instance && py_actor_instance->ob_refcnt != 1) - { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python AActor wrapper refcnt = %d"), py_actor_instance->ob_refcnt); - } -#endif Py_XDECREF(py_actor_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python AActor %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif - // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); + + } diff --git a/Source/UnrealEnginePython/Private/PyCharacter.cpp b/Source/UnrealEnginePython/Private/PyCharacter.cpp index d0c43b97c..ce5954922 100644 --- a/Source/UnrealEnginePython/Private/PyCharacter.cpp +++ b/Source/UnrealEnginePython/Private/PyCharacter.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyCharacter.h" @@ -27,7 +29,7 @@ void APyCharacter::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -189,7 +191,7 @@ void APyCharacter::SetPythonAttrObject(FString attr, UObject *object) FScopePythonGIL gil; - ue_PyUObject *py_obj = ue_get_python_wrapper(object); + ue_PyUObject *py_obj = ue_get_python_uobject(object); if (!py_obj) { PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); unreal_engine_py_log_error(); @@ -406,7 +408,7 @@ void APyCharacter::SetupPlayerInputComponent(class UInputComponent* input) // no need to check for method availability, we did it in begin_play - PyObject *ret = PyObject_CallMethod(py_character_instance, (char *)"setup_player_input_component", (char *)"O", ue_get_python_wrapper(input)); + PyObject *ret = PyObject_CallMethod(py_character_instance, (char *)"setup_player_input_component", (char *)"O", ue_get_python_uobject(input)); if (!ret) { unreal_engine_py_log_error(); return; @@ -441,19 +443,14 @@ APyCharacter::~APyCharacter() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); -#if defined(UEPY_MEMORY_DEBUG) - if (py_character_instance && py_character_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python ACharacter wrapper refcnt = %d"), py_character_instance->ob_refcnt); - } -#endif Py_XDECREF(py_character_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python ACharacter (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python ACharacter (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyCommandlet.cpp b/Source/UnrealEnginePython/Private/PyCommandlet.cpp index ab4873297..42d80c422 100644 --- a/Source/UnrealEnginePython/Private/PyCommandlet.cpp +++ b/Source/UnrealEnginePython/Private/PyCommandlet.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "PyCommandlet.h" @@ -28,7 +30,11 @@ int32 UPyCommandlet::Main(const FString& CommandLine) const FRegexPattern myPattern(RegexString); FRegexMatcher myMatcher(myPattern, *CommandLine); myMatcher.FindNext(); +#if ENGINE_MINOR_VERSION >= 18 + FString PyCommandLine = myMatcher.GetCaptureGroup(0).TrimStart().TrimEnd(); +#else FString PyCommandLine = myMatcher.GetCaptureGroup(0).Trim().TrimTrailing(); +#endif TArray PyArgv; PyArgv.Add(FString()); diff --git a/Source/UnrealEnginePython/Private/PyHUD.cpp b/Source/UnrealEnginePython/Private/PyHUD.cpp index 114e77581..dbedc3027 100644 --- a/Source/UnrealEnginePython/Private/PyHUD.cpp +++ b/Source/UnrealEnginePython/Private/PyHUD.cpp @@ -25,7 +25,7 @@ void APyHUD::BeginPlay() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); @@ -242,20 +242,14 @@ APyHUD::~APyHUD() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); -#if defined(UEPY_MEMORY_DEBUG) - if (py_hud_instance && py_hud_instance->ob_refcnt != 1) - { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python AHUD wrapper refcnt = %d"), py_hud_instance->ob_refcnt); - } -#endif Py_XDECREF(py_hud_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python AHUD %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python AHUD %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp b/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp new file mode 100644 index 000000000..486887f6b --- /dev/null +++ b/Source/UnrealEnginePython/Private/PyNativeWidgetHost.cpp @@ -0,0 +1,20 @@ +#include "UnrealEnginePythonPrivatePCH.h" +#include "PyNativeWidgetHost.h" + +#include "UnrealEnginePython.h" + + + + +UPyNativeWidgetHost::UPyNativeWidgetHost(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + bIsVariable = true; +} + +#if WITH_EDITOR +const FText UPyNativeWidgetHost::GetPaletteCategory() +{ + return NSLOCTEXT("Python", "Python", "Python"); +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PyPawn.cpp b/Source/UnrealEnginePython/Private/PyPawn.cpp index 5ca19eeda..066716863 100644 --- a/Source/UnrealEnginePython/Private/PyPawn.cpp +++ b/Source/UnrealEnginePython/Private/PyPawn.cpp @@ -41,7 +41,7 @@ void APyPawn::PreInitializeComponents() FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -239,20 +239,12 @@ APyPawn::~APyPawn() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - -#if defined(UEPY_MEMORY_DEBUG) - if (py_pawn_instance && py_pawn_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python APawn wrapper refcnt = %d"), py_pawn_instance->ob_refcnt); - } -#endif - Py_XDECREF(py_pawn_instance); - - +# #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python APawn (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python APawn (mapped to %p) wrapper XDECREF'ed"), py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PyUserWidget.cpp b/Source/UnrealEnginePython/Private/PyUserWidget.cpp index 5bd82a14e..a7efd3b92 100644 --- a/Source/UnrealEnginePython/Private/PyUserWidget.cpp +++ b/Source/UnrealEnginePython/Private/PyUserWidget.cpp @@ -1,21 +1,33 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PyUserWidget.h" +#include "PyNativeWidgetHost.h" #include "PythonDelegate.h" #include "Slate/UEPyFGeometry.h" #include "Slate/UEPyFPaintContext.h" +#include "Widgets/Layout/SBox.h" +#include "UMGStyle.h" +#include "Runtime/UMG/Public/Blueprint/WidgetTree.h" + void UPyUserWidget::NativeConstruct() { Super::NativeConstruct(); + WidgetTree->ForEachWidget([&](UWidget* Widget) { + if (Widget->IsA()) + { + PyNativeWidgetHost = Cast(Widget); + } + }); + if (PythonModule.IsEmpty()) return; FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -62,7 +74,7 @@ void UPyUserWidget::NativeConstruct() if (PythonPaintForceDisabled) bCanEverPaint = false; - + if (!PyObject_HasAttrString(py_user_widget_instance, (char *)"construct")) return; @@ -215,6 +227,33 @@ FReply UPyUserWidget::NativeOnKeyDown(const FGeometry & InGeometry, const FKeyEv return FReply::Unhandled(); } +#if WITH_EDITOR + +const FText UPyUserWidget::GetPaletteCategory() +{ + return NSLOCTEXT("Python", "Python", "Python"); +} +#endif + +void UPyUserWidget::SetSlateWidget(TSharedRef InContent) +{ + if (PyNativeWidgetHost.IsValid()) + { + PyNativeWidgetHost->SetContent(InContent); + } +} + + +void UPyUserWidget::ReleaseSlateResources(bool bReleaseChildren) +{ + Super::ReleaseSlateResources(bReleaseChildren); +} + +TSharedRef UPyUserWidget::RebuildWidget() +{ + return Super::RebuildWidget(); +} + FReply UPyUserWidget::NativeOnMouseWheel(const FGeometry & InGeometry, const FPointerEvent & InMouseEvent) { Super::NativeOnMouseWheel(InGeometry, InMouseEvent); @@ -308,25 +347,23 @@ void UPyUserWidget::NativePaint(FPaintContext & InContext) const Py_DECREF(ret); } +UPyUserWidget::UPyUserWidget(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{} + UPyUserWidget::~UPyUserWidget() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - -#if defined(UEPY_MEMORY_DEBUG) - if (py_user_widget_instance && py_user_widget_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python UUserWidget wrapper refcnt = %d"), py_user_widget_instance->ob_refcnt); -} -#endif Py_XDECREF(py_user_widget_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python UUserWidget %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python UUserWidget %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } void UPyUserWidget::CallPythonUserWidgetMethod(FString method_name, FString args) diff --git a/Source/UnrealEnginePython/Private/PythonComponent.cpp b/Source/UnrealEnginePython/Private/PythonComponent.cpp index db22fdb6f..60e8f71ed 100644 --- a/Source/UnrealEnginePython/Private/PythonComponent.cpp +++ b/Source/UnrealEnginePython/Private/PythonComponent.cpp @@ -24,7 +24,7 @@ void UPythonComponent::InitializePythonComponent() { FScopePythonGIL gil; - py_uobject = ue_get_python_wrapper(this); + py_uobject = ue_get_python_uobject(this); if (!py_uobject) { unreal_engine_py_log_error(); return; @@ -189,7 +189,7 @@ void UPythonComponent::SetPythonAttrObject(FString attr, UObject *object) FScopePythonGIL gil; - ue_PyUObject *py_obj = ue_get_python_wrapper(object); + ue_PyUObject *py_obj = ue_get_python_uobject(object); if (!py_obj) { PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); unreal_engine_py_log_error(); @@ -423,7 +423,7 @@ UObject *UPythonComponent::CallPythonComponentMethodObject(FString method_name, ret = PyObject_CallMethod(py_component_instance, TCHAR_TO_UTF8(*method_name), NULL); } else { - PyObject *py_arg_uobject = (PyObject *)ue_get_python_wrapper(arg); + PyObject *py_arg_uobject = (PyObject *)ue_get_python_uobject(arg); if (!py_arg_uobject) { unreal_engine_py_log_error(); return nullptr; @@ -554,19 +554,14 @@ UPythonComponent::~UPythonComponent() { FScopePythonGIL gil; - ue_pydelegates_cleanup(py_uobject); - -#if defined(UEPY_MEMORY_DEBUG) - if (py_component_instance && py_component_instance->ob_refcnt != 1) { - UE_LOG(LogPython, Error, TEXT("Inconsistent Python UActorComponent wrapper refcnt = %d"), py_component_instance->ob_refcnt); - } -#endif +# Py_XDECREF(py_component_instance); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Python UActorComponent %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->ue_object : nullptr); + UE_LOG(LogPython, Warning, TEXT("Python UActorComponent %p (mapped to %p) wrapper XDECREF'ed"), this, py_uobject ? py_uobject->py_proxy : nullptr); #endif // this could trigger the distruction of the python/uobject mapper Py_XDECREF(py_uobject); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); } diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index b009c0784..35955e817 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -1,7 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "PythonDelegate.h" -UPythonDelegate::UPythonDelegate() { +UPythonDelegate::UPythonDelegate() +{ py_callable = nullptr; signature_set = false; } @@ -29,15 +30,18 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) PyObject *py_args = nullptr; - if (signature_set) { + if (signature_set) + { py_args = PyTuple_New(signature->NumParms); Py_ssize_t argn = 0; TFieldIterator PArgs(signature); - for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) { + for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) + { UProperty *prop = *PArgs; PyObject *arg = ue_py_convert_property(prop, (uint8 *)Parms); - if (!arg) { + if (!arg) + { unreal_engine_py_log_error(); Py_DECREF(py_args); return; @@ -49,7 +53,8 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) PyObject *ret = PyObject_CallObject(py_callable, py_args); Py_XDECREF(py_args); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } @@ -75,7 +80,8 @@ void UPythonDelegate::PyInputHandler() { FScopePythonGIL gil; PyObject *ret = PyObject_CallObject(py_callable, NULL); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } @@ -86,48 +92,21 @@ void UPythonDelegate::PyInputAxisHandler(float value) { FScopePythonGIL gil; PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", value); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(ret); } -bool UPythonDelegate::Tick(float DeltaTime) -{ - FScopePythonGIL gil; - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", DeltaTime); - if (!ret) { - unreal_engine_py_log_error(); - return false; - } - if (PyObject_IsTrue(ret)) { - Py_DECREF(ret); - return true; - } - Py_DECREF(ret); - return false; -} - -#if WITH_EDITOR -void UPythonDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) -{ - FScopePythonGIL gil; - PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_wrapper((UObject *)factory), ue_get_python_wrapper(u_object)); - if (!ret) { - unreal_engine_py_log_error(); - return; - } - Py_DECREF(ret); -} -#endif - UPythonDelegate::~UPythonDelegate() { FScopePythonGIL gil; + Py_XDECREF(py_callable); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("PythonDelegate callable XDECREF'ed")); + UE_LOG(LogPython, Warning, TEXT("PythonDelegate %p callable XDECREF'ed"), this); #endif -} +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PythonFunction.cpp b/Source/UnrealEnginePython/Private/PythonFunction.cpp index 6208d84a9..f63aeb8b9 100644 --- a/Source/UnrealEnginePython/Private/PythonFunction.cpp +++ b/Source/UnrealEnginePython/Private/PythonFunction.cpp @@ -10,7 +10,11 @@ void UPythonFunction::SetPyCallable(PyObject *callable) } +#if ENGINE_MINOR_VERSION > 18 +void UPythonFunction::CallPythonCallable(UObject *Context, FFrame& Stack, RESULT_DECL) +#else void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) +#endif { FScopePythonGIL gil; @@ -32,7 +36,7 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) PyObject *py_args = PyTuple_New(argn); if (Stack.Object && !is_static) { - PyObject *py_obj = (PyObject *)ue_get_python_wrapper(Stack.Object); + PyObject *py_obj = (PyObject *)ue_get_python_uobject(Stack.Object); if (!py_obj) { unreal_engine_py_log_error(); on_error = true; @@ -118,7 +122,8 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) UPythonFunction::~UPythonFunction() { Py_XDECREF(py_callable); + FUnrealEnginePythonHouseKeeper::Get()->UnregisterPyUObject(this); #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("PythonFunction callable XDECREF'ed")); + UE_LOG(LogPython, Warning, TEXT("PythonFunction callable %p XDECREF'ed"), this); #endif } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/PythonScript.cpp b/Source/UnrealEnginePython/Private/PythonScript.cpp index 3d3b2ece8..84f4e2fbd 100644 --- a/Source/UnrealEnginePython/Private/PythonScript.cpp +++ b/Source/UnrealEnginePython/Private/PythonScript.cpp @@ -1,5 +1,4 @@ #include "UnrealEnginePythonPrivatePCH.h" - #include "PythonScript.h" static void callback(void *arg) { diff --git a/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp new file mode 100644 index 000000000..62e72dcaf --- /dev/null +++ b/Source/UnrealEnginePython/Private/PythonSmartDelegate.cpp @@ -0,0 +1,71 @@ +#include "UnrealEnginePythonPrivatePCH.h" +#include "PythonSmartDelegate.h" + +FPythonSmartDelegate::FPythonSmartDelegate() +{ + py_callable = nullptr; +} + +void FPythonSmartDelegate::SetPyCallable(PyObject *callable) +{ + // do not acquire the gil here as we set the callable in python call themselves + py_callable = callable; + Py_INCREF(py_callable); +} + + + +bool FPythonSmartDelegate::Tick(float DeltaTime) +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"f", DeltaTime); + if (!ret) + { + unreal_engine_py_log_error(); + return false; + } + if (PyObject_IsTrue(ret)) + { + Py_DECREF(ret); + return true; + } + Py_DECREF(ret); + return false; +} + +void FPythonSmartDelegate::Void() +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, nullptr); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +#if WITH_EDITOR +void FPythonSmartDelegate::PyFOnAssetPostImport(UFactory *factory, UObject *u_object) +{ + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"OO", ue_get_python_uobject((UObject *)factory), ue_get_python_uobject(u_object)); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); + +} +#endif + + +FPythonSmartDelegate::~FPythonSmartDelegate() +{ + FScopePythonGIL gil; + Py_XDECREF(py_callable); +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("PythonSmartDelegate callable XDECREF'ed")); +#endif +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp index 49c83b5c0..b56af1690 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.cpp @@ -53,10 +53,42 @@ static PyTypeObject ue_PyFCharacterEventType = { ue_PyFCharacterEvent_methods, /* tp_methods */ }; +static int ue_py_fcharacter_event_init(ue_PyFCharacterEvent *self, PyObject *args, PyObject *kwargs) +{ + char *key; + PyObject *py_repeat = nullptr; + PyObject *py_modifier = nullptr; + if (!PyArg_ParseTuple(args, "sO|O", &key, &py_repeat, &py_modifier)) + { + return -1; + } + + FModifierKeysState modifier; + if (py_modifier) + { + ue_PyFModifierKeysState *f_modifier = py_ue_is_fmodifier_keys_state(py_modifier); + if (!f_modifier) + { + PyErr_SetString(PyExc_Exception, "argument is not a FModifierKeysState"); + return -1; + } + modifier = f_modifier->modifier; + } + + + FCharacterEvent Event(*UTF8_TO_TCHAR(key), modifier, 0, (py_repeat && PyObject_IsTrue(py_repeat))); + + new(&self->character_event) FCharacterEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + void ue_python_init_fcharacter_event(PyObject *ue_module) { ue_PyFCharacterEventType.tp_base = &ue_PyFInputEventType; + ue_PyFCharacterEventType.tp_init = (initproc)ue_py_fcharacter_event_init; if (PyType_Ready(&ue_PyFCharacterEventType) < 0) return; @@ -72,3 +104,10 @@ PyObject *py_ue_new_fcharacter_event(FCharacterEvent key_event) new(&ret->f_input.input) FInputEvent(key_event); return (PyObject *)ret; } + +ue_PyFCharacterEvent *py_ue_is_fcharacter_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFCharacterEventType)) + return nullptr; + return (ue_PyFCharacterEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h index 79b40f4bd..7070e0ee5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFCharacterEvent.h @@ -15,3 +15,4 @@ typedef struct { void ue_python_init_fcharacter_event(PyObject *); PyObject *py_ue_new_fcharacter_event(FCharacterEvent); +ue_PyFCharacterEvent *py_ue_is_fcharacter_event(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp index f4177b3be..503bc028f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFGeometry.cpp @@ -8,6 +8,16 @@ static PyObject *py_ue_fgeometry_get_local_size(ue_PyFGeometry *self, PyObject * return Py_BuildValue("(ff)", size.X, size.Y); } +static PyObject *py_ue_fgeometry_get_absolute_position(ue_PyFGeometry *self, PyObject * args) +{ +#if ENGINE_MINOR_VERSION < 17 + FVector2D size = self->geometry.AbsolutePosition; +#else + FVector2D size = self->geometry.GetAbsolutePosition(); +#endif + return Py_BuildValue("(ff)", size.X, size.Y); +} + static PyObject *py_ue_fgeometry_absolute_to_local(ue_PyFGeometry *self, PyObject * args) { float x, y; @@ -20,6 +30,7 @@ static PyObject *py_ue_fgeometry_absolute_to_local(ue_PyFGeometry *self, PyObjec static PyMethodDef ue_PyFGeometry_methods[] = { { "get_local_size", (PyCFunction)py_ue_fgeometry_get_local_size, METH_VARARGS, "" }, + { "get_absolute_position", (PyCFunction)py_ue_fgeometry_get_absolute_position, METH_VARARGS, "" }, { "absolute_to_local", (PyCFunction)py_ue_fgeometry_absolute_to_local, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp index 2fe3a7d9f..0d90a7d98 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.cpp @@ -55,9 +55,33 @@ static PyTypeObject ue_PyFKeyEventType = { ue_PyFKeyEvent_methods, /* tp_methods */ }; +static int ue_py_fkey_event_init(ue_PyFKeyEvent *self, PyObject *args, PyObject *kwargs) +{ + char *key; + if (!PyArg_ParseTuple(args, "s", &key)) + { + return -1; + } + + FKey InKey(key); + + // TODO make it configurable + FModifierKeysState modifier; + + // TODO configure repeat + FKeyEvent Event(InKey, modifier, 0, false, 0, 0); + + new(&self->key_event) FKeyEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + + void ue_python_init_fkey_event(PyObject *ue_module) { ue_PyFKeyEventType.tp_base = &ue_PyFInputEventType; + ue_PyFKeyEventType.tp_init = (initproc)ue_py_fkey_event_init; if (PyType_Ready(&ue_PyFKeyEventType) < 0) return; @@ -72,3 +96,10 @@ PyObject *py_ue_new_fkey_event(FKeyEvent key_event) { new(&ret->f_input.input) FInputEvent(key_event); return (PyObject *)ret; } + +ue_PyFKeyEvent *py_ue_is_fkey_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFKeyEventType)) + return nullptr; + return (ue_PyFKeyEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h index 98cbfeaef..417153acc 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFKeyEvent.h @@ -15,3 +15,5 @@ typedef struct { void ue_python_init_fkey_event(PyObject *); PyObject *py_ue_new_fkey_event(FKeyEvent); + +ue_PyFKeyEvent *py_ue_is_fkey_event(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 4519c46aa..1b128f24f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -42,30 +42,32 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO char *tooltip; PyObject *py_callable; PyObject *py_obj = nullptr; - if (!PyArg_ParseTuple(args, "ssO|O:add_menu_entry", &label, &tooltip, &py_callable, &py_obj)) + PyObject *py_uiaction_obj = nullptr; + if (!PyArg_ParseTuple(args, "ssO|OO:add_menu_entry", &label, &tooltip, &py_callable, &py_obj, &py_uiaction_obj)) return nullptr; - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } + FExecuteAction handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); if (py_obj) { Py_INCREF(py_obj); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::ExecuteAction, py_obj); + handler.BindSP(py_delegate, &FPythonSlateDelegate::ExecuteAction, py_obj); } else { - handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); + handler.BindSP(py_delegate, &FPythonSlateDelegate::SimpleExecuteAction); } - self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler)); + ue_PyESlateEnums *py_uiaction_enum = py_uiaction_obj ? py_ue_is_eslate_enums(py_uiaction_obj) : nullptr; + self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler), NAME_None, + py_uiaction_enum ? (EUserInterfaceActionType::Type)(py_uiaction_enum->val) : EUserInterfaceActionType::Button); Py_RETURN_NONE; } @@ -193,7 +195,7 @@ static PyTypeObject ue_PyFMenuBuilderType = { static int ue_py_fmenu_builder_init(ue_PyFMenuBuilder *self, PyObject *args, PyObject *kwargs) { - self->menu_builder = FMenuBuilder(true, nullptr); + new(&self->menu_builder) FMenuBuilder(true, nullptr); return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp new file mode 100644 index 000000000..fa998a860 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.cpp @@ -0,0 +1,141 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPyFModifierKeysState.h" + +static PyObject *py_ue_fmodifier_keys_state_are_caps_locked(ue_PyFModifierKeysState *self, PyObject * args) +{ + if (self->modifier.AreCapsLocked()) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyMethodDef ue_PyFModifierKeysState_methods[] = { + { "are_caps_locked", (PyCFunction)py_ue_fmodifier_keys_state_are_caps_locked, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyFModifierKeysState_str(ue_PyFModifierKeysState *self) +{ + return PyUnicode_FromFormat("", self); +} + +static PyTypeObject ue_PyFModifierKeysStateType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FModifierKeysState", /* tp_name */ + sizeof(ue_PyFModifierKeysState), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyFModifierKeysState_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine FModifierKeysState", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFModifierKeysState_methods, /* tp_methods */ +}; + +static int ue_py_fmodifier_keys_state_init(ue_PyFModifierKeysState *self, PyObject *args, PyObject *kwargs) +{ + static char *kw_names[] = { + (char *)"left_shift_down", + (char *)"right_shift_down", + (char *)"left_control_down", + (char *)"right_control_down", + (char *)"left_alt_down", + (char *)"right_alt_down", + (char *)"left_command_down", + (char *)"right_command_down", + (char *)"are_caps_locked", + NULL }; + + PyObject *py_left_shift_down = nullptr; + PyObject *py_right_shift_down = nullptr; + PyObject *py_left_control_down = nullptr; + PyObject *py_right_control_down = nullptr; + PyObject *py_left_alt_down = nullptr; + PyObject *py_right_alt_down = nullptr; + PyObject *py_left_command_down = nullptr; + PyObject *py_right_command_down = nullptr; + PyObject *py_are_capse_locked = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOOOO", kw_names, + &py_left_shift_down, + &py_right_shift_down, + &py_left_control_down, + &py_right_control_down, + &py_left_alt_down, + &py_right_alt_down, + &py_left_command_down, + &py_right_command_down, + &py_are_capse_locked)) + { + return -1; + } + + bool bInIsLeftShiftDown = (py_left_shift_down && PyObject_IsTrue(py_left_shift_down)); + bool bInIsRightShiftDown = (py_right_shift_down && PyObject_IsTrue(py_right_shift_down)); + bool bInIsLeftControlDown = (py_left_control_down && PyObject_IsTrue(py_left_control_down)); + bool bInIsRightControlDown = (py_right_control_down && PyObject_IsTrue(py_right_control_down)); + bool bInIsLeftAltDown = (py_left_alt_down && PyObject_IsTrue(py_left_alt_down)); + bool bInIsRightAltDown = (py_right_alt_down && PyObject_IsTrue(py_right_alt_down)); + bool bInIsLeftCommandDown = (py_left_command_down && PyObject_IsTrue(py_left_command_down)); + bool bInIsRightCommandDown = (py_right_command_down && PyObject_IsTrue(py_right_command_down)); + bool bInAreCapsLocked = (py_are_capse_locked && PyObject_IsTrue(py_are_capse_locked)); + + new(&self->modifier) FModifierKeysState( + bInIsLeftShiftDown, + bInIsRightShiftDown, + bInIsLeftControlDown, + bInIsRightControlDown, + bInIsLeftAltDown, + bInIsRightAltDown, + bInIsLeftCommandDown, + bInIsRightCommandDown, + bInAreCapsLocked); + + return 0; +} + +void ue_python_init_fmodifier_keys_state(PyObject *ue_module) +{ + ue_PyFModifierKeysStateType.tp_new = PyType_GenericNew; + ue_PyFModifierKeysStateType.tp_init = (initproc)ue_py_fmodifier_keys_state_init; + + if (PyType_Ready(&ue_PyFModifierKeysStateType) < 0) + return; + + Py_INCREF(&ue_PyFModifierKeysStateType); + PyModule_AddObject(ue_module, "FModifierKeysState", (PyObject *)&ue_PyFModifierKeysStateType); +} + +PyObject *py_ue_new_fmodifier_keys_state(FModifierKeysState modifier) +{ + ue_PyFModifierKeysState *ret = (ue_PyFModifierKeysState *)PyObject_New(ue_PyFModifierKeysState, &ue_PyFModifierKeysStateType); + new(&ret->modifier) FModifierKeysState(modifier); + return (PyObject *)ret; +} + +ue_PyFModifierKeysState *py_ue_is_fmodifier_keys_state(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFModifierKeysStateType)) + return nullptr; + return (ue_PyFModifierKeysState *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h new file mode 100644 index 000000000..97b5190da --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFModifierKeysState.h @@ -0,0 +1,15 @@ +#pragma once + +#include "UnrealEnginePython.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + FModifierKeysState modifier; +} ue_PyFModifierKeysState; + +void ue_python_init_fmodifier_keys_state(PyObject *); + +PyObject *py_ue_new_fmodifier_keys_state(FModifierKeysState); +ue_PyFModifierKeysState *py_ue_is_fmodifier_keys_state(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp index 033c0e3b3..0a13b1e51 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.cpp @@ -2,27 +2,32 @@ #include "UEPyFPointerEvent.h" -static PyObject *py_ue_fpointer_event_get_effecting_button(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_effecting_button(ue_PyFPointerEvent *self, PyObject * args) +{ FKey key = self->pointer.GetEffectingButton(); return py_ue_new_uscriptstruct(FKey::StaticStruct(), (uint8*)&key); } -static PyObject *py_ue_fpointer_event_get_effecting_button_name(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_effecting_button_name(ue_PyFPointerEvent *self, PyObject * args) +{ FKey key = self->pointer.GetEffectingButton(); return PyUnicode_FromString(TCHAR_TO_UTF8(*key.ToString())); } -static PyObject *py_ue_fpointer_event_get_wheel_delta(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_wheel_delta(ue_PyFPointerEvent *self, PyObject * args) +{ float delta = self->pointer.GetWheelDelta(); return PyFloat_FromDouble(delta); } -static PyObject *py_ue_fpointer_event_get_cursor_delta(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_cursor_delta(ue_PyFPointerEvent *self, PyObject * args) +{ FVector2D delta = self->pointer.GetCursorDelta(); return Py_BuildValue((char *)"(ff)", delta.X, delta.Y); } -static PyObject *py_ue_fpointer_event_get_screen_space_position(ue_PyFPointerEvent *self, PyObject * args) { +static PyObject *py_ue_fpointer_event_get_screen_space_position(ue_PyFPointerEvent *self, PyObject * args) +{ FVector2D pos = self->pointer.GetScreenSpacePosition(); return Py_BuildValue((char *)"(ff)", pos.X, pos.Y); } @@ -73,9 +78,62 @@ static PyTypeObject ue_PyFPointerEventType = { ue_PyFPointerEvent_methods, /* tp_methods */ }; -void ue_python_init_fpointer_event(PyObject *ue_module) { +static int ue_py_fpointer_event_init(ue_PyFPointerEvent *self, PyObject *args, PyObject *kwargs) +{ + int pointer_index; + float ssp_x; + float ssp_y; + float lssp_x; + float lssp_y; + char *effecting; + float wheel_delta; + PyObject *py_modifier = nullptr; + + if (!PyArg_ParseTuple(args, "i(ff)(ff)s|fO", + &pointer_index, + &ssp_x, + &ssp_y, + &lssp_x, + &lssp_y, + &effecting, + &wheel_delta, + &py_modifier)) + { + return -1; + } + + TSet keys; + keys.Add(FKey(effecting)); + + FModifierKeysState modifier; + if (py_modifier) + { + ue_PyFModifierKeysState *f_modifier = py_ue_is_fmodifier_keys_state(py_modifier); + if (!f_modifier) + { + PyErr_SetString(PyExc_Exception, "argument is not a FModifierKeysState"); + return -1; + } + modifier = f_modifier->modifier; + } + + FPointerEvent Event(pointer_index, + FVector2D(ssp_x, ssp_y), + FVector2D(lssp_x, lssp_y), + keys, + FKey(effecting), wheel_delta, modifier); + + new(&self->pointer) FPointerEvent(Event); + new(&self->f_input.input) FInputEvent(Event); + + return 0; +} + +void ue_python_init_fpointer_event(PyObject *ue_module) +{ ue_PyFPointerEventType.tp_base = &ue_PyFInputEventType; + ue_PyFPointerEventType.tp_init = (initproc)ue_py_fpointer_event_init; if (PyType_Ready(&ue_PyFPointerEventType) < 0) return; @@ -84,9 +142,17 @@ void ue_python_init_fpointer_event(PyObject *ue_module) { PyModule_AddObject(ue_module, "FPointerEvent", (PyObject *)&ue_PyFPointerEventType); } -PyObject *py_ue_new_fpointer_event(FPointerEvent pointer) { +PyObject *py_ue_new_fpointer_event(FPointerEvent pointer) +{ ue_PyFPointerEvent *ret = (ue_PyFPointerEvent *)PyObject_New(ue_PyFPointerEvent, &ue_PyFPointerEventType); new(&ret->pointer) FPointerEvent(pointer); new(&ret->f_input.input) FInputEvent(pointer); return (PyObject *)ret; } + +ue_PyFPointerEvent *py_ue_is_fpointer_event(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFPointerEventType)) + return nullptr; + return (ue_PyFPointerEvent *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h index 0a6821a29..e66ad673f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFPointerEvent.h @@ -15,3 +15,4 @@ typedef struct { void ue_python_init_fpointer_event(PyObject *); PyObject *py_ue_new_fpointer_event(FPointerEvent); +ue_PyFPointerEvent *py_ue_is_fpointer_event(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp index bdd0662e2..c0618e126 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.cpp @@ -2,14 +2,21 @@ #include "UEPyFSlateIcon.h" +static PyObject *py_ue_fslate_icon_get_icon(ue_PyFSlateIcon *self, PyObject * args) +{ + PyObject *ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (uint8*)self->icon.GetIcon()); + return ret; +} + static PyMethodDef ue_PyFSlateIcon_methods[] = { + { "get_icon", (PyCFunction)py_ue_fslate_icon_get_icon, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; static PyObject *ue_PyFSlateIcon_str(ue_PyFSlateIcon *self) { return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->icon->GetStyleName().ToString())); + TCHAR_TO_UTF8(*self->icon.GetStyleName().ToString())); } static PyTypeObject ue_PyFSlateIconType = { @@ -55,10 +62,10 @@ static int ue_py_fslate_icon_init(ue_PyFSlateIcon *self, PyObject *args, PyObjec PyErr_SetString(PyExc_ValueError, "you have not specified as style name"); return -1; } - self->icon = new FSlateIcon(FName(style_set), FName(style)); + new(&self->icon) FSlateIcon(FName(style_set), FName(style)); } else { - self->icon = new FSlateIcon(); + new(&self->icon) FSlateIcon(); } return 0; } @@ -75,8 +82,15 @@ void ue_python_init_fslate_icon(PyObject *ue_module) { PyModule_AddObject(ue_module, "FSlateIcon", (PyObject *)&ue_PyFSlateIconType); } +ue_PyFSlateIcon *py_ue_new_fslate_icon(const FSlateIcon slate_icon) +{ + ue_PyFSlateIcon *ret = (ue_PyFSlateIcon *)PyObject_New(ue_PyFSlateIcon, &ue_PyFSlateIconType); + ret->icon = slate_icon; + return ret; +} + ue_PyFSlateIcon *py_ue_is_fslate_icon(PyObject *obj) { - if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFSlateIconType)) - return nullptr; - return (ue_PyFSlateIcon *)obj; + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFSlateIconType)) + return nullptr; + return (ue_PyFSlateIcon *)obj; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h index bfd24f9dd..b32b07af5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateIcon.h @@ -7,9 +7,10 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ - FSlateIcon *icon; + FSlateIcon icon; } ue_PyFSlateIcon; void ue_python_init_fslate_icon(PyObject *); +ue_PyFSlateIcon *py_ue_new_fslate_icon(const FSlateIcon slate_icon); ue_PyFSlateIcon *py_ue_is_fslate_icon(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp index 601a064e2..7f5e7e45e 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.cpp @@ -1,6 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" #include "UEPyFSlateStyleSet.h" +#include "SlateTypes.h" +#include "Map.h" static PyObject *py_ue_fslate_style_set_set_content_root(ue_PyFSlateStyleSet *self, PyObject * args) { @@ -87,9 +89,123 @@ static PyObject *py_ue_fslate_style_set_set(ue_PyFSlateStyleSet *self, PyObject return PyErr_Format(PyExc_ValueError, "unsupported value type"); } +namespace { + template + PyObject* pyGetWidgetStyle(FSlateStyleSet& InStyle, FName PropertyName) + { + const WidgetStyleType styleWidgetStyle = InStyle.GetWidgetStyle(PropertyName); + return py_ue_new_uscriptstruct(WidgetStyleType::StaticStruct(), (uint8*)&styleWidgetStyle); + } +} + +static PyObject *py_ue_fslate_style_set_get(ue_PyFSlateStyleSet *self, PyObject * args) +{ + if (!(self && self->style_set)) + return PyErr_Format(PyExc_Exception, "fstyleset is in invalid state"); + + char *name = nullptr; + PyObject *py_type = nullptr; + + if (!PyArg_ParseTuple(args, "sO:get", &name, &py_type)) + return NULL; + + PyObject *ret = nullptr; + + if (ue_py_check_struct(py_type)) + { + const FSlateSound& styleSound = self->style_set->GetSound(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateSound::StaticStruct(), (uint8*)&styleSound); + } + else if (ue_py_check_struct(py_type)) + { + if (const FSlateBrush* styleBrush = self->style_set->GetBrush(FName(name))) + { + ret = py_ue_new_uscriptstruct(FSlateBrush::StaticStruct(), (uint8*)styleBrush); + } + } + else if (ue_py_check_struct(py_type)) + { + const FSlateColor styleSlateColor = self->style_set->GetSlateColor(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateColor::StaticStruct(), (uint8*)&styleSlateColor); + } + else if (ue_py_check_struct(py_type)) + { + const FSlateFontInfo styleFontInfo = self->style_set->GetFontStyle(FName(name)); + ret = py_ue_new_uscriptstruct(FSlateFontInfo::StaticStruct(), (uint8*)&styleFontInfo); + } + else if (ue_py_check_childstruct(py_type)) + { + + typedef TFunction WStyleGetter; + typedef TPair WStylePair; +#if ENGINE_MINOR_VERSION > 15 + static const WStylePair validWidgetStyleUStructList[] = { + WStylePair{ FTextBlockStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FButtonStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FComboButtonStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FComboBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FHyperlinkStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FEditableTextStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBarStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FEditableTextBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FInlineEditableTextBlockStyle::StaticStruct(), WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle(InStyle, InName); }) }, + WStylePair{ FProgressBarStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FExpandableAreaStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSearchBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSliderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FVolumeControlStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FInlineTextImageStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSpinBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FSplitterStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FTableRowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FTableColumnHeaderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FHeaderRowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FDockTabStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBoxStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FScrollBorderStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + WStylePair{ FWindowStyle::StaticStruct() , WStyleGetter([](FSlateStyleSet& InStyle, FName InName) { return pyGetWidgetStyle (InStyle, InName); }) }, + }; + + + for(WStylePair widgetStyleUStruct : validWidgetStyleUStructList) + { + if (do_ue_py_check_struct(py_type, widgetStyleUStruct.Key)) + { + ret = widgetStyleUStruct.Value(*self->style_set, FName(name)); + break; + } + } +#endif + + if (!ret) + { + return PyErr_Format(PyExc_ValueError, "Unsupported FSlateWidgetStyle type. Add it manually."); + } + } + else if (py_ue_is_flinearcolor(py_type)) + { + ret = py_ue_new_flinearcolor(self->style_set->GetColor(FName(name))); + } + else if (PyNumber_Check(py_type)) + { + ret = PyFloat_FromDouble(self->style_set->GetFloat(FName(name))); + } + else + { + return PyErr_Format(PyExc_ValueError, "unsupported value type"); + } + + if (!ret) + return PyErr_Format(PyExc_Exception, "Retrieved style object is in invalid state"); + + return ret; + +} + static PyMethodDef ue_PyFSlateStyleSet_methods[] = { { "set_content_root", (PyCFunction)py_ue_fslate_style_set_set_content_root, METH_VARARGS, "" }, { "set", (PyCFunction)py_ue_fslate_style_set_set, METH_VARARGS, "" }, + { "get", (PyCFunction)py_ue_fslate_style_set_get, METH_VARARGS, "" }, { "register", (PyCFunction)py_ue_fslate_style_set_register, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -139,11 +255,18 @@ static int ue_py_fslate_style_set_init(ue_PyFSlateStyleSet *self, PyObject *args { return -1; } - + //TODO: roberto: ikrimae- I think this leaks? How is FSlateIcon destroyed ever? self->style_set = new FSlateStyleSet(name); return 0; } +ue_PyFSlateStyleSet* py_ue_new_fslate_style_set(FSlateStyleSet* styleSet) +{ + ue_PyFSlateStyleSet *ret = (ue_PyFSlateStyleSet *)PyObject_New(ue_PyFSlateStyleSet, &ue_PyFSlateStyleSetType); + ret->style_set = styleSet; + return ret; +} + void ue_python_init_fslate_style_set(PyObject *ue_module) { ue_PyFSlateStyleSetType.tp_new = PyType_GenericNew; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h index 7192ce4f7..58ffe57fd 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFSlateStyleSet.h @@ -2,12 +2,12 @@ #include "UnrealEnginePython.h" -#include "Runtime/SlateCore/Public/Styling/SlateStyle.h" - typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FSlateStyleSet *style_set; } ue_PyFSlateStyleSet; +ue_PyFSlateStyleSet* py_ue_new_fslate_style_set(FSlateStyleSet* styleSet); + void ue_python_init_fslate_style_set(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp index 684ea57c6..8a5fe212f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFToolBarBuilder.cpp @@ -4,7 +4,8 @@ #include "Runtime/Slate/Public/Framework/Commands/UIAction.h" -static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *name; if (!PyArg_ParseTuple(args, "s:begin_section", &name)) return NULL; @@ -15,14 +16,16 @@ static PyObject *py_ue_ftool_bar_builder_begin_section(ue_PyFToolBarBuilder *sel return Py_None; } -static PyObject *py_ue_ftool_bar_builder_end_section(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_end_section(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.EndSection(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *hook; char *label; char *tooltip; @@ -33,34 +36,37 @@ static PyObject *py_ue_ftool_bar_builder_add_tool_bar_button(ue_PyFToolBarBuilde return NULL; ue_PyFSlateIcon *py_slate_icon = py_ue_is_fslate_icon(py_icon); - if (!py_slate_icon) { + if (!py_slate_icon) + { return PyErr_Format(PyExc_Exception, "argument is not a FSlateIcon"); } - if (!PyCallable_Check(py_callable)) { + if (!PyCalllable_Check_Extended(py_callable)) + { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FExecuteAction handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); - if (py_obj) { + if (py_obj) + { Py_INCREF(py_obj); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::ExecuteAction, py_obj); + handler.BindSP(py_delegate, &FPythonSlateDelegate::ExecuteAction, py_obj); } - else { - handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction); + else + { + handler.BindSP(py_delegate, &FPythonSlateDelegate::SimpleExecuteAction); } - self->tool_bar_builder.AddToolBarButton(FUIAction(handler), FName(hook), FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), *py_slate_icon->icon); + self->tool_bar_builder.AddToolBarButton(FUIAction(handler), FName(hook), FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), py_slate_icon->icon); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *self, PyObject * args) +{ char *hook = nullptr; if (!PyArg_ParseTuple(args, "|s:add_separator", &hook)) @@ -78,25 +84,28 @@ static PyObject *py_ue_ftool_bar_builder_add_separator(ue_PyFToolBarBuilder *sel } -static PyObject *py_ue_ftool_bar_builder_begin_block_group(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_begin_block_group(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.BeginBlockGroup(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_end_block_group(ue_PyFToolBarBuilder *self, PyObject * args) { +static PyObject *py_ue_ftool_bar_builder_end_block_group(ue_PyFToolBarBuilder *self, PyObject * args) +{ self->tool_bar_builder.EndBlockGroup(); Py_INCREF(Py_None); return Py_None; } -static PyObject *py_ue_ftool_bar_builder_make_widget(ue_PyFToolBarBuilder *self, PyObject * args) { - ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType); +static PyObject *py_ue_ftool_bar_builder_make_widget(ue_PyFToolBarBuilder *self, PyObject * args) +{ + ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType); ue_py_setup_swidget(ret); - ret->s_widget = self->tool_bar_builder.MakeWidget(); - return (PyObject *)ret; + ret->s_widget = self->tool_bar_builder.MakeWidget(); + return (PyObject *)ret; } static PyMethodDef ue_PyFToolBarBuilder_methods[] = { @@ -106,7 +115,7 @@ static PyMethodDef ue_PyFToolBarBuilder_methods[] = { { "add_separator", (PyCFunction)py_ue_ftool_bar_builder_add_separator, METH_VARARGS, "" }, { "begin_block_group", (PyCFunction)py_ue_ftool_bar_builder_begin_block_group, METH_VARARGS, "" }, { "end_block_group", (PyCFunction)py_ue_ftool_bar_builder_end_block_group, METH_VARARGS, "" }, - { "make_widget", (PyCFunction)py_ue_ftool_bar_builder_make_widget, METH_VARARGS, "" }, + { "make_widget", (PyCFunction)py_ue_ftool_bar_builder_make_widget, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -117,7 +126,8 @@ static PyObject *ue_PyFToolBarBuilder_str(ue_PyFToolBarBuilder *self) &self->tool_bar_builder); } -static void ue_py_ftool_bar_builder_dealloc(ue_PyFToolBarBuilder *self) { +static void ue_py_ftool_bar_builder_dealloc(ue_PyFToolBarBuilder *self) +{ #if PY_MAJOR_VERSION < 3 self->ob_type->tp_free((PyObject*)self); #else @@ -156,13 +166,15 @@ static PyTypeObject ue_PyFToolBarBuilderType = { ue_PyFToolBarBuilder_methods, /* tp_methods */ }; -static int ue_py_ftool_bar_builder_init(ue_PyFToolBarBuilder *self, PyObject *args, PyObject *kwargs) { - self->tool_bar_builder = FToolBarBuilder(nullptr, FMultiBoxCustomization::None); +static int ue_py_ftool_bar_builder_init(ue_PyFToolBarBuilder *self, PyObject *args, PyObject *kwargs) +{ + new(&self->tool_bar_builder) FToolBarBuilder(TSharedPtr(), FMultiBoxCustomization::None); return 0; } -void ue_python_init_ftool_bar_builder(PyObject *ue_module) { +void ue_python_init_ftool_bar_builder(PyObject *ue_module) +{ ue_PyFToolBarBuilderType.tp_new = PyType_GenericNew; ue_PyFToolBarBuilderType.tp_init = (initproc)ue_py_ftool_bar_builder_init; @@ -174,7 +186,8 @@ void ue_python_init_ftool_bar_builder(PyObject *ue_module) { PyModule_AddObject(ue_module, "FToolBarBuilder", (PyObject *)&ue_PyFToolBarBuilderType); } -PyObject *py_ue_new_ftool_bar_builder(FToolBarBuilder tool_bar_builder) { +PyObject *py_ue_new_ftool_bar_builder(FToolBarBuilder tool_bar_builder) +{ ue_PyFToolBarBuilder *ret = (ue_PyFToolBarBuilder *)PyObject_New(ue_PyFToolBarBuilder, &ue_PyFToolBarBuilderType); new(&ret->tool_bar_builder) FToolBarBuilder(tool_bar_builder); return (PyObject *)ret; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp new file mode 100644 index 000000000..3fb5f2c1c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.cpp @@ -0,0 +1,96 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR + +#include "Editor/PropertyEditor/Public/IDetailsView.h" + +#include "UEPyIDetailsView.h" + + +#define sw_idetails_view StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + +static PyObject *py_ue_idetails_view_set_object(ue_PyIDetailsView *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_in_obj = nullptr; + PyObject *py_force_refresh = nullptr; + + char *kwlist[] = { + (char *)"uobject", + (char *)"force_refresh", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_object", kwlist, &py_in_obj, &py_force_refresh)) + { + return nullptr; + } + + UObject *u_object = ue_py_check_type(py_in_obj); + if (!u_object) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + + const bool bForceRefresh = py_force_refresh && PyObject_IsTrue(py_force_refresh); + + sw_idetails_view->SetObject(u_object, bForceRefresh); + + Py_RETURN_NONE; +} + +static PyMethodDef ue_PyIDetailsView_methods[] = { +#pragma warning(suppress: 4191) + { "set_object", (PyCFunction)py_ue_idetails_view_set_object, METH_VARARGS | METH_KEYWORDS, "" }, + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PyIDetailsViewType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.IDetailsView", /* tp_name */ + sizeof(ue_PyIDetailsView), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine IDetailsView", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyIDetailsView_methods, /* tp_methods */ +}; + +void ue_python_init_idetails_view(PyObject *ue_module) +{ + ue_PyIDetailsViewType.tp_base = &ue_PySCompoundWidgetType; + + if (PyType_Ready(&ue_PyIDetailsViewType) < 0) + return; + + Py_INCREF(&ue_PyIDetailsViewType); + PyModule_AddObject(ue_module, "IDetailsView", (PyObject *)&ue_PyIDetailsViewType); +} + +ue_PyIDetailsView *py_ue_is_idetails_view(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIDetailsViewType)) + return nullptr; + return (ue_PyIDetailsView *)obj; +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h new file mode 100644 index 000000000..b25ff89f8 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIDetailsView.h @@ -0,0 +1,19 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +#include "UEPySCompoundWidget.h" + +extern PyTypeObject ue_PySCompoundWidgetType; + +typedef struct { + ue_PySCompoundWidget s_compound_widget; + /* Type-specific fields go here. */ +} ue_PyIDetailsView; + +void ue_python_init_idetails_view(PyObject *); + +ue_PyIDetailsView * py_ue_is_idetails_view(PyObject *obj); + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp new file mode 100644 index 000000000..dea026a94 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.cpp @@ -0,0 +1,224 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR +#include "UEPyIStructureDetailsView.h" +#include "IStructureDetailsView.h" + + +//#define sw_idetails_view StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + + +static PyObject *ue_PyIStructureDetailsView_str(ue_PyIStructureDetailsView *self) +{ +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount(), + self->ob_base.ob_refcnt); +#else + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->istructure_details_view->GetWidget()->GetTypeAsString()), + self->istructure_details_view->GetWidget().Get(), + self->istructure_details_view->GetWidget().GetSharedReferenceCount()); +#endif +} + + +static PyObject *py_ue_istructure_details_view_set_structure_data(ue_PyIStructureDetailsView *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_object = nullptr; + PyObject *py_force_refresh = nullptr; + + char *kwlist[] = { + (char *)"struct_data", + (char *)"force_refresh", + nullptr + }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:set_structure_data", kwlist, &py_object, &py_force_refresh)) + { + return nullptr; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + + Py_XDECREF(self->ue_py_struct); + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view->SetStructureData(struct_scope); + +#if ENGINE_MINOR_VERSION > 17 + if (py_force_refresh && PyObject_IsTrue(py_force_refresh)) + { + self->istructure_details_view->GetDetailsView()->ForceRefresh(); + } +#endif + + Py_RETURN_NONE; +} + +static PyObject *py_ue_istructure_details_view_get_widget(ue_PyIStructureDetailsView *self, PyObject * args) +{ + if (!self->istructure_details_view.IsValid()) + { + return PyErr_Format(PyExc_Exception, "IStructureDetailsView is not valid"); + } + + TSharedPtr ret_widget = self->istructure_details_view->GetWidget(); + if (!ret_widget.IsValid()) + { + return PyErr_Format(PyExc_Exception, "unable to create SingleProperty widget"); + } + + return (PyObject *)py_ue_new_swidget(ret_widget->AsShared(), &ue_PySWidgetType); + + Py_RETURN_NONE; +} + +static PyMethodDef ue_PyIStructureDetailsView_methods[] = { +#pragma warning(suppress: 4191) + { "set_structure_data", (PyCFunction)py_ue_istructure_details_view_set_structure_data, METH_VARARGS | METH_KEYWORDS, "" }, + { "get_widget", (PyCFunction)py_ue_istructure_details_view_get_widget, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static void ue_PyIStructureDetailsView_dealloc(ue_PyIStructureDetailsView *self) +{ +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyIStructureDetailsView %p mapped to IStructureDetailsView %p"), self, self->istructure_details_view.Get()); +#endif + Py_DECREF(self->ue_py_struct); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +PyTypeObject ue_PyIStructureDetailsViewType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.IStructureDetailsView", /* tp_name */ + sizeof(ue_PyIStructureDetailsView), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ue_PyIStructureDetailsView_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyIStructureDetailsView_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine IStructureDetailsView", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyIStructureDetailsView_methods, /* tp_methods */ +}; + +static int ue_py_istructure_details_view_init(ue_PyIStructureDetailsView *self, PyObject *args, PyObject *kwargs) +{ + + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:__init__", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return -1; + } + + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object); + if (!ue_py_struct) + { + PyErr_SetString(PyExc_Exception, "argument is not a UScriptStruct"); + return -1; + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + new(&self->istructure_details_view) TSharedPtr(nullptr); + + self->ue_py_struct = ue_py_struct; + Py_INCREF(self->ue_py_struct); + TSharedPtr struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + self->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return 0; +} + +void ue_python_init_istructure_details_view(PyObject *ue_module) +{ + ue_PyIStructureDetailsViewType.tp_new = PyType_GenericNew; + + ue_PyIStructureDetailsViewType.tp_init = (initproc)ue_py_istructure_details_view_init; + + ue_PyIStructureDetailsViewType.tp_getattro = PyObject_GenericGetAttr; + ue_PyIStructureDetailsViewType.tp_setattro = PyObject_GenericSetAttr; + + if (PyType_Ready(&ue_PyIStructureDetailsViewType) < 0) + return; + + Py_INCREF(&ue_PyIStructureDetailsViewType); + PyModule_AddObject(ue_module, "IStructureDetailsView", (PyObject *)&ue_PyIStructureDetailsViewType); +} + +ue_PyIStructureDetailsView *py_ue_is_istructure_details_view(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyIStructureDetailsViewType)) + return nullptr; + return (ue_PyIStructureDetailsView *)obj; +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h new file mode 100644 index 000000000..54db231bf --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPyIStructureDetailsView.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + TSharedPtr istructure_details_view; + ue_PyUScriptStruct *ue_py_struct; +} ue_PyIStructureDetailsView; + +void ue_python_init_istructure_details_view(PyObject *); + +ue_PyIStructureDetailsView * py_ue_is_istructure_details_view(PyObject *obj); + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp index f1adb6dba..198307ed5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBorder.cpp @@ -10,10 +10,8 @@ static PyObject *py_ue_sborder_clear_content(ue_PySBorder *self, PyObject * args { sw_border->ClearContent(); - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject *py_ue_sborder_set_content(ue_PySBorder *self, PyObject * args) @@ -30,9 +28,9 @@ static PyObject *py_ue_sborder_set_content(ue_PySBorder *self, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); + Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_content = py_swidget; + sw_border->SetContent(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp index f9204e389..a64b6946d 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBox.cpp @@ -6,20 +6,21 @@ #define sw_box StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_sbox_set_content(ue_PySBox *self, PyObject * args) { +static PyObject *py_ue_sbox_set_content(ue_PySBox *self, PyObject * args) +{ PyObject *py_content; - if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) { + if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_panel.s_widget.py_swidget_content); Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_content = py_swidget; sw_box->SetContent(py_swidget->s_widget->AsShared()); @@ -63,7 +64,8 @@ PyTypeObject ue_PySBoxType = { ue_PySBox_methods, /* tp_methods */ }; -static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) { +static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) +{ ue_py_slate_setup_farguments(SBox); @@ -78,13 +80,14 @@ static int ue_py_sbox_init(ue_PySBox *self, PyObject *args, PyObject *kwargs) { ue_py_slate_farguments_optional_foptional_size("max_desired_width", MaxDesiredWidth); ue_py_slate_farguments_optional_foptional_size("min_desired_height", MinDesiredHeight); ue_py_slate_farguments_optional_foptional_size("min_desired_width", MinDesiredWidth); - + ue_py_snew(SBox, s_panel.s_widget); return 0; } -void ue_python_init_sbox(PyObject *ue_module) { +void ue_python_init_sbox(PyObject *ue_module) +{ ue_PySBoxType.tp_init = (initproc)ue_py_sbox_init; ue_PySBoxType.tp_call = (ternaryfunc)py_ue_sbox_set_content; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp index 59963667b..bd055e94f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySBoxPanel.cpp @@ -6,16 +6,12 @@ #define sw_box_panel StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_sbox_panel_clear_children(ue_PySGridPanel *self, PyObject * args) { +static PyObject *py_ue_sbox_panel_clear_children(ue_PySGridPanel *self, PyObject * args) +{ sw_box_panel->ClearChildren(); - for (ue_PySWidget *item : self->s_panel.s_widget.py_swidget_slots) { - Py_DECREF(item); - } - self->s_panel.s_widget.py_swidget_slots.Empty(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef ue_PySBoxPanel_methods[] = { @@ -54,7 +50,8 @@ PyTypeObject ue_PySBoxPanelType = { ue_PySBoxPanel_methods, /* tp_methods */ }; -void ue_python_init_sbox_panel(PyObject *ue_module) { +void ue_python_init_sbox_panel(PyObject *ue_module) +{ ue_PySBoxPanelType.tp_base = &ue_PySPanelType; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp index 7a3553935..f99bf5e9f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySButton.cpp @@ -27,17 +27,15 @@ static PyObject *py_ue_sbutton_bind_on_clicked(ue_PySButton *self, PyObject * ar return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FOnClicked handler; - UPythonSlateDelegate *py_delegate = NewObject(); + TSharedRefpy_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_border.s_compound_widget.s_widget.s_widget, py_callable); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnClicked); - self->s_border.s_compound_widget.s_widget.delegates.Add(py_delegate); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnClicked); sw_button->SetOnClicked(handler); @@ -106,6 +104,7 @@ static int ue_py_sbutton_init(ue_PySButton *self, PyObject *args, PyObject *kwar ue_py_slate_farguments_text("text", Text); ue_py_slate_farguments_optional_enum("text_flow_direction", TextFlowDirection, ETextFlowDirection); ue_py_slate_farguments_optional_enum("text_shaping_method", TextShapingMethod, ETextShapingMethod); + ue_py_slate_farguments_optional_struct_ptr("button_style", ButtonStyle, FButtonStyle); ue_py_slate_farguments_optional_struct_ptr("text_style", TextStyle, FTextBlockStyle); ue_py_slate_farguments_optional_enum("touch_method", TouchMethod, EButtonTouchMethod::Type); ue_py_slate_farguments_optional_enum("v_align", VAlign, EVerticalAlignment); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp index fd7734336..056de71d7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySCanvas.cpp @@ -6,18 +6,15 @@ #define sw_canvas StaticCastSharedRef(self->s_panel.s_widget.s_widget) -static PyObject *py_ue_scanvas_clear_children(ue_PySCanvas *self, PyObject * args) { - +static PyObject *py_ue_scanvas_clear_children(ue_PySCanvas *self, PyObject * args) +{ sw_canvas->ClearChildren(); - for (ue_PySWidget *item : self->s_panel.s_widget.py_swidget_slots) { - Py_DECREF(item); - } - self->s_panel.s_widget.py_swidget_slots.Empty(); Py_RETURN_NONE; } -static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyObject *kwargs) { +static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyObject *kwargs) +{ PyObject *py_content; int h_align = 0; int v_align = 0; @@ -37,28 +34,32 @@ static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyO &h_align, &v_align, &position, - &size)) { + &size)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SCanvas::FSlot &fslot = sw_canvas->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); fslot.HAlign((EHorizontalAlignment)h_align); fslot.VAlign((EVerticalAlignment)v_align); - if (position && PyTuple_Check(position)) { - if (PyTuple_Size(position) == 2) { + if (position && PyTuple_Check(position)) + { + if (PyTuple_Size(position) == 2) + { PyObject *py_x = PyTuple_GetItem(position, 0); PyObject *py_y = PyTuple_GetItem(position, 1); - if (PyNumber_Check(py_x)) { + if (PyNumber_Check(py_x)) + { PyObject *py_x_float = PyNumber_Float(py_x); float x = PyFloat_AsDouble(py_x_float); Py_DECREF(py_x_float); @@ -70,11 +71,14 @@ static PyObject *py_ue_scanvas_add_slot(ue_PySCanvas *self, PyObject * args, PyO } } - if (size && PyTuple_Check(size)) { - if (PyTuple_Size(size) == 2) { + if (size && PyTuple_Check(size)) + { + if (PyTuple_Size(size) == 2) + { PyObject *py_x = PyTuple_GetItem(size, 0); PyObject *py_y = PyTuple_GetItem(size, 1); - if (PyNumber_Check(py_x)) { + if (PyNumber_Check(py_x)) + { PyObject *py_x_float = PyNumber_Float(py_x); float x = PyFloat_AsDouble(py_x_float); Py_DECREF(py_x_float); @@ -128,12 +132,14 @@ PyTypeObject ue_PySCanvasType = { ue_PySCanvas_methods, /* tp_methods */ }; -static int ue_py_scanvas_init(ue_PySCanvas *self, PyObject *args, PyObject *kwargs) { +static int ue_py_scanvas_init(ue_PySCanvas *self, PyObject *args, PyObject *kwargs) +{ ue_py_snew_simple(SCanvas, s_panel.s_widget); return 0; } -void ue_python_init_scanvas(PyObject *ue_module) { +void ue_python_init_scanvas(PyObject *ue_module) +{ ue_PySCanvasType.tp_base = &ue_PySPanelType; ue_PySCanvasType.tp_call = (ternaryfunc)py_ue_scanvas_add_slot; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp index 9683be4ec..66c131ab0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.cpp @@ -1,9 +1,8 @@ -#if WITH_EDITOR -#if ENGINE_MINOR_VERSION > 13 #include "UnrealEnginePythonPrivatePCH.h" - #include "UEPySFilePathPicker.h" +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 13 #define sw_file_path_picker StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h index 2e649051a..b87bd4ac5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySFilePathPicker.h @@ -2,9 +2,9 @@ #include "UnrealEnginePython.h" - #include "UEPySCompoundWidget.h" +#if WITH_EDITOR #if ENGINE_MINOR_VERSION > 13 #include "Developer/DesktopWidgets/Public/Widgets/Input/SFilePathPicker.h" @@ -17,3 +17,4 @@ typedef struct { void ue_python_init_sfile_path_picker(PyObject *); #endif +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h index 00af7a55c..34e189067 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphEditor.h @@ -2,7 +2,7 @@ #include "UnrealEnginePython.h" - +#if WITH_EDITOR #include "UEPySCompoundWidget.h" #include "Editor/UnrealEd/Public/GraphEditor.h" @@ -15,3 +15,4 @@ typedef struct { } ue_PySGraphEditor; void ue_python_init_sgraph_editor(PyObject *); +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp new file mode 100644 index 000000000..03a611652 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.cpp @@ -0,0 +1,139 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 15 +#include "UEPySGraphPanel.h" + +#define sw_graph_panel StaticCastSharedRef(self->s_nodePanel.s_panel.s_widget) + +/* +static PyObject *py_ue_sgraph_panel_add_slot(ue_PySGraphPanel* self, PyObject *args, PyObject *kwargs) +{ + PyObject *py_content; + int z_order = -1; + int h_align = 0; + PyObject *padding = nullptr; + int v_align = 0; + + char *kwlist[] = { (char *)"widget", + (char *)"z_order", + (char *)"h_align", + (char *)"padding", + (char *)"v_align", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iiOi:add_slot", kwlist, + &py_content, + &z_order, + &h_align, + &padding, + &v_align)) + { + return nullptr; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + self->s_nodePanel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); + + sw_graph_panel; + + SOverlay::FOverlaySlot &fslot = sw_graph_panel->//sw_overlay->AddSlot(z_order); + fslot.AttachWidget(py_swidget->s_widget->AsShared()); + fslot.HAlign((EHorizontalAlignment)h_align); + if (padding) + { + if (PyTuple_Check(padding)) + { + FMargin margin; + if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) + { + return PyErr_Format(PyExc_Exception, "invalid padding value"); + } + fslot.Padding(margin); + } + else if (PyNumber_Check(padding)) + { + PyObject *py_float = PyNumber_Float(padding); + fslot.Padding(PyFloat_AsDouble(py_float)); + Py_DECREF(py_float); + } + else + { + return PyErr_Format(PyExc_Exception, "invalid padding value"); + } + } + fslot.VAlign((EVerticalAlignment)v_align); + + Py_INCREF(self); + return (PyObject *)self; +} +*/ + +static PyMethodDef ue_PySGraphPanel_methods[] = { + //{"add_slot", (PyCFunction)py_ue_sgraph_panel_add_slot, METH_VARARGS | METH_KEYWORDS, "" }, + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PySGraphPanelType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SGraphPanel", /* tp_name */ + sizeof(ue_PySGraphPanel), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SGraphPanel", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySGraphPanel_methods, /* tp_methods */ +}; + +//why do you need to return an integer? +static int ue_py_sgraph_panel_init(ue_PySGraphPanel *self, PyObject *args, PyObject *kwargs) +{ + //so for right now, let's just have this commented out to see if we get any errors + //if says we don't have s_nodePanel + ue_py_snew_simple(SGraphPanel, s_nodePanel.s_panel.s_widget);//s_nodePanel.s_panel); + //ue_py_snew(SGraphPanel, s_nodePanel.s_panel.s_widget); + + return 0; +} + +void ue_python_init_sgraph_panel(PyObject *ue_module) +{ + //ue_PySGraphPanelType.tp_init = (initproc)ue_py_sgraph_panel_init; + //ue_PySGraphPanelType.tp_call = (ternaryfunc)py_ue_sgraph_panel_add_slot; + ue_PySGraphPanelType.tp_base = &ue_PySNodePanelType; + + if (PyType_Ready(&ue_PySGraphPanelType) < 0) + return; + + Py_INCREF(&ue_PySGraphPanelType); + PyModule_AddObject(ue_module, "SGraphPanel", (PyObject *)&ue_PySGraphPanelType); +} + +#endif +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h new file mode 100644 index 000000000..abf11ec9b --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySGraphPanel.h @@ -0,0 +1,21 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 15 +#include "UEPySNodePanel.h" + +#include "Editor/GraphEditor/Public/SGraphPanel.h" + +extern PyTypeObject ue_PySGraphPanelType; + +typedef struct { + ue_PySNodePanel s_nodePanel; + /* Type-specific fields go here. */ +} ue_PySGraphPanel; + + +void ue_python_init_sgraph_panel(PyObject *); +#endif +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp index 760294214..d3b01bfd7 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.cpp @@ -4,49 +4,62 @@ #define sw_header_row StaticCastSharedRef(self->s_border.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *args, PyObject *kwargs) { - - char *column_id; - float fixed_width = 0; - int cell_h_align = 0; - int cell_v_align = 0; - char *default_label = nullptr; - char *default_tooltip = nullptr; - - char *kwlist[] = { - (char *)"column_id", - (char *)"fixed_width", - (char *)"cell_h_align", - (char *)"cell_v_align", - (char *)"default_label", - (char *)"default_tooltip", - nullptr - }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sf|iiss:add_column", kwlist, - &column_id, - &fixed_width, - &cell_h_align, - &cell_v_align, - &default_label, - &default_tooltip)) { - return NULL; - } - - if (!default_label) - default_label = column_id; - - if (!default_tooltip) - default_tooltip = default_label; - - SHeaderRow::FColumn column = SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) - .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) - .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) - .FixedWidth(fixed_width) - .HAlignCell((EHorizontalAlignment)cell_h_align) - .VAlignCell((EVerticalAlignment)cell_v_align); - - sw_header_row->AddColumn(column); +static PyObject *py_ue_sheader_row_add_column(ue_PySHeaderRow *self, PyObject *args, PyObject *kwargs) +{ + int32 retCode = [&]() { + ue_py_slate_setup_farguments(SHeaderRow::FColumn); + + // first of all check for values + PyObject *py_columnid = ue_py_dict_get_item(kwargs, "column_id"); + if (!py_columnid) { + PyErr_SetString(PyExc_TypeError, "you must specify the column_id"); + return -1; + } + + ue_py_slate_farguments_optional_string("column_id", ColumnId); + + ue_py_slate_farguments_text("default_label", DefaultLabel); + ue_py_slate_farguments_text("default_tooltip", DefaultTooltip); + + ue_py_slate_farguments_optional_enum("h_align_header", HAlignHeader, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align_header", VAlignHeader, EVerticalAlignment); + + ue_py_slate_farguments_float("fill_width", FillWidth); + ue_py_slate_farguments_optional_float("fixed_width", FixedWidth); + ue_py_slate_farguments_float("manual_width", ManualWidth); + + ue_py_slate_farguments_optional_named_slot("header_content", HeaderContent); + ue_py_slate_farguments_optional_struct("header_content_padding", HeaderContentPadding, FMargin); + + ue_py_slate_farguments_optional_named_slot("menu_content", MenuContent); + + ue_py_slate_farguments_optional_enum("h_align_cell", HAlignCell, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align_cell", VAlignCell, EVerticalAlignment); + + ue_py_slate_farguments_enum("sort_mode", SortMode, EColumnSortMode::Type); + ue_py_slate_farguments_enum("sort_priority", SortPriority, EColumnSortPriority::Type); + + ue_py_slate_farguments_event("on_sort", OnSort, FOnSortModeChanged, OnSort); + + ue_py_slate_farguments_bool("should_generate_widget", ShouldGenerateWidget); + + //sw_header_row->AddColumn( + // SHeaderRow::Column(FName(UTF8_TO_TCHAR(column_id))) + // .DefaultLabel(FText::FromString(UTF8_TO_TCHAR(default_label))) + // .DefaultTooltip(FText::FromString(UTF8_TO_TCHAR(default_tooltip))) + // .FixedWidth(fixed_width) + // .HAlignCell((EHorizontalAlignment)cell_h_align) + // .VAlignCell((EVerticalAlignment)cell_v_align) + //); + + sw_header_row->AddColumn(arguments); + return 0; + }(); + + if (retCode != 0) + { + return PyErr_Format(PyExc_Exception, "could not add column slot"); + } Py_INCREF(self); return (PyObject *)self; @@ -97,6 +110,7 @@ static int ue_py_sheader_row_init(ue_PySHeaderRow *self, PyObject *args, PyObjec void ue_python_init_sheader_row(PyObject *ue_module) { ue_PySHeaderRowType.tp_init = (initproc)ue_py_sheader_row_init; + ue_PySHeaderRowType.tp_call = (ternaryfunc)py_ue_sheader_row_add_column; ue_PySHeaderRowType.tp_base = &ue_PySBorderType; @@ -107,3 +121,10 @@ void ue_python_init_sheader_row(PyObject *ue_module) { PyModule_AddObject(ue_module, "SHeaderRow", (PyObject *)&ue_PySHeaderRowType); } +ue_PySHeaderRow * py_ue_is_sheader_row(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySHeaderRowType)) + return nullptr; + return (ue_PySHeaderRow *)obj; +} + diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h index 0c755ab0d..0a3129456 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHeaderRow.h @@ -14,3 +14,5 @@ typedef struct { } ue_PySHeaderRow; void ue_python_init_sheader_row(PyObject *); + +ue_PySHeaderRow *py_ue_is_sheader_row(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp index ea9e138f2..56a42c4a0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySHorizontalBox.cpp @@ -9,76 +9,48 @@ static PyObject *py_ue_shorizontal_box_add_slot(ue_PySHorizontalBox *self, PyObject * args, PyObject *kwargs) { - PyObject *py_content; - int h_align = 0; - float max_width = 0; - PyObject *padding = nullptr; - int v_align = 0; - float fill_width = 0; - PyObject *py_auto_width = nullptr; - - char *kwlist[] = { (char *)"widget", - (char *)"h_align", - (char *)"max_width", - (char *)"padding", - (char *)"v_align", - (char *)"fill_width", - (char *)"auto_width", - nullptr }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ifOifO:add_slot", kwlist, - &py_content, - &h_align, - &max_width, - &padding, - &v_align, - &fill_width, - &py_auto_width)) - { - return NULL; - } - - ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) - { - return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - } - - Py_INCREF(py_swidget); - self->s_box_panel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); - - SHorizontalBox::FSlot &fslot = sw_horizontal_box->AddSlot(); - fslot.AttachWidget(py_swidget->s_widget->AsShared()); - fslot.HAlign((EHorizontalAlignment)h_align); - if (max_width != 0) - fslot.MaxWidth(max_width); - if (fill_width) - fslot.FillWidth(fill_width); - if (padding) - { - if (PyTuple_Check(padding)) - { - FMargin margin; - if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) - { - return PyErr_Format(PyExc_Exception, "invalid padding value"); - } - fslot.Padding(margin); - } - else if (PyNumber_Check(padding)) - { - PyObject *py_float = PyNumber_Float(padding); - fslot.Padding(PyFloat_AsDouble(py_float)); - Py_DECREF(py_float); - } - else - { - return PyErr_Format(PyExc_Exception, "invalid padding value"); - } - } - fslot.VAlign((EVerticalAlignment)v_align); - if (py_auto_width && PyObject_IsTrue(py_auto_width)) - fslot.AutoWidth(); + int32 retCode = [&]() { + ue_py_slate_setup_hack_slot_args(SHorizontalBox, sw_horizontal_box); + ue_py_slate_farguments_float("fill_width", FillWidth); + ue_py_slate_farguments_float("max_width", MaxWidth); + ue_py_slate_farguments_optional_enum("h_align", HAlign, EHorizontalAlignment); + ue_py_slate_farguments_optional_enum("v_align", VAlign, EVerticalAlignment); + + //NOTE: Padding slot in slate is weird and manually supports different parameter constructions + if (PyObject *padding = ue_py_dict_get_item(kwargs, "padding")) + { + if (PyTuple_Check(padding)) + { + FMargin margin; + if (!PyArg_ParseTuple(padding, "f|fff", &margin.Left, &margin.Top, &margin.Right, &margin.Bottom)) + { + PyErr_SetString(PyExc_TypeError, "invalid padding value"); + return -1; + } + arguments.Padding(margin); + } + else if (PyNumber_Check(padding)) + { + PyObject *py_float = PyNumber_Float(padding); + arguments.Padding(PyFloat_AsDouble(py_float)); + Py_DECREF(py_float); + } + else + { + ue_py_slate_farguments_struct("padding", Padding, FMargin); + } + } + PyObject *py_auto_width = ue_py_dict_get_item(kwargs, "auto_width"); + if (py_auto_width && PyObject_IsTrue(py_auto_width)) + { arguments.AutoWidth(); } + + return 0; + }(); + + if (retCode != 0) + { + return PyErr_Format(PyExc_Exception, "could not add horizontal slot"); + } Py_INCREF(self); return (PyObject *)self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp index 96ec555cb..981cacce6 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySLevelViewport.cpp @@ -8,18 +8,16 @@ #define sw_level_viewport StaticCastSharedRef(self->s_editor_viewport.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_slevel_viewport_get_world(ue_PySLevelViewport *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(sw_level_viewport->GetWorld()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; +static PyObject *py_ue_slevel_viewport_get_world(ue_PySLevelViewport *self, PyObject * args) +{ + Py_RETURN_UOBJECT(sw_level_viewport->GetWorld()); } -static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) + { return NULL; } @@ -29,9 +27,11 @@ static PyObject *py_ue_slevel_viewport_set_show_bounds(ue_PySLevelViewport *self return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) + { return NULL; } @@ -41,9 +41,11 @@ static PyObject *py_ue_slevel_viewport_set_show_stats(ue_PySLevelViewport *self, return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) + { return NULL; } @@ -53,14 +55,17 @@ static PyObject *py_ue_slevel_viewport_set_view_mode(ue_PySLevelViewport *self, return (PyObject *)self; } -static PyObject *py_ue_slevel_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_slevel_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_settings; - if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) { + if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) + { return NULL; } FExposureSettings *settings = ue_py_check_struct(py_settings); - if (!settings) { + if (!settings) + { return PyErr_Format(PyExc_Exception, "argument is not a FExposureSettings"); } @@ -120,7 +125,8 @@ PyTypeObject ue_PySLevelViewportType = { ue_PySLevelViewport_methods, /* tp_methods */ }; -static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, PyObject *kwargs) { +static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, PyObject *kwargs) +{ FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); @@ -130,7 +136,7 @@ static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, ue_py_slate_farguments_optional_bool("realtime", Realtime); ue_py_slate_farguments_optional_enum("viewport_type", ViewportType, ELevelViewportType); - + ue_py_snew(SLevelViewport, s_editor_viewport.s_compound_widget.s_widget); EditorModule.GetFirstLevelEditor()->AddStandaloneLevelViewport(sw_level_viewport); @@ -138,7 +144,8 @@ static int ue_py_slevel_viewport_init(ue_PySLevelViewport *self, PyObject *args, return 0; } -void ue_python_init_slevel_viewport(PyObject *ue_module) { +void ue_python_init_slevel_viewport(PyObject *ue_module) +{ ue_PySLevelViewportType.tp_init = (initproc)ue_py_slevel_viewport_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp index 463b8a088..898ecd580 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySListView.cpp @@ -3,6 +3,8 @@ #include "UEPySListView.h" +#define sw_slist_view StaticCastSharedRef>>(self->s_table_view_base.s_compound_widget.s_widget.s_widget) + static PyMethodDef ue_PySListView_methods[] = { { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp new file mode 100644 index 000000000..d239341f4 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.cpp @@ -0,0 +1,57 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#if WITH_EDITOR +#include "UEPySNodePanel.h" + +#define sw_node_panel StaticCastSharedRef(self->s_panel.s_widget) + +static PyMethodDef ue_PySNodePanel_methods[] = { + { NULL } /* Sentinel */ +}; + + +PyTypeObject ue_PySNodePanelType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SNodePanel", /* tp_name */ + sizeof(ue_PySNodePanel), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SNode Panel", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySNodePanel_methods, /* tp_methods */ +}; + +void ue_python_init_snode_panel(PyObject *ue_module) +{ + + ue_PySNodePanelType.tp_base = &ue_PySPanelType; + + if (PyType_Ready(&ue_PySNodePanelType) < 0) + return; + + Py_INCREF(&ue_PySNodePanelType); + PyModule_AddObject(ue_module, "SNodePanel", (PyObject *)&ue_PySNodePanelType); +} + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h new file mode 100644 index 000000000..cb33072b4 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNodePanel.h @@ -0,0 +1,19 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#if WITH_EDITOR +#include "UEPySPanel.h" + +#include "Editor/GraphEditor/Public/SNodePanel.h" + +extern PyTypeObject ue_PySNodePanelType; + +typedef struct { + ue_PySPanel s_panel; + /* Type-specific fields go here. */ +} ue_PySNodePanel; + + +void ue_python_init_snode_panel(PyObject *); +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp index 716faec1c..a073b7b98 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySNumericEntryBox.cpp @@ -4,7 +4,7 @@ #include "UEPySNumericEntryBox.h" -#define sw_numeric_entry_box StaticCastSharedRef>(self->s_compound_widget.s_widget.s_widget) +#define sw_float_numeric_entry_box StaticCastSharedRef>(self->s_compound_widget.s_widget.s_widget) static PyMethodDef ue_PySNumericEntryBox_methods[] = { @@ -44,31 +44,63 @@ PyTypeObject ue_PySNumericEntryBoxType = { static int ue_py_snumeric_entry_box_init(ue_PySNumericEntryBox *self, PyObject *args, PyObject *kwargs) { - ue_py_slate_setup_farguments(SNumericEntryBox); - - ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); - ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); - ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); - ue_py_slate_farguments_float("delta", Delta); - ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); - ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); - ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); - ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); - ue_py_slate_farguments_tfloat("max_slider_value", MaxSliderValue); - ue_py_slate_farguments_tfloat("max_value", MaxValue); - ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); - ue_py_slate_farguments_tfloat("min_slider_value", MinSliderValue); - ue_py_slate_farguments_tfloat("min_value", MinValue); - ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); - ue_py_slate_farguments_float("slider_exponent", SliderExponent); - ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); - ue_py_slate_farguments_tfloat("value", Value); - ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); - ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnFloatValueChanged, OnFloatChanged); - ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnFloatValueChanged, OnFloatChanged); - ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnFloatValueCommitted, OnFloatCommitted); + PyObject *py_numeric_type = ue_py_dict_get_item(kwargs, "numeric_type"); + if (py_numeric_type && PyLong_Check(py_numeric_type)) + { + ue_py_slate_setup_farguments(SNumericEntryBox); + + ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); + ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); + ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); + ue_py_slate_farguments_int("delta", Delta); + ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); + ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); + ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); + ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); + ue_py_slate_farguments_tint("max_slider_value", MaxSliderValue); + ue_py_slate_farguments_tint("max_value", MaxValue); + ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); + ue_py_slate_farguments_tint("min_slider_value", MinSliderValue); + ue_py_slate_farguments_tint("min_value", MinValue); + ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); + ue_py_slate_farguments_float("slider_exponent", SliderExponent); + ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); + ue_py_slate_farguments_tint("value", Value); + ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); + ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnInt32ValueChanged, OnInt32Changed); + ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnInt32ValueChanged, OnInt32Changed); + ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnInt32ValueCommitted, OnInt32Committed); + + ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + } + else + { + ue_py_slate_setup_farguments(SNumericEntryBox); + + ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); + ue_py_slate_farguments_optional_struct("border_background_color", BorderBackgroundColor, FSlateColor); + ue_py_slate_farguments_optional_struct("border_foreground_color", BorderForegroundColor, FSlateColor); + ue_py_slate_farguments_float("delta", Delta); + ue_py_slate_farguments_optional_struct_ptr("editable_text_box_style", EditableTextBoxStyle, FEditableTextBoxStyle); + ue_py_slate_farguments_struct("font", Font, FSlateFontInfo); + ue_py_slate_farguments_optional_struct("label_padding", LabelPadding, FMargin); + ue_py_slate_farguments_optional_enum("label_v_align", LabelVAlign, EVerticalAlignment); + ue_py_slate_farguments_tfloat("max_slider_value", MaxSliderValue); + ue_py_slate_farguments_tfloat("max_value", MaxValue); + ue_py_slate_farguments_float("min_desired_value_width", MinDesiredValueWidth); + ue_py_slate_farguments_tfloat("min_slider_value", MinSliderValue); + ue_py_slate_farguments_tfloat("min_value", MinValue); + ue_py_slate_farguments_struct("override_text_margin", OverrideTextMargin, FMargin); + ue_py_slate_farguments_float("slider_exponent", SliderExponent); + ue_py_slate_farguments_optional_text("undetermined_string", UndeterminedString); + ue_py_slate_farguments_tfloat("value", Value); + ue_py_slate_farguments_event("on_begin_slider_movement", OnBeginSliderMovement, FSimpleDelegate, SimpleExecuteAction); + ue_py_slate_farguments_event("on_end_slider_movement", OnEndSliderMovement, FOnFloatValueChanged, OnFloatChanged); + ue_py_slate_farguments_event("on_value_changed", OnValueChanged, FOnFloatValueChanged, OnFloatChanged); + ue_py_slate_farguments_event("on_value_committed", OnValueCommitted, FOnFloatValueCommitted, OnFloatCommitted); - ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + ue_py_snew(SNumericEntryBox, s_compound_widget.s_widget); + } return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp index 3380a0363..fadef42f4 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySOverlay.cpp @@ -38,7 +38,6 @@ static PyObject *py_ue_soverlay_add_slot(ue_PySOverlay *self, PyObject * args, P } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SOverlay::FOverlaySlot &fslot = sw_overlay->AddSlot(z_order); fslot.AttachWidget(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp index dca4c64ca..83dedef20 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonComboBox.cpp @@ -99,8 +99,6 @@ static int ue_py_spython_combo_box_init(ue_PySPythonComboBox *self, PyObject *ar TArray> *items = new TArray>(); while (PyObject *item = PyIter_Next(values)) { Py_INCREF(item); - // keep track of items - self->s_panel.s_widget.py_refs.Add(item); items->Add(TSharedPtr(new FPythonItem(item))); } Py_DECREF(values); @@ -143,9 +141,6 @@ static int ue_py_spython_combo_box_init(ue_PySPythonComboBox *self, PyObject *ar // keep track of the list, so we can delete on destruction sw_python_combo_box->PythonOptionsSource = items; - // eventually track the content - self->s_panel.s_widget.py_swidget_content = s_widget_content; - return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp index 4123ca159..7874cf9d5 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonEditorViewport.cpp @@ -7,18 +7,16 @@ #define sw_python_editor_viewport StaticCastSharedRef(self->s_editor_viewport.s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_spython_editor_viewport_get_world(ue_PySPythonEditorViewport *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(sw_python_editor_viewport->GetPythonWorld()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; +static PyObject *py_ue_spython_editor_viewport_get_world(ue_PySPythonEditorViewport *self, PyObject * args) +{ + Py_RETURN_UOBJECT(sw_python_editor_viewport->GetPythonWorld()); } -static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_bounds", &py_bool)) + { return NULL; } @@ -29,9 +27,11 @@ static PyObject *py_ue_spython_editor_viewport_set_show_bounds(ue_PySPythonEdito return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:set_show_stats", &py_bool)) + { return NULL; } @@ -42,9 +42,11 @@ static PyObject *py_ue_spython_editor_viewport_set_show_stats(ue_PySPythonEditor return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorViewport *self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:set_view_mode", &mode)) + { return NULL; } @@ -55,14 +57,17 @@ static PyObject *py_ue_spython_editor_viewport_set_view_mode(ue_PySPythonEditorV return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_settings; - if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) { + if (!PyArg_ParseTuple(args, "O:set_exposure_settings", &py_settings)) + { return NULL; } FExposureSettings *settings = ue_py_check_struct(py_settings); - if (!settings) { + if (!settings) + { return PyErr_Format(PyExc_Exception, "argument is not a FExposureSettings"); } @@ -73,20 +78,25 @@ static PyObject *py_ue_spython_editor_viewport_set_exposure_settings(ue_PySPytho return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEditorViewport *self, PyObject * args) +{ float x = 0, y = 0, z = 0; FVector vec; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFVector *ue_py_vec = py_ue_is_fvector(PyTuple_GetItem(args, 0)); - if (!ue_py_vec) { + if (!ue_py_vec) + { return PyErr_Format(PyExc_Exception, "argument is not a FVector"); } vec = ue_py_vec->vec; } - else { - if (!PyArg_ParseTuple(args, "fff", &x, &y, &z)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &x, &y, &z)) + { return nullptr;; } vec.X = x; @@ -100,20 +110,25 @@ static PyObject *py_ue_spython_editor_viewport_set_view_location(ue_PySPythonEdi return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEditorViewport *self, PyObject * args) +{ float roll = 0, pitch = 0, yaw = 0; FRotator rot; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(PyTuple_GetItem(args, 0)); - if (!ue_py_rot) { + if (!ue_py_rot) + { return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); } rot = ue_py_rot->rot; } - else { - if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + { return nullptr;; } rot.Roll = roll; @@ -127,9 +142,11 @@ static PyObject *py_ue_spython_editor_viewport_set_view_rotation(ue_PySPythonEdi return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_bool; - if (!PyArg_ParseTuple(args, "O:simulate", &py_bool)) { + if (!PyArg_ParseTuple(args, "O:simulate", &py_bool)) + { return NULL; } @@ -138,7 +155,8 @@ static PyObject *py_ue_spython_editor_viewport_simulate(ue_PySPythonEditorViewpo Py_RETURN_NONE; } -static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEditorViewport *self, PyObject * args) +{ PyObject *py_obj; if (!PyArg_ParseTuple(args, "O", &py_obj)) @@ -154,20 +172,25 @@ static PyObject *py_ue_spython_editor_viewport_set_light_color(ue_PySPythonEdito return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonEditorViewport *self, PyObject * args) +{ float roll = 0, pitch = 0, yaw = 0; FRotator rot; - if (PyTuple_Size(args) == 1) { + if (PyTuple_Size(args) == 1) + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(PyTuple_GetItem(args, 0)); - if (!ue_py_rot) { + if (!ue_py_rot) + { return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); } rot = ue_py_rot->rot; } - else { - if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) { + else + { + if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + { return nullptr;; } rot.Roll = roll; @@ -181,7 +204,8 @@ static PyObject *py_ue_spython_editor_viewport_set_light_direction(ue_PySPythonE return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEditorViewport *self, PyObject * args) +{ float brightness; if (!PyArg_ParseTuple(args, "f", &brightness)) @@ -193,7 +217,8 @@ static PyObject *py_ue_spython_editor_viewport_set_sky_brightness(ue_PySPythonEd return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPythonEditorViewport *self, PyObject * args) +{ float brightness; if (!PyArg_ParseTuple(args, "f", &brightness)) @@ -205,16 +230,12 @@ static PyObject *py_ue_spython_editor_viewport_set_light_brightness(ue_PySPython return (PyObject *)self; } -static PyObject *py_ue_spython_editor_viewport_get_light(ue_PySPythonEditorViewport *self, PyObject * args) { +static PyObject *py_ue_spython_editor_viewport_get_light(ue_PySPythonEditorViewport *self, PyObject * args) +{ UDirectionalLightComponent *light = sw_python_editor_viewport->GetPreviewScene()->DirectionalLight; - ue_PyUObject *ret = ue_get_python_wrapper(light); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(light); } static PyMethodDef ue_PySPythonEditorViewport_methods[] = { @@ -234,10 +255,12 @@ static PyMethodDef ue_PySPythonEditorViewport_methods[] = { { NULL } /* Sentinel */ }; -class FPythonEditorViewportClient : public FEditorViewportClient { +class FPythonEditorViewportClient : public FEditorViewportClient +{ public: FPythonEditorViewportClient(FEditorModeTools * InModeTools, FPreviewScene * InPreviewScene, const TWeakPtr & InEditorViewportWidget) : - FEditorViewportClient(InModeTools, InPreviewScene, InEditorViewportWidget) { + FEditorViewportClient(InModeTools, InPreviewScene, InEditorViewportWidget) + { EngineShowFlags.SetSelection(true); EngineShowFlags.SetSelectionOutline(true); @@ -247,14 +270,17 @@ class FPythonEditorViewportClient : public FEditorViewportClient { SelectedActor = nullptr; } - virtual void ProcessClick(FSceneView & InView, HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) { - if (!HitProxy) { + virtual void ProcessClick(FSceneView & InView, HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) + { + if (!HitProxy) + { GEditor->SelectNone(true, true, true); SelectedActor = nullptr; return; } - if (HitProxy->IsA(HActor::StaticGetType())) { + if (HitProxy->IsA(HActor::StaticGetType())) + { HActor *h_actor = (HActor *)HitProxy; GEditor->SelectNone(true, true, true); GEditor->SelectActor(h_actor->Actor, true, true); @@ -263,35 +289,42 @@ class FPythonEditorViewportClient : public FEditorViewportClient { SetWidgetMode(FWidget::WM_Translate); } - else if (HitProxy->IsA(HWidgetAxis::StaticGetType())) { + else if (HitProxy->IsA(HWidgetAxis::StaticGetType())) + { // TODO do something ? } - else { + else + { GEditor->SelectNone(true, true, true); SelectedActor = nullptr; } } - virtual FWidget::EWidgetMode GetWidgetMode() const { + virtual FWidget::EWidgetMode GetWidgetMode() const + { if (SelectedActor == nullptr) return FWidget::WM_None; return FEditorViewportClient::GetWidgetMode(); } - virtual FMatrix GetWidgetCoordSystem() const { + virtual FMatrix GetWidgetCoordSystem() const + { if (!SelectedActor) return FMatrix::Identity; return FRotationMatrix(SelectedActor->GetActorTransform().Rotator()); } - virtual FVector GetWidgetLocation() const { + virtual FVector GetWidgetLocation() const + { if (!SelectedActor) return FVector(0, 0, 0); return SelectedActor->GetActorLocation(); } - virtual bool InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector &Scale) { - if (SelectedActor && !bAltPressed) { + virtual bool InputWidgetDelta(FViewport * InViewport, EAxisList::Type CurrentAxis, FVector & Drag, FRotator & Rot, FVector &Scale) + { + if (SelectedActor && !bAltPressed) + { SelectedActor->AddActorWorldOffset(Drag); SelectedActor->AddActorWorldRotation(Rot); SelectedActor->SetActorScale3D(SelectedActor->GetActorScale3D() + Scale); @@ -302,11 +335,13 @@ class FPythonEditorViewportClient : public FEditorViewportClient { return false; } - virtual void TrackingStarted(const FInputEventState & InInpuState, bool bIsDragginWidget, bool bNudge) { + virtual void TrackingStarted(const FInputEventState & InInpuState, bool bIsDragginWidget, bool bNudge) + { bAltPressed = InInpuState.IsAltButtonPressed(); } - virtual void ResetCamera() { + virtual void ResetCamera() + { if (!SelectedActor) return; FocusViewportOnBox(SelectedActor->GetComponentsBoundingBox()); @@ -318,11 +353,13 @@ class FPythonEditorViewportClient : public FEditorViewportClient { AActor *SelectedActor; }; -UWorld *SPythonEditorViewport::GetPythonWorld() { +UWorld *SPythonEditorViewport::GetPythonWorld() +{ return GetWorld(); } -TSharedRef SPythonEditorViewport::MakeEditorViewportClient() { +TSharedRef SPythonEditorViewport::MakeEditorViewportClient() +{ PreviewScene = new FPreviewScene(); @@ -334,30 +371,39 @@ TSharedRef SPythonEditorViewport::MakeEditorViewportClien FExposureSettings settings; settings.bFixed = true; +#if ENGINE_MINOR_VERSION > 18 + settings.FixedEV100 = 0; +#else settings.LogOffset = 0; +#endif client->ExposureSettings = settings; return client.ToSharedRef(); } -TSharedPtr SPythonEditorViewport::GetExtenders() const { +TSharedPtr SPythonEditorViewport::GetExtenders() const +{ TSharedPtr Result(MakeShareable(new FExtender())); return Result; } -TSharedRef SPythonEditorViewport::GetViewportWidget() { +TSharedRef SPythonEditorViewport::GetViewportWidget() +{ return SharedThis(this); } -TSharedPtr SPythonEditorViewport::MakeViewportToolbar() { +TSharedPtr SPythonEditorViewport::MakeViewportToolbar() +{ return SNew(SCommonEditorViewportToolbarBase, SharedThis(this)); } -void SPythonEditorViewport::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { +void SPythonEditorViewport::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ SEditorViewport::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); - if (bSimulate) { + if (bSimulate) + { GetWorld()->Tick(ELevelTick::LEVELTICK_All, InDeltaTime); } } @@ -393,12 +439,14 @@ PyTypeObject ue_PySPythonEditorViewportType = { ue_PySPythonEditorViewport_methods, /* tp_methods */ }; -static int ue_py_spython_editor_viewport_init(ue_PySPythonEditorViewport *self, PyObject *args, PyObject *kwargs) { +static int ue_py_spython_editor_viewport_init(ue_PySPythonEditorViewport *self, PyObject *args, PyObject *kwargs) +{ ue_py_snew_simple(SPythonEditorViewport, s_editor_viewport.s_compound_widget.s_widget); return 0; } -void ue_python_init_spython_editor_viewport(PyObject *ue_module) { +void ue_python_init_spython_editor_viewport(PyObject *ue_module) +{ ue_PySPythonEditorViewportType.tp_init = (initproc)ue_py_spython_editor_viewport_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp index 5181efc45..e8eb44747 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.cpp @@ -1,11 +1,15 @@ #include "UnrealEnginePythonPrivatePCH.h" -#include "Runtime/Slate/Public/Widgets/Views/SListView.h" #include "UEPySPythonListView.h" +void SPythonListView::SetHeaderRow(TSharedPtr InHeaderRowWidget) +{ + HeaderRow = InHeaderRowWidget; +} + #define sw_python_list_view StaticCastSharedRef(self->s_list_view.s_table_view_base.s_compound_widget.s_widget.s_widget) static PyObject *py_ue_spython_list_view_get_selected_items(ue_PySPythonListView *self, PyObject * args) { @@ -21,34 +25,101 @@ static PyObject *py_ue_spython_list_view_get_selected_items(ue_PySPythonListView return py_list; } -static PyObject *py_ue_spython_list_view_clear_selection(ue_PySPythonListView *self, PyObject * args) { - - +static PyObject *py_ue_spython_list_view_clear_selection(ue_PySPythonListView *self, PyObject * args) +{ sw_python_list_view->ClearSelection(); Py_INCREF(Py_None); return Py_None; - } static PyObject *py_ue_spython_list_view_get_num_items_selected(ue_PySPythonListView *self, PyObject * args) { return PyLong_FromLong(sw_python_list_view->GetNumItemsSelected()); } +static PyObject *py_ue_spython_list_view_set_header_row(ue_PySPythonListView *self, PyObject * args) +{ + PyObject *py_content; + if (!PyArg_ParseTuple(args, "O:set_header_row", &py_content)) + { + return NULL; + } + + ue_PySHeaderRow *py_sheader_row = py_ue_is_sheader_row(py_content); + if (!py_sheader_row) + { + return PyErr_Format(PyExc_Exception, "argument is not a SHeaderRow"); + } + + Py_INCREF(py_sheader_row); + sw_python_list_view->SetHeaderRow(StaticCastSharedRef(py_sheader_row->s_border.s_compound_widget.s_widget.s_widget->AsShared())); + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject *py_spython_list_view_update_item_source_list(ue_PySPythonListView *self, PyObject * args) +{ + PyObject *values; + if (!PyArg_ParseTuple(args, "O:update_item_source_list", &values)) + { + return NULL; + } + + values = PyObject_GetIter(values); + if (!values) { + return PyErr_Format(PyExc_Exception, "argument is not an iterable"); + } + + //NOTE: ikrimae: Increment first so we don't decrement and destroy python objects that + //we're passing in e.g. if you pass the same item source array into update_items(). + //Might not be necessary but I'm not too familiar with python's GC + TArray> tempNewArray; + while (PyObject *item = PyIter_Next(values)) { + Py_INCREF(item); + tempNewArray.Add(TSharedPtr(new FPythonItem(item))); + } + + for (TSharedPtr& item : self->item_source_list) + { + Py_XDECREF(item->py_object); + } + self->item_source_list.Empty(); + + Move>>(self->item_source_list, tempNewArray); + Py_RETURN_NONE; +} static PyMethodDef ue_PySPythonListView_methods[] = { { "get_selected_items", (PyCFunction)py_ue_spython_list_view_get_selected_items, METH_VARARGS, "" }, { "get_num_items_selected", (PyCFunction)py_ue_spython_list_view_get_num_items_selected, METH_VARARGS, "" }, { "clear_selection", (PyCFunction)py_ue_spython_list_view_clear_selection, METH_VARARGS, "" }, + { "set_header_row", (PyCFunction)py_ue_spython_list_view_set_header_row, METH_VARARGS, "" }, + { "update_item_source_list", (PyCFunction)py_spython_list_view_update_item_source_list, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; +static void ue_PySPythonListView_dealloc(ue_PySPythonListView *self) +{ +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PySPythonListView %p"), self); +#endif + + for (TSharedPtr& item : self->item_source_list) + { + Py_XDECREF(item->py_object); + } + self->item_source_list.Empty(); + + Py_TYPE(self)->tp_free((PyObject *)self); +} + PyTypeObject ue_PySPythonListViewType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.SPythonListView", /* tp_name */ sizeof(ue_PySPythonListView), /* tp_basicsize */ 0, /* tp_itemsize */ - 0, /* tp_dealloc */ + (destructor)ue_PySPythonListView_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -78,29 +149,40 @@ static int ue_py_spython_list_view_init(ue_PySPythonListView *self, PyObject *ar ue_py_slate_setup_farguments(SPythonListView); - // first of all check for values - PyObject *values = ue_py_dict_get_item(kwargs, "list_items_source"); - if (!values) { - PyErr_SetString(PyExc_Exception, "you must specify list items"); - return -1; - } - - values = PyObject_GetIter(values); - if (!values) { - PyErr_SetString(PyExc_Exception, "values field is not an iterable"); - return -1; - } - - TArray> *items = new TArray>(); - while (PyObject *item = PyIter_Next(values)) { - Py_INCREF(item); - // keep track of items - self->s_list_view.s_table_view_base.s_compound_widget.s_widget.py_refs.Add(item); - items->Add(TSharedPtr(new FPythonItem(item))); - } - Py_DECREF(values); - - arguments.ListItemsSource(items); + // first of all check for values + PyObject *values = ue_py_dict_get_item(kwargs, "list_items_source"); + if (!values) { + PyErr_SetString(PyExc_Exception, "you must specify list items"); + return -1; + } + + values = PyObject_GetIter(values); + if (!values) { + Py_DECREF(values); + return -1; + } + + new(&self->item_source_list) TArray>(); + while (PyObject *item = PyIter_Next(values)) { + Py_INCREF(item); + self->item_source_list.Add(TSharedPtr(new FPythonItem(item))); + } + arguments.ListItemsSource(&self->item_source_list); + + { + PyObject *value = ue_py_dict_get_item(kwargs, "header_row"); + if (value) { + if (ue_PySHeaderRow *_py_swidget = py_ue_is_sheader_row(value)) { + + Py_INCREF(_py_swidget); + arguments.HeaderRow(StaticCastSharedRef(((ue_PySWidget *)_py_swidget)->s_widget)); + } + else { + PyErr_SetString(PyExc_TypeError, "unsupported type for attribute " "header_row"); + return -1; + } + } + } ue_py_slate_farguments_optional_enum("allow_overscroll", AllowOverscroll, EAllowOverscroll); ue_py_slate_farguments_optional_bool("clear_selection_on_click", ClearSelectionOnClick); @@ -131,4 +213,4 @@ void ue_python_init_spython_list_view(PyObject *ue_module) { Py_INCREF(&ue_PySPythonListViewType); PyModule_AddObject(ue_module, "SPythonListView", (PyObject *)&ue_PySPythonListViewType); -} +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h index 31c1176b9..8e981336a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonListView.h @@ -11,11 +11,14 @@ class SPythonListView : public SListView> { ~SPythonListView() { delete(ItemsSource); } + + void SetHeaderRow(TSharedPtr InHeaderRowWidget); }; typedef struct { ue_PySListView s_list_view; /* Type-specific fields go here. */ + TArray> item_source_list; } ue_PySPythonListView; void ue_python_init_spython_list_view(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp new file mode 100644 index 000000000..a4d8b3d2a --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.cpp @@ -0,0 +1,83 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPySPythonMultiColumnTableRow.h" + + +#define sw_python_multicolumn_table_row StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) + +static PyMethodDef ue_PySPythonMultiColumnTableRow_methods[] = { + { NULL } /* Sentinel */ +}; + +PyTypeObject ue_PySPythonMultiColumnTableRowType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.SPythonMultiColumnTableRow", /* tp_name */ + sizeof(ue_PySPythonMultiColumnTableRow), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Unreal Engine SPythonMultiColumnTableRow", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PySPythonMultiColumnTableRow_methods, /* tp_methods */ +}; + +static int ue_py_spython_multicolumn_table_row_init(ue_PySPythonMultiColumnTableRow *self, PyObject *args, PyObject *kwargs) { + + PyObject *py_object = nullptr; + if (!PyArg_ParseTuple(args, "O", &py_object)) + { + return -1; + } + + ue_PySTableViewBase* py_owner_table_view_base = py_ue_is_stable_view_base(py_object); + if (!py_owner_table_view_base) { + PyErr_SetString(PyExc_Exception, "Argument is not a STableViewBase"); + return -1; + } + + Py_INCREF(py_owner_table_view_base); + ue_py_snew_simple_with_req_args( + SPythonMultiColumnTableRow, s_compound_widget.s_widget, + StaticCastSharedRef(py_owner_table_view_base->s_compound_widget.s_widget.s_widget), + (PyObject *)self); + return 0; +} + +void ue_python_init_spython_multicolumn_table_row(PyObject *ue_module) +{ + ue_PySPythonMultiColumnTableRowType.tp_base = &ue_PySCompoundWidgetType; + ue_PySPythonMultiColumnTableRowType.tp_init = (initproc)ue_py_spython_multicolumn_table_row_init; + + if (PyType_Ready(&ue_PySPythonMultiColumnTableRowType) < 0) + return; + + Py_INCREF(&ue_PySPythonMultiColumnTableRowType); + PyModule_AddObject(ue_module, "SPythonMultiColumnTableRow", (PyObject *)&ue_PySPythonMultiColumnTableRowType); +} + +ue_PySPythonMultiColumnTableRow *py_ue_is_spython_multicolumn_table_row(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySPythonMultiColumnTableRowType)) + return nullptr; + return (ue_PySPythonMultiColumnTableRow *)obj; +} diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h new file mode 100644 index 000000000..21ccda269 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonMultiColumnTableRow.h @@ -0,0 +1,111 @@ +#pragma once + +#include "UnrealEnginePython.h" + +#include "UEPySCompoundWidget.h" +#include "UEPyFGeometry.h" +#include "UEPyFPaintContext.h" +#include "UEPyFCharacterEvent.h" +#include "UEPyFKeyEvent.h" +#include "UEPyFPointerEvent.h" + +extern PyTypeObject ue_PySCompoundWidgetType; +extern PyTypeObject ue_PySTableViewBaseType; +class SPythonMultiColumnTableRow : public SMultiColumnTableRow> { +public: + SLATE_BEGIN_ARGS(SPythonMultiColumnTableRow) { } + SLATE_END_ARGS(); + + void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView, PyObject *in_py_self) + { + SetPyObject(in_py_self); + SMultiColumnTableRow>::Construct(FSuperRowType::FArguments(), InOwnerTableView); + } + + TSharedRef GenerateWidgetForColumn(const FName& ColumnName) + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"generate_widget_for_column")) + return SNullWidget::NullWidget; + + PyObject *py_callable_generate_widget_for_column = PyObject_GetAttrString(self, (char *)"generate_widget_for_column"); + if (!PyCalllable_Check_Extended(py_callable_generate_widget_for_column)) + { + UE_LOG(LogPython, Error, TEXT("generate_widget_for_column is not a callable")); + return SNullWidget::NullWidget; + } + + PyObject *ret = PyObject_CallFunction(py_callable_generate_widget_for_column, (char *)"s", TCHAR_TO_UTF8(*ColumnName.ToString())); + if (!ret) + { + unreal_engine_py_log_error(); + return SNullWidget::NullWidget; + } + + ue_PySWidget *s_widget = py_ue_is_swidget(ret); + if (!s_widget) + { + Py_DECREF(ret); + UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget")); + return SNullWidget::NullWidget; + } + + TSharedRef value = s_widget->s_widget; + Py_DECREF(ret); + return value; + } + + + FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) + { + FScopePythonGIL gil; + + if (PyObject_HasAttrString(self, (char *)"on_mouse_button_double_click")) + { + PyObject *py_callable_on_mouse_button_double_click = PyObject_GetAttrString(self, (char *)"on_mouse_button_double_click"); + if (!PyCalllable_Check_Extended(py_callable_on_mouse_button_double_click)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_double_click is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_double_click, (char *)"OO", py_ue_new_fgeometry(InMyGeometry), py_ue_new_fpointer_event(InMouseEvent)); + if (!ret) + { + unreal_engine_py_log_error(); + return FReply::Unhandled(); + } + + if (ret == Py_False) + { + Py_DECREF(ret); + return FReply::Unhandled(); + } + Py_DECREF(ret); + return FReply::Handled(); + } + else + { + return SPythonMultiColumnTableRow::OnMouseButtonDoubleClick(InMyGeometry, InMouseEvent); + } + } + + void SetPyObject(PyObject *py_obj) + { + self = py_obj; + } +private: + PyObject *self = nullptr; + + TSharedPtr RowPythonObject; +}; + +typedef struct { + ue_PySCompoundWidget s_compound_widget; + /* Type-specific fields go here. */ +} ue_PySPythonMultiColumnTableRow; + +void ue_python_init_spython_multicolumn_table_row(PyObject *); + +ue_PySPythonMultiColumnTableRow *py_ue_is_spython_multicolumn_table_row(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp index 524fd400e..267af55c3 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp @@ -46,7 +46,8 @@ PyTypeObject ue_PySPythonShelfType = { ue_PySPythonShelf_methods, /* tp_methods */ }; -static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyObject *kwargs) { +static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyObject *kwargs) +{ PyObject *py_classes_iterable = nullptr; PyObject *py_collections_iterable = nullptr; @@ -65,37 +66,45 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO &py_collections_iterable, &py_callable_double_clicked, &py_callable_get_context_menu, - &py_callable_asset_selected)) { + &py_callable_asset_selected)) + { return -1; } - if (py_classes_iterable) { + if (py_classes_iterable) + { py_classes_iterable = PyObject_GetIter(py_classes_iterable); - if (!py_classes_iterable) { + if (!py_classes_iterable) + { PyErr_SetString(PyExc_Exception, "argument is not an iterable"); return -1; } } - if (py_collections_iterable) { + if (py_collections_iterable) + { py_collections_iterable = PyObject_GetIter(py_collections_iterable); - if (!py_collections_iterable) { + if (!py_collections_iterable) + { PyErr_SetString(PyExc_Exception, "argument is not an iterable"); return -1; } } - if (py_callable_double_clicked && !PyCallable_Check(py_callable_double_clicked)) { + if (py_callable_double_clicked && !PyCalllable_Check_Extended(py_callable_double_clicked)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } - if (py_callable_get_context_menu && !PyCallable_Check(py_callable_get_context_menu)) { + if (py_callable_get_context_menu && !PyCalllable_Check_Extended(py_callable_get_context_menu)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } - if (py_callable_asset_selected && !PyCallable_Check(py_callable_asset_selected)) { + if (py_callable_asset_selected && !PyCalllable_Check_Extended(py_callable_asset_selected)) + { PyErr_SetString(PyExc_Exception, "argument is not callable"); return -1; } @@ -108,9 +117,12 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO asset_picker_config.bShowBottomToolbar = false; asset_picker_config.bAutohideSearchBar = false; - if (py_classes_iterable) { - while (PyObject *item = PyIter_Next(py_classes_iterable)) { - if (PyUnicode_Check(item)) { + if (py_classes_iterable) + { + while (PyObject *item = PyIter_Next(py_classes_iterable)) + { + if (PyUnicode_Check(item)) + { FName class_name = FName(UTF8_TO_TCHAR(PyUnicode_AsUTF8(item))); asset_picker_config.Filter.ClassNames.Add(class_name); } @@ -118,9 +130,12 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO Py_DECREF(py_classes_iterable); } - if (py_collections_iterable) { - while (PyObject *item = PyIter_Next(py_collections_iterable)) { - if (PyUnicode_Check(item)) { + if (py_collections_iterable) + { + while (PyObject *item = PyIter_Next(py_collections_iterable)) + { + if (PyUnicode_Check(item)) + { FName collection_name = FName(UTF8_TO_TCHAR(PyUnicode_AsUTF8(item))); asset_picker_config.Collections.Add(FCollectionNameType(collection_name, ECollectionShareType::CST_Local)); } @@ -128,36 +143,32 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO Py_DECREF(py_collections_iterable); } - if (py_callable_double_clicked) { + if (py_callable_double_clicked) + { FOnAssetDoubleClicked handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_double_clicked); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnAssetDoubleClicked); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_double_clicked); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetDoubleClicked); asset_picker_config.OnAssetDoubleClicked = handler; } - if (py_callable_get_context_menu) { + if (py_callable_get_context_menu) + { FOnGetAssetContextMenu handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_get_context_menu); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnGetAssetContextMenu); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_get_context_menu); + + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnGetAssetContextMenu); asset_picker_config.OnGetAssetContextMenu = handler; } - if (py_callable_asset_selected) { + if (py_callable_asset_selected) + { FOnAssetSelected handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_asset_selected); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnAssetSelected); - self->s_compound_widget.s_widget.delegates.Add(py_delegate); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable_asset_selected); + + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnAssetSelected); asset_picker_config.OnAssetSelected = handler; } @@ -167,7 +178,8 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO return 0; } -void ue_python_init_spython_shelf(PyObject *ue_module) { +void ue_python_init_spython_shelf(PyObject *ue_module) +{ ue_PySPythonShelfType.tp_init = (initproc)ue_py_spython_shelf_init; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp index d80a712ea..17d9909e0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonTreeView.cpp @@ -31,7 +31,6 @@ void SPythonTreeView::SetPythonItemExpansion(PyObject *item, bool InShouldExpand } } - static PyMethodDef ue_PySPythonTreeView_methods[] = { { "set_item_expansion", (PyCFunction)py_ue_spython_tree_view_set_item_expansion, METH_VARARGS, "" }, { NULL } /* Sentinel */ @@ -92,8 +91,6 @@ static int ue_py_spython_tree_view_init(ue_PySPythonTreeView *self, PyObject *ar while (PyObject *item = PyIter_Next(values)) { Py_INCREF(item); - // keep track of items - self->s_tree_view.s_list_view.s_table_view_base.s_compound_widget.s_widget.py_refs.Add(item); items->Add(TSharedPtr(new FPythonItem(item))); } Py_DECREF(values); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp index b55fe38c6..4369af222 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.cpp @@ -31,8 +31,40 @@ static PyObject *py_ue_spython_widget_set_active(ue_PySPythonWidget *self, PyObj return (PyObject *)self; } +static PyObject *py_ue_spython_widget_set_content(ue_PySPythonWidget *self, PyObject *args) +{ + PyObject *py_content; + if (!PyArg_ParseTuple(args, "O:set_content", &py_content)) + { + return NULL; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + + sw_python_widget->SetContent(py_swidget->s_widget->AsShared()); + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject *py_ue_spython_widget_clear_content(ue_PySPythonWidget *self, PyObject *args) +{ + sw_python_widget->ClearContent(); + + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef ue_PySPythonWidget_methods[] = { - { "set_active", (PyCFunction)py_ue_spython_widget_set_active, METH_VARARGS | METH_KEYWORDS, "" }, + { "set_active", (PyCFunction)py_ue_spython_widget_set_active, METH_VARARGS | METH_KEYWORDS, "" }, + { "set_content", (PyCFunction)py_ue_spython_widget_set_content, METH_VARARGS, "" }, + { "clear_content", (PyCFunction)py_ue_spython_widget_clear_content, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -70,7 +102,6 @@ PyTypeObject ue_PySPythonWidgetType = { static int ue_py_spython_widget_init(ue_PySPythonWidget *self, PyObject *args, PyObject *kwargs) { ue_py_snew_simple(SPythonWidget, s_compound_widget.s_widget); - UE_LOG(LogPython, Warning, TEXT("Initializing World Widget!!!")); sw_python_widget->SetPyObject((PyObject *)self); return 0; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h index 13f0942de..698cea267 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h @@ -37,7 +37,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_key_char = PyObject_GetAttrString(self, (char *)"on_key_char"); - if (!PyCallable_Check(py_callable_on_key_char)) + if (!PyCalllable_Check_Extended(py_callable_on_key_char)) { UE_LOG(LogPython, Error, TEXT("on_key_char is not a callable")); return FReply::Unhandled(); @@ -67,7 +67,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_key_down = PyObject_GetAttrString(self, (char *)"on_key_down"); - if (!PyCallable_Check(py_callable_on_key_down)) + if (!PyCalllable_Check_Extended(py_callable_on_key_down)) { UE_LOG(LogPython, Error, TEXT("on_key_down is not a callable")); return FReply::Unhandled(); @@ -97,7 +97,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_mouse_move = PyObject_GetAttrString(self, (char *)"on_mouse_move"); - if (!PyCallable_Check(py_callable_on_mouse_move)) + if (!PyCalllable_Check_Extended(py_callable_on_mouse_move)) { UE_LOG(LogPython, Error, TEXT("on_mouse_move is not a callable")); return FReply::Unhandled(); @@ -127,7 +127,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_mouse_wheel = PyObject_GetAttrString(self, (char *)"on_mouse_wheel"); - if (!PyCallable_Check(py_callable_on_mouse_wheel)) + if (!PyCalllable_Check_Extended(py_callable_on_mouse_wheel)) { UE_LOG(LogPython, Error, TEXT("on_mouse_wheel is not a callable")); return FReply::Unhandled(); @@ -157,7 +157,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_mouse_button_down = PyObject_GetAttrString(self, (char *)"on_mouse_button_down"); - if (!PyCallable_Check(py_callable_on_mouse_button_down)) + if (!PyCalllable_Check_Extended(py_callable_on_mouse_button_down)) { UE_LOG(LogPython, Error, TEXT("on_mouse_button_down is not a callable")); return FReply::Unhandled(); @@ -187,7 +187,7 @@ class SPythonWidget : public SCompoundWidget return FReply::Unhandled(); PyObject *py_callable_on_mouse_button_up = PyObject_GetAttrString(self, (char *)"on_mouse_button_up"); - if (!PyCallable_Check(py_callable_on_mouse_button_up)) + if (!PyCalllable_Check_Extended(py_callable_on_mouse_button_up)) { UE_LOG(LogPython, Error, TEXT("on_mouse_button_up is not a callable")); return FReply::Unhandled(); @@ -218,17 +218,18 @@ class SPythonWidget : public SCompoundWidget const FWidgetStyle & InWidgetStyle, bool bParentEnabled) const override { + int32 MaxLayer = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); FScopePythonGIL gil; if (!PyObject_HasAttrString(self, (char *)"paint")) - return LayerId + 1; + return MaxLayer; PyObject *py_callable_paint = PyObject_GetAttrString(self, (char *)"paint"); - if (!PyCallable_Check(py_callable_paint)) + if (!PyCalllable_Check_Extended(py_callable_paint)) { UE_LOG(LogPython, Error, TEXT("paint is not a callable")); - return LayerId + 1; + return MaxLayer; } FPaintContext context(AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); @@ -237,12 +238,12 @@ class SPythonWidget : public SCompoundWidget if (!ret) { unreal_engine_py_log_error(); - return LayerId + 1; + return MaxLayer; } Py_DECREF(ret); - return LayerId + 1; + return MaxLayer + 1; } @@ -256,7 +257,7 @@ class SPythonWidget : public SCompoundWidget return; PyObject *py_callable_tick = PyObject_GetAttrString(self, (char *)"tick"); - if (!PyCallable_Check(py_callable_tick)) + if (!PyCalllable_Check_Extended(py_callable_tick)) { UE_LOG(LogPython, Error, TEXT("tick is not a callable")); return; @@ -274,6 +275,8 @@ class SPythonWidget : public SCompoundWidget void SetPyObject(PyObject *py_obj) { + Py_XDECREF(self); + Py_INCREF(py_obj); self = py_obj; } @@ -289,6 +292,19 @@ class SPythonWidget : public SCompoundWidget } } + void SetContent(TSharedRef InContent) + { + ChildSlot + [ + InContent + ]; + } + + void ClearContent() + { + ChildSlot.DetachWidget(); + } + protected: PyObject *self; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp index 350e619bc..0684c89c0 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySScrollBox.cpp @@ -6,7 +6,8 @@ #define sw_scroll_box StaticCastSharedRef(self->s_compound_widget.s_widget.s_widget) -static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * args, PyObject *kwargs) { +static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * args, PyObject *kwargs) +{ PyObject *py_content; int h_align = 0; int v_align = 0; @@ -19,17 +20,18 @@ static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * ar if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ii:add_slot", kwlist, &py_content, &h_align, - &v_align)) { + &v_align)) + { return NULL; } ue_PySWidget *py_swidget = py_ue_is_swidget(py_content); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_slots.Add(py_swidget); SScrollBox::FSlot &fslot = sw_scroll_box->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); @@ -40,11 +42,8 @@ static PyObject *py_ue_sscroll_box_add_slot(ue_PySScrollBox *self, PyObject * ar return (PyObject *)self; } -static PyObject *py_ue_sscroll_box_clear_children(ue_PySScrollBox *self, PyObject * args) { - for (ue_PySWidget *child : self->s_compound_widget.s_widget.py_swidget_slots) { - Py_DECREF(child); - } - self->s_compound_widget.s_widget.py_swidget_slots.Empty(); +static PyObject *py_ue_sscroll_box_clear_children(ue_PySScrollBox *self, PyObject * args) +{ sw_scroll_box->ClearChildren(); Py_RETURN_NONE; @@ -88,7 +87,8 @@ PyTypeObject ue_PySScrollBoxType = { ue_PySScrollBox_methods, /* tp_methods */ }; -static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObject *kwargs) { +static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObject *kwargs) +{ ue_py_slate_setup_farguments(SScrollBox); @@ -105,7 +105,8 @@ static int ue_py_sscroll_box_init(ue_PySScrollBox *self, PyObject *args, PyObjec return 0; } -void ue_python_init_sscroll_box(PyObject *ue_module) { +void ue_python_init_sscroll_box(PyObject *ue_module) +{ ue_PySScrollBoxType.tp_init = (initproc)ue_py_sscroll_box_init; ue_PySScrollBoxType.tp_call = (ternaryfunc)py_ue_sscroll_box_add_slot; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp index 08e12f92f..d4f5e783a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySSplitter.cpp @@ -30,7 +30,6 @@ static PyObject *py_ue_ssplitter_add_slot(ue_PySSplitter *self, PyObject * args, } Py_INCREF(py_swidget); - self->s_panel.s_widget.py_swidget_slots.Add(py_swidget); SSplitter::FSlot &fslot = sw_splitter->AddSlot(index); if (size_value > -1) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp index e99605d68..8165fea28 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.cpp @@ -35,9 +35,17 @@ static PyObject *py_ue_stable_view_base_set_item_width(ue_PySTableViewBase *self return (PyObject *)self; } +static PyObject *py_ue_stable_view_base_request_list_refresh(ue_PySTableViewBase *self, PyObject * args) +{ + sw_table_view_base->RequestListRefresh(); + + Py_RETURN_NONE; +} + static PyMethodDef ue_PySTableViewBase_methods[] = { { "set_item_height", (PyCFunction)py_ue_stable_view_base_set_item_height, METH_VARARGS, "" }, { "set_item_width", (PyCFunction)py_ue_stable_view_base_set_item_width, METH_VARARGS, "" }, + { "request_list_refresh", (PyCFunction)py_ue_stable_view_base_request_list_refresh, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -83,3 +91,10 @@ void ue_python_init_stable_view_base(PyObject *ue_module) Py_INCREF(&ue_PySTableViewBaseType); PyModule_AddObject(ue_module, "STableViewBase", (PyObject *)&ue_PySTableViewBaseType); } + +ue_PySTableViewBase * py_ue_is_stable_view_base(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySTableViewBaseType)) + return nullptr; + return (ue_PySTableViewBase *)obj; +} diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h index a32ec85a8..5fd06b2cc 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTableViewBase.h @@ -13,4 +13,6 @@ typedef struct { /* Type-specific fields go here. */ } ue_PySTableViewBase; -void ue_python_init_stable_view_base(PyObject *); \ No newline at end of file +void ue_python_init_stable_view_base(PyObject *); + +ue_PySTableViewBase *py_ue_is_stable_view_base(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp index 384df56fa..f5c950e0a 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySVectorInputBox.cpp @@ -43,7 +43,6 @@ PyTypeObject ue_PySVectorInputBoxType = { static int ue_py_svector_input_box_init(ue_PySVectorInputBox *self, PyObject *args, PyObject *kwargs) { ue_py_slate_setup_farguments(SVectorInputBox); - //ue_py_slate_farguments_optional_float("delta", Delta); #if ENGINE_MINOR_VERSION > 15 ue_py_slate_farguments_optional_bool("allow_spin", AllowSpin); #endif diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp index 949995e44..dd1a48896 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySVerticalBox.cpp @@ -41,7 +41,6 @@ static PyObject *py_ue_svertical_box_add_slot(ue_PySVerticalBox *self, PyObject } Py_INCREF(py_swidget); - self->s_box_panel.s_panel.s_widget.py_swidget_slots.Add(py_swidget); SVerticalBox::FSlot &fslot = sw_vertical_box->AddSlot(); fslot.AttachWidget(py_swidget->s_widget->AsShared()); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp index 93eb3afe2..8b2b40421 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.cpp @@ -30,13 +30,42 @@ static PyObject *py_ue_swidget_get_children(ue_PySWidget *self, PyObject * args) static PyObject *py_ue_swidget_set_visibility(ue_PySWidget *self, PyObject * args) { - int visibility; - if (!PyArg_ParseTuple(args, "i:set_visibility", &visibility)) + PyObject* py_object; + if (!PyArg_ParseTuple(args, "O:set_visibility", &py_object)) { return nullptr; } - self->s_widget->SetVisibility(EVisibility::Visible); + if (!PyNumber_Check(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a ESlateVisibility"); + } + + PyObject *py_value = PyNumber_Long(py_object); + ESlateVisibility slateVisibility = (ESlateVisibility)PyLong_AsLong(py_value); + Py_DECREF(py_value); + + EVisibility visibility; + switch (slateVisibility) + { + case ESlateVisibility::Visible: + visibility = EVisibility::Visible; + break; + case ESlateVisibility::Collapsed: + visibility = EVisibility::Collapsed; + break; + case ESlateVisibility::Hidden: + visibility = EVisibility::Hidden; + break; + case ESlateVisibility::HitTestInvisible: + visibility = EVisibility::HitTestInvisible; + break; + case ESlateVisibility::SelfHitTestInvisible: + visibility = EVisibility::SelfHitTestInvisible; + break; + } + + self->s_widget->SetVisibility(visibility); Py_INCREF(self); return (PyObject *)self; @@ -93,16 +122,14 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_down(ue_PySWidget *self, PyO return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseButtonDown(handler); @@ -115,19 +142,17 @@ static PyObject *py_ue_swidget_bind_on_mouse_button_up(ue_PySWidget *self, PyObj PyObject *py_callable; if (!PyArg_ParseTuple(args, "O:bind_on_mouse_button_up", &py_callable)) { - return NULL; + return nullptr; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseButtonUp(handler); @@ -143,16 +168,14 @@ static PyObject *py_ue_swidget_bind_on_mouse_double_click(ue_PySWidget *self, Py return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseDoubleClick(handler); @@ -168,16 +191,14 @@ static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "argument is not callable"); } FPointerEventHandler handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnMouseEvent); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_widget, py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnMouseEvent); self->s_widget->SetOnMouseMove(handler); @@ -186,6 +207,7 @@ static PyObject *py_ue_swidget_bind_on_mouse_move(ue_PySWidget *self, PyObject * } #endif + static PyObject *py_ue_swidget_has_keyboard_focus(ue_PySWidget *self, PyObject * args) { @@ -212,6 +234,11 @@ static PyObject *py_ue_swidget_get_type(ue_PySWidget *self, PyObject * args) return PyUnicode_FromString(TCHAR_TO_UTF8(*(self->s_widget->GetTypeAsString()))); } +static PyObject *py_ue_swidget_get_cached_geometry(ue_PySWidget *self, PyObject * args) +{ + return py_ue_new_fgeometry(self->s_widget->GetCachedGeometry()); +} + static PyObject *py_ue_swidget_get_shared_reference_count(ue_PySWidget *self, PyObject * args) { return PyLong_FromLong(self->s_widget.GetSharedReferenceCount()); @@ -228,9 +255,71 @@ static PyObject *py_ue_swidget_invalidate(ue_PySWidget *self, PyObject * args) Py_RETURN_NONE; } +static PyObject *py_ue_swidget_on_mouse_button_down(ue_PySWidget *self, PyObject * args) +{ + PyObject *py_geometry; + PyObject *py_pointer_event; + if (!PyArg_ParseTuple(args, "OO:on_mouse_button_down", &py_geometry, &py_pointer_event)) + { + return nullptr; + } + + ue_PyFGeometry *geometry = py_ue_is_fgeometry(py_geometry); + if (!geometry) + { + return PyErr_Format(PyExc_Exception, "argument is not a FGeomtry"); + } + + ue_PyFPointerEvent *pointer = py_ue_is_fpointer_event(py_pointer_event); + if (!pointer) + { + return PyErr_Format(PyExc_Exception, "argument is not a FPointerEvent"); + } + + FReply reply = self->s_widget->OnMouseButtonDown(geometry->geometry, pointer->pointer); + + if (reply.IsEventHandled()) + { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} + +static PyObject *py_ue_swidget_on_mouse_button_up(ue_PySWidget *self, PyObject * args) +{ + PyObject *py_geometry; + PyObject *py_pointer_event; + if (!PyArg_ParseTuple(args, "OO:on_mouse_button_up", &py_geometry, &py_pointer_event)) + { + return nullptr; + } + + ue_PyFGeometry *geometry = py_ue_is_fgeometry(py_geometry); + if (!geometry) + { + return PyErr_Format(PyExc_Exception, "argument is not a FGeomtry"); + } + + ue_PyFPointerEvent *pointer = py_ue_is_fpointer_event(py_pointer_event); + if (!pointer) + { + return PyErr_Format(PyExc_Exception, "argument is not a FPointerEvent"); + } + + FReply reply = self->s_widget->OnMouseButtonUp(geometry->geometry, pointer->pointer); + + if (reply.IsEventHandled()) + { + Py_RETURN_TRUE; + } + + Py_RETURN_FALSE; +} static PyMethodDef ue_PySWidget_methods[] = { { "get_shared_reference_count", (PyCFunction)py_ue_swidget_get_shared_reference_count, METH_VARARGS, "" }, + { "get_cached_geometry", (PyCFunction)py_ue_swidget_get_cached_geometry, METH_VARARGS, "" }, { "get_children", (PyCFunction)py_ue_swidget_get_children, METH_VARARGS, "" }, { "get_type", (PyCFunction)py_ue_swidget_get_type, METH_VARARGS, "" }, { "set_tooltip_text", (PyCFunction)py_ue_swidget_set_tooltip_text, METH_VARARGS, "" }, @@ -246,6 +335,8 @@ static PyMethodDef ue_PySWidget_methods[] = { { "bind_on_mouse_double_click", (PyCFunction)py_ue_swidget_bind_on_mouse_double_click, METH_VARARGS, "" }, { "bind_on_mouse_move", (PyCFunction)py_ue_swidget_bind_on_mouse_move, METH_VARARGS, "" }, #endif + { "on_mouse_button_down", (PyCFunction)py_ue_swidget_on_mouse_button_down, METH_VARARGS, "" }, + { "on_mouse_button_up", (PyCFunction)py_ue_swidget_on_mouse_button_up, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -255,21 +346,6 @@ static void ue_PySWidgett_dealloc(ue_PySWidget *self) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PySWidget %p mapped to %s %p (slate refcount: %d)"), self, *self->s_widget->GetTypeAsString(), &self->s_widget.Get(), self->s_widget.GetSharedReferenceCount()); #endif Py_DECREF(self->py_dict); - for (UPythonSlateDelegate *item : self->delegates) - { - if (item->IsValidLowLevel() && item->IsRooted()) - item->RemoveFromRoot(); - } - for (ue_PySWidget *item : self->py_swidget_slots) - { - Py_DECREF(item); - } - // decref content (if any) - Py_XDECREF(self->py_swidget_content); - for (PyObject *item : self->py_refs) - { - Py_DECREF(item); - } ue_py_unregister_swidget(&self->s_widget.Get()); // decrement widget reference count // but only if python vm is still fully active (hack to avoid crashes on editor shutdown) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h index 9433fba00..ac3ca3645 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWidget.h @@ -13,16 +13,7 @@ struct ue_PySWidget /* Type-specific fields go here. */ TSharedRef s_widget; - PyObject *py_dict; - - TArray delegates; - - ue_PySWidget *py_swidget_content; - - TArray py_swidget_slots; - - TArray py_refs; }; void ue_python_init_swidget(PyObject *); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp index 1314cc3e2..7d22b4671 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.cpp @@ -39,6 +39,14 @@ static PyObject *py_ue_swindow_resize(ue_PySWindow *self, PyObject * args) return (PyObject *)self; } +static PyObject *py_ue_swindow_minimize(ue_PySWindow *self, PyObject * args) +{ + sw_window->Minimize(); + + Py_INCREF(self); + return (PyObject *)self; +} + static PyObject *py_ue_swindow_set_content(ue_PySWindow *self, PyObject * args) { PyObject *py_content; @@ -53,9 +61,7 @@ static PyObject *py_ue_swindow_set_content(ue_PySWindow *self, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } - Py_XDECREF(self->s_compound_widget.s_widget.py_swidget_content); Py_INCREF(py_swidget); - self->s_compound_widget.s_widget.py_swidget_content = py_swidget; sw_window->SetContent(py_swidget->s_widget->AsShared()); @@ -108,9 +114,31 @@ static PyObject *py_ue_swindow_add_modal(ue_PySWindow *self, PyObject * args) } #endif +static PyObject *py_ue_swindow_add_child(ue_PySWindow *self, PyObject * args) +{ + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:add_child", &py_obj)) + { + return NULL; + } + + ue_PySWindow *py_swindow_child = py_ue_is_swindow(py_obj); + if (!py_swindow_child) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWindow"); + } + + FSlateApplication::Get().AddWindowAsNativeChild( + StaticCastSharedRef(py_swindow_child->s_compound_widget.s_widget.s_widget), + sw_window); + + Py_RETURN_NONE; +} + static PyMethodDef ue_PySWindow_methods[] = { { "set_title", (PyCFunction)py_ue_swindow_set_title, METH_VARARGS, "" }, { "set_sizing_rule", (PyCFunction)py_ue_swindow_set_sizing_rule, METH_VARARGS, "" }, + { "minimize", (PyCFunction)py_ue_swindow_minimize, METH_VARARGS, "" }, { "resize", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_client_size", (PyCFunction)py_ue_swindow_resize, METH_VARARGS, "" }, { "set_content", (PyCFunction)py_ue_swindow_set_content, METH_VARARGS, "" }, @@ -119,6 +147,7 @@ static PyMethodDef ue_PySWindow_methods[] = { #if WITH_EDITOR { "add_modal", (PyCFunction)py_ue_swindow_add_modal, METH_VARARGS, "" }, #endif + { "add_child", (PyCFunction)py_ue_swindow_add_child, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; @@ -211,18 +240,21 @@ static int ue_py_swindow_init(ue_PySWindow *self, PyObject *args, PyObject *kwar #endif PyObject *on_closed = ue_py_dict_get_item(kwargs, "on_closed"); - if (on_closed && PyCallable_Check(on_closed)) + if (on_closed && PyCalllable_Check_Extended(on_closed)) { FOnWindowClosed handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(on_closed); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnWindowClosed); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(self->s_compound_widget.s_widget.s_widget, on_closed); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnWindowClosed); sw_window->SetOnWindowClosed(handler); } - FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); + // is it a child ? + PyObject *is_child = ue_py_dict_get_item(kwargs, "child"); + if (!(is_child && PyObject_IsTrue(is_child))) + { + FSlateApplication::Get().AddWindow(StaticCastSharedRef(sw_window->AsShared()), true); + } return 0; } @@ -241,3 +273,10 @@ void ue_python_init_swindow(PyObject *ue_module) Py_INCREF(&ue_PySWindowType); PyModule_AddObject(ue_module, "SWindow", (PyObject *)&ue_PySWindowType); } + +ue_PySWindow *py_ue_is_swindow(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PySWindowType)) + return nullptr; + return (ue_PySWindow *)obj; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h index e5aac3668..396c860de 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySWindow.h @@ -14,3 +14,5 @@ typedef struct { } ue_PySWindow; void ue_python_init_swindow(PyObject *); + +ue_PySWindow *py_ue_is_swindow(PyObject *obj); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 99cb31276..3064f7997 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -19,13 +19,14 @@ #include "Runtime/Slate/Public/Framework/Commands/UICommandInfo.h" #include "Runtime/Slate/Public/Framework/Docking/TabManager.h" #include "Runtime/Slate/Public/Widgets/Views/STableRow.h" +#include "Runtime/SlateCore/Public/Styling/SlateIconFinder.h" #include "Runtime/AppFramework/Public/Widgets/Colors/SColorPicker.h" - #include "UEPySlate.h" +#include "PyNativeWidgetHost.h" -FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) +FReply FPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event) { FScopePythonGIL gil; @@ -45,7 +46,7 @@ FReply UPythonSlateDelegate::OnMouseEvent(const FGeometry &geometry, const FPoin return FReply::Handled(); } -FReply UPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event) +FReply FPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event) { FScopePythonGIL gil; @@ -65,7 +66,7 @@ FReply UPythonSlateDelegate::OnKeyDown(const FGeometry &geometry, const FKeyEven return FReply::Handled(); } -FReply UPythonSlateDelegate::OnClicked() +FReply FPythonSlateDelegate::OnClicked() { FScopePythonGIL gil; @@ -85,7 +86,7 @@ FReply UPythonSlateDelegate::OnClicked() return FReply::Handled(); } -void UPythonSlateDelegate::OnTextChanged(const FText& text) +void FPythonSlateDelegate::OnTextChanged(const FText& text) { FScopePythonGIL gil; @@ -98,7 +99,7 @@ void UPythonSlateDelegate::OnTextChanged(const FText& text) Py_DECREF(ret); } -void UPythonSlateDelegate::OnStringChanged(const FString& text) +void FPythonSlateDelegate::OnStringChanged(const FString& text) { FScopePythonGIL gil; @@ -111,7 +112,7 @@ void UPythonSlateDelegate::OnStringChanged(const FString& text) Py_DECREF(ret); } -void UPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type commit_type) +void FPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type commit_type) { FScopePythonGIL gil; @@ -124,7 +125,33 @@ void UPythonSlateDelegate::OnTextCommitted(const FText& text, ETextCommit::Type Py_DECREF(ret); } -void UPythonSlateDelegate::OnFloatChanged(float value) +void FPythonSlateDelegate::OnInt32Changed(int32 value) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"i", value); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +void FPythonSlateDelegate::OnInt32Committed(int32 value, ETextCommit::Type commit_type) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"ii", value, (int)commit_type); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +void FPythonSlateDelegate::OnFloatChanged(float value) { FScopePythonGIL gil; @@ -137,7 +164,7 @@ void UPythonSlateDelegate::OnFloatChanged(float value) Py_DECREF(ret); } -void UPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) +void FPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) { FScopePythonGIL gil; @@ -150,7 +177,7 @@ void UPythonSlateDelegate::OnLinearColorChanged(FLinearColor color) Py_DECREF(ret); } -void UPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) +void FPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) { FScopePythonGIL gil; @@ -163,7 +190,7 @@ void UPythonSlateDelegate::OnWindowClosed(const TSharedRef &Window) Py_DECREF(ret); } -void UPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commit_type) +void FPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commit_type) { FScopePythonGIL gil; @@ -176,7 +203,20 @@ void UPythonSlateDelegate::OnFloatCommitted(float value, ETextCommit::Type commi Py_DECREF(ret); } -void UPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) +void FPythonSlateDelegate::OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"isi", (int)SortPriority, TCHAR_TO_UTF8(*ColumnName.ToString()), (int)NewSortMode); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + +void FPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) { FScopePythonGIL gil; @@ -190,7 +230,7 @@ void UPythonSlateDelegate::CheckBoxChanged(ECheckBoxState state) } #if WITH_EDITOR -void UPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -202,7 +242,7 @@ void UPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) Py_XDECREF(ret); } -void UPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -214,7 +254,7 @@ void UPythonSlateDelegate::OnAssetSelected(const FAssetData& AssetData) Py_XDECREF(ret); } -void UPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) +void FPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -226,7 +266,7 @@ void UPythonSlateDelegate::OnAssetChanged(const FAssetData& AssetData) Py_XDECREF(ret); } -bool UPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) +bool FPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) { FScopePythonGIL gil; @@ -241,7 +281,7 @@ bool UPythonSlateDelegate::OnShouldFilterAsset(const FAssetData& AssetData) return bValue; } -TSharedPtr UPythonSlateDelegate::OnGetAssetContextMenu(const TArray& SelectedAssets) +TSharedPtr FPythonSlateDelegate::OnGetAssetContextMenu(const TArray& SelectedAssets) { FScopePythonGIL gil; @@ -271,7 +311,7 @@ TSharedPtr UPythonSlateDelegate::OnGetAssetContextMenu(const TArray SelectedAssets) +void FPythonSlateDelegate::MenuPyAssetBuilder(FMenuBuilder &Builder, TArray SelectedAssets) { FScopePythonGIL gil; @@ -290,18 +330,18 @@ void UPythonSlateDelegate::MenuPyAssetBuilder(FMenuBuilder &Builder, TArray UPythonSlateDelegate::OnExtendContentBrowserMenu(const TArray& SelectedAssets) +TSharedRef FPythonSlateDelegate::OnExtendContentBrowserMenu(const TArray& SelectedAssets) { TSharedRef Extender(new FExtender()); - Extender->AddMenuExtension((char *)"GetAssetActions", EExtensionHook::After, nullptr, FMenuExtensionDelegate::CreateUObject(this, &UPythonSlateDelegate::MenuPyAssetBuilder, SelectedAssets)); + Extender->AddMenuExtension((char *)"GetAssetActions", EExtensionHook::After, nullptr, FMenuExtensionDelegate::CreateSP(this, &FPythonSlateDelegate::MenuPyAssetBuilder, SelectedAssets)); return Extender; } #endif -TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item) +TSharedRef FPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item) { FScopePythonGIL gil; @@ -312,6 +352,30 @@ TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr value = s_widget->s_widget; + + Py_INCREF(ret); + return value; +} + +TSharedRef FPythonSlateDelegate::OnGetMenuContent() +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)""); + if (!ret) + { + unreal_engine_py_log_error(); + return SNullWidget::NullWidget; + } + ue_PySWidget *s_widget = py_ue_is_swidget(ret); if (!s_widget) { @@ -324,8 +388,13 @@ TSharedRef UPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item, ESelectInfo::Type select_type) +void FPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type) { + if (!py_item.IsValid()) + { + return; + } + FScopePythonGIL gil; PyObject *ret = PyObject_CallFunction(py_callable, (char *)"Oi", py_item.Get()->py_object, (int)select_type); @@ -337,7 +406,7 @@ void UPythonSlateDelegate::OnSelectionChanged(TSharedPtr py_item, E Py_DECREF(ret); } -TSharedPtr UPythonSlateDelegate::OnContextMenuOpening() +TSharedPtr FPythonSlateDelegate::OnContextMenuOpening() { FScopePythonGIL gil; @@ -360,7 +429,7 @@ TSharedPtr UPythonSlateDelegate::OnContextMenuOpening() return value; } -void UPythonSlateDelegate::SimpleExecuteAction() +void FPythonSlateDelegate::SimpleExecuteAction() { FScopePythonGIL gil; @@ -372,7 +441,7 @@ void UPythonSlateDelegate::SimpleExecuteAction() Py_XDECREF(ret); } -void UPythonSlateDelegate::ExecuteAction(PyObject *py_obj) +void FPythonSlateDelegate::ExecuteAction(PyObject *py_obj) { FScopePythonGIL gil; @@ -384,7 +453,7 @@ void UPythonSlateDelegate::ExecuteAction(PyObject *py_obj) Py_XDECREF(ret); } -FText UPythonSlateDelegate::GetterFText() const +FText FPythonSlateDelegate::GetterFText() const { FScopePythonGIL gil; @@ -408,7 +477,7 @@ FText UPythonSlateDelegate::GetterFText() const return text; } -FString UPythonSlateDelegate::GetterFString() const +FString FPythonSlateDelegate::GetterFString() const { FScopePythonGIL gil; @@ -432,7 +501,7 @@ FString UPythonSlateDelegate::GetterFString() const return fstr; } -float UPythonSlateDelegate::GetterFloat() const +float FPythonSlateDelegate::GetterFloat() const { FScopePythonGIL gil; @@ -456,7 +525,7 @@ float UPythonSlateDelegate::GetterFloat() const return n; } -TOptional UPythonSlateDelegate::GetterTFloat() const +TOptional FPythonSlateDelegate::GetterTFloat() const { FScopePythonGIL gil; @@ -480,7 +549,7 @@ TOptional UPythonSlateDelegate::GetterTFloat() const return n; } -int UPythonSlateDelegate::GetterInt() const +int FPythonSlateDelegate::GetterInt() const { FScopePythonGIL gil; @@ -504,7 +573,7 @@ int UPythonSlateDelegate::GetterInt() const return n; } -bool UPythonSlateDelegate::GetterBool() const +bool FPythonSlateDelegate::GetterBool() const { FScopePythonGIL gil; @@ -524,7 +593,7 @@ bool UPythonSlateDelegate::GetterBool() const return false; } -FVector2D UPythonSlateDelegate::GetterFVector2D() const +FVector2D FPythonSlateDelegate::GetterFVector2D() const { FScopePythonGIL gil; @@ -571,7 +640,7 @@ FVector2D UPythonSlateDelegate::GetterFVector2D() const return FVector2D(x, y); } -FLinearColor UPythonSlateDelegate::GetterFLinearColor() const +FLinearColor FPythonSlateDelegate::GetterFLinearColor() const { FScopePythonGIL gil; @@ -597,7 +666,7 @@ FLinearColor UPythonSlateDelegate::GetterFLinearColor() const return color; } -TSharedRef UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &args) +TSharedRef FPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &args) { TSharedRef dock_tab = SNew(SDockTab).TabRole(ETabRole::NomadTab); PyObject *py_dock = (PyObject *)ue_py_get_swidget(dock_tab); @@ -613,25 +682,34 @@ TSharedRef UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &a return dock_tab; } -TSharedRef UPythonSlateDelegate::GenerateRow(TSharedPtr InItem, const TSharedRef& OwnerTable) +TSharedRef FPythonSlateDelegate::GenerateRow(TSharedPtr InItem, const TSharedRef& OwnerTable) { + FScopePythonGIL gil; + PyObject *ret = PyObject_CallFunction(py_callable, (char*)"O", InItem.Get()->py_object); if (!ret) { unreal_engine_py_log_error(); return SNew(STableRow>, OwnerTable); } - ue_PySWidget *s_widget = py_ue_is_swidget(ret); - if (!s_widget) + + if (ue_PySPythonMultiColumnTableRow *spython_multicolumn_table_row = py_ue_is_spython_multicolumn_table_row(ret)) { - UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); - return SNew(STableRow>, OwnerTable); + Py_INCREF(spython_multicolumn_table_row); + TSharedRef value = StaticCastSharedRef(spython_multicolumn_table_row->s_compound_widget.s_widget.s_widget->AsShared()); + return value; + } + else if (ue_PySWidget *s_widget = py_ue_is_swidget(ret)) + { + return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; } - return SNew(STableRow>, OwnerTable).Content()[s_widget->s_widget]; + UE_LOG(LogPython, Error, TEXT("python callable did not return a SWidget")); + return SNew(STableRow>, OwnerTable); + } -void UPythonSlateDelegate::GetChildren(TSharedPtr InItem, TArray>& OutChildren) +void FPythonSlateDelegate::GetChildren(TSharedPtr InItem, TArray>& OutChildren) { PyObject *ret = PyObject_CallFunction(py_callable, (char*)"O", InItem.Get()->py_object); if (!ret) @@ -694,10 +772,6 @@ void ue_py_setup_swidget(ue_PySWidget *self) #endif self->py_dict = PyDict_New(); new(&self->s_widget) TSharedRef(SNullWidget::NullWidget); - new(&self->delegates) TArray(); - new(&self->py_swidget_slots) TArray(); - new(&self->py_refs) TArray(); - self->py_swidget_content = nullptr; } void ue_py_register_swidget(SWidget *s_widget, ue_PySWidget *py_s_widget) @@ -735,6 +809,7 @@ void ue_python_init_slate(PyObject *module) ue_python_init_stable_view_base(module); ue_python_init_slist_view(module); ue_python_init_spython_list_view(module); + ue_python_init_spython_multicolumn_table_row(module); ue_python_init_stree_view(module); ue_python_init_spython_tree_view(module); ue_python_init_ssplitter(module); @@ -756,6 +831,12 @@ void ue_python_init_slate(PyObject *module) #if WITH_EDITOR + ue_python_init_snode_panel(module); +#if ENGINE_MINOR_VERSION > 15 + ue_python_init_sgraph_panel(module); +#endif + ue_python_init_idetails_view(module); + ue_python_init_istructure_details_view(module); ue_python_init_seditor_viewport(module); ue_python_init_slevel_viewport(module); ue_python_init_spython_editor_viewport(module); @@ -785,6 +866,8 @@ void ue_python_init_slate(PyObject *module) ue_python_init_fpointer_event(module); ue_python_init_fkey_event(module); ue_python_init_fcharacter_event(module); + ue_python_init_fmodifier_keys_state(module); + ue_python_init_eslate_enums(module); } PyObject *ue_py_dict_get_item(PyObject *dict, const char *key) @@ -805,6 +888,40 @@ PyObject *py_unreal_engine_get_editor_window(PyObject *self, PyObject *args) return (PyObject *)ue_py_get_swidget(FGlobalTabmanager::Get()->GetRootWindow().ToSharedRef()); } +PyObject *py_unreal_engine_find_slate_style(PyObject *self, PyObject *args) +{ + char *name = nullptr; + if (!PyArg_ParseTuple(args, "s:find_slate_style", &name)) + return nullptr; + + ISlateStyle const* const foundStyleSet = FSlateStyleRegistry::FindSlateStyle(FName(name)); + if (!foundStyleSet) + { + UE_LOG(LogPython, Warning, TEXT("Could not find SlateStyle")); + Py_RETURN_NONE; + } + + ue_PyFSlateStyleSet *ret = py_ue_new_fslate_style_set(static_cast(const_cast(foundStyleSet))); + return (PyObject *)ret; +} + +PyObject *py_unreal_engine_find_icon_for_class(PyObject *self, PyObject *args) +{ + PyObject* py_class = nullptr; + char *name = nullptr; + if (!PyArg_ParseTuple(args, "O|s:find_icon_for_class", &py_class, &name)) + return nullptr; + + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + return PyErr_Format(PyExc_Exception, "argument is not a UClass object"); + + FSlateIcon foundIcon = FSlateIconFinder::FindIconForClass(u_class, FName(UTF8_TO_TCHAR(name))); + ue_PyFSlateIcon *ret = py_ue_new_fslate_icon(foundIcon); + return (PyObject *)ret; +} + + // slate commands tool class class FPythonSlateCommands : public TCommands { @@ -892,39 +1009,135 @@ class FPythonSlateCommands : public TCommands }; #if WITH_EDITOR - PyObject *py_unreal_engine_create_detail_view(PyObject *self, PyObject * args, PyObject *kwargs) { PyObject *py_object; PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + char *kwlist[] = { (char *)"uobject", (char *)"allow_search", + + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", nullptr }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:create_detail_view", kwlist, - &py_object, &py_allow_search)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_detail_view", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) { return nullptr; } - UObject *u_object = ue_py_check_type(py_object); - if (!u_object) - { - return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - } FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); FDetailsViewArgs view_args; - view_args.bAllowSearch = (py_allow_search && PyObject_IsTrue(py_allow_search)); - view_args.NameAreaSettings = FDetailsViewArgs::ENameAreaSettings::HideNameArea; + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); TSharedPtr view = PropertyEditorModule.CreateDetailView(view_args); - view->SetObject(u_object); - return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PySWidgetType); + if (UObject *u_object = ue_py_check_type(py_object)) + { + view->SetObject(u_object); + } + + extern PyTypeObject ue_PyIDetailsViewType; + return (PyObject *)py_ue_new_swidget(view->AsShared(), &ue_PyIDetailsViewType); +} + +PyObject *py_unreal_engine_create_structure_detail_view(PyObject *self, PyObject * args, PyObject *kwargs) +{ + PyObject *py_object = nullptr; + + PyObject *py_allow_search = nullptr; + PyObject *py_update_from_selection = nullptr; + PyObject *py_lockable = nullptr; + char *py_name_area_settings = nullptr; + PyObject *py_hide_selection_tip = nullptr; + PyObject *py_search_initial_key_focus = nullptr; + + char *kwlist[] = { + (char*)"struct_data", + (char *)"allow_search", + (char *)"update_from_selection", + (char *)"lockable", + (char *)"name_area_settings", + (char *)"hide_selection_tip", + (char *)"search_initial_key_focus", + nullptr }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOsOO:create_structure_detail_view", kwlist, + &py_object, &py_allow_search, &py_update_from_selection, &py_lockable, &py_name_area_settings, &py_hide_selection_tip, &py_search_initial_key_focus)) + { + return nullptr; + } + + if (py_object && !py_ue_is_uscriptstruct(py_object)) + { + return PyErr_Format(PyExc_Exception, "argument is not a UScriptStruct"); + } + + FDetailsViewArgs view_args; + view_args.bAllowSearch = (py_allow_search) ? PyObject_IsTrue(py_allow_search) : view_args.bAllowSearch; + view_args.bUpdatesFromSelection = (py_update_from_selection) ? PyObject_IsTrue(py_update_from_selection) : view_args.bUpdatesFromSelection; + view_args.bLockable = (py_lockable) ? PyObject_IsTrue(py_lockable) : view_args.bLockable; + view_args.bHideSelectionTip = (py_hide_selection_tip) ? PyObject_IsTrue(py_hide_selection_tip) : view_args.bHideSelectionTip; + view_args.bSearchInitialKeyFocus = (py_search_initial_key_focus) ? PyObject_IsTrue(py_search_initial_key_focus) : view_args.bSearchInitialKeyFocus; + + FString name_area_string = py_name_area_settings ? FString(UTF8_TO_TCHAR(py_name_area_settings)) : FString(); + view_args.NameAreaSettings = [&name_area_string]() { + if (FCString::Stricmp(*name_area_string, TEXT("HideNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::HideNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ObjectsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ObjectsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + else if (FCString::Stricmp(*name_area_string, TEXT("ComponentsAndActorsUseNameArea")) == 0) { return FDetailsViewArgs::ENameAreaSettings::ComponentsAndActorsUseNameArea; } + else { return FDetailsViewArgs::ENameAreaSettings::ActorsUseNameArea; } + }(); + FStructureDetailsViewArgs struct_view_args; + { + struct_view_args.bShowObjects = true; + struct_view_args.bShowAssets = true; + struct_view_args.bShowClasses = true; + struct_view_args.bShowInterfaces = true; + } + + extern PyTypeObject ue_PyIStructureDetailsViewType; + ue_PyIStructureDetailsView *ret = (ue_PyIStructureDetailsView *)PyObject_New(ue_PyIStructureDetailsView, &ue_PyIStructureDetailsViewType); + new(&ret->istructure_details_view) TSharedPtr(nullptr); + ret->ue_py_struct = nullptr; + TSharedPtr struct_scope; + + if (ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_object)) + { + Py_INCREF(ue_py_struct); + ret->ue_py_struct = ue_py_struct; + struct_scope = MakeShared(ue_py_struct->u_struct, ue_py_struct->data); + } + + FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked("PropertyEditor"); + ret->istructure_details_view = PropertyEditorModule.CreateStructureDetailView(view_args, struct_view_args, struct_scope); + + return (PyObject *)ret; } PyObject *py_unreal_engine_create_property_view(PyObject *self, PyObject * args, PyObject *kwargs) @@ -1012,7 +1225,7 @@ PyObject *py_unreal_engine_add_menu_extension(PyObject * self, PyObject * args) if (!menu_extension_interface) return PyErr_Format(PyExc_Exception, "module %s is not supported", module); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1045,7 +1258,7 @@ PyObject *py_unreal_engine_add_menu_bar_extension(PyObject * self, PyObject * ar FLevelEditorModule &ExtensibleModule = FModuleManager::LoadModuleChecked("LevelEditor"); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1078,7 +1291,7 @@ PyObject *py_unreal_engine_add_tool_bar_extension(PyObject * self, PyObject * ar FLevelEditorModule &ExtensibleModule = FModuleManager::LoadModuleChecked("LevelEditor"); - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); @@ -1106,17 +1319,15 @@ PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); FContentBrowserModule &ContentBrowser = FModuleManager::LoadModuleChecked("ContentBrowser"); TArray &Extenders = ContentBrowser.GetAllAssetViewContextMenuExtenders(); FContentBrowserMenuExtender_SelectedAssets handler; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnExtendContentBrowserMenu); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); + handler.BindSP(py_delegate, &FPythonSlateDelegate::OnExtendContentBrowserMenu); Extenders.Add(handler); @@ -1134,14 +1345,12 @@ PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject * self, PyObject return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); FOnSpawnTab spawn_tab; - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - spawn_tab.BindUObject(py_delegate, &UPythonSlateDelegate::SpawnPythonTab); + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); + spawn_tab.BindSP(py_delegate, &FPythonSlateDelegate::SpawnPythonTab); FTabSpawnerEntry *spawner_entry = &FGlobalTabmanager::Get()->RegisterNomadTabSpawner(UTF8_TO_TCHAR(name), spawn_tab) // TODO: more generic way to set the group @@ -1186,32 +1395,68 @@ PyObject *py_unreal_engine_invoke_tab(PyObject * self, PyObject * args) } +PyObject * py_unreal_engine_get_swidget_from_wrapper(PyObject *self, PyObject *args) +{ + PyObject *py_object; + + if (!PyArg_ParseTuple(args, "O:get_swidget_from_wrapper", &py_object)) + { + return NULL; + } + + FPythonSWidgetWrapper *py_swidget_wrapper = ue_py_check_struct(py_object); + if (!py_swidget_wrapper) + return PyErr_Format(PyExc_Exception, "argument is not a FPythonSWidgetWrapper"); + + if (!py_swidget_wrapper->Widget.IsValid()) + return PyErr_Format(PyExc_Exception, "wrapper contained invalid SWidget!"); + + return (PyObject *)py_ue_new_swidget(py_swidget_wrapper->Widget->AsShared(), &ue_PySWidgetType); +} + +PyObject * py_unreal_engine_create_wrapper_from_pyswidget(PyObject *self, PyObject *args) +{ + PyObject *py_object; + + if (!PyArg_ParseTuple(args, "O:create_wrapper_from_pyswidget", &py_object)) + { + return NULL; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_object); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + Py_INCREF(py_swidget); + FPythonSWidgetWrapper py_swidget_wrapper; + py_swidget_wrapper.Widget = py_swidget->s_widget; + return py_ue_new_uscriptstruct(FPythonSWidgetWrapper::StaticStruct(), (uint8 *)&py_swidget_wrapper); +} + PyObject *py_unreal_engine_open_color_picker(PyObject *self, PyObject *args, PyObject *kwargs) { - PyObject *py_callable_on_color_committed = nullptr; + PyObject *py_callable = nullptr; char *kwlist[] = { (char *)"on_color_committed", nullptr }; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:open_color_picker", kwlist, - &py_callable_on_color_committed)) + &py_callable)) { return nullptr; } - if (!PyCallable_Check(py_callable_on_color_committed)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "on_color_committed must be a callable"); } - UPythonSlateDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable_on_color_committed); - // TODO: this memory should be freed... - py_delegate->AddToRoot(); - + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); FColorPickerArgs color_args; - color_args.OnColorCommitted.BindUObject(py_delegate, &UPythonSlateDelegate::OnLinearColorChanged); + color_args.OnColorCommitted.BindSP(py_delegate, &FPythonSlateDelegate::OnLinearColorChanged); if (OpenColorPicker(color_args)) { diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h index 34c7de74d..99a8ffc7b 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.h @@ -40,6 +40,7 @@ #include "UEPySTableViewBase.h" #include "UEPySListView.h" #include "UEPySPythonListView.h" +#include "UEPySPythonMultiColumnTableRow.h" #include "UEPySTreeView.h" #include "UEPySPythonTreeView.h" #include "UEPySSplitter.h" @@ -62,9 +63,9 @@ #include "UEPyFTabManager.h" #include "UEPyFTabSpawnerEntry.h" #include "UEPyFMenuBuilder.h" -#include "UEPyFSlateStyleSet.h" #include "UEPyFToolBarBuilder.h" #include "UEPyFSlateIcon.h" +#include "UEPyFSlateStyleSet.h" #include "UEPyFGeometry.h" #include "UEPyFPaintContext.h" @@ -73,6 +74,7 @@ #include "UEPyFPointerEvent.h" #include "UEPyFKeyEvent.h" #include "UEPyFCharacterEvent.h" +#include "UEPyFModifierKeysState.h" #if WITH_EDITOR #include "UEPySEditorViewport.h" @@ -84,6 +86,10 @@ #include "UEPySDropTarget.h" #include "UEPySAssetDropTarget.h" #include "UEPySObjectPropertyEntryBox.h" +#include "UEPyIDetailsView.h" +#include "UEPyIStructureDetailsView.h" +#include "UEPySNodePanel.h" +#include "UEPySGraphPanel.h" #endif #include "Runtime/Core/Public/Misc/Attribute.h" @@ -91,23 +97,25 @@ #include "PythonDelegate.h" -#include "UEPySlate.generated.h" - - - PyObject *py_unreal_engine_get_editor_window(PyObject *, PyObject *); +PyObject *py_unreal_engine_find_slate_style(PyObject *, PyObject *); +PyObject *py_unreal_engine_find_icon_for_class(PyObject *, PyObject *); + #if WITH_EDITOR PyObject *py_unreal_engine_add_menu_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_add_menu_bar_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_add_tool_bar_extension(PyObject *, PyObject *); PyObject *py_unreal_engine_create_detail_view(PyObject *, PyObject *, PyObject *); +PyObject *py_unreal_engine_create_structure_detail_view(PyObject *, PyObject *, PyObject *); PyObject *py_unreal_engine_create_property_view(PyObject *, PyObject *, PyObject *); PyObject *py_unreal_engine_add_asset_view_context_menu_extension(PyObject * self, PyObject *); #endif PyObject *py_unreal_engine_invoke_tab(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_swidget_from_wrapper(PyObject *, PyObject *); +PyObject *py_unreal_engine_create_wrapper_from_pyswidget(PyObject *, PyObject *); PyObject *py_unreal_engine_register_nomad_tab_spawner(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_nomad_tab_spawner(PyObject *, PyObject *); @@ -122,6 +130,7 @@ void ue_py_unregister_swidget(SWidget *); void ue_py_setup_swidget(ue_PySWidget *); + PyObject *ue_py_dict_get_item(PyObject *, const char *); template ue_PySWidget *py_ue_new_swidget(TSharedRef s_widget, PyTypeObject *py_type) @@ -140,6 +149,8 @@ template ue_PySWidget *py_ue_new_swidget(TSharedRef s_widge #define ue_py_snew_simple(T, field) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(), T::FArguments()) +#define ue_py_snew_simple_with_req_args(T, field, ... ) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(__VA_ARGS__), T::FArguments()) + #define ue_py_snew(T, field) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(), arguments) #define ue_py_snew_with_args(T, field, args) ue_py_snew_base(T, field, RequiredArgs::MakeRequiredArgs(args), arguments) @@ -151,13 +162,23 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); {\ PyObject *value = ue_py_dict_get_item(kwargs, _param);\ if (value) {\ - if (PyCallable_Check(value)) {\ + if (PyCalllable_Check_Extended(value)) {\ _base handler;\ - UPythonSlateDelegate *py_delegate = NewObject();\ - py_delegate->SetPyCallable(value);\ - py_delegate->AddToRoot();\ - handler.BindUObject(py_delegate, &UPythonSlateDelegate::_func);\ - ((ue_PySWidget *)self)->delegates.Add(py_delegate);\ + ue_PySWidget *py_swidget = (ue_PySWidget *)self;\ + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(py_swidget->s_widget, value);\ + handler.Bind(py_delegate, &FPythonSlateDelegate::_func);\ + arguments._attribute(handler);\ + } + +#define ue_py_slate_base_event_up(_base, _func, _param, _attribute) \ +{\ + PyObject *value = ue_py_dict_get_item(kwargs, _param);\ + if (value) {\ + if (PyCalllable_Check_Extended(value)) {\ + _base handler;\ + ue_PySWidget *py_swidget = (ue_PySWidget *)self;\ + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewSlateDelegate(py_swidget->s_widget, value);\ + handler.BindSP(py_delegate, &FPythonSlateDelegate::_func);\ arguments._attribute(handler);\ } @@ -231,6 +252,13 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ ue_py_slate_down(param) +#define ue_py_slate_farguments_tint(param, attribute) ue_py_slate_up(TOptional, GetterIntT>, param, attribute)\ + else if (PyNumber_Check(value)) {\ + PyObject *py_int = PyNumber_Long(value);\ + arguments.attribute((TOptional)PyLong_AsLong(py_int)); \ + Py_DECREF(py_int);\ + }\ + ue_py_slate_down(param) #define ue_py_slate_farguments_enum(param, attribute, _type) ue_py_slate_up(_type, GetterIntT<_type>, param, attribute)\ @@ -276,7 +304,6 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); if (value) {\ if (_type *u_struct = ue_py_check_struct<_type>(value)) {\ Py_INCREF(value);\ - ((ue_PySWidget *)self)->py_refs.Add(value);\ arguments.attribute((_type *)u_struct);\ }\ else {\ @@ -370,6 +397,12 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ } +#define ue_py_slate_farguments_optional_string(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + if (PyUnicode_Check(value)) {\ + arguments.attribute(UTF8_TO_TCHAR(PyUnicode_AsUTF8(value)));\ + }\ +} + #define ue_py_slate_farguments_optional_text(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ if (value) {\ if (PyUnicode_Check(value)) {\ @@ -382,6 +415,22 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); }\ } +#define ue_py_slate_farguments_optional_named_slot(param, attribute) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + if (value) {\ + if (ue_PySWidget *py_swidget = py_ue_is_swidget(value)) {\ + Py_INCREF(py_swidget);\ + arguments.attribute()\ + [\ + py_swidget->s_widget\ + ];\ + }\ + else {\ + PyErr_SetString(PyExc_TypeError, "unsupported type for attribute " param); \ + return -1;\ + }\ + }\ +} + #define ue_py_slate_farguments_bool(param, attribute) ue_py_slate_up(bool, GetterBool, param, attribute)\ else if (PyObject_IsTrue(value)) {\ @@ -407,7 +456,7 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); -#define ue_py_slate_farguments_event(param, attribute, type, method) ue_py_slate_base_up(type, method, param, attribute)\ +#define ue_py_slate_farguments_event(param, attribute, type, method) ue_py_slate_base_event_up(type, method, param, attribute)\ ue_py_slate_down(param) @@ -417,12 +466,27 @@ ue_PySWidget *ue_py_get_swidget(TSharedRef s_widget); ue_py_slate_farguments_text("tool_tip_text", ToolTipText);\ ue_py_slate_farguments_fvector2d("render_transform_pivot", RenderTransformPivot) +#define ue_py_slate_farguments_required_slot(param) { PyObject *value = ue_py_dict_get_item(kwargs, param);\ + value = value ? value : PyTuple_GetItem(args, 0);\ + if (ue_PySWidget *py_swidget = value ? py_ue_is_swidget(value) : nullptr) {\ + Py_INCREF(py_swidget);\ + ue_PySWidget *self_py_swidget = py_ue_is_swidget((PyObject*)self);\ + arguments.AttachWidget(py_swidget->s_widget->AsShared());\ + }\ + else {\ + PyErr_SetString(PyExc_TypeError, "unsupported type for required slot " param); \ + return -1;\ + }\ +} + +#define ue_py_slate_setup_hack_slot_args(_type, _swidget_ref) _type::FSlot &arguments = _swidget_ref->AddSlot();\ + ue_py_slate_farguments_required_slot("widget"); void ue_python_init_slate(PyObject *); struct FPythonItem { - PyObject *py_object; + PyObject *py_object = nullptr; FPythonItem(PyObject *item) { @@ -430,10 +494,9 @@ struct FPythonItem } }; -UCLASS() -class UPythonSlateDelegate : public UPythonDelegate + +class FPythonSlateDelegate : public FPythonSmartDelegate { - GENERATED_BODY() public: FReply OnMouseEvent(const FGeometry &geometry, const FPointerEvent &pointer_event); @@ -442,8 +505,11 @@ class UPythonSlateDelegate : public UPythonDelegate FReply OnKeyDown(const FGeometry &geometry, const FKeyEvent &key_event); void OnTextChanged(const FText &text); void OnTextCommitted(const FText &text, ETextCommit::Type commit_type); + void OnInt32Changed(int32 value); + void OnInt32Committed(int32 value, ETextCommit::Type commit_type); void OnFloatChanged(float value); void OnFloatCommitted(float value, ETextCommit::Type commit_type); + void OnSort(const EColumnSortPriority::Type SortPriority, const FName& ColumnName, const EColumnSortMode::Type NewSortMode); void OnLinearColorChanged(FLinearColor color); @@ -468,6 +534,7 @@ class UPythonSlateDelegate : public UPythonDelegate TSharedPtr OnContextMenuOpening(); TSharedRef OnGenerateWidget(TSharedPtr py_item); + TSharedRef OnGetMenuContent(); void OnSelectionChanged(TSharedPtr py_item, ESelectInfo::Type select_type); void SimpleExecuteAction(); diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp new file mode 100644 index 000000000..21935b116 --- /dev/null +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp @@ -0,0 +1,223 @@ + +#include "UnrealEnginePythonPrivatePCH.h" + +#include "UEPyFSlateApplication.h" + + +static PyObject *py_ue_get_average_delta_time(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetAverageDeltaTime()); +} + +static PyObject *py_ue_get_delta_time(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetDeltaTime()); +} + +static PyObject *py_ue_get_modifier_keys(PyObject *cls, PyObject * args) +{ + return py_ue_new_fmodifier_keys_state(FSlateApplication::Get().GetModifierKeys()); +} + +static PyObject *py_ue_get_cursor_radius(PyObject *cls, PyObject * args) +{ + return PyFloat_FromDouble(FSlateApplication::Get().GetCursorRadius()); +} + +static PyObject *py_ue_goto_line_in_source(PyObject *cls, PyObject * args) +{ + char *filename; + int line_number; + if (!PyArg_ParseTuple(args, "si:goto_line_in_source", &filename, &line_number)) + { + return nullptr; + } + + FSlateApplication::Get().GotoLineInSource(FString(UTF8_TO_TCHAR(filename)), line_number); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_is_gamepad_attached(PyObject *cls, PyObject * args) +{ + bool bAttached = FSlateApplication::Get().IsGamepadAttached(); + if (bAttached) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject *py_ue_is_mouse_attached(PyObject *cls, PyObject * args) +{ + bool bAttached = FSlateApplication::Get().IsMouseAttached(); + if (bAttached) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +static PyObject *py_ue_set_all_user_focus(PyObject *cls, PyObject * args) +{ + PyObject *py_widget; + int focus_cause = (int)EFocusCause::SetDirectly; + + if (!PyArg_ParseTuple(args, "O|i:set_all_user_focus", &py_widget, &focus_cause)) + { + return nullptr; + } + + ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); + if (!py_swidget) + { + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + } + + TSharedPtr widget_ptr(py_swidget->s_widget); + + FSlateApplication::Get().SetAllUserFocus(widget_ptr, (EFocusCause)focus_cause); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_set_application_scale(PyObject *cls, PyObject * args) +{ + float scale; + if (!PyArg_ParseTuple(args, "f:set_application_scale", &scale)) + { + return nullptr; + } + + FSlateApplication::Get().SetApplicationScale(scale); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_set_cursor_pos(PyObject *cls, PyObject * args) +{ + float x; + float y; + if (!PyArg_ParseTuple(args, "(ff):set_cursor_pos", &x, &y)) + { + return nullptr; + } + + FSlateApplication::Get().SetCursorPos(FVector2D(x, y)); + + Py_RETURN_NONE; +} + +static PyObject *py_ue_process_key_down_event(PyObject *cls, PyObject * args) +{ + PyObject *py_event; + if (!PyArg_ParseTuple(args, "O:process_key_down_event", &py_event)) + { + return nullptr; + } + + FKeyEvent *InKeyEvent = ue_py_check_struct(py_event); + if (!InKeyEvent) + { + ue_PyFKeyEvent *py_fkey_event = py_ue_is_fkey_event(py_event); + if (!py_fkey_event) + { + return PyErr_Format(PyExc_Exception, "argument is not a FKeyEvent"); + } + InKeyEvent = &py_fkey_event->key_event; + } + + if (FSlateApplication::Get().ProcessKeyDownEvent(*InKeyEvent)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyObject *py_ue_process_key_char_event(PyObject *cls, PyObject * args) +{ + PyObject *py_event; + if (!PyArg_ParseTuple(args, "O:process_key_char_event", &py_event)) + { + return nullptr; + } + + FCharacterEvent *InCharEvent = ue_py_check_struct(py_event); + if (!InCharEvent) + { + ue_PyFCharacterEvent *py_fchar_event = py_ue_is_fcharacter_event(py_event); + if (!py_fchar_event) + { + return PyErr_Format(PyExc_Exception, "argument is not a FCharacterEvent"); + } + InCharEvent = &py_fchar_event->character_event; + } + + if (FSlateApplication::Get().ProcessKeyCharEvent(*InCharEvent)) + Py_RETURN_TRUE; + + Py_RETURN_FALSE; +} + +static PyMethodDef ue_PyFSlateApplication_methods[] = { + { "get_average_delta_time", (PyCFunction)py_ue_get_average_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "get_cursor_radius", (PyCFunction)py_ue_get_cursor_radius, METH_VARARGS | METH_CLASS, "" }, + { "get_delta_time", (PyCFunction)py_ue_get_delta_time, METH_VARARGS | METH_CLASS, "" }, + { "get_modifier_keys", (PyCFunction)py_ue_get_modifier_keys, METH_VARARGS | METH_CLASS, "" }, + { "goto_line_in_source", (PyCFunction)py_ue_goto_line_in_source, METH_VARARGS | METH_CLASS, "" }, + { "is_gamepad_attached", (PyCFunction)py_ue_is_gamepad_attached, METH_VARARGS | METH_CLASS, "" }, + { "is_mouse_attached", (PyCFunction)py_ue_is_mouse_attached, METH_VARARGS | METH_CLASS, "" }, + { "process_key_down_event", (PyCFunction)py_ue_process_key_down_event, METH_VARARGS | METH_CLASS, "" }, + { "process_key_char_event", (PyCFunction)py_ue_process_key_char_event, METH_VARARGS | METH_CLASS, "" }, + { "set_application_scale", (PyCFunction)py_ue_set_application_scale, METH_VARARGS | METH_CLASS, "" }, + { "set_all_user_focus", (PyCFunction)py_ue_set_all_user_focus, METH_VARARGS | METH_CLASS, "" }, + { "set_cursor_pos", (PyCFunction)py_ue_set_cursor_pos, METH_VARARGS | METH_CLASS, "" }, + { NULL } /* Sentinel */ +}; + + +static PyTypeObject ue_PyFSlateApplicationType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FSlateApplication", /* tp_name */ + sizeof(ue_PyFSlateApplication), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine SlateApplication", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFSlateApplication_methods, /* tp_methods */ + 0, + 0, +}; + +static int py_ue_fslate_application_init(ue_PyFSlateApplication *self, PyObject * args) +{ + PyErr_SetString(PyExc_Exception, "FSlateApplication is a singleton"); + return -1; +} + +void ue_python_init_fslate_application(PyObject *ue_module) +{ + ue_PyFSlateApplicationType.tp_new = PyType_GenericNew; + ue_PyFSlateApplicationType.tp_init = (initproc)py_ue_fslate_application_init; + + if (PyType_Ready(&ue_PyFSlateApplicationType) < 0) + return; + + Py_INCREF(&ue_PyFSlateApplicationType); + PyModule_AddObject(ue_module, "FSlateApplication", (PyObject *)&ue_PyFSlateApplicationType); +} diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h new file mode 100644 index 000000000..8c9caa4f3 --- /dev/null +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.h @@ -0,0 +1,11 @@ +#pragma once + +#include "UnrealEnginePython.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ +} ue_PyFSlateApplication; + +void ue_python_init_fslate_application(PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp index 808b66929..1ca8e7d13 100644 --- a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp +++ b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp @@ -2,20 +2,23 @@ #if WITH_EDITOR -PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); - if (!u_property) { + if (!u_property) + { return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); - + } UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); FAssetImportInfo *import_info = &import_data->SourceData; PyObject *ret = PyList_New(import_info->SourceFiles.Num()); - for (int i=0; i < import_info->SourceFiles.Num(); i++) { + for (int i = 0; i < import_info->SourceFiles.Num(); i++) + { PyObject *py_source_file = PyDict_New(); PyDict_SetItemString(py_source_file, "absolute_filepath", PyUnicode_FromString(TCHAR_TO_UTF8(*import_data->ResolveImportFilename(import_info->SourceFiles[i].RelativeFilename, NULL)))); PyDict_SetItemString(py_source_file, "relative_filepath", PyUnicode_FromString(TCHAR_TO_UTF8(*import_info->SourceFiles[i].RelativeFilename))); @@ -25,4 +28,65 @@ PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) { } return ret; } + +PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_files; + if (!PyArg_ParseTuple(args, "O:asset_import_data_set_sources", &py_files)) + { + return nullptr; + } + + TArray filenames; + + UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); + UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); + if (!u_property) + { + return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); + } + + if (PyUnicode_Check(py_files)) + { + filenames.Add(FString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_files)))); + } + else + { + PyObject *py_iter = PyObject_GetIter(py_files); + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not a string or an interable of strings"); + } + + while (PyObject *py_item = PyIter_Next(py_iter)) + { + if (!PyUnicode_Check(py_item)) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not a string or an interable of strings"); + } + filenames.Add(FString(UTF8_TO_TCHAR(PyUnicode_AsUTF8(py_item)))); + } + + Py_DECREF(py_iter); + } + + + UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); + FAssetImportInfo *import_info = &import_data->SourceData; + + TArray sources; + + for (FString filename : filenames) + { + sources.Add(FAssetImportInfo::FSourceFile(filename)); + } + + import_info->SourceFiles = sources; + + Py_RETURN_NONE; +} #endif diff --git a/Source/UnrealEnginePython/Private/UEPyAssetUserData.h b/Source/UnrealEnginePython/Private/UEPyAssetUserData.h index 09dcce21a..9506d13c3 100644 --- a/Source/UnrealEnginePython/Private/UEPyAssetUserData.h +++ b/Source/UnrealEnginePython/Private/UEPyAssetUserData.h @@ -6,4 +6,5 @@ #if WITH_EDITOR PyObject *py_ue_asset_import_data(ue_PyUObject *, PyObject *); +PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index fa96aecb2..50ff5b6d0 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -26,6 +26,7 @@ #include "Editor/LandscapeEditor/Public/LandscapeFileFormatInterface.h" #include "Developer/Settings/Public/ISettingsModule.h" +#include "Engine/Blueprint.h" PyObject *py_unreal_engine_editor_play_in_viewport(PyObject * self, PyObject * args) @@ -69,6 +70,26 @@ PyObject *py_unreal_engine_editor_play_in_viewport(PyObject * self, PyObject * a } +PyObject *py_unreal_engine_request_play_session(PyObject * self, PyObject * args) +{ + + PyObject *py_at_player_start = nullptr; + PyObject *py_simulate_in_editor = nullptr; + + if (!PyArg_ParseTuple(args, "|OO:request_play_session", &py_at_player_start, &py_simulate_in_editor)) + { + return nullptr; + } + + bool bAtPlayerStart = py_at_player_start && PyObject_IsTrue(py_at_player_start); + bool bSimulate = py_simulate_in_editor && PyObject_IsTrue(py_simulate_in_editor); + + GEditor->RequestPlaySession(bAtPlayerStart, nullptr, bSimulate); + + Py_RETURN_NONE; + +} + PyObject *py_unreal_engine_get_editor_world(PyObject * self, PyObject * args) { @@ -77,11 +98,7 @@ PyObject *py_unreal_engine_get_editor_world(PyObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "no GEditor found"); UWorld *world = GEditor->GetEditorWorldContext().World(); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(world); } PyObject *py_unreal_engine_console_exec(PyObject * self, PyObject * args) @@ -99,8 +116,7 @@ PyObject *py_unreal_engine_console_exec(PyObject * self, PyObject * args) GEditor->Exec(GEditor->GetEditorWorldContext().World(), UTF8_TO_TCHAR(command), *GLog); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_unreal_engine_allow_actor_script_execution_in_editor(PyObject * self, PyObject * args) @@ -143,7 +159,7 @@ PyObject *py_unreal_engine_editor_get_selected_actors(PyObject * self, PyObject if (!obj->IsA()) continue; AActor *actor = (AActor *)obj; - ue_PyUObject *item = ue_get_python_wrapper(actor); + ue_PyUObject *item = ue_get_python_uobject(actor); if (item) PyList_Append(actors, (PyObject *)item); } @@ -344,9 +360,11 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); if (u_class) { - ue_PyUObject *py_obj = ue_get_python_wrapper(u_class); - if (py_obj) - Py_INCREF(py_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_class); + if (!py_obj) + { + return PyErr_Format(PyExc_Exception, "invalid uobject"); + } factory_class = (UClass *)py_obj->ue_object; } } @@ -416,11 +434,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) { UObject *object = objects[0]; - ue_PyUObject *ret = ue_get_python_wrapper(object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(object); } else if (objects.Num() > 1) { @@ -428,9 +442,13 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) for (UObject *object : objects) { - ue_PyUObject *ret = ue_get_python_wrapper(object); + ue_PyUObject *ret = ue_get_python_uobject(object); if (!ret) + { + Py_DECREF(assets_list); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); + } + PyList_Append(assets_list, (PyObject *)ret); } @@ -438,8 +456,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_unreal_engine_editor_tick(PyObject * self, PyObject * args) @@ -520,11 +537,7 @@ PyObject *py_unreal_engine_get_asset(PyObject * self, PyObject * args) FAssetData asset = AssetRegistryModule.Get().GetAssetByObjectPath(UTF8_TO_TCHAR(path)); if (!asset.IsValid()) return PyErr_Format(PyExc_Exception, "unable to find asset %s", path); - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(asset.GetAsset()); } PyObject *py_unreal_engine_find_asset(PyObject * self, PyObject * args) @@ -546,11 +559,7 @@ PyObject *py_unreal_engine_find_asset(PyObject * self, PyObject * args) Py_INCREF(Py_None); return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(asset.GetAsset()); } PyObject *py_unreal_engine_get_asset_referencers(PyObject * self, PyObject * args) @@ -685,11 +694,7 @@ PyObject *py_unreal_engine_duplicate_asset(PyObject * self, PyObject * args) return PyErr_Format(PyExc_Exception, "unable to duplicate asset %s", path); } - ue_PyUObject *ret = ue_get_python_wrapper(new_asset); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_asset); } PyObject *py_unreal_engine_delete_asset(PyObject * self, PyObject * args) @@ -800,7 +805,7 @@ PyObject *py_unreal_engine_get_assets(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -851,8 +856,9 @@ PyObject *py_unreal_engine_get_assets_by_filter(PyObject * self, PyObject * args } else { - ret = (PyObject *)ue_get_python_wrapper(asset.GetAsset()); + ret = (PyObject *)ue_get_python_uobject(asset.GetAsset()); } + if (ret) { PyList_Append(assets_list, ret); @@ -950,7 +956,7 @@ PyObject *py_unreal_engine_get_assets_by_class(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -978,7 +984,7 @@ PyObject *py_unreal_engine_get_selected_assets(PyObject * self, PyObject * args) { if (!asset.IsValid()) continue; - ue_PyUObject *ret = ue_get_python_wrapper(asset.GetAsset()); + ue_PyUObject *ret = ue_get_python_uobject(asset.GetAsset()); if (ret) { PyList_Append(assets_list, (PyObject *)ret); @@ -1112,14 +1118,37 @@ PyObject *py_unreal_engine_create_blueprint(PyObject * self, PyObject * args) outer->MarkPackageDirty(); - ue_PyUObject *ret = ue_get_python_wrapper(bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(bp); } +PyObject *py_unreal_engine_get_blueprint_hierarchy_from_class(PyObject * self, PyObject * args) +{ + PyObject *py_class; + if (!PyArg_ParseTuple(args, "O:get_blueprint_hierarchy_from_class", &py_class)) + { + return NULL; + } + + UClass* u_class = ue_py_check_type(py_class); + if (!u_class) + { + return PyErr_Format(PyExc_Exception, "argument is not a UClass"); + } + + + TArray outBPs; + UBlueprint::GetBlueprintHierarchyFromClass(u_class, outBPs); + + PyObject *py_bpClasses = PyList_New(0); + + for (UBlueprint* bpClass : outBPs) + { + ue_PyUObject *item = ue_get_python_uobject(bpClass); + if (item) + PyList_Append(py_bpClasses, (PyObject *)item); + } + return py_bpClasses; +} PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) { @@ -1142,12 +1171,7 @@ PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) UBlueprint *reloaded_bp = FKismetEditorUtilities::ReloadBlueprint(bp); - ue_PyUObject *ret = ue_get_python_wrapper(reloaded_bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(reloaded_bp); } PyObject *py_unreal_engine_compile_blueprint(PyObject * self, PyObject * args) @@ -1208,12 +1232,7 @@ PyObject *py_unreal_engine_replace_blueprint(PyObject * self, PyObject * args) UBlueprint *replaced_bp = FKismetEditorUtilities::ReplaceBlueprint(bp, bp_new); - ue_PyUObject *ret = ue_get_python_wrapper(replaced_bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(replaced_bp); } PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject * self, PyObject * args) @@ -1242,12 +1261,7 @@ PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject * self, PyObject UBlueprint *bp = FKismetEditorUtilities::CreateBlueprintFromActor(UTF8_TO_TCHAR(name), actor, true); - ue_PyUObject *ret = ue_get_python_wrapper(bp); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(bp); } PyObject *py_unreal_engine_get_blueprint_components(PyObject * self, PyObject * args) @@ -1269,7 +1283,7 @@ PyObject *py_unreal_engine_get_blueprint_components(PyObject * self, PyObject * for (USCS_Node *node : bp->SimpleConstructionScript->GetAllNodes()) { - ue_PyUObject *item = ue_get_python_wrapper(node->ComponentTemplate); + ue_PyUObject *item = ue_get_python_uobject(node->ComponentTemplate); if (item) PyList_Append(py_list, (PyObject *)item); @@ -1351,12 +1365,7 @@ PyObject *py_unreal_engine_add_component_to_blueprint(PyObject * self, PyObject bp->SimpleConstructionScript->AddNode(node); } - ue_PyUObject *ret = ue_get_python_wrapper(node->ComponentTemplate); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(node->ComponentTemplate); } PyObject *py_unreal_engine_blueprint_add_member_variable(PyObject * self, PyObject * args) @@ -1465,11 +1474,7 @@ PyObject *py_unreal_engine_blueprint_add_new_timeline(PyObject * self, PyObject return PyErr_Format(PyExc_Exception, "unable to add new timeline %s", name); } - ue_PyUObject *ret = ue_get_python_wrapper(timeline); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(timeline); } PyObject *py_unreal_engine_blueprint_add_function(PyObject * self, PyObject * args) @@ -1495,11 +1500,7 @@ PyObject *py_unreal_engine_blueprint_add_function(PyObject * self, PyObject * ar UEdGraph *graph = FBlueprintEditorUtils::CreateNewGraph(bp, FName(UTF8_TO_TCHAR(name)), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddFunctionGraph(bp, graph, true, nullptr); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); } PyObject *py_unreal_engine_blueprint_add_event_dispatcher(PyObject * self, PyObject * args) @@ -1546,11 +1547,7 @@ PyObject *py_unreal_engine_blueprint_add_event_dispatcher(PyObject * self, PyObj bp->DelegateSignatureGraphs.Add(graph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); } PyObject *py_unreal_engine_blueprint_mark_as_structurally_modified(PyObject * self, PyObject * args) @@ -1595,11 +1592,36 @@ PyObject *py_unreal_engine_blueprint_add_ubergraph_page(PyObject * self, PyObjec UEdGraph *graph = FBlueprintEditorUtils::CreateNewGraph(bp, FName(UTF8_TO_TCHAR(name)), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); FBlueprintEditorUtils::AddUbergraphPage(bp, graph); - PyObject *ret = (PyObject *)ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(graph); +} + +PyObject *py_unreal_engine_blueprint_get_all_graphs(PyObject * self, PyObject * args) +{ + PyObject *py_blueprint; + + if (!PyArg_ParseTuple(args, "O:blueprint_get_all_graphs", &py_blueprint)) + { + return nullptr; + } + + UBlueprint *bp = ue_py_check_type(py_blueprint); + if (!bp) + return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); + + PyObject *py_graphs = PyList_New(0); + + TArray graphs; + + bp->GetAllGraphs(graphs); + + for (UEdGraph *graph : graphs) + { + ue_PyUObject *item = ue_get_python_uobject(graph); + if (item) + PyList_Append(py_graphs, (PyObject *)item); + } + + return py_graphs; } PyObject *py_unreal_engine_create_new_graph(PyObject * self, PyObject * args) @@ -1665,35 +1687,6 @@ PyObject *py_unreal_engine_create_new_graph(PyObject * self, PyObject * args) Py_RETURN_UOBJECT(graph); } -PyObject *py_unreal_engine_editor_blueprint_graphs(PyObject * self, PyObject * args) -{ - PyObject *py_blueprint; - - if (!PyArg_ParseTuple(args, "O:blueprint_graphs", &py_blueprint)) - { - return nullptr; - } - - UBlueprint *bp = ue_py_check_type(py_blueprint); - if (!bp) - return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); - - PyObject *py_graphs = PyList_New(0); - - TArray graphs; - - bp->GetAllGraphs(graphs); - - for (UEdGraph *graph : graphs) - { - ue_PyUObject *item = ue_get_python_wrapper(graph); - if (item) - PyList_Append(py_graphs, (PyObject *)item); - } - - return py_graphs; -} - PyObject *py_unreal_engine_editor_on_asset_post_import(PyObject * self, PyObject * args) { PyObject *py_callable; @@ -1705,12 +1698,10 @@ PyObject *py_unreal_engine_editor_on_asset_post_import(PyObject * self, PyObject if (!PyCallable_Check(py_callable)) return PyErr_Format(PyExc_Exception, "object is not a callable"); - UPythonDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - FEditorDelegates::OnAssetPostImport.AddUObject(py_delegate, &UPythonDelegate::PyFOnAssetPostImport); - Py_INCREF(Py_None); - return Py_None; + FEditorDelegates::OnAssetPostImport.AddSP(py_delegate, &FPythonSmartDelegate::PyFOnAssetPostImport); + Py_RETURN_NONE; } PyObject *py_unreal_engine_create_material_instance(PyObject * self, PyObject * args) @@ -2237,6 +2228,27 @@ PyObject *py_unreal_engine_register_settings(PyObject * self, PyObject * args) Py_RETURN_NONE; } +PyObject * py_unreal_engine_show_viewer(PyObject * self, PyObject * args) +{ + char *container_name; + char *category_name; + char *section_name; + + if (!PyArg_ParseTuple(args, "sss:register_settings", &container_name, &category_name, §ion_name)) + return nullptr; + + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->ShowViewer(container_name, category_name, section_name); + } + else + { + return PyErr_Format(PyExc_Exception, "unable to find the Settings Module"); + } + + Py_RETURN_NONE; +} + PyObject *py_unreal_engine_unregister_settings(PyObject * self, PyObject * args) { char *container_name; @@ -2322,5 +2334,48 @@ PyObject *py_unreal_engine_editor_sync_browser_to_assets(PyObject * self, PyObje Py_RETURN_NONE; } + +PyObject *py_unreal_engine_export_assets(PyObject * self, PyObject * args) +{ + + if (!GEditor) + return PyErr_Format(PyExc_Exception, "no GEditor found"); + + PyObject * py_assets = nullptr; + char *filename; + + if (!PyArg_ParseTuple(args, "Os:export_assets", &py_assets, &filename)) + { + return nullptr; + } + + TArray UObjects; + PyObject *py_iter = PyObject_GetIter(py_assets); + + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UObject"); + } + + while (PyObject *py_item = PyIter_Next(py_iter)) + { + UObject *Object = ue_py_check_type(py_item); + if (!Object) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UObject"); + } + UObjects.Add(Object); + } + + Py_DECREF(py_iter); + + FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); +#if ENGINE_MINOR_VERSION > 16 + AssetToolsModule.Get().ExportAssets(UObjects, FString(UTF8_TO_TCHAR(filename))); +#endif + + Py_RETURN_NONE; +} #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index fa76df038..36e24c6ad 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -40,6 +40,7 @@ PyObject *py_unreal_engine_get_long_package_path(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_compile_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_message_dialog_open(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_blueprint_hierarchy_from_class(PyObject *, PyObject *); PyObject *py_unreal_engine_reload_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_replace_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject *, PyObject *); @@ -75,6 +76,7 @@ PyObject *py_unreal_engine_editor_take_high_res_screen_shots(PyObject *, PyObjec PyObject *py_unreal_engine_blueprint_add_function(PyObject *, PyObject *); PyObject *py_unreal_engine_blueprint_add_ubergraph_page(PyObject *, PyObject *); +PyObject *py_unreal_engine_blueprint_get_all_graphs(PyObject *, PyObject *); PyObject *py_unreal_engine_create_new_graph(PyObject *, PyObject *); @@ -114,5 +116,9 @@ PyObject *py_unreal_engine_heightmap_import(PyObject *, PyObject *); PyObject *py_unreal_engine_play_preview_sound(PyObject *, PyObject *); PyObject *py_unreal_engine_register_settings(PyObject *, PyObject *); +PyObject *py_unreal_engine_show_viewer(PyObject *, PyObject *); PyObject *py_unreal_engine_unregister_settings(PyObject *, PyObject *); + +PyObject *py_unreal_engine_request_play_session(PyObject *, PyObject *); +PyObject *py_unreal_engine_export_assets(PyObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 8f4017ab9..aa807ea51 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #include "UnrealEnginePythonPrivatePCH.h" #include "Kismet/KismetSystemLibrary.h" @@ -5,7 +7,13 @@ #include "Developer/DesktopPlatform/Public/IDesktopPlatform.h" #include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" +#if WITH_EDITOR +#include "PackageTools.h" +#endif +#if ENGINE_MINOR_VERSION >= 18 +#include "HAL/PlatformApplicationMisc.h" +#endif PyObject *py_unreal_engine_log(PyObject * self, PyObject * args) @@ -161,7 +169,25 @@ PyObject *py_unreal_engine_get_up_vector(PyObject * self, PyObject * args) PyObject *py_unreal_engine_get_content_dir(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION >= 18 return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::ProjectContentDir())); +#else + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameContentDir())); +#endif +} + +PyObject *py_unreal_engine_get_game_saved_dir(PyObject * self, PyObject * args) +{ +#if ENGINE_MINOR_VERSION >= 18 + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::ProjectSavedDir())); +#else + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameSavedDir())); +#endif +} + +PyObject * py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *) +{ + return PyUnicode_FromString(TCHAR_TO_UTF8(*FPaths::GameUserDeveloperDir())); } PyObject *py_unreal_engine_convert_relative_path_to_full(PyObject * self, PyObject * args) @@ -214,11 +240,7 @@ PyObject *py_unreal_engine_create_world(PyObject * self, PyObject * args) UWorld *world = UWorld::CreateWorld((EWorldType::Type)world_type, false); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(world); } PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) @@ -234,11 +256,7 @@ PyObject *py_unreal_engine_find_class(PyObject * self, PyObject * args) if (!u_class) return PyErr_Format(PyExc_Exception, "unable to find class %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_class); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class); } PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) @@ -254,12 +272,7 @@ PyObject *py_unreal_engine_find_enum(PyObject * self, PyObject * args) if (!u_enum) return PyErr_Format(PyExc_Exception, "unable to find enum %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_enum); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_enum); } PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) @@ -275,13 +288,34 @@ PyObject *py_unreal_engine_load_package(PyObject * self, PyObject * args) if (!u_package) return PyErr_Format(PyExc_Exception, "unable to load package %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } +#if WITH_EDITOR +PyObject *py_unreal_engine_unload_package(PyObject * self, PyObject * args) +{ + PyObject *obj; + if (!PyArg_ParseTuple(args, "O:unload_package", &obj)) + { + return NULL; + } + + UPackage* packageToUnload = ue_py_check_type(obj); + if (!packageToUnload) + { + return PyErr_Format(PyExc_Exception, "argument is not a UPackage"); + } + + FText outErrorMsg; + if (!PackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) + { + return PyErr_Format(PyExc_Exception, TCHAR_TO_UTF8(*outErrorMsg.ToString())); + } + + Py_RETURN_NONE; +} +#endif + PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) { char *name; @@ -300,11 +334,7 @@ PyObject *py_unreal_engine_load_class(PyObject * self, PyObject * args) if (!u_class) return PyErr_Format(PyExc_Exception, "unable to find class %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_class); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class); } PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) @@ -325,11 +355,7 @@ PyObject *py_unreal_engine_load_enum(PyObject * self, PyObject * args) if (!u_enum) return PyErr_Format(PyExc_Exception, "unable to find enum %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_enum); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_enum); } PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) @@ -345,12 +371,7 @@ PyObject *py_unreal_engine_find_struct(PyObject * self, PyObject * args) if (!u_struct) return PyErr_Format(PyExc_Exception, "unable to find struct %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_struct); } PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) @@ -371,12 +392,7 @@ PyObject *py_unreal_engine_load_struct(PyObject * self, PyObject * args) if (!u_struct) return PyErr_Format(PyExc_Exception, "unable to find struct %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(u_struct); } @@ -412,11 +428,7 @@ PyObject *py_unreal_engine_load_object(PyObject * self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to load object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } @@ -501,11 +513,7 @@ PyObject *py_unreal_engine_find_object(PyObject * self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to find object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } @@ -515,7 +523,8 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) PyObject *obj; PyObject *py_outer = NULL; char *name = nullptr; - if (!PyArg_ParseTuple(args, "O|Os:new_object", &obj, &py_outer, &name)) + uint64 flags = (uint64)(RF_Public); + if (!PyArg_ParseTuple(args, "O|OsK:new_object", &obj, &py_outer, &name, &flags)) { return NULL; } @@ -553,17 +562,13 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) outer = py_outer_obj->ue_object; } - UObject *new_object = NewObject(outer, obj_class, f_name, RF_Public | RF_Standalone); + UObject *new_object = NewObject(outer, obj_class, f_name, (EObjectFlags)flags); if (!new_object) return PyErr_Format(PyExc_Exception, "unable to create object"); new_object->PostLoad(); - ue_PyUObject *ret = ue_get_python_wrapper(new_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_object); } PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) @@ -591,11 +596,7 @@ PyObject *py_unreal_engine_get_mutable_default(PyObject * self, PyObject * args) if (!mutable_object) return PyErr_Format(PyExc_Exception, "unable to create object"); - ue_PyUObject *ret = ue_get_python_wrapper(mutable_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(mutable_object); } @@ -627,11 +628,7 @@ PyObject *py_unreal_engine_new_class(PyObject * self, PyObject * args) if (!new_object) return PyErr_Format(PyExc_Exception, "unable to create UClass"); - ue_PyUObject *ret = ue_get_python_wrapper(new_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(new_object); } PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) @@ -641,7 +638,7 @@ PyObject *py_unreal_engine_all_classes(PyObject * self, PyObject * args) for (TObjectIterator Itr; Itr; ++Itr) { - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -654,7 +651,7 @@ PyObject *py_unreal_engine_all_worlds(PyObject * self, PyObject * args) PyObject *ret = PyList_New(0); for (TObjectIterator Itr; Itr; ++Itr) { - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -683,7 +680,7 @@ PyObject *py_unreal_engine_tobject_iterator(PyObject * self, PyObject * args) if (!(*Itr)->IsA(u_class)) continue; - ue_PyUObject *py_obj = ue_get_python_wrapper(*Itr); + ue_PyUObject *py_obj = ue_get_python_uobject(*Itr); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -1043,11 +1040,7 @@ PyObject *py_unreal_engine_create_package(PyObject *self, PyObject * args) u_package->FullyLoad(); u_package->MarkPackageDirty(); - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args) @@ -1073,21 +1066,12 @@ PyObject *py_unreal_engine_get_or_create_package(PyObject *self, PyObject * args u_package->MarkPackageDirty(); } - ue_PyUObject *ret = ue_get_python_wrapper(u_package); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_package); } PyObject *py_unreal_engine_get_transient_package(PyObject *self, PyObject * args) { - - ue_PyUObject *ret = ue_get_python_wrapper(GetTransientPackage()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(GetTransientPackage()); } PyObject *py_unreal_engine_open_file_dialog(PyObject *self, PyObject * args) @@ -1231,7 +1215,6 @@ PyObject *py_unreal_engine_save_file_dialog(PyObject *self, PyObject * args) return py_list; } - PyObject *py_unreal_engine_copy_properties_for_unrelated_objects(PyObject * self, PyObject * args, PyObject *kwargs) { @@ -1312,4 +1295,31 @@ PyObject *py_unreal_engine_set_random_seed(PyObject * self, PyObject * args) FGenericPlatformMath::RandInit(seed); Py_RETURN_NONE; +} + +PyObject *py_unreal_engine_clipboard_copy(PyObject * self, PyObject * args) +{ + char *text; + if (!PyArg_ParseTuple(args, "s:clipboard_copy", &text)) + { + return nullptr; + } + +#if ENGINE_MINOR_VERSION >= 18 + FPlatformApplicationMisc::ClipboardCopy(UTF8_TO_TCHAR(text)); +#else + FGenericPlatformMisc::ClipboardCopy(UTF8_TO_TCHAR(text)); +#endif + Py_RETURN_NONE; +} + +PyObject *py_unreal_engine_clipboard_paste(PyObject * self, PyObject * args) +{ + FString clipboard; +#if ENGINE_MINOR_VERSION >= 18 + FPlatformApplicationMisc::ClipboardPaste(clipboard); +#else + FGenericPlatformMisc::ClipboardPaste(clipboard); +#endif + return PyUnicode_FromString(TCHAR_TO_UTF8(*clipboard)); } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index 93dd6c5b9..40e78f182 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -13,12 +13,17 @@ PyObject *py_unreal_engine_get_forward_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_right_vector(PyObject *, PyObject *); PyObject *py_unreal_engine_get_up_vector(PyObject *, PyObject *); +PyObject *py_unreal_engine_clipboard_copy(PyObject *, PyObject *); +PyObject *py_unreal_engine_clipboard_paste(PyObject *, PyObject *); + PyObject *py_unreal_engine_set_random_seed(PyObject *, PyObject *); PyObject *py_unreal_engine_get_game_viewport_size(PyObject *, PyObject *); PyObject *py_unreal_engine_get_resolution(PyObject *, PyObject *); PyObject *py_unreal_engine_get_content_dir(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_game_saved_dir(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_game_user_developer_dir(PyObject *, PyObject *); PyObject *py_unreal_engine_find_object(PyObject *, PyObject *); PyObject *py_unreal_engine_find_class(PyObject *, PyObject *); @@ -30,6 +35,9 @@ PyObject *py_unreal_engine_load_class(PyObject *, PyObject *); PyObject *py_unreal_engine_load_struct(PyObject *, PyObject *); PyObject *py_unreal_engine_load_enum(PyObject *, PyObject *); PyObject *py_unreal_engine_load_package(PyObject *, PyObject *); +#if WITH_EDITOR +PyObject *py_unreal_engine_unload_package(PyObject *, PyObject *); +#endif PyObject *py_unreal_engine_string_to_guid(PyObject *, PyObject *); PyObject *py_unreal_engine_new_guid(PyObject *, PyObject *); @@ -40,6 +48,8 @@ PyObject *py_unreal_engine_slate_tick(PyObject *, PyObject *); PyObject *py_unreal_engine_get_delta_time(PyObject *, PyObject *); PyObject *py_unreal_engine_all_classes(PyObject *, PyObject *); +PyObject *py_unreal_engine_all_worlds(PyObject *, PyObject *); +PyObject *py_unreal_engine_tobject_iterator(PyObject *, PyObject *); PyObject *py_unreal_engine_all_worlds(PyObject *, PyObject *); PyObject *py_unreal_engine_tobject_iterator(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp b/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp index 798c9c7eb..a502e6325 100644 --- a/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEnumsImporter.cpp @@ -1,25 +1,26 @@ #include "UnrealEnginePythonPrivatePCH.h" -static PyObject *ue_PyEnumsImporter_getattro(ue_PyEnumsImporter *self, PyObject *attr_name) { - PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); - if (!ret) { - if (PyUnicodeOrString_Check(attr_name)) { +static PyObject *ue_PyEnumsImporter_getattro(ue_PyEnumsImporter *self, PyObject *attr_name) +{ + PyObject *py_attr = PyObject_GenericGetAttr((PyObject *)self, attr_name); + if (!py_attr) + { + if (PyUnicodeOrString_Check(attr_name)) + { char *attr = PyUnicode_AsUTF8(attr_name); - if (attr[0] != '_') { + if (attr[0] != '_') + { UEnum *u_enum = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(attr)); - if (u_enum) { + if (u_enum) + { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_enum); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_enum); } } } } - return ret; + return py_attr; } static PyTypeObject ue_PyEnumsImporterType = { @@ -55,7 +56,8 @@ static PyTypeObject ue_PyEnumsImporterType = { 0, }; -void ue_python_init_enumsimporter(PyObject *ue_module) { +void ue_python_init_enumsimporter(PyObject *ue_module) +{ ue_PyEnumsImporterType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyEnumsImporterType) < 0) @@ -65,7 +67,8 @@ void ue_python_init_enumsimporter(PyObject *ue_module) { PyModule_AddObject(ue_module, "EnumsImporter", (PyObject *)&ue_PyEnumsImporterType); } -PyObject *py_ue_new_enumsimporter() { +PyObject *py_ue_new_enumsimporter() +{ ue_PyEnumsImporter *ret = (ue_PyEnumsImporter *)PyObject_New(ue_PyEnumsImporter, &ue_PyEnumsImporterType); return (PyObject *)ret; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp b/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp index 415724572..bee1b113a 100644 --- a/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp +++ b/Source/UnrealEnginePython/Private/UEPyIPlugin.cpp @@ -245,12 +245,10 @@ static PyObject *py_ue_iplugin_get_requires_build_platform(ue_PyIPlugin *self, v { if (self->plugin->GetDescriptor().bRequiresBuildPlatform) { - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } static PyGetSetDef ue_PyIPlugin_getseters[] = { diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 77f510941..0f9236f85 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1,7 +1,5 @@ #include "UnrealEnginePythonPrivatePCH.h" - - #include "UEPyEngine.h" #include "UEPyTimer.h" #include "UEPyTicker.h" @@ -37,6 +35,7 @@ #include "UObject/UEPyLandscape.h" #include "UObject/UEPyUserDefinedStruct.h" #include "UObject/UEPyDataTable.h" +#include "UObject/UEPyExporter.h" #include "UEPyAssetUserData.h" @@ -68,6 +67,7 @@ DEFINE_LOG_CATEGORY(LogPython); PyDoc_STRVAR(unreal_engine_py_doc, "Unreal Engine Python module."); + #if PY_MAJOR_VERSION >= 3 static PyModuleDef unreal_engine_module = { PyModuleDef_HEAD_INIT, @@ -76,40 +76,21 @@ static PyModuleDef unreal_engine_module = { -1, NULL, }; +static PyObject *init_unreal_engine(void); -static PyObject *init_unreal_engine(void) + + +void init_unreal_engine_builtin() { - return PyModule_Create(&unreal_engine_module); + PyImport_AppendInittab("unreal_engine", &init_unreal_engine); } #endif -std::map ue_python_gc; - static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) { - std::list broken_list; - for (auto it : ue_python_gc) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), it.first); -#endif - UObject *u_obj = it.first; - if (!u_obj || !u_obj->IsValidLowLevel() || u_obj->IsPendingKillOrUnreachable()) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), it.first, it.second->ob_base.ob_refcnt); -#endif - broken_list.push_back(u_obj); - } - } - for (UObject *u_obj : broken_list) - { - ue_PyUObject *py_obj = ue_python_gc.at(u_obj); - Py_DECREF(py_obj); - } - - return PyLong_FromLong(broken_list.size()); + int32 Garbaged = FUnrealEnginePythonHouseKeeper::Get()->RunGC(); + return PyLong_FromLong(Garbaged); } @@ -135,8 +116,7 @@ static PyObject *py_unreal_engine_sandbox_exec(PyObject * self, PyObject * args) } FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); PythonModule.RunFileSandboxed(filename, nullptr, nullptr); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) @@ -147,11 +127,11 @@ static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) if (self->py_proxy) { Py_INCREF(self->py_proxy); + UE_LOG(LogPython, Error, TEXT("PROXY %d"), self->py_proxy->ob_refcnt); return (PyObject *)self->py_proxy; } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } static PyMethodDef unreal_engine_methods[] = { @@ -176,12 +156,16 @@ static PyMethodDef unreal_engine_methods[] = { { "load_object", py_unreal_engine_load_object, METH_VARARGS, "" }, { "load_package", py_unreal_engine_load_package, METH_VARARGS, "" }, - +#if WITH_EDITOR + { "unload_package", py_unreal_engine_unload_package, METH_VARARGS, "" }, +#endif { "get_forward_vector", py_unreal_engine_get_forward_vector, METH_VARARGS, "" }, { "get_up_vector", py_unreal_engine_get_up_vector, METH_VARARGS, "" }, { "get_right_vector", py_unreal_engine_get_right_vector, METH_VARARGS, "" }, { "get_content_dir", py_unreal_engine_get_content_dir, METH_VARARGS, "" }, + { "get_game_saved_dir", py_unreal_engine_get_game_saved_dir, METH_VARARGS, "" }, + { "get_game_user_developer_dir", py_unreal_engine_get_game_user_developer_dir, METH_VARARGS, "" }, { "convert_relative_path_to_full", py_unreal_engine_convert_relative_path_to_full, METH_VARARGS, "" }, { "get_path", py_unreal_engine_get_path, METH_VARARGS, "" }, @@ -211,9 +195,14 @@ static PyMethodDef unreal_engine_methods[] = { // slate + { "find_slate_style", py_unreal_engine_find_slate_style, METH_VARARGS, "" }, + { "find_icon_for_class", py_unreal_engine_find_icon_for_class, METH_VARARGS, "" }, + { "register_nomad_tab_spawner", py_unreal_engine_register_nomad_tab_spawner, METH_VARARGS, "" }, { "unregister_nomad_tab_spawner", py_unreal_engine_unregister_nomad_tab_spawner, METH_VARARGS, "" }, { "invoke_tab", py_unreal_engine_invoke_tab, METH_VARARGS, "" }, + { "get_swidget_from_wrapper", py_unreal_engine_get_swidget_from_wrapper, METH_VARARGS, "" }, + { "create_wrapper_from_pyswidget", py_unreal_engine_create_wrapper_from_pyswidget, METH_VARARGS, "" }, #if WITH_EDITOR { "get_editor_window", py_unreal_engine_get_editor_window, METH_VARARGS, "" }, { "add_menu_extension", py_unreal_engine_add_menu_extension, METH_VARARGS, "" }, @@ -223,7 +212,9 @@ static PyMethodDef unreal_engine_methods[] = { { "add_asset_view_context_menu_extension", py_unreal_engine_add_asset_view_context_menu_extension, METH_VARARGS, "" }, #pragma warning(suppress: 4191) - { "create_detail_view", (PyCFunction)py_unreal_engine_create_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, + { "create_detail_view", (PyCFunction)py_unreal_engine_create_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, +#pragma warning(suppress: 4191) + { "create_structure_detail_view", (PyCFunction)py_unreal_engine_create_structure_detail_view, METH_VARARGS | METH_KEYWORDS, "" }, #pragma warning(suppress: 4191) { "create_property_view", (PyCFunction)py_unreal_engine_create_property_view, METH_VARARGS | METH_KEYWORDS, "" }, @@ -237,6 +228,7 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_select_actor", py_unreal_engine_editor_select_actor, METH_VARARGS, "" }, { "editor_deselect_actors", py_unreal_engine_editor_deselect_actors, METH_VARARGS, "" }, { "import_asset", py_unreal_engine_import_asset, METH_VARARGS, "" }, + { "export_assets", py_unreal_engine_export_assets, METH_VARARGS, "" }, { "get_asset", py_unreal_engine_get_asset, METH_VARARGS, "" }, { "find_asset", py_unreal_engine_find_asset, METH_VARARGS, "" }, { "delete_object", py_unreal_engine_delete_object, METH_VARARGS, "" }, @@ -280,6 +272,7 @@ static PyMethodDef unreal_engine_methods[] = { { "create_blueprint", py_unreal_engine_create_blueprint, METH_VARARGS, "" }, { "create_blueprint_from_actor", py_unreal_engine_create_blueprint_from_actor, METH_VARARGS, "" }, { "replace_blueprint", py_unreal_engine_replace_blueprint, METH_VARARGS, "" }, + { "get_blueprint_hierarchy_from_class", py_unreal_engine_get_blueprint_hierarchy_from_class, METH_VARARGS, "" }, { "reload_blueprint", py_unreal_engine_reload_blueprint, METH_VARARGS, "" }, { "compile_blueprint", py_unreal_engine_compile_blueprint, METH_VARARGS, "" }, { "blueprint_add_member_variable", py_unreal_engine_blueprint_add_member_variable, METH_VARARGS, "" }, @@ -288,6 +281,7 @@ static PyMethodDef unreal_engine_methods[] = { { "blueprint_set_variable_visibility", py_unreal_engine_blueprint_set_variable_visibility, METH_VARARGS, "" }, { "blueprint_add_function", py_unreal_engine_blueprint_add_function, METH_VARARGS, "" }, { "blueprint_add_ubergraph_page", py_unreal_engine_blueprint_add_ubergraph_page, METH_VARARGS, "" }, + { "blueprint_get_all_graphs", py_unreal_engine_blueprint_get_all_graphs, METH_VARARGS, "" }, { "blueprint_mark_as_structurally_modified", py_unreal_engine_blueprint_mark_as_structurally_modified, METH_VARARGS, "" }, { "add_component_to_blueprint", py_unreal_engine_add_component_to_blueprint, METH_VARARGS, "" }, { "get_blueprint_components", py_unreal_engine_get_blueprint_components, METH_VARARGS, "" }, @@ -339,7 +333,6 @@ static PyMethodDef unreal_engine_methods[] = { { "all_worlds", (PyCFunction)py_unreal_engine_all_worlds, METH_VARARGS, "" }, { "tobject_iterator", (PyCFunction)py_unreal_engine_tobject_iterator, METH_VARARGS, "" }, - { "new_class", py_unreal_engine_new_class, METH_VARARGS, "" }, @@ -371,6 +364,7 @@ static PyMethodDef unreal_engine_methods[] = { { "play_sound", py_unreal_engine_play_sound, METH_VARARGS, "" }, #if WITH_EDITOR { "editor_play_in_viewport", py_unreal_engine_editor_play_in_viewport, METH_VARARGS, "" }, + { "request_play_session", py_unreal_engine_request_play_session, METH_VARARGS, "" }, { "get_editor_pie_game_viewport_client", py_unreal_engine_get_editor_pie_game_viewport_client, METH_VARARGS, "" }, { "editor_get_active_viewport_screenshot", py_unreal_engine_editor_get_active_viewport_screenshot, METH_VARARGS, "" }, { "editor_get_pie_viewport_screenshot", py_unreal_engine_editor_get_pie_viewport_screenshot, METH_VARARGS, "" }, @@ -386,11 +380,17 @@ static PyMethodDef unreal_engine_methods[] = { { "editor_take_high_res_screen_shots", py_unreal_engine_editor_take_high_res_screen_shots, METH_VARARGS, "" }, { "register_settings", py_unreal_engine_register_settings, METH_VARARGS, "" }, + { "show_viewer", py_unreal_engine_show_viewer, METH_VARARGS, "" }, { "unregister_settings", py_unreal_engine_unregister_settings, METH_VARARGS, "" }, + + { "in_editor_capture", py_unreal_engine_in_editor_capture, METH_VARARGS, "" }, #endif //duplicate for now for testing { "run_on_gt", py_unreal_engine_create_and_dispatch_when_ready, METH_VARARGS, "" }, + { "clipboard_copy", py_unreal_engine_clipboard_copy, METH_VARARGS, "" }, + { "clipboard_paste", py_unreal_engine_clipboard_paste, METH_VARARGS, "" }, + #pragma warning(suppress: 4191) { "copy_properties_for_unrelated_objects", (PyCFunction)py_unreal_engine_copy_properties_for_unrelated_objects, METH_VARARGS | METH_KEYWORDS, "" }, { NULL, NULL }, @@ -477,6 +477,10 @@ static PyMethodDef ue_PyUObject_methods[] = { { "get_path_name", (PyCFunction)py_ue_get_path_name, METH_VARARGS, "" }, { "get_full_name", (PyCFunction)py_ue_get_full_name, METH_VARARGS, "" }, +#if WITH_EDITOR + { "import_custom_properties", (PyCFunction)py_ue_import_custom_properties, METH_VARARGS, "" }, +#endif + #if ENGINE_MINOR_VERSION >= 15 { "can_modify", (PyCFunction)py_ue_can_modify, METH_VARARGS, "" }, #endif @@ -548,6 +552,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "data_table_get_all_rows", (PyCFunction)py_ue_data_table_get_all_rows, METH_VARARGS, "" }, #endif + { "export_to_file", (PyCFunction)py_ue_export_to_file, METH_VARARGS, "" }, + { "is_rooted", (PyCFunction)py_ue_is_rooted, METH_VARARGS, "" }, { "add_to_root", (PyCFunction)py_ue_add_to_root, METH_VARARGS, "" }, { "auto_root", (PyCFunction)py_ue_auto_root, METH_VARARGS, "" }, @@ -571,6 +577,7 @@ static PyMethodDef ue_PyUObject_methods[] = { #if WITH_EDITOR // AssetUserData { "asset_import_data", (PyCFunction)py_ue_asset_import_data, METH_VARARGS, "" }, + { "asset_import_data_set_sources", (PyCFunction)py_ue_asset_import_data_set_sources, METH_VARARGS, "" }, #endif // AnimSequence @@ -653,6 +660,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "class_generated_by", (PyCFunction)py_ue_class_generated_by, METH_VARARGS, "" }, { "class_get_flags", (PyCFunction)py_ue_class_get_flags, METH_VARARGS, "" }, { "class_set_flags", (PyCFunction)py_ue_class_set_flags, METH_VARARGS, "" }, + { "get_obj_flags", (PyCFunction)py_ue_get_obj_flags, METH_VARARGS, "" }, + { "set_obj_flags", (PyCFunction)py_ue_set_obj_flags, METH_VARARGS, "" }, #if WITH_EDITOR { "class_get_config_name", (PyCFunction)py_ue_class_get_config_name, METH_VARARGS, "" }, @@ -723,6 +732,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "add_actor_component", (PyCFunction)py_ue_add_actor_component, METH_VARARGS, "" }, { "add_instance_component", (PyCFunction)py_ue_add_instance_component, METH_VARARGS, "" }, + { "get_actor_root_component", (PyCFunction)py_ue_get_actor_root_component, METH_VARARGS, "" }, { "add_actor_root_component", (PyCFunction)py_ue_add_actor_root_component, METH_VARARGS, "" }, { "get_actor_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, { "get_component_by_type", (PyCFunction)py_ue_get_actor_component_by_type, METH_VARARGS, "" }, @@ -747,6 +757,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "has_world", (PyCFunction)py_ue_has_world, METH_VARARGS, "" }, { "get_game_viewport", (PyCFunction)py_ue_get_game_viewport, METH_VARARGS, "" }, + + { "game_viewport_client_set_rendering_flag", (PyCFunction)py_ue_game_viewport_client_set_rendering_flag, METH_VARARGS, "" }, { "get_world_location_at_distance_along_spline", (PyCFunction)py_ue_get_world_location_at_distance_along_spline, METH_VARARGS, "" }, { "get_spline_length", (PyCFunction)py_ue_get_spline_length, METH_VARARGS, "" }, @@ -798,6 +810,11 @@ static PyMethodDef ue_PyUObject_methods[] = { { "capture_initialize", (PyCFunction)py_ue_capture_initialize, METH_VARARGS, "" }, { "capture_start", (PyCFunction)py_ue_capture_start, METH_VARARGS, "" }, { "capture_stop", (PyCFunction)py_ue_capture_stop, METH_VARARGS, "" }, + { "capture_load_from_config", (PyCFunction)py_ue_capture_load_from_config, METH_VARARGS, "" }, + +#if WITH_EDITOR + { "set_level_sequence_asset", (PyCFunction)py_ue_set_level_sequence_asset, METH_VARARGS, "" }, +#endif // Pawn { "get_controller", (PyCFunction)py_ue_pawn_get_controller, METH_VARARGS, "" }, @@ -884,6 +901,7 @@ static PyMethodDef ue_PyUObject_methods[] = { // Sequencer { "sequencer_master_tracks", (PyCFunction)py_ue_sequencer_master_tracks, METH_VARARGS, "" }, { "sequencer_possessable_tracks", (PyCFunction)py_ue_sequencer_possessable_tracks, METH_VARARGS, "" }, + { "sequencer_get_camera_cut_track", (PyCFunction)py_ue_sequencer_get_camera_cut_track, METH_VARARGS, "" }, #if WITH_EDITOR { "sequencer_folders", (PyCFunction)py_ue_sequencer_folders, METH_VARARGS, "" }, { "sequencer_create_folder", (PyCFunction)py_ue_sequencer_create_folder, METH_VARARGS, "" }, @@ -892,6 +910,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "sequencer_changed", (PyCFunction)py_ue_sequencer_changed, METH_VARARGS, "" }, { "sequencer_add_camera_cut_track", (PyCFunction)py_ue_sequencer_add_camera_cut_track, METH_VARARGS, "" }, { "sequencer_add_actor", (PyCFunction)py_ue_sequencer_add_actor, METH_VARARGS, "" }, + { "sequencer_add_actor_component", (PyCFunction)py_ue_sequencer_add_actor_component, METH_VARARGS, "" }, { "sequencer_make_new_spawnable", (PyCFunction)py_ue_sequencer_make_new_spawnable, METH_VARARGS, "" }, { "sequencer_add_possessable", (PyCFunction)py_ue_sequencer_add_possessable, METH_VARARGS, "" }, @@ -950,50 +969,35 @@ static PyMethodDef ue_PyUObject_methods[] = { #if ENGINE_MINOR_VERSION >= 15 { "enum_user_defined_names", (PyCFunction)py_ue_enum_user_defined_names, METH_VARARGS, "" }, #endif + + // serialization + { "to_bytes", (PyCFunction)py_ue_to_bytes, METH_VARARGS, "" }, + { "to_bytearray", (PyCFunction)py_ue_to_bytearray, METH_VARARGS, "" }, + { "from_bytes", (PyCFunction)py_ue_from_bytes, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; -void ue_pydelegates_cleanup(ue_PyUObject *self) + +// destructor +static void ue_pyobject_dealloc(ue_PyUObject *self) { - // this could happen during engine shutdown, so have mercy - // start deallocating delegates mapped to the object - if (!self || !self->python_delegates_gc) - return; - for (UPythonDelegate *py_delegate : *(self->python_delegates_gc)) - { - if (py_delegate && py_delegate->IsValidLowLevel()) - { #if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Removing UPythonDelegate %p from ue_PyUObject %p mapped to UObject %p"), py_delegate, self, self->ue_object); + UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); #endif - py_delegate->RemoveFromRoot(); - } - } - self->python_delegates_gc->clear(); - delete self->python_delegates_gc; - self->python_delegates_gc = nullptr; - - if (self->auto_rooted && self->ue_object->IsRooted()) + if (self->auto_rooted && (self->ue_object && self->ue_object->IsValidLowLevel() && self->ue_object->IsRooted())) { self->ue_object->RemoveFromRoot(); } Py_XDECREF(self->py_dict); -} -// destructor -static void ue_pyobject_dealloc(ue_PyUObject *self) -{ -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); -#endif - ue_pydelegates_cleanup(self); - ue_python_gc.erase(self->ue_object); Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) { + ue_py_check(self); + PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); if (!ret) { @@ -1093,6 +1097,8 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObject *value) { + ue_py_check_int(self); + // first of all check for UProperty if (PyUnicodeOrString_Check(attr_name)) { @@ -1130,6 +1136,8 @@ static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObje static PyObject *ue_PyUObject_str(ue_PyUObject *self) { + ue_py_check(self); + #if PY_MAJOR_VERSION >= 3 return PyUnicode_FromFormat("", TCHAR_TO_UTF8(*self->ue_object->GetName()), self->ue_object, TCHAR_TO_UTF8(*self->ue_object->GetClass()->GetName()), self->ob_base.ob_refcnt); @@ -1141,6 +1149,7 @@ static PyObject *ue_PyUObject_str(ue_PyUObject *self) static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject *kw) { + ue_py_check(self); // if it is a class, create a new object if (self->ue_object->IsA()) { @@ -1178,7 +1187,7 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (self->ue_object->IsA()) { UScriptStruct *u_script_struct = (UScriptStruct *)self->ue_object; - uint8 *data = (uint8*)FMemory_Alloca(u_script_struct->GetStructureSize()); + uint8 *data = (uint8*)FMemory::Malloc(u_script_struct->GetStructureSize()); u_script_struct->InitializeStruct(data); #if WITH_EDITOR u_script_struct->InitializeDefaultValue(data); @@ -1192,7 +1201,10 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (!key) { if (PyErr_Occurred()) + { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "unable to build struct from dictionary"); + } break; } if (!PyUnicodeOrString_Check(key)) @@ -1203,7 +1215,10 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject if (!value) { if (PyErr_Occurred()) + { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "unable to build struct from dictionary"); + } break; } @@ -1212,16 +1227,18 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject { if (!ue_py_convert_pyobject(value, u_property, data)) { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "invalid value for UProperty"); } } else { + FMemory::Free(data); return PyErr_Format(PyExc_Exception, "UProperty %s not found", struct_key); } } } - return py_ue_new_uscriptstruct(u_script_struct, data); + return py_ue_wrap_uscriptstruct(u_script_struct, data); } return PyErr_Format(PyExc_Exception, "the specified uobject has no __call__ support"); } @@ -1247,7 +1264,7 @@ static PyTypeObject ue_PyUObjectType = { (setattrofunc)ue_PyUObject_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "Unreal Engine generic UObject", /* tp_doc */ + "Unreal Engine UObject wrapper", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -1257,6 +1274,11 @@ static PyTypeObject ue_PyUObjectType = { ue_PyUObject_methods, /* tp_methods */ }; + + + + + UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) { bool is_overwriting = false; @@ -1367,497 +1389,24 @@ static void UEPyClassConstructor(UClass *u_class, const FObjectInitializer &Obje ue_py_class_constructor_placeholder = nullptr; } -static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) -{ - // is it subclassing ? - if (PyTuple_Size(args) == 3) - { - // TODO make it smarter on error checking - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0))))); - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 1))))); - UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 2))))); - - PyObject *parents = PyTuple_GetItem(args, 1); - ue_PyUObject *parent = (ue_PyUObject *)PyTuple_GetItem(parents, 0); - - PyObject *class_attributes = PyTuple_GetItem(args, 2); - - PyObject *class_name = PyDict_GetItemString(class_attributes, (char *)"__qualname__"); - char *name = PyUnicode_AsUTF8(class_name); - // check if parent is a uclass - UClass *new_class = unreal_engine_new_uclass(name, (UClass *)parent->ue_object); - if (!new_class) - return -1; - - // map the class to the python object - self->ue_object = new_class; - - PyObject *py_additional_properties = PyDict_New(); - - PyObject *class_attributes_keys = PyObject_GetIter(class_attributes); - for (;;) - { - PyObject *key = PyIter_Next(class_attributes_keys); - if (!key) - { - if (PyErr_Occurred()) - return -1; - break; - } - if (!PyUnicodeOrString_Check(key)) - continue; - char *class_key = PyUnicode_AsUTF8(key); - - PyObject *value = PyDict_GetItem(class_attributes, key); - - if (strlen(class_key) > 2 && class_key[0] == '_' && class_key[1] == '_') - { - continue; - } - - bool prop_added = false; - - if (UProperty *u_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) - { - UE_LOG(LogPython, Warning, TEXT("Found UProperty %s"), UTF8_TO_TCHAR(class_key)); - PyDict_SetItem(py_additional_properties, key, value); - prop_added = true; - } - // add simple property - else if (ue_is_pyuobject(value)) - { - ue_PyUObject *py_obj = (ue_PyUObject *)value; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - else - { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()), class_key, value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - else if (py_obj->ue_object->IsA()) - { - if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()), class_key, value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - - // add array property - else if (PyList_Check(value)) - { - if (PyList_Size(value) == 1) - { - PyObject *first_item = PyList_GetItem(value, 0); - if (ue_is_pyuobject(first_item)) - { - ue_PyUObject *py_obj = (ue_PyUObject *)first_item; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - - else - { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()), class_key, first_item))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - else if (py_obj->ue_object->IsA()) - { - if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()), class_key, first_item))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - - } - } -#if ENGINE_MINOR_VERSION >= 15 - else if (PyDict_Check(value)) - { - if (PyDict_Size(value) == 1) - { - PyObject *py_key = nullptr; - PyObject *py_value = nullptr; - Py_ssize_t pos = 0; - PyDict_Next(value, &pos, &py_key, &py_value); - if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) - { - PyObject *first_item = nullptr; - PyObject *second_item = nullptr; - - ue_PyUObject *py_obj = (ue_PyUObject *)py_key; - if (py_obj->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj->ue_object; - if (p_class->IsChildOf()) - { - first_item = py_key; - } - else - { - first_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()); - } - } - else if (py_obj->ue_object->IsA()) - { - first_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()); - } - - ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; - if (py_obj2->ue_object->IsA()) - { - UClass *p_class = (UClass *)py_obj2->ue_object; - if (p_class->IsChildOf()) - { - second_item = py_value; - } - else - { - second_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass()); - } - } - else if (py_obj2->ue_object->IsA()) - { - second_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass()); - } - - if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) - { - unreal_engine_py_log_error(); - return -1; - } - prop_added = true; - } - } - } -#endif - // function ? - else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') - { - uint32 func_flags = FUNC_Native | FUNC_BlueprintCallable | FUNC_Public; - PyObject *is_event = PyObject_GetAttrString(value, (char *)"event"); - if (is_event && PyObject_IsTrue(is_event)) - { - func_flags |= FUNC_Event | FUNC_BlueprintEvent; - } - else if (!is_event) - PyErr_Clear(); - - PyObject *is_multicast = PyObject_GetAttrString(value, (char *)"multicast"); - if (is_multicast && PyObject_IsTrue(is_multicast)) - { - func_flags |= FUNC_NetMulticast; - } - else if (!is_multicast) - PyErr_Clear(); - PyObject *is_server = PyObject_GetAttrString(value, (char *)"server"); - if (is_server && PyObject_IsTrue(is_server)) - { - func_flags |= FUNC_NetServer; - } - else if (!is_server) - PyErr_Clear(); - PyObject *is_client = PyObject_GetAttrString(value, (char *)"client"); - if (is_client && PyObject_IsTrue(is_client)) - { - func_flags |= FUNC_NetClient; - } - else if (!is_client) - PyErr_Clear(); - PyObject *is_reliable = PyObject_GetAttrString(value, (char *)"reliable"); - if (is_reliable && PyObject_IsTrue(is_reliable)) - { - func_flags |= FUNC_NetReliable; - } - else if (!is_reliable) - PyErr_Clear(); - - - PyObject *is_pure = PyObject_GetAttrString(value, (char *)"pure"); - if (is_pure && PyObject_IsTrue(is_pure)) - { - func_flags |= FUNC_BlueprintPure; - } - else if (!is_pure) - PyErr_Clear(); - PyObject *is_static = PyObject_GetAttrString(value, (char *)"static"); - if (is_static && PyObject_IsTrue(is_static)) - { - func_flags |= FUNC_Static; - } - else if (!is_static) - PyErr_Clear(); - PyObject *override_name = PyObject_GetAttrString(value, (char *)"override"); - if (override_name && PyUnicodeOrString_Check(override_name)) - { - class_key = PyUnicode_AsUTF8(override_name); - } - else if (override_name && PyUnicodeOrString_Check(override_name)) - { - class_key = PyUnicode_AsUTF8(override_name); - } - else if (!override_name) - PyErr_Clear(); - if (!unreal_engine_add_function(new_class, class_key, value, func_flags)) - { - UE_LOG(LogPython, Error, TEXT("unable to add function %s"), UTF8_TO_TCHAR(class_key)); - return -1; - } - prop_added = true; - } - - - if (!prop_added) - { - UE_LOG(LogPython, Warning, TEXT("Adding %s as attr"), UTF8_TO_TCHAR(class_key)); - PyObject_SetAttr((PyObject *)self, key, value); - } - } - - if (PyDict_Size(py_additional_properties) > 0) - { - PyObject_SetAttrString((PyObject *)self, (char*)"__additional_uproperties__", py_additional_properties); - } - - UPythonClass *new_u_py_class = (UPythonClass *)new_class; - // TODO: check if we can use this to decref the ue_PyUbject mapped to the class - new_u_py_class->py_uobject = self; - new_u_py_class->ClassConstructor = [](const FObjectInitializer &ObjectInitializer) - { - FScopePythonGIL gil; - UClass *u_class = ue_py_class_constructor_placeholder ? ue_py_class_constructor_placeholder : ObjectInitializer.GetClass(); - ue_py_class_constructor_placeholder = nullptr; - - UEPyClassConstructor(u_class->GetSuperClass(), ObjectInitializer); - - if (UPythonClass *u_py_class_casted = Cast(u_class)) - { - ue_PyUObject *new_self = ue_get_python_wrapper(ObjectInitializer.GetObj()); - if (!new_self) - { - unreal_engine_py_log_error(); - return; - } - - // fill __dict__ from class - if (u_py_class_casted->py_uobject && u_py_class_casted->py_uobject->py_dict) - { - PyObject *found_additional_props = PyDict_GetItemString(u_py_class_casted->py_uobject->py_dict, (char *)"__additional_uproperties__"); - // manage UProperties (and automatically maps multicast properties) - if (found_additional_props) - { - PyObject *keys = PyDict_Keys(found_additional_props); - Py_ssize_t items_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < items_len; i++) - { - PyObject *mc_key = PyList_GetItem(keys, i); - PyObject *mc_value = PyDict_GetItem(found_additional_props, mc_key); - - char *mc_name = PyUnicode_AsUTF8(mc_key); - UProperty *u_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); - if (u_property) - { - if (auto casted_prop = Cast(u_property)) - { - FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); - - FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(mc_value); - py_delegate->SetSignature(casted_prop->SignatureFunction); - // avoid delegates to be destroyed by the GC - py_delegate->AddToRoot(); - - // fake UFUNCTION for bypassing checks - script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); - - // add the new delegate - multiscript_delegate.Add(script_delegate); - - // re-assign multicast delegate - casted_prop->SetPropertyValue_InContainer(ObjectInitializer.GetObj(), multiscript_delegate); - } - else - { - PyObject_SetAttr((PyObject *)new_self, mc_key, mc_value); - } - } - - } - Py_DECREF(keys); - } - else - { - PyErr_Clear(); - } - PyObject *keys = PyDict_Keys(u_py_class_casted->py_uobject->py_dict); - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(u_py_class_casted->py_uobject->py_dict, key); - if (PyUnicode_Check(key)) - { - char *key_name = PyUnicode_AsUTF8(key); - if (!strcmp(key_name, (char *)"__additional_uproperties__")) - continue; - } - // special case to bound function to method - if (PyFunction_Check(value)) - { - PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_self); - if (bound_function) - { - PyObject_SetAttr((PyObject *)new_self, key, bound_function); - Py_DECREF(bound_function); - } - else - { - unreal_engine_py_log_error(); - } - } - else - { - PyObject_SetAttr((PyObject *)new_self, key, value); - } - } - Py_DECREF(keys); - } - // call __init__ - u_py_class_casted->CallPyConstructor(new_self); - } - }; - - - - if (self->py_dict) - { - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); - - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to set dict on new ClassDefaultObject")); - return -1; - } - PyObject *keys = PyDict_Keys(self->py_dict); - - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(self->py_dict, key); - // special case to bound function to method - if (PyFunction_Check(value)) - { - PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_default_self); - if (bound_function) - { - PyObject_SetAttr((PyObject *)new_default_self, key, bound_function); - Py_DECREF(bound_function); - } - else - { - unreal_engine_py_log_error(); - } - } - else - { - PyObject_SetAttr((PyObject *)new_default_self, key, value); - } - } - Py_DECREF(keys); - } - - // add default uproperties values - if (py_additional_properties) - { - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to set properties on new ClassDefaultObject")); - return -1; - } - PyObject *keys = PyDict_Keys(py_additional_properties); - Py_ssize_t keys_len = PyList_Size(keys); - for (Py_ssize_t i = 0; i < keys_len; i++) - { - PyObject *key = PyList_GetItem(keys, i); - PyObject *value = PyDict_GetItem(py_additional_properties, key); - - PyObject_SetAttr((PyObject *)new_default_self, key, value); - } - Py_DECREF(keys); - } - - // add custom constructor (__init__) - PyObject *py_init = PyDict_GetItemString(class_attributes, (char *)"__init__"); - if (py_init && PyCallable_Check(py_init)) - { - // fake initializer - FObjectInitializer initializer(new_u_py_class->ClassDefaultObject, nullptr, false, true); - new_u_py_class->SetPyConstructor(py_init); - ue_PyUObject *new_default_self = ue_get_python_wrapper(new_u_py_class->ClassDefaultObject); - - if (!new_default_self) - { - unreal_engine_py_log_error(); - UE_LOG(LogPython, Error, TEXT("unable to call __init__ on new ClassDefaultObject")); - return -1; - } - - new_u_py_class->CallPyConstructor(new_default_self); - } - - } - - return 0; -} +int unreal_engine_py_init(ue_PyUObject *, PyObject *, PyObject *); void unreal_engine_init_py_module() { #if PY_MAJOR_VERSION >= 3 - PyImport_AppendInittab("unreal_engine", init_unreal_engine); PyObject *new_unreal_engine_module = PyImport_AddModule("unreal_engine"); #else PyObject *new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); #endif + ue_PyUObjectType.tp_new = PyType_GenericNew; + ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; + ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); + + if (PyType_Ready(&ue_PyUObjectType) < 0) + return; + Py_INCREF(&ue_PyUObjectType); + PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); PyObject *unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); @@ -1869,16 +1418,6 @@ void unreal_engine_init_py_module() Py_DECREF(func); } - - ue_PyUObjectType.tp_new = PyType_GenericNew; - ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; - ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); - if (PyType_Ready(&ue_PyUObjectType) < 0) - return; - - Py_INCREF(&ue_PyUObjectType); - PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); - ue_python_init_fvector(new_unreal_engine_module); ue_python_init_frotator(new_unreal_engine_module); ue_python_init_ftransform(new_unreal_engine_module); @@ -1887,6 +1426,7 @@ void unreal_engine_init_py_module() ue_python_init_flinearcolor(new_unreal_engine_module); ue_python_init_fquat(new_unreal_engine_module); + ue_python_init_frandomstream(new_unreal_engine_module); ue_python_init_fraw_anim_sequence_track(new_unreal_engine_module); @@ -1943,7 +1483,10 @@ void unreal_engine_init_py_module() ue_python_init_iconsole_manager(new_unreal_engine_module); + ue_python_init_fslate_application(new_unreal_engine_module); + #if WITH_EDITOR + ue_python_init_fmaterial_editor_utilities(new_unreal_engine_module); ue_python_init_icollection_manager(new_unreal_engine_module); #endif @@ -1997,6 +1540,40 @@ void unreal_engine_init_py_module() PyDict_SetItemString(unreal_engine_dict, "CLASS_ABSTRACT", PyLong_FromUnsignedLongLong((uint64)CLASS_Abstract)); PyDict_SetItemString(unreal_engine_dict, "CLASS_INTERFACE", PyLong_FromUnsignedLongLong((uint64)CLASS_Interface)); + // Objects + PyDict_SetItemString(unreal_engine_dict, "RF_NOFLAGS", PyLong_FromUnsignedLongLong((uint64)RF_NoFlags)); + PyDict_SetItemString(unreal_engine_dict, "RF_PUBLIC", PyLong_FromUnsignedLongLong((uint64)RF_Public)); + PyDict_SetItemString(unreal_engine_dict, "RF_STANDALONE", PyLong_FromUnsignedLongLong((uint64)RF_Standalone)); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASNATIVE", PyLong_FromUnsignedLongLong((uint64)RF_MarkAsNative)); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSACTIONAL", PyLong_FromUnsignedLongLong((uint64)RF_Transactional)); + PyDict_SetItemString(unreal_engine_dict, "RF_CLASSDEFAULTOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_ClassDefaultObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_ARCHETYPEOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_ArchetypeObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_TRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_Transient)); + PyDict_SetItemString(unreal_engine_dict, "RF_MARKASROOTSET", PyLong_FromUnsignedLongLong((uint64)RF_MarkAsRootSet)); + PyDict_SetItemString(unreal_engine_dict, "RF_TAGGARBAGETEMP", PyLong_FromUnsignedLongLong((uint64)RF_TagGarbageTemp)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDINITIALIZATION", PyLong_FromUnsignedLongLong((uint64)RF_NeedInitialization)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDLOAD", PyLong_FromUnsignedLongLong((uint64)RF_NeedLoad)); + PyDict_SetItemString(unreal_engine_dict, "RF_KEEPFORCOOKER", PyLong_FromUnsignedLongLong((uint64)RF_KeepForCooker)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOAD", PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoad)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEEDPOSTLOADSUBOBJECTS", PyLong_FromUnsignedLongLong((uint64)RF_NeedPostLoadSubobjects)); + PyDict_SetItemString(unreal_engine_dict, "RF_NEWERVERSIONEXISTS", PyLong_FromUnsignedLongLong((uint64)RF_NewerVersionExists)); + PyDict_SetItemString(unreal_engine_dict, "RF_BEGINDESTROYED", PyLong_FromUnsignedLongLong((uint64)RF_BeginDestroyed)); + PyDict_SetItemString(unreal_engine_dict, "RF_FINISHDESTROYED", PyLong_FromUnsignedLongLong((uint64)RF_FinishDestroyed)); + PyDict_SetItemString(unreal_engine_dict, "RF_BEINGREGENERATED", PyLong_FromUnsignedLongLong((uint64)RF_BeingRegenerated)); + PyDict_SetItemString(unreal_engine_dict, "RF_DEFAULTSUBOBJECT", PyLong_FromUnsignedLongLong((uint64)RF_DefaultSubObject)); + PyDict_SetItemString(unreal_engine_dict, "RF_WASLOADED", PyLong_FromUnsignedLongLong((uint64)RF_WasLoaded)); + PyDict_SetItemString(unreal_engine_dict, "RF_TEXTEXPORTTRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_TextExportTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_LOADCOMPLETED", PyLong_FromUnsignedLongLong((uint64)RF_LoadCompleted)); + PyDict_SetItemString(unreal_engine_dict, "RF_INHERITABLECOMPONENTTEMPLATE", PyLong_FromUnsignedLongLong((uint64)RF_InheritableComponentTemplate)); + PyDict_SetItemString(unreal_engine_dict, "RF_DUPLICATETRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_DuplicateTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_STRONGREFONFRAME", PyLong_FromUnsignedLongLong((uint64)RF_StrongRefOnFrame)); + PyDict_SetItemString(unreal_engine_dict, "RF_NONPIEDUPLICATETRANSIENT", PyLong_FromUnsignedLongLong((uint64)RF_NonPIEDuplicateTransient)); + PyDict_SetItemString(unreal_engine_dict, "RF_DYNAMIC", PyLong_FromUnsignedLongLong((uint64)RF_Dynamic)); + +#if ENGINE_MINOR_VERSION > 15 + PyDict_SetItemString(unreal_engine_dict, "RF_WILLBELOADED", PyLong_FromUnsignedLongLong((uint64)RF_WillBeLoaded)); +#endif + // Properties PyDict_SetItemString(unreal_engine_dict, "CPF_CONFIG", PyLong_FromUnsignedLongLong((uint64)CPF_Config)); PyDict_SetItemString(unreal_engine_dict, "CPF_GLOBAL_CONFIG", PyLong_FromUnsignedLongLong((uint64)CPF_GlobalConfig)); @@ -2027,16 +1604,19 @@ void unreal_engine_init_py_module() } + // utility functions -ue_PyUObject *ue_get_python_wrapper(UObject *ue_obj) +ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) { - if (!ue_obj || !ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) + if (!ue_obj) return nullptr; - std::map::iterator it = ue_python_gc.find(ue_obj); - // not found ?? - if (it == ue_python_gc.end()) + + ue_PyUObject *ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); + if (!ret) { + if (!ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) + return nullptr; ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); if (!ue_py_object) @@ -2044,19 +1624,29 @@ ue_PyUObject *ue_get_python_wrapper(UObject *ue_obj) return nullptr; } ue_py_object->ue_object = ue_obj; - ue_py_object->python_delegates_gc = new std::list; + ue_py_object->py_proxy = nullptr; + ue_py_object->auto_rooted = 0; ue_py_object->py_dict = PyDict_New(); - ue_python_gc[ue_obj] = ue_py_object; + FUnrealEnginePythonHouseKeeper::Get()->RegisterPyUObject(ue_obj, ue_py_object); #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("CREATED UPyObject at %p for %p %s"), ue_py_object, ue_obj, *ue_obj->GetName()); #endif - //Py_INCREF(ue_py_object); return ue_py_object; } + return ret; + +} - return it->second; +ue_PyUObject *ue_get_python_uobject_inc(UObject *ue_obj) +{ + ue_PyUObject *ret = ue_get_python_uobject(ue_obj); + if (ret) + { + Py_INCREF(ret); + } + return ret; } void unreal_engine_py_log_error() @@ -2260,11 +1850,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) auto value = casted_prop->GetObjectPropertyValue_InContainer(buffer); if (value) { - ue_PyUObject *ret = ue_get_python_wrapper(value); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(value); } Py_RETURN_NONE; } @@ -2274,11 +1860,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) auto value = casted_prop->GetPropertyValue_InContainer(buffer); if (value) { - ue_PyUObject *ret = ue_get_python_wrapper(value); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(value); } return PyErr_Format(PyExc_Exception, "invalid UClass type for %s", TCHAR_TO_UTF8(*casted_prop->GetName())); } @@ -2330,11 +1912,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) UObject *strong_obj = value.Get(); if (strong_obj) { - ue_PyUObject *ret = ue_get_python_wrapper(strong_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(strong_obj); } // nullptr Py_INCREF(Py_None); @@ -2343,11 +1921,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) if (auto casted_prop = Cast(prop)) { - ue_PyUObject *ret = ue_get_python_wrapper(casted_prop); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(casted_prop); } if (auto casted_prop = Cast(prop)) @@ -2900,7 +2474,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class { if (n == 1) { - if (!ue_bind_pyevent(ue_get_python_wrapper(actor), parts[0], item, true)) + if (!ue_bind_pyevent(ue_get_python_uobject(actor), parts[0], item, true)) { unreal_engine_py_log_error(); } @@ -2912,7 +2486,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class { if (component->GetFName() == FName(*parts[0])) { - if (!ue_bind_pyevent(ue_get_python_wrapper(component), parts[1], item, true)) + if (!ue_bind_pyevent(ue_get_python_uobject(component), parts[1], item, true)) { unreal_engine_py_log_error(); } @@ -3013,29 +2587,41 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } + //NOTE: u_function->PropertiesSize maps to local variable uproperties + ufunction paramaters uproperties uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); - // initialize args - TFieldIterator IArgs(u_function); - for (; IArgs && (IArgs->PropertyFlags & CPF_Parm); ++IArgs) + for (TFieldIterator IArgs(u_function); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) { UProperty *prop = *IArgs; - prop->InitializeValue_InContainer(buffer); -#if WITH_EDITOR - FString default_key = FString("CPP_Default_") + prop->GetName(); - FString default_key_value = u_function->GetMetaData(FName(*default_key)); - if (!default_key_value.IsEmpty()) + if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { + prop->InitializeValue_InContainer(buffer); + } + + //UObject::CallFunctionByNameWithArguments() only does this part on non return value params + if ((IArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm) + { + if (!prop->IsInContainer(u_function->ParmsSize)) + { + return PyErr_Format(PyExc_Exception, "Attempting to import func param property that's out of bounds. %s", *u_function->GetName()); + } +#if WITH_EDITOR + FString default_key = FString("CPP_Default_") + prop->GetName(); + FString default_key_value = u_function->GetMetaData(FName(*default_key)); + if (!default_key_value.IsEmpty()) + { #if ENGINE_MINOR_VERSION >= 17 - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_None, NULL); #else - prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); + prop->ImportText(*default_key_value, prop->ContainerPtrToValuePtr(buffer), PPF_Localized, NULL); #endif - } + } #endif + } } + Py_ssize_t tuple_len = PyTuple_Size(args); int has_out_params = 0; @@ -3071,7 +2657,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } } } - if ((PArgs->PropertyFlags & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { has_out_params++; } @@ -3115,7 +2701,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * for (; OProps; ++OProps) { UProperty *prop = *OProps; - if ((prop->GetPropertyFlags() & (CPF_ConstParm | CPF_OutParm)) == CPF_OutParm) + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { // skip return param as it must be always the first if (prop->GetPropertyFlags() & CPF_ReturnParm) @@ -3155,8 +2741,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ { if (fail_on_wrong_property) return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } if (auto casted_prop = Cast(u_property)) @@ -3164,13 +2749,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->SetSignature(casted_prop->SignatureFunction); - // avoid delegates to be destroyed by the GC - py_delegate->AddToRoot(); - u_obj->python_delegates_gc->push_back(py_delegate); - + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3186,8 +2765,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ return PyErr_Format(PyExc_Exception, "property %s is not an event", TCHAR_TO_UTF8(*event_name)); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_callable, uint32 function_flags) @@ -3480,7 +3058,11 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ function->FunctionFlags = function_flags; #endif +#if ENGINE_MINOR_VERSION > 18 + function->SetNativeFunc((FNativeFuncPtr)&UPythonFunction::CallPythonCallable); +#else function->SetNativeFunc((Native)&UPythonFunction::CallPythonCallable); +#endif function->Next = u_class->Children; @@ -3533,3 +3115,43 @@ FGuid *ue_py_check_fguid(PyObject *py_obj) return nullptr; } +uint8 * do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct) +{ + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) + { + return nullptr; + } + + if (ue_py_struct->u_struct == chk_u_struct) + { + return ue_py_struct->data; + } + + return nullptr; +} + +bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct) +{ + ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + if (!ue_py_struct) + { + return false; + } + + return ue_py_struct->u_struct->IsChildOf(parent_u_struct); +} + + + +#if PY_MAJOR_VERSION >= 3 +static PyObject *init_unreal_engine() +{ + + PyObject *new_unreal_engine_module = PyModule_Create(&unreal_engine_module); + if (!new_unreal_engine_module) + return nullptr; + + return new_unreal_engine_module; +} +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index a1657daed..375245a1c 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -2,20 +2,20 @@ #include "UnrealEnginePython.h" #include "PythonDelegate.h" +#include "PythonSmartDelegate.h" #include "UEPyUScriptStruct.h" -#include -#include + + //#include "UEPyModule.generated.h" -typedef struct { +typedef struct +{ PyObject_HEAD /* Type-specific fields go here. */ UObject *ue_object; // reference to proxy class (can be null) PyObject *py_proxy; - // list of exposed delegates - std::list *python_delegates_gc; // the __dict__ PyObject *py_dict; // if true RemoveFromRoot will be called at object destruction time @@ -23,8 +23,12 @@ typedef struct { } ue_PyUObject; + + + void unreal_engine_py_log_error(); -ue_PyUObject *ue_get_python_wrapper(UObject *); +ue_PyUObject *ue_get_python_uobject(UObject *); +ue_PyUObject *ue_get_python_uobject_inc(UObject *); UWorld *ue_get_uworld(ue_PyUObject *); AActor *ue_get_actor(ue_PyUObject *); PyObject *ue_py_convert_property(UProperty *, uint8 *); @@ -38,36 +42,50 @@ PyObject *ue_bind_pyevent(ue_PyUObject *, FString, PyObject *, bool); PyObject *py_ue_ufunction_call(UFunction *, UObject *, PyObject *, int, PyObject *); -void ue_pydelegates_cleanup(ue_PyUObject *); - UClass *unreal_engine_new_uclass(char *, UClass *); UFunction *unreal_engine_add_function(UClass *, char *, PyObject *, uint32); -template T *ue_py_check_type(PyObject *py_obj) { +template T *ue_py_check_type(PyObject *py_obj) +{ ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { + return nullptr; + } + + if (!ue_py_obj->ue_object || !ue_py_obj->ue_object->IsValidLowLevel() || ue_py_obj->ue_object->IsPendingKillOrUnreachable()) + { + UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), ue_py_obj); return nullptr; } return Cast(ue_py_obj->ue_object); } -template T *ue_py_check_type(ue_PyUObject *py_obj) { +template T *ue_py_check_type(ue_PyUObject *py_obj) +{ + if (!py_obj->ue_object || !py_obj->ue_object->IsValidLowLevel() || py_obj->ue_object->IsPendingKillOrUnreachable()) + { + UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), py_obj); + return nullptr; + } return Cast(py_obj->ue_object); } -template T *ue_py_check_struct(PyObject *py_obj) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); - if (!ue_py_struct) { - return nullptr; - } +uint8 *do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct); - if (ue_py_struct->u_struct == T::StaticStruct()) { - return (T*)ue_py_struct->data; - } +template T *ue_py_check_struct(PyObject *py_obj) +{ + return (T*)do_ue_py_check_struct(py_obj, T::StaticStruct()); +} - return nullptr; +bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct); + +template bool ue_py_check_childstruct(PyObject *py_obj) +{ + return do_ue_py_check_childstruct(py_obj, T::StaticStruct()); } FGuid *ue_py_check_fguid(PyObject *); + diff --git a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp new file mode 100644 index 000000000..a7e7f19ab --- /dev/null +++ b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp @@ -0,0 +1,484 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) +{ + + // is it subclassing ? + if (PyTuple_Size(args) == 3) + { + // TODO make it smarter on error checking + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0))))); + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 1))))); + UE_LOG(LogPython, Warning, TEXT("%s"), UTF8_TO_TCHAR(PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 2))))); + + PyObject *parents = PyTuple_GetItem(args, 1); + ue_PyUObject *parent = (ue_PyUObject *)PyTuple_GetItem(parents, 0); + + PyObject *class_attributes = PyTuple_GetItem(args, 2); + + PyObject *class_name = PyDict_GetItemString(class_attributes, (char *)"__qualname__"); + char *name = PyUnicode_AsUTF8(class_name); + // check if parent is a uclass + UClass *new_class = unreal_engine_new_uclass(name, (UClass *)parent->ue_object); + if (!new_class) + return -1; + + // map the class to the python object + self->ue_object = new_class; + self->py_proxy = nullptr; + self->auto_rooted = 0; + self->py_dict = PyDict_New(); + + FUnrealEnginePythonHouseKeeper::Get()->RegisterPyUObject(new_class, self); + + PyObject *py_additional_properties = PyDict_New(); + + PyObject *class_attributes_keys = PyObject_GetIter(class_attributes); + for (;;) + { + PyObject *key = PyIter_Next(class_attributes_keys); + if (!key) + { + if (PyErr_Occurred()) + return -1; + break; + } + if (!PyUnicodeOrString_Check(key)) + continue; + char *class_key = PyUnicode_AsUTF8(key); + + PyObject *value = PyDict_GetItem(class_attributes, key); + + if (strlen(class_key) > 2 && class_key[0] == '_' && class_key[1] == '_') + { + continue; + } + + bool prop_added = false; + + if (UProperty *u_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) + { + UE_LOG(LogPython, Warning, TEXT("Found UProperty %s"), UTF8_TO_TCHAR(class_key)); + PyDict_SetItem(py_additional_properties, key, value); + prop_added = true; + } + // add simple property + else if (ue_is_pyuobject(value)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)value; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + else + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + + // add array property + else if (PyList_Check(value)) + { + if (PyList_Size(value) == 1) + { + PyObject *first_item = PyList_GetItem(value, 0); + if (ue_is_pyuobject(first_item)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)first_item; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + + else + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + + } + } +#if ENGINE_MINOR_VERSION >= 15 + else if (PyDict_Check(value)) + { + if (PyDict_Size(value) == 1) + { + PyObject *py_key = nullptr; + PyObject *py_value = nullptr; + Py_ssize_t pos = 0; + PyDict_Next(value, &pos, &py_key, &py_value); + if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) + { + PyObject *first_item = nullptr; + PyObject *second_item = nullptr; + + ue_PyUObject *py_obj = (ue_PyUObject *)py_key; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + if (p_class->IsChildOf()) + { + first_item = py_key; + } + else + { + first_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); + } + } + else if (py_obj->ue_object->IsA()) + { + first_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); + } + + ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; + if (py_obj2->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj2->ue_object; + if (p_class->IsChildOf()) + { + second_item = py_value; + } + else + { + second_item = (PyObject *)ue_get_python_uobject(UObjectProperty::StaticClass()); + } + } + else if (py_obj2->ue_object->IsA()) + { + second_item = (PyObject *)ue_get_python_uobject(UStructProperty::StaticClass()); + } + + if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + } +#endif + // function ? + else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') + { + uint32 func_flags = FUNC_Native | FUNC_BlueprintCallable | FUNC_Public; + PyObject *is_event = PyObject_GetAttrString(value, (char *)"event"); + if (is_event && PyObject_IsTrue(is_event)) + { + func_flags |= FUNC_Event | FUNC_BlueprintEvent; + } + else if (!is_event) + PyErr_Clear(); + + PyObject *is_multicast = PyObject_GetAttrString(value, (char *)"multicast"); + if (is_multicast && PyObject_IsTrue(is_multicast)) + { + func_flags |= FUNC_NetMulticast; + } + else if (!is_multicast) + PyErr_Clear(); + PyObject *is_server = PyObject_GetAttrString(value, (char *)"server"); + if (is_server && PyObject_IsTrue(is_server)) + { + func_flags |= FUNC_NetServer; + } + else if (!is_server) + PyErr_Clear(); + PyObject *is_client = PyObject_GetAttrString(value, (char *)"client"); + if (is_client && PyObject_IsTrue(is_client)) + { + func_flags |= FUNC_NetClient; + } + else if (!is_client) + PyErr_Clear(); + PyObject *is_reliable = PyObject_GetAttrString(value, (char *)"reliable"); + if (is_reliable && PyObject_IsTrue(is_reliable)) + { + func_flags |= FUNC_NetReliable; + } + else if (!is_reliable) + PyErr_Clear(); + + + PyObject *is_pure = PyObject_GetAttrString(value, (char *)"pure"); + if (is_pure && PyObject_IsTrue(is_pure)) + { + func_flags |= FUNC_BlueprintPure; + } + else if (!is_pure) + PyErr_Clear(); + PyObject *is_static = PyObject_GetAttrString(value, (char *)"static"); + if (is_static && PyObject_IsTrue(is_static)) + { + func_flags |= FUNC_Static; + } + else if (!is_static) + PyErr_Clear(); + PyObject *override_name = PyObject_GetAttrString(value, (char *)"override"); + if (override_name && PyUnicodeOrString_Check(override_name)) + { + class_key = PyUnicode_AsUTF8(override_name); + } + else if (override_name && PyUnicodeOrString_Check(override_name)) + { + class_key = PyUnicode_AsUTF8(override_name); + } + else if (!override_name) + PyErr_Clear(); + if (!unreal_engine_add_function(new_class, class_key, value, func_flags)) + { + UE_LOG(LogPython, Error, TEXT("unable to add function %s"), UTF8_TO_TCHAR(class_key)); + return -1; + } + prop_added = true; + } + + + if (!prop_added) + { + UE_LOG(LogPython, Warning, TEXT("Adding %s as attr"), UTF8_TO_TCHAR(class_key)); + PyObject_SetAttr((PyObject *)self, key, value); + } + } + + if (PyDict_Size(py_additional_properties) > 0) + { + PyObject_SetAttrString((PyObject *)self, (char*)"__additional_uproperties__", py_additional_properties); + } + + UPythonClass *new_u_py_class = (UPythonClass *)new_class; + // TODO: check if we can use this to decref the ue_PyUbject mapped to the class + new_u_py_class->py_uobject = self; + new_u_py_class->ClassConstructor = [](const FObjectInitializer &ObjectInitializer) + { + FScopePythonGIL gil; + UClass *u_class = ue_py_class_constructor_placeholder ? ue_py_class_constructor_placeholder : ObjectInitializer.GetClass(); + ue_py_class_constructor_placeholder = nullptr; + + UEPyClassConstructor(u_class->GetSuperClass(), ObjectInitializer); + + if (UPythonClass *u_py_class_casted = Cast(u_class)) + { + ue_PyUObject *new_self = ue_get_python_uobject(ObjectInitializer.GetObj()); + if (!new_self) + { + unreal_engine_py_log_error(); + return; + } + + // fill __dict__ from class + if (u_py_class_casted->py_uobject && u_py_class_casted->py_uobject->py_dict) + { + PyObject *found_additional_props = PyDict_GetItemString(u_py_class_casted->py_uobject->py_dict, (char *)"__additional_uproperties__"); + // manage UProperties (and automatically maps multicast properties) + if (found_additional_props) + { + PyObject *keys = PyDict_Keys(found_additional_props); + Py_ssize_t items_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < items_len; i++) + { + PyObject *mc_key = PyList_GetItem(keys, i); + PyObject *mc_value = PyDict_GetItem(found_additional_props, mc_key); + + char *mc_name = PyUnicode_AsUTF8(mc_key); + UProperty *u_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); + if (u_property) + { + if (auto casted_prop = Cast(u_property)) + { + FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); + + FScriptDelegate script_delegate; + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); + // fake UFUNCTION for bypassing checks + script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); + + // add the new delegate + multiscript_delegate.Add(script_delegate); + + // re-assign multicast delegate + casted_prop->SetPropertyValue_InContainer(ObjectInitializer.GetObj(), multiscript_delegate); + } + else + { + PyObject_SetAttr((PyObject *)new_self, mc_key, mc_value); + } + } + + } + Py_DECREF(keys); + } + else + { + PyErr_Clear(); + } + PyObject *keys = PyDict_Keys(u_py_class_casted->py_uobject->py_dict); + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(u_py_class_casted->py_uobject->py_dict, key); + if (PyUnicode_Check(key)) + { + char *key_name = PyUnicode_AsUTF8(key); + if (!strcmp(key_name, (char *)"__additional_uproperties__")) + continue; + } + // special case to bound function to method + if (PyFunction_Check(value)) + { + PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_self); + if (bound_function) + { + PyObject_SetAttr((PyObject *)new_self, key, bound_function); + Py_DECREF(bound_function); + } + else + { + unreal_engine_py_log_error(); + } + } + else + { + PyObject_SetAttr((PyObject *)new_self, key, value); + } + } + Py_DECREF(keys); + } + // call __init__ + u_py_class_casted->CallPyConstructor(new_self); + } + }; + + + + if (self->py_dict) + { + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to set dict on new ClassDefaultObject")); + return -1; + } + PyObject *keys = PyDict_Keys(self->py_dict); + + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(self->py_dict, key); + // special case to bound function to method + if (PyFunction_Check(value)) + { + PyObject *bound_function = PyObject_CallMethod(value, (char*)"__get__", (char*)"O", (PyObject *)new_default_self); + if (bound_function) + { + PyObject_SetAttr((PyObject *)new_default_self, key, bound_function); + Py_DECREF(bound_function); + } + else + { + unreal_engine_py_log_error(); + } + } + else + { + PyObject_SetAttr((PyObject *)new_default_self, key, value); + } + } + Py_DECREF(keys); + } + + // add default uproperties values + if (py_additional_properties) + { + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to set properties on new ClassDefaultObject")); + return -1; + } + PyObject *keys = PyDict_Keys(py_additional_properties); + Py_ssize_t keys_len = PyList_Size(keys); + for (Py_ssize_t i = 0; i < keys_len; i++) + { + PyObject *key = PyList_GetItem(keys, i); + PyObject *value = PyDict_GetItem(py_additional_properties, key); + + PyObject_SetAttr((PyObject *)new_default_self, key, value); + } + Py_DECREF(keys); + } + + // add custom constructor (__init__) + PyObject *py_init = PyDict_GetItemString(class_attributes, (char *)"__init__"); + if (py_init && PyCallable_Check(py_init)) + { + // fake initializer + FObjectInitializer initializer(new_u_py_class->ClassDefaultObject, nullptr, false, true); + new_u_py_class->SetPyConstructor(py_init); + ue_PyUObject *new_default_self = ue_get_python_uobject(new_u_py_class->ClassDefaultObject); + + if (!new_default_self) + { + unreal_engine_py_log_error(); + UE_LOG(LogPython, Error, TEXT("unable to call __init__ on new ClassDefaultObject")); + return -1; + } + + new_u_py_class->CallPyConstructor(new_default_self); + } + + } + + return 0; +} \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.cpp b/Source/UnrealEnginePython/Private/UEPyTicker.cpp index 04de782ea..73af0f7a5 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTicker.cpp @@ -3,15 +3,20 @@ #include "UEPyTicker.h" // destructor -static void ue_pyfdelegatehandle_dealloc(ue_PyFDelegateHandle *self) { - if (!self->garbaged) { +static void ue_pyfdelegatehandle_dealloc(ue_PyFDelegateHandle *self) +{ + if (!self->garbaged) + { FTicker::GetCoreTicker().RemoveTicker(self->dhandle); // useless ;) self->garbaged = true; } - if (self->py_delegate && self->py_delegate->IsValidLowLevel() && self->py_delegate->IsRooted()) { - self->py_delegate->RemoveFromRoot(); + + if (self->delegate_ptr.IsValid()) + { + self->delegate_ptr.Reset(); } + Py_TYPE(self)->tp_free((PyObject *)self); } @@ -46,7 +51,8 @@ static PyTypeObject ue_PyFDelegateHandleType = { 0, /* tp_methods */ }; -void ue_python_init_fdelegatehandle(PyObject *ue_module) { +void ue_python_init_fdelegatehandle(PyObject *ue_module) +{ ue_PyFDelegateHandleType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyFDelegateHandleType) < 0) return; @@ -55,44 +61,49 @@ void ue_python_init_fdelegatehandle(PyObject *ue_module) { PyModule_AddObject(ue_module, "FDelegateHandle", (PyObject *)&ue_PyFDelegateHandleType); } -PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) { + +PyObject *py_unreal_engine_add_ticker(PyObject * self, PyObject * args) +{ PyObject *py_callable; float delay = 0; - if (!PyArg_ParseTuple(args, "O|f:add_ticker", &py_callable, &delay)) { + if (!PyArg_ParseTuple(args, "O|f:add_ticker", &py_callable, &delay)) + { return nullptr; } if (!PyCallable_Check(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not a callable"); + ue_PyFDelegateHandle *ret = (ue_PyFDelegateHandle *)PyObject_New(ue_PyFDelegateHandle, &ue_PyFDelegateHandleType); + if (!ret) + { + return PyErr_Format(PyExc_Exception, "unable to allocate FDelegateHandle python object"); + } + FTickerDelegate ticker_delegate; - UPythonDelegate *py_delegate = NewObject(); + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); py_delegate->SetPyCallable(py_callable); - ticker_delegate.BindUObject(py_delegate, &UPythonDelegate::Tick); + ticker_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Tick); - // avoid the delegate to be GC'ed - py_delegate->AddToRoot(); - - FDelegateHandle dhandle = FTicker::GetCoreTicker().AddTicker(ticker_delegate, delay); - ue_PyFDelegateHandle *ret = (ue_PyFDelegateHandle *)PyObject_New(ue_PyFDelegateHandle, &ue_PyFDelegateHandleType); - if (!ret) { - FTicker::GetCoreTicker().RemoveTicker(dhandle); - py_delegate->RemoveFromRoot(); - return PyErr_Format(PyExc_Exception, "unable to allocate FDelegateHandle python object"); + ret->dhandle = FTicker::GetCoreTicker().AddTicker(ticker_delegate, delay); + if (!ret->dhandle.IsValid()) + { + Py_DECREF(ret); + return PyErr_Format(PyExc_Exception, "unable to add FTicker"); } - ret->dhandle = dhandle; - ret->py_delegate = py_delegate; + new(&ret->delegate_ptr) TSharedPtr(py_delegate); ret->garbaged = false; - return (PyObject *)ret; } -PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) +{ PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:remove_ticker", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:remove_ticker", &py_obj)) + { return NULL; } @@ -101,14 +112,14 @@ PyObject *py_unreal_engine_remove_ticker(PyObject * self, PyObject * args) { ue_PyFDelegateHandle *py_handle = (ue_PyFDelegateHandle *)py_obj; - if (!py_handle->garbaged) { + if (!py_handle->garbaged) + { FTicker::GetCoreTicker().RemoveTicker(py_handle->dhandle); py_handle->garbaged = true; - } - - if (py_handle->py_delegate) { - py_handle->py_delegate->RemoveFromRoot(); - py_handle->py_delegate = nullptr; + if (py_handle->delegate_ptr.IsValid()) + { + py_handle->delegate_ptr.Reset(); + } } Py_RETURN_NONE; diff --git a/Source/UnrealEnginePython/Private/UEPyTicker.h b/Source/UnrealEnginePython/Private/UEPyTicker.h index 22b9396ff..0972a3055 100644 --- a/Source/UnrealEnginePython/Private/UEPyTicker.h +++ b/Source/UnrealEnginePython/Private/UEPyTicker.h @@ -1,3 +1,5 @@ +// Copyright 20Tab S.r.l. + #pragma once #include "UnrealEnginePython.h" @@ -6,8 +8,8 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FDelegateHandle dhandle; - UPythonDelegate *py_delegate; bool garbaged; + TSharedPtr delegate_ptr; } ue_PyFDelegateHandle; PyObject *py_unreal_engine_add_ticker(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.cpp b/Source/UnrealEnginePython/Private/UEPyTimer.cpp index cf1ef4841..f00a22b3c 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.cpp +++ b/Source/UnrealEnginePython/Private/UEPyTimer.cpp @@ -4,23 +4,26 @@ static PyObject *py_ue_ftimerhandle_clear(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().ClearTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().ClearTimer(self->thandle); + Py_RETURN_NONE; } static PyObject *py_ue_ftimerhandle_pause(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().PauseTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().PauseTimer(self->thandle); + Py_RETURN_NONE; } static PyObject *py_ue_ftimerhandle_unpause(ue_PyFTimerHandle *self, PyObject * args) { - self->world->GetTimerManager().UnPauseTimer(self->thandle); - Py_INCREF(Py_None); - return Py_None; + if (!self->world.IsValid()) + return PyErr_Format(PyExc_Exception, "World's timer is invalid"); + self->world.Get()->GetTimerManager().UnPauseTimer(self->thandle); + Py_RETURN_NONE; } @@ -37,7 +40,14 @@ static PyMethodDef ue_PyFTimerHandle_methods[] = { // destructor static void ue_pyftimerhandle_dealloc(ue_PyFTimerHandle *self) { - Py_DECREF(self->py_callable); + if (self->world.IsValid()) + { + self->world.Get()->GetTimerManager().ClearTimer(self->thandle); + } + if (self->delegate_ptr.IsValid()) + { + self->delegate_ptr.Reset(); + } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -107,30 +117,24 @@ PyObject *py_ue_set_timer(ue_PyUObject *self, PyObject * args) if (!world) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - FTimerDelegate timer_delegate; - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - // fake UFUNCTION for bypassing checks - timer_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); - - // allow the delegate to not be destroyed - py_delegate->AddToRoot(); - self->python_delegates_gc->push_back(py_delegate); - FTimerHandle thandle; - world->GetTimerManager().SetTimer(thandle, timer_delegate, rate, loop, first_delay); ue_PyFTimerHandle *ret = (ue_PyFTimerHandle *)PyObject_New(ue_PyFTimerHandle, &ue_PyFTimerHandleType); if (!ret) { - world->GetTimerManager().ClearTimer(thandle); return PyErr_Format(PyExc_Exception, "unable to allocate FTimerHandle python object"); } - ret->thandle = thandle; - ret->py_callable = py_callable; - // will be decref'ed on clear - Py_INCREF(ret->py_callable); - ret->world = world; + + FTimerDelegate timer_delegate; + TSharedRef py_delegate = MakeShareable(new FPythonSmartDelegate); + py_delegate->SetPyCallable(py_callable); + + timer_delegate.BindSP(py_delegate, &FPythonSmartDelegate::Void); + + world->GetTimerManager().SetTimer(ret->thandle, timer_delegate, rate, loop, first_delay); + + new(&ret->delegate_ptr) TSharedPtr(py_delegate); + ret->world = TWeakObjectPtr(world); return (PyObject *)ret; } diff --git a/Source/UnrealEnginePython/Private/UEPyTimer.h b/Source/UnrealEnginePython/Private/UEPyTimer.h index 65e3caac4..aec770fae 100644 --- a/Source/UnrealEnginePython/Private/UEPyTimer.h +++ b/Source/UnrealEnginePython/Private/UEPyTimer.h @@ -8,8 +8,8 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FTimerHandle thandle; - PyObject *py_callable; - UWorld *world; + TWeakObjectPtr world; + TSharedPtr delegate_ptr; } ue_PyFTimerHandle; PyObject *py_ue_set_timer(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp b/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp index 293ea2e97..1d2ee0a28 100644 --- a/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUClassesImporter.cpp @@ -2,8 +2,8 @@ static PyObject *ue_PyUClassesImporter_getattro(ue_PyUClassesImporter *self, PyObject *attr_name) { - PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); - if (!ret) + PyObject *py_attr = PyObject_GenericGetAttr((PyObject *)self, attr_name); + if (!py_attr) { if (PyUnicodeOrString_Check(attr_name)) { @@ -15,16 +15,12 @@ static PyObject *ue_PyUClassesImporter_getattro(ue_PyUClassesImporter *self, PyO { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_class); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_class); } } } } - return ret; + return py_attr; } static PyTypeObject ue_PyUClassesImporterType = { @@ -75,4 +71,4 @@ PyObject *py_ue_new_uclassesimporter() { ue_PyUClassesImporter *ret = (ue_PyUClassesImporter *)PyObject_New(ue_PyUClassesImporter, &ue_PyUClassesImporterType); return (PyObject *)ret; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 27b9f5d52..7b230baf8 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -1,4 +1,4 @@ - +// Copyright 20Tab S.r.l. #include "UnrealEnginePythonPrivatePCH.h" @@ -8,7 +8,7 @@ static PyObject *py_ue_uscriptstruct_get_field(ue_PyUScriptStruct *self, PyObjec char *name; if (!PyArg_ParseTuple(args, "s:get_field", &name)) { - return NULL; + return nullptr; } UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); @@ -24,7 +24,7 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec PyObject *value; if (!PyArg_ParseTuple(args, "sO:set_field", &name, &value)) { - return NULL; + return nullptr; } UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); @@ -37,8 +37,7 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec return PyErr_Format(PyExc_Exception, "unable to set property %s", name); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -59,11 +58,7 @@ static PyObject *py_ue_uscriptstruct_fields(ue_PyUScriptStruct *self, PyObject * static PyObject *py_ue_uscriptstruct_get_struct(ue_PyUScriptStruct *self, PyObject * args) { - ue_PyUObject *ret = ue_get_python_wrapper(self->u_struct); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(self->u_struct); } static PyObject *py_ue_uscriptstruct_clone(ue_PyUScriptStruct *self, PyObject * args) @@ -110,6 +105,10 @@ PyObject *py_ue_uscriptstruct_as_dict(ue_PyUScriptStruct * self, PyObject * args return py_struct_dict; } +static PyObject *py_ue_uscriptstruct_ref(ue_PyUScriptStruct *, PyObject *); + + + static PyMethodDef ue_PyUScriptStruct_methods[] = { { "get_field", (PyCFunction)py_ue_uscriptstruct_get_field, METH_VARARGS, "" }, @@ -118,14 +117,15 @@ static PyMethodDef ue_PyUScriptStruct_methods[] = { { "get_struct", (PyCFunction)py_ue_uscriptstruct_get_struct, METH_VARARGS, "" }, { "clone", (PyCFunction)py_ue_uscriptstruct_clone, METH_VARARGS, "" }, { "as_dict", (PyCFunction)py_ue_uscriptstruct_as_dict, METH_VARARGS, "" }, + { "ref", (PyCFunction)py_ue_uscriptstruct_ref, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; static PyObject *ue_PyUScriptStruct_str(ue_PyUScriptStruct *self) { - return PyUnicode_FromFormat("", - TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize()); + return PyUnicode_FromFormat("", + TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize(), self->data); } static UProperty *get_field_from_name(UScriptStruct *u_struct, char *name) @@ -203,13 +203,18 @@ static int ue_PyUScriptStruct_setattro(ue_PyUScriptStruct *self, PyObject *attr_ return PyObject_GenericSetAttr((PyObject *)self, attr_name, value); } + + // destructor static void ue_PyUScriptStruct_dealloc(ue_PyUScriptStruct *self) { #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUScriptStruct %p with size %d"), self, self->u_struct->GetStructureSize()); #endif - FMemory::Free(self->data); + if (!self->is_ptr) + { + FMemory::Free(self->data); + } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -271,9 +276,22 @@ static int ue_py_uscriptstruct_init(ue_PyUScriptStruct *self, PyObject *args, Py #if WITH_EDITOR self->u_struct->InitializeDefaultValue(self->data); #endif + self->original_data = self->data; + self->is_ptr = 0; return 0; } +// get the original pointer of a struct +static PyObject *py_ue_uscriptstruct_ref(ue_PyUScriptStruct *self, PyObject * args) +{ + ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); + ret->u_struct = self->u_struct; + ret->data = self->original_data; + ret->original_data = ret->data; + ret->is_ptr = 1; + return (PyObject *)ret; +} + static PyObject *ue_py_uscriptstruct_richcompare(ue_PyUScriptStruct *u_struct1, PyObject *py_obj, int op) { ue_PyUScriptStruct *u_struct2 = py_ue_is_uscriptstruct(py_obj); @@ -288,23 +306,21 @@ static PyObject *ue_py_uscriptstruct_richcompare(ue_PyUScriptStruct *u_struct1, { if (equals) { - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } if (equals) { - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } + + void ue_python_init_uscriptstruct(PyObject *ue_module) { ue_PyUScriptStructType.tp_new = PyType_GenericNew; @@ -328,6 +344,19 @@ PyObject *py_ue_new_uscriptstruct(UScriptStruct *u_struct, uint8 *data) ret->u_struct->InitializeStruct(struct_data); ret->u_struct->CopyScriptStruct(struct_data, data); ret->data = struct_data; + ret->original_data = data; + ret->is_ptr = 0; + return (PyObject *)ret; +} + +// generate a new python UScriptStruct from an already allocated data block +PyObject *py_ue_wrap_uscriptstruct(UScriptStruct *u_struct, uint8 *data) +{ + ue_PyUScriptStruct *ret = (ue_PyUScriptStruct *)PyObject_New(ue_PyUScriptStruct, &ue_PyUScriptStructType); + ret->u_struct = u_struct; + ret->data = data; + ret->original_data = data; + ret->is_ptr = 0; return (PyObject *)ret; } diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h index c39a8346e..d7fcba2e2 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h @@ -5,12 +5,17 @@ typedef struct { PyObject_HEAD - /* Type-specific fields go here. */ - UScriptStruct *u_struct; + /* Type-specific fields go here. */ + UScriptStruct *u_struct; uint8 *data; + // if 1, data points to un-owned memory + int is_ptr; + // points to the original struct memory (do not try this at home !) + uint8 *original_data; } ue_PyUScriptStruct; PyObject *py_ue_new_uscriptstruct(UScriptStruct *, uint8 *); +PyObject *py_ue_wrap_uscriptstruct(UScriptStruct *, uint8 *); ue_PyUScriptStruct *py_ue_is_uscriptstruct(PyObject *); UProperty *ue_struct_get_field_from_name(UScriptStruct *, char *); diff --git a/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp b/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp index 7142eceee..873824889 100644 --- a/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUStructsImporter.cpp @@ -1,8 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" static PyObject *ue_PyUStructsImporter_getattro(ue_PyUStructsImporter *self, PyObject *attr_name) { - PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); - if (!ret) { + PyObject *py_attr = PyObject_GenericGetAttr((PyObject *)self, attr_name); + if (!py_attr) { if (PyUnicodeOrString_Check(attr_name)) { char *attr = PyUnicode_AsUTF8(attr_name); if (attr[0] != '_') { @@ -10,16 +10,12 @@ static PyObject *ue_PyUStructsImporter_getattro(ue_PyUStructsImporter *self, PyO if (u_struct) { // swallow old exception PyErr_Clear(); - ue_PyUObject *u_ret = ue_get_python_wrapper(u_struct); - if (!u_ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(u_ret); - return (PyObject *)u_ret; + Py_RETURN_UOBJECT(u_struct); } } } } - return ret; + return py_attr; } static PyTypeObject ue_PyUStructsImporterType = { @@ -68,4 +64,4 @@ void ue_python_init_ustructsimporter(PyObject *ue_module) { PyObject *py_ue_new_ustructsimporter() { ue_PyUStructsImporter *ret = (ue_PyUStructsImporter *)PyObject_New(ue_PyUStructsImporter, &ue_PyUStructsImporterType); return (PyObject *)ret; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e5025b4f6..f705074ef 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -5,22 +5,26 @@ #include "PythonComponent.h" #include "UEPyObject.h" -PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *tag; - if (!PyArg_ParseTuple(args, "s:actor_has_tag", &tag)) { + if (!PyArg_ParseTuple(args, "s:actor_has_tag", &tag)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (actor->ActorHasTag(FName(UTF8_TO_TCHAR(tag)))) { + if (actor->ActorHasTag(FName(UTF8_TO_TCHAR(tag)))) + { Py_INCREF(Py_True); return Py_True; } @@ -29,7 +33,8 @@ PyObject *py_ue_actor_has_tag(ue_PyUObject * self, PyObject * args) { return Py_False; } -PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -45,12 +50,14 @@ PyObject *py_ue_actor_begin_play(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } @@ -65,7 +72,8 @@ PyObject *py_ue_get_actor_bounds(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -74,25 +82,24 @@ PyObject *py_ue_get_actor_component(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); char *name; - if (!PyArg_ParseTuple(args, "s:get_actor_component", &name)) { + if (!PyArg_ParseTuple(args, "s:get_actor_component", &name)) + { return NULL; } - for (UActorComponent *component : actor->GetComponents()) { - if (component->GetName().Equals(UTF8_TO_TCHAR(name))) { - ue_PyUObject *py_obj = ue_get_python_wrapper(component); - if (!py_obj) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(py_obj); - return (PyObject *)py_obj; + for (UActorComponent *component : actor->GetComponents()) + { + if (component->GetName().Equals(UTF8_TO_TCHAR(name))) + { + Py_RETURN_UOBJECT(component); } } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -101,7 +108,8 @@ PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); PyObject *py_component; - if (!PyArg_ParseTuple(args, "O:actor_destroy_component", &py_component)) { + if (!PyArg_ParseTuple(args, "O:actor_destroy_component", &py_component)) + { return NULL; } @@ -119,26 +127,26 @@ PyObject *py_ue_actor_destroy_component(ue_PyUObject * self, PyObject * args) { return Py_None; } -PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_destroy(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + AActor *actor = ue_py_check_type(self); + if (!actor) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } - AActor *actor = (AActor *)self->ue_object; - actor->Destroy(); - Py_INCREF(Py_None); - return Py_None; - + Py_RETURN_NONE; } -PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -148,8 +156,9 @@ PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { PyObject *ret = PyList_New(0); - for (UActorComponent *component : actor->GetComponents()) { - ue_PyUObject *py_obj = ue_get_python_wrapper(component); + for (UActorComponent *component : actor->GetComponents()) + { + ue_PyUObject *py_obj = ue_get_python_uobject(component); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -159,7 +168,8 @@ PyObject *py_ue_actor_components(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -173,16 +183,19 @@ PyObject *py_ue_get_actor_velocity(ue_PyUObject *self, PyObject * args) { #if WITH_EDITOR -PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { AActor *actor = (AActor *)self->ue_object; return PyUnicode_FromString(TCHAR_TO_UTF8(*(actor->GetActorLabel()))); } - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UActorComponent *component = (UActorComponent *)self->ue_object; return PyUnicode_FromString(TCHAR_TO_UTF8(*(component->GetOwner()->GetActorLabel()))); } @@ -190,7 +203,8 @@ PyObject *py_ue_get_actor_label(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not an actor or a component"); } -PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -199,7 +213,8 @@ PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); char *label; - if (!PyArg_ParseTuple(args, "s:set_actor_label", &label)) { + if (!PyArg_ParseTuple(args, "s:set_actor_label", &label)) + { return NULL; } @@ -209,12 +224,14 @@ PyObject *py_ue_set_actor_label(ue_PyUObject *self, PyObject * args) { return Py_None; } -PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name; - if (!PyArg_ParseTuple(args, "s:find_actor_by_label", &name)) { + if (!PyArg_ParseTuple(args, "s:find_actor_by_label", &name)) + { return NULL; } @@ -224,51 +241,46 @@ PyObject *py_ue_find_actor_by_label(ue_PyUObject * self, PyObject * args) { UObject *u_object = nullptr; - for (TActorIterator Itr(world); Itr; ++Itr) { + for (TActorIterator Itr(world); Itr; ++Itr) + { AActor *u_obj = *Itr; - if (u_obj->GetActorLabel().Equals(UTF8_TO_TCHAR(name))) { + if (u_obj->GetActorLabel().Equals(UTF8_TO_TCHAR(name))) + { u_object = u_obj; break; } } - if (u_object) { - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + if (u_object) + { + Py_RETURN_UOBJECT(u_object); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #endif -PyObject *py_ue_get_owner(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_owner(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + UActorComponent *component = ue_py_check_type(self); + if (!component) return PyErr_Format(PyExc_Exception, "uobject is not a component"); - } - - UActorComponent *component = (UActorComponent *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(component->GetOwner()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(component->GetOwner()); } -PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a component"); } @@ -281,17 +293,20 @@ PyObject *py_ue_register_component(ue_PyUObject *self, PyObject * args) { return Py_None; } -PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a component"); } UActorComponent *component = (UActorComponent *)self->ue_object; - if (component->IsRegistered()) { + if (component->IsRegistered()) + { Py_INCREF(Py_True); return Py_True; } @@ -300,7 +315,8 @@ PyObject *py_ue_component_is_registered(ue_PyUObject *self, PyObject * args) { return Py_False; } -PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -309,7 +325,8 @@ PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { return nullptr; USceneComponent *child = ue_py_check_type(self); - if (!child) { + if (!child) + { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } @@ -318,7 +335,8 @@ PyObject *py_ue_setup_attachment(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UActorComponent *component = ue_py_check_type(self); @@ -331,7 +349,8 @@ PyObject *py_ue_unregister_component(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_destroy_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_destroy_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); UActorComponent *component = ue_py_check_type(self); @@ -373,44 +392,52 @@ PyObject *py_ue_add_instance_component(ue_PyUObject * self, PyObject * args) } -PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; PyObject *py_parent = nullptr; - if (!PyArg_ParseTuple(args, "Os|O:add_actor_component", &obj, &name, &py_parent)) { + if (!PyArg_ParseTuple(args, "Os|O:add_actor_component", &obj, &name, &py_parent)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass"); } UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from UActorComponent"); } USceneComponent *parent_component = nullptr; - if (py_parent) { + if (py_parent) + { parent_component = ue_py_check_type(py_parent); - if (!parent_component) { + if (!parent_component) + { return PyErr_Format(PyExc_Exception, "argument is not a USceneComponent"); } } @@ -419,38 +446,39 @@ PyObject *py_ue_add_actor_component(ue_PyUObject * self, PyObject * args) { if (!component) return PyErr_Format(PyExc_Exception, "unable to create component"); - if (py_parent && component->IsA()) { + if (py_parent && component->IsA()) + { USceneComponent *scene_component = (USceneComponent *)component; scene_component->SetupAttachment(parent_component); } - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); char *name; char *module_name; char *class_name; - if (!PyArg_ParseTuple(args, "sss:add_python_component", &name, &module_name, &class_name)) { + if (!PyArg_ParseTuple(args, "sss:add_python_component", &name, &module_name, &class_name)) + { return NULL; } AActor *actor = ue_py_check_type(self); - if (!actor) { + if (!actor) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } @@ -461,27 +489,26 @@ PyObject *py_ue_add_python_component(ue_PyUObject * self, PyObject * args) { component->PythonModule = FString(UTF8_TO_TCHAR(module_name)); component->PythonClass = FString(UTF8_TO_TCHAR(class_name)); - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; - if (!PyArg_ParseTuple(args, "Os:actor_create_default_subobject", &obj, &name)) { + if (!PyArg_ParseTuple(args, "Os:actor_create_default_subobject", &obj, &name)) + { return NULL; } @@ -500,37 +527,55 @@ PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * a if (!ret_obj) return PyErr_Format(PyExc_Exception, "unable to create component"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(ret_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(ret_obj); +} +PyObject *py_ue_get_actor_root_component(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + AActor *actor = ue_get_actor(self); + if (!actor) + return PyErr_Format(PyExc_Exception, "cannot retrieve Actor from uobject"); + + UActorComponent *component = actor->GetRootComponent(); + if (component) + { + Py_RETURN_UOBJECT(component); + } + + Py_RETURN_NONE; } -PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *name; - if (!PyArg_ParseTuple(args, "Os:add_actor_root_component", &obj, &name)) { + if (!PyArg_ParseTuple(args, "Os:add_actor_root_component", &obj, &name)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "argument is not a class"); } @@ -540,43 +585,44 @@ PyObject *py_ue_add_actor_root_component(ue_PyUObject * self, PyObject * args) { actor->SetRootComponent(component); - if (actor->GetWorld() && !component->IsRegistered()) { + if (actor->GetWorld() && !component->IsRegistered()) + { component->RegisterComponent(); } if (component->bWantsInitializeComponent && !component->HasBeenInitialized() && component->IsRegistered()) component->InitializeComponent(); - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; - + Py_RETURN_UOBJECT(component); } -PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:actor_has_component_of_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:actor_has_component_of_type", &obj)) + { return NULL; } - if (!ue_is_pyuobject(obj)) { + if (!ue_is_pyuobject(obj)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not an AActor"); } AActor *actor = (AActor *)self->ue_object; - if (actor->GetComponentByClass((UClass *)py_obj->ue_object)) { + if (actor->GetComponentByClass((UClass *)py_obj->ue_object)) + { Py_INCREF(Py_True); return Py_True; } @@ -586,27 +632,32 @@ PyObject *py_ue_actor_has_component_of_type(ue_PyUObject * self, PyObject * args } -PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:get_actor_component_by_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:get_actor_component_by_type", &obj)) + { return NULL; } ue_PyUObject *py_obj = nullptr; - if (ue_is_pyuobject(obj)) { + if (ue_is_pyuobject(obj)) + { py_obj = (ue_PyUObject *)obj; } // shortcut for finding class by string - else if (PyUnicodeOrString_Check(obj)) { + else if (PyUnicodeOrString_Check(obj)) + { char *class_name = PyUnicode_AsUTF8(obj); UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); - if (u_class) { - py_obj = ue_get_python_wrapper(u_class); + if (u_class) + { + py_obj = ue_get_python_uobject(u_class); } } @@ -621,40 +672,41 @@ PyObject *py_ue_get_actor_component_by_type(ue_PyUObject * self, PyObject * args return PyErr_Format(PyExc_Exception, "argument is not a UClass"); UActorComponent *component = actor->GetComponentByClass((UClass *)py_obj->ue_object); - if (component) { - PyObject *ret = (PyObject *)ue_get_python_wrapper(component); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + if (component) + { + Py_RETURN_UOBJECT(component); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:get_actor_components_by_type", &obj)) { + if (!PyArg_ParseTuple(args, "O:get_actor_components_by_type", &obj)) + { return NULL; } ue_PyUObject *py_obj = nullptr; - if (ue_is_pyuobject(obj)) { + if (ue_is_pyuobject(obj)) + { py_obj = (ue_PyUObject *)obj; } // shortcut for finding class by string - else if (PyUnicodeOrString_Check(obj)) { + else if (PyUnicodeOrString_Check(obj)) + { char *class_name = PyUnicode_AsUTF8(obj); UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(class_name)); - if (u_class) { - py_obj = ue_get_python_wrapper(u_class); + if (u_class) + { + py_obj = ue_get_python_uobject(u_class); } } @@ -670,8 +722,9 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg PyObject *components = PyList_New(0); - for (UActorComponent *component : actor->GetComponentsByClass((UClass *)py_obj->ue_object)) { - ue_PyUObject *item = ue_get_python_wrapper(component); + for (UActorComponent *component : actor->GetComponentsByClass((UClass *)py_obj->ue_object)) + { + ue_PyUObject *item = ue_get_python_uobject(component); if (item) PyList_Append(components, (PyObject *)item); } @@ -681,95 +734,91 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg } -PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwargs) { +PyObject *py_ue_actor_spawn(ue_PyUObject * self, PyObject * args, PyObject *kwargs) +{ ue_py_check(self); + PyObject *py_class; + PyObject *py_obj_location = nullptr; PyObject *py_obj_rotation = nullptr; + if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &py_class, &py_obj_location, &py_obj_rotation)) + { + return nullptr; + } + UWorld *world = ue_get_uworld(self); if (!world) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - PyObject *obj; - if (!PyArg_ParseTuple(args, "O|OO:actor_spawn", &obj, &py_obj_location, &py_obj_rotation)) { - return NULL; - } - - if (!ue_is_pyuobject(obj)) { - return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - } - - ue_PyUObject *py_obj = (ue_PyUObject *)obj; - if (!py_obj->ue_object->IsA()) { - return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from AActor"); - } + UClass *u_class = ue_py_check_type(py_class); + if (!u_class) + return PyErr_Format(PyExc_Exception, "argument is not a UClass"); - UClass *u_class = (UClass *)py_obj->ue_object; - if (!u_class->IsChildOf()) { + if (!u_class->IsChildOf()) + { return PyErr_Format(PyExc_Exception, "argument is not a UClass derived from AActor"); } FVector location = FVector(0, 0, 0); FRotator rotation = FRotator(0, 0, 0); - if (py_obj_location) { + if (py_obj_location) + { ue_PyFVector *py_location = py_ue_is_fvector(py_obj_location); if (!py_location) return PyErr_Format(PyExc_Exception, "location must be an FVector"); location = py_location->vec; } - if (py_obj_rotation) { + if (py_obj_rotation) + { ue_PyFRotator *py_rotation = py_ue_is_frotator(py_obj_rotation); if (!py_rotation) return PyErr_Format(PyExc_Exception, "location must be an FRotator"); rotation = py_rotation->rot; } - AActor *actor = nullptr; - PyObject *ret = nullptr; - - if (kwargs && PyDict_Size(kwargs) > 0) { + if (kwargs && PyDict_Size(kwargs) > 0) + { FTransform transform; transform.SetTranslation(location); transform.SetRotation(rotation.Quaternion()); - actor = world->SpawnActorDeferred((UClass *)py_obj->ue_object, transform); + AActor *actor = world->SpawnActorDeferred(u_class, transform); if (!actor) return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ue_PyUObject *py_u_obj = ue_get_python_wrapper(actor); - if (!py_u_obj) + ue_PyUObject *py_actor = ue_get_python_uobject(actor); + if (!py_actor) return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); PyObject *py_iter = PyObject_GetIter(kwargs); - while (PyObject *py_key = PyIter_Next(py_iter)) { - PyObject *void_ret = py_ue_set_property(py_u_obj, Py_BuildValue("OO", py_key, PyDict_GetItem(kwargs, py_key))); - if (!void_ret) { + while (PyObject *py_key = PyIter_Next(py_iter)) + { + PyObject *void_ret = py_ue_set_property(py_actor, Py_BuildValue("OO", py_key, PyDict_GetItem(kwargs, py_key))); + if (!void_ret) + { return PyErr_Format(PyExc_Exception, "unable to set property for new Actor"); } } Py_DECREF(py_iter); UGameplayStatics::FinishSpawningActor(actor, transform); - ret = (PyObject *)py_u_obj; - } - else { - actor = world->SpawnActor((UClass *)py_obj->ue_object, &location, &rotation); - if (!actor) - return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); - ret = (PyObject *)ue_get_python_wrapper(actor); + return (PyObject *)py_actor; } - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + AActor *actor = world->SpawnActor(u_class, &location, &rotation); + if (!actor) + return PyErr_Format(PyExc_Exception, "unable to spawn a new Actor"); + Py_RETURN_UOBJECT(actor); + } -PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -778,13 +827,15 @@ PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "cannot retrieve actor from UObject"); PyObject *class_filter = nullptr; - if (!PyArg_ParseTuple(args, "|O:get_overlapping_actors", &class_filter)) { + if (!PyArg_ParseTuple(args, "|O:get_overlapping_actors", &class_filter)) + { return NULL; } UClass *filtering = AActor::StaticClass(); - if (class_filter) { + if (class_filter) + { if (!ue_is_pyuobject(class_filter)) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); @@ -802,31 +853,37 @@ PyObject *py_ue_get_overlapping_actors(ue_PyUObject * self, PyObject * args) { TArray overalpping_actors; actor->GetOverlappingActors(overalpping_actors, filtering); - for (AActor *overlapping_actor : overalpping_actors) { - ue_PyUObject *item = ue_get_python_wrapper(overlapping_actor); - if (item) { + for (AActor *overlapping_actor : overalpping_actors) + { + ue_PyUObject *item = ue_get_python_uobject(overlapping_actor); + if (item) + { PyList_Append(py_overlapping_actors, (PyObject *)item); } } return py_overlapping_actors; } -PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_sequence; - if (!PyArg_ParseTuple(args, "O:actor_set_level_sequence", &py_sequence)) { + if (!PyArg_ParseTuple(args, "O:actor_set_level_sequence", &py_sequence)) + { return NULL; } ALevelSequenceActor *actor = ue_py_check_type(self); - if (!actor) { + if (!actor) + { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequenceActor"); } ULevelSequence *sequence = ue_py_check_type(py_sequence); - if (!sequence) { + if (!sequence) + { return PyErr_Format(PyExc_Exception, "argument is not a LevelSequence"); } @@ -837,7 +894,8 @@ PyObject *py_ue_actor_set_level_sequence(ue_PyUObject * self, PyObject * args) { #if WITH_EDITOR -PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -849,10 +907,6 @@ PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject if (!editor_actor) return PyErr_Format(PyExc_Exception, "unable to retrieve editor counterpart actor"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(editor_actor); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(editor_actor); } #endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.h b/Source/UnrealEnginePython/Private/UObject/UEPyActor.h index 39486eda1..5e46abbb1 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.h @@ -21,6 +21,7 @@ PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject *, PyObject *); PyObject *py_ue_get_owner(ue_PyUObject *, PyObject *); PyObject *py_ue_add_actor_component(ue_PyUObject *, PyObject *); PyObject *py_ue_add_python_component(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_actor_root_component(ue_PyUObject *, PyObject *); PyObject *py_ue_add_actor_root_component(ue_PyUObject *, PyObject *); PyObject *py_ue_actor_has_component_of_type(ue_PyUObject *, PyObject *); PyObject *py_ue_get_actor_component_by_type(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp index ae94e6464..215b0fb63 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp @@ -15,11 +15,7 @@ PyObject *py_ue_anim_get_skeleton(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)skeleton); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)skeleton); } @@ -95,6 +91,22 @@ PyObject *py_ue_anim_sequence_add_new_raw_track(ue_PyUObject * self, PyObject * return PyLong_FromLong(index); } + +PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { + ue_py_check(self); + + char *name; + float time; + if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) + return nullptr; + + UAnimMontage *anim = ue_py_check_type(self); + if (!anim) + return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); + + return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); +} + #endif #endif @@ -118,23 +130,6 @@ PyObject *py_ue_anim_set_skeleton(ue_PyUObject * self, PyObject * args) { Py_RETURN_NONE; } -#if WITH_EDITOR -PyObject *py_ue_add_anim_composite_section(ue_PyUObject * self, PyObject * args) { - ue_py_check(self); - - char *name; - float time; - if (!PyArg_ParseTuple(args, "sf:add_anim_composite_section", &name, &time)) - return nullptr; - - UAnimMontage *anim = ue_py_check_type(self); - if (!anim) - return PyErr_Format(PyExc_Exception, "UObject is not a UAnimMontage."); - - return PyLong_FromLong(anim->AddAnimCompositeSection(FName(UTF8_TO_TCHAR(name)), time)); -} -#endif - PyObject *py_ue_get_blend_parameter(ue_PyUObject * self, PyObject * args) { ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h index c6992101a..4bae0606b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.h @@ -15,4 +15,4 @@ PyObject *py_ue_add_anim_composite_section(ue_PyUObject *, PyObject *); PyObject *py_ue_anim_set_skeleton(ue_PyUObject *, PyObject *); PyObject *py_ue_get_blend_parameter(ue_PyUObject *, PyObject *); -PyObject *py_ue_set_blend_parameter(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_set_blend_parameter(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp index f73b22f47..4fd21b6b0 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAttaching.cpp @@ -2,12 +2,14 @@ -PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_location", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_location", &socket_name)) + { return NULL; } @@ -20,12 +22,14 @@ PyObject *py_ue_get_socket_location(ue_PyUObject *self, PyObject * args) { return py_ue_new_fvector(vec); } -PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_rotation", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_rotation", &socket_name)) + { return NULL; } @@ -38,12 +42,14 @@ PyObject *py_ue_get_socket_rotation(ue_PyUObject *self, PyObject * args) { return py_ue_new_frotator(rot); } -PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_transform", &socket_name)) + { return NULL; } @@ -56,12 +62,14 @@ PyObject *py_ue_get_socket_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_world_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_world_transform", &socket_name)) + { return NULL; } @@ -74,12 +82,14 @@ PyObject *py_ue_get_socket_world_transform(ue_PyUObject *self, PyObject * args) return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *socket_name; - if (!PyArg_ParseTuple(args, "s:get_socket_actor_transform", &socket_name)) { + if (!PyArg_ParseTuple(args, "s:get_socket_actor_transform", &socket_name)) + { return NULL; } @@ -92,11 +102,13 @@ PyObject *py_ue_get_socket_actor_transform(ue_PyUObject *self, PyObject * args) return py_ue_new_ftransform(transform); } -PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); PyObject *py_include_descendants = NULL; - if (!PyArg_ParseTuple(args, "|O:get_all_child_actors", &py_include_descendants)) { + if (!PyArg_ParseTuple(args, "|O:get_all_child_actors", &py_include_descendants)) + { return NULL; } @@ -114,15 +126,17 @@ PyObject *py_ue_get_all_child_actors(ue_PyUObject * self, PyObject * args) { TArray children; actor->GetAllChildActors(children, include_descendants); - for (AActor *child : children) { - ue_PyUObject *item = ue_get_python_wrapper(child); + for (AActor *child : children) + { + ue_PyUObject *item = ue_get_python_uobject(child); if (item) PyList_Append(py_children, (PyObject *)item); } return py_children; } -PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -135,15 +149,17 @@ PyObject *py_ue_get_attached_actors(ue_PyUObject * self, PyObject * args) { TArray children; actor->GetAttachedActors(children); - for (AActor *child : children) { - ue_PyUObject *item = ue_get_python_wrapper(child); + for (AActor *child : children) + { + ue_PyUObject *item = ue_get_python_uobject(child); if (item) PyList_Append(py_children, (PyObject *)item); } return py_children; } -PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -153,7 +169,8 @@ PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { int rotation_rule = (int)EAttachmentRule::KeepWorld; int scale_rule = (int)EAttachmentRule::SnapToTarget; PyObject *py_weld = nullptr; - if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_actor", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) { + if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_actor", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) + { return NULL; } @@ -182,7 +199,8 @@ PyObject *py_ue_attach_to_actor(ue_PyUObject * self, PyObject * args) { } -PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -192,7 +210,8 @@ PyObject *py_ue_attach_to_component(ue_PyUObject * self, PyObject * args) { int rotation_rule = (int)EAttachmentRule::KeepWorld; int scale_rule = (int)EAttachmentRule::SnapToTarget; PyObject *py_weld = nullptr; - if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_component", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) { + if (!PyArg_ParseTuple(args, "O|siiiO:attach_to_component", &obj, &socket_name, &location_rule, &rotation_rule, &scale_rule, &py_weld)) + { return NULL; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 48b5c40c8..02aec9493 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -2,14 +2,383 @@ #include "Runtime/MovieSceneCapture/Public/MovieSceneCapture.h" +#if WITH_EDITOR + +/* + +This is taken as-is (more or less) from MovieSceneCaptureDialogModule.cpp +to automate sequencer capturing. The only relevant implementation is the support +for a queue of UMovieSceneCapture objects + +*/ + +#include "AudioDevice.h" +#include "Editor/EditorEngine.h" +#include "Slate/SceneViewport.h" +#include "AutomatedLevelSequenceCapture.h" + +struct FInEditorMultiCapture : TSharedFromThis +{ + + static TWeakPtr CreateInEditorMultiCapture(TArray InCaptureObjects) + { + // FInEditorCapture owns itself, so should only be kept alive by itself, or a pinned (=> temporary) weakptr + FInEditorMultiCapture* Capture = new FInEditorMultiCapture; + Capture->CaptureObjects = InCaptureObjects; + for (UMovieSceneCapture *SceneCapture : Capture->CaptureObjects) + { + SceneCapture->AddToRoot(); + } + Capture->Dequeue(); + return Capture->AsShared(); + } + +private: + FInEditorMultiCapture() + { + CapturingFromWorld = nullptr; + } + + void Die() + { + for (UMovieSceneCapture *SceneCapture : CaptureObjects) + { + SceneCapture->RemoveFromRoot(); + } + OnlyStrongReference = nullptr; + } + + void Dequeue() + { + + if (CaptureObjects.Num() < 1) + { + Die(); + return; + } + + CurrentCaptureObject = CaptureObjects[0]; + + check(CurrentCaptureObject); + + CapturingFromWorld = nullptr; + + if (!OnlyStrongReference.IsValid()) + OnlyStrongReference = MakeShareable(this); + + ULevelEditorPlaySettings* PlayInEditorSettings = GetMutableDefault(); + + bScreenMessagesWereEnabled = GAreScreenMessagesEnabled; + GAreScreenMessagesEnabled = false; + + if (!CurrentCaptureObject->Settings.bEnableTextureStreaming) + { + const int32 UndefinedTexturePoolSize = -1; + IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); + if (CVarStreamingPoolSize) + { + BackedUpStreamingPoolSize = CVarStreamingPoolSize->GetInt(); + CVarStreamingPoolSize->Set(UndefinedTexturePoolSize, ECVF_SetByConsole); + } + + IConsoleVariable* CVarUseFixedPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.UseFixedPoolSize")); + if (CVarUseFixedPoolSize) + { + BackedUpUseFixedPoolSize = CVarUseFixedPoolSize->GetInt(); + CVarUseFixedPoolSize->Set(0, ECVF_SetByConsole); + } + } + + // cleanup from previous run + BackedUpPlaySettings.Empty(); + + FObjectWriter(PlayInEditorSettings, BackedUpPlaySettings); + + OverridePlaySettings(PlayInEditorSettings); + + //CurrentCaptureObject->AddToRoot(); + CurrentCaptureObject->OnCaptureFinished().AddRaw(this, &FInEditorMultiCapture::OnEnd); + + UGameViewportClient::OnViewportCreated().AddRaw(this, &FInEditorMultiCapture::OnStart); + FEditorDelegates::EndPIE.AddRaw(this, &FInEditorMultiCapture::OnEndPIE); + + FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); + if (AudioDevice != nullptr) + { + TransientMasterVolume = AudioDevice->GetTransientMasterVolume(); + AudioDevice->SetTransientMasterVolume(0.0f); + } + + // play at the next tick + FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FInEditorMultiCapture::PlaySession), 0); + } + + bool PlaySession(float DeltaTime) + { + GEditor->RequestPlaySession(true, nullptr, false); + return false; + } + + void OverridePlaySettings(ULevelEditorPlaySettings* PlayInEditorSettings) + { + const FMovieSceneCaptureSettings& Settings = CurrentCaptureObject->GetSettings(); + + PlayInEditorSettings->NewWindowWidth = Settings.Resolution.ResX; + PlayInEditorSettings->NewWindowHeight = Settings.Resolution.ResY; + PlayInEditorSettings->CenterNewWindow = true; + PlayInEditorSettings->LastExecutedPlayModeType = EPlayModeType::PlayMode_InEditorFloating; + + TSharedRef CustomWindow = SNew(SWindow) + .Title(FText::FromString("Movie Render - Preview")) + .AutoCenter(EAutoCenter::PrimaryWorkArea) + .UseOSWindowBorder(true) + .FocusWhenFirstShown(false) +#if ENGINE_MINOR_VERSION > 15 + .ActivationPolicy(EWindowActivationPolicy::Never) +#endif + .HasCloseButton(true) + .SupportsMaximize(false) + .SupportsMinimize(true) + .MaxWidth(Settings.Resolution.ResX) + .MaxHeight(Settings.Resolution.ResY) + .SizingRule(ESizingRule::FixedSize); + + FSlateApplication::Get().AddWindow(CustomWindow); + + PlayInEditorSettings->CustomPIEWindow = CustomWindow; + + // Reset everything else + PlayInEditorSettings->GameGetsMouseControl = false; + PlayInEditorSettings->ShowMouseControlLabel = false; + PlayInEditorSettings->ViewportGetsHMDControl = false; +#if ENGINE_MINOR_VERSION >= 17 + PlayInEditorSettings->ShouldMinimizeEditorOnVRPIE = true; + PlayInEditorSettings->EnableGameSound = false; +#endif + PlayInEditorSettings->bOnlyLoadVisibleLevelsInPIE = false; + PlayInEditorSettings->bPreferToStreamLevelsInPIE = false; + PlayInEditorSettings->PIEAlwaysOnTop = false; + PlayInEditorSettings->DisableStandaloneSound = true; + PlayInEditorSettings->AdditionalLaunchParameters = TEXT(""); + PlayInEditorSettings->BuildGameBeforeLaunch = EPlayOnBuildMode::PlayOnBuild_Never; + PlayInEditorSettings->LaunchConfiguration = EPlayOnLaunchConfiguration::LaunchConfig_Default; + PlayInEditorSettings->SetPlayNetMode(EPlayNetMode::PIE_Standalone); + PlayInEditorSettings->SetRunUnderOneProcess(true); + PlayInEditorSettings->SetPlayNetDedicated(false); + PlayInEditorSettings->SetPlayNumberOfClients(1); + } + + void OnStart() + { + for (const FWorldContext& Context : GEngine->GetWorldContexts()) + { + if (Context.WorldType == EWorldType::PIE) + { + FSlatePlayInEditorInfo* SlatePlayInEditorSession = GEditor->SlatePlayInEditorMap.Find(Context.ContextHandle); + if (SlatePlayInEditorSession) + { + CapturingFromWorld = Context.World(); + + TSharedPtr Window = SlatePlayInEditorSession->SlatePlayInEditorWindow.Pin(); + + const FMovieSceneCaptureSettings& Settings = CurrentCaptureObject->GetSettings(); + + SlatePlayInEditorSession->SlatePlayInEditorWindowViewport->SetViewportSize(Settings.Resolution.ResX, Settings.Resolution.ResY); + + FVector2D PreviewWindowSize(Settings.Resolution.ResX, Settings.Resolution.ResY); + + // Keep scaling down the window size while we're bigger than half the destop width/height + { + FDisplayMetrics DisplayMetrics; + FSlateApplication::Get().GetDisplayMetrics(DisplayMetrics); + + while (PreviewWindowSize.X >= DisplayMetrics.PrimaryDisplayWidth*.5f || PreviewWindowSize.Y >= DisplayMetrics.PrimaryDisplayHeight*.5f) + { + PreviewWindowSize *= .5f; + } + } + + // Resize and move the window into the desktop a bit + FVector2D PreviewWindowPosition(50, 50); + Window->ReshapeWindow(PreviewWindowPosition, PreviewWindowSize); + + if (CurrentCaptureObject->Settings.GameModeOverride != nullptr) + { + CachedGameMode = CapturingFromWorld->GetWorldSettings()->DefaultGameMode; + CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CurrentCaptureObject->Settings.GameModeOverride; + } + + CurrentCaptureObject->Initialize(SlatePlayInEditorSession->SlatePlayInEditorWindowViewport, Context.PIEInstance); + } + return; + } + } + + } + + void Shutdown() + { + FEditorDelegates::EndPIE.RemoveAll(this); + UGameViewportClient::OnViewportCreated().RemoveAll(this); + CurrentCaptureObject->OnCaptureFinished().RemoveAll(this); + + GAreScreenMessagesEnabled = bScreenMessagesWereEnabled; + + if (!CurrentCaptureObject->Settings.bEnableTextureStreaming) + { + IConsoleVariable* CVarStreamingPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.PoolSize")); + if (CVarStreamingPoolSize) + { + CVarStreamingPoolSize->Set(BackedUpStreamingPoolSize, ECVF_SetByConsole); + } + + IConsoleVariable* CVarUseFixedPoolSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Streaming.UseFixedPoolSize")); + if (CVarUseFixedPoolSize) + { + CVarUseFixedPoolSize->Set(BackedUpUseFixedPoolSize, ECVF_SetByConsole); + } + } + + if (CurrentCaptureObject->Settings.GameModeOverride != nullptr) + { + CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CachedGameMode; + } + + FObjectReader(GetMutableDefault(), BackedUpPlaySettings); -PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) { + FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); + if (AudioDevice != nullptr) + { + AudioDevice->SetTransientMasterVolume(TransientMasterVolume); + } + CurrentCaptureObject->Close(); + //CurrentCaptureObject->RemoveFromRoot(); + + } + void OnEndPIE(bool bIsSimulating) + { + Shutdown(); + + Die(); + } + + void NextCapture(bool bIsSimulating) + { + + FEditorDelegates::EndPIE.RemoveAll(this); + // remove item from the TArray; + CaptureObjects.RemoveAt(0); + + if (CaptureObjects.Num() > 0) + { + Dequeue(); + } + else + { + Die(); + } + } + + void OnEnd() + { + Shutdown(); + + FEditorDelegates::EndPIE.AddRaw(this, &FInEditorMultiCapture::NextCapture); + GEditor->RequestEndPlayMap(); + } + + TSharedPtr OnlyStrongReference; + UWorld* CapturingFromWorld; + + bool bScreenMessagesWereEnabled; + float TransientMasterVolume; + int32 BackedUpStreamingPoolSize; + int32 BackedUpUseFixedPoolSize; + TArray BackedUpPlaySettings; + UMovieSceneCapture* CurrentCaptureObject; + + TSubclassOf CachedGameMode; + TArray CaptureObjects; +}; + +PyObject *py_unreal_engine_in_editor_capture(PyObject * self, PyObject * args) +{ + PyObject *py_scene_captures; + + if (!PyArg_ParseTuple(args, "O:in_editor_capture", &py_scene_captures)) + { + return nullptr; + } + + TArray Captures; + + UMovieSceneCapture *capture = ue_py_check_type(py_scene_captures); + if (!capture) + { + PyObject *py_iter = PyObject_GetIter(py_scene_captures); + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneCapture or an iterable of UMovieSceneCapture"); + } + while (PyObject *py_item = PyIter_Next(py_iter)) + { + capture = ue_py_check_type(py_item); + if (!capture) + { + Py_DECREF(py_iter); + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UMovieSceneCapture"); + } + Captures.Add(capture); + } + Py_DECREF(py_iter); + } + else + { + Captures.Add(capture); + } + + FInEditorMultiCapture::CreateInEditorMultiCapture(Captures); + + Py_RETURN_NONE; +} + +PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *self, PyObject *args) +{ ue_py_check(self); - PyObject *py_widget; + PyObject *py_sequence = nullptr; - if (!PyArg_ParseTuple(args, "O", &py_widget)) { + if (!PyArg_ParseTuple(args, "O:set_level_sequence_asset", &py_sequence)) + { + return nullptr; + } + + ULevelSequence *sequence = ue_py_check_type(py_sequence); + if (!sequence) + { + return PyErr_Format(PyExc_Exception, "uobject is not a ULevelSequence"); + } + + UAutomatedLevelSequenceCapture *capture = ue_py_check_type(self); + if (!capture) + return PyErr_Format(PyExc_Exception, "uobject is not a UAutomatedLevelSequenceCapture"); + + capture->SetLevelSequenceAsset(sequence->GetPathName()); + + Py_RETURN_NONE; +} +#endif + +PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_widget = nullptr; + + if (!PyArg_ParseTuple(args, "|O:capture_initialize", &py_widget)) + { return nullptr; } @@ -17,26 +386,46 @@ PyObject *py_ue_capture_initialize(ue_PyUObject * self, PyObject * args) { if (!capture) return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); - ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); - if (!s_widget) - return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - #if WITH_EDITOR - if (s_widget->s_widget->GetType().Compare(FName("SPythonEditorViewport")) == 0) { - TSharedRef s_viewport = StaticCastSharedRef(s_widget->s_widget); - capture->Initialize(s_viewport->GetSceneViewport()); - capture->StartWarmup(); - } - else { - return PyErr_Format(PyExc_Exception, "argument is not a supported Viewport-based SWidget"); + if (py_widget) + { + ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); + if (!s_widget) + return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); + + + if (s_widget->s_widget->GetType().Compare(FName("SPythonEditorViewport")) == 0) + { + TSharedRef s_viewport = StaticCastSharedRef(s_widget->s_widget); + capture->Initialize(s_viewport->GetSceneViewport()); + capture->StartWarmup(); + } + else + { + return PyErr_Format(PyExc_Exception, "argument is not a supported Viewport-based SWidget"); + } + } #endif + Py_RETURN_NONE; +} + +PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + UMovieSceneCapture *capture = ue_py_check_type(self); + if (!capture) + return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); + + capture->StartCapture(); Py_RETURN_NONE; } -PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_capture_load_from_config(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -44,12 +433,13 @@ PyObject *py_ue_capture_start(ue_PyUObject * self, PyObject * args) { if (!capture) return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneCapture"); - capture->StartCapture(); + capture->LoadFromConfig(); Py_RETURN_NONE; } -PyObject *py_ue_capture_stop(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_capture_stop(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h index 55de8ddcf..1ef58340d 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.h @@ -6,4 +6,8 @@ PyObject *py_ue_capture_initialize(ue_PyUObject *, PyObject *); PyObject *py_ue_capture_start(ue_PyUObject *, PyObject *); -PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_capture_load_from_config(ue_PyUObject *, PyObject *); +PyObject *py_ue_capture_stop(ue_PyUObject *, PyObject *); + +PyObject *py_ue_set_level_sequence_asset(ue_PyUObject *, PyObject *); +PyObject *py_unreal_engine_in_editor_capture(PyObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp index 3f0c845fd..3dcd6e2a2 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyController.cpp @@ -35,11 +35,7 @@ PyObject *py_ue_controller_get_hud(ue_PyUObject * self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "uobject is not an AController"); - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)controller->GetHUD()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)controller->GetHUD()); } PyObject *py_ue_controller_unposses(ue_PyUObject * self, PyObject * args) @@ -75,11 +71,7 @@ PyObject *py_ue_get_controlled_pawn(ue_PyUObject * self, PyObject * args) Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(pawn); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(pawn); } PyObject *py_ue_controller_project_world_location_to_screen(ue_PyUObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp new file mode 100644 index 000000000..3edbc509d --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.cpp @@ -0,0 +1,39 @@ +#include "UnrealEnginePythonPrivatePCH.h" + + +#include "Exporters/Exporter.h" + + +PyObject *py_ue_export_to_file(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_object; + char *filename; + + if (!PyArg_ParseTuple(args, "Os:export_to_file", &py_object, &filename)) + { + return nullptr; + } + + UExporter *Exporter = ue_py_check_type(self); + if (!Exporter) + { + return PyErr_Format(PyExc_Exception, "uobject is not a UExporter"); + } + + UObject *Object = ue_py_check_type(py_object); + if (!Object) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + + if (UExporter::ExportToFile(Object, Exporter, UTF8_TO_TCHAR(filename), false, false, false) > 0) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + + diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h new file mode 100644 index 000000000..72eda9b35 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyExporter.h @@ -0,0 +1,8 @@ +#pragma once + + + +#include "UnrealEnginePython.h" + + +PyObject *py_ue_export_to_file(ue_PyUObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp index 458a1d5fd..47578a83b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyInput.cpp @@ -380,7 +380,7 @@ PyObject *py_ue_bind_action(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } @@ -405,19 +405,13 @@ PyObject *py_ue_bind_action(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputActionBinding input_action_binding(FName(UTF8_TO_TCHAR(action_name)), (const EInputEvent)key); input_action_binding.ActionDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); input->AddActionBinding(input_action_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -433,7 +427,7 @@ PyObject *py_ue_bind_axis(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } @@ -458,19 +452,13 @@ PyObject *py_ue_bind_axis(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputAxisBinding input_axis_binding(FName(UTF8_TO_TCHAR(axis_name))); input_axis_binding.AxisDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputAxisHandler); input->AxisBindings.Add(input_axis_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -487,7 +475,7 @@ PyObject *py_ue_bind_key(ue_PyUObject *self, PyObject * args) return NULL; } - if (!PyCallable_Check(py_callable)) + if (!PyCalllable_Check_Extended(py_callable)) { return PyErr_Format(PyExc_Exception, "object is not a callable"); } @@ -515,19 +503,13 @@ PyObject *py_ue_bind_key(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "no input manager for this uobject"); } - UPythonDelegate *py_delegate = NewObject(); - py_delegate->SetPyCallable(py_callable); - py_delegate->AddToRoot(); - - // allow the delegate to not be destroyed - self->python_delegates_gc->push_back(py_delegate); + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(input, py_callable, nullptr); FInputKeyBinding input_key_binding(FKey(UTF8_TO_TCHAR(key_name)), (const EInputEvent)key); input_key_binding.KeyDelegate.BindDelegate(py_delegate, &UPythonDelegate::PyInputHandler); input->KeyBindings.Add(input_key_binding); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp index ec76ddd59..900c1434a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp @@ -12,11 +12,7 @@ PyObject *py_ue_create_landscape_info(ue_PyUObject *self, PyObject * args) if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); - ue_PyUObject *ret = ue_get_python_wrapper(landscape->CreateLandscapeInfo()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(landscape->CreateLandscapeInfo()); } PyObject *py_ue_get_landscape_info(ue_PyUObject *self, PyObject * args) @@ -32,11 +28,7 @@ PyObject *py_ue_get_landscape_info(ue_PyUObject *self, PyObject * args) if (!info) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(info); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(info); } PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index a2f90161f..381e3ee6a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -7,13 +7,15 @@ #include "Editor/UnrealEd/Classes/MaterialGraph/MaterialGraphSchema.h" #endif -PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; float scalarValue = 0; - if (!PyArg_ParseTuple(args, "sf:set_material_scalar_parameter", &scalarName, &scalarValue)) { + if (!PyArg_ParseTuple(args, "sf:set_material_scalar_parameter", &scalarName, &scalarValue)) + { return NULL; } @@ -22,38 +24,43 @@ PyObject *py_ue_set_material_scalar_parameter(ue_PyUObject *self, PyObject * arg bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetScalarParameterValueEditorOnly(parameterName, scalarValue); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetScalarParameterValue(parameterName, scalarValue); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_scalar_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_scalar_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -69,16 +76,19 @@ PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * arg } -PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *switchName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_static_switch_parameter", &switchName)) { + if (!PyArg_ParseTuple(args, "s:get_material_static_switch_parameter", &switchName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -91,40 +101,45 @@ PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObjec FGuid guid; material_instance->GetStaticSwitchParameterValue(parameterName, value, guid); - if (value) { - Py_INCREF(Py_True); - return Py_True; + if (value) + { + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } -PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *vectorName; PyObject *vectorValue = nullptr; - if (!PyArg_ParseTuple(args, "sO:set_material_vector_parameter", &vectorName, &vectorValue)) { + if (!PyArg_ParseTuple(args, "sO:set_material_vector_parameter", &vectorName, &vectorValue)) + { return NULL; } FLinearColor vectorParameter(0, 0, 0, 0); ue_PyFLinearColor *py_vector = py_ue_is_flinearcolor(vectorValue); - if (!py_vector) { + if (!py_vector) + { ue_PyFVector *py_true_vector = py_ue_is_fvector(vectorValue); - if (!py_true_vector) { + if (!py_true_vector) + { return PyErr_Format(PyExc_Exception, "argument must be an FLinearColor or FVector"); } - else { + else + { vectorParameter.R = py_true_vector->vec.X; vectorParameter.G = py_true_vector->vec.Y; vectorParameter.B = py_true_vector->vec.Z; vectorParameter.A = 1; } } - else { + else + { vectorParameter = py_vector->color; } @@ -133,37 +148,42 @@ PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * arg bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetVectorParameterValueEditorOnly(parameterName, vectorParameter); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetVectorParameterValue(parameterName, vectorParameter); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_vector_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_vector_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -179,16 +199,19 @@ PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *self, PyObject * arg } -PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *textureName; PyObject *textureObject = nullptr; - if (!PyArg_ParseTuple(args, "sO:set_texture_parameter", &textureName, &textureObject)) { + if (!PyArg_ParseTuple(args, "sO:set_texture_parameter", &textureName, &textureObject)) + { return NULL; } - if (!ue_is_pyuobject(textureObject)) { + if (!ue_is_pyuobject(textureObject)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -203,37 +226,42 @@ PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *self, PyObject * ar bool valid = false; #if WITH_EDITOR - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceConstant *material_instance = (UMaterialInstanceConstant *)self->ue_object; material_instance->SetTextureParameterValueEditorOnly(parameterName, ue_texture); valid = true; } #endif - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMaterialInstanceDynamic *material_instance = (UMaterialInstanceDynamic *)self->ue_object; material_instance->SetTextureParameterValue(parameterName, ue_texture); valid = true; } - if (!valid) { + if (!valid) + { return PyErr_Format(PyExc_Exception, "uobject is not a MaterialInstance"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *scalarName = nullptr; - if (!PyArg_ParseTuple(args, "s:get_material_texture_parameter", &scalarName)) { + if (!PyArg_ParseTuple(args, "s:get_material_texture_parameter", &scalarName)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstance"); } @@ -243,25 +271,23 @@ PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *self, PyObject * ar UTexture *texture = nullptr; - if (!material_instance->GetTextureParameterValue(parameterName, texture)) { - Py_INCREF(Py_None); - return Py_None; + if (!material_instance->GetTextureParameterValue(parameterName, texture)) + { + Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } -PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "O:create_material_instance_dynamic", &py_material)) { + if (!PyArg_ParseTuple(args, "O:create_material_instance_dynamic", &py_material)) + { return nullptr; } @@ -271,38 +297,38 @@ PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *self, PyObject * UMaterialInstanceDynamic *material_dynamic = UMaterialInstanceDynamic::Create(material_interface, self->ue_object); - ue_PyUObject *ret = ue_get_python_wrapper(material_dynamic); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(material_dynamic); } -PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int index; PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "iO:set_material", &index, &py_material)) { + if (!PyArg_ParseTuple(args, "iO:set_material", &index, &py_material)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UPrimitiveComponent"); } UPrimitiveComponent *component = (UPrimitiveComponent *)self->ue_object; - if (!ue_is_pyuobject(py_material)) { + if (!ue_is_pyuobject(py_material)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } ue_PyUObject *py_obj = (ue_PyUObject *)py_material; - if (!py_obj->ue_object->IsA()) { + if (!py_obj->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInterface"); } @@ -310,29 +336,32 @@ PyObject *py_ue_set_material(ue_PyUObject *self, PyObject * args) { component->SetMaterial(index, material_interface); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } #if WITH_EDITOR -PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_material = nullptr; - if (!PyArg_ParseTuple(args, "O:set_parent", &py_material)) { + if (!PyArg_ParseTuple(args, "O:set_parent", &py_material)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInstanceConstant"); } - if (!ue_is_pyuobject(py_material)) { + if (!ue_is_pyuobject(py_material)) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } @@ -346,30 +375,33 @@ PyObject *py_ue_set_material_parent(ue_PyUObject *self, PyObject * args) { material_instance->SetParentEditorOnly(materialInterface); material_instance->PostEditChange(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int lod_index; int material_index; PyObject *py_bool; - if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_collision_for_lod", &lod_index, &material_index, &py_bool)) { + if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_collision_for_lod", &lod_index, &material_index, &py_bool)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a StaticMesh"); } UStaticMesh *mesh = (UStaticMesh *)self->ue_object; bool enabled = false; - if (PyObject_IsTrue(py_bool)) { + if (PyObject_IsTrue(py_bool)) + { enabled = true; } @@ -383,25 +415,29 @@ PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * return Py_None; } -PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int lod_index; int material_index; PyObject *py_bool; - if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_shadow_for_lod", &lod_index, &material_index, &py_bool)) { + if (!PyArg_ParseTuple(args, "iiO:static_mesh_set_shadow_for_lod", &lod_index, &material_index, &py_bool)) + { return NULL; } - if (!self->ue_object->IsA()) { + if (!self->ue_object->IsA()) + { return PyErr_Format(PyExc_Exception, "uobject is not a StaticMesh"); } UStaticMesh *mesh = (UStaticMesh *)self->ue_object; bool enabled = false; - if (PyObject_IsTrue(py_bool)) { + if (PyObject_IsTrue(py_bool)) + { enabled = true; } @@ -411,31 +447,28 @@ PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * ar mesh->MarkPackageDirty(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_material_graph(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (!self->ue_object->IsA()) { + UMaterial *material = ue_py_check_type(self); + if (!material) return PyErr_Format(PyExc_Exception, "uobject is not a UMaterialInterface"); - } - - UMaterial *material = (UMaterial *)self->ue_object; UMaterialGraph *graph = material->MaterialGraph; if (!graph) - material->MaterialGraph = (UMaterialGraph *)FBlueprintEditorUtils::CreateNewGraph(material, NAME_None, UMaterialGraph::StaticClass(), UMaterialGraphSchema::StaticClass()); + { + graph = (UMaterialGraph *)FBlueprintEditorUtils::CreateNewGraph(material, NAME_None, UMaterialGraph::StaticClass(), UMaterialGraphSchema::StaticClass()); + material->MaterialGraph = graph; + } if (!graph) return PyErr_Format(PyExc_Exception, "Unable to retrieve/allocate MaterialGraph"); - ue_PyUObject *ret = ue_get_python_wrapper(graph); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(graph); } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index e987c84e2..5420dd74a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -7,6 +7,7 @@ #include "Runtime/AssetRegistry/Public/AssetRegistryModule.h" #include "ObjectTools.h" #include "UnrealEd.h" +#include "Runtime/Core/Public/HAL/FeedbackContextAnsi.h" #endif PyObject *py_ue_get_class(ue_PyUObject * self, PyObject * args) @@ -14,11 +15,7 @@ PyObject *py_ue_get_class(ue_PyUObject * self, PyObject * args) ue_py_check(self); - ue_PyUObject *ret = ue_get_python_wrapper(self->ue_object->GetClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(self->ue_object->GetClass()); } PyObject *py_ue_class_generated_by(ue_PyUObject * self, PyObject * args) @@ -34,11 +31,7 @@ PyObject *py_ue_class_generated_by(ue_PyUObject * self, PyObject * args) if (!u_object) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } PyObject *py_ue_class_get_flags(ue_PyUObject * self, PyObject * args) @@ -73,6 +66,30 @@ PyObject *py_ue_class_set_flags(ue_PyUObject * self, PyObject * args) Py_RETURN_NONE; } +PyObject *py_ue_get_obj_flags(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + return PyLong_FromUnsignedLongLong((uint64)self->ue_object->GetFlags()); +} + +PyObject *py_ue_set_obj_flags(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + uint64 flags; + if (!PyArg_ParseTuple(args, "K:set_obj_flags", &flags)) + { + return nullptr; + } + + self->ue_object->SetFlags((EObjectFlags)flags); + + Py_RETURN_NONE; +} + + #if WITH_EDITOR PyObject *py_ue_class_set_config_name(ue_PyUObject * self, PyObject * args) { @@ -156,11 +173,7 @@ PyObject *py_ue_get_super_class(ue_PyUObject * self, PyObject * args) u_class = self->ue_object->GetClass(); } - ue_PyUObject *ret = ue_get_python_wrapper(u_class->GetSuperClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class->GetSuperClass()); } PyObject *py_ue_get_outer(ue_PyUObject *self, PyObject * args) @@ -172,11 +185,7 @@ PyObject *py_ue_get_outer(ue_PyUObject *self, PyObject * args) if (!outer) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(outer); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(outer); } PyObject *py_ue_get_outermost(ue_PyUObject *self, PyObject * args) @@ -188,11 +197,7 @@ PyObject *py_ue_get_outermost(ue_PyUObject *self, PyObject * args) if (!outermost) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(outermost); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(outermost); } PyObject *py_ue_conditional_begin_destroy(ue_PyUObject *self, PyObject * args) @@ -513,12 +518,7 @@ PyObject *py_ue_find_function(ue_PyUObject * self, PyObject * args) return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)function); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT((UObject *)function); } #if ENGINE_MINOR_VERSION >= 15 @@ -604,6 +604,11 @@ PyObject *py_ue_get_display_name(ue_PyUObject *self, PyObject * args) ue_py_check(self); #if WITH_EDITOR + if (UClass *uclass = ue_py_check_type(self)) + { + return PyUnicode_FromString(TCHAR_TO_UTF8(*uclass->GetDisplayNameText().ToString())); + } + if (AActor *actor = ue_py_check_type(self)) { return PyUnicode_FromString(TCHAR_TO_UTF8(*actor->GetActorLabel())); @@ -1061,11 +1066,7 @@ PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); - ue_PyUObject *ret = ue_get_python_wrapper(u_property); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property); } @@ -1123,11 +1124,8 @@ PyObject *py_ue_get_property_class(ue_PyUObject *self, PyObject * args) if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); - ue_PyUObject *ret = ue_get_python_wrapper(u_property->GetClass()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property->GetClass()); + } PyObject *py_ue_is_rooted(ue_PyUObject *self, PyObject * args) @@ -1566,11 +1564,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) // TODO add default value - ue_PyUObject *ret = ue_get_python_wrapper(u_property); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_property); } PyObject *py_ue_as_dict(ue_PyUObject * self, PyObject * args) @@ -1622,11 +1616,7 @@ PyObject *py_ue_get_cdo(ue_PyUObject * self, PyObject * args) UClass *u_class = (UClass *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(u_class->GetDefaultObject()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_class->GetDefaultObject()); } @@ -1662,7 +1652,7 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) package = (UPackage *)outer; has_package = true; } - else if (u_object->IsA() && u_object != GetTransientPackage()) + else if (u_object && u_object->IsA() && u_object != GetTransientPackage()) { package = (UPackage *)u_object; has_package = true; @@ -1724,19 +1714,46 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) UE_LOG(LogPython, Warning, TEXT("no file mapped to UPackage %s, setting its FileName to %s"), *package->GetPathName(), *package->FileName.ToString()); } + // NOTE: FileName may not be a fully qualified filepath + if (FPackageName::IsValidLongPackageName(package->FileName.ToString())) + { + package->FileName = *FPackageName::LongPackageNameToFilename(package->GetPathName(), FPackageName::GetAssetPackageExtension()); + } + if (UPackage::SavePackage(package, u_object, RF_Public | RF_Standalone, *package->FileName.ToString())) { FAssetRegistryModule::AssetCreated(u_object); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } return PyErr_Format(PyExc_Exception, "unable to save package"); } +PyObject *py_ue_import_custom_properties(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + char *t3d; + + if (!PyArg_ParseTuple(args, "s:import_custom_properties", &t3d)) + { + return nullptr; + } + + FFeedbackContextAnsi context; + self->ue_object->ImportCustomProperties(UTF8_TO_TCHAR(t3d), &context); + + TArray errors; + context.GetErrors(errors); + + if (errors.Num() > 0) + { + return PyErr_Format(PyExc_Exception, "%s", TCHAR_TO_UTF8(*errors[0])); + } + + Py_RETURN_NONE; +} + PyObject *py_ue_asset_can_reimport(ue_PyUObject * self, PyObject * args) { @@ -1813,12 +1830,50 @@ PyObject *py_ue_duplicate(ue_PyUObject * self, PyObject * args) if (!new_asset) return PyErr_Format(PyExc_Exception, "unable to duplicate object"); - ue_PyUObject *ret = ue_get_python_wrapper(new_asset); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(new_asset); } #endif + +PyObject *py_ue_to_bytes(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + TArray Bytes; + + FObjectWriter(self->ue_object, Bytes); + + return PyBytes_FromStringAndSize((const char *)Bytes.GetData(), Bytes.Num()); +} + +PyObject *py_ue_to_bytearray(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + TArray Bytes; + + FObjectWriter(self->ue_object, Bytes); + + return PyByteArray_FromStringAndSize((const char *)Bytes.GetData(), Bytes.Num()); +} + +PyObject *py_ue_from_bytes(ue_PyUObject * self, PyObject * args) +{ + + Py_buffer py_buf; + + if (!PyArg_ParseTuple(args, "z*:from_bytes", &py_buf)) + return nullptr; + + ue_py_check(self); + + TArray Bytes; + Bytes.AddUninitialized(py_buf.len); + FMemory::Memcpy(Bytes.GetData(), py_buf.buf, py_buf.len); + + FObjectReader(self->ue_object, Bytes); + + Py_RETURN_NONE; +} diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index c32e9776c..a941f2ce9 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -69,6 +69,8 @@ PyObject *py_ue_class_generated_by(ue_PyUObject *, PyObject *); PyObject *py_ue_class_get_flags(ue_PyUObject *, PyObject *); PyObject *py_ue_class_set_flags(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_obj_flags(ue_PyUObject *, PyObject *); +PyObject *py_ue_set_obj_flags(ue_PyUObject *, PyObject *); #if WITH_EDITOR @@ -82,7 +84,13 @@ PyObject *py_ue_asset_reimport(ue_PyUObject *, PyObject *); PyObject *py_ue_get_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_set_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_has_metadata(ue_PyUObject *, PyObject *); + +PyObject *py_ue_import_custom_properties(ue_PyUObject *, PyObject *); #endif PyObject *py_ue_get_thumbnail(ue_PyUObject *, PyObject *); -PyObject *py_ue_render_thumbnail(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_render_thumbnail(ue_PyUObject *, PyObject *); + +PyObject *py_ue_to_bytes(ue_PyUObject *, PyObject *); +PyObject *py_ue_to_bytearray(ue_PyUObject *, PyObject *); +PyObject *py_ue_from_bytes(ue_PyUObject *, PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp index 5b817a2fa..9a2f8124f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPawn.cpp @@ -13,11 +13,7 @@ PyObject *py_ue_pawn_get_controller(ue_PyUObject * self, PyObject * args) APawn *pawn = (APawn *)self->ue_object; - ue_PyUObject *ret = ue_get_python_wrapper(pawn->GetController()); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(pawn->GetController()); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp index 3c1e829a2..4abfde503 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPhysics.cpp @@ -137,10 +137,13 @@ PyObject *py_ue_add_angular_impulse(ue_PyUObject * self, PyObject * args) if (py_obj_b_vel_change && PyObject_IsTrue(py_obj_b_vel_change)) b_vel_change = true; +#if ENGINE_MINOR_VERSION >= 18 + primitive->AddAngularImpulseInRadians(impulse, f_bone_name, b_vel_change); +#else primitive->AddAngularImpulse(impulse, f_bone_name, b_vel_change); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -237,10 +240,13 @@ PyObject *py_ue_add_torque(ue_PyUObject * self, PyObject * args) if (py_obj_b_accel_change && PyObject_IsTrue(py_obj_b_accel_change)) b_accel_change = true; +#if ENGINE_MINOR_VERSION >= 18 + primitive->AddTorqueInRadians(torque, f_bone_name, b_accel_change); +#else primitive->AddTorque(torque, f_bone_name, b_accel_change); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } @@ -369,13 +375,15 @@ PyObject *py_ue_set_physics_angular_velocity(ue_PyUObject * self, PyObject * arg f_bone_name = FName(UTF8_TO_TCHAR(bone_name)); } +#if ENGINE_MINOR_VERSION >= 18 + primitive->SetPhysicsAngularVelocityInDegrees(new_ang_vel, add_to_current, f_bone_name); +#else primitive->SetPhysicsAngularVelocity(new_ang_vel, add_to_current, f_bone_name); +#endif - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - PyObject *py_ue_get_physics_angular_velocity(ue_PyUObject * self, PyObject * args) { @@ -458,11 +466,11 @@ PyObject *py_ue_destructible_apply_damage(ue_PyUObject * self, PyObject * args) #if ENGINE_MINOR_VERSION < 18 destructible = (UDestructibleComponent *)actor->GetComponentByClass(UDestructibleComponent::StaticClass()); #else - for (UActorComponent *component : actor->GetComponents()) + for (UActorComponent *checked_component : actor->GetComponents()) { - if (Cast(component)) + if (Cast(checked_component)) { - destructible = (IDestructibleInterface *)component; + destructible = (IDestructibleInterface *)checked_component; break; } } @@ -501,4 +509,4 @@ PyObject *py_ue_destructible_apply_damage(ue_PyUObject * self, PyObject * args) } Py_RETURN_NONE; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp index 0852250fe..745c1f426 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp @@ -19,11 +19,7 @@ PyObject *py_ue_get_player_controller(ue_PyUObject *self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "unable to retrieve controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(controller); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller); } PyObject *py_ue_get_player_camera_manager(ue_PyUObject *self, PyObject * args) @@ -45,11 +41,7 @@ PyObject *py_ue_get_player_camera_manager(ue_PyUObject *self, PyObject * args) if (!camera) return PyErr_Format(PyExc_Exception, "unable to retrieve camera manager for controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(camera); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(camera); } PyObject *py_ue_get_player_hud(ue_PyUObject *self, PyObject * args) @@ -71,11 +63,7 @@ PyObject *py_ue_get_player_hud(ue_PyUObject *self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "unable to retrieve controller %d", controller_id); - ue_PyUObject *ret = ue_get_python_wrapper(controller->GetHUD()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller->GetHUD()); } PyObject *py_ue_set_player_hud(ue_PyUObject *self, PyObject * args) @@ -130,11 +118,7 @@ PyObject *py_ue_get_player_pawn(ue_PyUObject *self, PyObject * args) if (!controller->GetPawn()) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(controller->GetPawn()); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(controller->GetPawn()); } PyObject *py_ue_create_player(ue_PyUObject *self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 6218631ce..3bd6669d3 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -23,12 +23,14 @@ #endif #if WITH_EDITOR -PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_bool = nullptr; - if (!PyArg_ParseTuple(args, "|O:sequencer_changed", &py_bool)) { + if (!PyArg_ParseTuple(args, "|O:sequencer_changed", &py_bool)) + { return NULL; } @@ -39,13 +41,15 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { ULevelSequence *seq = (ULevelSequence *)self->ue_object; - if (py_bool && PyObject_IsTrue(py_bool)) { + if (py_bool && PyObject_IsTrue(py_bool)) + { // try to open the editor for the asset FAssetEditorManager::Get().OpenEditorForAsset(seq); } IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (editor) { + if (editor) + { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; ISequencer *sequencer = toolkit->GetSequencer().Get(); #if ENGINE_MINOR_VERSION < 13 @@ -61,12 +65,14 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { } #endif -PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_possessable_tracks", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_possessable_tracks", &guid)) + { return NULL; } @@ -74,7 +80,8 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } @@ -88,12 +95,15 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return PyErr_Format(PyExc_Exception, "GUID not found"); TArray bindings = scene->GetBindings(); - for (FMovieSceneBinding binding : bindings) { + for (FMovieSceneBinding binding : bindings) + { if (binding.GetObjectGuid() != f_guid) continue; - for (UMovieSceneTrack *track : binding.GetTracks()) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)track); - if (!ret) { + for (UMovieSceneTrack *track : binding.GetTracks()) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)track); + if (!ret) + { Py_DECREF(py_tracks); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -105,52 +115,64 @@ PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *self, PyObject * args return py_tracks; } -PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_find_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_find_possessable", &guid)) { - return NULL; + PyObject *py_parent = nullptr; + if (!PyArg_ParseTuple(args, "s|O:sequencer_find_possessable", &guid, &py_parent)) + { + return nullptr; } - if (!self->ue_object->IsA()) - return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + { + return PyErr_Format(PyExc_Exception, "uobject is not a ULevelSequence"); + } FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } - ULevelSequence *seq = (ULevelSequence *)self->ue_object; - #if ENGINE_MINOR_VERSION < 15 UObject *u_obj = seq->FindPossessableObject(f_guid, seq); #else + UObject *parent = nullptr; + if (py_parent) + { + parent = ue_py_check_type(py_parent); + if (!parent) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + } UObject *u_obj = nullptr; TArray> u_objects; - seq->LocateBoundObjects(f_guid, nullptr, u_objects); - if (u_objects.Num() > 0) { + seq->LocateBoundObjects(f_guid, parent, u_objects); + if (u_objects.Num() > 0) + { u_obj = u_objects[0]; } #endif if (!u_obj) return PyErr_Format(PyExc_Exception, "unable to find uobject with GUID \"%s\"", guid); - PyObject *ret = (PyObject *)ue_get_python_wrapper(u_obj); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; -} + Py_RETURN_UOBJECT(u_obj); + } -PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_find_spawnable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_find_spawnable", &guid)) + { return NULL; } @@ -158,7 +180,8 @@ PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid GUID"); } @@ -171,12 +194,14 @@ PyObject *py_ue_sequencer_find_spawnable(ue_PyUObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_possessable", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_possessable", &py_obj)) + { return NULL; } @@ -195,7 +220,8 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { name = actor->GetActorLabel(); FGuid new_guid = seq->MovieScene->AddPossessable(name, py_ue_obj->ue_object->GetClass()); - if (!new_guid.IsValid()) { + if (!new_guid.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to possess object"); } @@ -204,12 +230,14 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *self, PyObject * args) { return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } -PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_actor", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_actor", &py_obj)) + { return NULL; } @@ -232,12 +260,14 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { FAssetEditorManager::Get().OpenEditorForAsset(seq); IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (editor) { + if (editor) + { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; ISequencer *sequencer = toolkit->GetSequencer().Get(); sequencer->AddActors(actors); } - else { + else + { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); } @@ -248,19 +278,71 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) { #else FGuid new_guid = seq->FindPossessableObjectId(u_obj, u_obj.GetWorld()); #endif - if (!new_guid.IsValid()) { + if (!new_guid.IsValid()) + { + return PyErr_Format(PyExc_Exception, "unable to find guid"); + } + + return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); + } + +PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:sequencer_add_actor_component", &py_obj)) + { + return NULL; + } + + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + + ue_PyUObject *py_ue_obj = ue_is_pyuobject(py_obj); + if (!py_ue_obj) + return PyErr_Format(PyExc_Exception, "argument is not a uobject"); + + if (!py_ue_obj->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "argument is not an actorcomponent"); + + ULevelSequence *seq = (ULevelSequence *)self->ue_object; + + UActorComponent* actorComponent = (UActorComponent *)py_ue_obj->ue_object; + + // try to open the editor for the asset + FAssetEditorManager::Get().OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + FGuid new_guid; + if (editor) + { + FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; + ISequencer *sequencer = toolkit->GetSequencer().Get(); + new_guid = sequencer->GetHandleToObject(actorComponent); + } + else + { + return PyErr_Format(PyExc_Exception, "unable to access sequencer"); + } + + if (!new_guid.IsValid()) + { return PyErr_Format(PyExc_Exception, "unable to find guid"); } return PyUnicode_FromString(TCHAR_TO_UTF8(*new_guid.ToString())); } -PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_spawnable", &py_obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_make_new_spawnable", &py_obj)) + { return NULL; } @@ -282,7 +364,8 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args FAssetEditorManager::Get().OpenEditorForAsset(seq); IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); - if (!editor) { + if (!editor) + { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); } @@ -294,7 +377,8 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args } #endif -PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -308,9 +392,11 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { TArray tracks = scene->GetMasterTracks(); - for (UMovieSceneTrack *track : tracks) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)track); - if (!ret) { + for (UMovieSceneTrack *track : tracks) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)track); + if (!ret) + { Py_DECREF(py_tracks); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -320,7 +406,28 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *self, PyObject * args) { return py_tracks; } -PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_get_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + + ULevelSequence *seq = (ULevelSequence *)self->ue_object; + UMovieScene *scene = seq->GetMovieScene(); + + UMovieSceneTrack *track = scene->GetCameraCutTrack(); + if (!track) + { + return PyErr_Format(PyExc_Exception, "unable to find camera cut track"); + } + + Py_RETURN_UOBJECT(track); +} + +PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -331,7 +438,8 @@ PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { UMovieScene *scene = seq->GetMovieScene(); PyObject *py_possessables = PyList_New(0); - for (int32 i = 0; i < scene->GetPossessableCount(); i++) { + for (int32 i = 0; i < scene->GetPossessableCount(); i++) + { FMovieScenePossessable possessable = scene->GetPossessable(i); PyObject *py_possessable = py_ue_new_uscriptstruct(possessable.StaticStruct(), (uint8 *)&possessable); PyList_Append(py_possessables, py_possessable); @@ -340,7 +448,8 @@ PyObject *py_ue_sequencer_possessables(ue_PyUObject *self, PyObject * args) { return py_possessables; } -PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -351,7 +460,8 @@ PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) UMovieScene *scene = seq->GetMovieScene(); PyObject *py_possessables = PyList_New(0); - for (int32 i = 0; i < scene->GetPossessableCount(); i++) { + for (int32 i = 0; i < scene->GetPossessableCount(); i++) + { FMovieScenePossessable possessable = scene->GetPossessable(i); PyObject *py_possessable = PyUnicode_FromString(TCHAR_TO_UTF8(*possessable.GetGuid().ToString())); PyList_Append(py_possessables, py_possessable); @@ -361,7 +471,8 @@ PyObject *py_ue_sequencer_possessables_guid(ue_PyUObject *self, PyObject * args) } #if WITH_EDITOR -PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -369,19 +480,23 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); PyObject *py_obj = nullptr; - if (!PyArg_ParseTuple(args, "|O:sequencer_folders", &py_obj)) { + if (!PyArg_ParseTuple(args, "|O:sequencer_folders", &py_obj)) + { return NULL; } UMovieSceneFolder *parent_folder = nullptr; - if (py_obj) { + if (py_obj) + { ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } parent_folder = Cast(ue_py_obj->ue_object); - if (!parent_folder) { + if (!parent_folder) + { return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneFolder"); } } @@ -394,13 +509,16 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { TArray folders; if (!parent_folder) folders = scene->GetRootFolders(); - else { + else + { folders = parent_folder->GetChildFolders(); } - for (UMovieSceneFolder *folder : folders) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)folder); - if (!ret) { + for (UMovieSceneFolder *folder : folders) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)folder); + if (!ret) + { Py_DECREF(py_folders); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -410,7 +528,8 @@ PyObject *py_ue_sequencer_folders(ue_PyUObject *self, PyObject * args) { return py_folders; } -PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -419,19 +538,23 @@ PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { PyObject *py_obj = nullptr; char *name; - if (!PyArg_ParseTuple(args, "s|O:sequencer_create_folder", &name, &py_obj)) { + if (!PyArg_ParseTuple(args, "s|O:sequencer_create_folder", &name, &py_obj)) + { return NULL; } UMovieSceneFolder *parent_folder = nullptr; - if (py_obj) { + if (py_obj) + { ue_PyUObject *ue_py_obj = ue_is_pyuobject(py_obj); - if (!ue_py_obj) { + if (!ue_py_obj) + { return PyErr_Format(PyExc_Exception, "argument is not a UObject"); } parent_folder = Cast(ue_py_obj->ue_object); - if (!parent_folder) { + if (!parent_folder) + { return PyErr_Format(PyExc_Exception, "argument is not a UMovieSceneFolder"); } } @@ -442,24 +565,23 @@ PyObject *py_ue_sequencer_create_folder(ue_PyUObject *self, PyObject * args) { UMovieSceneFolder *new_folder = NewObject(scene, NAME_None, RF_Transactional); new_folder->SetFolderName(FName(name)); - if (parent_folder) { + if (parent_folder) + { parent_folder->Modify(); parent_folder->AddChildFolder(new_folder); } - else { + else + { scene->Modify(); scene->GetRootFolders().Add(new_folder); } - PyObject *ret = (PyObject *)ue_get_python_wrapper(new_folder); - if (!ret) { - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - } - return ret; + Py_RETURN_UOBJECT(new_folder); } #endif -PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -473,9 +595,12 @@ PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { TArray sections = scene->GetAllSections(); - for (UMovieSceneSection *section : sections) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)section); - if (!ret) { + for (UMovieSceneSection *section : sections) + { + + ue_PyUObject *ret = ue_get_python_uobject((UObject *)section); + if (!ret) + { Py_DECREF(py_sections); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -485,7 +610,8 @@ PyObject *py_ue_sequencer_sections(ue_PyUObject *self, PyObject * args) { return py_sections; } -PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -498,9 +624,11 @@ PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { TArray sections = track->GetAllSections(); - for (UMovieSceneSection *section : sections) { - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)section); - if (!ret) { + for (UMovieSceneSection *section : sections) + { + ue_PyUObject *ret = ue_get_python_uobject((UObject *)section); + if (!ret) + { Py_DECREF(py_sections); return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); } @@ -511,7 +639,8 @@ PyObject *py_ue_sequencer_track_sections(ue_PyUObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -524,21 +653,18 @@ PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *self, PyObject * args) track->AddSection(*new_section); - PyObject *ret = (PyObject *)ue_get_python_wrapper(new_section); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(new_section); } #endif -PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *obj; - if (!PyArg_ParseTuple(args, "O:sequencer_add_master_track", &obj)) { + if (!PyArg_ParseTuple(args, "O:sequencer_add_master_track", &obj)) + { return NULL; } @@ -564,23 +690,20 @@ PyObject *py_ue_sequencer_add_master_track(ue_PyUObject *self, PyObject * args) if (!track) return PyErr_Format(PyExc_Exception, "unable to create new master track"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } #if WITH_EDITOR -PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); float time; PyObject *py_value; int interpolation = 0; - if (!PyArg_ParseTuple(args, "fO|i:sequencer_section_add_key", &time, &py_value, &interpolation)) { + if (!PyArg_ParseTuple(args, "fO|i:sequencer_section_add_key", &time, &py_value, &interpolation)) + { return NULL; } @@ -589,30 +712,34 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { UMovieSceneSection *section = (UMovieSceneSection *)self->ue_object; - if (auto section_float = Cast(section)) { - if (PyNumber_Check(py_value)) { + if (auto section_float = Cast(section)) + { + if (PyNumber_Check(py_value)) + { PyObject *f_value = PyNumber_Float(py_value); float value = PyFloat_AsDouble(f_value); Py_DECREF(f_value); section_float->AddKey(time, value, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_bool = Cast(section)) { - if (PyBool_Check(py_value)) { + else if (auto section_bool = Cast(section)) + { + if (PyBool_Check(py_value)) + { bool value = false; if (PyObject_IsTrue(py_value)) value = true; section_bool->AddKey(time, value, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_transform = Cast(section)) { - if (ue_PyFTransform *py_transform = py_ue_is_ftransform(py_value)) { + else if (auto section_transform = Cast(section)) + { + if (ue_PyFTransform *py_transform = py_ue_is_ftransform(py_value)) + { bool unwind = false; FTransform transform = py_transform->transform; @@ -624,9 +751,12 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { section_transform->AddKey(time, ty, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, tz, (EMovieSceneKeyInterpolation)interpolation); - FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().X, unwind); - FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Y, unwind); - FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Z, unwind); + /*FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().Rotator().Roll, unwind); + FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Rotator().Pitch, unwind); + FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Rotator().Yaw, unwind);*/ + FTransformKey rx = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, transform.GetRotation().Euler().X, unwind); + FTransformKey ry = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, transform.GetRotation().Euler().Y, unwind); + FTransformKey rz = FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, transform.GetRotation().Euler().Z, unwind); section_transform->AddKey(time, rx, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, ry, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, rz, (EMovieSceneKeyInterpolation)interpolation); @@ -638,13 +768,14 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { section_transform->AddKey(time, sy, (EMovieSceneKeyInterpolation)interpolation); section_transform->AddKey(time, sz, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } - else if (auto section_vector = Cast(section)) { - if (ue_PyFVector *py_vector = py_ue_is_fvector(py_value)) { + else if (auto section_vector = Cast(section)) + { + if (ue_PyFVector *py_vector = py_ue_is_fvector(py_value)) + { FVector vec = py_vector->vec; FVectorKey vx = FVectorKey(EKeyVectorChannel::X, vec.X); FVectorKey vy = FVectorKey(EKeyVectorChannel::Y, vec.Y); @@ -654,15 +785,15 @@ PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { section_vector->AddKey(time, vy, (EMovieSceneKeyInterpolation)interpolation); section_vector->AddKey(time, vz, (EMovieSceneKeyInterpolation)interpolation); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } } return PyErr_Format(PyExc_Exception, "unsupported section type: %s", TCHAR_TO_UTF8(*section->GetClass()->GetName())); } -PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -674,46 +805,46 @@ PyObject *py_ue_sequencer_add_camera_cut_track(ue_PyUObject *self, PyObject * ar UMovieSceneTrack *track = scene->AddCameraCutTrack(UMovieSceneCameraCutTrack::StaticClass()); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } -PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_remove_possessable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_remove_possessable", &guid)) + { return nullptr; } - + ULevelSequence *seq = ue_py_check_type(self); if (!seq) return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); UMovieScene *scene = seq->GetMovieScene(); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } - + if (scene->RemovePossessable(f_guid)) Py_RETURN_TRUE; Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *guid; - if (!PyArg_ParseTuple(args, "s:sequencer_remove_spawnable", &guid)) { + if (!PyArg_ParseTuple(args, "s:sequencer_remove_spawnable", &guid)) + { return nullptr; } @@ -723,7 +854,8 @@ PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) UMovieScene *scene = seq->GetMovieScene(); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } @@ -733,7 +865,8 @@ PyObject *py_ue_sequencer_remove_spawnable(ue_PyUObject *self, PyObject * args) Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -747,12 +880,14 @@ PyObject *py_ue_sequencer_remove_camera_cut_track(ue_PyUObject *self, PyObject * Py_RETURN_NONE; } -PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "track:sequencer_remove_master_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_master_track", &py_track)) + { return nullptr; } @@ -772,12 +907,14 @@ PyObject *py_ue_sequencer_remove_master_track(ue_PyUObject *self, PyObject * arg Py_RETURN_FALSE; } -PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_track; - if (!PyArg_ParseTuple(args, "track:sequencer_remove_track", &py_track)) { + if (!PyArg_ParseTuple(args, "O:sequencer_remove_track", &py_track)) + { return nullptr; } @@ -799,13 +936,15 @@ PyObject *py_ue_sequencer_remove_track(ue_PyUObject *self, PyObject * args) { #endif -PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *obj; char *guid; - if (!PyArg_ParseTuple(args, "Os:sequencer_add_track", &obj, &guid)) { + if (!PyArg_ParseTuple(args, "Os:sequencer_add_track", &obj, &guid)) + { return NULL; } @@ -828,49 +967,50 @@ PyObject *py_ue_sequencer_add_track(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a UMovieSceneTrack class"); FGuid f_guid; - if (!FGuid::Parse(FString(guid), f_guid)) { + if (!FGuid::Parse(FString(guid), f_guid)) + { return PyErr_Format(PyExc_Exception, "invalid guid"); } UMovieSceneTrack *track = scene->AddTrack(u_class, f_guid); if (!track) return PyErr_Format(PyExc_Exception, "unable to create new track"); - PyObject *ret = (PyObject *)ue_get_python_wrapper(track); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(track); } #if WITH_EDITOR // smart functions allowing the set/get of display names to various sequencer objects -PyObject *py_ue_sequencer_set_display_name(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_set_display_name(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); char *name; - if (!PyArg_ParseTuple(args, "s:sequencer_set_display_name", &name)) { + if (!PyArg_ParseTuple(args, "s:sequencer_set_display_name", &name)) + { return NULL; } - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMovieSceneNameableTrack *track = (UMovieSceneNameableTrack *)self->ue_object; track->SetDisplayName(FText::FromString(UTF8_TO_TCHAR(name))); } - else { + else + { return PyErr_Format(PyExc_Exception, "the uobject does not expose the SetDisplayName() method"); } - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_sequencer_get_display_name(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_sequencer_get_display_name(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { UMovieSceneNameableTrack *track = (UMovieSceneNameableTrack *)self->ue_object; FText name = track->GetDefaultDisplayName(); return PyUnicode_FromString(TCHAR_TO_UTF8(*name.ToString())); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h index ded9524ef..4e10c1187 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h @@ -7,6 +7,7 @@ PyObject *py_ue_sequencer_master_tracks(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_possessable_tracks(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_track_sections(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_get_camera_cut_track(ue_PyUObject *, PyObject *); #if WITH_EDITOR PyObject *py_ue_sequencer_folders(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_create_folder(ue_PyUObject *, PyObject *); @@ -21,6 +22,7 @@ PyObject *py_ue_sequencer_add_possessable(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_track_add_section(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_add_actor(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_remove_possessable(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp index 789a94fe8..3d11753e7 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp @@ -3,6 +3,9 @@ #include "Runtime/Engine/Public/ComponentReregisterContext.h" #if WITH_EDITOR #include "Developer/MeshUtilities/Public/MeshUtilities.h" +#if ENGINE_MINOR_VERSION > 18 +#include "Runtime/Engine/Public/Rendering/SkeletalMeshModel.h" +#endif #endif @@ -23,11 +26,7 @@ PyObject *py_ue_get_anim_instance(ue_PyUObject *self, PyObject * args) return Py_None; } - ue_PyUObject *ret = ue_get_python_wrapper((UObject *)anim); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT((UObject *)anim); } PyObject *py_ue_set_skeletal_mesh(ue_PyUObject *self, PyObject * args) @@ -213,12 +212,20 @@ PyObject *py_ue_skeletal_mesh_set_soft_vertices(ue_PyUObject *self, PyObject * a if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -284,12 +291,20 @@ PyObject *py_ue_skeletal_mesh_get_soft_vertices(ue_PyUObject *self, PyObject * a if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -319,25 +334,41 @@ PyObject *py_ue_skeletal_mesh_get_lod(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); TArray indices; +#if ENGINE_MINOR_VERSION > 18 + indices = model.IndexBuffer; +#else model.MultiSizeIndexContainer.GetIndexBuffer(indices); +#endif for (int32 index = 0; index < indices.Num(); index++) { int32 section_index; int32 vertex_index; +#if ENGINE_MINOR_VERSION > 18 + model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index); +#else bool has_extra_influences; model.GetSectionFromVertexIndex(indices[index], section_index, vertex_index, has_extra_influences); +#endif FSoftSkinVertex ssv = model.Sections[section_index].SoftVertices[vertex_index]; // fix bone id for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; i++) @@ -374,12 +405,20 @@ PyObject *py_ue_skeletal_mesh_get_raw_indices(ue_PyUObject *self, PyObject * arg if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -448,12 +487,20 @@ PyObject *py_ue_skeletal_mesh_set_bone_map(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -516,12 +563,20 @@ PyObject *py_ue_skeletal_mesh_get_bone_map(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif if (section_index < 0 || section_index >= model.Sections.Num()) return PyErr_Format(PyExc_Exception, "invalid Section index, must be between 0 and %d", model.Sections.Num() - 1); @@ -549,12 +604,20 @@ PyObject *py_ue_skeletal_mesh_get_active_bone_indices(ue_PyUObject *self, PyObje if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -579,12 +642,20 @@ PyObject *py_ue_skeletal_mesh_set_active_bone_indices(ue_PyUObject *self, PyObje if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_iter = PyObject_GetIter(py_map); if (!py_iter) @@ -642,12 +713,20 @@ PyObject *py_ue_skeletal_mesh_get_required_bones(ue_PyUObject *self, PyObject * if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); @@ -672,12 +751,20 @@ PyObject *py_ue_skeletal_mesh_set_required_bones(ue_PyUObject *self, PyObject * if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel &model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &model = resource->LODModels[lod_index]; +#endif PyObject *py_iter = PyObject_GetIter(py_map); if (!py_iter) @@ -730,7 +817,11 @@ PyObject *py_ue_skeletal_mesh_lods_num(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif return PyLong_FromLong(resource->LODModels.Num()); } @@ -747,7 +838,11 @@ PyObject *py_ue_skeletal_mesh_sections_num(ue_PyUObject *self, PyObject * args) if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index >= resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num() - 1); @@ -778,7 +873,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a SkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); @@ -787,16 +886,28 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO if (lod_index == resource->LODModels.Num()) { +#if ENGINE_MINOR_VERSION < 19 resource->LODModels.Add(new FStaticLODModel()); +#else + resource->LODModels.Add(new FSkeletalMeshLODModel()); +#endif mesh->LODInfo.AddZeroed(); } else { // reinitialized already existent LOD +#if ENGINE_MINOR_VERSION < 19 new(&resource->LODModels[lod_index]) FStaticLODModel(); +#else + new(&resource->LODModels[lod_index]) FSkeletalMeshLODModel(); +#endif } +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel& LODModel = resource->LODModels[lod_index]; +#endif mesh->LODInfo[lod_index].LODHysteresis = 0.02; @@ -895,7 +1006,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO faces.Add(face); } +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel & lod_model = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel & lod_model = resource->LODModels[lod_index]; +#endif IMeshUtilities::MeshBuildOptions build_settings; build_settings.bUseMikkTSpace = (py_use_mikk && PyObject_IsTrue(py_use_mikk)); @@ -910,10 +1025,12 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO return PyErr_Format(PyExc_Exception, "unable to create new Skeletal LOD"); } +#if ENGINE_MINOR_VERSION < 19 for (int32 i = 0; i < lod_model.Sections.Num(); i++) { mesh->LODInfo[lod_index].TriangleSortSettings.AddZeroed(); } +#endif mesh->CalculateRequiredBones(LODModel, mesh->RefSkeleton, nullptr); mesh->CalculateInvRefMatrices(); @@ -1007,7 +1124,12 @@ PyObject *py_ue_morph_target_populate_deltas(ue_PyUObject *self, PyObject * args Py_DECREF(py_iter); +#if ENGINE_MINOR_VERSION < 19 morph->PopulateDeltas(deltas, lod_index); +#else + FSkeletalMeshModel *model = morph->BaseSkelMesh->GetImportedModel(); + morph->PopulateDeltas(deltas, lod_index, model->LODModels[lod_index].Sections); +#endif #if ENGINE_MINOR_VERSION > 16 if (morph->HasValidData()) @@ -1064,12 +1186,20 @@ PyObject *py_ue_skeletal_mesh_to_import_vertex_map(ue_PyUObject *self, PyObject if (!mesh) return PyErr_Format(PyExc_Exception, "uobject is not a USkeletalMesh"); +#if ENGINE_MINOR_VERSION < 19 FSkeletalMeshResource *resource = mesh->GetImportedResource(); +#else + FSkeletalMeshModel *resource = mesh->GetImportedModel(); +#endif if (lod_index < 0 || lod_index > resource->LODModels.Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index, must be between 0 and %d", resource->LODModels.Num()); +#if ENGINE_MINOR_VERSION < 19 FStaticLODModel& LODModel = resource->LODModels[lod_index]; +#else + FSkeletalMeshLODModel &LODModel = resource->LODModels[lod_index]; +#endif PyObject *py_list = PyList_New(0); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp index 71699e980..52ec3c289 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp @@ -268,11 +268,7 @@ PyObject *py_unreal_engine_create_checkerboard_texture(PyObject * self, PyObject UTexture2D *texture = FImageUtils::CreateCheckerboardTexture(color_one->color, color_two->color, checker_size); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * args) @@ -292,11 +288,7 @@ PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * texture->UpdateResource(); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * self, PyObject * args) @@ -316,11 +308,7 @@ PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * s texture->InitCustomFormat(width, height, (EPixelFormat)format, py_linear && PyObject_IsTrue(py_linear)); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } #if WITH_EDITOR @@ -366,11 +354,7 @@ PyObject *py_unreal_engine_create_texture(PyObject * self, PyObject * args) if (!texture) return PyErr_Format(PyExc_Exception, "unable to create texture"); - ue_PyUObject *ret = ue_get_python_wrapper(texture); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(texture); } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp index 71c0d88e1..d6f2041da 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp @@ -1,25 +1,30 @@ #include "UnrealEnginePythonPrivatePCH.h" -static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &teleport_physics) { +static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &teleport_physics) +{ PyObject *py_vec = nullptr; float x = 0, y = 0, z = 0; PyObject *py_sweep = nullptr; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|OO", &py_vec, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|OO", &py_vec, &py_sweep, &py_teleport_physics)) + { return false; } ue_PyFVector *ue_py_vec = py_ue_is_fvector(py_vec); - if (!ue_py_vec) { - if (!PyArg_ParseTuple(args, "fff|OO", &x, &y, &z, &py_sweep, &py_teleport_physics)) { + if (!ue_py_vec) + { + if (!PyArg_ParseTuple(args, "fff|OO", &x, &y, &z, &py_sweep, &py_teleport_physics)) + { return false; } vec.X = x; vec.Y = y; vec.Z = z; } - else { + else + { vec = ue_py_vec->vec; } @@ -29,34 +34,42 @@ static bool check_vector_args(PyObject *args, FVector &vec, bool &sweep, bool &t return true; } -static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool &teleport_physics) { +static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool &teleport_physics) +{ PyObject *py_rotation = nullptr; float roll = 0, pitch = 0, yaw = 0; PyObject *py_sweep = nullptr; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|OO", &py_rotation, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|OO", &py_rotation, &py_sweep, &py_teleport_physics)) + { PyErr_Clear(); - if (!PyArg_ParseTuple(args, "fff|OO", &roll, &pitch, &yaw, &py_sweep, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "fff|OO", &roll, &pitch, &yaw, &py_sweep, &py_teleport_physics)) + { return false; } } - if (py_rotation) { + if (py_rotation) + { ue_PyFQuat *ue_py_quat = py_ue_is_fquat(py_rotation); - if (ue_py_quat) { + if (ue_py_quat) + { quat = ue_py_quat->quat; } - else { + else + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(py_rotation); - if (!ue_py_rot) { + if (!ue_py_rot) + { PyErr_SetString(PyExc_Exception, "argument is not a FQuat or FRotator"); return false; } quat = ue_py_rot->rot.Quaternion(); } } - else { + else + { quat = FQuat(FRotator(pitch, yaw, roll)); } @@ -66,33 +79,41 @@ static bool check_rotation_args(PyObject *args, FQuat &quat, bool &sweep, bool & return true; } -static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &teleport_physics) { +static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &teleport_physics) +{ PyObject *py_rotation = nullptr; float roll = 0, pitch = 0, yaw = 0; PyObject *py_teleport_physics = nullptr; - if (!PyArg_ParseTuple(args, "O|O", &py_rotation, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "O|O", &py_rotation, &py_teleport_physics)) + { PyErr_Clear(); - if (!PyArg_ParseTuple(args, "fff|O", &roll, &pitch, &yaw, &py_teleport_physics)) { + if (!PyArg_ParseTuple(args, "fff|O", &roll, &pitch, &yaw, &py_teleport_physics)) + { return false; } } - if (py_rotation) { + if (py_rotation) + { ue_PyFQuat *ue_py_quat = py_ue_is_fquat(py_rotation); - if (ue_py_quat) { + if (ue_py_quat) + { quat = ue_py_quat->quat; } - else { + else + { ue_PyFRotator *ue_py_rot = py_ue_is_frotator(py_rotation); - if (!ue_py_rot) { + if (!ue_py_rot) + { PyErr_SetString(PyExc_Exception, "argument is not a FQuat or FRotator"); return false; } quat = ue_py_rot->rot.Quaternion(); } } - else { + else + { quat = FQuat(FRotator(pitch, yaw, roll)); } @@ -101,7 +122,8 @@ static bool check_rotation_args_no_sweep(PyObject *args, FQuat &quat, bool &tele return true; } -PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -113,7 +135,8 @@ PyObject *py_ue_get_actor_location(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -125,7 +148,8 @@ PyObject *py_ue_get_actor_scale(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -137,7 +161,8 @@ PyObject *py_ue_get_actor_transform(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -149,7 +174,8 @@ PyObject *py_ue_get_actor_forward(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -161,7 +187,8 @@ PyObject *py_ue_get_actor_right(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -173,7 +200,8 @@ PyObject *py_ue_get_actor_up(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -187,7 +215,8 @@ PyObject *py_ue_get_actor_rotation(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -204,8 +233,10 @@ PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { FHitResult hit; bool success = actor->SetActorLocation(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { - if (success) { + if (!sweep) + { + if (success) + { Py_RETURN_TRUE; } Py_RETURN_FALSE; @@ -214,7 +245,8 @@ PyObject *py_ue_set_actor_location(ue_PyUObject *self, PyObject * args) { return Py_BuildValue("(OO)", success ? Py_True : Py_False, py_ue_new_fhitresult(hit)); } -PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -231,14 +263,16 @@ PyObject *py_ue_add_actor_world_offset(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorWorldOffset(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); } -PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -255,7 +289,8 @@ PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorLocalOffset(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } @@ -263,7 +298,8 @@ PyObject *py_ue_add_actor_local_offset(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -279,14 +315,16 @@ PyObject *py_ue_add_actor_world_rotation(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorWorldRotation(quat, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); } -PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -302,7 +340,8 @@ PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { FHitResult hit; actor->AddActorLocalRotation(quat, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } @@ -310,7 +349,8 @@ PyObject *py_ue_add_actor_local_rotation(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -329,7 +369,8 @@ PyObject *py_ue_set_actor_scale(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -341,14 +382,16 @@ PyObject *py_ue_set_actor_rotation(ue_PyUObject *self, PyObject * args) { AActor *actor = ue_get_actor(self); if (!actor) return PyErr_Format(PyExc_Exception, "uobject is not an actor or a component"); - if (actor->SetActorRotation(quat, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None)) { + if (actor->SetActorRotation(quat, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None)) + { Py_RETURN_TRUE; } Py_RETURN_FALSE; } -PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -366,61 +409,74 @@ PyObject *py_ue_set_actor_transform(ue_PyUObject *self, PyObject * args) { } -PyObject *py_ue_get_world_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetComponentLocation(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FRotator rot = ((USceneComponent *)self->ue_object)->GetComponentRotation(); return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_world_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetComponentScale(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeLocation; return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FRotator rot = ((USceneComponent *)self->ue_object)->RelativeRotation; return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeScale3D; return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); USceneComponent *component = ue_py_check_type(self); if (!component) @@ -430,7 +486,8 @@ PyObject *py_ue_get_relative_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(t); } -PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); USceneComponent *component = ue_py_check_type(self); if (!component) @@ -440,34 +497,41 @@ PyObject *py_ue_get_world_transform(ue_PyUObject *self, PyObject * args) { return py_ue_new_ftransform(t); } -PyObject *py_ue_get_forward_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_forward_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetForwardVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_up_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_up_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetUpVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_get_right_vector(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_get_right_vector(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { FVector vec3 = ((USceneComponent *)self->ue_object)->GetRightVector(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; bool sweep; @@ -477,9 +541,11 @@ PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { FHitResult hit; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldLocation(vec, sweep, &hit, teleport_physics ? ETeleportType::TeleportPhysics : ETeleportType::None); - if (!sweep) { + if (!sweep) + { Py_RETURN_NONE; } return py_ue_new_fhitresult(hit); @@ -487,12 +553,14 @@ PyObject *py_ue_set_world_location(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldRotation(rot); Py_INCREF(Py_None); return Py_None; @@ -500,13 +568,15 @@ PyObject *py_ue_set_world_rotation(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetWorldScale3D(vec); Py_INCREF(Py_None); return Py_None; @@ -514,7 +584,8 @@ PyObject *py_ue_set_world_scale(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FTransform t; @@ -530,7 +601,8 @@ PyObject *py_ue_set_world_transform(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FTransform t; @@ -546,13 +618,15 @@ PyObject *py_ue_set_relative_transform(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeLocation(vec); Py_INCREF(Py_None); return Py_None; @@ -560,12 +634,14 @@ PyObject *py_ue_set_relative_location(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FRotator rot; if (!py_ue_rotator_arg(args, rot)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeRotation(rot); Py_INCREF(Py_None); return Py_None; @@ -573,13 +649,15 @@ PyObject *py_ue_set_relative_rotation(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); } -PyObject *py_ue_set_relative_scale(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_set_relative_scale(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); FVector vec; if (!py_ue_vector_arg(args, vec)) return NULL; - if (self->ue_object->IsA()) { + if (self->ue_object->IsA()) + { ((USceneComponent *)self->ue_object)->SetRelativeScale3D(vec); Py_INCREF(Py_None); return Py_None; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 12e7bc0a2..0ac579604 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp @@ -6,38 +6,36 @@ #include "Editor/UnrealEd/Public/LevelEditorViewport.h" #endif -PyObject *py_unreal_engine_get_game_viewport_client(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_game_viewport_client(PyObject * self, PyObject * args) +{ UGameViewportClient *viewport_client = GEngine->GameViewport; - if (!viewport_client) { + if (!viewport_client) + { return PyErr_Format(PyExc_Exception, "no engine GameViewport found"); } - ue_PyUObject *ret = ue_get_python_wrapper(GEngine->GameViewport); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(GEngine->GameViewport); } #if WITH_EDITOR -PyObject *py_unreal_engine_get_editor_pie_game_viewport_client(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_get_editor_pie_game_viewport_client(PyObject * self, PyObject * args) +{ UGameViewportClient *viewport_client = GEditor->GameViewport; - if (!viewport_client) { + if (!viewport_client) + { return PyErr_Format(PyExc_Exception, "no editor GameViewport found"); } - ue_PyUObject *ret = ue_get_python_wrapper(viewport_client); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(viewport_client); } -PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args) +{ int mode; - if (!PyArg_ParseTuple(args, "i:editor_set_view_mode", &mode)) { + if (!PyArg_ParseTuple(args, "i:editor_set_view_mode", &mode)) + { return NULL; } @@ -49,15 +47,17 @@ PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); viewport_client.SetViewMode((EViewModeIndex)mode); - + Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * args) +{ int speed; - if (!PyArg_ParseTuple(args, "f:editor_set_camera_speed", &speed)) { + if (!PyArg_ParseTuple(args, "f:editor_set_camera_speed", &speed)) + { return NULL; } @@ -73,11 +73,13 @@ PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * a Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * args) +{ PyObject *py_vector; - if (!PyArg_ParseTuple(args, "O:editor_set_view_location", &py_vector)) { + if (!PyArg_ParseTuple(args, "O:editor_set_view_location", &py_vector)) + { return NULL; } @@ -97,11 +99,13 @@ PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * Py_RETURN_NONE; } -PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * args) +{ PyObject *py_rotator; - if (!PyArg_ParseTuple(args, "O:editor_set_view_rotation", &py_rotator)) { + if (!PyArg_ParseTuple(args, "O:editor_set_view_rotation", &py_rotator)) + { return NULL; } @@ -123,19 +127,22 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * #endif -PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_widget; int z_order = 0; - if (!PyArg_ParseTuple(args, "O|i:add_viewport_widget_content", &py_widget, &z_order)) { + if (!PyArg_ParseTuple(args, "O|i:add_viewport_widget_content", &py_widget, &z_order)) + { return NULL; } UGameViewportClient *viewport = ue_py_check_type(self); - if (!viewport) { + if (!viewport) + { UWorld *world = ue_py_check_type(self); if (!world) return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient or a UWorld"); @@ -145,7 +152,8 @@ PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) } ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } // Do not increment reference count as it is assumed this function is used in a PyComponent/PyActor/ that can holds reference to @@ -154,17 +162,18 @@ PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *self, PyObject * args) viewport->AddViewportWidgetContent(py_swidget->s_widget, z_order); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); PyObject *py_widget; - if (!PyArg_ParseTuple(args, "O:remove_viewport_widget_content", &py_widget)) { + if (!PyArg_ParseTuple(args, "O:remove_viewport_widget_content", &py_widget)) + { return NULL; } @@ -173,18 +182,19 @@ PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *self, PyObject * ar return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient"); ue_PySWidget *py_swidget = py_ue_is_swidget(py_widget); - if (!py_swidget) { + if (!py_swidget) + { return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); } Py_DECREF(py_swidget); viewport->RemoveViewportWidgetContent(py_swidget->s_widget); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } -PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -192,10 +202,34 @@ PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *self, PyObject * args) if (!viewport) return PyErr_Format(PyExc_Exception, "object is not a GameViewportClient"); - + viewport->RemoveAllViewportWidgets(); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; +} + +PyObject *py_ue_game_viewport_client_set_rendering_flag(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_bool; + + if (!PyArg_ParseTuple(args, "O:game_viewport_client_set_rendering_flag", &py_bool)) + { + return nullptr; + } + + bool bEnabled = PyObject_IsTrue(py_bool) ? true : false; + + UGameViewportClient *ViewportClient = ue_py_check_type(self); + if (!ViewportClient) + { + return PyErr_Format(PyExc_Exception, "object is not a UGameViewportClient"); + } + + ViewportClient->EngineShowFlags.Rendering = bEnabled; + + Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h index 2f6a8dc2b..f5a31a9b7 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.h @@ -16,3 +16,4 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject *, PyObject *); PyObject *py_ue_add_viewport_widget_content(ue_PyUObject *, PyObject *); PyObject *py_ue_remove_viewport_widget_content(ue_PyUObject *, PyObject *); PyObject *py_ue_remove_all_viewport_widgets(ue_PyUObject *, PyObject *); +PyObject *py_ue_game_viewport_client_set_rendering_flag(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp index 0f833c15d..bac67a6d8 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWidget.cpp @@ -4,7 +4,8 @@ #include "Runtime/UMG/Public/Components/Widget.h" -PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -17,7 +18,8 @@ PyObject *py_ue_take_widget(ue_PyUObject * self, PyObject * args) { return (PyObject *)s_widget; } -PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) { +PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) +{ ue_py_check(self); @@ -40,9 +42,5 @@ PyObject *py_ue_create_widget(ue_PyUObject * self, PyObject * args) { if (!widget) return PyErr_Format(PyExc_Exception, "unable to create new widget"); - ue_PyUObject *ret = ue_get_python_wrapper(widget); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(widget); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp index 285e0d2a6..a548be8f6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWidgetComponent.cpp @@ -2,6 +2,7 @@ #include "Runtime/UMG/Public/Components/WidgetComponent.h" +#include "PyUserWidget.h" PyObject *py_ue_set_slate_widget(ue_PyUObject * self, PyObject * args) @@ -17,14 +18,16 @@ PyObject *py_ue_set_slate_widget(ue_PyUObject * self, PyObject * args) } UWidgetComponent *widget_component = ue_py_check_type(self); - if (!widget_component) - return PyErr_Format(PyExc_Exception, "uobject is not a UWidgetComponent"); + UPyUserWidget *py_user_widget = ue_py_check_type(self); + if (!widget_component && !py_user_widget) + return PyErr_Format(PyExc_Exception, "uobject is not a UWidgetComponent or UPyUserWidget"); ue_PySWidget *s_widget = py_ue_is_swidget(py_widget); if (!s_widget) return PyErr_Format(PyExc_Exception, "argument is not a SWidget"); - widget_component->SetSlateWidget(s_widget->s_widget); + if (widget_component) { widget_component->SetSlateWidget(s_widget->s_widget); } + else { py_user_widget->SetSlateWidget(s_widget->s_widget); } Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp index e6bbd0945..fa58027fa 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp @@ -111,7 +111,7 @@ PyObject *py_ue_all_objects(ue_PyUObject * self, PyObject * args) UObject *u_obj = *Itr; if (u_obj->GetWorld() != world) continue; - ue_PyUObject *py_obj = ue_get_python_wrapper(u_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_obj); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -133,7 +133,7 @@ PyObject *py_ue_all_actors(ue_PyUObject * self, PyObject * args) for (TActorIterator Itr(world); Itr; ++Itr) { UObject *u_obj = *Itr; - ue_PyUObject *py_obj = ue_get_python_wrapper(u_obj); + ue_PyUObject *py_obj = ue_get_python_uobject(u_obj); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -164,11 +164,7 @@ PyObject *py_ue_find_object(ue_PyUObject *self, PyObject * args) if (!u_object) return PyErr_Format(PyExc_Exception, "unable to find object %s", name); - ue_PyUObject *ret = ue_get_python_wrapper(u_object); - if (!ret) - return PyErr_Format(PyExc_Exception, "uobject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(u_object); } PyObject *py_ue_get_world(ue_PyUObject *self, PyObject * args) @@ -180,12 +176,7 @@ PyObject *py_ue_get_world(ue_PyUObject *self, PyObject * args) if (!world) return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject"); - ue_PyUObject *ret = ue_get_python_wrapper(world); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; - + Py_RETURN_UOBJECT(world); } PyObject *py_ue_get_game_viewport(ue_PyUObject *self, PyObject * args) @@ -204,6 +195,9 @@ PyObject *py_ue_get_game_viewport(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(viewport_client); } + + + PyObject *py_ue_has_world(ue_PyUObject *self, PyObject * args) { @@ -271,7 +265,7 @@ PyObject *py_ue_get_levels(ue_PyUObject * self, PyObject * args) for (ULevel *level : world->GetLevels()) { - ue_PyUObject *py_obj = ue_get_python_wrapper(level); + ue_PyUObject *py_obj = ue_get_python_uobject(level); if (!py_obj) continue; PyList_Append(ret, (PyObject *)py_obj); @@ -293,11 +287,7 @@ PyObject *py_ue_get_current_level(ue_PyUObject *self, PyObject * args) if (!level) Py_RETURN_NONE; - ue_PyUObject *ret = ue_get_python_wrapper(level); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(level); } PyObject *py_ue_set_current_level(ue_PyUObject *self, PyObject * args) @@ -371,7 +361,7 @@ PyObject *py_ue_add_foliage_asset(ue_PyUObject *self, PyObject * args) { foliage_type = (UFoliageType *)u_object; ifa->AddFoliageType(foliage_type); - + } if (!foliage_type) diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 1b5d335ca..7c1457159 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -14,16 +14,20 @@ #endif void unreal_engine_init_py_module(); +void init_unreal_engine_builtin(); #if defined(UNREAL_ENGINE_PYTHON_ON_LINUX) const char *ue4_module_options = "linux_global_symbols"; #endif #if PY_MAJOR_VERSION < 3 -char *PyUnicode_AsUTF8(PyObject *py_str) { - if (PyUnicode_Check(py_str)) { +char *PyUnicode_AsUTF8(PyObject *py_str) +{ + if (PyUnicode_Check(py_str)) + { PyObject *unicode = PyUnicode_AsUTF8String(py_str); - if (unicode) { + if (unicode) + { return PyString_AsString(unicode); } // just a hack to avoid crashes @@ -32,18 +36,22 @@ char *PyUnicode_AsUTF8(PyObject *py_str) { return PyString_AsString(py_str); } -int PyGILState_Check() { +int PyGILState_Check() +{ PyThreadState * tstate = _PyThreadState_Current; return tstate && (tstate == PyGILState_GetThisThreadState()); } #endif -bool PyUnicodeOrString_Check(PyObject *py_obj) { - if (PyUnicode_Check(py_obj)) { +bool PyUnicodeOrString_Check(PyObject *py_obj) +{ + if (PyUnicode_Check(py_obj)) + { return true; } #if PY_MAJOR_VERSION < 3 - else if (PyString_Check(py_obj)) { + else if (PyString_Check(py_obj)) + { return true; } #endif @@ -53,26 +61,31 @@ bool PyUnicodeOrString_Check(PyObject *py_obj) { #define LOCTEXT_NAMESPACE "FUnrealEnginePythonModule" -void FUnrealEnginePythonModule::PythonGILRelease() { +void FUnrealEnginePythonModule::PythonGILRelease() +{ #if defined(UEPY_THREADING) - if (PyGILState_Check() == 1) { + if (PyGILState_Check() == 1) + { ue_python_gil = PyEval_SaveThread(); } #endif } -bool FUnrealEnginePythonModule::PythonGILAcquire() { +bool FUnrealEnginePythonModule::PythonGILAcquire() +{ #if defined(UEPY_THREADING) - if (PyGILState_Check() == 0) { + if (PyGILState_Check() == 0) + { PyEval_RestoreThread((PyThreadState *)ue_python_gil); return true; } return false; #endif return true; -} + } -void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) { +void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) +{ #if PY_MAJOR_VERSION >= 3 wchar_t *argv[] = { UTF8_TO_TCHAR("UnrealEngine"), NULL }; @@ -114,13 +127,15 @@ void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) { PyObject *py_additional_modules_path = PyUnicode_FromString(additional_modules_path); PyList_Insert(py_path, 0, py_additional_modules_path); - if (verbose) { + if (verbose) + { UE_LOG(LogPython, Log, TEXT("Python VM initialized: %s"), UTF8_TO_TCHAR(Py_GetVersion())); UE_LOG(LogPython, Log, TEXT("Python Scripts search path: %s"), UTF8_TO_TCHAR(scripts_path)); } } -static void setup_stdout_stderr() { +static void setup_stdout_stderr() +{ // Redirecting stdout char const* code = "import sys\n" "import unreal_engine\n" @@ -147,7 +162,8 @@ static void setup_stdout_stderr() { PyRun_SimpleString(code); } -namespace { +namespace +{ static void consoleExecScript(const TArray& Args) { if (Args.Num() != 1) @@ -161,39 +177,71 @@ namespace { } } + static void consoleExecString(const TArray& Args) + { + if (Args.Num() == 0) + { + UE_LOG(LogPython, Warning, TEXT("Usage: 'py.cmd '.")); + UE_LOG(LogPython, Warning, TEXT(" scriptname: Name of script, must reside in Scripts folder. Ex: myscript.py")); + } + else + { + FString cmdString; + for (const FString& argStr : Args) + { + cmdString += argStr.TrimQuotes() + '\n'; + } + + UPythonBlueprintFunctionLibrary::ExecutePythonString(cmdString); + } + } + } FAutoConsoleCommand ExecPythonScriptCommand( TEXT("py.exec"), *NSLOCTEXT("UnrealEnginePython", "CommandText_Exec", "Execute python script").ToString(), FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecScript)); +FAutoConsoleCommand ExecPythonStringCommand( + TEXT("py.cmd"), + *NSLOCTEXT("UnrealEnginePython", "CommandText_Cmd", "Execute python string").ToString(), + FConsoleCommandWithArgsDelegate::CreateStatic(consoleExecString)); + void FUnrealEnginePythonModule::StartupModule() { BrutalFinalize = false; // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - FString IniValue; - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), IniValue, GEngineIni)) { + FString PythonHome; + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) + { #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); Py_SetPythonHome(home); - } +} - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), IniValue, GEngineIni)) { - IniValue = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeHome"), PythonHome, GEngineIni)) + { + PythonHome = FPaths::Combine(*PROJECT_CONTENT_DIR, *PythonHome); + FPaths::NormalizeFilename(PythonHome); + PythonHome = FPaths::ConvertRelativePathToFull(PythonHome); #if PY_MAJOR_VERSION >= 3 - wchar_t *home = (wchar_t *)*IniValue; + wchar_t *home = (wchar_t *)*PythonHome; #else - char *home = TCHAR_TO_UTF8(*IniValue); + char *home = TCHAR_TO_UTF8(*PythonHome); #endif + Py_SetPythonHome(home); - } +} - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) { + FString IniValue; + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) + { #if PY_MAJOR_VERSION >= 3 wchar_t *program_name = (wchar_t *)*IniValue; #else @@ -202,8 +250,11 @@ void FUnrealEnginePythonModule::StartupModule() Py_SetProgramName(program_name); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeProgramName"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeProgramName"), IniValue, GEngineIni)) + { IniValue = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); + FPaths::NormalizeFilename(IniValue); + IniValue = FPaths::ConvertRelativePathToFull(IniValue); #if PY_MAJOR_VERSION >= 3 wchar_t *program_name = (wchar_t *)*IniValue; #else @@ -212,43 +263,95 @@ void FUnrealEnginePythonModule::StartupModule() Py_SetProgramName(program_name); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ScriptsPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ScriptsPath"), IniValue, GEngineIni)) + { ScriptsPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeScriptsPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeScriptsPath"), IniValue, GEngineIni)) + { ScriptsPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("AdditionalModulesPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("AdditionalModulesPath"), IniValue, GEngineIni)) + { AdditionalModulesPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeAdditionalModulesPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeAdditionalModulesPath"), IniValue, GEngineIni)) + { AdditionalModulesPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ZipPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ZipPath"), IniValue, GEngineIni)) + { ZipPath = IniValue; } - if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeZipPath"), IniValue, GEngineIni)) { + if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("RelativeZipPath"), IniValue, GEngineIni)) + { ZipPath = FPaths::Combine(*PROJECT_CONTENT_DIR, *IniValue); } - if (ScriptsPath.IsEmpty()) { + if (ScriptsPath.IsEmpty()) + { ScriptsPath = FPaths::Combine(*PROJECT_CONTENT_DIR, UTF8_TO_TCHAR("Scripts")); } - if (ZipPath.IsEmpty()) { + if (ZipPath.IsEmpty()) + { ZipPath = FPaths::Combine(*PROJECT_CONTENT_DIR, UTF8_TO_TCHAR("ue_python.zip")); } - if (!FPaths::DirectoryExists(ScriptsPath)) { + if (!FPaths::DirectoryExists(ScriptsPath)) + { IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); PlatformFile.CreateDirectory(*ScriptsPath); } + // To ensure there are no path conflicts, if we have a valid python home at this point, + // we override the current environment entirely with the environment we want to use, + // removing any paths to other python environments we aren't using. + if (PythonHome.Len() > 0) + { + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONHOME"), *PythonHome); + + const int32 MaxPathVarLen = 32768; + FString OrigPathVar = FString::ChrN(MaxPathVarLen, TEXT('\0')); + FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"), OrigPathVar.GetCharArray().GetData(), MaxPathVarLen); + + // Get the current path and remove elements with python in them, we don't want any conflicts + const TCHAR* PathDelimiter = FPlatformMisc::GetPathVarDelimiter(); + TArray PathVars; + OrigPathVar.ParseIntoArray(PathVars, PathDelimiter, true); + for (int32 PathRemoveIndex = PathVars.Num() - 1; PathRemoveIndex >= 0; --PathRemoveIndex) + { + if (PathVars[PathRemoveIndex].Contains(TEXT("python"), ESearchCase::IgnoreCase)) + { + UE_LOG(LogPython, Verbose, TEXT("Removing other python Path: '%s'"), *PathVars[PathRemoveIndex]); + PathVars.RemoveAt(PathRemoveIndex); + } + } + + // Setup our own paths for PYTHONPATH + TArray OurPythonPaths = { + PythonHome, + FPaths::Combine(PythonHome, TEXT("Lib")), + FPaths::Combine(PythonHome, TEXT("Lib/site-packages")), + }; + FString PythonPathVars = FString::Join(OurPythonPaths, PathDelimiter); + FPlatformMisc::SetEnvironmentVar(TEXT("PYTHONPATH"), *PythonPathVars); + + // Also add our paths to PATH, just so any searching will find our local python + PathVars.Append(OurPythonPaths); + FString ModifiedPath = FString::Join(PathVars, PathDelimiter); + FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); + } + +#if PY_MAJOR_VERSION >= 3 + init_unreal_engine_builtin(); +#endif + Py_Initialize(); PyEval_InitThreads(); @@ -271,7 +374,7 @@ void FUnrealEnginePythonModule::StartupModule() // Redirecting stdout setup_stdout_stderr(); - + //import upymodule_importer PyImport_ImportModule("upymodule_importer"); @@ -280,10 +383,12 @@ void FUnrealEnginePythonModule::StartupModule() "upystartup.startup()\n"; PyRun_SimpleString(startupCode); - if (PyImport_ImportModule("ue_site")) { + if (PyImport_ImportModule("ue_site")) + { UE_LOG(LogPython, Log, TEXT("ue_site Python module successfully imported")); } - else { + else + { // TODO gracefully manage the error UE_LOG(LogPython, Warning, TEXT("ue_site not found (if you don't use the startup file ignore this warning)")); //unreal_engine_py_log_error(); @@ -292,7 +397,7 @@ void FUnrealEnginePythonModule::StartupModule() // release the GIL PythonGILRelease(); -} + } void FUnrealEnginePythonModule::ShutdownModule() { @@ -300,25 +405,30 @@ void FUnrealEnginePythonModule::ShutdownModule() // we call this function before unloading the module. UE_LOG(LogPython, Log, TEXT("Goodbye Python")); - if (!BrutalFinalize) { + if (!BrutalFinalize) + { PythonGILAcquire(); Py_Finalize(); } } -void FUnrealEnginePythonModule::RunString(char *str) { +void FUnrealEnginePythonModule::RunString(char *str) +{ FScopePythonGIL gil; PyObject *eval_ret = PyRun_String(str, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(eval_ret); } -FString FUnrealEnginePythonModule::Pep8ize(FString Code) { +FString FUnrealEnginePythonModule::Pep8ize(FString Code) +{ PyObject *pep8izer_module = PyImport_ImportModule("autopep8"); - if (!pep8izer_module) { + if (!pep8izer_module) + { unreal_engine_py_log_error(); UE_LOG(LogPython, Error, TEXT("unable to load autopep8 module, please install it")); // return the original string to avoid losing data @@ -326,7 +436,8 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } PyObject *pep8izer_func = PyObject_GetAttrString(pep8izer_module, (char*)"fix_code"); - if (!pep8izer_func) { + if (!pep8izer_func) + { unreal_engine_py_log_error(); UE_LOG(LogPython, Error, TEXT("unable to get autopep8.fix_code function")); // return the original string to avoid losing data @@ -334,13 +445,15 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } PyObject *ret = PyObject_CallFunction(pep8izer_func, (char *)"s", TCHAR_TO_UTF8(*Code)); - if (!ret) { + if (!ret) + { unreal_engine_py_log_error(); // return the original string to avoid losing data return Code; } - if (!PyUnicode_Check(ret)) { + if (!PyUnicode_Check(ret)) + { UE_LOG(LogPython, Error, TEXT("returned value is not a string")); // return the original string to avoid losing data return Code; @@ -354,13 +467,15 @@ FString FUnrealEnginePythonModule::Pep8ize(FString Code) { } // run a python string in a new sub interpreter -void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { +void FUnrealEnginePythonModule::RunStringSandboxed(char *str) +{ FScopePythonGIL gil; PyThreadState *_main = PyThreadState_Get(); PyThreadState *py_new_state = Py_NewInterpreter(); - if (!py_new_state) { + if (!py_new_state) + { UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter")); return; } @@ -372,16 +487,18 @@ void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { setup_stdout_stderr(); PyObject *m = PyImport_AddModule("__main__"); - if (m == NULL) { + if (m == NULL) + { UE_LOG(LogPython, Error, TEXT("Unable to create new global dict")); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); return; - } +} PyObject *global_dict = PyModule_GetDict(m); PyObject *eval_ret = PyRun_String(str, Py_file_input, global_dict, global_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -392,41 +509,46 @@ void FUnrealEnginePythonModule::RunStringSandboxed(char *str) { PyThreadState_Swap(_main); } -void FUnrealEnginePythonModule::RunFile(char *filename) { +void FUnrealEnginePythonModule::RunFile(char *filename) +{ FScopePythonGIL gil; - char *full_path = filename; + FString full_path = UTF8_TO_TCHAR(filename); if (!FPaths::FileExists(filename)) { - full_path = TCHAR_TO_UTF8(*FPaths::Combine(*ScriptsPath, UTF8_TO_TCHAR(filename))); + full_path = FPaths::Combine(*ScriptsPath, full_path); } #if PY_MAJOR_VERSION >= 3 FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + if (fopen_s(&fd, TCHAR_TO_UTF8(*full_path), "r") != 0) + { + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #else - fd = fopen(full_path, "r"); - if (!fd) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + fd = fopen(TCHAR_TO_UTF8(*full_path), "r"); + if (!fd) + { + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; -} + } #endif - PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); + PyObject *eval_ret = PyRun_File(fd, TCHAR_TO_UTF8(*full_path), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); fclose(fd); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } Py_DECREF(eval_ret); #else // damn, this is horrible, but it is the only way i found to avoid the CRT error :( - FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); + FString command = FString::Printf(TEXT("execfile(\"%s\")"), *full_path); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, (PyObject *)main_dict, (PyObject *)local_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); return; } @@ -435,18 +557,20 @@ void FUnrealEnginePythonModule::RunFile(char *filename) { } // run a python script in a new sub interpreter (useful for unit tests) -void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback)(void *arg), void *arg) { +void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback)(void *arg), void *arg) +{ FScopePythonGIL gil; - char *full_path = filename; + FString full_path = filename; if (!FPaths::FileExists(filename)) { - full_path = TCHAR_TO_UTF8(*FPaths::Combine(*ScriptsPath, UTF8_TO_TCHAR(filename))); + full_path = FPaths::Combine(*ScriptsPath, full_path); } PyThreadState *_main = PyThreadState_Get(); PyThreadState *py_new_state = Py_NewInterpreter(); - if (!py_new_state) { + if (!py_new_state) + { UE_LOG(LogPython, Error, TEXT("Unable to create new Python interpreter")); return; } @@ -458,33 +582,37 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) setup_stdout_stderr(); PyObject *m = PyImport_AddModule("__main__"); - if (m == NULL) { + if (m == NULL) + { UE_LOG(LogPython, Error, TEXT("Unable to create new global dict")); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); return; -} + } PyObject *global_dict = PyModule_GetDict(m); #if PY_MAJOR_VERSION >= 3 FILE *fd = nullptr; #if PLATFORM_WINDOWS - if (fopen_s(&fd, full_path, "r") != 0) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + if (fopen_s(&fd, TCHAR_TO_UTF8(*full_path), "r") != 0) + { + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #else - fd = fopen(full_path, "r"); - if (!fd) { - UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), UTF8_TO_TCHAR(full_path)); + fd = fopen(TCHAR_TO_UTF8(*full_path), "r"); + if (!fd) + { + UE_LOG(LogPython, Error, TEXT("Unable to open file %s"), *full_path); return; } #endif - PyObject *eval_ret = PyRun_File(fd, full_path, Py_file_input, global_dict, global_dict); + PyObject *eval_ret = PyRun_File(fd, TCHAR_TO_UTF8(*full_path), Py_file_input, global_dict, global_dict); fclose(fd); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -493,9 +621,10 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) Py_DECREF(eval_ret); #else // damn, this is horrible, but it is the only way i found to avoid the CRT error :( - FString command = FString::Printf(TEXT("execfile(\"%s\")"), UTF8_TO_TCHAR(full_path)); + FString command = FString::Printf(TEXT("execfile(\"%s\")"), *full_path); PyObject *eval_ret = PyRun_String(TCHAR_TO_UTF8(*command), Py_file_input, global_dict, global_dict); - if (!eval_ret) { + if (!eval_ret) + { unreal_engine_py_log_error(); Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); @@ -508,7 +637,7 @@ void FUnrealEnginePythonModule::RunFileSandboxed(char *filename, void(*callback) Py_EndInterpreter(py_new_state); PyThreadState_Swap(_main); - } +} void FUnrealEnginePythonModule::AddPathToSysPath(const FString& Path) { diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h index db17d50f5..76531afe3 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h +++ b/Source/UnrealEnginePython/Private/UnrealEnginePythonPrivatePCH.h @@ -1,4 +1,6 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 20Tab S.r.l. + +#pragma once //#define UEPY_MEMORY_DEBUG 1 #define UEPY_THREADING 1 @@ -29,6 +31,8 @@ #include "UEPyModule.h" +#include "Wrappers/UEPyESlateEnums.h" + #include "Wrappers/UEPyFVector.h" #include "Wrappers/UEPyFHitResult.h" #include "Wrappers/UEPyFRotator.h" @@ -68,16 +72,26 @@ #include "Blueprint/UEPyEdGraphPin.h" #include "UEPyIPlugin.h" #include "CollectionManager/UEPyICollectionManager.h" +#include "MaterialEditorUtilities/UEPyFMaterialEditorUtilities.h" #endif #include "Slate/UEPySlate.h" #include "Http/UEPyIHttp.h" #include "ConsoleManager/UEPyIConsoleManager.h" +#include "SlateApplication/UEPyFSlateApplication.h" #include "Voice/UEPyIVoiceCapture.h" +#include "PythonHouseKeeper.h" + -#define ue_py_check(py_u) if (!py_u->ue_object || !py_u->ue_object->IsValidLowLevel() || py_u->ue_object->IsPendingKillOrUnreachable())\ - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") +#define ue_py_check(py_u) if (!FUnrealEnginePythonHouseKeeper::Get()->IsValidPyUObject(py_u))\ + return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state") + +#define ue_py_check_int(py_u) if (!FUnrealEnginePythonHouseKeeper::Get()->IsValidPyUObject(py_u))\ + {\ + PyErr_SetString(PyExc_Exception, "PyUObject is in invalid state");\ + return -1;\ + } #if PY_MAJOR_VERSION < 3 char *PyUnicode_AsUTF8(PyObject *py_str); @@ -85,8 +99,20 @@ int PyGILState_Check(); #endif bool PyUnicodeOrString_Check(PyObject *py_obj); -#define Py_RETURN_UOBJECT(py_uobj) ue_PyUObject *ret = ue_get_python_wrapper(py_uobj);\ +#define Py_RETURN_UOBJECT(py_uobj) ue_PyUObject *ret = ue_get_python_uobject_inc(py_uobj);\ if (!ret)\ return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ - Py_INCREF(ret);\ return (PyObject *)ret; + +#define Py_RETURN_UOBJECT_NOINC(py_uobj) ue_PyUObject *ret = ue_get_python_uobject(py_uobj);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ + return (PyObject *)ret; + +#if ENGINE_MINOR_VERSION < 16 +template +struct TStructOpsTypeTraitsBase2 : TStructOpsTypeTraitsBase +{ + +}; +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp new file mode 100644 index 000000000..71113bd09 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp @@ -0,0 +1,98 @@ +#include "UnrealEnginePythonPrivatePCH.h" + +static PyObject *py_ue_eslate_enums_get(ue_PyESlateEnums *self, void *closure) +{ + return PyLong_FromLong(self->val); +} + +static PyGetSetDef ue_PyESlateEnums_getseters[] = { + { (char*)"val", (getter)py_ue_eslate_enums_get, 0, (char *)"", NULL }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyESlateEnums_str(ue_PyESlateEnums *self) +{ + return PyUnicode_FromFormat("", PyLong_FromLong(self->val)); +} + +static PyTypeObject ue_PyESlateEnumsType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.ESlateEnums", /* tp_name */ + sizeof(ue_PyESlateEnums), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)ue_PyESlateEnums_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine ESlateEnums", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, + ue_PyESlateEnums_getseters, +}; + +static int ue_py_eslate_enums_init(ue_PyESlateEnums *self, PyObject *args, PyObject *kwargs) { + int val = 0; + if (!PyArg_ParseTuple(args, "i", &val)) + return -1; + + self->val = val; + return 0; +} + +void ue_python_init_eslate_enums(PyObject *ue_module) +{ + ue_PyESlateEnumsType.tp_new = PyType_GenericNew; + + ue_PyESlateEnumsType.tp_init = (initproc)ue_py_eslate_enums_init; + + if (PyType_Ready(&ue_PyESlateEnumsType) < 0) + return; + + Py_INCREF(&ue_PyESlateEnumsType); + PyModule_AddObject(ue_module, "ESlateEnums", (PyObject *)&ue_PyESlateEnumsType); + + auto add_native_enum = [](const char *enum_name, uint8 val) + { + ue_PyESlateEnums* native_enum = (ue_PyESlateEnums *)PyObject_New(ue_PyESlateEnums, &ue_PyESlateEnumsType); + native_enum->val = val; + PyDict_SetItemString(ue_PyESlateEnumsType.tp_dict, enum_name, (PyObject *)native_enum); + }; + +#if ENGINE_MINOR_VERSION > 15 +#define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)EnumType::Type::EnumVal) + ADD_NATIVE_ENUM(EUserInterfaceActionType, None ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, Button ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, ToggleButton ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, RadioButton ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, Check ); + ADD_NATIVE_ENUM(EUserInterfaceActionType, CollapsedButton); +#undef ADD_NATIVE_ENUM +#endif + +} + +ue_PyESlateEnums *py_ue_is_eslate_enums(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyESlateEnumsType)) + return nullptr; + return (ue_PyESlateEnums *)obj; + +} diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h new file mode 100644 index 000000000..dae16da2c --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.h @@ -0,0 +1,15 @@ +#pragma once + + + +#include "UnrealEnginePython.h" + +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + uint8 val; +} ue_PyESlateEnums; + +void ue_python_init_eslate_enums(PyObject *); + +ue_PyESlateEnums *py_ue_is_eslate_enums(PyObject *); \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp index 9749c5c67..ea4251cc0 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp @@ -6,13 +6,7 @@ static PyObject *py_ue_fassetdata_get_asset(ue_PyFAssetData *self, PyObject * args) { - PyObject *ret = (PyObject *)ue_get_python_wrapper(self->asset_data.GetAsset()); - if (!ret) - { - return PyErr_Format(PyExc_Exception, "unable to get UObject from asset"); - } - Py_INCREF(ret); - return ret; + Py_RETURN_UOBJECT(self->asset_data.GetAsset()); } static PyObject *py_ue_fassetdata_is_asset_loaded(ue_PyFAssetData *self, PyObject * args) @@ -43,11 +37,16 @@ static PyObject *py_ue_fassetdata_get_thumbnail(ue_PyFAssetData *self, PyObject return py_ue_new_fobject_thumbnail(*thumbnail); } -#if ENGINE_MINOR_VERSION > 17 +#if ENGINE_MINOR_VERSION >= 18 + static PyObject *py_ue_fassetdata_has_custom_thumbnail(ue_PyFAssetData *self, PyObject * args) { +#if ENGINE_MINOR_VERSION > 18 + if (!ThumbnailTools::AssetHasCustomThumbnail(self->asset_data.GetFullName())) +#else if (!ThumbnailTools::AssetHasCustomThumbnail(self->asset_data)) +#endif { Py_RETURN_FALSE; } @@ -71,7 +70,8 @@ static PyMethodDef ue_PyFAssetData_methods[] = { { "get_asset", (PyCFunction)py_ue_fassetdata_get_asset, METH_VARARGS, "" }, { "is_asset_loaded", (PyCFunction)py_ue_fassetdata_is_asset_loaded, METH_VARARGS, "" }, { "get_thumbnail", (PyCFunction)py_ue_fassetdata_get_thumbnail, METH_VARARGS, "" }, -#if ENGINE_MINOR_VERSION > 17 + +#if ENGINE_MINOR_VERSION >= 18 { "has_custom_thumbnail", (PyCFunction)py_ue_fassetdata_has_custom_thumbnail, METH_VARARGS, "" }, #endif { "has_cached_thumbnail", (PyCFunction)py_ue_fassetdata_has_cached_thumbnail, METH_VARARGS, "" }, diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp index d348a7291..6a30692df 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp @@ -83,6 +83,18 @@ static PyObject *py_ue_feditor_viewport_client_set_view_location(ue_PyFEditorVie Py_RETURN_NONE; } +static PyObject *py_ue_feditor_viewport_client_set_realtime(ue_PyFEditorViewportClient *self, PyObject * args) +{ + PyObject* bInRealtime; + PyObject* bStoreCurrentValue; + if (!PyArg_ParseTuple(args, "OO", &bInRealtime, &bStoreCurrentValue)) + return nullptr; + + self->editor_viewport_client->SetRealtime(PyObject_IsTrue(bInRealtime) ? true : false, + PyObject_IsTrue(bStoreCurrentValue) ? true : false); + Py_RETURN_NONE; +} + static PyMethodDef ue_PyFEditorViewportClient_methods[] = { { "take_high_res_screen_shot", (PyCFunction)py_ue_feditor_viewport_client_take_high_res_screen_shot, METH_VARARGS, "" }, { "tick", (PyCFunction)py_ue_feditor_viewport_client_tick, METH_VARARGS, "" }, @@ -94,6 +106,7 @@ static PyMethodDef ue_PyFEditorViewportClient_methods[] = { { "get_scene_depth_at_location", (PyCFunction)py_ue_feditor_viewport_client_get_scene_depth_at_location, METH_VARARGS, "" }, { "set_look_at_location", (PyCFunction)py_ue_feditor_viewport_client_set_look_at_location, METH_VARARGS, "" }, { "set_view_location", (PyCFunction)py_ue_feditor_viewport_client_set_view_location, METH_VARARGS, "" }, + { "set_realtime", (PyCFunction)py_ue_feditor_viewport_client_set_realtime, METH_VARARGS, "" }, { nullptr } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp index ea978bc11..6aeb0ce83 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFHitResult.cpp @@ -1,7 +1,8 @@ #include "UnrealEnginePythonPrivatePCH.h" -static PyObject *py_ue_fhitresult_get_reversed_hit(ue_PyFHitResult *self, PyObject * args) { +static PyObject *py_ue_fhitresult_get_reversed_hit(ue_PyFHitResult *self, PyObject * args) +{ return py_ue_new_fhitresult(FHitResult::GetReversedHit(self->hit)); } @@ -11,45 +12,49 @@ static PyMethodDef ue_PyFHitResult_methods[] = { { NULL } /* Sentinel */ }; -static PyObject *py_ue_fhitresult_get_location(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_location(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.Location); } -static PyObject *py_ue_fhitresult_get_normal(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_normal(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.Normal); } -static PyObject *py_ue_fhitresult_get_impact_point(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_impact_point(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.ImpactPoint); } -static PyObject *py_ue_fhitresult_get_impact_normal(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_impact_normal(ue_PyFHitResult *self, void *closure) +{ return py_ue_new_fvector(self->hit.ImpactNormal); } -static PyObject *py_ue_fhitresult_get_distance(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_distance(ue_PyFHitResult *self, void *closure) +{ return PyFloat_FromDouble(self->hit.Distance); } -static PyObject *py_ue_fhitresult_get_time(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_time(ue_PyFHitResult *self, void *closure) +{ return PyFloat_FromDouble(self->hit.Time); } -static PyObject *py_ue_fhitresult_get_bone_name(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_bone_name(ue_PyFHitResult *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->hit.BoneName.ToString())); } -static PyObject *py_ue_fhitresult_get_actor(ue_PyFHitResult *self, void *closure) { +static PyObject *py_ue_fhitresult_get_actor(ue_PyFHitResult *self, void *closure) +{ AActor *actor = self->hit.Actor.Get(); - if (!actor) { - Py_INCREF(Py_None); - return Py_None; + if (!actor) + { + Py_RETURN_NONE; } - ue_PyUObject *ret = ue_get_python_wrapper(actor); - if (!ret) - return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state"); - Py_INCREF(ret); - return (PyObject *)ret; + Py_RETURN_UOBJECT(actor); } @@ -106,7 +111,8 @@ static PyTypeObject ue_PyFHitResultType = { ue_PyFHitResult_getseters, }; -void ue_python_init_fhitresult(PyObject *ue_module) { +void ue_python_init_fhitresult(PyObject *ue_module) +{ ue_PyFHitResultType.tp_new = PyType_GenericNew; if (PyType_Ready(&ue_PyFHitResultType) < 0) @@ -116,13 +122,15 @@ void ue_python_init_fhitresult(PyObject *ue_module) { PyModule_AddObject(ue_module, "FHitResult", (PyObject *)&ue_PyFHitResultType); } -PyObject *py_ue_new_fhitresult(FHitResult hit) { +PyObject *py_ue_new_fhitresult(FHitResult hit) +{ ue_PyFHitResult *ret = (ue_PyFHitResult *)PyObject_New(ue_PyFHitResult, &ue_PyFHitResultType); ret->hit = hit; return (PyObject *)ret; } -ue_PyFHitResult *py_ue_is_fhitresult(PyObject *obj) { +ue_PyFHitResult *py_ue_is_fhitresult(PyObject *obj) +{ if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFHitResultType)) return nullptr; return (ue_PyFHitResult *)obj; diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp index 8f17146d7..d9537a0bd 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp @@ -191,7 +191,7 @@ static int ue_py_flinearcolor_init(ue_PyFLinearColor *self, PyObject *args, PyOb float r = 0; float g = 0; float b = 0; - float a = 255; + float a = 1.0; if (!PyArg_ParseTuple(args, "|ffff", &r, &g, &b, &a)) return -1; diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h index 1738d9ed4..4b0c81788 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFMorphTargetDelta.h @@ -1,6 +1,8 @@ #pragma once #include "UnrealEnginePython.h" +#if ENGINE_MINOR_VERSION > 12 + #include "Runtime/Engine/Classes/Animation/MorphTarget.h" struct ue_PyFMorphTargetDelta { @@ -14,3 +16,4 @@ void ue_python_init_fmorph_target_delta(PyObject *); PyObject *py_ue_new_fmorph_target_delta(FMorphTargetDelta); ue_PyFMorphTargetDelta *py_ue_is_fmorph_target_delta(PyObject *); +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h index a8ed3f6e5..edc386582 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.h @@ -3,6 +3,12 @@ #include "Runtime/Engine/Public/SkeletalMeshTypes.h" +#if ENGINE_MINOR_VERSION > 18 + +#include "Runtime/Engine/Public/Rendering/SkeletalMeshLODModel.h" + +#endif + struct ue_PyFSoftSkinVertex { PyObject_HEAD /* Type-specific fields go here. */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp index 1ddb2b6f5..ec600856d 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.cpp @@ -104,6 +104,7 @@ void ue_python_init_fstring_asset_reference(PyObject *ue_module) Py_INCREF(&ue_PyFStringAssetReferenceType); PyModule_AddObject(ue_module, "FStringAssetReference", (PyObject *)&ue_PyFStringAssetReferenceType); + PyModule_AddObject(ue_module, "FSoftObjectPath", (PyObject *)&ue_PyFStringAssetReferenceType); } PyObject *py_ue_new_fstring_asset_reference(FStringAssetReference ref) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h index 058cf005a..f70e84329 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFStringAssetReference.h @@ -4,7 +4,9 @@ #include "UnrealEnginePython.h" +#if ENGINE_MINOR_VERSION < 18 #include "Runtime/CoreUObject/Public/Misc/StringAssetReference.h" +#endif typedef struct { PyObject_HEAD diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp index b76be90a8..e91f2dc49 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp @@ -26,10 +26,22 @@ static PyObject *py_ue_ftransform_get_relative_transform(ue_PyFTransform *self, return py_ue_new_ftransform(self->transform.GetRelativeTransform(py_transform->transform)); } +static PyObject *py_ue_ftransform_get_matrix(ue_PyFTransform *self, PyObject * args) +{ + FMatrix matrix = self->transform.ToMatrixWithScale(); + UScriptStruct *u_struct = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR("Matrix")); + if (!u_struct) + { + return PyErr_Format(PyExc_Exception, "unable to get Matrix struct"); + } + return py_ue_new_uscriptstruct(u_struct, (uint8 *)&matrix); +} + static PyMethodDef ue_PyFTransform_methods[] = { { "inverse", (PyCFunction)py_ue_ftransform_inverse, METH_VARARGS, "" }, { "get_relative_transform", (PyCFunction)py_ue_ftransform_get_relative_transform, METH_VARARGS, "" }, { "normalize_rotation", (PyCFunction)py_ue_ftransform_normalize_rotation, METH_VARARGS, "" }, + { "get_matrix", (PyCFunction)py_ue_ftransform_get_matrix, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Public/PyActor.h b/Source/UnrealEnginePython/Public/PyActor.h index 1670fc201..dcc03fbde 100644 --- a/Source/UnrealEnginePython/Public/PyActor.h +++ b/Source/UnrealEnginePython/Public/PyActor.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyActor : public AActor +class UNREALENGINEPYTHON_API APyActor : public AActor { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyCharacter.h b/Source/UnrealEnginePython/Public/PyCharacter.h index 1eea75bb0..f1136c9cf 100644 --- a/Source/UnrealEnginePython/Public/PyCharacter.h +++ b/Source/UnrealEnginePython/Public/PyCharacter.h @@ -7,7 +7,7 @@ UCLASS() -class APyCharacter : public ACharacter +class UNREALENGINEPYTHON_API APyCharacter : public ACharacter { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyHUD.h b/Source/UnrealEnginePython/Public/PyHUD.h index 107cb7ff5..de5ffdd40 100644 --- a/Source/UnrealEnginePython/Public/PyHUD.h +++ b/Source/UnrealEnginePython/Public/PyHUD.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyHUD : public AHUD +class UNREALENGINEPYTHON_API APyHUD : public AHUD { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h new file mode 100644 index 000000000..fa20c574f --- /dev/null +++ b/Source/UnrealEnginePython/Public/PyNativeWidgetHost.h @@ -0,0 +1,45 @@ +// Copyright Kite & Lightning + +#pragma once + +#include "UObject/Class.h" + +#include "Components/NativeWidgetHost.h" +#include "PyNativeWidgetHost.generated.h" + + + +USTRUCT(BlueprintType) +struct UNREALENGINEPYTHON_API FPythonSWidgetWrapper +{ + GENERATED_USTRUCT_BODY() + + TSharedPtr Widget; +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithCopy = true, + }; +}; + +/** + * + */ +UCLASS() +class UNREALENGINEPYTHON_API UPyNativeWidgetHost : public UNativeWidgetHost +{ + GENERATED_BODY() + + + UPyNativeWidgetHost(const FObjectInitializer& ObjectInitializer); + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + +}; + diff --git a/Source/UnrealEnginePython/Public/PyPawn.h b/Source/UnrealEnginePython/Public/PyPawn.h index 9e7fa632b..974c930a2 100644 --- a/Source/UnrealEnginePython/Public/PyPawn.h +++ b/Source/UnrealEnginePython/Public/PyPawn.h @@ -8,7 +8,7 @@ UCLASS(BlueprintType, Blueprintable) -class APyPawn : public APawn +class UNREALENGINEPYTHON_API APyPawn : public APawn { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PyUserWidget.h b/Source/UnrealEnginePython/Public/PyUserWidget.h index 3a7468204..20b856f32 100644 --- a/Source/UnrealEnginePython/Public/PyUserWidget.h +++ b/Source/UnrealEnginePython/Public/PyUserWidget.h @@ -7,12 +7,12 @@ UCLASS(BlueprintType, Blueprintable) -class UPyUserWidget : public UUserWidget +class UNREALENGINEPYTHON_API UPyUserWidget : public UUserWidget { GENERATED_BODY() public: - + UPyUserWidget(const FObjectInitializer& ObjectInitializer); ~UPyUserWidget(); virtual void NativeConstruct() override; @@ -47,6 +47,21 @@ class UPyUserWidget : public UUserWidget UFUNCTION(BlueprintCallable, Category = "Python") void CallPythonUserWidgetMethod(FString method_name, FString args); + UPROPERTY(EditAnywhere, Category = "Python", BlueprintReadWrite) + TWeakObjectPtr PyNativeWidgetHost; + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; +#endif + + void SetSlateWidget(TSharedRef InContent); + virtual void ReleaseSlateResources(bool bReleaseChildren) override; + +protected: + // UWidget interface + virtual TSharedRef RebuildWidget() override; + // End of UWidget interface + private: PyObject *py_user_widget_instance; // mapped uobject, required for debug and advanced reflection diff --git a/Source/UnrealEnginePython/Public/PythonComponent.h b/Source/UnrealEnginePython/Public/PythonComponent.h index 98e88d44e..51f227db3 100644 --- a/Source/UnrealEnginePython/Public/PythonComponent.h +++ b/Source/UnrealEnginePython/Public/PythonComponent.h @@ -5,7 +5,7 @@ #include "PythonComponent.generated.h" UCLASS(BlueprintType, Blueprintable, ClassGroup = (Python), meta = (BlueprintSpawnableComponent)) -class UPythonComponent : public UActorComponent +class UNREALENGINEPYTHON_API UPythonComponent : public UActorComponent { GENERATED_BODY() diff --git a/Source/UnrealEnginePython/Public/PythonDelegate.h b/Source/UnrealEnginePython/Public/PythonDelegate.h index 2a8b0bc8a..2a80205c6 100644 --- a/Source/UnrealEnginePython/Public/PythonDelegate.h +++ b/Source/UnrealEnginePython/Public/PythonDelegate.h @@ -18,12 +18,6 @@ class UPythonDelegate : public UObject void PyInputHandler(); void PyInputAxisHandler(float value); - bool Tick(float DeltaTime); - -#if WITH_EDITOR - void PyFOnAssetPostImport(UFactory *factory, UObject *u_object); -#endif - protected: UFunction *signature; bool signature_set; @@ -33,6 +27,6 @@ class UPythonDelegate : public UObject PyObject *py_callable; - + }; diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h new file mode 100644 index 000000000..0063db4df --- /dev/null +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -0,0 +1,243 @@ +#pragma once + +#include "UnrealEnginePythonPrivatePCH.h" + +class FUnrealEnginePythonHouseKeeper +{ + + struct FPythonUOjectTracker + { + FWeakObjectPtr Owner; + ue_PyUObject *PyUObject; + + FPythonUOjectTracker(UObject *Object, ue_PyUObject *InPyUObject) + { + Owner = FWeakObjectPtr(Object); + PyUObject = InPyUObject; + } + }; + + struct FPythonDelegateTracker + { + FWeakObjectPtr Owner; + UPythonDelegate *Delegate; + + FPythonDelegateTracker(UPythonDelegate *DelegateToTrack, UObject *DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonDelegateTracker() + { + } + }; + + struct FPythonSWidgetTracker + { + TWeakPtr Owner; + ue_PySWidget *PySWidget; + + FPythonSWidgetTracker(TSharedRef InOwner, ue_PySWidget *InPySWidget) + { + Owner = InOwner; + PySWidget = InPySWidget; + } + }; + + struct FPythonSWidgetDelegateTracker + { + TWeakPtr Owner; + TSharedPtr Delegate; + + FPythonSWidgetDelegateTracker(TSharedRef DelegateToTrack, TSharedRef DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonSWidgetDelegateTracker() + { + } + }; + +public: + + static FUnrealEnginePythonHouseKeeper *Get() + { + static FUnrealEnginePythonHouseKeeper *Singleton; + if (!Singleton) + { + Singleton = new FUnrealEnginePythonHouseKeeper(); + // register a new delegate for the GC +#if ENGINE_MINOR_VERSION >= 18 + FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#else + FCoreUObjectDelegates::PostGarbageCollect.AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#endif + } + return Singleton; + } + + void RunGCDelegate() + { + FScopePythonGIL gil; + RunGC(); + } + + int32 RunGC() + { + int32 Garbaged = PyUObjectsGC(); + Garbaged += DelegatesGC(); + return Garbaged; + } + + bool IsValidPyUObject(ue_PyUObject *PyUObject) + { + if (!PyUObject) + return false; + + UObject *Object = PyUObject->ue_object; + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return false; + } + + if (!Tracker->Owner.IsValid()) + return false; + + return true; + + } + + void RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject) + { + UObjectPyMapping.Add(Object, FPythonUOjectTracker(Object, InPyUObject)); + } + + void UnregisterPyUObject(UObject *Object) + { + UObjectPyMapping.Remove(Object); + } + + ue_PyUObject *GetPyUObject(UObject *Object) + { + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return nullptr; + } + + if (!Tracker->Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("DEFREF'ing UObject at %p (refcnt: %d)"), Object, Tracker->PyUObject->ob_base.ob_refcnt); +#endif + Py_DECREF((PyObject *)Tracker->PyUObject); + UnregisterPyUObject(Object); + return nullptr; + } + + return Tracker->PyUObject; + } + + uint32 PyUObjectsGC() + { + uint32 Garbaged = 0; + TArray BrokenList; + for (auto &UObjectPyItem : UObjectPyMapping) + { + UObject *Object = UObjectPyItem.Key; + FPythonUOjectTracker &Tracker = UObjectPyItem.Value; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), Object); +#endif + if (!Tracker.Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), Object, Tracker.PyUObject->ob_base.ob_refcnt); +#endif + BrokenList.Add(Object); + Garbaged++; + } + else + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), Object, *Object->GetName()); +#endif + } + } + + for (UObject *Object : BrokenList) + { + FPythonUOjectTracker &Tracker = UObjectPyMapping[Object]; + Py_DECREF((PyObject *)Tracker.PyUObject); + UnregisterPyUObject(Object); + } + + return Garbaged; + + } + + + int32 DelegatesGC() + { + int32 Garbaged = 0; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Display, TEXT("Garbage collecting %d delegates"), PyDelegatesTracker.Num()); +#endif + for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; + if (!Tracker.Owner.IsValid(true)) + { + Tracker.Delegate->RemoveFromRoot(); + PyDelegatesTracker.RemoveAt(i); + Garbaged++; + } + + } + return Garbaged; + } + + UPythonDelegate *NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature) + { + UPythonDelegate *Delegate = NewObject(); + + Delegate->AddToRoot(); + Delegate->SetPyCallable(PyCallable); + Delegate->SetSignature(Signature); + + FPythonDelegateTracker Tracker(Delegate, Owner); + PyDelegatesTracker.Add(Tracker); + + return Delegate; + } + + TSharedRef NewSlateDelegate(TSharedRef Owner, PyObject *PyCallable) + { + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); + PySlateDelegatesTracker.Add(Tracker); + + return Delegate; + } + + TSharedRef NewStaticSlateDelegate(PyObject *PyCallable) + { + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + PyStaticSlateDelegatesTracker.Add(Delegate); + + return Delegate; + } + +private: + TMap UObjectPyMapping; + TArray PyDelegatesTracker; + + + TArray PySlateTracker; + TArray PySlateDelegatesTracker; + TArray> PyStaticSlateDelegatesTracker; +}; diff --git a/Source/UnrealEnginePython/Public/PythonScript.h b/Source/UnrealEnginePython/Public/PythonScript.h index cac7b554a..15ecaa284 100644 --- a/Source/UnrealEnginePython/Public/PythonScript.h +++ b/Source/UnrealEnginePython/Public/PythonScript.h @@ -1,6 +1,7 @@ #pragma once - +#include "UnrealEnginePython.h" +#include "UObject/Object.h" #include "PythonScript.generated.h" UCLASS(MinimalAPI) diff --git a/Source/UnrealEnginePython/Public/PythonSmartDelegate.h b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h new file mode 100644 index 000000000..22296a298 --- /dev/null +++ b/Source/UnrealEnginePython/Public/PythonSmartDelegate.h @@ -0,0 +1,28 @@ +#pragma once + +#include "UnrealEnginePythonPrivatePCH.h" + +class FPythonSmartDelegate : public TSharedFromThis +{ + +public: + FPythonSmartDelegate(); + ~FPythonSmartDelegate(); + + void SetPyCallable(PyObject *callable); + + + + bool Tick(float DeltaTime); + void Void(); + +#if WITH_EDITOR + void PyFOnAssetPostImport(UFactory *factory, UObject *u_object); +#endif + +protected: + + PyObject *py_callable; + +}; + diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index 7cb0fbebb..1e1d28f35 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -10,9 +10,17 @@ #define UEPY_THREADING 1 #include "Engine.h" +#include "Runtime/Launch/Resources/Version.h" #include "Runtime/SlateCore/Public/Styling/ISlateStyle.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" +// We need to make sure reference structs do not mistaken for callable +#define PyCalllable_Check_Extended(value) PyCallable_Check(value) && py_ue_is_uscriptstruct(value) == nullptr + +#if ENGINE_MINOR_VERSION >= 18 +#define FStringAssetReference FSoftObjectPath +#endif + DECLARE_LOG_CATEGORY_EXTERN(LogPython, Log, All); @@ -78,7 +86,10 @@ class UNREALENGINEPYTHON_API FUnrealEnginePythonModule : public IModuleInterface TSharedPtr StyleSet; }; -struct FScopePythonGIL { + + +struct FScopePythonGIL +{ FScopePythonGIL() { #if defined(UEPY_THREADING) @@ -90,7 +101,8 @@ struct FScopePythonGIL { ~FScopePythonGIL() { #if defined(UEPY_THREADING) - if (safeForRelease) { + if (safeForRelease) + { UnrealEnginePythonModule.PythonGILRelease(); } #endif diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 87819ffee..161f3dd06 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -43,7 +43,6 @@ private string BinariesPath get { return Path.GetFullPath(Path.Combine(ModuleDirectory, "../../Binaries/")); } } - private string[] windowsKnownPaths = { "C:/Program Files/Python36", @@ -107,6 +106,7 @@ public UnrealEnginePython(TargetInfo Target) #endif { + PublicIncludePaths.AddRange( new string[] { "UnrealEnginePython/Public", @@ -154,12 +154,24 @@ public UnrealEnginePython(TargetInfo Target) "RenderCore", "MovieSceneCapture", "Landscape", - "Foliage" + "Foliage", // ... add private dependencies that you statically link with here ... } ); +#if WITH_FORWARDED_MODULE_RULES_CTOR + BuildVersion Version; + if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) + { + if (Version.MinorVersion >= 18) + { + PrivateDependencyModuleNames.Add("ApplicationCore"); + } + } +#endif + + DynamicallyLoadedModuleNames.AddRange( new string[] { @@ -167,8 +179,11 @@ public UnrealEnginePython(TargetInfo Target) } ); - +#if WITH_FORWARDED_MODULE_RULES_CTOR + if (Target.bBuildEditor) +#else if (UEBuildConfiguration.bBuildEditor) +#endif { PrivateDependencyModuleNames.AddRange(new string[]{ "UnrealEd", @@ -192,7 +207,8 @@ public UnrealEnginePython(TargetInfo Target) "FBX", "Persona", "PropertyEditor", - "LandscapeEditor" + "LandscapeEditor", + "MaterialEditor" }); } @@ -274,6 +290,15 @@ public UnrealEnginePython(TargetInfo Target) }*/ } + private bool IsPathRelative(string Path) + { + bool IsRooted = Path.StartsWith("\\", System.StringComparison.Ordinal) || // Root of the current directory on Windows. Also covers "\\" for UNC or "network" paths. + Path.StartsWith("/", System.StringComparison.Ordinal) || // Root of the current directory on Windows, root on UNIX-likes. + // Also covers "\\", considering normalization replaces "\\" with "//". + (Path.Length >= 2 && char.IsLetter(Path[0]) && Path[1] == ':'); // Starts with ":" + return !IsRooted; + } + private string DiscoverPythonPath(string[] knownPaths) { // insert the PYTHONHOME content as the first known path @@ -284,16 +309,23 @@ private string DiscoverPythonPath(string[] knownPaths) foreach (string path in paths) { - string headerFile = Path.Combine(path, "include", "Python.h"); + string actualPath = path; + + if (IsPathRelative(actualPath)) + { + actualPath = Path.GetFullPath(Path.Combine(ModuleDirectory, actualPath)); + } + + string headerFile = Path.Combine(actualPath, "include", "Python.h"); if (File.Exists(headerFile)) { - return path; + return actualPath; } // this is mainly useful for OSX - headerFile = Path.Combine(path, "Headers", "Python.h"); + headerFile = Path.Combine(actualPath, "Headers", "Python.h"); if (File.Exists(headerFile)) { - return path; + return actualPath; } } return ""; @@ -384,7 +416,8 @@ private string GetWindowsPythonLibFile(string basePath) } if (!found) { - System.Console.WriteLine("[WARNING] Your Python installation is not in the system PATH environment variable, very probably the plugin will fail to load"); + System.Console.WriteLine("[WARNING] Your Python installation is not in the system PATH environment variable."); + System.Console.WriteLine("[WARNING] Ensure your python paths are set in GlobalConfig (DefaultEngine.ini) so the path can be corrected at runtime."); } // first try with python3 for (int i = 9; i >= 0; i--) diff --git a/UnrealEnginePython.uplugin b/UnrealEnginePython.uplugin index 59ce1091d..645169b3d 100644 --- a/UnrealEnginePython.uplugin +++ b/UnrealEnginePython.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "1.4.3", + "VersionName": "1.5.0", "FriendlyName": "UnrealEnginePython", "Description": "Embed a Python VM in your project", "Category": "Scripting Languages", @@ -20,16 +20,16 @@ "Type": "Runtime", "LoadingPhase": "PreDefault" }, - { - "Name": "PythonConsole", - "Type": "Editor", - "LoadingPhase": "PostDefault" - }, - { - "Name": "PythonEditor", - "Type": "Editor", - "LoadingPhase": "PostDefault" - } + { + "Name": "PythonConsole", + "Type": "Editor", + "LoadingPhase": "PostDefault" + }, + { + "Name" : "PythonEditor", + "Type" : "Editor", + "LoadingPhase": "PostDefault" + } ], "Plugins": [ { diff --git a/docs/Material_API.md b/docs/Material_API.md index 8d7bcda55..c802beca0 100644 --- a/docs/Material_API.md +++ b/docs/Material_API.md @@ -54,9 +54,9 @@ You have two ways to create a instanced material: (new_material is a reference to a previously created/loaded material) ```python -from unreal_engine.classes import MaterialInstancedConstant +from unreal_engine.classes import MaterialInstanceConstant -material_instance = MaterialInstancedConstant() +material_instance = MaterialInstanceConstant() material_instance.set_name('New Funny Material Instance') material_instance.set_material_parent(new_material) material_instance.save_package('/Game/Materials/instanced') @@ -70,6 +70,13 @@ import unreal_engine as ue material_instance = ue.create_material_instance(new_material) ``` +```python +import unreal_engine as ue +# you can also specify a path and a name to the new material instance. +material_instance = ue.create_material_instance(new_material, '/Game/Materials/', 'New Funny Material Instance') +``` + + Or the factory way: ```python diff --git a/docs/PythonConsole.md b/docs/PythonConsole.md index f8886c1ab..598edbc2b 100644 --- a/docs/PythonConsole.md +++ b/docs/PythonConsole.md @@ -121,6 +121,8 @@ This is an ad-hoc factory subclassing the standard FbxFactory (many thanks to Ch import unreal_engine as ue import os import os.path +from unreal_engine.classes import PyFbxFactory, Skeleton +from unreal_engine.enums import EFBXImportType """ Scan a directory for FBX's and import them with a single skeleton @@ -135,23 +137,19 @@ skeleton_name = 'UE4_Mannequin_Skeleton' skeleton = ue.find_object(skeleton_name) # get the PyFbxFactory -factory = ue.find_class('PyFbxFactory') +factory = PyFbxFactory() # setup fbx importer options -fbx_import_ui_class = ue.find_class('FbxImportUI') -fbx_import_ui = ue.new_object(fbx_import_ui_class) -fbx_import_ui.set_property('Skeleton', skeleton) - +factory.ImportUI.MeshTypeToImport = EFBXImportType.FBXIT_Animation +factory.ImportUI.Skeleton = skeleton # the fbx import procedure def fbx_import(filename, asset_name): ue.log('importing ' + filename + ' to ' + asset_name) - # set import options - ue.set_fbx_import_option(fbx_import_ui) - # import he file using PyFbxFactory - asset = ue.import_asset(filename, asset_name, factory) + # import the file using PyFbxFactory + asset = factory.factory_import_object(filename, asset_name) # print the skeleton name - ue.log('FBX imported with skeleton: ' + asset.get_property('Skeleton').get_name()) + ue.log('FBX imported with skeleton: ' + asset.Skeleton.get_name()) # scan the directory and uatomatically assign a name using an incremental id diff --git a/docs/Transactions_API.md b/docs/Transactions_API.md index 51064b6cf..d788ea8df 100644 --- a/docs/Transactions_API.md +++ b/docs/Transactions_API.md @@ -39,6 +39,39 @@ ue.editor_undo() ue.editor_redo() ``` + +Managing transactions with property modification +- +In this little example we are creating a cone using a static mesh object from the engine content. We start a transaction and move the cone by 15 units and use the `modify` call to keep our property modifications pushed to the undo stack. + +```python + +import unreal_engine as ue +from unreal_engine import FVector +from unreal_engine.classes import StaticMesh, StaticMeshActor + +#Create procedurally a Cone in the Editor +world = ue.get_editor_world() +cone2 = world.actor_spawn(StaticMeshActor) +cone2.StaticMeshComponent.StaticMesh = ue.load_object(StaticMesh, '/Engine/BasicShapes/Cone') + +#Start Transaction +ue.begin_transaction("Move Up +15") +position = cone2.get_actor_location() +position.y += 15 + +#Call modify to track property changes before setting the position +cone2.modify() +cone2.set_actor_location(position) +ue.end_transaction() + +#Undo "Move Up +15" +##ue.editor_undo() +``` + + + + Functions - diff --git a/examples/MaterialExpressionStaticSwitch.md b/examples/MaterialExpressionStaticSwitch.md new file mode 100644 index 000000000..91bd959af --- /dev/null +++ b/examples/MaterialExpressionStaticSwitch.md @@ -0,0 +1,75 @@ + +![MaterialExpressionStaticSwitch Screenshot](https://github.com/20tab/UnrealEnginePython/blob/master/examples/MaterialExpressionStaticSwitch.png) + +```python +import unreal_engine as ue +from unreal_engine.classes import MaterialFactoryNew +from unreal_engine.classes import MaterialExpressionStaticSwitch, MaterialExpressionStaticSwitchParameter, MaterialExpressionVectorParameter +from unreal_engine.structs import ColorMaterialInput, ExpressionInput +import time + +material_factory = MaterialFactoryNew() +material = material_factory.factory_create_new('/Game/Materials/Test' + str(int(time.time()))) + +material.modify() + +static_switch_node = MaterialExpressionStaticSwitch('', material) +static_switch_node.MaterialExpressionEditorX = -200 +static_switch_node.MaterialExpressionEditorY = 50 + +static_switch_parameter_node_true = MaterialExpressionStaticSwitchParameter('', material) +static_switch_parameter_node_true.MaterialExpressionEditorX = -400 +static_switch_parameter_node_true.MaterialExpressionEditorY = 0 +static_switch_parameter_node_true.ParameterName = 'True' + +static_switch_parameter_node_false = MaterialExpressionStaticSwitchParameter('', material) +static_switch_parameter_node_false.MaterialExpressionEditorX = -400 +static_switch_parameter_node_false.MaterialExpressionEditorY = 200 +static_switch_parameter_node_false.ParameterName = 'False' + +static_switch_node.A = ExpressionInput(Expression=static_switch_parameter_node_true) +static_switch_node.B = ExpressionInput(Expression=static_switch_parameter_node_false) + +vector_parameter_one = MaterialExpressionVectorParameter('', material) +vector_parameter_one.MaterialExpressionEditorX = -600 +vector_parameter_one.MaterialExpressionEditorY = -150 +vector_parameter_one.ParameterName = 'One' + +vector_parameter_two = MaterialExpressionVectorParameter('', material) +vector_parameter_two.MaterialExpressionEditorX = -800 +vector_parameter_two.MaterialExpressionEditorY = -50 +vector_parameter_two.ParameterName = 'Two' + +vector_parameter_three = MaterialExpressionVectorParameter('', material) +vector_parameter_three.MaterialExpressionEditorX = -600 +vector_parameter_three.MaterialExpressionEditorY = 150 +vector_parameter_three.ParameterName = 'Three' + +vector_parameter_four = MaterialExpressionVectorParameter('', material) +vector_parameter_four.MaterialExpressionEditorX = -800 +vector_parameter_four.MaterialExpressionEditorY = 250 +vector_parameter_four.ParameterName = 'Four' + + +static_switch_parameter_node_true.A = ExpressionInput(Expression=vector_parameter_one) +static_switch_parameter_node_true.B = ExpressionInput(Expression=vector_parameter_two) + +static_switch_parameter_node_false.A = ExpressionInput(Expression=vector_parameter_three) +static_switch_parameter_node_false.B = ExpressionInput(Expression=vector_parameter_four) + +material.BaseColor = ColorMaterialInput(Expression=static_switch_node) + +material.Expressions = [ + static_switch_node, + static_switch_parameter_node_true, + static_switch_parameter_node_false, + vector_parameter_one, + vector_parameter_two, + vector_parameter_three, + vector_parameter_four + ] + +material.post_edit_change() + +ue.open_editor_for_asset(material) +``` diff --git a/examples/MaterialExpressionStaticSwitch.png b/examples/MaterialExpressionStaticSwitch.png new file mode 100644 index 000000000..247a19eef Binary files /dev/null and b/examples/MaterialExpressionStaticSwitch.png differ diff --git a/tests/test_clipboard.py b/tests/test_clipboard.py new file mode 100644 index 000000000..bdabedf73 --- /dev/null +++ b/tests/test_clipboard.py @@ -0,0 +1,12 @@ +import unittest +import unreal_engine as ue + +class TestClipboard(unittest.TestCase): + + def test_copy_and_paste(self): + ue.clipboard_copy('Hello from python unit test') + self.assertEqual(ue.clipboard_paste(), 'Hello from python unit test') + + def test_copy_and_paste_red_light(self): + ue.clipboard_copy('Hello from red light test') + self.assertNotEqual(ue.clipboard_paste(), 'Hello from python unit test') \ No newline at end of file diff --git a/tests/test_structs.py b/tests/test_structs.py index 52eee7bf8..9b6bb351f 100644 --- a/tests/test_structs.py +++ b/tests/test_structs.py @@ -1,6 +1,7 @@ import unittest import unreal_engine as ue from unreal_engine.structs import ColorMaterialInput, Key +from unreal_engine.structs import StaticMeshSourceModel, MeshBuildSettings class TestStructs(unittest.TestCase): @@ -37,6 +38,21 @@ def test_cmp(self): key2 = Key(KeyName='SpaceBar') self.assertEqual(key1, key2) + def test_ptr(self): + source_model = StaticMeshSourceModel() + source_model.ref().BuildSettings.ref().bRecomputeNormals=False + source_model.ref().BuildSettings.ref().bRecomputeTangents=True + source_model.ref().BuildSettings.ref().bUseMikkTSpace=True + source_model.ref().BuildSettings.ref().bBuildAdjacencyBuffer=True + source_model.ref().BuildSettings.ref().bRemoveDegenerates=True + + source_model2 = source_model.clone() + self.assertEqual(source_model2.BuildSettings.bRecomputeNormals, False) + self.assertEqual(source_model2.BuildSettings.bRecomputeTangents, True) + self.assertEqual(source_model2.BuildSettings.bUseMikkTSpace, True) + self.assertEqual(source_model2.BuildSettings.bBuildAdjacencyBuffer, True) + self.assertEqual(source_model2.BuildSettings.bRemoveDegenerates, True) + diff --git a/tutorials/FixingMixamoRootMotionWithPython.md b/tutorials/FixingMixamoRootMotionWithPython.md index 4eabad28d..3abb1266d 100644 --- a/tutorials/FixingMixamoRootMotionWithPython.md +++ b/tutorials/FixingMixamoRootMotionWithPython.md @@ -169,7 +169,7 @@ bone_map = mesh.skeletal_mesh_get_bone_map() and to assign a new one: ```python -mesh.skeletal_mesh_get_bone_map([17, 22, 26, 30, ...]) +mesh.skeletal_mesh_set_bone_map([17, 22, 26, 30, ...]) ``` In the same way you can retrieve soft skin vertices: