├── .flake8 ├── .gitignore ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── framework.py ├── hooks ├── core │ └── bootstrap.py └── tk-multi-publish2 │ └── tk-maya │ ├── basic │ ├── collector.py │ ├── publish_fbx.py │ └── publish_turntable.py │ ├── icons │ ├── fbx.png │ ├── geometry.png │ ├── maya.png │ ├── publish.png │ ├── unreal.png │ └── version_up.png │ └── unreal │ ├── unreal_importer.py │ └── unreal_setup_turntable.py ├── info.yml ├── resources ├── build_packages.sh ├── requirements.txt └── ueprojects │ ├── 4.20 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.21 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.22 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.23 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.24 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.25 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.26 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 4.27 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 5.0 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ └── DefaultGame.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 5.2 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ ├── DefaultGame.ini │ │ └── DefaultInput.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ ├── 5.3 │ └── turntable │ │ ├── Config │ │ ├── DefaultEditor.ini │ │ ├── DefaultEngine.ini │ │ ├── DefaultGame.ini │ │ └── DefaultInput.ini │ │ ├── Content │ │ └── turntable │ │ │ ├── level │ │ │ ├── turntable.umap │ │ │ ├── turntable_BuiltData.uasset │ │ │ └── unlit_grey.uasset │ │ │ └── sequence │ │ │ └── turntable_sequence.uasset │ │ └── turntable.uproject │ └── 5.4 │ └── turntable │ ├── Config │ ├── DefaultEditor.ini │ ├── DefaultEngine.ini │ ├── DefaultGame.ini │ └── DefaultInput.ini │ ├── Content │ └── turntable │ │ ├── level │ │ ├── turntable.umap │ │ ├── turntable_BuiltData.uasset │ │ └── unlit_grey.uasset │ │ └── sequence │ │ └── turntable_sequence.uasset │ └── turntable.uproject └── software_credits /.flake8: -------------------------------------------------------------------------------- 1 | # Copyright 2018 GPL Solutions, LLC. All rights reserved. 2 | # 3 | # Use of this software is subject to the terms of the GPL Solutions license 4 | # agreement provided at the time of installation or download, or which otherwise 5 | # accompanies this software in either electronic or hard copy form. 6 | # 7 | 8 | # Flake 8 PEP and lint configuration - https://gitlab.com/pycqa/flake8 9 | # 10 | # This defines the official lint and PEP8 rules for this repository 11 | # 12 | # You can run this locally by doing pep install flake8 and then 13 | # >flake8 . 14 | 15 | [flake8] 16 | 17 | # Things we don't want to lint 18 | exclude = 19 | .tox, 20 | .git, 21 | .flake8, 22 | .gitignore, 23 | .travis.yml, 24 | .cache, 25 | .eggs, 26 | *.rst, 27 | *.yml, 28 | *.pyc, 29 | *.pyo, 30 | *.egg-info, 31 | __pycache__, 32 | # Those are our third parties, do not lint them 33 | vendors, 34 | # Skip __init__.py files, to not have a lot of 'xxx' imported but unused 35 | python/__init__.py, 36 | python/*/__init__.py, 37 | # Skip the auto-generated ui file. 38 | python/*/ui, 39 | venv, 40 | winenv, 41 | osx_env, 42 | win_env, 43 | 44 | # Ignore some errors 45 | # 46 | # E402 module level import not at top of file 47 | # E501 line too long (112 > 79 characters) 48 | # N802 Variables should be lower case. (clashes with Qt naming conventions) 49 | # N806 Variables should be lower case. (clashes with Qt naming conventions) 50 | # W503 line break before binary operator (it breaks before, not after) 51 | 52 | ignore = E501, E402, N802, N806, W503 53 | 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | build/ 11 | develop-eggs/ 12 | dist/ 13 | eggs/ 14 | parts/ 15 | sdist/ 16 | *.egg-info/ 17 | .installed.cfg 18 | *.egg 19 | *.dist-info 20 | 21 | # Installer logs 22 | pip-log.txt 23 | pip-delete-this-directory.txt 24 | 25 | # Unit test / coverage reports 26 | htmlcov/ 27 | .tox/ 28 | .coverage 29 | .cache 30 | nosetests.xml 31 | coverage.xml 32 | test 33 | 34 | # Translations 35 | *.mo 36 | 37 | # Mr Developer 38 | .mr.developer.cfg 39 | .project 40 | .pydevproject 41 | 42 | # Editors 43 | .idea 44 | .vscode 45 | 46 | # Rope 47 | .ropeproject 48 | 49 | # Django stuff: 50 | *.log 51 | *.pot 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # Mac stuff 57 | .DS_Store 58 | 59 | # Package building 60 | packagevenv* 61 | python/vendors 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Your use of this Unreal® Engine 4 – Shotgun integration, as distributed in this GitHub repository, is governed by the BSD 3-clause license. 2 | 3 | Copyright 2018 Epic Games, Inc. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | ######### 16 | 17 | Your use of the Shotgun Pipeline Toolkit is governed by the applicable license agreement between you and Autodesk/Shotgun. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unreal QT Framework 2 | 3 | This is a helper library designed to be used in 4 | conjunction with the [tk-unreal](https://github.com/ue4plugins/tk-unreal) engine. 5 | 6 | This framework contains hooks which can be re-used in configurations where the 7 | Unreal integration is needed, and PySide binaries needed to run the SG Toolkit 8 | integration in Unreal. 9 | 10 | Please see the engine for more details. 11 | 12 | ## The PySide libraries 13 | 14 | Binaries for PySide libraries for all platforms are not included in the 15 | source tree and must be built before being able to use this framework. 16 | 17 | ### Building the PySide libraries locally 18 | 19 | Use the [build_packages.sh](resources/build_packages.sh) script with the `-b` option to build and install 20 | the packages specified in the [requirements.txt](resources/requirements.txt) file. 21 | ``` 22 | Usage : ./build_packages.sh [-h] [-b] [-p 47 | 48 | ## Using this framework in your Setup 49 | 50 | Add this framework in the `frameworks` settings section of the Toolkit application needing 51 | it. 52 | For example: 53 | 54 | ``` 55 | frameworks: 56 | - {"name": "tk-framework-unrealqt", "version": "v1.x.x"} 57 | ``` 58 | 59 | - If you're using a local descriptor (dev or path) you need to build the PySide libraries 60 | yourself. See [building the PySide libraries locally](#building-the-pyside2-libraries-locally). 61 | 62 | - If you're using a remote descriptor, just in time download must be added so these binaries are downloaded in SG TK bootstrap process. The provided [bootstrap.py](hooks/core/bootstrap.py) script implements just in time downloads from Github releases with a `git` or a `github_release` descriptor: 63 | - Copy the `hooks/core/bootstrap.py` file to your config `core/hooks/bootstrap.py` 64 | hook. 65 | - Use a `git` descriptor for the framework, e.g.: 66 | ``` 67 | tk-framework-unrealqt_v1.x.x: 68 | location: 69 | version: v1.3.0 70 | type: git 71 | path: git@github.com:ue4plugins/tk-framework-unrealqt.git 72 | ``` 73 | - Or use a `github_release` descriptor for the framework, e.g.: 74 | ``` 75 | tk-framework-unrealqt_v1.x.x: 76 | location: 77 | version: v1.3.0 78 | type: github_release 79 | organization: ue4plugins 80 | repository: tk-framework-unrealqt 81 | ``` 82 | 83 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # We want builds to trigger for 3 reasons: 2 | # - The master branch sees new commits 3 | # - Each PR should get rebuilt when commits are added to it. 4 | # - When we tag something 5 | trigger: 6 | branches: 7 | include: 8 | - master 9 | tags: 10 | include: 11 | # Tags must start with "v" 12 | - v* 13 | pr: 14 | branches: 15 | include: 16 | - "*" 17 | 18 | # The github_ci_token variable is defined in the Azure Pipeline web page as a 19 | # secret variable and is a github personal token. 20 | 21 | jobs: 22 | 23 | - job: 'Build' 24 | strategy: 25 | matrix: 26 | # Define all the platform/python version we need 27 | MacPython39: 28 | platform: 'osx' # Short name for us 29 | imageName: 'macOS-latest' 30 | python.version: '3.9' 31 | WinPython39: 32 | platform: 'win' # Short name for us 33 | imageName: 'windows-latest' 34 | python.version: '3.9' 35 | LinuxPython39: 36 | platform: 'linux' # Short name for us 37 | imageName: 'ubuntu-latest' 38 | python.version: '3.9' 39 | MacPython311: 40 | platform: 'osx' # Short name for us 41 | imageName: 'macOS-latest' 42 | python.version: '3.11' 43 | WinPython311: 44 | platform: 'win' # Short name for us 45 | imageName: 'windows-latest' 46 | python.version: '3.11' 47 | LinuxPython311: 48 | platform: 'linux' # Short name for us 49 | imageName: 'ubuntu-latest' 50 | python.version: '3.11' 51 | maxParallel: 1 52 | 53 | pool: 54 | # Retrieve the value set by the strategy above 55 | vmImage: $(imageName) 56 | 57 | steps: 58 | - task: UsePythonVersion@0 59 | inputs: 60 | versionSpec: '$(python.version)' 61 | architecture: 'x64' 62 | 63 | - script: | 64 | python -m pip install --upgrade pip 65 | displayName: 'Install dependencies' 66 | 67 | # Not needed for Python 3, virtual env are built with python3 -m venv 68 | - script: | 69 | pip install virtualenv 70 | condition: eq( variables['python.version'], '2.7' ) 71 | displayName: 'Install virtualenv' 72 | 73 | # Couldn't get the script to be found using a bash task. 74 | - script: | 75 | ./build_packages.sh -b 76 | workingDirectory: "resources" 77 | displayName: 'Build' 78 | env: 79 | # Pass the variable into the environment 80 | GITHUB_CI_TOKEN: $(github_ci_token) 81 | condition: in( variables['Agent.OS'], 'Darwin', 'Linux' ) 82 | 83 | # Couldn't get the script to be found using a bash task. 84 | - script: | 85 | bash.exe ./build_packages.sh -b 86 | workingDirectory: resources 87 | displayName: 'Build Windows' 88 | env: 89 | # Pass the variable into the environment 90 | GITHUB_CI_TOKEN: $(github_ci_token) 91 | condition: eq( variables['Agent.OS'], 'Windows_NT' ) 92 | 93 | # Delete files 94 | # Delete folders, or files matching a pattern 95 | - task: DeleteFiles@1 96 | inputs: 97 | Contents: | 98 | .git 99 | resources/packagevenv_osx_3.11 100 | resources/packagevenv_windows_3.11 101 | resources/packagevenv_linux_3.11 102 | 103 | # Archive files 104 | # Compress files into .7z, .tar.gz, or .zip 105 | - task: ArchiveFiles@2 106 | inputs: 107 | rootFolderOrFile: '$(Build.SourcesDirectory)' 108 | includeRootFolder: false 109 | archiveType: 'zip' # Options: zip, 7z, tar, wim 110 | #tarCompression: 'gz' # Optional. Options: gz, bz2, xz, none 111 | # archive name will be something like v2.1.2-py2.7-osx.zip 112 | archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.SourceBranchName)-py$(python.version)-$(platform).zip' 113 | replaceExistingArchive: true 114 | verbose: true 115 | #quiet: # Optional 116 | 117 | - bash: | 118 | echo Source branch is ${BUILD_SOURCEBRANCHNAME} for ${PLATFORM} 119 | condition: startsWith(variables['build.sourceBranch'], 'refs/tags/') 120 | 121 | # Create, edit, or delete a GitHub release 122 | - task: GitHubRelease@0 123 | displayName: 'Create GitHub Release' 124 | inputs: 125 | # Note: the service connection needs to be created manually with curl 126 | # not from the Azure web UI 127 | # https://github.com/microsoft/azure-pipelines-tasks/issues/11558 128 | gitHubConnection: "Github release" 129 | repositoryName: '$(Build.Repository.Name)' 130 | action: 'edit' 131 | target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit 132 | tagSource: 'auto' # Required when action == Create# Options: auto, manual 133 | assets: '$(Build.ArtifactStagingDirectory)/$(Build.SourceBranchName)-py$(python.version)-$(platform).zip' 134 | assetUploadMode: 'replace' # Optional. Options: delete, replace 135 | tag: '$(Build.SourceBranchName)' 136 | #tagPattern: # Optional 137 | #tag: # Required when action == Edit || Action == Delete || TagSource == Manual 138 | #title: # Optional 139 | #releaseNotesSource: 'file' # Optional. Options: file, input 140 | #releaseNotesFile: # Optional 141 | #releaseNotes: # Optional 142 | #isDraft: false # Optional 143 | #isPreRelease: false # Optional 144 | #addChangeLog: true # Optional 145 | #compareWith: 'lastFullRelease' # Required when addChangeLog == True. Options: lastFullRelease, lastRelease, lastReleaseByTag 146 | #releaseTag: # Required when compareWith == LastReleaseByTag 147 | condition: startsWith(variables['build.sourceBranch'], 'refs/tags/') 148 | 149 | -------------------------------------------------------------------------------- /framework.py: -------------------------------------------------------------------------------- 1 | # This file is based on templates provided and copyrighted by Autodesk, Inc. 2 | # This file has been modified by Epic Games, Inc. and is subject to the license 3 | # file included in this repository. 4 | 5 | """ 6 | Framework containing PySide distributions for the Unreal engine 7 | 8 | Because Unreal does not include PySide/Qt distributions but does use its own 9 | version of Python, we have to distribute full versions for the engine to function. 10 | """ 11 | 12 | import os 13 | import platform 14 | import re 15 | import site 16 | import sys 17 | 18 | import sgtk 19 | 20 | 21 | class UnrealQtFramework(sgtk.platform.Framework): 22 | 23 | ########################################################################################## 24 | # init and destroy 25 | 26 | def init_framework(self): 27 | """ 28 | This framework ships with additional Python packages and tweak the Python 29 | paths environment to make these packages available to apps and engines. 30 | 31 | Something similar to what `virtualenv` does is done when this framework is 32 | loaded by SG TK. 33 | """ 34 | self.logger.debug("%s: Initializing UnrealQtFramework..." % self) 35 | 36 | # Check if PySide is already available, do nothing if it is the case 37 | try: 38 | from sgtk.platform.qt import QtCore # noqa 39 | self.logger.debug("Qt is already available, not activating any custom package.") 40 | return 41 | except ImportError as e: 42 | self.logger.debug("Qt is not available: %s, activating custom package." % e) 43 | pass 44 | # Remap the platform name to our names 45 | pname = self.platform_name() 46 | 47 | # Virtual env has different structures on Windows 48 | if pname == "windows": 49 | bin_folder = "Scripts" 50 | else: 51 | bin_folder = "bin" 52 | 53 | # Copied over from activate_this.py script which does not exist anymore 54 | # from Python 3. 55 | python_major = sys.version_info[0] # 2 or 3 56 | python_minor = sys.version_info[1] # 6, 7, 8, etc 57 | 58 | base_path = os.path.realpath( 59 | os.path.join( 60 | os.path.dirname(__file__), 61 | "python", 62 | "vendors", 63 | "py%d.%d" % (python_major, python_minor), 64 | pname, 65 | ) 66 | ) 67 | self.logger.debug("Activating custom packages with %s" % base_path) 68 | 69 | if pname == "windows": 70 | site_path = os.path.join(base_path, "Lib", "site-packages") 71 | else: 72 | lib_folders = os.listdir( 73 | os.path.join( 74 | base_path, 75 | "lib" 76 | ) 77 | ) 78 | python_pattern = r"^python%d\.\d+$" % python_major 79 | for folder in lib_folders: 80 | if re.match(python_pattern, folder): 81 | break 82 | else: 83 | raise RuntimeError( 84 | "Couldn't find python libraries for Python %s from %s" % ( 85 | python_major, 86 | lib_folders 87 | ) 88 | ) 89 | site_path = os.path.join( 90 | base_path, 91 | "lib", 92 | folder, 93 | "site-packages" 94 | ) 95 | 96 | os.environ["VIRTUAL_ENV"] = base_path 97 | # Split PATH, prepend our bin_folder and join back using the os path 98 | # separator (":" or ";") 99 | os.environ["PATH"] = os.pathsep.join( 100 | [os.path.join(base_path, bin_folder)] + os.environ.get("PATH", "").split(os.pathsep) 101 | ) 102 | 103 | # Add the libraries to the host python import mechanism 104 | prev_length = len(sys.path) 105 | site.addsitedir(site_path) 106 | sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length] 107 | 108 | sys.real_prefix = sys.prefix 109 | sys.prefix = base_path 110 | 111 | def destroy_framework(self): 112 | self.logger.debug("%s: Destroying UnrealQtFramework..." % self) 113 | 114 | @classmethod 115 | def platform_name(cls): 116 | """ 117 | Return a name for the current os that can be used 118 | when building os specific paths 119 | 120 | :returns: A name that can be used when building os specific paths. 121 | :raises ValueError: if the current os is not supported. 122 | """ 123 | platform_names = {"Darwin": "osx", "Linux": "linux", "Windows": "windows"} 124 | pname = platform_names.get(platform.system()) 125 | 126 | if not pname: 127 | raise ValueError("Platform %s is not supported" % platform.system()) 128 | return pname 129 | 130 | @classmethod 131 | def bin_folder(cls, vendor=None): 132 | """ 133 | Returns the bin folder for the current os 134 | and the given vendor where binaries can be found 135 | 136 | :param str vendor: An optional vendor name that will be included in the path. 137 | :returns: Full path to a folder. 138 | """ 139 | pname = cls.platform_name() 140 | if vendor: 141 | return os.path.join(os.path.dirname(__file__), "vendors", vendor, "bin", pname) 142 | return os.path.join(os.path.dirname(__file__), "vendors", "bin", pname) 143 | -------------------------------------------------------------------------------- /hooks/core/bootstrap.py: -------------------------------------------------------------------------------- 1 | # This file is provided by Epic Games, Inc. and is subject to the license 2 | # file included in this repository. 3 | 4 | """ 5 | This hook is used override some of the functionality of the :class:`~sgtk.bootstrap.ToolkitManager`. 6 | 7 | It will be instantiated only after a configuration has been selected by the :class:`~sgtk.bootstrap.ToolkitManager`. 8 | Therefore, this hook will not be invoked to download a configuration. However, the Toolkit Core, 9 | applications, frameworks and engines can be downloaded through the hook. 10 | """ 11 | 12 | import os 13 | import zipfile 14 | import json 15 | import platform 16 | import re 17 | 18 | from sgtk import get_hook_baseclass 19 | 20 | 21 | _SIX_IMPORT_WARNING = ( 22 | "Unable to import six.moves from tk-core, this can happen " 23 | "if an old version of tk-core < 0.19.1 is used in a site " 24 | "pipeline configuration. Falling back on using urllib2." 25 | ) 26 | 27 | 28 | class Bootstrap(get_hook_baseclass()): 29 | """ 30 | Override the bootstrap core hook to cache ourself some bundles. 31 | http://developer.shotgunsoftware.com/tk-core/core.html#bootstrap.Bootstrap 32 | """ 33 | # List of github repos for which we download releases, with a github token to 34 | # do the download if the repo is private 35 | _download_release_from_github = [ 36 | ("ue4plugins/tk-framework-unrealqt", ""), 37 | ("GPLgithub/tk-framework-unrealqt", ""), 38 | ] 39 | 40 | def can_cache_bundle(self, descriptor): 41 | """ 42 | Indicates if a bundle can be cached by the :meth:`populate_bundle_cache_entry` method. 43 | 44 | This method is invoked when the bootstrap manager wants to cache a bundle used by a configuration. 45 | 46 | .. note:: This method is not called if the bundle is already cached so it 47 | can't be used to update an existing cached bundle. 48 | 49 | :param descriptor: Descriptor of the bundle that needs to be cached. 50 | 51 | :returns: ``True`` if the bundle can be cached with this hook, ``False`` 52 | if not. 53 | :rtype: bool 54 | :raises RuntimeError: If six.moves is not available. 55 | """ 56 | descd = descriptor.get_dict() 57 | return bool(self._should_download_release(descd)) 58 | 59 | def populate_bundle_cache_entry(self, destination, descriptor, **kwargs): 60 | """ 61 | Populates an entry from the bundle cache. 62 | 63 | This method will be invoked for every bundle for which :meth:`can_cache_bundle` 64 | returned ``True``. The hook is responsible for writing the bundle inside 65 | the destination folder. If an exception is raised by this method, the files 66 | will be deleted from disk and the bundle cache will be left intact. 67 | 68 | It has to properly copy all the files or the cache for this bundle 69 | will be left in an inconsistent state. 70 | 71 | :param str destination: Folder where the bundle needs to be written. Note 72 | that this is not the final destination folder inside the bundle cache. 73 | 74 | :param descriptor: Descriptor of the bundle that needs to be cached. 75 | """ 76 | # This logic can be removed once we can assume tk-core is > v0.19.1 not 77 | # just in configs but also in the bundled Shotgun.app. 78 | try: 79 | from tank_vendor.six.moves.urllib import request as url2 80 | from tank_vendor.six.moves.urllib import error as error_url2 81 | except ImportError as e: 82 | self.logger.warning(_SIX_IMPORT_WARNING) 83 | self.logger.debug("%s" % e, exc_info=True) 84 | # Fallback on using urllib2 85 | import urllib2 as url2 86 | import urllib2 as error_url2 87 | 88 | descd = descriptor.get_dict() 89 | version = descriptor.version 90 | self.logger.info("Treating %s" % descd) 91 | specs = self._should_download_release(descd) 92 | if not specs: 93 | raise RuntimeError("Don't know how to download %s" % descd) 94 | name = specs[0] 95 | token = specs[1] 96 | try: 97 | if self.shotgun.config.proxy_handler: 98 | # Re-use proxy settings from the Shotgun connection 99 | opener = url2.build_opener( 100 | self.shotgun.config.proxy_handler, 101 | ) 102 | url2.install_opener(opener) 103 | 104 | # Retrieve the release from the tag 105 | url = "https://api.github.com/repos/%s/releases/tags/%s" % (name, version) 106 | request = url2.Request(url) 107 | # Add the authorization token if we have one (private repos) 108 | if token: 109 | request.add_header("Authorization", "token %s" % token) 110 | request.add_header("Accept", "application/vnd.github.v3+json") 111 | try: 112 | response = url2.urlopen(request) 113 | except error_url2.URLError as e: 114 | if hasattr(e, "code"): 115 | if e.code == 404: 116 | self.logger.error("Release %s does not exists" % version) 117 | elif e.code == 401: 118 | self.logger.error("Not authorised to access release %s." % version) 119 | raise 120 | response_d = json.loads(response.read()) 121 | # Look up for suitable assets for this platform. Assets names 122 | # follow this convention: 123 | # -py-.zip 124 | # We download and extract all assets for any Python version for 125 | # the current platform and version. We're assuming that the cached 126 | # config for a user will never be shared between machines with 127 | # different os. 128 | pname = { 129 | "Darwin": "osx", 130 | "Linux": "linux", 131 | "Windows": "win" 132 | }.get(platform.system()) 133 | 134 | if not pname: 135 | raise ValueError("Unsupported platform %s" % platform.system()) 136 | 137 | extracted = [] 138 | for asset in response_d["assets"]: 139 | name = asset["name"] 140 | m = re.match( 141 | r"%s-py\d.\d+-%s.zip" % (version, pname), 142 | name 143 | ) 144 | if m: 145 | # Download the asset payload 146 | self._download_zip_github_asset( 147 | asset, 148 | destination, 149 | token 150 | ) 151 | extracted.append(asset) 152 | 153 | if not extracted: 154 | raise RuntimeError( 155 | "Couldn't retrieve a suitable asset from %s" % [ 156 | a["name"] for a in response_d["assets"] 157 | ] 158 | ) 159 | self.logger.info( 160 | "Extracted files: %s from %s" % ( 161 | os.listdir(destination), 162 | ",".join([a["name"] for a in extracted]) 163 | ) 164 | ) 165 | except Exception as e: 166 | # Log the exception with the backtrace because TK obfuscates it. 167 | self.logger.exception(e) 168 | raise 169 | 170 | def _should_download_release(self, desc): 171 | """ 172 | Return a repo name and a token if the given descriptor should be downloaded 173 | from a github release. 174 | 175 | :param str desc: A Toolkit descriptor. 176 | :returns: A name, token tuple or ``None``. 177 | """ 178 | if desc["type"] == "github_release": 179 | # Let's be safe... 180 | if not desc.get("organization") or not desc.get("repository"): 181 | return None 182 | desc_path = "%s/%s" % (desc["organization"], desc["repository"]) 183 | for name, token in self._download_release_from_github: 184 | if name == desc_path: 185 | return name, token 186 | elif desc.get("path"): 187 | # Check the path for a git descriptor 188 | desc_path = desc["path"] 189 | for name, token in self._download_release_from_github: 190 | if "git@github.com:%s.git" % name == desc_path: 191 | return name, token 192 | return None 193 | 194 | def _download_zip_github_asset(self, asset, destination, token): 195 | """ 196 | Download the zipped github asset and extract it into the given destination 197 | folder. 198 | 199 | Assets can be retrieved with the releases github REST api endpoint. 200 | https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name 201 | 202 | :param str asset: A Github asset dictionary. 203 | :param str destination: Full path to a folder where to extract the downloaded 204 | zipped archive. The folder is created if it does not 205 | exist. 206 | :param str token: A Github OAuth or personal token. 207 | """ 208 | try: 209 | from tank_vendor.six.moves.urllib import request as url2 210 | except ImportError as e: 211 | self.logger.warning(_SIX_IMPORT_WARNING) 212 | self.logger.debug("%s" % e, exc_info=True) 213 | # Fallback on using urllib2 214 | import urllib2 as url2 215 | # If we have a token use a basic auth handler 216 | # just a http handler otherwise 217 | if token: 218 | passman = url2.HTTPPasswordMgrWithDefaultRealm() 219 | passman.add_password( 220 | None, 221 | asset["url"], 222 | token, 223 | token 224 | ) 225 | auth_handler = url2.HTTPBasicAuthHandler(passman) 226 | else: 227 | auth_handler = url2.HTTPHandler() 228 | 229 | if self.shotgun.config.proxy_handler: 230 | # Re-use proxy settings from the Shotgun connection 231 | opener = url2.build_opener( 232 | self.shotgun.config.proxy_handler, 233 | auth_handler 234 | ) 235 | else: 236 | opener = url2.build_opener(auth_handler) 237 | 238 | url2.install_opener(opener) 239 | request = url2.Request(asset["url"]) 240 | if token: 241 | # We will be redirected and the Auth shouldn't be in the header 242 | # for the redirection. 243 | request.add_unredirected_header("Authorization", "token %s" % token) 244 | request.add_header("Accept", "application/octet-stream") 245 | response = url2.urlopen(request) 246 | if not os.path.exists(destination): 247 | self.logger.info("Creating %s" % destination) 248 | os.makedirs(destination) 249 | tmp_file = os.path.join(destination, asset["name"]) 250 | with open(tmp_file, "wb") as f: 251 | f.write(response.read()) 252 | with zipfile.ZipFile(tmp_file, "r") as zip_ref: 253 | zip_ref.extractall(destination) 254 | -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/basic/collector.py: -------------------------------------------------------------------------------- 1 | # This file is based on templates provided and copyrighted by Autodesk, Inc. 2 | # This file has been modified by Epic Games, Inc. and is subject to the license 3 | # file included in this repository. 4 | 5 | import sgtk 6 | 7 | 8 | HookBaseClass = sgtk.get_hook_baseclass() 9 | 10 | 11 | class MayaSessionCollectorWithSecondaries(HookBaseClass): 12 | """ 13 | Collector that operates on the Maya session. 14 | 15 | Extend the base implementation to add some items under the `session` item. 16 | 17 | This hook relies on functionality found in the Maya collector hook in 18 | the tk-maya Engine and should inherit from it in the configuration. 19 | The setting for this plugin should look something like this: 20 | 21 | collector: "{self}/collector.py:{engine}/tk-multi-publish2/basic/collector.py:{config}/tk-multi-publish2/tk-maya/basic/collector.py" 22 | """ 23 | 24 | def collect_current_maya_session(self, settings, parent_item): 25 | """ 26 | Creates an item that represents the current maya session. 27 | 28 | Override base implementation to add a "secondaries" item under the session item. 29 | This item can be used to export and publish the current Maya scene 30 | to different file formats. 31 | 32 | :param parent_item: Parent Item instance 33 | :returns: Item of type maya.session 34 | """ 35 | session_item = super(MayaSessionCollectorWithSecondaries, self).collect_current_maya_session( 36 | settings, 37 | parent_item 38 | ) 39 | secondaries_item = session_item.create_item( 40 | "maya.session.secondaries", 41 | "Secondary actions", 42 | "Additional Items" 43 | ) 44 | # Copy the work template from the session item to the secondaries item 45 | # so base publish hooks which rely on it can be used. 46 | work_template = session_item.properties.get("work_template") 47 | if work_template: 48 | secondaries_item.properties["work_template"] = work_template 49 | 50 | return session_item 51 | -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/basic/publish_fbx.py: -------------------------------------------------------------------------------- 1 | # This file is based on templates provided and copyrighted by Autodesk, Inc. 2 | # This file has been modified by Epic Games, Inc. and is subject to the license 3 | # file included in this repository. 4 | 5 | import os 6 | import maya.cmds as cmds 7 | import maya.mel as mel 8 | import sgtk 9 | from sgtk.util.filesystem import ensure_folder_exists 10 | 11 | HookBaseClass = sgtk.get_hook_baseclass() 12 | 13 | 14 | class MayaFBXPublishPlugin(HookBaseClass): 15 | """ 16 | Plugin for publishing an open maya session as an exported FBX. 17 | 18 | This hook relies on functionality found in the base file publisher hook in 19 | the publish2 app and should inherit from it in the configuration. The hook 20 | setting for this plugin should look something like this: 21 | 22 | hook: "{self}/publish_file.py:{config}/tk-multi-publish2/tk-maya/basic/publish_fbx.py" 23 | 24 | .. note :: Most of this code was copied over from tk-maya publish_session hook 25 | since it is not possible to derive from it to publish something 26 | else than a Maya scene, and since it populates properties on the 27 | publish item rather than using local properties, so it conflicts 28 | with other plugins acting on the same item. 29 | """ 30 | 31 | # NOTE: The plugin icon and name are defined by the base file plugin. 32 | 33 | @property 34 | def description(self): 35 | """ 36 | Verbose, multi-line description of what the plugin does. This can 37 | contain simple html for formatting. 38 | """ 39 | 40 | return """ 41 |

