Skip to content

Commit

Permalink
Merge branch 'pr/9'
Browse files Browse the repository at this point in the history
  • Loading branch information
getnamo committed Jul 21, 2018
2 parents b488657 + ca07c3f commit 3b04a35
Show file tree
Hide file tree
Showing 386 changed files with 8,932 additions and 4,653 deletions.
136 changes: 82 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ 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

Teaser (by Kite & Lightning): https://twitter.com/KNLstudio/status/932657812466843648

Fixing Mixamo RootMotion tuturial: https://github.com/20tab/UnrealEnginePython/blob/master/tutorials/FixingMixamoRootMotionWithPython.md

Funny snippets for working with StaticMesh and SkeletalMesh assets: https://github.com/20tab/UnrealEnginePython/blob/master/tutorials/SnippetsForStaticAndSkeletalMeshes.md

More tutorials: https://github.com/20tab/UnrealEnginePython/tree/master/tutorials

# How and Why ?

Expand All @@ -29,10 +36,12 @@ 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, 4.18 and 4.19
The currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19 and 4.20

We support official python.org releases as well as IntelPython and Anaconda distributions.

Note: this plugin has nothing to do with the experimental 'PythonScriptPlugin' included in Unreal Engine >= 4.19. We aim at full integration with engine and editor (included the Slate api), as well as support for the vast majority of python features like asyncio, coroutines, generators, threads and third party modules.

# Binary installation on Windows (64 bit)

