diff --git a/Source/UnrealEnginePython/Private/CollectionManager/UEPyICollectionManager.cpp b/Source/UnrealEnginePython/Private/CollectionManager/UEPyICollectionManager.cpp index 4ef3eef9d..d75ce9153 100644 --- a/Source/UnrealEnginePython/Private/CollectionManager/UEPyICollectionManager.cpp +++ b/Source/UnrealEnginePython/Private/CollectionManager/UEPyICollectionManager.cpp @@ -59,8 +59,8 @@ static PyObject *py_ue_icollection_manager_get_child_collection_names(PyObject * ICollectionManager &CollectionManager = FCollectionManagerModule::GetModule().Get(); TArray names; CollectionManager.GetChildCollectionNames(FName(UTF8_TO_TCHAR(name)), (ECollectionShareType::Type)type, (ECollectionShareType::Type)child_type, names); - for (FName name : names) { - PyList_Append(py_list, PyUnicode_FromString(TCHAR_TO_UTF8(*name.ToString()))); + for (FName cname : names) { + PyList_Append(py_list, PyUnicode_FromString(TCHAR_TO_UTF8(*cname.ToString()))); } return py_list; } @@ -463,7 +463,7 @@ static PyMethodDef ue_PyICollectionManager_methods[] = { { "rename_collection", (PyCFunction)py_ue_icollection_manager_rename_collection, METH_VARARGS | METH_CLASS, "" }, { "add_to_collection", (PyCFunction)py_ue_icollection_manager_add_to_collection, METH_VARARGS | METH_CLASS, "" }, { "collection_exists", (PyCFunction)py_ue_icollection_manager_collection_exists, METH_VARARGS | METH_CLASS, "" }, - { "create_unique_connection_name", (PyCFunction)py_ue_icollection_manager_create_unique_collection_name, METH_VARARGS | METH_CLASS, "" }, + { "create_unique_collection_name", (PyCFunction)py_ue_icollection_manager_create_unique_collection_name, METH_VARARGS | METH_CLASS, "" }, { "destroy_collection", (PyCFunction)py_ue_icollection_manager_destroy_collection, METH_VARARGS | METH_CLASS, "" }, { "empty_collection", (PyCFunction)py_ue_icollection_manager_empty_collection, METH_VARARGS | METH_CLASS, "" }, { "get_dynamic_query_text", (PyCFunction)py_ue_icollection_manager_get_dynamic_query_text, METH_VARARGS | METH_CLASS, "" }, diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h index 7d2ceaf57..13f0942de 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySPythonWidget.h @@ -7,6 +7,7 @@ #include "UEPyFPaintContext.h" #include "UEPyFCharacterEvent.h" #include "UEPyFKeyEvent.h" +#include "UEPyFPointerEvent.h" extern PyTypeObject ue_PySPythonWidgetType; @@ -88,6 +89,127 @@ class SPythonWidget : public SCompoundWidget return FReply::Handled(); } + virtual FReply OnMouseMove(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_move")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_move = PyObject_GetAttrString(self, (char *)"on_mouse_move"); + if (!PyCallable_Check(py_callable_on_mouse_move)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_move is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_move, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + 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(); + } + + virtual FReply OnMouseWheel(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_wheel")) + return FReply::Unhandled(); + + PyObject *py_callable_on_mouse_wheel = PyObject_GetAttrString(self, (char *)"on_mouse_wheel"); + if (!PyCallable_Check(py_callable_on_mouse_wheel)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_wheel is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_wheel, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + 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(); + } + + virtual FReply OnMouseButtonDown(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_button_down")) + 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)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_down is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_down, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + 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(); + } + + virtual FReply OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MyEvent) override + { + FScopePythonGIL gil; + + if (!PyObject_HasAttrString(self, (char *)"on_mouse_button_up")) + 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)) + { + UE_LOG(LogPython, Error, TEXT("on_mouse_button_up is not a callable")); + return FReply::Unhandled(); + } + + PyObject *ret = PyObject_CallFunction(py_callable_on_mouse_button_up, (char *)"OO", py_ue_new_fgeometry(MyGeometry), py_ue_new_fpointer_event(MyEvent)); + 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(); + } + + virtual int32 OnPaint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyClippingRect, diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index e739f2b4e..fa96aecb2 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -2270,5 +2270,57 @@ PyObject *py_unreal_engine_all_viewport_clients(PyObject * self, PyObject * args } return py_list; } + +PyObject *py_unreal_engine_editor_sync_browser_to_assets(PyObject * self, PyObject * args) +{ + PyObject *py_items; + PyObject *py_focus = nullptr; + + if (!PyArg_ParseTuple(args, "O|O:sync_browser_to_assets", &py_items, &py_focus)) + return nullptr; + + PyObject *py_iter = PyObject_GetIter(py_items); + if (!py_iter) + { + return PyErr_Format(PyExc_Exception, "argument is not an iterable of UObject or FAssetData"); + } + + FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + + TArray asset_data; + TArray uobjects; + + while (PyObject *py_item = PyIter_Next(py_iter)) + { + ue_PyFAssetData *py_data = py_ue_is_fassetdata(py_item); + if (py_data) + { + asset_data.Add(py_data->asset_data); + } + else + { + UObject *u_object = ue_py_check_type(py_item); + if (!u_object) + { + return PyErr_Format(PyExc_Exception, "invalid item in iterable, must be UObject or FAssetData"); + } + uobjects.Add(u_object); + } + } + + if (asset_data.Num() > 0) + { + ContentBrowserModule.Get().SyncBrowserToAssets(asset_data, false, py_focus && PyObject_IsTrue(py_focus)); + } + + if (uobjects.Num() > 0) + { + ContentBrowserModule.Get().SyncBrowserToAssets(uobjects, false, py_focus && PyObject_IsTrue(py_focus)); + } + + Py_DECREF(py_iter); + + Py_RETURN_NONE; +} #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index 0498fa498..fa76df038 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -106,6 +106,8 @@ PyObject *py_unreal_engine_transactions(PyObject *, PyObject *); PyObject *py_unreal_engine_all_viewport_clients(PyObject *, PyObject *); +PyObject *py_unreal_engine_editor_sync_browser_to_assets(PyObject *, PyObject *); + PyObject *py_unreal_engine_heightmap_expand(PyObject *, PyObject *); PyObject *py_unreal_engine_heightmap_import(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 6f0dc7d4b..50d2eb67c 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -1281,5 +1281,21 @@ PyObject *py_unreal_engine_copy_properties_for_unrelated_objects(PyObject * self new_object, params); + Py_RETURN_NONE; +} + +PyObject *py_unreal_engine_set_random_seed(PyObject * self, PyObject * args) +{ + int seed; + if (!PyArg_ParseTuple(args, "i:set_random_seed", &seed)) + { + return nullptr; + } + + // Thanks to Sven Mika (Ducandu GmbH) for spotting this + FMath::RandInit(seed); + FGenericPlatformMath::SRandInit(seed); + FGenericPlatformMath::RandInit(seed); + Py_RETURN_NONE; } \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.h b/Source/UnrealEnginePython/Private/UEPyEngine.h index b48266824..207386825 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.h +++ b/Source/UnrealEnginePython/Private/UEPyEngine.h @@ -13,6 +13,8 @@ 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_set_random_seed(PyObject *, PyObject *); + PyObject *py_unreal_engine_get_game_viewport_size(PyObject *, PyObject *); PyObject *py_unreal_engine_get_resolution(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 218d2070a..f37638c3c 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -154,6 +154,8 @@ static PyMethodDef unreal_engine_methods[] = { { "add_on_screen_debug_message", py_unreal_engine_add_on_screen_debug_message, METH_VARARGS, "" }, { "print_string", py_unreal_engine_print_string, METH_VARARGS, "" }, + { "set_random_seed", py_unreal_engine_set_random_seed, METH_VARARGS, "" }, + { "find_class", py_unreal_engine_find_class, METH_VARARGS, "" }, { "find_struct", py_unreal_engine_find_struct, METH_VARARGS, "" }, { "find_enum", py_unreal_engine_find_enum, METH_VARARGS, "" }, @@ -234,6 +236,8 @@ static PyMethodDef unreal_engine_methods[] = { { "get_selected_assets", py_unreal_engine_get_selected_assets, METH_VARARGS, "" }, { "get_assets_by_class", py_unreal_engine_get_assets_by_class, METH_VARARGS, "" }, + { "sync_browser_to_assets", py_unreal_engine_editor_sync_browser_to_assets, METH_VARARGS, "" }, + { "get_asset_referencers", py_unreal_engine_get_asset_referencers, METH_VARARGS, "" }, { "get_asset_dependencies", py_unreal_engine_get_asset_dependencies, METH_VARARGS, "" }, @@ -839,6 +843,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "texture_get_width", (PyCFunction)py_ue_texture_get_width, METH_VARARGS, "" }, { "texture_get_height", (PyCFunction)py_ue_texture_get_height, METH_VARARGS, "" }, { "render_target_get_data", (PyCFunction)py_ue_render_target_get_data, METH_VARARGS, "" }, + { "render_target_get_data_to_buffer", (PyCFunction)py_ue_render_target_get_data_to_buffer, METH_VARARGS, "" }, { "texture_update_resource", (PyCFunction)py_ue_texture_update_resource, METH_VARARGS, "" }, #if WITH_EDITOR diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp index 9f7f9a667..ec76ddd59 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp @@ -63,7 +63,7 @@ PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) int size_y = component_y * quads_per_component + 1; if (heightmap_buffer.len < (Py_ssize_t)(size_x * size_y * sizeof(uint16))) - return PyErr_Format(PyExc_Exception, "not enough heightmap data, expecting %d bytes", size_x * size_y * sizeof(uint16)); + return PyErr_Format(PyExc_Exception, "not enough heightmap data, expecting %lu bytes", size_x * size_y * sizeof(uint16)); uint16 *data = (uint16 *)heightmap_buffer.buf; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp index 01bd5ea9a..6ca241693 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.cpp @@ -3,7 +3,8 @@ #include "Runtime/Engine/Public/ImageUtils.h" #include "Runtime/Engine/Classes/Engine/Texture.h" -PyObject *py_ue_texture_update_resource(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_texture_update_resource(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -15,7 +16,8 @@ PyObject *py_ue_texture_update_resource(ue_PyUObject *self, PyObject * args) { Py_RETURN_NONE; } -PyObject *py_ue_texture_get_width(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_texture_get_width(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -26,7 +28,8 @@ PyObject *py_ue_texture_get_width(ue_PyUObject *self, PyObject * args) { return PyLong_FromLong(texture->GetSizeX()); } -PyObject *py_ue_texture_get_height(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_texture_get_height(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); @@ -37,13 +40,15 @@ PyObject *py_ue_texture_get_height(ue_PyUObject *self, PyObject * args) { return PyLong_FromLong(texture->GetSizeY()); } -PyObject *py_ue_texture_get_data(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_texture_get_data(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int mipmap = 0; - if (!PyArg_ParseTuple(args, "|i:texture_get_data", &mipmap)) { + if (!PyArg_ParseTuple(args, "|i:texture_get_data", &mipmap)) + { return NULL; } @@ -61,13 +66,15 @@ PyObject *py_ue_texture_get_data(ue_PyUObject *self, PyObject * args) { } #if WITH_EDITOR -PyObject *py_ue_texture_get_source_data(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_texture_get_source_data(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int mipmap = 0; - if (!PyArg_ParseTuple(args, "|i:texture_get_data", &mipmap)) { + if (!PyArg_ParseTuple(args, "|i:texture_get_data", &mipmap)) + { return NULL; } @@ -87,13 +94,15 @@ PyObject *py_ue_texture_get_source_data(ue_PyUObject *self, PyObject * args) { } #endif -PyObject *py_ue_render_target_get_data(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_render_target_get_data(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); int mipmap = 0; - if (!PyArg_ParseTuple(args, "|i:render_target_get_data", &mipmap)) { + if (!PyArg_ParseTuple(args, "|i:render_target_get_data", &mipmap)) + { return NULL; } @@ -102,26 +111,68 @@ PyObject *py_ue_render_target_get_data(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "object is not a TextureRenderTarget"); FTextureRenderTarget2DResource *resource = (FTextureRenderTarget2DResource *)tex->Resource; - if (!resource) { + if (!resource) + { return PyErr_Format(PyExc_Exception, "cannot get render target resource"); } TArray pixels; - if (!resource->ReadPixels(pixels)) { + if (!resource->ReadPixels(pixels)) + { return PyErr_Format(PyExc_Exception, "unable to read pixels"); } return PyByteArray_FromStringAndSize((const char *)pixels.GetData(), (Py_ssize_t)(tex->GetSurfaceWidth() * 4 * tex->GetSurfaceHeight())); } -PyObject *py_ue_texture_set_data(ue_PyUObject *self, PyObject * args) { +PyObject *py_ue_render_target_get_data_to_buffer(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + Py_buffer py_buf; + int mipmap = 0; + + if (!PyArg_ParseTuple(args, "z*|i:render_target_get_data_to_buffer", &py_buf, &mipmap)) + { + return NULL; + } + + UTextureRenderTarget2D *tex = ue_py_check_type(self); + if (!tex) + return PyErr_Format(PyExc_Exception, "object is not a TextureRenderTarget"); + + FTextureRenderTarget2DResource *resource = (FTextureRenderTarget2DResource *)tex->Resource; + if (!resource) + { + return PyErr_Format(PyExc_Exception, "cannot get render target resource"); + } + + Py_ssize_t data_len = (Py_ssize_t)(tex->GetSurfaceWidth() * 4 * tex->GetSurfaceHeight()); + if (py_buf.len < data_len) + { + return PyErr_Format(PyExc_Exception, "buffer is not big enough"); + } + + TArray pixels; + if (!resource->ReadPixels(pixels)) + { + return PyErr_Format(PyExc_Exception, "unable to read pixels"); + } + + FMemory::Memcpy(py_buf.buf, pixels.GetData(), data_len); + Py_RETURN_NONE; +} + +PyObject *py_ue_texture_set_data(ue_PyUObject *self, PyObject * args) +{ ue_py_check(self); Py_buffer py_buf; int mipmap = 0; - if (!PyArg_ParseTuple(args, "z*|i:texture_set_data", &py_buf, &mipmap)) { + if (!PyArg_ParseTuple(args, "z*|i:texture_set_data", &py_buf, &mipmap)) + { return NULL; } @@ -140,7 +191,8 @@ PyObject *py_ue_texture_set_data(ue_PyUObject *self, PyObject * args) { int32 len = tex->PlatformData->Mips[mipmap].BulkData.GetBulkDataSize(); int32 wanted_len = py_buf.len; // avoid making mess - if (wanted_len > len) { + if (wanted_len > len) + { UE_LOG(LogPython, Warning, TEXT("truncating buffer to %d bytes"), len); wanted_len = len; } @@ -158,22 +210,26 @@ PyObject *py_ue_texture_set_data(ue_PyUObject *self, PyObject * args) { return Py_None; } -PyObject *py_unreal_engine_compress_image_array(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_compress_image_array(PyObject * self, PyObject * args) +{ int width; int height; Py_buffer py_buf; - if (!PyArg_ParseTuple(args, "iiz*:compress_image_array", &width, &height, &py_buf)) { + if (!PyArg_ParseTuple(args, "iiz*:compress_image_array", &width, &height, &py_buf)) + { return NULL; } - if (py_buf.buf == nullptr || py_buf.len <= 0) { + if (py_buf.buf == nullptr || py_buf.len <= 0) + { PyBuffer_Release(&py_buf); return PyErr_Format(PyExc_Exception, "invalid image data"); } TArray colors; uint8 *buf = (uint8 *)py_buf.buf; - for (int32 i = 0; i < py_buf.len; i += 4) { + for (int32 i = 0; i < py_buf.len; i += 4) + { colors.Add(FColor(buf[i], buf[1 + 1], buf[i + 2], buf[i + 3])); } @@ -184,11 +240,13 @@ PyObject *py_unreal_engine_compress_image_array(PyObject * self, PyObject * args return PyBytes_FromStringAndSize((char *)output.GetData(), output.Num()); } -PyObject *py_unreal_engine_create_checkerboard_texture(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_create_checkerboard_texture(PyObject * self, PyObject * args) +{ PyObject *py_color_one; PyObject *py_color_two; int checker_size; - if (!PyArg_ParseTuple(args, "OOi:create_checkboard_texture", &py_color_one, &py_color_two, &checker_size)) { + if (!PyArg_ParseTuple(args, "OOi:create_checkboard_texture", &py_color_one, &py_color_two, &checker_size)) + { return NULL; } @@ -209,11 +267,13 @@ PyObject *py_unreal_engine_create_checkerboard_texture(PyObject * self, PyObject return (PyObject *)ret; } -PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * args) +{ int width; int height; int format = PF_B8G8R8A8; - if (!PyArg_ParseTuple(args, "ii|i:create_transient_texture", &width, &height, &format)) { + if (!PyArg_ParseTuple(args, "ii|i:create_transient_texture", &width, &height, &format)) + { return NULL; } @@ -231,12 +291,14 @@ PyObject *py_unreal_engine_create_transient_texture(PyObject * self, PyObject * return (PyObject *)ret; } -PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * self, PyObject * args) +{ int width; int height; int format = PF_B8G8R8A8; PyObject *py_linear = nullptr; - if (!PyArg_ParseTuple(args, "ii|iO:create_transient_texture_render_target2d", &width, &height, &format, &py_linear)) { + if (!PyArg_ParseTuple(args, "ii|iO:create_transient_texture_render_target2d", &width, &height, &format, &py_linear)) + { return NULL; } @@ -254,24 +316,29 @@ PyObject *py_unreal_engine_create_transient_texture_render_target2d(PyObject * s } #if WITH_EDITOR -PyObject *py_unreal_engine_create_texture(PyObject * self, PyObject * args) { +PyObject *py_unreal_engine_create_texture(PyObject * self, PyObject * args) +{ PyObject *py_package; char *name; int width; int height; Py_buffer py_buf; - if (!PyArg_ParseTuple(args, "Osiiz*:create_texture", &py_package, &name, &width, &height, &py_buf)) { + if (!PyArg_ParseTuple(args, "Osiiz*:create_texture", &py_package, &name, &width, &height, &py_buf)) + { return nullptr; } UPackage *u_package = nullptr; - if (py_package == Py_None) { + if (py_package == Py_None) + { u_package = GetTransientPackage(); } - else { + else + { u_package = ue_py_check_type(py_package); - if (!u_package) { + if (!u_package) + { return PyErr_Format(PyExc_Exception, "argument is not a UPackage"); } } @@ -286,7 +353,7 @@ PyObject *py_unreal_engine_create_texture(PyObject * self, PyObject * args) { wanted_len = py_buf.len; FMemory::Memcpy(colors.GetData(), py_buf.buf, wanted_len); - + UTexture2D *texture = FImageUtils::CreateTexture2D(width, height, colors, u_package, UTF8_TO_TCHAR(name), RF_Public | RF_Standalone, params); if (!texture) return PyErr_Format(PyExc_Exception, "unable to create texture"); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.h b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.h index edd86a8df..6bdc09f89 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTexture.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTexture.h @@ -6,6 +6,7 @@ PyObject *py_ue_texture_get_data(ue_PyUObject *, PyObject *); PyObject *py_ue_render_target_get_data(ue_PyUObject *, PyObject *); +PyObject *py_ue_render_target_get_data_to_buffer(ue_PyUObject *, PyObject *); PyObject *py_ue_texture_set_data(ue_PyUObject *, PyObject *); PyObject *py_ue_texture_get_width(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp index e2d9b8687..bcc10d5d2 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp @@ -75,9 +75,10 @@ PyObject *py_ue_world_tick(ue_PyUObject *self, PyObject * args) ue_py_check(self); float delta_time; - if (!PyArg_ParseTuple(args, "f:world_tick", &delta_time)) + PyObject *py_increase_fc = nullptr; + if (!PyArg_ParseTuple(args, "f|O:world_tick", &delta_time, &py_increase_fc)) { - return NULL; + return nullptr; } UWorld *world = ue_get_uworld(self); @@ -86,6 +87,9 @@ PyObject *py_ue_world_tick(ue_PyUObject *self, PyObject * args) world->Tick(LEVELTICK_All, delta_time); + if (py_increase_fc && PyObject_IsTrue(py_increase_fc)) + GFrameCounter++; + Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp index 01e5a04bc..9749c5c67 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFAssetData.cpp @@ -2,58 +2,121 @@ #if WITH_EDITOR -static PyObject *py_ue_fassetdata_get_asset(ue_PyFAssetData *self, PyObject * args) { +#include "ObjectTools.h" + +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) { + if (!ret) + { return PyErr_Format(PyExc_Exception, "unable to get UObject from asset"); } Py_INCREF(ret); return ret; } -static PyObject *py_ue_fassetdata_is_asset_loaded(ue_PyFAssetData *self, PyObject * args) { +static PyObject *py_ue_fassetdata_is_asset_loaded(ue_PyFAssetData *self, PyObject * args) +{ if (self->asset_data.IsAssetLoaded()) Py_RETURN_TRUE; Py_RETURN_FALSE; } +static PyObject *py_ue_fassetdata_get_thumbnail(ue_PyFAssetData *self, PyObject * args) +{ + TArray names; + FName name = FName(*self->asset_data.GetFullName()); + names.Add(name); + FThumbnailMap map; + + if (!ThumbnailTools::ConditionallyLoadThumbnailsForObjects(names, map)) + { + return PyErr_Format(PyExc_Exception, "Unable to retrieve thumbnail from FAssetData"); + } + + FObjectThumbnail *thumbnail = map.Find(name); + if (!thumbnail) + { + return PyErr_Format(PyExc_Exception, "Unable to retrieve thumbnail from FAssetData"); + } + + return py_ue_new_fobject_thumbnail(*thumbnail); +} + +#if ENGINE_MINOR_VERSION > 17 +static PyObject *py_ue_fassetdata_has_custom_thumbnail(ue_PyFAssetData *self, PyObject * args) +{ + + if (!ThumbnailTools::AssetHasCustomThumbnail(self->asset_data)) + { + Py_RETURN_FALSE; + } + + Py_RETURN_TRUE; +} +#endif + +static PyObject *py_ue_fassetdata_has_cached_thumbnail(ue_PyFAssetData *self, PyObject * args) +{ + + if (!ThumbnailTools::FindCachedThumbnail(self->asset_data.GetFullName())) + { + Py_RETURN_FALSE; + } + + Py_RETURN_TRUE; +} + 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 + { "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, "" }, { NULL } /* Sentinel */ }; -static PyObject *py_ue_fassetdata_get_asset_class(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_asset_class(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.AssetClass.ToString())); } -static PyObject *py_ue_fassetdata_get_asset_name(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_asset_name(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.AssetName.ToString())); } #if ENGINE_MINOR_VERSION < 17 -static PyObject *py_ue_fassetdata_get_group_names(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_group_names(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.GroupNames.ToString())); } #endif -static PyObject *py_ue_fassetdata_get_object_path(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_object_path(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.ObjectPath.ToString())); } -static PyObject *py_ue_fassetdata_get_package_flags(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_package_flags(ue_PyFAssetData *self, void *closure) +{ return PyLong_FromUnsignedLong(self->asset_data.PackageFlags); } -static PyObject *py_ue_fassetdata_get_package_name(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_package_name(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.PackageName.ToString())); } -static PyObject *py_ue_fassetdata_get_package_path(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_package_path(ue_PyFAssetData *self, void *closure) +{ return PyUnicode_FromString(TCHAR_TO_UTF8(*self->asset_data.PackagePath.ToString())); } -static PyObject *py_ue_fassetdata_get_tags_and_values(ue_PyFAssetData *self, void *closure) { +static PyObject *py_ue_fassetdata_get_tags_and_values(ue_PyFAssetData *self, void *closure) +{ PyObject *ret = PyDict_New(); for (auto It = self->asset_data.TagsAndValues.CreateConstIterator(); It; ++It) { @@ -78,7 +141,8 @@ static PyGetSetDef ue_PyFAssetData_getseters[] = { { NULL } /* Sentinel */ }; -static int ue_py_fassetdata_init(ue_PyFAssetData *self, PyObject *args, PyObject *kwargs) { +static int ue_py_fassetdata_init(ue_PyFAssetData *self, PyObject *args, PyObject *kwargs) +{ // avoid FAssetData manual creation return -1; } @@ -122,7 +186,8 @@ static PyTypeObject ue_PyFAssetDataType = { ue_PyFAssetData_getseters, /* tp_getset */ }; -void ue_python_init_fassetdata(PyObject *ue_module) { +void ue_python_init_fassetdata(PyObject *ue_module) +{ ue_PyFAssetDataType.tp_new = PyType_GenericNew;; ue_PyFAssetDataType.tp_init = (initproc)ue_py_fassetdata_init; if (PyType_Ready(&ue_PyFAssetDataType) < 0) @@ -132,14 +197,16 @@ void ue_python_init_fassetdata(PyObject *ue_module) { PyModule_AddObject(ue_module, "FAssetData", (PyObject *)&ue_PyFAssetDataType); } -PyObject *py_ue_new_fassetdata(FAssetData asset_data) { +PyObject *py_ue_new_fassetdata(FAssetData asset_data) +{ ue_PyFAssetData *ret = (ue_PyFAssetData *)PyObject_New(ue_PyFAssetData, &ue_PyFAssetDataType); new(&ret->asset_data) FAssetData(asset_data); return (PyObject *)ret; } -ue_PyFAssetData *py_ue_is_fassetdata(PyObject *obj) { +ue_PyFAssetData *py_ue_is_fassetdata(PyObject *obj) +{ if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFAssetDataType)) return nullptr; return (ue_PyFAssetData *)obj; diff --git a/docs/Collections_API.md b/docs/Collections_API.md index edef3a3fa..22616d28d 100644 --- a/docs/Collections_API.md +++ b/docs/Collections_API.md @@ -75,4 +75,54 @@ create a new dynamic collections given a name, a type and a storage mode move the 'name' collection to the 'new_name' parent +### add_to_collection(name, ECollectionShareType, objectPath) +add an asset to a static collection using asset's path + +### collection_exists(name, ECollectionShareType) + +Returns true if a collection already exists + +### create_unique_collection_name(name, ECollectionShareType) + +returns a string with a unique collection name + +### destroy_collection(name, ECollectionShareType) + +deletes an existing collection and returns true on success and false on failure + +### empty_collection(name, ECollectionShareType) + +removes all the existing assets from a collection. Returns true on success and false on failure + +### get_dynamic_query_text(name, ECollectionShareType) + +returns the search text from a dynamic collection. If you use it on a static collection you will get an error + +### set_dynamic_query_text(name, ECollectionShareType, searchText) + +sets the search text to a dynamic collection. Returns true on success and false on failure + +### remove_from_collection(name, ECollectionShareType, objectPath) + +removes an asset from a static collection using asset's path. Returns true on success and false on failure + +### get_assets_in_collection(name, ECollectionShareType, recursion(optional)) + +returns a list with all the asset paths. There is a third optional parameter for recursion. Default value is 1. This function works only with static collections + +### get_objects_in_collection(name, ECollectionShareType, recursion(optional)) + +returns a list with all the object paths. There is a third optional parameter for recursion. Default value is 1. This function works only with static collections + +### get_classes_in_collection(name, ECollectionShareType, recursion(optional)) + +returns a list with all the Classes. There is a third optional parameter for recursion. Default value is 1. This function works only with static collections + +### get_parent_collection(name, ECollectionShareType) + +returns the parent collection. If the collection has no parent, it returns None. + +### has_collections() + +returns True or False if the project has or doesn't have any collections. diff --git a/examples/fasset_data_thumbnails.py b/examples/fasset_data_thumbnails.py new file mode 100644 index 000000000..214906e8b --- /dev/null +++ b/examples/fasset_data_thumbnails.py @@ -0,0 +1,15 @@ +import unreal_engine as ue +from unreal_engine import FARFilter + +_filter = FARFilter() +_filter.class_names = ['StaticMesh'] + +# when passing True to the second argument of get_assets_by_filter(), you will get FAssetData instead of UObject +for asset_data in ue.get_assets_by_filter(_filter, True): + has_custom_thumbnail = asset_data.has_custom_thumbnail() + has_cached_thumbnail = asset_data.has_cached_thumbnail() + try: + thumbnail = asset_data.get_thumbnail() + except: + thumbnail = None + ue.log('Asset: {0} Loaded: {1} CustomThumbnail: {2} CachedThumbnail: {3} Thumbnail: {4}'.format(asset_data.object_path, asset_data.is_asset_loaded(), has_custom_thumbnail, has_cached_thumbnail, thumbnail)) \ No newline at end of file