This plugin exports the current Maya session as an FBX file and publishes it. 42 | The scene will be exported to the path defined by this plugin's configured 43 | "Publish Template" setting. The resulting FBX file can then be imported 44 | into Unreal Engine via the Loader.

45 | """ 46 | 47 | @property 48 | def icon(self): 49 | """ 50 | Return the path to this item's icon. 51 | 52 | :returns: Full path to an icon. 53 | """ 54 | return os.path.join( 55 | self.disk_location, 56 | os.path.pardir, 57 | "icons", 58 | "fbx.png" 59 | ) 60 | 61 | @property 62 | def settings(self): 63 | """ 64 | Dictionary defining the settings that this plugin expects to receive 65 | through the settings parameter in the accept, validate, publish and 66 | finalize methods. 67 | 68 | A dictionary on the following form:: 69 | 70 | { 71 | "Settings Name": { 72 | "type": "settings_type", 73 | "default": "default_value", 74 | "description": "One line description of the setting" 75 | } 76 | 77 | The type string should be one of the data types that toolkit accepts as 78 | part of its environment configuration. 79 | """ 80 | 81 | # Inherit the settings from the base publish plugin 82 | base_settings = super(MayaFBXPublishPlugin, self).settings or {} 83 | 84 | # Settings specific to this class 85 | maya_publish_settings = { 86 | "Publish Template": { 87 | "type": "template", 88 | "default": None, 89 | "description": "Template path for published work files. Should" 90 | "correspond to a template defined in " 91 | "templates.yml.", 92 | } 93 | } 94 | 95 | # Update the base settings 96 | base_settings.update(maya_publish_settings) 97 | return base_settings 98 | 99 | @property 100 | def item_filters(self): 101 | """ 102 | List of item types that this plugin is interested in. 103 | 104 | Only items matching entries in this list will be presented to the 105 | accept() method. Strings can contain glob patters such as *, for example 106 | ["maya.*", "file.maya"] 107 | 108 | By accepting a child item from the `maya.session` item we ensure that 109 | publishes will have a dependency to the main publish for the session. 110 | """ 111 | return ["maya.session.secondaries"] 112 | 113 | def accept(self, settings, item): 114 | """ 115 | Method called by the publisher to determine if an item is of any 116 | interest to this plugin. Only items matching the filters defined via the 117 | item_filters property will be presented to this method. 118 | 119 | A publish task will be generated for each item accepted here. Returns a 120 | dictionary with the following booleans: 121 | 122 | - accepted: Indicates if the plugin is interested in this value at 123 | all. Required. 124 | - enabled: If True, the plugin will be enabled in the UI, otherwise 125 | it will be disabled. Optional, True by default. 126 | - visible: If True, the plugin will be visible in the UI, otherwise 127 | it will be hidden. Optional, True by default. 128 | - checked: If True, the plugin will be checked in the UI, otherwise 129 | it will be unchecked. Optional, True by default. 130 | 131 | :param settings: Dictionary of Settings. The keys are strings, matching 132 | the keys returned in the settings property. The values are `Setting` 133 | instances. 134 | :param item: Item to process 135 | 136 | :returns: dictionary with boolean keys accepted, required and enabled 137 | """ 138 | 139 | accepted = True 140 | publisher = self.parent 141 | 142 | # Check the publish template if one defined 143 | # If a publish template is configured, disable context change. This 144 | # is a temporary measure until the publisher handles context switching 145 | # natively. 146 | template_name = settings["Publish Template"].value 147 | if template_name: 148 | item.context_change_allowed = False 149 | publish_template = publisher.get_template_by_name(template_name) 150 | if not publish_template: 151 | self.logger.debug( 152 | "The valid publish template could not be determined for the " 153 | "FBX publish. Not accepting the item." 154 | ) 155 | accepted = False 156 | 157 | # We've validated the publish template. add it to the item properties 158 | # for use in subsequent methods 159 | item.local_properties["publish_template"] = publish_template 160 | 161 | path = _session_path() 162 | if not path: 163 | # the session has not been saved before (no path determined). 164 | # provide a save button. the session will need to be saved before 165 | # validation will succeed. 166 | self.logger.warn( 167 | "The Maya session has not been saved.", extra=_get_save_as_action() 168 | ) 169 | 170 | if accepted: 171 | self.logger.info( 172 | "Maya '%s' plugin accepted the current Maya session." % (self.name,) 173 | ) 174 | return {"accepted": accepted, "checked": True} 175 | 176 | def validate(self, settings, item): 177 | """ 178 | Validates the given item to check that it is ok to publish. Returns a 179 | boolean to indicate validity. 180 | 181 | :param settings: Dictionary of Settings. The keys are strings, matching 182 | the keys returned in the settings property. The values are `Setting` 183 | instances. 184 | :param item: Item to process. 185 | :returns: ``True`` if item is valid, ``False`` otherwise. 186 | :raises ValueError: For problems which can't be solved in the current session. 187 | """ 188 | 189 | path = _session_path() 190 | 191 | # ---- ensure the session has been saved 192 | 193 | if not path: 194 | # the session still requires saving. provide a save button. 195 | # validation fails. 196 | error_msg = "The Maya session has not been saved." 197 | self.logger.error(error_msg, extra=_get_save_as_action()) 198 | return False 199 | 200 | # Ensure we have an updated project root 201 | project_root = cmds.workspace(q=True, rootDirectory=True) 202 | # log if no project root could be determined. 203 | if not project_root: 204 | self.logger.error( 205 | "Your session is not part of a Maya project.", 206 | extra={ 207 | "action_button": { 208 | "label": "Set Project", 209 | "tooltip": "Set the Maya project", 210 | "callback": lambda: mel.eval('setProject ""'), 211 | } 212 | }, 213 | ) 214 | return False 215 | item.properties["project_root"] = project_root 216 | 217 | # ---- check the session against any attached work template 218 | 219 | # get the path in a normalized state. no trailing separator, 220 | # separators are appropriate for current os, no double separators, 221 | # etc. 222 | path = sgtk.util.ShotgunPath.normalize(path) 223 | publish_name = os.path.splitext(os.path.basename(path))[0] 224 | # if the session item has a known work template, see if the path 225 | # matches. if not, warn the user and provide a way to save the file to 226 | # a different path 227 | work_template = item.properties.get("work_template") 228 | publish_template = item.local_properties.get("publish_template") 229 | if work_template and publish_template: 230 | # Ensure the fields work for the publish template 231 | # This raises an error if the path does not match the template. 232 | work_fields = work_template.get_fields(path) 233 | missing_keys = publish_template.missing_keys(work_fields) 234 | if missing_keys: 235 | error_msg = ( 236 | "Work file '%s' missing keys required for the " 237 | "publish template %s: %s" % (path, publish_template, missing_keys) 238 | ) 239 | self.logger.error(error_msg) 240 | return False 241 | # Create the publish path by applying the fields. store it in the item's 242 | # properties. This is the path we'll create and then publish in the base 243 | # publish plugin. Also set the publish_path to be explicit. 244 | # NOTE: local_properties is used here as directed in the publisher 245 | # docs when there may be more than one plugin operating on the 246 | # same item in order for each plugin to have it's own values that 247 | # aren't overwritten by the other. 248 | item.local_properties["publish_path"] = publish_template.apply_fields(work_fields) 249 | 250 | # use the work file's version number when publishing 251 | if "version" in work_fields: 252 | item.properties["publish_version"] = work_fields["version"] 253 | else: 254 | # Derive a publish path from the current scene path 255 | workspace = cmds.workspace(q=True, openWorkspace=True) 256 | if not workspace: 257 | self.logger.error( 258 | "A Maya workspace must be set when no SG TK templates are provided", 259 | extra={ 260 | "action_button": { 261 | "label": "Set Project", 262 | "tooltip": "Set the Maya project", 263 | # This is a Mel script not available as a Python command. 264 | "callback": lambda: mel.eval('setProject ""'), 265 | } 266 | }, 267 | ) 268 | return False 269 | fbx_dir = cmds.workspace(fileRuleEntry="FBX") or "data" 270 | # Append a "publishes" folder and get the full path 271 | fbx_dir = cmds.workspace(expandName=os.path.join(fbx_dir, "publishes")) 272 | 273 | # Build a name from the Maya scene 274 | base_name = "%s.fbx" % publish_name 275 | publish_path = os.path.join(fbx_dir, base_name) 276 | # NOTE: local_properties is used here as directed in the publisher 277 | # docs when there may be more than one plugin operating on the 278 | # same item in order for each plugin to have it's own values that 279 | # aren't overwritten by the other. 280 | # Set the publish_path to be explicit. 281 | item.local_properties["publish_path"] = publish_path 282 | 283 | # Set the session path on the item for use by the base plugin validation 284 | # step. 285 | # NOTE: this path could change prior to the publish phase. 286 | item.properties["path"] = path 287 | # NOTE: local_properties is used here as directed in the publisher 288 | # docs when there may be more than one plugin operating on the 289 | # same item in order for each plugin to have it's own values that 290 | # aren't overwritten by the other. 291 | item.local_properties["publish_type"] = "Maya FBX" 292 | item.local_properties["publish_name"] = publish_name 293 | 294 | return True 295 | 296 | def _copy_to_publish(self, settings, item): 297 | """ 298 | Override base implementation to do nothing 299 | since we're not copying a file but exporting 300 | directly to the publish location. 301 | """ 302 | pass 303 | 304 | def _copy_local_to_publish(self, settings, item): 305 | """ 306 | Override base implementation to do nothing 307 | since we're not copying a file but exporting 308 | directly to the publish location. 309 | """ 310 | pass 311 | 312 | def _copy_work_to_publish(self, settings, item): 313 | """ 314 | Override base implementation to do nothing 315 | since we're not copying a file but exporting 316 | directly to the publish location. 317 | """ 318 | pass 319 | 320 | def publish(self, settings, item): 321 | """ 322 | Executes the publish logic for the given item and settings. 323 | 324 | :param settings: Dictionary of Settings. The keys are strings, matching 325 | the keys returned in the settings property. The values are `Setting` 326 | instances. 327 | :param item: Item to process 328 | """ 329 | 330 | # Get the path in a normalized state. no trailing separator, separators 331 | # are appropriate for current os, no double separators, etc. 332 | path = sgtk.util.ShotgunPath.normalize(_session_path()) 333 | 334 | # Ensure the session is saved 335 | _save_session(path) 336 | 337 | # Update the item with the saved session path 338 | item.properties["path"] = path 339 | 340 | # Get the path to create and publish 341 | publish_path = self.get_publish_path(settings, item) 342 | 343 | # ensure the publish folder exists: 344 | publish_folder = os.path.dirname(publish_path) 345 | self.parent.ensure_folder_exists(publish_folder) 346 | 347 | # Export scene to FBX 348 | self.logger.info( 349 | "Exporting scene %s to FBX %s" % ( 350 | path, publish_path 351 | ) 352 | ) 353 | cmds.FBXResetExport() 354 | cmds.FBXExportSmoothingGroups("-v", True) 355 | # Mel script equivalent: mel.eval('FBXExport -f "fbx_output_path"') 356 | cmds.FBXExport("-f", publish_path) 357 | 358 | # Store the exported fbx path onto the parent item so other items can 359 | # retrieve it and use it without having to export themself an fbx. 360 | item.parent.properties["exported_fbx_path"] = publish_path 361 | 362 | # Let the base class register the publish 363 | super(MayaFBXPublishPlugin, self).publish(settings, item) 364 | # Save publish data locally on the item to be able to restore it later 365 | item.local_properties["sg_publish_data"] = item.properties.sg_publish_data 366 | # Store the published fbx data onto the parent item so other items can 367 | # retrieve it and use it without having to export themself an fbx. 368 | item.parent.properties["sg_fbx_publish_data"] = item.properties.sg_publish_data 369 | 370 | def finalize(self, settings, item): 371 | """ 372 | Set the global item sg_publish_data property from the local property and 373 | call base the implementation which needs it. 374 | """ 375 | item.properties.sg_publish_data = item.local_properties.sg_publish_data 376 | # do the base class finalization 377 | super(MayaFBXPublishPlugin, self).finalize(settings, item) 378 | 379 | 380 | def _session_path(): 381 | """ 382 | Return the path to the current session 383 | :return: A string or ``None``. 384 | """ 385 | path = cmds.file(query=True, sn=True) 386 | return path 387 | 388 | 389 | def _save_session(path): 390 | """ 391 | Save the current session to the supplied path. 392 | """ 393 | 394 | # Maya can choose the wrong file type so we should set it here 395 | # explicitly based on the extension 396 | maya_file_type = None 397 | if path.lower().endswith(".ma"): 398 | maya_file_type = "mayaAscii" 399 | elif path.lower().endswith(".mb"): 400 | maya_file_type = "mayaBinary" 401 | 402 | # Maya won't ensure that the folder is created when saving, so we must make sure it exists 403 | folder = os.path.dirname(path) 404 | ensure_folder_exists(folder) 405 | 406 | cmds.file(rename=path) 407 | 408 | # save the scene: 409 | if maya_file_type: 410 | cmds.file(save=True, force=True, type=maya_file_type) 411 | else: 412 | cmds.file(save=True, force=True) 413 | 414 | 415 | # TODO: method duplicated in all the maya hooks 416 | def _get_save_as_action(): 417 | """ 418 | Simple helper for returning a log action dict for saving the session 419 | """ 420 | 421 | engine = sgtk.platform.current_engine() 422 | 423 | # default save callback 424 | callback = cmds.SaveScene 425 | 426 | # if workfiles2 is configured, use that for file save 427 | if "tk-multi-workfiles2" in engine.apps: 428 | app = engine.apps["tk-multi-workfiles2"] 429 | if hasattr(app, "show_file_save_dlg"): 430 | callback = app.show_file_save_dlg 431 | 432 | return { 433 | "action_button": { 434 | "label": "Save As...", 435 | "tooltip": "Save the current session", 436 | "callback": callback, 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/basic/publish_turntable.py: -------------------------------------------------------------------------------- 1 | # This file is based on templates provided and copyrighted by Autodesk, Inc. 2 | # This file has been modified by Epic Games, Inc. and is subject to the license 3 | # file included in this repository. 4 | 5 | import sgtk 6 | from tank_vendor import six 7 | from sgtk.platform.qt import QtGui, QtCore 8 | 9 | import maya.cmds as cmds 10 | import maya.mel as mel 11 | 12 | import copy 13 | import datetime 14 | import logging 15 | import os 16 | import pprint 17 | import re 18 | import shutil 19 | import subprocess 20 | import sys 21 | import tempfile 22 | from urllib import parse 23 | 24 | HookBaseClass = sgtk.get_hook_baseclass() 25 | 26 | 27 | class BrowsablePathWidget(QtGui.QFrame): 28 | """ 29 | A :class:`QtGui.QFrame` with an input field, an open and a browse button. 30 | """ 31 | def __init__(self, with_open_button=False, *args, **kwargs): 32 | """ 33 | Instantiate a new :class:`BrowsablePathWidget`. 34 | 35 | :param bool with_open_button: Whether or not an open button should be 36 | shown. 37 | """ 38 | super(BrowsablePathWidget, self).__init__(*args, **kwargs) 39 | self.combo_box = QtGui.QComboBox() 40 | self.combo_box.setEditable(True) 41 | self.combo_box.setMaxVisibleItems(10) 42 | # Prevent the QComboBox to get too big if the path is long. 43 | engine = sgtk.platform.current_engine() 44 | # Note: it would be better to test for Qt4 or Qt5 so this 45 | # wouldn't have to be changed when moving to new major 46 | # releases of Qt, but has_qt4 and has_qt5 returns True 47 | # even when PySide6 is being used. 48 | if engine.has_qt6: 49 | self.combo_box.setSizeAdjustPolicy( 50 | QtGui.QComboBox.AdjustToMinimumContentsLengthWithIcon 51 | ) 52 | else: # Qt4 or Qt5 53 | self.combo_box.setSizeAdjustPolicy( 54 | QtGui.QComboBox.AdjustToMinimumContentsLength 55 | ) 56 | 57 | self.open_button = QtGui.QToolButton() 58 | icon = QtGui.QIcon() 59 | icon.addPixmap( 60 | QtGui.QPixmap(":/tk_multi_publish2/file.png"), 61 | QtGui.QIcon.Normal, 62 | QtGui.QIcon.Off 63 | ) 64 | self.open_button.setIcon(icon) 65 | self.open_button.clicked.connect(self._open_current_path) 66 | self.combo_box.editTextChanged.connect(self._enable_open_button) 67 | if not with_open_button: 68 | # Hide the button if not needed. 69 | self.open_button.hide() 70 | 71 | self.browse_button = QtGui.QToolButton() 72 | icon = QtGui.QIcon() 73 | icon.addPixmap( 74 | QtGui.QPixmap(":/tk_multi_publish2/browse_white.png"), 75 | QtGui.QIcon.Normal, 76 | QtGui.QIcon.Off 77 | ) 78 | self.browse_button.setIcon(icon) 79 | self.browse_button.clicked.connect(self._browse) 80 | 81 | layout = QtGui.QHBoxLayout() 82 | layout.setContentsMargins(0, 0, 0, 0) 83 | layout.addWidget(self.combo_box) 84 | layout.addWidget(self.open_button) 85 | layout.addWidget(self.browse_button) 86 | self.setLayout(layout) 87 | 88 | @property 89 | def sgtk(self): 90 | """ 91 | :returns: A Toolkit API instance retrieved from the current Engine, or 92 | ``None``. 93 | """ 94 | current_engine = sgtk.platform.current_engine() 95 | if not current_engine: 96 | return None 97 | return current_engine.sgtk 98 | 99 | def get_path(self): 100 | """ 101 | Return the current path value. 102 | 103 | :returns: An utf-8 encoded string. 104 | """ 105 | return self.combo_box.currentText() 106 | 107 | def set_path(self, path): 108 | """ 109 | Set the current path to the given value. 110 | 111 | :param str path: The path value to set. 112 | """ 113 | # TODO: this was copied over from another tool where users could enter 114 | # a path and similar paths were added from matching TK templates. If not 115 | # needeed, let's remove it. 116 | self.combo_box.model().clear() 117 | if path: 118 | templates = self.sgtk.templates_from_path(path) 119 | other_paths = [] 120 | for template in templates: 121 | fields = template.validate_and_get_fields(path, skip_keys=["version"]) 122 | if fields: 123 | other_paths.extend( 124 | self.sgtk.paths_from_template( 125 | template, 126 | fields, 127 | ) 128 | ) 129 | if other_paths: 130 | self.combo_box.addItems(sorted(other_paths, reverse=True)) 131 | # Set the value last to not lose it when setting the combo box items 132 | self.combo_box.lineEdit().setText(path) 133 | 134 | def _enable_open_button(self, path): 135 | """ 136 | Enable the open button if a path is set, disable it otherwise. 137 | """ 138 | self.open_button.setEnabled(bool(path)) 139 | 140 | def _open_current_path(self): 141 | """ 142 | Open the current file in an external application. 143 | """ 144 | current_path = self.get_path() 145 | 146 | # Would be awesome to use launch app, but launch_from_path does not work 147 | # in SotfwareEntity mode. 148 | # if engine and "tk-multi-launchapp" in engine.apps: 149 | # app = engine.apps["tk-multi-launchapp"] 150 | # app.launch_from_path(current_path) 151 | 152 | # By default on Mac a single running Maya is used to open new files from 153 | # desktop services, which could lead to our current scene being replaced 154 | # when dealing with Maya files. We use `open -n` to force new instances 155 | # of the application to be used. 156 | if sys.platform == "darwin": 157 | os.system("open -n %s" % current_path) 158 | else: 159 | QtGui.QDesktopServices.openUrl( 160 | QtCore.QUrl("file:///%s" % current_path, QtCore.QUrl.TolerantMode) 161 | ) 162 | 163 | def _browse(self, folders=False): 164 | """ 165 | Opens a file dialog to browse to a file or folders. 166 | 167 | The file dialog can be run in 'folders' mode, which can be useful to 168 | select sequences of images by selecting the folder they are in. 169 | 170 | :param bool folders: If ``True`` allow to select folders, allow to select 171 | a single file otherwise. 172 | """ 173 | current_path = self.get_path() 174 | 175 | # Options for either browse type 176 | options = [ 177 | QtGui.QFileDialog.DontResolveSymlinks, 178 | QtGui.QFileDialog.DontUseNativeDialog 179 | ] 180 | 181 | if folders: 182 | # browse folders specifics 183 | caption = "Browse folders to image sequences" 184 | file_mode = QtGui.QFileDialog.Directory 185 | options.append(QtGui.QFileDialog.ShowDirsOnly) 186 | else: 187 | # browse files specifics 188 | # TODO: allow Mac .app folders to be selected instead of having to 189 | # browse to the UE4Editor.app/Contents/MacOS/UE4Editor file. 190 | caption = "Browse files" 191 | file_mode = QtGui.QFileDialog.ExistingFile # Single file selection 192 | 193 | # Create the dialog 194 | file_dialog = QtGui.QFileDialog(parent=self, caption=caption) 195 | file_dialog.setLabelText(QtGui.QFileDialog.Accept, "Select") 196 | file_dialog.setLabelText(QtGui.QFileDialog.Reject, "Cancel") 197 | file_dialog.setFileMode(file_mode) 198 | 199 | if current_path: 200 | # TODO: refine this for folders mode. 201 | file_dialog.selectFile(current_path) 202 | 203 | for option in options: 204 | file_dialog.setOption(option) 205 | 206 | if not file_dialog.exec_(): 207 | return 208 | 209 | paths = file_dialog.selectedFiles() 210 | if paths: 211 | self.set_path(paths[0]) 212 | 213 | 214 | class UnrealSetupWidget(QtGui.QFrame): 215 | """ 216 | A :class:`QtGui.QFrame` handling Unreal setup. 217 | """ 218 | def __init__(self, hook, *args, **kwargs): 219 | """ 220 | Instantiate a new :class:`UnrealSetupWidget`. 221 | """ 222 | super(UnrealSetupWidget, self).__init__(*args, **kwargs) 223 | self._hook = hook 224 | self._unreal_project_path_template = None 225 | self.unreal_engine_label = QtGui.QLabel("Unreal Engine:") 226 | # A ComboBox for detected Unreal versions 227 | self.unreal_engine_versions_widget = QtGui.QComboBox() 228 | # Changing the Unreal version updates the executable path and 229 | # the project path 230 | self.unreal_engine_versions_widget.currentIndexChanged.connect( 231 | self._current_unreal_version_changed 232 | ) 233 | # Let the user pick the Unreal executable from the file system if not 234 | # automatically detected. 235 | self.unreal_engine_widget = BrowsablePathWidget() 236 | 237 | # Let the user pick a project path from the file system 238 | self.unreal_project_label = QtGui.QLabel("Unreal Project Path:") 239 | self.unreal_project_widget = BrowsablePathWidget() 240 | 241 | settings_layout = QtGui.QVBoxLayout() 242 | settings_layout.setContentsMargins(0, 0, 0, 0) 243 | settings_layout.addWidget(self.unreal_engine_label) 244 | settings_layout.addWidget(self.unreal_engine_versions_widget) 245 | settings_layout.addWidget(self.unreal_engine_widget) 246 | settings_layout.addWidget(self.unreal_project_label) 247 | settings_layout.addWidget(self.unreal_project_widget) 248 | self.setLayout(settings_layout) 249 | 250 | @property 251 | def sgtk(self): 252 | """ 253 | :returns: A Toolkit API instance retrieved from the current Engine, or 254 | ``None``. 255 | """ 256 | current_engine = sgtk.platform.current_engine() 257 | if not current_engine: 258 | return None 259 | return current_engine.sgtk 260 | 261 | def populate_unreal_versions(self, unreal_versions, current_version): 262 | """ 263 | Populate the Unreal Versions combo box with the given list of versions. 264 | 265 | Set the selection to the given current version if there is a matching 266 | version. 267 | 268 | :param unreal_versions: A list of :class:`SoftwareVersion` instances. 269 | :param current_version: An Unreal version number, as a string. 270 | """ 271 | # See if we can match with just a major.minor 272 | short_current_version = _short_version(current_version) 273 | for i, unreal_version in enumerate(unreal_versions): 274 | self.unreal_engine_versions_widget.addItem( 275 | unreal_version.display_name, 276 | userData=unreal_version, 277 | ) 278 | short_version = _short_version(unreal_version.version) 279 | if short_version == short_current_version: 280 | self.unreal_engine_versions_widget.setCurrentIndex( 281 | i, 282 | ) 283 | sel = self.unreal_engine_versions_widget.currentIndex() 284 | if sel != -1: 285 | self._current_unreal_version_changed(sel) 286 | 287 | def set_unreal_project_path_template(self, project_path_template): 288 | """ 289 | Set the Unreal project path template used to build a project path from 290 | the Unreal version and other values. 291 | 292 | :param str project_path_template: A template string. 293 | """ 294 | self._unreal_project_path_template = project_path_template 295 | project_path = self._hook.evaluate_unreal_project_path( 296 | project_path_template, 297 | self.unreal_version, 298 | ) or "" 299 | self.unreal_project_widget.set_path(project_path) 300 | 301 | def _current_unreal_version_changed(self, index): 302 | """ 303 | Called when the Unreal version is changed in the list of versions. 304 | 305 | :param int index: The index of the current selection. 306 | """ 307 | self.unreal_engine_widget.combo_box.lineEdit().setText( 308 | self.unreal_engine_versions_widget.itemData(index).path 309 | ) 310 | project_path = self._hook.evaluate_unreal_project_path( 311 | self._unreal_project_path_template, 312 | self.unreal_version, 313 | ) or "" 314 | self.unreal_project_widget.set_path(project_path) 315 | 316 | @property 317 | def unreal_version(self): 318 | """ 319 | Return the selected Unreal version string. 320 | 321 | :returns: An Unreal version number as a string, or `None`. 322 | """ 323 | sel = self.unreal_engine_versions_widget.currentIndex() 324 | if sel != -1: 325 | return self.unreal_engine_versions_widget.itemData(sel).version 326 | return None 327 | 328 | @property 329 | def unreal_path(self): 330 | """ 331 | Return the current Unreal executable path. 332 | 333 | :returns: A string. 334 | """ 335 | return self.unreal_engine_widget.get_path() 336 | 337 | @property 338 | def unreal_project_path(self): 339 | """ 340 | Return the current Unreal project path. 341 | 342 | :returns: A string. 343 | """ 344 | return self.unreal_project_widget.get_path() 345 | 346 | 347 | class MayaUnrealTurntablePublishPlugin(HookBaseClass): 348 | """ 349 | Plugin for publishing an open maya session as an exported FBX. 350 | 351 | This hook relies on functionality found in the base file publisher hook in 352 | the publish2 app and should inherit from it in the configuration. The hook 353 | setting for this plugin should look something like this:: 354 | 355 | hook: "{self}/publish_file.py:{engine}/tk-multi-publish2/basic/publish_session.py" 356 | 357 | """ 358 | 359 | # NOTE: The plugin icon and name are defined by the base file plugin. 360 | 361 | # List of settings to save and their save code 362 | _save_settings = ( 363 | ("Unreal Engine Version", "publish2.unreal_engine_version"), 364 | ("Unreal Engine Path", "publish2.unreal_engine_path"), 365 | ("Unreal Project Path", "publish2.turntable.unreal_project_path"), 366 | ("Turntable Map Path", "publish2.turntable.map_path"), 367 | ("Sequence Path", "publish2.turntable.sequence_path"), 368 | ("Turntable Assets Path", "publish2.turntable.assets_path"), 369 | ) 370 | 371 | @property 372 | def description(self): 373 | """ 374 | Verbose, multi-line description of what the plugin does. This can 375 | contain simple html for formatting. 376 | """ 377 | return """ 378 |