Check in the releases page (https://github.com/20tab/UnrealEnginePython/releases) if there is a binary version that matches your configuration (otherwise open an issue asking us for it [please specify the python version too]) and download it.
Expand Down Expand Up @@ -516,6 +525,8 @@ ts.dumb = DumbStruct(Foo=17, Bar=22)
ts.ref().dumb.foo().Foo = 22
```

More details here: https://github.com/20tab/UnrealEnginePython/blob/master/docs/MemoryManagement.md

The ue_site.py file
-------------------

Expand Down Expand Up @@ -783,55 +794,81 @@ It allows you to run, create, modify and delete scripts directly from the UE edi

The first pull request for the editor has been issued by https://github.com/sun5471 so many thanks to him ;)

Integration with PyQT
---------------------
Integration with Qt4/Qt5/PySide2
--------------------------------

To correctly integrates PyQT with UnrealEngine the python plugin must correctly setup the GIL (and this is done) and exceptions must be managed ad-hoc (not doing it will result in a deadlock whenever a qt signal handler raises an exception)
Thanks to solid GIL management, you can integrate Qt python apps in Unreal Engine 4.

This is an example of having a QT window along the editor to trigger asset reimporting (pay attention to the sys.excepthook usage):
Pay attention to not call app.exec_() as it will result in Qt taking control of the UE loop. Instead use a ticker to integrate the Qt loop in the editor loop:

```py
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget
import unreal_engine as ue
```python

# save it as ueqt.py
import sys
import traceback
import unreal_engine as ue
import PySide2
from PySide2 import QtWidgets

def ue_exception(_type, value, back):
ue.log_error(value)
tb_lines = traceback.format_exception(_type, value, back)
for line in tb_lines:
ue.log_error(line)
app = QtWidgets.QApplication(sys.argv)

sys.excepthook = ue_exception
def ticker_loop(delta_time):
app.processEvents()
return True

skeletal_mappings = {}
ticker = ue.add_ticker(ticker_loop)
```
now you can start writing your gui (this is a simple example loading asset thumbnail):

def selected_skeletal_mesh(item):
uobject = skeletal_mappings[item.data()]
ue.log('Ready to reimport: ' + uobject.get_name())
uobject.asset_reimport()
```python
import ueqt
from PySide2 import QtCore, QtWidgets, QtGui
import unreal_engine as ue

#check if an instance of the application is already running
app = QApplication.instance()
if app is None:
app = QApplication([])
else:
print("App already running.")
from unreal_engine import FARFilter

win = QWidget()
win.setWindowTitle('Unreal Engine 4 skeletal meshes reimporter')
_filter = FARFilter()
_filter.class_names = ['SkeletalMesh', 'Material']

wlist = QListWidget(win)
for asset in ue.get_assets_by_class('SkeletalMesh'):
wlist.addItem(asset.get_name())
skeletal_mappings[asset.get_name()] = asset

wlist.clicked.connect(selected_skeletal_mesh)
wlist.show()
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.vertical = QtWidgets.QVBoxLayout()
self.scroll = QtWidgets.QScrollArea()
self.content = QtWidgets.QWidget()
self.scroll.setWidget(self.content)
self.scroll.setWidgetResizable(True)
self.layout = QtWidgets.QVBoxLayout()

for asset_data in ue.get_assets_by_filter(_filter, True):
try:
thumbnail = asset_data.get_thumbnail()
except:
continue

win.show()
label = QtWidgets.QLabel()
data = thumbnail.get_uncompressed_image_data()
image = QtGui.QImage(data, 256, 256, QtGui.QImage.Format_RGB32)
label.setPixmap(QtGui.QPixmap.fromImage(image).scaled(256, 256))
self.layout.addWidget(label)

self.content.setLayout(self.layout)
self.vertical.addWidget(self.scroll)
self.setLayout(self.vertical)



widget = MyWidget()
widget.resize(800, 600)
widget.show()

root_window = ue.get_editor_window()
root_window.set_as_owner(widget.winId())
```

(no need to allocate a new Qt app, or start it, as the UE4 Editor, thanks to to ueqt module is now the Qt app itself)

Note the 2 final lines: they 'attach' the Qt window as a 'child' of the editor root window. Note that on windows platform this is not simple parenting but 'ownership'.

Memory management
-----------------

Expand All @@ -841,6 +878,7 @@ Starting from release 20180226 a new memory management system has been added (FU

The same system works for delegates, as well as Slate.

More details here: https://github.com/20tab/UnrealEnginePython/blob/master/docs/MemoryManagement.md

Unit Testing
------------
Expand All @@ -851,25 +889,14 @@ To run the unit tests (ensure to run them on an empty/useless project to avoid m

```python
import unreal_engine as ue
ue.sandbox_exec(ue.find_plugin('UnrealEnginePython').get_base_dir() + '/run_tests.py')
ue.py_exec(ue.find_plugin('UnrealEnginePython').get_base_dir() + '/run_tests.py')
```
if you plan to add new features to the plugin, including a test suite in your pull request will be really appreciated ;)

Threading (Experimental)
Threading
------------------------

By default the plugin is compiled without effective python threads support. This is for 2 main reasons:

* we still do not have numbers about the performance impact of constantly acquiring and releasing the GIL
* we need a better test suite

By the way, if you want to play with experimental threading support, just uncomment

```c
//#define UEPY_THREADING 1
```

on top of UnrealEnginePythonPrivatePCH.h and rebuild the plugin.
Since release 20180624 threading is fully supported.

As with native threads, do not modify (included deletion) UObjects from non-main threads.

Expand All @@ -885,7 +912,7 @@ Sometimes you may have a UObject and know that it is backed by a python object.

This would be resolved as shown below:

```
```python
import unreal_engine as ue

class Explosive:
Expand All @@ -911,12 +938,12 @@ What is going on here in `BadGuy` is that self.uobject is a reference to the PyA
Status and Known issues
-----------------------

The project could be considered in beta state.

Exposing the full ue4 api is a huge amount of work, feel free to make pull requests for your specific needs.

We still do not have a plugin icon ;)

We try to do our best to "protect" the user, but you can effectively crash UE from python as you are effectively calling the C/C++ api

Contacts and Commercial Support
-------------------------------

Expand All @@ -933,3 +960,4 @@ Such a big project requires constant sponsorship, special thanks go to:

* GoodTH.INC https://www.goodthinc.com/ (they are sponsoring the sequencer api)

* Quixel AB https://megascans.se/ (built their integration tool over UnrealEnginePython giving us tons of useful feedbacks and ideas)
20 changes: 20 additions & 0 deletions Source/PythonAutomation/Private/PythonAutomationModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

#include "PythonAutomationModule.h"
#include "UnrealEnginePython.h"
#include "UEPyFAutomationEditorCommonUtils.h"

IMPLEMENT_MODULE(FPythonAutomationModule, PythonAutomation);


void FPythonAutomationModule::StartupModule()
{
FScopePythonGIL gil;
PyObject *py_automation_module = ue_py_register_module("unreal_engine.automation");
ue_python_init_fautomation_editor_common_utils(py_automation_module);
}

void FPythonAutomationModule::ShutdownModule()
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

#include "UEPyFAutomationEditorCommonUtils.h"

#include "UnrealEnginePython.h"
#include "Engine/World.h"


static PyObject *py_ue_fautomation_editor_common_utils_run_pie(PyObject *cls, PyObject * args)
{
Py_BEGIN_ALLOW_THREADS;
FAutomationEditorCommonUtils::RunPIE();
Py_END_ALLOW_THREADS;
Py_RETURN_NONE;
}

static PyObject *py_ue_fautomation_editor_common_utils_load_map(PyObject *cls, PyObject * args)
{
char *map_name;
if (!PyArg_ParseTuple(args, "s:load_map", &map_name))
return nullptr;
Py_BEGIN_ALLOW_THREADS;
FAutomationEditorCommonUtils::LoadMap(FString(UTF8_TO_TCHAR(map_name)));
Py_END_ALLOW_THREADS;
Py_RETURN_NONE;
}

static PyObject *py_ue_fautomation_editor_common_utils_create_new_map(PyObject *cls, PyObject * args)
{
Py_RETURN_UOBJECT(FAutomationEditorCommonUtils::CreateNewMap());
}

static PyMethodDef ue_PyFAutomationEditorCommonUtils_methods[] = {
{ "run_pie", (PyCFunction)py_ue_fautomation_editor_common_utils_run_pie, METH_VARARGS | METH_CLASS, "" },
{ "load_map", (PyCFunction)py_ue_fautomation_editor_common_utils_load_map, METH_VARARGS | METH_CLASS, "" },
{ "create_new_map", (PyCFunction)py_ue_fautomation_editor_common_utils_create_new_map, METH_VARARGS | METH_CLASS, "" },

{ NULL } /* Sentinel */
};


static PyTypeObject ue_PyFAutomationEditorCommonUtilsType = {
PyVarObject_HEAD_INIT(NULL, 0)
"unreal_engine.FAutomationEditorCommonUtils", /* tp_name */
sizeof(ue_PyFAutomationEditorCommonUtils), /* 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 AutomationEditorCommonUtils", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
ue_PyFAutomationEditorCommonUtils_methods, /* tp_methods */
0,
0,
};

static int py_ue_fautomation_editor_common_utils_init(ue_PyFAutomationEditorCommonUtils *self, PyObject * args)
{
PyErr_SetString(PyExc_Exception, "FAutomationEditorCommonUtils is a singleton");
return -1;
}

void ue_python_init_fautomation_editor_common_utils(PyObject *ue_module)
{
ue_PyFAutomationEditorCommonUtilsType.tp_new = PyType_GenericNew;
ue_PyFAutomationEditorCommonUtilsType.tp_init = (initproc)py_ue_fautomation_editor_common_utils_init;

if (PyType_Ready(&ue_PyFAutomationEditorCommonUtilsType) < 0)
return;

Py_INCREF(&ue_PyFAutomationEditorCommonUtilsType);
PyModule_AddObject(ue_module, "FAutomationEditorCommonUtils", (PyObject *)&ue_PyFAutomationEditorCommonUtilsType);
}
13 changes: 13 additions & 0 deletions Source/PythonAutomation/Private/UEPyFAutomationEditorCommonUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "UnrealEnginePython.h"

#include "Tests/AutomationEditorCommon.h"

typedef struct
{
PyObject_HEAD
/* Type-specific fields go here. */
} ue_PyFAutomationEditorCommonUtils;

void ue_python_init_fautomation_editor_common_utils(PyObject *);
14 changes: 14 additions & 0 deletions Source/PythonAutomation/Public/PythonAutomationModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 1998-2018 20Tab S.r.l. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "ModuleInterface.h"

class FPythonAutomationModule : public IModuleInterface
{
public:
virtual void StartupModule();
virtual void ShutdownModule();

};
36 changes: 36 additions & 0 deletions Source/PythonAutomation/PythonAutomation.Build.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 1998-2018 20Tab S.r.l All Rights Reserved.

using UnrealBuildTool;
using System.IO;

public class PythonAutomation : ModuleRules
{
#if WITH_FORWARDED_MODULE_RULES_CTOR
public PythonAutomation(ReadOnlyTargetRules Target) : base(Target)
#else
public PythonAutomation(TargetInfo Target)
#endif
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
string enableUnityBuild = System.Environment.GetEnvironmentVariable("UEP_ENABLE_UNITY_BUILD");
bFasterWithoutUnity = string.IsNullOrEmpty(enableUnityBuild);

PrivateIncludePaths.AddRange(
new string[] {
"PythonConsole/Private",
// ... add other private include paths required here ...
}
);

PrivateDependencyModuleNames.AddRange(
new string[] {
"Core",
"CoreUObject", // @todo Mac: for some reason it's needed to link in debug on Mac
"Engine",
"UnrealEd",
"UnrealEnginePython"
}
);

}
}
Loading

0 comments on commit 3b04a35

Please sign in to comment.