This plugin renders a turntable of the asset for the current session 379 | in Unreal Engine. The asset will be exported to FBX and imported into 380 | an Unreal Project for rendering turntables. A command line Unreal render 381 | will then be initiated and output to a templated location on disk. Then, 382 | the turntable render will be published to Shotgun and submitted for review 383 | as a Version.

384 | """ 385 | 386 | @property 387 | def icon(self): 388 | """ 389 | Return the path to this item's icon. 390 | 391 | :returns: Full path to an icon. 392 | """ 393 | return os.path.join( 394 | self.disk_location, 395 | os.path.pardir, 396 | "icons", 397 | "unreal.png" 398 | ) 399 | 400 | @property 401 | def settings(self): 402 | """ 403 | Dictionary defining the settings that this plugin expects to receive 404 | through the settings parameter in the accept, validate, publish and 405 | finalize methods. 406 | 407 | A dictionary on the following form:: 408 | 409 | { 410 | "Settings Name": { 411 | "type": "settings_type", 412 | "default": "default_value", 413 | "description": "One line description of the setting" 414 | } 415 | 416 | The type string should be one of the data types that toolkit accepts as 417 | part of its environment configuration. 418 | """ 419 | 420 | # inherit the settings from the base publish plugin 421 | base_settings = super(MayaUnrealTurntablePublishPlugin, self).settings or {} 422 | 423 | # settings specific to this class 424 | settings = { 425 | "Publish Template": { 426 | "type": "template", 427 | "default": None, 428 | "description": "Template path for published work files. Should" 429 | "correspond to a template defined in " 430 | "templates.yml.", 431 | }, 432 | "Work Template": { 433 | "type": "template", 434 | "default": None, 435 | "description": "Template path for exported FBX files. Should" 436 | "correspond to a template defined in " 437 | "templates.yml.", 438 | }, 439 | "Unreal Engine Version": { 440 | "type": "string", 441 | "default": "4.26", 442 | "description": "Version of the Unreal Engine executable to use." 443 | }, 444 | "Unreal Engine Path": { 445 | "type": "string", 446 | "default": None, 447 | "description": "Full path the Unreal Engine executable to use." 448 | }, 449 | # TODO: check if this should actually be a TK template. 450 | "Unreal Project Path Template": { 451 | "type": "string", 452 | "default": "{config}/tk-multi-publish2/tk-maya/unreal/resources/{unreal_engine_version}/turntable/turntable.uproject", 453 | "description": "Path template to the Unreal project to load." 454 | "{config}, {engine}, {unreal_engine_version} keys " 455 | "can be used and are replaced with runtime values." 456 | }, 457 | "Unreal Project Path": { 458 | "type": "string", 459 | "default": None, 460 | "description": "Path to the Unreal project to load." 461 | }, 462 | "Turntable Map Path": { 463 | "type": "string", 464 | "default": "/Game/turntable/level/turntable.umap", 465 | "description": "Unreal path to the turntable map to use to render the turntable." 466 | }, 467 | "Sequence Path": { 468 | "type": "string", 469 | "default": "/Game/turntable/sequence/turntable_sequence.turntable_sequence", 470 | "description": "Unreal path to the level sequence to use to render the turntable." 471 | }, 472 | "Turntable Assets Path": { 473 | "type": "string", 474 | "default": "/Game/maya_turntable_assets", 475 | "description": "Unreal output path where the turntable assets will be imported." 476 | }, 477 | } 478 | 479 | # Update the base settings with our settings 480 | base_settings.update(settings) 481 | return base_settings 482 | 483 | @property 484 | def item_filters(self): 485 | """ 486 | List of item types that this plugin is interested in. 487 | 488 | Only items matching entries in this list will be presented to the 489 | accept() method. Strings can contain glob patters such as *, for example 490 | ["maya.*", "file.maya"] 491 | """ 492 | return ["maya.session.secondaries"] 493 | 494 | def create_settings_widget(self, parent): 495 | """ 496 | Creates a Qt widget, for the supplied parent widget (a container widget 497 | on the right side of the publish UI). 498 | 499 | :param parent: The parent to use for the widget being created 500 | :return: A :class:`QtGui.QFrame` that displays editable widgets for 501 | modifying the plugin's settings. 502 | """ 503 | # defer Qt-related imports 504 | from sgtk.platform.qt import QtGui 505 | 506 | # Create a QFrame with all our widgets 507 | settings_frame = QtGui.QFrame(parent) 508 | # Create our widgets, we add them as properties on the QFrame so we can 509 | # retrieve them easily. Qt uses camelCase so our xxxx_xxxx names can't 510 | # clash with existing Qt properties. 511 | 512 | # Show this plugin description 513 | settings_frame.description_label = QtGui.QLabel(self.description) 514 | settings_frame.description_label.setWordWrap(True) 515 | settings_frame.description_label.setOpenExternalLinks(True) 516 | 517 | # Unreal setttings 518 | settings_frame.unreal_setup_widget = UnrealSetupWidget(self) 519 | settings_frame.unreal_turntable_map_label = QtGui.QLabel("Unreal Turntable Map Path:") 520 | settings_frame.unreal_turntable_map_widget = QtGui.QLineEdit("") 521 | 522 | settings_frame.unreal_sequence_label = QtGui.QLabel("Unreal Sequence Path:") 523 | settings_frame.unreal_sequence_widget = QtGui.QLineEdit("") 524 | 525 | settings_frame.unreal_turntable_asset_label = QtGui.QLabel("Unreal Turntable Assets Path:") 526 | settings_frame.unreal_turntable_asset_widget = QtGui.QLineEdit("") 527 | 528 | # Create the layout to use within the QFrame 529 | settings_layout = QtGui.QVBoxLayout() 530 | settings_layout.addWidget(settings_frame.description_label) 531 | settings_layout.addWidget(settings_frame.unreal_setup_widget) 532 | settings_layout.addWidget(settings_frame.unreal_turntable_map_label) 533 | settings_layout.addWidget(settings_frame.unreal_turntable_map_widget) 534 | settings_layout.addWidget(settings_frame.unreal_sequence_label) 535 | settings_layout.addWidget(settings_frame.unreal_sequence_widget) 536 | settings_layout.addWidget(settings_frame.unreal_turntable_asset_label) 537 | settings_layout.addWidget(settings_frame.unreal_turntable_asset_widget) 538 | 539 | settings_layout.addStretch() 540 | settings_frame.setLayout(settings_layout) 541 | return settings_frame 542 | 543 | def get_ui_settings(self, widget): 544 | """ 545 | Method called by the publisher to retrieve setting values from the UI. 546 | 547 | :returns: A dictionary with setting values. 548 | """ 549 | self.logger.info("Getting settings from UI") 550 | # Please note that we don't have to return all settings here, just the 551 | # settings which are editable in the UI. 552 | settings = { 553 | "Unreal Engine Version": widget.unreal_setup_widget.unreal_version, 554 | "Unreal Engine Path": widget.unreal_setup_widget.unreal_path, 555 | # Get the project path evaluated from the template or the value which 556 | # was manually set. 557 | "Unreal Project Path": widget.unreal_setup_widget.unreal_project_path, 558 | "Turntable Map Path": widget.unreal_turntable_map_widget.text(), 559 | "Sequence Path": widget.unreal_sequence_widget.text(), 560 | "Turntable Assets Path": widget.unreal_turntable_asset_widget.text(), 561 | # "HDR Path": widget.hdr_image_template_widget.get_path(), 562 | # "Start Frame": widget.start_frame_spin_box.value(), 563 | # "End Frame": widget.end_frame_spin_box.value(), 564 | } 565 | return settings 566 | 567 | def set_ui_settings(self, widget, settings): 568 | """ 569 | Method called by the publisher to populate the UI with the setting values. 570 | 571 | :param widget: A QFrame we created in `create_settings_widget`. 572 | :param settings: A list of dictionaries. 573 | :raises NotImplementedError: if editing multiple items. 574 | """ 575 | self.logger.info("Setting UI settings") 576 | if len(settings) > 1: 577 | # We do not allow editing multiple items 578 | raise NotImplementedError 579 | cur_settings = settings[0] 580 | 581 | # Set the paths first, they can be changed when matching UE versions below. 582 | widget.unreal_setup_widget.unreal_engine_widget.set_path( 583 | cur_settings["Unreal Engine Path"] 584 | ) 585 | widget.unreal_setup_widget.set_unreal_project_path_template( 586 | cur_settings["Unreal Project Path Template"] 587 | ) 588 | widget.unreal_setup_widget.unreal_project_widget.set_path( 589 | cur_settings["Unreal Project Path"] 590 | ) 591 | 592 | try: 593 | unreal_versions = self.get_unreal_versions() 594 | widget.unreal_setup_widget.populate_unreal_versions( 595 | unreal_versions, 596 | cur_settings["Unreal Engine Version"], 597 | ) 598 | except sgtk.platform.TankMissingEngineError: 599 | self.logger.warning( 600 | "Unable to retrieve existing Unreal versions from an installed TK Unreal Engine" 601 | ) 602 | 603 | widget.unreal_turntable_map_widget.setText(cur_settings["Turntable Map Path"]) 604 | widget.unreal_sequence_widget.setText(cur_settings["Sequence Path"]) 605 | widget.unreal_turntable_asset_widget.setText(cur_settings["Turntable Assets Path"]) 606 | 607 | def load_saved_ui_settings(self, settings): 608 | """ 609 | Load saved settings and update the given settings dictionary with them. 610 | 611 | :param settings: A dictionary where keys are settings names and 612 | values Settings instances. 613 | """ 614 | # Retrieve SG utils framework settings module and instantiate a manager 615 | fw = self.load_framework("tk-framework-shotgunutils_v5.x.x") 616 | module = fw.import_module("settings") 617 | settings_manager = module.UserSettings(self.parent) 618 | 619 | # Retrieve saved settings 620 | for name, saved_name in self._save_settings: 621 | settings[name].value = settings_manager.retrieve( 622 | saved_name, 623 | settings[name].value, 624 | settings_manager.SCOPE_PROJECT, 625 | ) 626 | self.logger.debug("Loaded settings %s" % settings[name]) 627 | 628 | def save_ui_settings(self, settings): 629 | """ 630 | Save UI settings. 631 | 632 | :param settings: A dictionary of Settings instances. 633 | """ 634 | # Retrieve SG utils framework settings module and instantiate a manager 635 | fw = self.load_framework("tk-framework-shotgunutils_v5.x.x") 636 | module = fw.import_module("settings") 637 | settings_manager = module.UserSettings(self.parent) 638 | 639 | # Save settings 640 | for name, saved_name in self._save_settings: 641 | value = settings[name].value 642 | settings_manager.store( 643 | saved_name, 644 | value, 645 | settings_manager.SCOPE_PROJECT 646 | ) 647 | 648 | def accept(self, settings, item): 649 | """ 650 | Method called by the publisher to determine if an item is of any 651 | interest to this plugin. Only items matching the filters defined via the 652 | item_filters property will be presented to this method. 653 | 654 | A publish task will be generated for each item accepted here. Returns a 655 | dictionary with the following booleans: 656 | 657 | - accepted: Indicates if the plugin is interested in this value at 658 | all. Required. 659 | - enabled: If True, the plugin will be enabled in the UI, otherwise 660 | it will be disabled. Optional, True by default. 661 | - visible: If True, the plugin will be visible in the UI, otherwise 662 | it will be hidden. Optional, True by default. 663 | - checked: If True, the plugin will be checked in the UI, otherwise 664 | it will be unchecked. Optional, True by default. 665 | 666 | :param settings: Dictionary of Settings. The keys are strings, matching 667 | the keys returned in the settings property. The values are `Setting` 668 | instances. 669 | :param item: Item to process 670 | 671 | :returns: dictionary with boolean keys accepted, required and enabled 672 | """ 673 | 674 | accepted = True 675 | publisher = self.parent 676 | # Check the publish template if one defined 677 | template_name = settings["Publish Template"].value 678 | if template_name: 679 | publish_template = publisher.get_template_by_name(template_name) 680 | if not publish_template: 681 | self.logger.debug( 682 | "The valid publish template could not be determined for the " 683 | "turntable. Not accepting the item." 684 | ) 685 | accepted = False 686 | 687 | # We've validated the publish template. add it to the item properties 688 | # for use in subsequent methods 689 | item.local_properties["publish_template"] = publish_template 690 | 691 | # Because a publish template is configured, disable context change. This 692 | # is a temporary measure until the publisher handles context switching 693 | # natively. 694 | item.context_change_allowed = False 695 | if accepted: 696 | self.logger.info("Accepting item %s" % item) 697 | self.load_saved_ui_settings(settings) 698 | 699 | return { 700 | "accepted": accepted, 701 | "checked": True 702 | } 703 | 704 | def validate(self, settings, item): 705 | """ 706 | Validates the given item to check that it is ok to publish. Returns a 707 | boolean to indicate validity. 708 | 709 | :param settings: Dictionary of Settings. The keys are strings, matching 710 | the keys returned in the settings property. The values are `Setting` 711 | instances. 712 | :param item: Item to process 713 | :returns: ``True`` if item is valid, ``False`` otherwise. 714 | :raises ValueError: For problems which can't be solved in the current session. 715 | """ 716 | 717 | path = _session_path() 718 | 719 | # ---- ensure the session has been saved 720 | 721 | if not path: 722 | # the session still requires saving. provide a save button. 723 | # validation fails. 724 | error_msg = "The Maya session has not been saved." 725 | self.logger.error( 726 | error_msg, 727 | extra=_get_save_as_action() 728 | ) 729 | return False 730 | 731 | # Get the normalized path 732 | path = sgtk.util.ShotgunPath.normalize(path) 733 | # Store it in properties 734 | item.properties["path"] = path 735 | 736 | # get the configured work file template 737 | work_template = item.properties.get("work_template") 738 | publish_template = item.local_properties.get("publish_template") 739 | if work_template and publish_template: 740 | # get the current scene path and extract fields from it using the work 741 | # template: 742 | work_fields = work_template.get_fields(path) 743 | 744 | # Add additional keys needed by the template 745 | if sys.platform == "win32": 746 | work_fields["ue_mov_ext"] = "avi" 747 | else: 748 | work_fields["ue_mov_ext"] = "mov" 749 | 750 | # Ensure the fields work for the publish template 751 | missing_keys = publish_template.missing_keys(work_fields) 752 | if missing_keys: 753 | error_msg = "Work file '%s' missing keys required for the " \ 754 | "publish template: %s" % (path, missing_keys) 755 | self.logger.error(error_msg) 756 | return False 757 | # Create the publish path by applying the fields. store it in the item's 758 | # properties. This is the path we'll create and then publish in the base 759 | # publish plugin. Also set the publish_path to be explicit. 760 | # NOTE: local_properties is used here as directed in the publisher 761 | # docs when there may be more than one plugin operating on the 762 | # same item in order for each plugin to have it's own values that 763 | # aren't overwritten by the other. 764 | item.local_properties["publish_path"] = publish_template.apply_fields(work_fields) 765 | 766 | # use the work file's version number when publishing 767 | if "version" in work_fields: 768 | item.properties["publish_version"] = work_fields["version"] 769 | else: 770 | # Derive a publish path from the current scene path 771 | workspace = cmds.workspace(q=True, openWorkspace=True) 772 | if not workspace: 773 | self.logger.error( 774 | "A Maya workspace must be set when no SG TK templates are provided", 775 | extra={ 776 | "action_button": { 777 | "label": "Set Project", 778 | "tooltip": "Set the Maya project", 779 | # This is a Mel script not available as a Python command. 780 | "callback": lambda: mel.eval('setProject ""'), 781 | } 782 | }, 783 | ) 784 | return False 785 | movie_dir = cmds.workspace(fileRuleEntry="movie") or "data" 786 | # Get the full path. We append a "publishes" folder, otherwise the 787 | # base implementation will keep offering movies which are already 788 | # published 789 | movie_dir = cmds.workspace(expandName=os.path.join(movie_dir, "publishes")) 790 | 791 | # Build a name from the Maya scene 792 | base_name, _ = os.path.splitext(os.path.basename(path)) 793 | if sys.platform == "win32": 794 | base_name = "%s.avi" % base_name 795 | else: 796 | base_name = "%s.mov" % base_name 797 | publish_path = os.path.join(movie_dir, base_name) 798 | # NOTE: local_properties is used here as directed in the publisher 799 | # docs when there may be more than one plugin operating on the 800 | # same item in order for each plugin to have it's own values that 801 | # aren't overwritten by the other. 802 | # Set the publish_path to be explicit. 803 | item.local_properties["publish_path"] = publish_path 804 | 805 | self.logger.info("Turntable will be published as %s" % item.local_properties["publish_path"]) 806 | item.local_properties["publish_type"] = "Unreal Turntable Render" 807 | 808 | # Validate the Unreal executable and project, stash it in properties 809 | self.get_unreal_exec_property(settings, item) 810 | 811 | # Check the Unreal project path and store it in properties. 812 | self.get_unreal_project_property(settings, item) 813 | 814 | # Validate the Unreal data settings, stash in properties 815 | turntable_map_path = settings["Turntable Map Path"].value 816 | if not turntable_map_path: 817 | self.logger.debug("No Unreal turntable map configured.") 818 | return False 819 | item.properties["turntable_map_path"] = turntable_map_path 820 | 821 | # Validate the Unreal level sequence path, stash in properties 822 | sequence_path = settings["Sequence Path"].value 823 | if not sequence_path: 824 | self.logger.debug("No Unreal turntable sequence configured.") 825 | return False 826 | item.properties["sequence_path"] = sequence_path 827 | 828 | # Validate the Unreal turntable assets path, stash in properties 829 | turntable_assets_path = settings["Turntable Assets Path"].value 830 | if not turntable_assets_path: 831 | self.logger.debug("No Unreal turntable assets path configured.") 832 | return False 833 | item.properties["turntable_assets_path"] = turntable_assets_path 834 | 835 | self.save_ui_settings(settings) 836 | return True 837 | 838 | def get_unreal_exec_property(self, settings, item): 839 | """ 840 | Retrieve the Unreal Engine executable and store it as a property on the 841 | item. 842 | 843 | This can be overridden in deriving hooks if a different logic is needed. 844 | The `unreal_exec_path` and `unreal_engine_version` properties must be set 845 | by this method. 846 | 847 | :param settings: Dictionary of Settings. The keys are strings, matching 848 | the keys returned in the settings property. The values 849 | are `Setting` instances. 850 | :param item: Item to process 851 | :raises RuntimeError: If the Unreal Engine path and its version can't be 852 | resolved to valid values. 853 | """ 854 | # Validate the Unreal executable and project, stash in properties 855 | unreal_exec_path = settings["Unreal Engine Path"].value 856 | unreal_engine_version = settings["Unreal Engine Version"].value 857 | # See if we can match with just a major.minor 858 | short_engine_version = _short_version(unreal_engine_version) 859 | if not unreal_exec_path: 860 | # The path was not explicitely set, either from settings or the UI, 861 | # compute one from detected Unreal versions and the default version 862 | # Collect Unreal versions 863 | unreal_versions = self.get_unreal_versions() 864 | if not unreal_versions: 865 | raise RuntimeError( 866 | "No Unreal version could be detected on this machine, please " 867 | "set explicitely a value in this item's UI." 868 | ) 869 | unreal_exec_path = None 870 | 871 | for unreal_version in unreal_versions: 872 | short_version = _short_version(unreal_version.version) 873 | if short_version == short_engine_version: 874 | self.logger.info( 875 | "Found matching Unreal version %s for %s" % (unreal_version, short_engine_version) 876 | ) 877 | unreal_exec_path = unreal_version.path 878 | break 879 | else: 880 | # Pick the first entry 881 | self.logger.info( 882 | "Couldn't find a matching Unreal version %s, using %s" % (unreal_engine_version, unreal_versions[0]) 883 | ) 884 | unreal_exec_path = unreal_versions[0].path 885 | unreal_engine_version = unreal_versions[0].version 886 | 887 | if not unreal_exec_path or not os.path.exists(unreal_exec_path): 888 | raise RuntimeError( 889 | "Unreal executable not found at %s" % unreal_exec_path 890 | ) 891 | 892 | item.properties["unreal_exec_path"] = unreal_exec_path 893 | item.properties["unreal_engine_version"] = unreal_engine_version 894 | 895 | def get_unreal_project_property(self, settings, item): 896 | """ 897 | Retrieve the Unreal project path and store it in the item `unreal_project_path` 898 | property. 899 | 900 | This can be overridden in deriving hooks if a different logic is needed. 901 | The `unreal_project_path` property must be set by this method. 902 | 903 | :param settings: Dictionary of Settings. The keys are strings, matching 904 | the keys returned in the settings property. The values 905 | are `Setting` instances. 906 | :param item: Item to process 907 | :raises RuntimeError: If the a valid project path can't be resolved. 908 | """ 909 | unreal_project_path = settings["Unreal Project Path"].value 910 | if not unreal_project_path: 911 | # The path was not explicitely set, either from settings or the UI, 912 | # compute one from detected Unreal versions and the default version 913 | unreal_project_path_template = settings["Unreal Project Path Template"].value 914 | unreal_project_path = self.evaluate_unreal_project_path( 915 | unreal_project_path_template, 916 | item.properties["unreal_engine_version"], 917 | ) 918 | if not unreal_project_path: 919 | raise RuntimeError( 920 | "Unable to build an Unreal project path from %s with Unreal version %s" % ( 921 | unreal_project_path_template, 922 | item.properties["unreal_engine_version"], 923 | ) 924 | ) 925 | if not os.path.isfile(unreal_project_path): 926 | raise RuntimeError( 927 | "Unreal project not found at %s" % unreal_project_path 928 | ) 929 | item.properties["unreal_project_path"] = unreal_project_path 930 | 931 | def _get_local_path(self, published_file): 932 | """ 933 | Returns the local path for the given published file. 934 | 935 | :returns: The local path to the published file. 936 | :raises ValueError: If no local path can be retrieved. 937 | """ 938 | if "local_path" in published_file["path"]: 939 | return published_file["path"]["local_path"] 940 | 941 | if "url" in published_file["path"]: 942 | url = published_file["path"]["url"] 943 | parsed = parse.urlparse(url) 944 | if parsed.scheme == "file": 945 | # Copied from 946 | # https://github.com/shotgunsoftware/tk-core/blob/2fc8287a19f8f002e23101836bafba0ec0de9dc9/python/tank/util/shotgun/publish_resolve.py#L311 947 | if parsed.netloc: 948 | # UNC path 949 | path = parse.unquote( 950 | "//%s%s" % (parsed.netloc, parsed.path) 951 | ) 952 | else: 953 | path = parse.unquote(parsed.path) 954 | # Deal with bad paths being set 955 | # file:///C:/Users/me/default/data/publishes/Trooper_Full_NoKnife_2.fbx 956 | # Leading to /C:/Users/me/default/data/publishes/Trooper_Full_NoKnife_2.fbx 957 | # as path. 958 | if re.match("^/[A-Za-z]:/", path): 959 | path = path[1:] 960 | return path 961 | 962 | raise ValueError("Unable to get a local path from %s" % published_file) 963 | 964 | def publish(self, settings, item): 965 | """ 966 | Executes the publish logic for the given item and settings. 967 | 968 | :param settings: Dictionary of Settings. The keys are strings, matching 969 | the keys returned in the settings property. The values are `Setting` 970 | instances. 971 | :param item: Item to process 972 | """ 973 | 974 | # Get the Unreal settings again 975 | unreal_exec_path = item.properties["unreal_exec_path"] 976 | unreal_project_path = item.properties["unreal_project_path"] 977 | turntable_map_path = item.properties["turntable_map_path"] 978 | sequence_path = item.properties["sequence_path"] 979 | turntable_assets_path = item.properties["turntable_assets_path"] 980 | publish_path = self.get_publish_path(settings, item) 981 | publish_path = os.path.normpath(publish_path) 982 | 983 | # This plugin publishes a turntable movie to FPTR 984 | # These are the steps needed to do that 985 | 986 | # ======================= 987 | # 1. Export the Maya scene to FBX 988 | # The FBX will be exported to a temp folder 989 | # Another folder can be specified as long as the name has no spaces 990 | # Spaces are not allowed in command line Unreal Python args 991 | 992 | # Set a base temp dir on Windows to avoid having 993 | # the user name in the temp path which can include 994 | # "." e.g. firstname.name and makes UE crashes 995 | base_temp_dir = None 996 | if sys.platform == "win32": 997 | if os.path.isdir(r"C:\Temp"): 998 | base_temp_dir = r"C:\Temp" 999 | elif os.path.isdir(r"C:\Windows\Temp"): 1000 | base_temp_dir = r"C:\Windows\Temp" 1001 | else: 1002 | self.logger.warning( 1003 | "Couldn't retrieve a temp base directory without " 1004 | "the user name in it, using standard TEMP base" 1005 | ) 1006 | temp_folder = tempfile.mkdtemp(suffix="temp_unreal_shotgun", dir=base_temp_dir) 1007 | # Store the temp folder path on the item for cleanup in finalize 1008 | item.local_properties["temp_folder"] = temp_folder 1009 | fbx_folder = temp_folder 1010 | 1011 | # Get the filename from the work file 1012 | work_path = item.properties["path"] 1013 | work_path = os.path.normpath(work_path) 1014 | work_name = os.path.splitext(os.path.basename(work_path))[0] 1015 | 1016 | # Replace non-word characters in filenames, Unreal doesn't like those 1017 | # Substitute '_' instead 1018 | exp = re.compile(r"\W", re.UNICODE) 1019 | work_name = exp.sub("_", work_name) 1020 | 1021 | # Use current time as string as a unique identifier 1022 | now = datetime.datetime.now() 1023 | timestamp = str(now.hour) + str(now.minute) + str(now.second) 1024 | 1025 | # Use the Fbx which was published by another item or save one now. 1026 | published_fbx = item.parent.properties.get("sg_fbx_publish_data") 1027 | if published_fbx: 1028 | fbx_published_path = self._get_local_path(published_fbx) 1029 | # Check the path: Unreal doesn't like non-word characters in filenames 1030 | self.logger.info("Using published FBX file %s" % fbx_published_path) 1031 | if re.search(exp, os.path.splitext(fbx_published_path)[0]): 1032 | fbx_name = work_name + "_" + timestamp + "_turntable.fbx" 1033 | fbx_output_path = os.path.join(fbx_folder, fbx_name) 1034 | # Make a copy of the file 1035 | self.logger.debug( 1036 | "Copying %s to %s" % ( 1037 | fbx_published_path, 1038 | fbx_output_path, 1039 | ) 1040 | ) 1041 | shutil.copy(fbx_published_path, fbx_output_path) 1042 | else: 1043 | fbx_output_path = fbx_published_path 1044 | else: 1045 | # Replace file extension with .fbx and suffix it with "_turntable" 1046 | fbx_name = "%s_%s_turntable.fbx" % (work_name, timestamp) 1047 | fbx_output_path = os.path.join(fbx_folder, fbx_name) 1048 | 1049 | # Export the FBX to the given output path 1050 | if not self._maya_export_fbx(fbx_output_path): 1051 | return False 1052 | 1053 | # Keep the fbx path for cleanup at finalize 1054 | item.properties["temp_fbx_path"] = fbx_output_path 1055 | # ======================= 1056 | # 2. Import the FBX into Unreal. 1057 | # 3. Instantiate the imported asset into a duplicate of the turntable map. 1058 | # Use the unreal_setup_turntable to do this in Unreal 1059 | self.logger.info("Setting up Unreal turntable project...") 1060 | # Copy the Unreal project in a temp location so we can modify it 1061 | temp_dir = tempfile.mkdtemp(dir=base_temp_dir) 1062 | project_path, project_file = os.path.split(unreal_project_path) 1063 | project_folder = os.path.basename(project_path) 1064 | temp_project_dir = os.path.join(temp_dir, project_folder) 1065 | temp_project_path = os.path.join(temp_project_dir, project_file) 1066 | self.logger.debug("Copying %s to %s" % (unreal_project_path, temp_project_path)) 1067 | shutil.copytree(project_path, temp_project_dir) 1068 | 1069 | current_folder = os.path.dirname(__file__) 1070 | script_path = os.path.abspath( 1071 | os.path.join( 1072 | current_folder, 1073 | os.path.pardir, 1074 | "unreal", 1075 | "unreal_setup_turntable.py" 1076 | ) 1077 | ) 1078 | 1079 | # Workaround for script path with spaces in it 1080 | if " " in script_path: 1081 | # Make temporary copies of the scripts to a path without spaces 1082 | script_destination = os.path.join(self.temp_folder, "unreal_setup_turntable.py") 1083 | shutil.copy(script_path, script_destination) 1084 | script_path = script_destination 1085 | 1086 | importer_path = os.path.abspath( 1087 | os.path.join( 1088 | current_folder, 1089 | os.path.pardir, 1090 | "unreal", 1091 | "unreal_importer.py", 1092 | ) 1093 | ) 1094 | importer_destination = os.path.join(self.temp_folder, "unreal_importer.py") 1095 | shutil.copy(importer_path, importer_destination) 1096 | # UE5 does some weird things with \ so let's replace them with / 1097 | script_path = script_path.replace("\\", "/") 1098 | 1099 | if " " in temp_project_path: 1100 | temp_project_path = '"{}"'.format(temp_project_path) 1101 | 1102 | # Set the script arguments in the environment variables since we don't 1103 | # have ways to run the Editor with a Python script and pass its arguments 1104 | # on the command line. 1105 | run_env = copy.copy(os.environ) 1106 | # Environment variables for turntable script 1107 | # Make sure they are strings and not unicode with py2, otherwise 1108 | # "environment can only contain strings" erors will happen. 1109 | extra_env = { 1110 | # The FBX to import into Unreal 1111 | "UNREAL_SG_FBX_OUTPUT_PATH": fbx_output_path, 1112 | # The Unreal content browser folder where the asset will be imported into 1113 | "UNREAL_SG_ASSETS_PATH": turntable_assets_path, 1114 | # The Unreal turntable map to duplicate where the asset will be instantiated into 1115 | "UNREAL_SG_MAP_PATH": turntable_map_path, 1116 | "UNREAL_SG_SEQUENCE_PATH": sequence_path, 1117 | "UNREAL_SG_MOVIE_OUTPUT_PATH": publish_path, 1118 | } 1119 | self.logger.info("Adding %s to the environment" % extra_env) 1120 | run_env.update(extra_env) 1121 | self._unreal_execute_script( 1122 | unreal_exec_path, 1123 | temp_project_path, 1124 | script_path, 1125 | env=run_env, 1126 | ) 1127 | 1128 | # ======================= 1129 | # 4. Render the turntable to movie. 1130 | 1131 | # Split the destination path into folder and filename 1132 | destination_folder = os.path.split(publish_path)[0] 1133 | movie_name = os.path.split(publish_path)[1] 1134 | movie_name = os.path.splitext(movie_name)[0] 1135 | 1136 | # Ensure that the destination path exists before rendering the sequence 1137 | self.parent.ensure_folder_exists(destination_folder) 1138 | self.logger.info("Rendering turntable to %s" % publish_path) 1139 | 1140 | # Check if a Movie render queue manifest was saved in the Unreal Project: if so 1141 | # use it. 1142 | manifest_path = "MovieRenderPipeline/QueueManifest.utxt" 1143 | manifest_full_path = os.path.join( 1144 | temp_project_dir, 1145 | "Saved", 1146 | manifest_path, 1147 | ) 1148 | if os.path.isfile(manifest_full_path): 1149 | self.logger.info( 1150 | "Found Movie render queue manifest %s, " 1151 | "using Movie render queue for rendering..." % manifest_full_path 1152 | ) 1153 | # Workaround for Level Sequencer only rendering avi on Windows and Movie Queue rendering 1154 | # mov on all platforms 1155 | publish_path = re.sub(r"\.avi$", ".mov", publish_path) 1156 | item.local_properties["publish_path"] = publish_path 1157 | self._unreal_render_movie_with_movie_render_queue( 1158 | unreal_exec_path, 1159 | temp_project_path, 1160 | manifest_path, 1161 | publish_path, 1162 | ) 1163 | else: 1164 | self.logger.info( 1165 | "Couldn't find a Movie render queue manifest in %s, " 1166 | "using Level sequencer for rendering..." % manifest_full_path 1167 | ) 1168 | # Render the turntable 1169 | self._unreal_render_movie_with_sequencer( 1170 | unreal_exec_path, 1171 | temp_project_path, 1172 | turntable_map_path, 1173 | sequence_path, 1174 | publish_path, 1175 | ) 1176 | 1177 | if not os.path.isfile(publish_path): 1178 | raise RuntimeError( 1179 | "Expected file %s was not generated" % publish_path 1180 | ) 1181 | # Publish the movie file to Shotgun 1182 | # If we generated the render from a published FBX, add it as dependency. 1183 | # Note: adding the published path to the item "publish_dependencies" property 1184 | # does not work when not having a default2 config with defined roots, and it's 1185 | # not possible to give a list of ids to `publish`, so we hack the parent 1186 | # sg_publish_data to be able to setup our published id. 1187 | if published_fbx: 1188 | parent_had_sg_publish_data = False 1189 | if "sg_publish_data" in item.parent.properties: 1190 | parent_sg_publish_data = item.parent.properties.sg_publish_data 1191 | parent_had_sg_publish_data = True 1192 | item.parent.properties.sg_publish_data = published_fbx 1193 | try: 1194 | super(MayaUnrealTurntablePublishPlugin, self).publish(settings, item) 1195 | finally: 1196 | if parent_had_sg_publish_data: 1197 | # Restore the value 1198 | item.parent.properties.sg_publish_data = parent_sg_publish_data 1199 | else: 1200 | # Removed the new key we added 1201 | del item.parent.properties["sg_publish_data"] 1202 | else: 1203 | super(MayaUnrealTurntablePublishPlugin, self).publish(settings, item) 1204 | # Save publish data locally on the item to be able to restore it later 1205 | item.local_properties["sg_publish_data"] = item.properties.sg_publish_data 1206 | 1207 | # Create a Version entry linked with the new publish 1208 | # Populate the version data to send to SG 1209 | self.logger.info("Creating Version...") 1210 | version_data = { 1211 | "project": item.context.project, 1212 | "code": movie_name, 1213 | "description": item.description, 1214 | "entity": self._get_version_entity(item), 1215 | "sg_path_to_movie": publish_path, 1216 | "sg_task": item.context.task, 1217 | "published_files": [item.properties.sg_publish_data], 1218 | } 1219 | # Log the version data for debugging 1220 | self.logger.debug( 1221 | "Populated Version data...", 1222 | extra={ 1223 | "action_show_more_info": { 1224 | "label": "Version Data", 1225 | "tooltip": "Show the complete Version data dictionary", 1226 | "text": "
%s
" % ( 1227 | pprint.pformat(version_data), 1228 | ) 1229 | } 1230 | } 1231 | ) 1232 | 1233 | # Create the version 1234 | self.logger.info("Creating version for review...") 1235 | version = self.parent.shotgun.create("Version", version_data) 1236 | 1237 | # Stash the version info in the item just in case 1238 | item.local_properties["sg_version_data"] = version 1239 | 1240 | # Ensure the path is utf-8 encoded to avoid issues with 1241 | # the shotgun api 1242 | upload_path = six.ensure_text( 1243 | self._get_local_path(item.properties.sg_publish_data) 1244 | ) 1245 | 1246 | # Upload the file to SG 1247 | self.logger.info("Uploading content...") 1248 | self.parent.shotgun.upload( 1249 | "Version", 1250 | version["id"], 1251 | upload_path, 1252 | "sg_uploaded_movie" 1253 | ) 1254 | self.logger.info("Upload complete!") 1255 | 1256 | def finalize(self, settings, item): 1257 | """ 1258 | Execute the finalization pass. This pass executes once all the publish 1259 | tasks have completed, and can for example be used to version up files. 1260 | 1261 | :param settings: Dictionary of Settings. The keys are strings, matching 1262 | the keys returned in the settings property. The values are `Setting` 1263 | instances. 1264 | :param item: Item to process 1265 | """ 1266 | 1267 | # The base implementation needs a property, not a local property 1268 | item.properties.sg_publish_data = item.local_properties.sg_publish_data 1269 | # do the base class finalization 1270 | super(MayaUnrealTurntablePublishPlugin, self).finalize(settings, item) 1271 | 1272 | # bump the session file to the next version 1273 | # self._save_to_next_version(item.properties["maya_path"], item, _save_session) 1274 | 1275 | # Delete the exported FBX and scripts from the temp folder 1276 | # Unless DEBUG is enabled 1277 | if self.logger.getEffectiveLevel() > logging.DEBUG: 1278 | temp_folder = item.local_properties.get("temp_folder") 1279 | if temp_folder: 1280 | shutil.rmtree(temp_folder) 1281 | 1282 | # Revive this when Unreal supports spaces in command line Python args 1283 | # fbx_path = item.properties.get("temp_fbx_path") 1284 | # if fbx_path: 1285 | # try: 1286 | # os.remove(fbx_path) 1287 | # except: 1288 | # pass 1289 | 1290 | def _get_version_entity(self, item): 1291 | """ 1292 | Returns the best entity to link the version to. 1293 | """ 1294 | if item.context.entity: 1295 | return item.context.entity 1296 | elif item.context.project: 1297 | return item.context.project 1298 | else: 1299 | return None 1300 | 1301 | def _maya_export_fbx(self, fbx_output_path): 1302 | # Export scene to FBX 1303 | try: 1304 | self.logger.info("Exporting scene to FBX {}".format(fbx_output_path)) 1305 | cmds.FBXResetExport() 1306 | cmds.FBXExportSmoothingGroups('-v', True) 1307 | # Mel script equivalent: 1308 | # import maya.mel as mel 1309 | # mel.eval('FBXExport -f "fbx_output_path"') 1310 | cmds.FBXExport('-f', fbx_output_path) 1311 | except Exception: 1312 | self.logger.error("Could not export scene to FBX") 1313 | return False 1314 | 1315 | return True 1316 | 1317 | def _unreal_execute_script(self, unreal_exec_path, unreal_project_path, script_path, env=None): 1318 | """ 1319 | Execute in a subprocess the given Python script in Unreal for the given project. 1320 | 1321 | :param str unreal_exec_path: Full path to Unreal executable. 1322 | :param str unreal_project_path: Full path to Unreal project to load. 1323 | :param str script_path: Full path to a Python script. 1324 | :param str env: Optional dictionary with the environment variables to set 1325 | in the subprocess. If not set, the current environment 1326 | variables are used. 1327 | """ 1328 | command_args = self._get_unreal_base_command( 1329 | unreal_exec_path, 1330 | unreal_project_path, 1331 | ) 1332 | command_args.append( 1333 | '-ExecutePythonScript="{}"'.format(script_path) # Script to run in Unreal 1334 | ) 1335 | self.logger.info( 1336 | "Executing script in Unreal with arguments: {}".format(command_args) 1337 | ) 1338 | subprocess.call( 1339 | command_args, 1340 | env=env, 1341 | ) 1342 | 1343 | def _get_unreal_base_command(self, unreal_exec_path, unreal_project_path): 1344 | """ 1345 | Return the base command line arguments to run Unreal in a subprocess. 1346 | 1347 | :param str unreal_exec_path: Full path to Unreal executable. 1348 | :param str unreal_project_path: Full path to the Unreal Project file to load. 1349 | :returns: A list of command line arguments. 1350 | """ 1351 | if sys.platform == "darwin" and os.path.splitext(unreal_exec_path)[1] == ".app": 1352 | # Special case for Osx if the Unreal.app was chosen instead of the 1353 | # executable 1354 | cmdline_args = [ 1355 | "open", 1356 | "-W", 1357 | "-n", 1358 | "-a", 1359 | unreal_exec_path, # Unreal executable path 1360 | "--args", 1361 | unreal_project_path, # Unreal project 1362 | ] 1363 | else: 1364 | cmdline_args = [ 1365 | unreal_exec_path, # Unreal executable path 1366 | unreal_project_path, # Unreal project 1367 | ] 1368 | return cmdline_args 1369 | 1370 | def _unreal_render_movie_with_sequencer( 1371 | self, 1372 | unreal_exec_path, 1373 | unreal_project_path, 1374 | unreal_map_path, 1375 | sequence_path, 1376 | output_path 1377 | ): 1378 | """ 1379 | Renders a given sequence in a given level to a movie file with Level Sequencer. 1380 | 1381 | :param str unreal_exec_path: Full path to Unreal executable. 1382 | :param str unreal_project_path: Full path to the Unreal Project file. 1383 | :param str unreal_map_path: Path of the Unreal map in which to run the sequence 1384 | :param str sequence_path: Content Browser path of sequence to render 1385 | :param str output_path: Full path to the movie to render. 1386 | :returns: True if a movie file was generated, False otherwise 1387 | string representing the path of the generated movie file 1388 | """ 1389 | output_folder, output_file = os.path.split(output_path) 1390 | movie_name = os.path.splitext(output_file)[0] 1391 | 1392 | # First, check if there's a file that will interfere with the output of the Sequencer 1393 | if os.path.isfile(output_path): 1394 | # Must delete it first, otherwise the Sequencer will add a number in the filename 1395 | try: 1396 | os.remove(output_path) 1397 | except OSError: 1398 | self.logger.debug("Couldn't delete {}. The Sequencer won't be able to output the movie to that file.".format(output_path)) 1399 | return False, None 1400 | 1401 | # Render the sequence to a movie file using the following command-line arguments 1402 | cmdline_args = self._get_unreal_base_command( 1403 | unreal_exec_path, 1404 | unreal_project_path, 1405 | ) 1406 | 1407 | # Note that any command-line arguments (usually paths) that could contain spaces must be enclosed between quotes 1408 | 1409 | # Command-line arguments for Sequencer Render to Movie 1410 | # See: https://docs.unrealengine.com/en-us/Engine/Sequencer/Workflow/RenderingCmdLine 1411 | cmdline_args.extend([ 1412 | unreal_map_path, # Level to load for rendering the sequence 1413 | "-LevelSequence={}".format(sequence_path), # The sequence to render 1414 | '-MovieFolder="{}"'.format(output_folder), # Output folder, must match the work template 1415 | "-MovieName={}".format(movie_name), # Output filename 1416 | "-game", 1417 | "-MovieSceneCaptureType=/Script/MovieSceneCapture.AutomatedLevelSequenceCapture", 1418 | "-ResX=1280", 1419 | "-ResY=720", 1420 | "-ForceRes", 1421 | "-Windowed", 1422 | "-MovieCinematicMode=yes", 1423 | "-MovieFormat=Video", 1424 | "-MovieFrameRate=24", 1425 | "-MovieQuality=75", 1426 | "-MovieWarmUpFrames=30", 1427 | "-NoTextureStreaming", 1428 | "-NoLoadingScreen", 1429 | "-NoScreenMessages", 1430 | ]) 1431 | self.logger.info("Sequencer command-line arguments: {}".format(cmdline_args)) 1432 | 1433 | # TODO: fix command line arguments which contain space. 1434 | subprocess.call(cmdline_args) 1435 | 1436 | return os.path.isfile(output_path), output_path 1437 | 1438 | def _unreal_render_movie_with_movie_render_queue( 1439 | self, 1440 | unreal_exec_path, 1441 | unreal_project_path, 1442 | manifest_path, 1443 | output_path 1444 | ): 1445 | """ 1446 | Renders a given sequence in a given level to a movie file with Movie Render queue. 1447 | 1448 | :param str unreal_exec_path: Full path to Unreal executable. 1449 | :param str unreal_project_path: Full path to the Unreal Project file. 1450 | :param str manifest_path: Path a to Movie Render Queue manifest file, local 1451 | to the Unreal Project, e.g. 'Saved/MovieRenderPipeline/QueueManifest.utxt'. 1452 | :param str output_path: Full path to the movie to render. 1453 | :returns: True if a movie file was generated, False otherwise 1454 | string representing the path of the generated movie file 1455 | """ 1456 | output_folder, output_file = os.path.split(output_path) 1457 | 1458 | cmdline_args = self._get_unreal_base_command( 1459 | unreal_exec_path, 1460 | unreal_project_path, 1461 | ) 1462 | 1463 | # Command line parameters were retrieved by submitting a queue in Unreal Editor with 1464 | # a MoviePipelineNewProcessExecutor executor. 1465 | # https://docs.unrealengine.com/4.27/en-US/PythonAPI/class/MoviePipelineNewProcessExecutor.html?highlight=executor 1466 | cmdline_args.extend([ 1467 | "MoviePipelineEntryMap?game=/Script/MovieRenderPipelineCore.MoviePipelineGameMode", 1468 | "-game", 1469 | "-Multiprocess", 1470 | "-NoLoadingScreen", 1471 | "-FixedSeed", 1472 | "-log", 1473 | "-Unattended", 1474 | "-messaging", 1475 | "-SessionName=\"Maya Turntable Movie Render\"", 1476 | "-nohmd", 1477 | "-windowed", 1478 | "-ResX=1280", 1479 | "-ResY=720", 1480 | # TODO: check what these settings are 1481 | "-dpcvars=%s" % ",".join([ 1482 | "sg.ViewDistanceQuality=4", 1483 | "sg.AntiAliasingQuality=4", 1484 | "sg.ShadowQuality=4", 1485 | "sg.PostProcessQuality=4", 1486 | "sg.TextureQuality=4", 1487 | "sg.EffectsQuality=4", 1488 | "sg.FoliageQuality=4", 1489 | "sg.ShadingQuality=4", 1490 | "r.TextureStreaming=0", 1491 | "r.ForceLOD=0", 1492 | "r.SkeletalMeshLODBias=-10", 1493 | "r.ParticleLODBias=-10", 1494 | "foliage.DitheredLOD=0", 1495 | "foliage.ForceLOD=0", 1496 | "r.Shadow.DistanceScale=10", 1497 | "r.ShadowQuality=5", 1498 | "r.Shadow.RadiusThreshold=0.001000", 1499 | "r.ViewDistanceScale=50", 1500 | "r.D3D12.GPUTimeout=0", 1501 | "a.URO.Enable=0", 1502 | ]), 1503 | "-execcmds=r.HLOD 0", 1504 | # This need to be a path relative the to the Unreal project "Saved" folder. 1505 | "-MoviePipelineConfig=\"%s\"" % manifest_path, 1506 | ]) 1507 | self.logger.info("Running %s" % cmdline_args) 1508 | subprocess.call(cmdline_args) 1509 | return os.path.isfile(output_path), output_path 1510 | 1511 | def get_unreal_versions(self): 1512 | """ 1513 | Return a list of all known Unreal versions installed locally. 1514 | 1515 | Uses the Engine Launcher logic to scan for Unreal executables and selects the one that 1516 | matches the version defined in the settings, prioritizing non-development builds 1517 | 1518 | :returns: A list of TK software versions. 1519 | """ 1520 | 1521 | # Create a launcher. 1522 | # Since we only care about Unreal paths, we use the current project 1523 | # context to retrieve the paths. Otherwise it would require having a valid 1524 | # tk-unreal context matching the current one. 1525 | engine = sgtk.platform.current_engine() 1526 | project_context = engine.sgtk.context_from_entity_dictionary( 1527 | engine.context.project 1528 | ) 1529 | 1530 | software_launcher = sgtk.platform.create_engine_launcher( 1531 | engine.sgtk, 1532 | project_context, 1533 | "tk-unreal" 1534 | ) 1535 | 1536 | # Discover which versions of Unreal are available 1537 | software_versions = software_launcher.scan_software() 1538 | versions = [] 1539 | dev_versions = [] 1540 | for software_version in software_versions: 1541 | # Insert non-dev builds at the start of the list 1542 | if "(Dev Build)" not in software_version.display_name: 1543 | versions.append(software_version) 1544 | else: 1545 | dev_versions.append(software_version) 1546 | fake_versions = [] 1547 | # Can be uncommented to fake multiple SW versions if needed. 1548 | # fake_versions = [ 1549 | # SoftwareVersion( 1550 | # "Faked 5", 1551 | # "Faked", 1552 | # "faked" 1553 | # ), 1554 | # SoftwareVersion( 1555 | # "Faked 6", 1556 | # "Faked", 1557 | # "faked" 1558 | # ) 1559 | # 1560 | # ] 1561 | return fake_versions + versions + dev_versions 1562 | 1563 | return None 1564 | 1565 | def evaluate_unreal_project_path(self, unreal_project_path_template, unreal_engine_version): 1566 | """ 1567 | Return the path to the Unreal project to use based on the given template and 1568 | Unreal version. 1569 | 1570 | It uses the same path resolution as for hook paths to expand {config} and {engine} 1571 | to their absolute path equivalent, except that the 'hooks' folder is not 1572 | added at the end, so if needed, it must be set explicitly in the template. 1573 | 1574 | .. note :: The project template is not a regular TK template but a string 1575 | with {self}, {config}, {engine} and {unreal_engine_version} which are replaced. 1576 | 1577 | :param str unreal_project_path_template: A path template to use to resolve the 1578 | the project path. 1579 | :param str unreal_engine_version: An Unreal version number as a string. 1580 | :returns: An absolute path to the Unreal project to use, or `None`. 1581 | """ 1582 | if not unreal_project_path_template: 1583 | return None 1584 | 1585 | if not unreal_engine_version: 1586 | return None 1587 | # Only keep major.minor from the Unreal version 1588 | short_version = ".".join(unreal_engine_version.split(".")[:2]) 1589 | # Evaluate the "template" 1590 | engine = sgtk.platform.current_engine() 1591 | hooks_folder = engine.sgtk.pipeline_configuration.get_hooks_location() 1592 | fw = self.load_framework("tk-framework-unrealqt_v1.x.x") 1593 | return os.path.normpath( 1594 | unreal_project_path_template.replace( 1595 | "{config}", 1596 | os.path.dirname(hooks_folder), # removed the hooks folder at the end 1597 | ).replace( 1598 | "{engine}", 1599 | engine.disk_location 1600 | ).replace( 1601 | "{unreal_engine_version}", 1602 | short_version 1603 | ).replace( 1604 | "{self}", 1605 | fw.disk_location 1606 | ) 1607 | ) 1608 | 1609 | def _copy_to_publish(self, settings, item): 1610 | """ 1611 | Override base implementation to do nothing 1612 | since we're not copying a file but rendering 1613 | directly to the publish location. 1614 | """ 1615 | pass 1616 | 1617 | def _copy_local_to_publish(self, settings, item): 1618 | """ 1619 | Override base implementation to do nothing 1620 | since we're not copying a file but rendering 1621 | directly to the publish location. 1622 | """ 1623 | pass 1624 | 1625 | def _copy_work_to_publish(self, settings, item): 1626 | """ 1627 | Override base implementation to do nothing 1628 | since we're not copying a file but rendering 1629 | directly to the publish location. 1630 | """ 1631 | pass 1632 | 1633 | 1634 | def _short_version(version): 1635 | """ 1636 | Return a short major.minor version for the given version. 1637 | 1638 | Return the given version if a major and minor can't be extracted. 1639 | 1640 | :param str version: A version, as string, e.g. 5.0.2. 1641 | :returns: A string. 1642 | """ 1643 | parts = version.split(".", 2) 1644 | if len(parts) > 2: 1645 | return ".".join(parts[:2]) 1646 | return version 1647 | 1648 | 1649 | def _session_path(): 1650 | """ 1651 | Return the path to the current session 1652 | :return: 1653 | """ 1654 | path = cmds.file(query=True, sn=True) 1655 | 1656 | return six.ensure_text(path) 1657 | 1658 | 1659 | def _save_session(path): 1660 | """ 1661 | Save the current session to the supplied path. 1662 | """ 1663 | 1664 | # Maya can choose the wrong file type so we should set it here 1665 | # explicitly based on the extension 1666 | maya_file_type = None 1667 | if path.lower().endswith(".ma"): 1668 | maya_file_type = "mayaAscii" 1669 | elif path.lower().endswith(".mb"): 1670 | maya_file_type = "mayaBinary" 1671 | 1672 | cmds.file(rename=path) 1673 | 1674 | # save the scene: 1675 | if maya_file_type: 1676 | cmds.file(save=True, force=True, type=maya_file_type) 1677 | else: 1678 | cmds.file(save=True, force=True) 1679 | 1680 | 1681 | # TODO: method duplicated in all the maya hooks 1682 | def _get_save_as_action(): 1683 | """ 1684 | Simple helper for returning a log action dict for saving the session 1685 | """ 1686 | 1687 | engine = sgtk.platform.current_engine() 1688 | 1689 | # default save callback 1690 | callback = cmds.SaveScene 1691 | 1692 | # if workfiles2 is configured, use that for file save 1693 | if "tk-multi-workfiles2" in engine.apps: 1694 | app = engine.apps["tk-multi-workfiles2"] 1695 | if hasattr(app, "show_file_save_dlg"): 1696 | callback = app.show_file_save_dlg 1697 | 1698 | return { 1699 | "action_button": { 1700 | "label": "Save As...", 1701 | "tooltip": "Save the current session", 1702 | "callback": callback 1703 | } 1704 | } 1705 | -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/fbx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/fbx.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/geometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/geometry.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/maya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/maya.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/publish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/publish.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/unreal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/unreal.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/icons/version_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/hooks/tk-multi-publish2/tk-maya/icons/version_up.png -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/unreal/unreal_importer.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Epic Games, Inc. 2 | 3 | import unreal 4 | import re 5 | import sys 6 | 7 | """ 8 | Functions to import FBX into Unreal 9 | """ 10 | 11 | 12 | def _sanitize_name(name): 13 | # Remove the default Shotgun versioning number if found (of the form '.v001') 14 | name_no_version = re.sub(r'.v[0-9]{3}', '', name) 15 | 16 | # Replace any remaining '.' with '_' since they are not allowed in Unreal asset names 17 | return name_no_version.replace('.', '_') 18 | 19 | 20 | def _generate_fbx_import_task( 21 | filename, 22 | destination_path, 23 | destination_name=None, 24 | replace_existing=True, 25 | automated=True, 26 | save=True, 27 | materials=True, 28 | textures=True, 29 | as_skeletal=False 30 | ): 31 | """ 32 | Create and configure an Unreal AssetImportTask 33 | 34 | :param filename: The fbx file to import, 35 | :param destination_path: The Content Browser path where the asset will be placed. 36 | :returns: The configured AssetImportTask. 37 | """ 38 | task = unreal.AssetImportTask() 39 | task.filename = filename 40 | task.destination_path = destination_path 41 | 42 | # By default, destination_name is the filename without the extension 43 | if destination_name is not None: 44 | task.destination_name = destination_name 45 | 46 | task.replace_existing = replace_existing 47 | task.automated = automated 48 | task.save = save 49 | 50 | task.options = unreal.FbxImportUI() 51 | task.options.import_materials = materials 52 | task.options.import_textures = textures 53 | task.options.import_as_skeletal = as_skeletal 54 | 55 | task.options.static_mesh_import_data.combine_meshes = True 56 | 57 | task.options.mesh_type_to_import = unreal.FBXImportType.FBXIT_STATIC_MESH 58 | if as_skeletal: 59 | task.options.mesh_type_to_import = unreal.FBXImportType.FBXIT_SKELETAL_MESH 60 | 61 | return task 62 | 63 | 64 | def import_fbx(filename, destination_path): 65 | """ 66 | Import the given FBX file under the given path. 67 | :param filename: The fbx file to import, 68 | :param destination_path: The Content Browser path where the asset will be placed. 69 | """ 70 | tasks = [_generate_fbx_import_task(filename, destination_path)] 71 | unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks) 72 | unreal.EditorLoadingAndSavingUtils.save_dirty_packages(False, True) 73 | 74 | 75 | def main(argv): 76 | import_fbx(*argv) 77 | 78 | 79 | if __name__ == "__main__": 80 | # Script arguments must be, in order: 81 | # Path to FBX to import 82 | # Unreal content browser path where to store the imported asset 83 | main(sys.argv[1:]) 84 | -------------------------------------------------------------------------------- /hooks/tk-multi-publish2/tk-maya/unreal/unreal_setup_turntable.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Epic Games, Inc. 2 | 3 | # Setup the asset in the turntable level for rendering 4 | 5 | import unreal 6 | import os 7 | import sys 8 | 9 | 10 | def setup_render_with_movie_render_queue(output_path, unreal_map_path, sequence_path): 11 | """ 12 | Setup rendering the given map and sequence to the given movie with the Movie render queue. 13 | 14 | :param str output_path: Full path to the movie to render. 15 | :param str unreal_map_path: Unreal map path. 16 | :param str sequence_path: Unreal level sequence path. 17 | :returns: Full path to a manifest file to use for rendering or None if rendering 18 | with the Movie Render queue is not possible. 19 | """ 20 | # Check if we can use the Movie render queue, bail out if we can't 21 | if not hasattr(unreal, "MoviePipelineQueueEngineSubsystem"): 22 | unreal.log( 23 | "Movie Render Queue is not available, Movie queue rendering can't be setup." 24 | ) 25 | return None 26 | if not hasattr(unreal, "MoviePipelineAppleProResOutput"): 27 | unreal.log( 28 | "Apple ProRes Media plugin must be loaded to be able to render with the Movie Render Queue, " 29 | "Movie queue rendering can't be setup." 30 | ) 31 | return None 32 | 33 | unreal.log("Setting rendering %s %s to %s..." % (unreal_map_path, sequence_path, output_path)) 34 | output_folder, output_file = os.path.split(output_path) 35 | movie_name = os.path.splitext(output_file)[0] 36 | 37 | qsub = unreal.MoviePipelineQueueEngineSubsystem() 38 | queue = qsub.get_queue() 39 | job = queue.allocate_new_job(unreal.MoviePipelineExecutorJob) 40 | job.sequence = unreal.SoftObjectPath(sequence_path) 41 | job.map = unreal.SoftObjectPath(unreal_map_path) 42 | # Set settings 43 | config = job.get_configuration() 44 | output_setting = config.find_or_add_setting_by_class(unreal.MoviePipelineOutputSetting) 45 | # https://docs.unrealengine.com/4.26/en-US/PythonAPI/class/MoviePipelineOutputSetting.html?highlight=setting#unreal.MoviePipelineOutputSetting 46 | output_setting.output_directory = unreal.DirectoryPath(output_folder) 47 | output_setting.output_resolution = unreal.IntPoint(1280, 720) 48 | output_setting.file_name_format = movie_name 49 | output_setting.override_existing_output = True # Overwrite existing files 50 | # Render to a movie 51 | config.find_or_add_setting_by_class(unreal.MoviePipelineAppleProResOutput) 52 | # TODO: check which codec we should use. 53 | # Default rendering 54 | config.find_or_add_setting_by_class(unreal.MoviePipelineDeferredPassBase) 55 | # Additional pass with detailed lighting? 56 | # render_pass = config.find_or_add_setting_by_class(unreal.MoviePipelineDeferredPass_DetailLighting) 57 | _, manifest_path = unreal.MoviePipelineEditorLibrary.save_queue_to_manifest_file(queue) 58 | unreal.log("Saved rendering manifest to %s" % manifest_path) 59 | return manifest_path 60 | 61 | 62 | def setup_turntable(fbx_file_path, assets_path, turntable_map_path): 63 | """ 64 | Setup the turntable project to render the given FBX file. 65 | 66 | :param str fbx_file_path: Full path to the FBX file to import and render. 67 | :param str assets_path: Unreal path under which the FBX file should be imported. 68 | :param str turntable_map_path: Unreal map path. 69 | :returns: The loaded map path. 70 | """ 71 | # Import the FBX into Unreal using the unreal_importer script 72 | current_folder = os.path.dirname(__file__) 73 | if current_folder not in sys.path: 74 | sys.path.append(current_folder) 75 | # TODO: check if there is any reason to keep this in another .py file 76 | # instead of having it just here. 77 | import unreal_importer 78 | 79 | unreal.log("Importing FBX file %s under %s..." % (fbx_file_path, assets_path)) 80 | unreal_importer.import_fbx(fbx_file_path, assets_path) 81 | 82 | unreal.log("Loading map %s..." % turntable_map_path) 83 | # Load the turntable map where to instantiate the imported asset 84 | world = unreal.EditorLoadingAndSavingUtils.load_map(turntable_map_path) 85 | 86 | if not world: 87 | unreal.error("Unable to load map %s" % turntable_map_path) 88 | return 89 | unreal.log("Setting up turntable actor...") 90 | # Find the turntable actor, which is used in the turntable sequence that rotates it 360 degrees 91 | turntable_actor = None 92 | level_actors = unreal.EditorLevelLibrary.get_all_level_actors() 93 | for level_actor in level_actors: 94 | if level_actor.get_actor_label() == "turntable": 95 | turntable_actor = level_actor 96 | break 97 | 98 | if not turntable_actor: 99 | return 100 | 101 | # Destroy any actors attached to the turntable (attached for a previous render) 102 | for attached_actor in turntable_actor.get_attached_actors(): 103 | unreal.EditorLevelLibrary.destroy_actor(attached_actor) 104 | 105 | # Derive the imported asset path from the given FBX filename and content browser path 106 | fbx_filename = os.path.basename(fbx_file_path) 107 | asset_name = os.path.splitext(fbx_filename)[0] 108 | # The path here is not a file path, it is an Unreal path so '/' is always used. 109 | asset_path_to_load = "%s/%s" % (assets_path, asset_name) 110 | 111 | # Load the asset to spawn it at origin 112 | asset = unreal.EditorAssetLibrary.load_asset(asset_path_to_load) 113 | if not asset: 114 | return 115 | 116 | actor = unreal.EditorLevelLibrary.spawn_actor_from_object(asset, unreal.Vector(0, 0, 0)) 117 | 118 | # Scale the actor to fit the frame, which is dependent on the settings of the camera used in the turntable sequence 119 | # The scale values are based on a volume that fits safely in the frustum of the camera and account for the frame ratio 120 | # and must be tweaked if the camera settings change 121 | origin, bounds = actor.get_actor_bounds(True) 122 | scale_x = 250 / min(bounds.x, bounds.y) 123 | scale_y = 300 / max(bounds.x, bounds.y) 124 | scale_z = 200 / bounds.z 125 | scale = min(scale_x, scale_y, scale_z) 126 | actor.set_actor_scale3d(unreal.Vector(scale, scale, scale)) 127 | 128 | # Offset the actor location so that it rotates around its center 129 | origin = origin * scale 130 | actor.set_actor_location(unreal.Vector(-origin.x, -origin.y, -origin.z), False, True) 131 | 132 | # Attach the newly spawned actor to the turntable 133 | actor.attach_to_actor(turntable_actor, "", unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False) 134 | 135 | unreal.log("Saving current level...") 136 | unreal.EditorLevelLibrary.save_current_level() 137 | unreal.log("Saving map %s" % world.get_path_name()) 138 | # This seems to fail with: 139 | # [2021.09.15-15.17.52:220][ 1]Message dialog closed, result: Ok, title: Message, text: Failed to save the map. The filename '../../../../../../../var/folders/rt/vlgl5dzj75q2qg4t9fp9gfx80000gp/T/tmpO8A5Lw/turntable/Content/turntable/level/turntable.turntable.umap' is not within the game or engine content folders found in '/Users/Shared/Epic Games/UE_4.26/'. 140 | unreal.EditorLoadingAndSavingUtils.save_map(world, world.get_path_name()) 141 | unreal.log("Turntable setup done.") 142 | return world.get_path_name() 143 | 144 | 145 | if __name__ == "__main__": 146 | # Script arguments must be, in order: 147 | # Path to FBX to import 148 | # Unreal content browser path where to store the imported asset 149 | # Unreal content browser path to the turntable map to duplicate and where to spawn the asset 150 | # Retrieve arguments from the environment 151 | fbx_file_path = os.environ["UNREAL_SG_FBX_OUTPUT_PATH"] 152 | assets_path = os.environ["UNREAL_SG_ASSETS_PATH"] 153 | turntable_map_path = os.environ["UNREAL_SG_MAP_PATH"] 154 | 155 | # Additional optional settings to render with the Movie Render Queue 156 | movie_path = os.environ.get("UNREAL_SG_MOVIE_OUTPUT_PATH") 157 | level_sequence_path = os.environ.get("UNREAL_SG_SEQUENCE_PATH") 158 | 159 | map_path = setup_turntable(fbx_file_path, assets_path, turntable_map_path) 160 | 161 | if movie_path and level_sequence_path: 162 | setup_render_with_movie_render_queue( 163 | movie_path, 164 | map_path, 165 | level_sequence_path, 166 | ) 167 | -------------------------------------------------------------------------------- /info.yml: -------------------------------------------------------------------------------- 1 | # Metadata defining the behaviour and requirements for this framework 2 | 3 | # expected fields in the configuration file for this engine 4 | configuration: 5 | 6 | # the Shotgun fields that this engine needs in order to operate correctly 7 | requires_shotgun_fields: 8 | 9 | # More verbose description of this item 10 | display_name: "Unreal Qt Framework" 11 | description: "Qt & Pyside2 distributions for the Unreal engine" 12 | 13 | # Required minimum versions for this item to run 14 | requires_shotgun_version: 15 | requires_core_version: "v0.18.127" -------------------------------------------------------------------------------- /resources/build_packages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 GPL Solutions, LLC. All rights reserved. 3 | # 4 | # Use of this software is subject to the terms of the GPL Solutions license 5 | # agreement provided at the time of installation or download, or which otherwise 6 | # accompanies this software in either electronic or hard copy form. 7 | # 8 | 9 | ##################################################################### 10 | long_help=' 11 | 12 | This script helps building and shipping Python packages 13 | with this framework. 14 | 15 | It uses virtual env and pip to do so, so you will need them 16 | on your machine to use this script. On Windows, git-bash is 17 | needed. 18 | 19 | == Typical workflow ================================================ 20 | 21 | * Run this script once with no option to get a virtual env and a 22 | requirements.txt file 23 | * Use the virtual env to install packages with standard pip install 24 | or update commands 25 | * Update the requirements.txt file with pip freeze -r requirements.txt > requirements.txt 26 | * Build and copy the packages into their shipping destination with the 27 | -b option 28 | * Run some tests 29 | * Update git with the copied packages and push 30 | 31 | 32 | == Structure ======================================================= 33 | 34 | * This script use a "packagevenv_$platform_name_$python_major_version" virtual env to 35 | do packages downloads and buildings, where platform_name can either 36 | be "linux", "osx" or "windows". 37 | * Packages are shipped in "../python/vendors/py${python_major_version}/${platform_name}". 38 | They are full copy of the virtual env for the current platform, 39 | with .pyc and .egg-info files deleted. 40 | * They need to be added in git to ship with the framework 41 | * Packages dependencies are stored in a standard requirements.txt 42 | pip file, build with regular pip commands 43 | 44 | == Starting from scratch =========================================== 45 | 46 | * A virtual env for the current platform will be created if needed 47 | by running the script without any option. 48 | * An empty requirements.txt will be created if needed 49 | 50 | == Building packages for a platform ================================ 51 | 52 | * If you are on Windows, you will have to run the script from 53 | a git-bash, NOT a cygwin shell, as cygmin will not use the right 54 | compiler and libraries 55 | * On Windows, A Microsoft Visual C++ for Python 2.7 can be downloaded from : 56 | http://aka.ms/vcpython27 57 | * Just run the script with the -b option, and all needed packages 58 | should be downloaded, build and copied into the shipping folder 59 | 60 | == Adding packages ================================================= 61 | 62 | * Activate the virtual env for your platform, e.g. : 63 | source packagevenv_linux/bin/activate 64 | * Install / update packages using pip, e.g. : 65 | pip install pycrypto 66 | * Update the requirements.txt file : 67 | pip freeze -r requirements.txt > requirements.txt 68 | * It is possible to edit the requirements.txt manually, although this 69 | is not recommended 70 | * Copy the packages into their shipping location with the -b option 71 | 72 | == Commiting packages in git ======================================== 73 | 74 | * Go into ../python/vendors shipping directory, and do: 75 | git add -f -A ./$platform 76 | where $platform is the platform name you want to update. 77 | * The -f option is used to bypass .gitignore and add .so or .dll files. 78 | ' 79 | 80 | ##################################################################### 81 | # Stop on errors 82 | set -e 83 | usage() 84 | { 85 | echo "Usage : $0 [-h] [-b] [-p ]" 86 | echo "Options :" 87 | echo " -h : show this help message" 88 | echo " -b : build packages into their shipping destination" 89 | echo " -p : specify which python command to use, e.g. python, python2, python3" 90 | echo "$long_help" 91 | } 92 | # Parse command line arguments 93 | do_build=0 94 | python_cmd="python" 95 | while getopts “hp:b” option 96 | do 97 | case $option in 98 | h) 99 | usage 100 | exit 1 101 | ;; 102 | b) 103 | do_build=1 104 | ;; 105 | p) 106 | python_cmd=$OPTARG 107 | ;; 108 | ?) 109 | usage 110 | exit 111 | ;; 112 | esac 113 | done 114 | 115 | uname_os_str=`uname` 116 | 117 | 118 | # Build nice platform names from the os 119 | if [[ "$uname_os_str" == Linux ]]; 120 | then 121 | platform_name="linux" 122 | elif [[ "$uname_os_str" == Darwin ]]; 123 | then 124 | platform_name="osx" 125 | elif [[ "$uname_os_str" == MINGW??_NT-* ]]; 126 | then 127 | platform_name="windows" 128 | else 129 | echo "Unsupported platform !" 130 | exit 1 131 | fi 132 | echo "Detecting Python version..." 133 | python_version=$($python_cmd --version 2>&1) 134 | # 2 or 3 135 | python_major_version=${python_version:7:1} 136 | # Remove patch number from Python 3.9.17 137 | no_patch=${python_version%\.*} 138 | # 3.9 139 | python_major_minor=${no_patch:7} 140 | 141 | if [ -z $python_major_version ] || [ -z $python_major_minor ]; 142 | then 143 | echo "Unable to detect python version, aborting" 144 | exit 1 145 | fi 146 | 147 | echo "Detected python version ${python_major_version} from ${python_version} (${python_major_minor})" 148 | 149 | packagevenv="packagevenv_${platform_name}_${python_major_minor}" 150 | if [ ! -d $packagevenv ]; then 151 | if [ ${python_major_version} == 2 ]; 152 | then 153 | # Create the virtual env to install packages 154 | # Please note that we used --no-site-packages but this option does not 155 | # exist anymore with virtualenv v20 is the default. 156 | virtualenv --always-copy $packagevenv 157 | elif [ ${python_major_version} == 3 ]; 158 | then 159 | $python_cmd -m venv --copies $packagevenv 160 | else 161 | echo "Unsupported python version ${python_major_version}" 162 | exit 1 163 | fi 164 | echo "Created virtual env in $packagevenv" 165 | fi 166 | # Activate the virtual env 167 | if [ $platform_name == "windows" ]; then 168 | . ${packagevenv}/Scripts/activate 169 | else 170 | . ${packagevenv}/bin/activate 171 | fi 172 | # If the requirements.txt file does 173 | # not already exist, create it and exit 174 | if [ ! -f requirements.txt ]; then 175 | pip freeze > requirements.txt 176 | echo "Created requirements.txt file" 177 | echo "You can build it with virtualenv and pip" 178 | echo "Activate the virtual env :" 179 | echo " source ${packagevenv}/bin/activate" 180 | echo "Install your modules :" 181 | echo " pip install the_module_I_need" 182 | echo "Update the requirements.txt file :" 183 | echo " pip freeze -r requirements.txt > requirements.txt" 184 | echo "Deactivate the virtual env:" 185 | echo " deactivate" 186 | exit 0 187 | fi 188 | if [ $do_build == 1 ]; then 189 | # Build needed packages and copy them 190 | # into their shipping destination 191 | echo "Building packages ..." 192 | # Install packages from the requirements 193 | pip install -r requirements.txt 194 | # If there are some platform specific requirements, install them as well 195 | platform_requirements="${platform_name}_requirements.txt" 196 | if [ -f $platform_requirements ]; then 197 | echo "Installing plaform specific packages from $platform_requirements" 198 | pip install -r $platform_requirements 199 | fi 200 | # If there are requirements for a specific version of python, install them as well 201 | python_version_requirements="python_${python_major_version}_requirements.txt" 202 | if [ -f $python_version_requirements ]; then 203 | echo "Installing Python version specific packages from $python_version_requirements" 204 | pip install -r $python_version_requirements 205 | fi 206 | # Copy packages to their shipping destination 207 | if [ -d ./${packagevenv} ]; then 208 | mkdir -p ../python/vendors/py${python_major_minor} 209 | target="../python/vendors/py${python_major_minor}/${platform_name}" 210 | if [ -d $target ]; then 211 | echo "Deleting previous build in $target" 212 | # Clean up git but don't fail if there is nothing matching 213 | git rm -r -f ${target} --ignore-unmatch 214 | rm -r -f ${target} 215 | mkdir -p ${target} 216 | fi 217 | echo "Copying in ${target}" 218 | cp -R ./${packagevenv}/ $target 219 | cd ${target} 220 | # Do some cleanup 221 | find . -name "*.pyc" | xargs rm -f 222 | find . -name "*.egg-info" | xargs rm -rf 223 | fi 224 | fi 225 | -------------------------------------------------------------------------------- /resources/requirements.txt: -------------------------------------------------------------------------------- 1 | ## The following requirements were added by pip --freeze: 2 | 3 | PySide6==6.7.0 4 | -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.20/turntable/Config/DefaultEditor.ini -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.20/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.20/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.20/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.20/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.20/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.20", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | [/Script/IOSRuntimeSettings.IOSRuntimeSettings] 10 | bSupportsPortraitOrientation=False 11 | bSupportsUpsideDownOrientation=False 12 | bSupportsLandscapeLeftOrientation=True 13 | PreferredLandscapeOrientation=LandscapeLeft 14 | 15 | -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.21/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.21/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.21/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.21/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.21/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.21", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.22/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.22/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.22/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.22/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.22/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.22", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.23/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.23/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.23/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.23/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.23/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.23", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.24/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.24/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.24/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.24/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.24/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.24", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.25/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.25/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.25/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.25/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.25/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.25", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.26/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.26/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.26/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.26/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.26/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.26", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | 10 | -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.27/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.27/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.27/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/4.27/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/4.27/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.27", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [/Script/HardwareTargeting.HardwareTargetingSettings] 2 | TargetedHardwareClass=Desktop 3 | AppliedTargetedHardwareClass=Desktop 4 | DefaultGraphicsPerformance=Maximum 5 | AppliedDefaultGraphicsPerformance=Maximum 6 | 7 | [/Script/WmfMediaFactory.WmfMediaSettings] 8 | AllowNonStandardCodecs=True 9 | LowLatency=False 10 | NativeAudioOut=False 11 | HardwareAcceleratedVideoDecoding=True 12 | 13 | -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.0/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.0/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.0/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.0/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.0/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "5.0EA", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | }, 27 | { 28 | "Name": "Bridge", 29 | "Enabled": true, 30 | "SupportedTargetPlatforms": [ 31 | "Win64", 32 | "Mac", 33 | "Linux" 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [/Script/HardwareTargeting.HardwareTargetingSettings] 2 | TargetedHardwareClass=Desktop 3 | AppliedTargetedHardwareClass=Desktop 4 | DefaultGraphicsPerformance=Maximum 5 | AppliedDefaultGraphicsPerformance=Maximum 6 | 7 | [/Script/WmfMediaFactory.WmfMediaSettings] 8 | AllowNonStandardCodecs=True 9 | LowLatency=False 10 | NativeAudioOut=False 11 | HardwareAcceleratedVideoDecoding=True 12 | 13 | [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] 14 | bEnablePlugin=True 15 | bAllowNetworkConnection=True 16 | SecurityToken=4F99E4E87844874F1DAD32833EBC3ACB 17 | bIncludeInShipping=False 18 | bAllowExternalStartInShipping=False 19 | bCompileAFSProject=False 20 | bUseCompression=False 21 | bLogFiles=False 22 | bReportStats=False 23 | ConnectionType=USBOnly 24 | bUseManualIPAddress=False 25 | ManualIPAddress= 26 | 27 | -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Config/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | [/Script/Engine.InputSettings] 2 | -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 3 | -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 4 | -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 5 | -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 6 | -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 7 | -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 8 | -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 9 | +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 10 | +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 11 | +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 12 | +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 13 | +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 14 | +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 15 | +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 16 | +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 17 | +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 18 | +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 19 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 20 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 21 | +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 22 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 23 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 24 | +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 25 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 26 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 27 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 28 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 29 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 30 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 31 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 32 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 33 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 34 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 35 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 36 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 37 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 38 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 39 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 40 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 41 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 42 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 43 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 44 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 45 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 46 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 47 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 48 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 49 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 50 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 51 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 52 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 53 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 54 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 55 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 56 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 57 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 58 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 59 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 60 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 61 | bAltEnterTogglesFullscreen=True 62 | bF11TogglesFullscreen=True 63 | bUseMouseForTouch=False 64 | bEnableMouseSmoothing=True 65 | bEnableFOVScaling=True 66 | bCaptureMouseOnLaunch=True 67 | bEnableLegacyInputScales=True 68 | bEnableMotionControls=True 69 | bFilterInputByPlatformUser=False 70 | bEnableInputDeviceSubsystem=True 71 | bShouldFlushPressedKeysOnViewportFocusLost=True 72 | bEnableDynamicComponentInputBinding=True 73 | bAlwaysShowTouchInterface=False 74 | bShowConsoleOnFourFingerTap=True 75 | bEnableGestureRecognizer=False 76 | bUseAutocorrect=False 77 | DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown 78 | DefaultViewportMouseLockMode=LockOnCapture 79 | FOVScale=0.011110 80 | DoubleClickTime=0.200000 81 | DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput 82 | DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent 83 | DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks 84 | -ConsoleKeys=Tilde 85 | +ConsoleKeys=Tilde 86 | 87 | -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.2/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.2/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.2/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.2/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.2/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "5.2", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | }, 27 | { 28 | "Name": "Bridge", 29 | "Enabled": true, 30 | "SupportedTargetPlatforms": [ 31 | "Win64", 32 | "Mac", 33 | "Linux" 34 | ] 35 | }, 36 | { 37 | "Name": "Shotgrid", 38 | "Enabled": true 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [/Script/HardwareTargeting.HardwareTargetingSettings] 2 | TargetedHardwareClass=Desktop 3 | AppliedTargetedHardwareClass=Desktop 4 | DefaultGraphicsPerformance=Maximum 5 | AppliedDefaultGraphicsPerformance=Maximum 6 | 7 | [/Script/WmfMediaFactory.WmfMediaSettings] 8 | AllowNonStandardCodecs=True 9 | LowLatency=False 10 | NativeAudioOut=False 11 | HardwareAcceleratedVideoDecoding=True 12 | 13 | [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] 14 | bEnablePlugin=True 15 | bAllowNetworkConnection=True 16 | SecurityToken=F056F64C7547988CBCDDFAA1579C44AF 17 | bIncludeInShipping=False 18 | bAllowExternalStartInShipping=False 19 | bCompileAFSProject=False 20 | bUseCompression=False 21 | bLogFiles=False 22 | bReportStats=False 23 | ConnectionType=USBOnly 24 | bUseManualIPAddress=False 25 | ManualIPAddress= 26 | 27 | -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Config/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | [/Script/Engine.InputSettings] 2 | -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 3 | -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 4 | -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 5 | -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 6 | -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 7 | -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 8 | -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 9 | +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 10 | +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 11 | +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 12 | +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 13 | +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 14 | +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 15 | +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 16 | +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 17 | +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 18 | +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 19 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 20 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 21 | +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 22 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 23 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 24 | +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 25 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 26 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 27 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 28 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 29 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 30 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 31 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 32 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 33 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 34 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 35 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 36 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 37 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 38 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 39 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 40 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 41 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 42 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 43 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 44 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 45 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 46 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 47 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 48 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 49 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 50 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 51 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 52 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 53 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 54 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 55 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 56 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 57 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 58 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 59 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 60 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 61 | bAltEnterTogglesFullscreen=True 62 | bF11TogglesFullscreen=True 63 | bUseMouseForTouch=False 64 | bEnableMouseSmoothing=True 65 | bEnableFOVScaling=True 66 | bCaptureMouseOnLaunch=True 67 | bEnableLegacyInputScales=True 68 | bEnableMotionControls=True 69 | bFilterInputByPlatformUser=False 70 | bEnableInputDeviceSubsystem=True 71 | bShouldFlushPressedKeysOnViewportFocusLost=True 72 | bEnableDynamicComponentInputBinding=True 73 | bAlwaysShowTouchInterface=False 74 | bShowConsoleOnFourFingerTap=True 75 | bEnableGestureRecognizer=False 76 | bUseAutocorrect=False 77 | DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown 78 | DefaultViewportMouseLockMode=LockOnCapture 79 | FOVScale=0.011110 80 | DoubleClickTime=0.200000 81 | DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput 82 | DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent 83 | DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks 84 | -ConsoleKeys=Tilde 85 | +ConsoleKeys=Tilde 86 | 87 | -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.3/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.3/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.3/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.3/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.3/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "5.3", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | }, 27 | { 28 | "Name": "Bridge", 29 | "Enabled": true, 30 | "SupportedTargetPlatforms": [ 31 | "Win64", 32 | "Mac", 33 | "Linux" 34 | ] 35 | }, 36 | { 37 | "Name": "Shotgrid", 38 | "Enabled": true 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [/Script/HardwareTargeting.HardwareTargetingSettings] 2 | TargetedHardwareClass=Desktop 3 | AppliedTargetedHardwareClass=Desktop 4 | DefaultGraphicsPerformance=Maximum 5 | AppliedDefaultGraphicsPerformance=Maximum 6 | 7 | [/Script/WmfMediaFactory.WmfMediaSettings] 8 | AllowNonStandardCodecs=True 9 | LowLatency=False 10 | NativeAudioOut=False 11 | HardwareAcceleratedVideoDecoding=True 12 | 13 | [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] 14 | bEnablePlugin=True 15 | bAllowNetworkConnection=True 16 | SecurityToken=BEED8F838C4906410A3B87AEC0F60DA6 17 | bIncludeInShipping=False 18 | bAllowExternalStartInShipping=False 19 | bCompileAFSProject=False 20 | bUseCompression=False 21 | bLogFiles=False 22 | bReportStats=False 23 | ConnectionType=USBOnly 24 | bUseManualIPAddress=False 25 | ManualIPAddress= 26 | 27 | -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B6599CB54F66B6CFD93DC3A95CB55C3D 3 | -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Config/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | [/Script/Engine.InputSettings] 2 | -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 3 | -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 4 | -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 5 | -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 6 | -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 7 | -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 8 | -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 9 | +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 10 | +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 11 | +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 12 | +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 13 | +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 14 | +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 15 | +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 16 | +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 17 | +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 18 | +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 19 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 20 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 21 | +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 22 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 23 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 24 | +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 25 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 26 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 27 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 28 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 29 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 30 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 31 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 32 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 33 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 34 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 35 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 36 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 37 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 38 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 39 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 40 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 41 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 42 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 43 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 44 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 45 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 46 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 47 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 48 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 49 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 50 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 51 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 52 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 53 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 54 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 55 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 56 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 57 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 58 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 59 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 60 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 61 | bAltEnterTogglesFullscreen=True 62 | bF11TogglesFullscreen=True 63 | bUseMouseForTouch=False 64 | bEnableMouseSmoothing=True 65 | bEnableFOVScaling=True 66 | bCaptureMouseOnLaunch=True 67 | bEnableLegacyInputScales=True 68 | bEnableMotionControls=True 69 | bFilterInputByPlatformUser=False 70 | bEnableInputDeviceSubsystem=True 71 | bShouldFlushPressedKeysOnViewportFocusLost=True 72 | bEnableDynamicComponentInputBinding=True 73 | bAlwaysShowTouchInterface=False 74 | bShowConsoleOnFourFingerTap=True 75 | bEnableGestureRecognizer=False 76 | bUseAutocorrect=False 77 | DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown 78 | DefaultViewportMouseLockMode=LockOnCapture 79 | FOVScale=0.011110 80 | DoubleClickTime=0.200000 81 | DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput 82 | DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent 83 | DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks 84 | -ConsoleKeys=Tilde 85 | +ConsoleKeys=Tilde 86 | 87 | -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Content/turntable/level/turntable.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.4/turntable/Content/turntable/level/turntable.umap -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Content/turntable/level/turntable_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.4/turntable/Content/turntable/level/turntable_BuiltData.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Content/turntable/level/unlit_grey.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.4/turntable/Content/turntable/level/unlit_grey.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/Content/turntable/sequence/turntable_sequence.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/tk-framework-unrealqt/c092ea92e87fa50e6108f6e103ea06811bb8f416/resources/ueprojects/5.4/turntable/Content/turntable/sequence/turntable_sequence.uasset -------------------------------------------------------------------------------- /resources/ueprojects/5.4/turntable/turntable.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "5.4", 4 | "Category": "", 5 | "Description": "", 6 | "Enterprise": true, 7 | "Plugins": [ 8 | { 9 | "Name": "PythonScriptPlugin", 10 | "Enabled": true 11 | }, 12 | { 13 | "Name": "EditorScriptingUtilities", 14 | "Enabled": true 15 | }, 16 | { 17 | "Name": "MovieRenderPipeline", 18 | "Enabled": true 19 | }, 20 | { 21 | "Name": "AppleProResMedia", 22 | "Enabled": true, 23 | "SupportedTargetPlatforms": [ 24 | "Win64" 25 | ] 26 | }, 27 | { 28 | "Name": "Bridge", 29 | "Enabled": true, 30 | "SupportedTargetPlatforms": [ 31 | "Win64", 32 | "Mac", 33 | "Linux" 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /software_credits: -------------------------------------------------------------------------------- 1 | The Unreal Qt framework uses the following software. Thanks to their creators, license information below. 2 | 3 | ============================== PYSIDE ============================== 4 | 5 | PySide is licensed under the LGPL version 2.1 license, allowing both Free/Open source software 6 | and proprietary software development. 7 | 8 | ============================== Qt ============================== 9 | 10 | Qt can be used under open source (GPL v3 and LGPL v2.1) 11 | --------------------------------------------------------------------------------