New naming convention proposal #149
10
README.md
10
README.md
@ -7,3 +7,13 @@ The complete collection of documents, add-ons, scripts and tools that make up th
|
|||||||
Before checking out this repo, ensure that you have `git-lfs` installed and enabled (use `git lfs install` to verify this).
|
Before checking out this repo, ensure that you have `git-lfs` installed and enabled (use `git lfs install` to verify this).
|
||||||
|
|
||||||
To learn more see https://git-lfs.com/
|
To learn more see https://git-lfs.com/
|
||||||
|
|
||||||
|
#### Developer Tip
|
||||||
|
If you are working with a multiple remotes for this repository (e.g. fork and upstream) and you are receiving errors related to git lfs like `smudge filter lfs failed` you can try enabling autodetect in the repo's local git config with the following command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config lfs.remote.autodetect true
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Requirements
|
||||||
|
git lfs version: `3.3.0+`
|
@ -949,7 +949,7 @@ class MV_OT_next_media_file(bpy.types.Operator):
|
|||||||
area_fb = opsdata.find_area(context, "FILE_BROWSER")
|
area_fb = opsdata.find_area(context, "FILE_BROWSER")
|
||||||
ctx = opsdata.get_context_for_area(area_fb)
|
ctx = opsdata.get_context_for_area(area_fb)
|
||||||
with context.temp_override(**ctx):
|
with context.temp_override(**ctx):
|
||||||
bpy.ops.file.select_walk(ctx, "INVOKE_DEFAULT", direction=self.direction)
|
bpy.ops.file.select_walk("INVOKE_DEFAULT", direction=self.direction)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
# Get all files and folders and sort them alphabetically.
|
# Get all files and folders and sort them alphabetically.
|
||||||
|
@ -51,16 +51,50 @@ export default defineConfig({
|
|||||||
items: [
|
items: [
|
||||||
{ text: 'Introduction', link: '/pipeline-overview/introduction'},
|
{ text: 'Introduction', link: '/pipeline-overview/introduction'},
|
||||||
{ text: 'Design Principles', link: '/pipeline-overview/design-principles'},
|
{ text: 'Design Principles', link: '/pipeline-overview/design-principles'},
|
||||||
{ text: 'Infrastructure', link: '/pipeline-overview/infrastructure'},
|
{
|
||||||
{ text: 'Editorial and Previz', link: '/pipeline-overview/editorial-and-previz'},
|
text: 'Organization',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Infrastructure', link: '/pipeline-overview/organization/infrastructure'},
|
||||||
|
{ text: 'Task Review', link: '/pipeline-overview/organization/task-review'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Pre-Production',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Storyboard', link: '/pipeline-overview/pre-production/storyboard'},
|
||||||
|
{ text: 'Editorial', link: '/pipeline-overview/pre-production/editorial'},
|
||||||
|
{ text: 'Previz', link: '/pipeline-overview/pre-production/previz'},
|
||||||
|
{ text: 'Research and Development', link: '/pipeline-overview/pre-production/research-and-development'},
|
||||||
|
{ text: 'Concept and Design', link: '/pipeline-overview/pre-production/concept-and-design'},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Asset Creation',
|
text: 'Asset Creation',
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Modeling', link: '/pipeline-overview/asset-creation/modeling'},
|
{ text: 'Modeling and Sculpting', link: '/pipeline-overview/asset-creation/modeling'},
|
||||||
|
{ text: 'Shading', link: '/pipeline-overview/asset-creation/shading'},
|
||||||
{ text: 'Rigging', link: '/pipeline-overview/asset-creation/rigging'},
|
{ text: 'Rigging', link: '/pipeline-overview/asset-creation/rigging'},
|
||||||
|
{ text: 'Animation Testing', link: '/pipeline-overview/asset-creation/animation-testing'},
|
||||||
|
{ text: '2D Assets', link: '/pipeline-overview/asset-creation/2d-assets'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'Shot Production',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Shot Assembly', link: '/pipeline-overview/shot-production/shot-assembly'},
|
||||||
|
{ text: 'Layout', link: '/pipeline-overview/shot-production/layout'},
|
||||||
|
{ text: 'Animation', link: '/pipeline-overview/shot-production/animation'},
|
||||||
|
{ text: 'Lighting', link: '/pipeline-overview/shot-production/lighting'},
|
||||||
|
{ text: 'Effects', link: '/pipeline-overview/shot-production/effects'},
|
||||||
|
{ text: 'Rendering', link: '/pipeline-overview/shot-production/rendering'},
|
||||||
|
{ text: 'Coloring', link: '/pipeline-overview/shot-production/coloring'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ text: 'Publishing', link: '/pipeline-overview/publishing'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -93,46 +127,42 @@ export default defineConfig({
|
|||||||
{ text: 'File Types', link: '/naming-conventions/file-types'},
|
{ text: 'File Types', link: '/naming-conventions/file-types'},
|
||||||
{ text: 'In-file Prefixes', link: '/naming-conventions/in-file-prefixes'},
|
{ text: 'In-file Prefixes', link: '/naming-conventions/in-file-prefixes'},
|
||||||
{ text: 'Examples', link: '/naming-conventions/examples'},
|
{ text: 'Examples', link: '/naming-conventions/examples'},
|
||||||
|
{ text: 'Shared Folder Structure', link: '/naming-conventions/shared-folder-structure'},
|
||||||
|
{ text: 'SVN Folder Structure', link: '/naming-conventions/svn-folder-structure'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'User Guide',
|
text: 'User Guide',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
items: [
|
items: [
|
||||||
{text: 'Project Setup', link: '/user-guide/project-setup'},
|
|
||||||
{
|
{
|
||||||
text: 'Workstation',
|
text: 'Project Tools',
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Introduction', link: '/user-guide/workstations/introduction'},
|
{ text: 'Project Overview', link: '/user-guide/project_tools/project-overview' },
|
||||||
{ text: 'Installing Software', link: '/user-guide/workstations/installing-software'},
|
{ text: 'Project Usage', link: '/user-guide/project_tools/project-usage'},
|
||||||
{ text: 'Running Blender', link: '/user-guide/workstations/running-blender'},
|
{ text: 'Project Blender', link: '/user-guide/project_tools/project-blender' },
|
||||||
{ text: 'Troubleshooting', link: '/user-guide/workstations/troubleshooting'},
|
],
|
||||||
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{text: 'SVN', link: '/user-guide/svn'},
|
|
||||||
{text: 'Debugging', link: '/user-guide/debugging'},
|
|
||||||
{text: 'Kitsu', link: '/user-guide/kitsu'}
|
|
||||||
|
|
||||||
]
|
{
|
||||||
|
text: 'Organization',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Planning', link: '/user-guide/organization/planning' },
|
||||||
|
{ text: 'Task Review', link: '/user-guide/organization/task-review' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
{ text: 'Debugging', link: '/user-guide/debugging' },
|
||||||
|
{ text: 'Kitsu', link: '/user-guide/kitsu' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
text: 'TD Guide',
|
text: 'TD Guide',
|
||||||
collapsed: false,
|
|
||||||
items: [
|
|
||||||
{text: 'Project Setup', link: '/td-guide/project-setup'},
|
|
||||||
{
|
|
||||||
text: 'Workstation',
|
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Overview', link: '/td-guide/workstations/overview'},
|
{text: 'Project Tools Setup', link: '/td-guide/project-tools-setup'},
|
||||||
{ text: 'Installation', link: '/td-guide/workstations/installation'},
|
|
||||||
{ text: 'Maintenance', link: '/td-guide/workstations/maintaince'},
|
|
||||||
{ text: 'Render Farm', link: '/td-guide/workstations/render_farm'},
|
|
||||||
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -152,7 +182,36 @@ export default defineConfig({
|
|||||||
{text: 'Asset Publishing', link: '/archive/pipeline-proposal-2019/asset-publishing/introduction'},
|
{text: 'Asset Publishing', link: '/archive/pipeline-proposal-2019/asset-publishing/introduction'},
|
||||||
{text: 'Character Pipeline Assistant', link: '/archive/pipeline-proposal-2019/asset-publishing/character-pipeline-assistant'},
|
{text: 'Character Pipeline Assistant', link: '/archive/pipeline-proposal-2019/asset-publishing/character-pipeline-assistant'},
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
text: 'Gentoo',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
text: 'TD',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: 'Overview', link: '/gentoo/td/overview'},
|
||||||
|
{ text: 'Installation', link: '/gentoo/td/installation'},
|
||||||
|
{ text: 'Maintenance', link: '/gentoo/td/maintaince'},
|
||||||
|
{ text: 'Render Farm', link: '/gentoo/td/render_farm'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
text: 'User',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{ text: 'Introduction', link: '/gentoo/user/introduction' },
|
||||||
|
{ text: 'Installing Software', link: '/gentoo/user/installing-software' },
|
||||||
|
{ text: 'Running Blender', link: '/gentoo/user/running-blender' },
|
||||||
|
{ text: 'SVN', link: '/gentoo/user/svn' },
|
||||||
|
{ text: 'Troubleshooting', link: '/gentoo/user/troubleshooting' },
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
markdown: {
|
markdown: {
|
||||||
|
@ -1,6 +1,20 @@
|
|||||||
# Blender Studio Pipeline Docs
|
# Blender Studio Pipeline Docs
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
To setup the environment to work on Blender Studio Pipeline Docs.
|
||||||
|
|
||||||
* Make sure you have NodeJS installed
|
* Make sure you have NodeJS installed
|
||||||
|
* `cd blender-studio-pipeline/docs/`
|
||||||
* `npm install`
|
* `npm install`
|
||||||
|
|
||||||
|
## Review Changes
|
||||||
|
To review your changes by generating a preview of the site, this preview will update as users save any changes to the site's content.
|
||||||
|
|
||||||
* `npm run docs:dev`
|
* `npm run docs:dev`
|
||||||
* Refer to the [vitepress docs](https://vitepress.dev/) for how to edit the documents
|
* Refer to the [vitepress docs](https://vitepress.dev/guide/getting-started#up-and-running) for how to edit the documents
|
||||||
|
|
||||||
|
## Test Changes
|
||||||
|
To ensure the site will build on production site without errors.
|
||||||
|
|
||||||
|
* `npm run docs:build`
|
||||||
|
* Refer to the [vitapress docs](https://vitepress.dev/guide/deploy#build-and-test-locally) for more info
|
@ -1,2 +1 @@
|
|||||||
<!--@include: ../../scripts-blender/README.md-->
|
<!--@include: ../../scripts/pipeline-release/overview.md-->
|
||||||
|
|
@ -8,11 +8,19 @@ To update Client Workstations; the Build Server will pull all of the latest chan
|
|||||||
3. `cd` to change directory to the root home folder
|
3. `cd` to change directory to the root home folder
|
||||||
4. Run `./update_build_server.sh`
|
4. Run `./update_build_server.sh`
|
||||||
|
|
||||||
|
## Update custom package recipes on Build Server
|
||||||
|
Some applications on Blender Studio workstations are not included in the main Gentoo repository
|
||||||
|
and are instead sourced from an external repository. To learn more about how external repositories
|
||||||
|
are used in Gentoo see the [Gentoo Handbook](https://wiki.gentoo.org/wiki/Ebuild_repository#Repository_synchronization). This is known as an overlay repository
|
||||||
|
[Blender Studio Overlay Repo](https://projects.blender.org/ZedDB/blender-studio-overlay). To update the recipe for an existing package in an overlay repo follow the steps below.
|
||||||
|
1. Commit changes to the package recipes on the [Blender Studio Overlay Repo](https://projects.blender.org/ZedDB/blender-studio-overlay)
|
||||||
|
2. `ssh user@build-server-addr` connect to your build server via ssh
|
||||||
|
3. Update the package recipes on the Build Server `emaint sync --repo blender-studio-overlay`
|
||||||
|
|
||||||
## Update Add-ons in `/shared/software/addons`
|
Once an update of the recipe is complete any affected packages can be installed our updated by following the [Installing Packages on Build Server](/gentoo/td/maintaince#installing-packages-on-build-server) guide.
|
||||||
|
|
||||||
The software inside the `shared/software/addons` directory are the [Blender Studio Pipeline Add-ons](/addons/overview) and any other Add-ons that need to be distributed to all Blender Studio Users. These Add-ons are considered Live Packages. Live Packages are packages that fetch updates from the source repository directly and are not tied to a specific release. To update packages not included in `/shared/software/addons` see [Installing Software](/user-guide/workstations/installing-software)
|
|
||||||
|
|
||||||
|
## Installing Packages on Build Server
|
||||||
|
Packages need to be compiled on the Build Server and marked with a date to be able to discovered by the workstations. Follow the below steps to install/update a package on the build server.
|
||||||
1. `ssh user@build-server-addr` connect to your build server via ssh
|
1. `ssh user@build-server-addr` connect to your build server via ssh
|
||||||
2. Use `su` to Login as root or login as root directly
|
2. Use `su` to Login as root or login as root directly
|
||||||
3. Run `emerge --oneshot {package-name}` to update a live package.
|
3. Run `emerge --oneshot {package-name}` to update a live package.
|
||||||
@ -22,6 +30,17 @@ To update Client Workstations; the Build Server will pull all of the latest chan
|
|||||||
::: info Info
|
::: info Info
|
||||||
The command `emerge --oneshot {package-name}` compiles package, but does not add the packages to the [@world](https://wiki.gentoo.org/wiki/World_set_(Portage)), this means these packages will be removed when running `--depclean`.. We add this because these packages are already pulled in by another set. So we don’t want to add it again to the @world set. To learn more visit the [Gentoo Handbook](https://wiki.gentoo.org/wiki/Emerge#:~:text=fetchonly%20%2D%2Demptytree%20%40world-,Do%20not%20add%20dependencies%20to%20the%20world%20file,-If%20a%20dependency)
|
The command `emerge --oneshot {package-name}` compiles package, but does not add the packages to the [@world](https://wiki.gentoo.org/wiki/World_set_(Portage)), this means these packages will be removed when running `--depclean`.. We add this because these packages are already pulled in by another set. So we don’t want to add it again to the @world set. To learn more visit the [Gentoo Handbook](https://wiki.gentoo.org/wiki/Emerge#:~:text=fetchonly%20%2D%2Demptytree%20%40world-,Do%20not%20add%20dependencies%20to%20the%20world%20file,-If%20a%20dependency)
|
||||||
:::
|
:::
|
||||||
|
## Update Local Add-Ons
|
||||||
|
|
||||||
|
Add-Ons are locally stored in the following directories; `/usr/share/flamenco` and `/usr/share/blender_studio_tools`. These are updated by running the following commands. These directories are automatically updated daily by the Gentoo package manager.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
emerge -1 blender-studio-tools
|
||||||
|
emerge -1 flamenco
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### How to update to specific version?
|
### How to update to specific version?
|
||||||
In some cases, users may want to specify what version of an add-on to deploy into the `/shared/software/addons` folder. Users can accomplish this using `eclass` variables found in the [Gentoo Devmanual](https://devmanual.gentoo.org/eclass-reference/git-r3.eclass/index.html#:~:text=more%20creative%20ways.-,EGIT_BRANCH,-The%20branch%20name).
|
In some cases, users may want to specify what version of an add-on to deploy into the `/shared/software/addons` folder. Users can accomplish this using `eclass` variables found in the [Gentoo Devmanual](https://devmanual.gentoo.org/eclass-reference/git-r3.eclass/index.html#:~:text=more%20creative%20ways.-,EGIT_BRANCH,-The%20branch%20name).
|
@ -43,7 +43,7 @@ The Blender add-on also needs to be reloaded, make sure that Blender is restarte
|
|||||||
|
|
||||||
This section will only provide a quick rundown of how to update the Flamenco package that is part of the Render Farm setup.
|
This section will only provide a quick rundown of how to update the Flamenco package that is part of the Render Farm setup.
|
||||||
|
|
||||||
For more details and how to update other packages, see the [Maintenance](/td-guide/workstations/maintaince) page.
|
For more details and how to update other packages, see the [Maintenance](/gentoo/td/maintaince) page.
|
||||||
|
|
||||||
::: info Note
|
::: info Note
|
||||||
The Flamenco package should already have the correct [USE flags](https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/USE) configured.
|
The Flamenco package should already have the correct [USE flags](https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/USE) configured.
|
@ -22,7 +22,7 @@ emerge is the default package manager, use emerge to install, update, and genera
|
|||||||
|
|
||||||
**emerge** provides commands to install packages onto your system including any dependencies. You must be **root** to make changes via **emerge**. _In this example package name is **spotify**._
|
**emerge** provides commands to install packages onto your system including any dependencies. You must be **root** to make changes via **emerge**. _In this example package name is **spotify**._
|
||||||
|
|
||||||
1. [Open Terminal and Become Root](/user-guide/workstations/installing-software.md#open-terminal-and-become-root)
|
1. [Open Terminal and Become Root](/gentoo/user/installing-software.md#open-terminal-and-become-root)
|
||||||
2. Find avaliable packages using the `eix` for example: `eix spotify`
|
2. Find avaliable packages using the `eix` for example: `eix spotify`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -68,7 +68,7 @@ Dependency resolution took 8.40 s.
|
|||||||
|
|
||||||
In this example package name is **spotify**.
|
In this example package name is **spotify**.
|
||||||
|
|
||||||
1. [Open Terminal and Become Root](/user-guide/workstations/installing-software.md#open-terminal-and-become-root)
|
1. [Open Terminal and Become Root](/gentoo/user/installing-software.md#open-terminal-and-become-root)
|
||||||
2. Remove a package and it’s dependacies safely.
|
2. Remove a package and it’s dependacies safely.
|
||||||
1. `emerge --deselect spotify`
|
1. `emerge --deselect spotify`
|
||||||
2. `emerge --depclean`
|
2. `emerge --depclean`
|
@ -1,6 +1,9 @@
|
|||||||
# Working with Blender
|
# Working with Blender
|
||||||
|
|
||||||
## Launch Blender from Taskbar/Start Menu
|
## Launch Blender from Taskbar/Start Menu
|
||||||
|
This will launch the Blender that is pre-installed on your workstation and updated daily via the package manager. Your Blender configuration including add-ons is stored at `$HOME/.config/blender/{version}/`
|
||||||
|
|
||||||
|
|
||||||
1. Search for Blender in your Application Launcher
|
1. Search for Blender in your Application Launcher
|
||||||
2. The application with the exact name `blender` without any suffix is the latest build.
|
2. The application with the exact name `blender` without any suffix is the latest build.
|
||||||
![Image of Blender Icon in KDE Taskbar/Start Menu](/media/user-guide/launch_blender.mp4)
|
![Image of Blender Icon in KDE Taskbar/Start Menu](/media/user-guide/launch_blender.mp4)
|
||||||
@ -9,6 +12,9 @@
|
|||||||
1. Open a new Terminal window
|
1. Open a new Terminal window
|
||||||
2. Type command `blender`
|
2. Type command `blender`
|
||||||
## Roll Back Blender Build
|
## Roll Back Blender Build
|
||||||
|
|
||||||
|
Your workstation's package manager will update blender daily, to roll back to a previous version follow the guide below.
|
||||||
|
|
||||||
1. Login as ********root******** user using `su`
|
1. Login as ********root******** user using `su`
|
||||||
2. Run `rollback_blender.sh` from any folder
|
2. Run `rollback_blender.sh` from any folder
|
||||||
|
|
||||||
@ -28,7 +34,7 @@
|
|||||||
```
|
```
|
||||||
3. Select a build by enter a number and pressing Enter to confirm
|
3. Select a build by enter a number and pressing Enter to confirm
|
||||||
|
|
||||||
## Build Blender Locallly
|
## Build Blender Locally
|
||||||
|
|
||||||
1. Download Git repo
|
1. Download Git repo
|
||||||
|
|
||||||
@ -47,3 +53,11 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. Find your built blender `cd ../build_linux`
|
3. Find your built blender `cd ../build_linux`
|
||||||
|
|
||||||
|
## Debug Build
|
||||||
|
1. Search for Blender in your Application Launcher
|
||||||
|
2. The application with the exact name `blender debug` without any suffix is the latest build, it should have a grey icon.
|
||||||
|
|
||||||
|
<!---
|
||||||
|
TODO Add Image of Debug Build on Gentoo
|
||||||
|
--->
|
@ -16,11 +16,11 @@ _If successful, you will be greated with the following screen_
|
|||||||
![IPXE Boot Menu](/media/user-guide/workstations/gentoo_boot_menu.png)
|
![IPXE Boot Menu](/media/user-guide/workstations/gentoo_boot_menu.png)
|
||||||
|
|
||||||
## Run MEM Test
|
## Run MEM Test
|
||||||
1. [Boot into IPXE](/user-guide/workstations/troubleshooting.md#run-mem-test)
|
1. [Boot into IPXE](/gentoo/user/troubleshooting.md#run-mem-test)
|
||||||
2. Select “Run MemTest”
|
2. Select “Run MemTest”
|
||||||
|
|
||||||
## Chrooting into the root drive (for recovery)
|
## Chrooting into the root drive (for recovery)
|
||||||
1. [Boot into IPXE](/user-guide/workstations/troubleshooting.md#run-mem-test)
|
1. [Boot into IPXE](/gentoo/user/troubleshooting.md#run-mem-test)
|
||||||
2. Select Gentoo Installer (Blender Institute Customized)
|
2. Select Gentoo Installer (Blender Institute Customized)
|
||||||
3. Wait for Gentoo Installer to boot ![Gentoo Installer](/media/user-guide/workstations/gentoo_installer_boot.png)
|
3. Wait for Gentoo Installer to boot ![Gentoo Installer](/media/user-guide/workstations/gentoo_installer_boot.png)
|
||||||
4. Use CTR+C to cancel Installer
|
4. Use CTR+C to cancel Installer
|
||||||
@ -30,7 +30,7 @@ _If successful, you will be greated with the following screen_
|
|||||||
|
|
||||||
|
|
||||||
## Recovery/Inspection of Linux Kernel
|
## Recovery/Inspection of Linux Kernel
|
||||||
1. [Follow steps in Chrooting into the root drive](/user-guide/workstations/troubleshooting.md#chrooting-into-the-root-drive-for-recovery)
|
1. [Follow steps in Chrooting into the root drive](/gentoo/user/troubleshooting.md#chrooting-into-the-root-drive-for-recovery)
|
||||||
2. Ensure the mount the boot drive is mounted **`mount /boot`**
|
2. Ensure the mount the boot drive is mounted **`mount /boot`**
|
||||||
3. Go into the kernel source dir **`cd /usr/src/linux`**
|
3. Go into the kernel source dir **`cd /usr/src/linux`**
|
||||||
4. Use `make menuconfig` to enter linux kernel configuration
|
4. Use `make menuconfig` to enter linux kernel configuration
|
@ -11,7 +11,7 @@ hero:
|
|||||||
link: /pipeline-overview/introduction
|
link: /pipeline-overview/introduction
|
||||||
- theme: alt
|
- theme: alt
|
||||||
text: User Guide
|
text: User Guide
|
||||||
link: /user-guide/project-setup
|
link: /user-guide/project_tools/project-overview
|
||||||
|
|
||||||
features:
|
features:
|
||||||
- title: Free Software
|
- title: Free Software
|
||||||
|
BIN
docs/media/addons/blender_kitsu/Shot_as_Image_Sequence.jpg
(Stored with Git LFS)
Normal file
BIN
docs/media/addons/blender_kitsu/Shot_as_Image_Sequence.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/media/pipeline-overview/shot-production/Pets_Previz__Meta_Strips.png
(Stored with Git LFS)
Normal file
BIN
docs/media/pipeline-overview/shot-production/Pets_Previz__Meta_Strips.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/media/pipeline-overview/shot-production/frame_range_out_of_date.png
(Stored with Git LFS)
Normal file
BIN
docs/media/pipeline-overview/shot-production/frame_range_out_of_date.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/media/pipeline-overview/shot-production/new_shot_file.png
(Stored with Git LFS)
Normal file
BIN
docs/media/pipeline-overview/shot-production/new_shot_file.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/media/td-guide/kitsu_pref.jpg
(Stored with Git LFS)
Normal file
BIN
docs/media/td-guide/kitsu_pref.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
docs/media/td-guide/render_review_pref.jpg
(Stored with Git LFS)
Normal file
BIN
docs/media/td-guide/render_review_pref.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -2,37 +2,35 @@
|
|||||||
|
|
||||||
## Assets
|
## Assets
|
||||||
|
|
||||||
```txt
|
```plaintext
|
||||||
- bark_oak.normal.png
|
bark_oak_normal.png
|
||||||
- maps/bark/bark_oak.normal.png
|
bark_oak_albedo.png
|
||||||
- maps/bark/bark_oak.albedo.png
|
bark_birch_normal.png
|
||||||
- maps/bark/bark_birch.normal.png
|
spring-anim_test.blend
|
||||||
- bark_oak.albedo.png
|
|
||||||
- spring.anim_test.blend
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Shots
|
## Shots
|
||||||
|
|
||||||
```txt
|
```plaintext
|
||||||
- 010_110_A.anim.blend
|
010_110-anim.blend
|
||||||
- 010_110_A.anim.mp4
|
010_110-compositing.blend
|
||||||
```
|
```
|
||||||
|
|
||||||
## Export of animation preview
|
## Export of animation preview
|
||||||
|
|
||||||
```txt
|
```plaintext
|
||||||
- 011_1140_A.anim.v001.mp4
|
011_1140-anim-v001.mp4
|
||||||
- 015_1050_A.anim.v001.mp4
|
015_1050-anim-v001.mp4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
```txt
|
```plaintext
|
||||||
- sf-edit.v002.voice_recording.blend
|
sf-edit-v002_voice_recording.blend
|
||||||
- sf-edit.v002.blend
|
sf-edit-v002.blend
|
||||||
- sf-edit.v002.mov
|
sf-edit-v002.mov
|
||||||
- sf-edit.v002.noblur.mkv
|
sf-edit-v002_noblur.mkv
|
||||||
- sf-edit.v002.720p.mkv
|
sf-edit-v002_720p.mkv
|
||||||
- sf-edit.v002.voices.lossy.mp3
|
sf-edit-v002_voices_lossy.mp3
|
||||||
```
|
```
|
||||||
|
@ -1,49 +1,45 @@
|
|||||||
# File Naming
|
# File Naming
|
||||||
|
|
||||||
::: warning Work in Progress
|
|
||||||
30 Apr. 2023 - The content of this page is currently being edited/updated.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Characters
|
## Characters
|
||||||
|
|
||||||
**Location:** `{project root}/lib/char/{char subfolder}/{char name}.blend`
|
**Location:** `{project root}/pro/assets/char/{char subfolder}/{type}-{char name}-{task}.blend`
|
||||||
|
|
||||||
A character asset is a collection which contains shaded geometry, rigs, rigging objects such as mesh deformers and lattices. All of these are needed to give animators control over their movement and make up the final rendered representation of the character in the movie
|
A character asset is a collection which contains shaded geometry, rigs, rigging objects such as mesh deformers and lattices. All of these are needed to give animators control over their movement and make up the final rendered representation of the character in the movie
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
**Location:** `{project root}/lib/prop/{prop subfolder}/{prop name}.blend`
|
**Location:** `{project root}/pro/assets/prop/{prop subfolder}/{type}-{prop name}-{task}.blend`
|
||||||
|
|
||||||
A prop in real life is a rigged, animatable object which characters interact with or can be constrained to. An environment library asset can sometimes be turned into a prop (e.g. Autumn picks up a branch from the ground and breaks it into pieces).
|
A prop in real life is a rigged, animatable object which characters interact with or can be constrained to. An environment library asset can sometimes be turned into a prop (e.g. Autumn picks up a branch from the ground and breaks it into pieces).
|
||||||
|
|
||||||
## Environment library assets
|
## Libraries
|
||||||
|
|
||||||
**Location:** `{project root}/lib/env/{env subfolder}/{env name}.blend`
|
**Location:** `{project root}/pro/assets/lib/{lib subfolder}/{type}-{lib name}-{task}.blend`
|
||||||
|
|
||||||
The building blocks needed to turn over sets and construct the movie stage. These are either static, or can be animated in a more limited way than props. Typically a larger set of them is needed with different variations, so we group them into asset library files. For example on *Spring* there was **trees.blend, rocks.blend**, etc. Each asset is in its own collection, but is also placed in a library collection that groups them together for easier linking access.
|
A library is a collection of assets that form a coherent group and are supposed to be used in sets. They are **non-rigged**. A lib file can have one asset or multiples.
|
||||||
|
Ask yourself the question: not a set, not a char, no rig? → library asset
|
||||||
|
|
||||||
## Sets
|
## Sets
|
||||||
|
|
||||||
**Location:** `{project root}/lib/set/{set subfolder}/{set name}.blend`
|
**Location:** `{project root}/pro/assets/set/{set subfolder}/{type}-{set name}-{task}.blend`
|
||||||
|
|
||||||
Sets are more tricky to define, since they can differ even on a shot level. In practice they are formed by a ground plane and dressed with environment assets. They can also contain individually created assets (*Spring* example: `riverbed.blend` contained two non-library trees which were modelled and shaded uniquely in this set). Sets can be connected together to build a larger world if necessary. Sets are contained in a main collection which has sub-collections for visibility management)
|
Sets are more tricky to define, since they can differ even on a shot level. In practice they are formed by a ground plane and dressed with environment assets. They can also contain individually created assets (*Spring* example: `riverbed.blend` contained two non-library trees which were modelled and shaded uniquely in this set). Sets can be connected together to build a larger world if necessary. Sets are contained in a main collection which has sub-collections for visibility management)
|
||||||
|
|
||||||
## Shots
|
|
||||||
|
|
||||||
**Location:** `{project root}**/**shots**/**{sequence number}**/**{shot identifier}/{shot identifier}.{task identifier}.blend`
|
|
||||||
|
|
||||||
## Textures and maps
|
## Textures and maps
|
||||||
|
|
||||||
**Location:**
|
**Location:**
|
||||||
|
- `{project root}/pro/assets/maps/` for general textures which are used across the entire project
|
||||||
- `{project root}/lib/maps/` for general textures which are used across the entire project
|
|
||||||
- `{asset folder}/maps/` for specific textures related to an asset
|
- `{asset folder}/maps/` for specific textures related to an asset
|
||||||
|
|
||||||
|
|
||||||
|
## Shots
|
||||||
|
|
||||||
Example: `dresser_wood.faded.col.png`
|
**Location:** `{project root}/pro/shots/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||||
|
|
||||||
|
##
|
||||||
|
Example: `prop-dresser_wood.faded-modeling.png`
|
||||||
|
|
||||||
- As for all other files, format the entire name in lowercase, separated by `_` instead of spaces.
|
- As for all other files, format the entire name in lowercase, separated by `_` instead of spaces.
|
||||||
- Textures which are specific to a prop should include the name of the prop and THEN the name (if it's a label, or tex type such as metal or wood e.g.)
|
- Maps which are specific to a prop should include the name of the prop and THEN the name (if it's a label, or tex type such as metal or wood e.g.)
|
||||||
- If there's more than one type, Textures should also include the type (col, bump, nor, disp, spec) LAST in the filename (separated by a dot)
|
- If there's more than one type, Maps should also include the type (col, bump, nor, disp, spec) LAST in the filename (separated by a `_`)
|
||||||
- make sure to clean up texture file names coming from the Blender Cloud according to these conventions. Sometimes there can be a .png.png at the end or files can have an uppercase first letter.
|
- make sure to clean up map file names coming from the Blender Studio Website according to these conventions. Sometimes there can be a .png.png at the end or files can have an uppercase first letter.
|
@ -23,7 +23,7 @@ The identifier can also have underscores and numbers in it, which may be the cas
|
|||||||
We use prefixes for the root collections (and only the root) of assets to help distinguish different types of assets in the Outliner of a complex shot file. The name of the asset itself should be lowercase.
|
We use prefixes for the root collections (and only the root) of assets to help distinguish different types of assets in the Outliner of a complex shot file. The name of the asset itself should be lowercase.
|
||||||
|
|
||||||
- `CH` : Character
|
- `CH` : Character
|
||||||
- `PR` : Rigger Prop
|
- `PR` : Rigged Prop
|
||||||
- `LI` : Library/Environment Asset
|
- `LI` : Library/Environment Asset
|
||||||
- `SE` : Set
|
- `SE` : Set
|
||||||
- `LG` : Light Rig
|
- `LG` : Light Rig
|
||||||
|
@ -1,30 +1,42 @@
|
|||||||
# Naming Conventions
|
# Naming Conventions
|
||||||
|
|
||||||
Naming things well is notoriousy difficult, mostly because a name that works well within one context does not communicate as well in another. Here are the guidelines that we try to follow at Blender Studio when it comes to file naming.
|
Naming things well is notoriousy difficult, mostly because a name that works well within one context, does not communicate as well in another. Here are the guidelines that we try to follow at Blender Studio when it comes to file naming.
|
||||||
|
|
||||||
* No caps, no gaps: Name files with lowercase letter and avoid whitespaces.
|
* *No caps, no gaps*: name files with lowercase letter and avoid whitespaces.
|
||||||
* Retain a sense of hierarchy within the file name, from wide to narrow context
|
* No special characters like +=#^*&^$()?!
|
||||||
|
* Underscores are allowed as spacing, dashes to separate items.
|
||||||
|
* Retain a sense of hierarchy within the file name, from wide to narrow context.
|
||||||
|
* We use dot notations to specify a different representation or variant of an asset.
|
||||||
|
|
||||||
## Versions and variations for file naming
|
## Versions and variations for file naming
|
||||||
|
|
||||||
`{show_prefix:optional}-{name}-v{version:optional}.{variant:optional}.{representation:optional}.{extension}`
|
`{show_prefix:optional}-{type:only for assets}-{name}.{variant:optional}-{task}-v{version:optional}_{version_info:optional}.{extension}`
|
||||||
|
|
||||||
- version (v001, v002)
|
- **type** (char, set, lib, props) of asset - not for shots
|
||||||
- variant (voice, red, blurry) changes meaning of the content
|
- **variant** (voice, red, blurry) changes meaning of the content > only for exports and renders, not for work files
|
||||||
- representation (720p, lowres, cache) the content is the same
|
- **version** (v001, v002) only for renders and exports, not for work files as they have a history on the SVN
|
||||||
|
- **version_info** (720p, lowres, cache) the content is the same
|
||||||
|
|
||||||
|
|
||||||
## Asset file names
|
## Asset file names
|
||||||
|
|
||||||
Examples: **gabby.blend, gabby.shading.blend, gabby.rigging.blend**
|
Examples:
|
||||||
|
* `char-gabby-concept.blend`
|
||||||
|
* `char-gabby-shading-v001.mp4` > we add the `{version}` because it is an export/render, like a turntable
|
||||||
|
* `char-gabby-rigging.blend`
|
||||||
|
* `char-gabby.red-shading-v006.png` > we can include the `.{variant}` because it is an export/render
|
||||||
|
* `lib-sea_shells-design-v001.kra`
|
||||||
|
* `lib-sea_shells.broken-modeling-v006.png` > we can include the `.{variant}` because it is an export/render
|
||||||
|
|
||||||
The main asset files should be as simple as possible: {asset name}.blend. These main asset files are meant to be written by our asset pipeline, but should rarely be touched by hand (other than solving errors). Data is consolidated
|
The main asset files should be as simple as possible, however we have decided to make it longer than our previous system, to better classify and structure them: `{type}-{asset name}-{task}.blend`
|
||||||
|
|
||||||
We use task identifiers to distinguish between the different stages of the asset. {asset name}.{task identifier}.blend.
|
These main asset files are meant to be written by our asset pipeline, but should rarely be touched by hand (other than solving errors).
|
||||||
|
|
||||||
task identifiers:
|
We use task identifiers to distinguish between the different stages of the asset:
|
||||||
|
|
||||||
|
- reference
|
||||||
- concept
|
- concept
|
||||||
|
- design
|
||||||
- sculpting
|
- sculpting
|
||||||
- modeling
|
- modeling
|
||||||
- shading
|
- shading
|
||||||
@ -32,63 +44,27 @@ task identifiers:
|
|||||||
|
|
||||||
## Shot identifier
|
## Shot identifier
|
||||||
|
|
||||||
E.g. `**010_0030_A** {scene number}_{shot number}_{variant}`
|
Example: `010_0030`
|
||||||
|
```
|
||||||
**Scene:** 3 digits, incremented by 10 at first creation
|
010_0030
|
||||||
|
─┬─ ─┬──
|
||||||
**Shot number:** 4 digits, incremented by 10 at first creation
|
│ └──► Shot: 4 digits, incremented by 10 at creation
|
||||||
|
└──► Sequence: 3 digits, incremented by 10 at creation
|
||||||
**Variant:** Letters A - Z
|
```
|
||||||
|
|
||||||
## Shot file name
|
## Shot file name
|
||||||
|
|
||||||
- No uppercase letters except for naming shot file takes (100_0002_A.anim.blend)
|
Example: `140_0010-anim.blend`
|
||||||
- No special characters like +=#^*&^$()?!
|
|
||||||
- Underscores are allowed as spacing, no actual spaces or - as separators (no caps, no gaps!)
|
|
||||||
- We use dot notations to specify a different representation or variant of an asset (hairdryer.more_ugly.blend)
|
|
||||||
|
|
||||||
Example: `**140_0010_B.anim.blend**`
|
```
|
||||||
|
010_0030-anim.blend
|
||||||
- **140** : scene
|
─┬─ ─┬── ──┬─
|
||||||
- **0010** : shot
|
│ │ └──► Task (layout, anim, lighting, comp, fx, sim_hair, etc.)
|
||||||
- **A**: variant (only one variant per shot is picked in the edit)
|
│ └────────► Shot
|
||||||
- **anim**: task (layout, anim, lighting, comp, sim_hair, sim_fluid, sim_smoke)
|
└────────────► Sequence
|
||||||
|
```
|
||||||
|
|
||||||
Its position in the repository would be at:
|
Its position in the repository would be at:
|
||||||
|
|
||||||
`pro/**shots/140_credits/140_0010_A/140_0010_A.anim.blend**`
|
`svn/pro/shots/140_credits/140_0010/140_0010-anim.blend`
|
||||||
|
|
||||||
Output for the animation playblasts at:
|
|
||||||
|
|
||||||
`/render/**shots/140_credits/140_0010_A/140_0010_A.anim/140_0010_A.anim.v001.mp4**`
|
|
||||||
|
|
||||||
`/render/**shots/140_credits/140_0010_A/140_0010_A.anim/140_0010_A.anim.v002.mp4**`
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
Output of the rendered frames:
|
|
||||||
|
|
||||||
`/render/**shots/140_credits/140_0010_A/140_0010_A.lighting/000001.exr**`
|
|
||||||
|
|
||||||
Generated previews from frames:
|
|
||||||
|
|
||||||
`/render/**shots/140_credits/140_0010_A/140_0010_A.lighting/140_0010_A.lighting.mp4**`
|
|
||||||
|
|
||||||
Example of a Backup copy of frames:
|
|
||||||
|
|
||||||
```
|
|
||||||
/render/**shots/140_credits/140_0010_A/140_0010_A.lighting_bak/140_0010_A.lighting.mp4**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_0010_A/110_0010_A.anim.blend**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_0010_A/110_0010_A.layout.blend**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_0010_A/110_0010_A.fx.blend**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_0010_A/110_0010_A.comp.blend**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_rextoria.layout.blend**
|
|
||||||
|
|
||||||
pro/**shots/110_rextoria/110_rextoria.scene.blend**
|
|
||||||
|
|
||||||
```
|
|
||||||
|
77
docs/naming-conventions/shared-folder-structure.md
Normal file
77
docs/naming-conventions/shared-folder-structure.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Shared Folder Structure
|
||||||
|
|
||||||
|
The shared folder is mainly meant for content that should not be stored in SVN. In particular,
|
||||||
|
the `footage` folder contains all the previews and renders for the work being done on sequences
|
||||||
|
and shots. The `footage` folder contains directories named after the different produciton
|
||||||
|
stages (dev, pre, pro and post). This helps to keep track of data depending on its production
|
||||||
|
stage. The directory structure inside each directory is similar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
<sequence identifier>
|
||||||
|
├── sequence_previews # Videos for review
|
||||||
|
├── thumbnails # Loose frames that can be combined in any order
|
||||||
|
└── [<shot identifier>] # Individual shots that make up the sequence
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
shared
|
||||||
|
├── artifacts # Where Global Blender & Add-Ons are stored
|
||||||
|
└── editorial
|
||||||
|
├── audio
|
||||||
|
├── deliver
|
||||||
|
├── export # Renders coming out of edit
|
||||||
|
│ ├── _archive
|
||||||
|
│ │ └── gold-edit-v001_storyboard.mp4
|
||||||
|
│ └── gold-edit-v001_previs.mp4
|
||||||
|
└── footage
|
||||||
|
├── dev
|
||||||
|
│ └── 010_intro
|
||||||
|
│ ├── sequence_previews
|
||||||
|
│ │ └── 010_intro-beats-v001.mp4
|
||||||
|
│ └── thumbnails
|
||||||
|
│ └── 010_intro-beats-001.jpg
|
||||||
|
├── pre
|
||||||
|
│ ├── 010_intro
|
||||||
|
│ │ ├── thumbnails
|
||||||
|
│ │ │ ├── 010_intro-previs-001.jpg
|
||||||
|
│ │ │ ├── 010_intro-previs-002.jpg
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── sequence_previews
|
||||||
|
│ │ │ ├── 010_intro-previs-v001.mp4
|
||||||
|
│ │ │ ├── 010_intro-previs-v001_weekly.mp4
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── 010_0100
|
||||||
|
│ │ ├── 010_0100-previs
|
||||||
|
│ │ │ ├── thumbnails
|
||||||
|
│ │ │ │ └── 010_0010-previs-001.jpg
|
||||||
|
│ │ │ ├── 010_0010-previs-v001.mp4
|
||||||
|
│ │ │ ├── 010_0010-previs-v002.mp4
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── 010_0100-storyboard
|
||||||
|
│ │ ├── thumbnails
|
||||||
|
│ │ │ ├── 010_0010-storyboard-001.jpg
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── 010_0010-storyboard-v001
|
||||||
|
│ │ ├── 010_0010-storyboard-v001-001.jpg
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── 070_explosion
|
||||||
|
│ └── 090_explosion
|
||||||
|
├── pro
|
||||||
|
│ ├── 010_intro
|
||||||
|
│ │ └── 010_0010
|
||||||
|
│ │ ├── 010_0010-animation
|
||||||
|
│ │ │ ├── 010_0010-animation-v001.mp4
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ └── 010_0010-lighting
|
||||||
|
│ │ ├── 010_0010-lighting-v001.mp4
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── 070_explosion
|
||||||
|
│ └── 099_happy
|
||||||
|
└── post
|
||||||
|
└── 010_intro
|
||||||
|
└── 010_0010
|
||||||
|
└── 010_0010-lighting
|
||||||
|
├── 000001.exr
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
```
|
70
docs/naming-conventions/svn-folder-structure.md
Normal file
70
docs/naming-conventions/svn-folder-structure.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# SVN Folder Structure
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 17th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Logic
|
||||||
|
|
||||||
|
* ‘pre’ folder: like a sandbox, can be messy
|
||||||
|
* ‘pro’ folder should be the very structured one
|
||||||
|
* Keep the structure as simple as possible, with assets / shots / others
|
||||||
|
* Inside an asset folder:
|
||||||
|
* `blend` file
|
||||||
|
* `published` folder
|
||||||
|
* `maps` folder
|
||||||
|
|
||||||
|
## Proposal adopted in October 2023:
|
||||||
|
``` bash
|
||||||
|
svn
|
||||||
|
├── pro # All files from the production
|
||||||
|
│ ├── assets # All assets from the production
|
||||||
|
│ │ ├── chars # Characters & character variations
|
||||||
|
│ │ │ ├── dog
|
||||||
|
│ │ │ ├── cat
|
||||||
|
│ │ │ └── ..
|
||||||
|
│ │ ├── props # Rigged props that can have animation
|
||||||
|
│ │ │ ├── rocket_exterior
|
||||||
|
│ │ │ ├── wings
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── sets # Static background elements
|
||||||
|
│ │ │ ├── barn
|
||||||
|
│ │ │ ├── rocket_interior
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── lib # Libraries of smaller assets
|
||||||
|
│ │ │ ├── rocks
|
||||||
|
│ │ │ ├── trees
|
||||||
|
│ │ │ ├── crates
|
||||||
|
│ │ │ └── ...
|
||||||
|
│ │ ├── maps # General textures and HDRIs
|
||||||
|
│ │ ├── lgt # Lighting setups
|
||||||
|
│ │ ├── cam # Camera rig
|
||||||
|
│ │ ├── fx # Effects
|
||||||
|
│ │ ├── poses # Pose libraries for animation
|
||||||
|
│ │ ├── nodes # General Node groups
|
||||||
|
│ │ └── scripts
|
||||||
|
│ ├── shots
|
||||||
|
│ │ ├── 000_titles
|
||||||
|
│ │ ├── 010_intro
|
||||||
|
│ │ ├── 020_tryout
|
||||||
|
│ │ ├── ...
|
||||||
|
│ │ ├── 900_animtest
|
||||||
|
│ │ └── 990_promo
|
||||||
|
│ └── config
|
||||||
|
├── pre # For pre-production
|
||||||
|
│ ├── shots # Structured into sequences
|
||||||
|
│ └── assets
|
||||||
|
│ ├── cam
|
||||||
|
│ └── ...
|
||||||
|
│ ├── char
|
||||||
|
├── dev # Anything related to early development or tests
|
||||||
|
│ ├── concepts
|
||||||
|
│ ├── boards
|
||||||
|
│ ├── tests
|
||||||
|
│ └── ...
|
||||||
|
├── edit # Where the editorial .blend file lives
|
||||||
|
│ └── my_project_edit-v001.blend
|
||||||
|
├── .blender_project
|
||||||
|
├── promo
|
||||||
|
└── tools
|
||||||
|
```
|
1690
docs/package-lock.json
generated
1690
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,8 +3,9 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.0.0-alpha.75"
|
"vitepress": "^1.0.0-rc.22"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev",
|
"docs:dev": "vitepress dev",
|
||||||
|
5
docs/pipeline-overview/asset-creation/2d-assets.md
Normal file
5
docs/pipeline-overview/asset-creation/2d-assets.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 2D Assets: Texturing, Matte Paintings, Brushes...
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
6 October 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
49
docs/pipeline-overview/asset-creation/animation-testing.md
Normal file
49
docs/pipeline-overview/asset-creation/animation-testing.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Animation Testing
|
||||||
|
|
||||||
|
## General requirements
|
||||||
|
|
||||||
|
To start with the animation testing we need **the initial version of the character rigs**. For that, the character design needs to be approved, with sculpts expression of the facials, that later will be applied to the pose library and some heroe poses for the body, that will guide the animation posing marking some do and don’ts.
|
||||||
|
|
||||||
|
## Goal of the task
|
||||||
|
|
||||||
|
The goal of the animation testing is to:
|
||||||
|
- Test the rig weight painting and deformations, having feedback sessions with rigging and modeling until it moves correctly
|
||||||
|
- Develop an animation style with the help of the sculpts and heroe poses
|
||||||
|
- Create clear guidelines for both art style and animation style
|
||||||
|
|
||||||
|
The steps to follow are the next:
|
||||||
|
* Test the character rigs
|
||||||
|
* Feedback with rigging and modeling
|
||||||
|
* Create selection sets
|
||||||
|
* Create animation design guides and pose libraries
|
||||||
|
* Animation tests
|
||||||
|
|
||||||
|
## Test the character rigs
|
||||||
|
|
||||||
|
This is what we call **stress testing** the character: basically we start doing simple transformations on the controllers to see if they behave how we expect, seeing if there are any flaws on the weight painting or the behavior of the controllers, also we check if more controllers are needed on the rig.
|
||||||
|
|
||||||
|
### Feedback with rigging/modeling
|
||||||
|
|
||||||
|
In case we find any problems with the rig, we have feedback with rigging. They will decide if they can fix it or if it is needed to have some modeling tweaks in the geometry, this process of the feedback can be repeated as many times as needed.
|
||||||
|
|
||||||
|
### Create selection sets
|
||||||
|
|
||||||
|
This step is made by the animators but translated to the rigger: he will add them to the character blend file so the selection sets will be by default every time the rig is linked.
|
||||||
|
|
||||||
|
The process we use is to create a selection of controllers we are gonna need for certain parts, for example the face of the character, and like this it is really easy to select them any time we want.
|
||||||
|
|
||||||
|
For parts like the arms/legs, we just make the selection sets from one side of the body and flip the selection when we want to select the opposite side, like this we keep the pop up selection sets menu cleaner.
|
||||||
|
|
||||||
|
## Create animation design guide and pose libraries
|
||||||
|
|
||||||
|
We will create the animation design guide based on the sculpts and heroe poses modeling will provide, this is basically a do & don'ts in terms of animation. Some benefits of this is giving all the animators a common reference point making poses and expressions more consistent, will give the new animators an easier jumping onboard and adjusting to the animation style and will shorten the time needed for feedback and revisions resulting in a faster animation output.
|
||||||
|
|
||||||
|
Next to that, creating an animation poselib will make all the process even faster because everybody will share the same approved poses, being easier to keep the character on model.
|
||||||
|
|
||||||
|
Animation poselibs will be created at least for facials and hands, being this the more complex part to pose.
|
||||||
|
|
||||||
|
## Animation test
|
||||||
|
|
||||||
|
We will do this **to find the animation style and personality of the characters**.
|
||||||
|
|
||||||
|
We will start mostly with some kind of walkcycle, because is the easiest way to find personality in motion, and later we will start to do more complex animation test, body mechanics and later lipsyncs if needed, until we find the correct personality and animation style ( level of cartoon/realism, be on 1s or 2s, etc).
|
@ -1,9 +1,152 @@
|
|||||||
# Modeling
|
# Modeling
|
||||||
|
|
||||||
::: warning
|
::: warning Work in Progress
|
||||||
4 June 2023 - The content of this page is currently being edited/updated.
|
October 17th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## **General**
|
||||||
|
|
||||||
|
* The **approved concept design** is required at this point. But overlap in these stages is possible.
|
||||||
|
|
||||||
|
* The important difference for this page is the focus on **delivering a production ready model** based on an approved design.
|
||||||
|
|
||||||
|
|
||||||
|
## Organic
|
||||||
|
|
||||||
|
There needs to be a handover of design material:
|
||||||
|
|
||||||
|
* Concept Design. Typically sculpted ([T-Pose](https://studio.blender.org/films/sprite-fright/39362c69939a56/?asset=4319) & optional [Hero Pose](https://studio.blender.org/training/stylized-character-workflow/6-pose-test/))
|
||||||
|
* [Deformation Tests](https://studio.blender.org/films/sprite-fright/39362c69939a56/?asset=4356) (Like [expressions](https://studio.blender.org/films/sprite-fright/character_lineup/?asset=4340) & [poses](https://studio.blender.org/films/sprite-fright/sprite/?asset=4243))
|
||||||
|
* [Style guides & limitations](https://studio.blender.org/films/sprite-fright/3993b3b741b636/?asset=4910)
|
||||||
|
|
||||||
|
**The outcome usually is a final topology**. More can be added if required:
|
||||||
|
|
||||||
|
* Basic UV maps
|
||||||
|
* Animation Shape Keys
|
||||||
|
* Baked textures from sculpted details
|
||||||
|
* Pivot Point Markers (via empty objects)
|
||||||
|
* Helper Shape Keys (Closed eyes, open mouth)
|
||||||
|
* Hero Pose Shape Keys
|
||||||
|
* Cleanup (Applied Transforms, manifold check, naming, dummy materials)
|
||||||
|
|
||||||
|
### Topology
|
||||||
|
|
||||||
|
**The goal is to create an optimized topology** for next asset creation tasks (shading, texturing, rigging & animation). \
|
||||||
|
**A full UV unwrap** is also added at this stage and the modeling is done with them in mind.
|
||||||
|
|
||||||
|
Typically we do a retopology since the shape and design was already developed via sculpting and loose modeling. \
|
||||||
|
For more, read the [full explanation of our workflow and theory](https://studio.blender.org/blog/live-retopology-at-bcon22/).
|
||||||
|
|
||||||
|
We also have a [deeper look into the workflow for stylized characters \
|
||||||
|
](https://studio.blender.org/training/stylized-character-workflow/chapter/5d384edea5b8f5c2c32c8507/)as well as [realistic characters](https://studio.blender.org/training/realistic-human-research/chapter/retopology-layering/). \
|
||||||
|
This information can be extrapolated to other types of assets.
|
||||||
|
|
||||||
|
* [Retopology in Blender](https://studio.blender.org/blog/live-retopology-at-bcon22/)
|
||||||
|
* [Retopology & Layers from Charge](https://studio.blender.org/training/realistic-human-research/chapter/retopology-layering/)
|
||||||
|
* [Retopology Course based on Spring/Coffee Run](https://studio.blender.org/training/stylized-character-workflow/chapter/5d384edea5b8f5c2c32c8507/)
|
||||||
|
* [Retopology Cheat Sheets](https://studio.blender.org/training/stylized-character-workflow/chapter/5e5fea8470bde75aac156718/)
|
||||||
|
|
||||||
|
### UV Unwrapping
|
||||||
|
|
||||||
|
**The topology heavily influences where UV seams can be placed.** Especially for clothing this is vital to keep in mind.
|
||||||
|
|
||||||
|
The focus is mostly on **minimized stretching and texel density**. \
|
||||||
|
UDIMs are great for this to order uv islands by needed detail level. \
|
||||||
|
Areas that are closer to the camera are given higher texture resolution, \
|
||||||
|
while covered or hidden parts could be heavily reduced in resolution or even stripped of textures.
|
||||||
|
|
||||||
|
**Pattern aligned UV maps** are useful. \
|
||||||
|
They align texture patterns better on surfaces like for clothing. \
|
||||||
|
Add them as secondary UV maps, as they can have more visible stretching.
|
||||||
|
|
||||||
|
|
||||||
|
* [UV Mapping Live Streams for Snow](https://www.youtube.com/watch?v=_LI28r-Nk5g&list=PLav47HAVZMjl5VQRoVPd0481fsJNNQi9J&index=9&ab_channel=BlenderStudio)
|
||||||
|
|
||||||
|
### Sculpted Details & Animation Shapes
|
||||||
|
|
||||||
|
Existing sculpted surface details are ideally reprojected onto the subdivided retopology and refined for production. \
|
||||||
|
For this the **multiresolution modifier** is used and the final topology should be made of **relatively evenly distributed quads for the best results**.
|
||||||
|
|
||||||
|
For the use of sculpted animation shapes and detailed sculpted layers we use Shape Keys extensively as well as the \
|
||||||
|
[Sculpt Layers](https://blenderartists.org/t/sculpt-layers-addon/1288145) addon when needed. \
|
||||||
|
Also see the use of [Corrective Shape Keys](https://hackmd.io/VZ4wN5VmQBS5w9MLFolD3A) for already rigged characters.
|
||||||
|
|
||||||
|
* [Layered Sculpting from Charge](https://studio.blender.org/training/realistic-human-research/chapter/shapes-and-baking/)
|
||||||
|
* [Manual Clothing Dynamics from Charge](https://studio.blender.org/training/realistic-human-research/chapter/clothing-shapes-rotation/)
|
||||||
|
* [Baking Sculpted Details from Charge](https://studio.blender.org/training/realistic-human-research/chapter/baking-and-exporting/)
|
||||||
|
|
||||||
|
#### Helper Shape Keys
|
||||||
|
|
||||||
|
For handover to the next steps it’s good to add some shape keys.
|
||||||
|
|
||||||
|
For example shapes to **open/close the eyes and mouth**. If multiple objects are affected, the shape keys can be driven by a single property. \
|
||||||
|
These shape keys are extremely helpful for texturing and rigging.
|
||||||
|
|
||||||
|
For previz purposes a **full hero pose** is also useful. This way the texturing, shading and grooming could be rendered in an appealing pose instead of overly relying on the default T-Pose.
|
||||||
|
|
||||||
|
|
||||||
|
## Static/Hard-Surface
|
||||||
|
|
||||||
|
### Reference
|
||||||
|
|
||||||
|
For simple “static” assets it helps to use webshops or manufacturer websites to view specific dimensions of an object. These often come with many reference images and videos. The best way to gather reference is by physically holding it and being able to measure the object.
|
||||||
|
|
||||||
|
Creating hard-surface assets often comes with mechanical or moving parts, making the modeling more elaborate. With a specific design or concept, it helps to create a collage of images to work more accurately when parts move and slide in a believable manner.
|
||||||
|
|
||||||
|
These may include videos, photos, separate dimensions, technical diagrams, and blueprints (multiple angles) to help understand the asset better. When using blueprints or diagrams make sure the vertical and horizontal lines are perfectly straight before modeling.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Starting with a cube to only show “Wire”, match the rough dimensions to maintain consistent scale and create a visual guide with the help of imported reference images or dimensions. Linking existing assets or characters to the scene is another useful way to match scale and proportions. For example, the hands of a character that will use the prop.
|
||||||
|
|
||||||
|
### Blocking
|
||||||
|
|
||||||
|
Starting with simple shapes and topology to block a basic version of the asset. Use modifiers to increase the speed and flexibility which is usually preferred at this stage for changes later.
|
||||||
|
|
||||||
|
Common modifiers used are:
|
||||||
|
|
||||||
|
* If the asset is symmetrical use the Mirror modifier
|
||||||
|
* Using the Solidify modifier to add thickness to a planar surface
|
||||||
|
* The Bevel modifier adds a chamfer to the edges. It is common in conjunction with the solidify modifier
|
||||||
|
* Array modifier is useful to repeat multiple instances of the same object in multiple directions or based on another object to repeat the object radially
|
||||||
|
* Edge Split modifier can be used to create non-destructive part lines using Mark Sharp in edit mode, in conjunction with the Solidify and Bevel modifiers
|
||||||
|
* Booleans to quickly shape an object without managing topology too much
|
||||||
|
* Curve modifier has a lot of uses combined with the array modifier to deform the mesh
|
||||||
|
|
||||||
|
Testing movement is achievable in different ways without the use of an armature. The way the asset is split into different collections can be important with some of these methods:
|
||||||
|
|
||||||
|
|
||||||
|
* Using Empty objects where the various (instanced) collections are parented to. Using instances of the collections is more flexible to avoid unwanted changes in topology
|
||||||
|
* Using shapekeys to animate more simplified movement
|
||||||
|
* Using the 3D cursor with basic object parenting to manipulate simplified set-ups
|
||||||
|
|
||||||
|
Creating a simplified animated version is a good method to improve the model and design. This new information feeds back into modeling and changes can be made easily and viewed again with the instanced collections.
|
||||||
|
|
||||||
|
### Refinement
|
||||||
|
|
||||||
|
At this point it is wise to strategically apply modifiers to remain flexible enough before fully committing. Retopology might be necessary to improve the surface curvature and clean-up any previous booleans.
|
||||||
|
|
||||||
|
Adding smaller details like screws, cables, insets and more to increase believability. Adding Subdivision Surface modifier, creasing and manual bevels for smoothing and control over edge sharpness. Inspecting the model from different angles helps determine if bigger shapes aren’t lost and to avoid errors.
|
||||||
|
|
||||||
|
|
||||||
|
### Sets
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 17th 2023 - The content of this page is currently being edited/updated.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
* [Stylized Character Creation Workflow](https://www.youtube.com/watch?v=f-mx-Jfx9lA)
|
## Delivery
|
||||||
* [Updates](https://www.youtube.com/watch?v=BJHede3Oagw)
|
|
||||||
|
Any asset needs refinements and additions to make sure they are ready to hand over:
|
||||||
|
|
||||||
|
* **Check for ‘doubles’** or vertices that are too close and merge them (Use ‘Merge by Distance’)
|
||||||
|
* **Consistent direction of normals** (‘Face Orientation’ overlay and ‘Recalculate Normals’)
|
||||||
|
* **Use smooth shading on objects**
|
||||||
|
* **Apply the object scale** and if needed also the location & rotation
|
||||||
|
* **Apply most modifiers**. Any that are best to have static in the model.
|
||||||
|
* **Double check UV Maps**. Any cleanup modeling could affect them negatively.
|
||||||
|
* **Logical naming and sorting** into collections
|
||||||
|
* **Add placeholder materials** with basic viewport colors. Very helpful for handover to initial shading and animation
|
||||||
|
* **Delete unused attributes** and any data that isn’t needed for the handover
|
||||||
|
* **Add Empty objects to mark pivot points** of deformations. These are helpful helper objects especially if the rotation points on joints are not obvious
|
85
docs/pipeline-overview/asset-creation/shading.md
Normal file
85
docs/pipeline-overview/asset-creation/shading.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Shading
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
6 October 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
* Reference (Concepts, Material)
|
||||||
|
* Base Models for tests/ Final Topology for task
|
||||||
|
* Topology requirements
|
||||||
|
* Required LOD
|
||||||
|
|
||||||
|
## Initial Choices
|
||||||
|
|
||||||
|
### Rendering Style Choice (PBR vs NPR)
|
||||||
|
* General shading model
|
||||||
|
|
||||||
|
### Engine Choice (Cycles vs Eevee)
|
||||||
|
* Eevee shading settings
|
||||||
|
* Cycles shader displacement
|
||||||
|
* Hybrid workflow
|
||||||
|
|
||||||
|
### Workflow Choice (Procedural vs Data)
|
||||||
|
* Ups and Downs
|
||||||
|
* Texture specs and color management
|
||||||
|
* Semi-procedural
|
||||||
|
|
||||||
|
### Coordinate space (UV vs 3D)
|
||||||
|
* Deformation
|
||||||
|
* Procedurals
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Data Layers
|
||||||
|
|
||||||
|
Besides texture maps, data that the shader can use can be written directly onto the geometry. This data thus depends highly on the topology and needs to be checked under topological changes.
|
||||||
|
|
||||||
|
#### UV Maps
|
||||||
|
|
||||||
|
There are [certain requirements for UV maps](https://studio.blender.org/pipeline/pipeline-overview/asset-creation/modeling#uv-unwrapping) that are used for image texture painting and baking. At the same time UV maps can be used as a way to align generic patterns (procedural or not) to the surface in a way that takes advantage of the topology.
|
||||||
|
|
||||||
|
#### Color Attributes
|
||||||
|
|
||||||
|
Since the geometry is typically lower resolution than the texture maps color attributes are more suited for colors that are soft and gradual, follow the topology or are meant as a preview for quick editing.
|
||||||
|
It can also be useful to bake a preview of the color from the shader into a color attribute instead of a texture for viewport display.
|
||||||
|
|
||||||
|
#### Attributes
|
||||||
|
|
||||||
|
Beyond these specialized attribute types any generic type of data on the respective domains can be used for shading. With Blender's node tools this allows to make a name-based attribute system for the production that the tools can hook into in a convenient way for the editing process.
|
||||||
|
Developing these tools should be part of the pre-production phase.
|
||||||
|
|
||||||
|
|
||||||
|
### Texture Baking
|
||||||
|
* HiRes to LowRes
|
||||||
|
* Procedural to texture
|
||||||
|
* Pattern baking
|
||||||
|
|
||||||
|
### Shading Data Generation
|
||||||
|
|
||||||
|
#### Geometry Data
|
||||||
|
|
||||||
|
Besides using the stored data from baked or painted maps and attributes there are several types of implicit data to be used in the shader, such as for example the curve intercept or the pointiness of the mesh. This type of data is not stored on the geometry but implicitly derived and can change depending on outside parameters like the deformation.
|
||||||
|
|
||||||
|
#### Geometry Nodes
|
||||||
|
|
||||||
|
On top of the available shading information it can be incredibly useful to generate additional data for shading on the fly using geometry nodes. Instead of writing information as static data it can be generated depending on the context. For example to generate a mask based on the proximity of a mesh to another. This data can be calculated with Geometry Nodes and stored as an attribute that can be used in the shader.
|
||||||
|
([Example](https://youtu.be/1nvzwhbL-k0?si=BY-X-1Xe9D4FyySj&t=458))
|
||||||
|
|
||||||
|
### Camera Based Effects
|
||||||
|
|
||||||
|
### Node Group Structure (modularity)
|
||||||
|
|
||||||
|
#### Main shader
|
||||||
|
A lot of the time it is useful to build the shader network in such a modular way that large parts can be shared between assets of the entire production. Breaking down the base of all shaders into just a few main shaders makes it very easy to make adjustments on a global scale.
|
||||||
|
|
||||||
|
#### Utility node groups
|
||||||
|
Project specific, as well as generic node-setups that are reused throughout the production should be isolated into utility nodegroups and shared. That was adding functionality, making tweaks or improving the behavior propagates whereever they are used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
All these nodegroups should sit in their respective library files and get linked into the asset files where they can be integrated into a local shading network.
|
||||||
|
|
||||||
|
Iterating over these library node-groups comes with the responsibility to make sure that there are no regressional changes. New nodegroup inputs should be set up in a way that the default means no visible change. Otherwise this can have consequences for the assets that use these libraries.
|
||||||
|
|
||||||
|
### External Control (Custom Properties)
|
@ -1,6 +0,0 @@
|
|||||||
# Editorial and Previsualization
|
|
||||||
|
|
||||||
At Blender Studio we use the Blender VSE to create and maintain a story edit. A couple of reference links for now:
|
|
||||||
|
|
||||||
* [Charge Previsualization](https://studio.blender.org/blog/charge-previsualization/)
|
|
||||||
* [Story Pencil](https://www.youtube.com/watch?v=b25kfE6qd_c) - Currently not used at Blender Studio, but worth checking when working with storyboard artists.
|
|
@ -10,8 +10,8 @@ Artists at Blender Studio use Linux workstations, running Gentoo Linux. While th
|
|||||||
|
|
||||||
Artist workstations double as clients for the render farm.
|
Artist workstations double as clients for the render farm.
|
||||||
|
|
||||||
* [**Link to reference manual**](/user-guide/workstations/introduction.md)
|
* [**Link to reference manual**](/gentoo/user/introduction.md)
|
||||||
* [**Link to setup guide**](/td-guide/workstations/overview.md)
|
* [**Link to setup guide**](/gentoo/td/overview.md)
|
||||||
|
|
||||||
## Shared storage
|
## Shared storage
|
||||||
We use two shared drives
|
We use two shared drives
|
5
docs/pipeline-overview/organization/task-review.md
Normal file
5
docs/pipeline-overview/organization/task-review.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Task Review
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
@ -0,0 +1,6 @@
|
|||||||
|
# Concept and Design
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
6 October 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
89
docs/pipeline-overview/pre-production/editorial.md
Normal file
89
docs/pipeline-overview/pre-production/editorial.md
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Editorial
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 20th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
At Blender Studio we use the Blender VSE to create and maintain a story edit. A couple of reference links for now:
|
||||||
|
|
||||||
|
* [Charge Previsualization](https://studio.blender.org/blog/charge-previsualization/)
|
||||||
|
* [Story Pencil](https://www.youtube.com/watch?v=b25kfE6qd_c) - Currently not used at Blender Studio, but worth checking when working with storyboard artists.
|
||||||
|
|
||||||
|
## Requirements, any of the following:
|
||||||
|
* Film script
|
||||||
|
* Thumbnails/sketches
|
||||||
|
* Storyboard drawings
|
||||||
|
* Previs images
|
||||||
|
* Previs videos
|
||||||
|
* Concept art
|
||||||
|
* Temp music
|
||||||
|
* Temp sfx
|
||||||
|
* Temp vocals
|
||||||
|
|
||||||
|
## Goal of the task
|
||||||
|
|
||||||
|
During the early stages of the project, the goal is to take any available materials into the VSE and create a rough animatic version of the film
|
||||||
|
- based on the most current version of the scrip
|
||||||
|
- or based on the most recent feedback from the director.
|
||||||
|
|
||||||
|
Over the course of the project, sections of the rough animatic **get replaced** with
|
||||||
|
- alternative storyboard drawings,
|
||||||
|
- layout shots,
|
||||||
|
- animated playblasts
|
||||||
|
- and final renders.
|
||||||
|
|
||||||
|
So at any given moment, parts of the edit are in different stages until the entire thing finally crosses the finish line with only final renders.
|
||||||
|
|
||||||
|
## Resolution
|
||||||
|
|
||||||
|
Set the file to the correct resolution, our most commonly used one is 2048x858. For some films it’s helpful to also include a letterbox border, to add subtitles or notes from the director. In those cases we usually simply add a bit to the height to make room for it.
|
||||||
|
|
||||||
|
As an example, in ‘Sprite Fright’ the final resolution was 2048x858 but in the beginning we were working with 2048x1146 in the edit and adding two black color strips (above and below) to keep the film area 2048x858. Then a text strip got added above to indicate which scene we were in, and a text strip below for the subtitles.
|
||||||
|
|
||||||
|
## Export settings
|
||||||
|
|
||||||
|
We usually export an **.mp4 file**, with the video codec **H.264** and audio codec AAC.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
The edit file should get “-v001” at the end of its name (e.g. `short_film-edit-v001.blend`).
|
||||||
|
|
||||||
|
The version number is bumped up whenever there is a new export of the film, so that the export has the same version number as the work file (e.g. `short_film-edit-v001.mp4`). This might usually happen 1-4 times a week, depending on the situation.
|
||||||
|
|
||||||
|
## Legacy cleaning
|
||||||
|
|
||||||
|
As the project goes forward and gets more refined, it becomes increasingly more important to **keep the edit organized and tidy**.
|
||||||
|
|
||||||
|
A lingering alt version of a scene might be allowed to exist hidden inside the edit for 2-3 versions but it needs to be eventually deleted and the edit cleaned up. Otherwise it becomes too bogged down in legacy issues. You will always have the older versions to go back to, if an old scene needs to be revived.
|
||||||
|
|
||||||
|
## Organizing external files
|
||||||
|
|
||||||
|
All external files for the edit are usually put inside a well organized “editorial” folder, or in an adjacent folder.
|
||||||
|
|
||||||
|
### Organizing strips
|
||||||
|
|
||||||
|
In the beginning there is no hard and fast rule. Whatever works best for the editor to create a rough animatic.
|
||||||
|
|
||||||
|
As the project progresses and the complexity level of the edit gets raised, it becomes more important to create some rules of thumb.
|
||||||
|
- Allow 4-12 channels for the storyboards/previs/shots (or however many are needed),
|
||||||
|
- 3-5 for the temp music and ambiance,
|
||||||
|
- 2-3 channels for dialogue (depending on how many characters are in the film)
|
||||||
|
- and 10-20 channels for temp sfx.
|
||||||
|
|
||||||
|
The temp sfx can get very messy and unwieldy so be very selective in the beginning how many to use and for what purpose.
|
||||||
|
|
||||||
|
### Organizing temp vocals / scratch dialogue
|
||||||
|
|
||||||
|
When the film is dialogue heavy, it can be a nightmare to keep track of all the different versions of takes for each and every character.
|
||||||
|
|
||||||
|
For ‘Sprite Fright’ I made a new workspace for each character, with its own scene data pinned to it. There I could have a long edit of the various recordings of that character, using markers to help organize them. When I went over a recording, I would cut each take and move the ones I liked up one channel and the ones I didn’t like down one channel. Once I had the takes I needed, I would copy the strips, go back to the “main edit” workspace, paste them there and integrate them into the edit.with
|
||||||
|
|
||||||
|
### Adding meta strips
|
||||||
|
|
||||||
|
At some point during the production process, the animatic will become solid enough that scenes and shots will get **officially named**. The edit is not necessarily locked, but at this point changes to the story will be kept to a minimum.
|
||||||
|
|
||||||
|
In a single channel, a color strip will be created for each shot (to represent the position and timing of each shot). The **Kitsu addon** can then use those color strips to create meta strips out of them, all in a single channel. Each meta strip represents a shot and is connected to Kitsu, so that once any edit changes are made the information can be sent to Kitsu.
|
||||||
|
|
||||||
|
### Organizing exported shots
|
||||||
|
|
||||||
|
Once a shot is exported via the Kitsu addon, it needs to be **manually inserted** into the edit (this is for quality control and troubleshooting) and placed below the corresponding meta strip. From there on out the Kitsu addon can be used to update the shot to the latest version, if a new version has been exported.
|
5
docs/pipeline-overview/pre-production/previz.md
Normal file
5
docs/pipeline-overview/pre-production/previz.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Previsualization
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 20th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
@ -0,0 +1,5 @@
|
|||||||
|
# Research and Development
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
6 October 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
35
docs/pipeline-overview/pre-production/storyboard.md
Normal file
35
docs/pipeline-overview/pre-production/storyboard.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Storyboard
|
||||||
|
|
||||||
|
## General requirements
|
||||||
|
|
||||||
|
* The script of the film
|
||||||
|
* Sequence Briefing from the director
|
||||||
|
* Concept art and character designs
|
||||||
|
|
||||||
|
## Goal of the task
|
||||||
|
|
||||||
|
Storyboarding is the first step into visualizing the vision of the director.
|
||||||
|
|
||||||
|
This is the time to
|
||||||
|
- try different angles,
|
||||||
|
- beats,
|
||||||
|
- acting choices
|
||||||
|
to determine the storytelling in visual format.
|
||||||
|
|
||||||
|
The goal is to create a version of the movie that eliminates as many alternative possibilities as possible and is solid enough to be transferred to the next stage: Previz.
|
||||||
|
|
||||||
|
## Storyboarding using Story Pencil
|
||||||
|
|
||||||
|
For the workflow of storyboarding we use the Grease Pencil tool combined with the Story Pencil addon.
|
||||||
|
|
||||||
|
The advantage of this workflow is the **possibility to combine 2D drawings within a 3d environment** which allows us to use 3d environment as a base to create spacial awareness from the beginning that can be used in modeling/previz as a point of reference.
|
||||||
|
|
||||||
|
The Story Pencil addon allows us to **use the VSE editor** in Blender and swap easily between shots and edit, or alter different shots.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
To start, we have been using rough sketches to simply **convey the idea** and **progression** of each shot. A first pass will be shown to the director and any feedback will be addressed.
|
||||||
|
|
||||||
|
Once a version is approved by the director, we'll continue to **refine** the drawings and add more definition to the shot by using a **2 color shading method**.
|
||||||
|
|
||||||
|
As an initial edit pass with Story Pencil, once we finalize a sequence, the drawings will be exported to individual images for the editorial department. They can use these images to refine the timing and adjust composition to the drawings if needed.
|
5
docs/pipeline-overview/publishing.md
Normal file
5
docs/pipeline-overview/publishing.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Publishing
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
69
docs/pipeline-overview/shot-production/animation.md
Normal file
69
docs/pipeline-overview/shot-production/animation.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Animation
|
||||||
|
|
||||||
|
## General requirements
|
||||||
|
|
||||||
|
|
||||||
|
In order to start the animation process the animators require **animation-ready rigs**. This means the rigs that are needed to animate a shot are **well tested and refined** by the rigging department.
|
||||||
|
|
||||||
|
In case the rigs are partially ready (i.e. when the character's facial rig is in progress) this needs to be communicated with the director and coordinator in order to decide if a shot is ready to be animated. A shot might heavily rely on body mechanics where a facial performance is minimal. In this case, the animator starts animating the body rig. Once the rig gets the facial rig updated, the animator is able to finish the shot by animating the face as well.
|
||||||
|
|
||||||
|
Another factor that is required for the animation process is the **shotfile created by the shotbuilder**. This file includes the characters, props and the set. The shotbuilder also has generated the correct framerange and (if needed) an audiofile.
|
||||||
|
|
||||||
|
## Goal of the task
|
||||||
|
|
||||||
|
The goal of the animation process is to create an animation performance according to the directors briefing. The characters and/or props need to be moving in a **believable** way and expected to be **cohesive** to the style that has been set in pre production.
|
||||||
|
|
||||||
|
These steps are taken into account for successfully completing a task in animation:
|
||||||
|
|
||||||
|
* Briefing
|
||||||
|
* Scene setup
|
||||||
|
* Blocking pass
|
||||||
|
* Asset updates
|
||||||
|
* Polishing pass
|
||||||
|
* Shot delivery
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Before the animator can start on his assigned shot, they need to get a detailed briefing from the director about the **performance** and **intent** of the shot/sequence.
|
||||||
|
|
||||||
|
The animator has a chance to ask questions and discusses acting beats with the director if needed. Once it is clear what needs to be done in animation, the animator can open the shotfile and prepare for animation.
|
||||||
|
|
||||||
|
## Scene setup
|
||||||
|
|
||||||
|
When opening the shotfile for the first time, it is important to **check** if
|
||||||
|
- the shotbuilder has built the shot correctly
|
||||||
|
- all the required assets are available in the scene
|
||||||
|
- as well as the framerate and camera
|
||||||
|
|
||||||
|
Once this is the case, the animator starts to do a **technical planning** for the shot. For example, when a character is in a car, they need to be parent constrained accordingly. This setup is necessary in order to continue the animation process and eliminate as many technical issues as possible along the way.
|
||||||
|
|
||||||
|
## Blocking pass
|
||||||
|
|
||||||
|
A first pass of the animated shot will contain a **rough version** of the animation performance in order to get feedback quickly from the director.
|
||||||
|
|
||||||
|
This way, any feedback can be addressed early on and will give the director the opportunity to change the performance if necessary.
|
||||||
|
|
||||||
|
## Asset updates
|
||||||
|
|
||||||
|
Assets are continuously updated during production so it is important to **keep rigs and props up to date** to avoid issues with out-of-sync assets later on.
|
||||||
|
|
||||||
|
Once a shot is finished, you make sure all assets are up to date so the lighting department won't run into unexpected issues when opening the shot in a lighting file.
|
||||||
|
|
||||||
|
## Polishing pass
|
||||||
|
|
||||||
|
Once a blocking pass is approved by the director, it is the task of the animator to refine the animation to a final state.
|
||||||
|
|
||||||
|
This means they will add the necessary inbetweens and animating details to the character that haven't been refined in the blocking pass. Think of things like earrings, tails, fingers etc.
|
||||||
|
|
||||||
|
The Polishing pass also ensures the characters/props have **proper contact** with the environment and no intersections are visible on screen.
|
||||||
|
|
||||||
|
## Shot delivery
|
||||||
|
|
||||||
|
Once a shot is finished, we run a bunch of checks to make sure the lighting department can take over without unexpected issues. This includes checking:
|
||||||
|
|
||||||
|
* Framerate is set correctly
|
||||||
|
* The latest version is rendered to kitsu
|
||||||
|
* All assets are up to date and linked into the output collection
|
||||||
|
* Checking if animation is good for motion blur (start and end frame, parent switches)
|
||||||
|
|
||||||
|
Once all of these factors are set correctly, the shot is ready to commit for lighting to take over.
|
5
docs/pipeline-overview/shot-production/coloring.md
Normal file
5
docs/pipeline-overview/shot-production/coloring.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Coloring
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
44
docs/pipeline-overview/shot-production/effects.md
Normal file
44
docs/pipeline-overview/shot-production/effects.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Effects
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
* Usually final animation at this point to ensure no mismatch in contact points, timing, etc. This is important as a lot of the FX are usually custom for the exact file and animation, so any further changes would require them to be redone.
|
||||||
|
* There Should be at least a basic pass on lighting for most FX to be placed in the right context with the visuals. This depends on the specific production and type of FX though. Preliminary FX can be useful before that, usually we just go with the layout version, or what the animators mock up.
|
||||||
|
* Different file for each type of FX in the shot, set up in the same way as the lighting file with linked animation data and local collection for the FX
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### Set FX
|
||||||
|
Sometimes FX don’t need to be made uniquely for a shot but can just be created once in a way that they can be used in all shots that use the asset they belong to (e.g. Charge - Flags outside hut).
|
||||||
|
|
||||||
|
### Physics Simulations
|
||||||
|
The use of physics simulations on a shot level is relatively straight-forward. Small simulations can be cached directly into the FX file. Usually the cache should live outside in a place that is accessible by everyone working on the shot.
|
||||||
|
|
||||||
|
### Procedural FX
|
||||||
|
|
||||||
|
#### Geometry Nodes/Modifiers
|
||||||
|
A lot of the time instead of doing an elaborate physics simulation it is simpler and gives more control to fake something with a procedural setup (e.g. Charge - Paint can explosion). This can be very custom solutions on a shot basis or a general setup that is reusable over multiple shots (e.g. Sprite Fright melting).
|
||||||
|
|
||||||
|
#### Parametric FX assets
|
||||||
|
Whenever some FX need to be reused over and over in a generic way that needs some simple adjustments/animation for the shot, we use specific FX assets that usually have a rig with just a few bones for transforms and constraints. Usually those also have parameters to control a Geometry Nodes setup (e.g. [Charge - Bullet Impacts](https://studio.blender.org/films/charge/3b0f29b4825fa2/?asset=6191))
|
||||||
|
|
||||||
|
#### Shaders
|
||||||
|
Some FX need to hook into the shader of an existing asset, like a character (e.g. Charge - Bruised and sweaty face). Usually we make this a part of the asset itself and then expose the controlling parameter in a way that we can control it from outside using custom properties on the object level, so we can control it in the rig.
|
||||||
|
|
||||||
|
### Frame by frame
|
||||||
|
|
||||||
|
#### Grease Pencil
|
||||||
|
Some FX are better drawn directly, so Grease Pencil is a great way to do that.
|
||||||
|
|
||||||
|
#### 'Keymesh' Style
|
||||||
|
The same applies for 3D meshes. In some cases it can be easier or better for a few frames to sculpt a mesh frame by frame (e.g. Sprite Fright - Bird Poop Wipe). The currently most reliable way to do that, until Blender has native functionality, is to create a separate object per frame and animate its scale to be 1 on the required frame and 0 on any other
|
||||||
|
|
||||||
|
Of course, this technique does not allow for 'proper' motion blur.
|
||||||
|
|
||||||
|
## Cache Files
|
||||||
|
* It's very important that any data that is cached is accessible to the render farm and anyone working on the shot. We work with a cache directory that is centralized on our server that everyone at the studio, including our render farm machines, has mounted on `/render/`.
|
||||||
|
* Since this directory is outside of our project's file structure, these paths must be absolute to work reliably.
|
||||||
|
* There are different file formates viable for caching data to disk. Usually it is best to stick to Blender's native caching formats, when possible to make absolutely sure no data is lost.
|
||||||
|
|
||||||
|
## Integration into Lighting File
|
||||||
|
* The FX collection(s) need to be linked back into the lighting file and integrated properly into the view layer setup and comp. **It is important that the collection is directly linked into the lighting scene, not instanced. This allows proper visibility control.**
|
||||||
|
* For FX that affect the lighting, either by casting additional light (e.g. sparks) or obscuring it, the lighting needs to be adjusted accordingly.
|
5
docs/pipeline-overview/shot-production/layout.md
Normal file
5
docs/pipeline-overview/shot-production/layout.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Layout
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
62
docs/pipeline-overview/shot-production/lighting.md
Normal file
62
docs/pipeline-overview/shot-production/lighting.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Lighting
|
||||||
|
|
||||||
|
::: warning
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
We can start lighting a shot as soon as there is a first blocking pass available. Further, all assets within the framing of the shot should have at least a basic shading pass to give us a rough estimate. It can be helpful to have a color script of the whole film at hand, as well as a color key or a set of lighting guidelines to keep the continuity of the movie in place. Often we would already have made a simple test setup in look development that can be used as base for the shot.
|
||||||
|
|
||||||
|
## Render engine choice
|
||||||
|
|
||||||
|
Which renderer we use depends very much on the style of the production. That choice factors into the lighting workflow and how assets are authored and shaded, so it's important to select an engine already in pre-production.
|
||||||
|
|
||||||
|
## Lighting workflow
|
||||||
|
|
||||||
|
The workflow is highly dependent on the production requirements and its art style. In general, we try to identify the key shots in a sequence first and set up their lighting first. When there is an establishing shot or a wide angle view of the scene, we derive close ups and inserts from that as a starting point.
|
||||||
|
|
||||||
|
## Eye highlights
|
||||||
|
|
||||||
|
A large part of lighting is making characters appealing. Eye highlights are an important aspect of that appeal, so extra care is given to them. We used a variety of different approaches for eye highlights in the past, each of them is perfectly valid so the technique is dependent on the art style of the production. For example, “Spring” and “Agent 327: Operation Barbershop” both use the real reflections of the scene's lights plus additional hand-placed highlight lights. “Sprite Fright”, being more cartoony in style, utilizes eye highlights which are placed by the animators using the character rig.
|
||||||
|
|
||||||
|
|
||||||
|
## Maintaining Continuity
|
||||||
|
|
||||||
|
There are different methods for maintaining the continuity between each shot in a film. They all need to link up to the adjacent shots in the sequence in terms of light intensity, color and style. We use different techniques for that.
|
||||||
|
|
||||||
|
In cases where there is a strong emphasis on spatial continuity (like a dialogue scene, or a fast action set piece) we have to ensure that the light direction is consistent across a sequence. In outdoor shots it can be helpful to draw a top-down view of the scene to discuss the camera angles and general light direction.
|
||||||
|
In “Sprite Fright” we used a simple [light direction chart](https://studio.blender.org/films/sprite-fright/391c5d112594c2/?asset=5023) which showed colored arrows on a grid of viewport rendered shots to indicate the direction of each shot's key light. The same chart was also used by the animators to match the (cheated) eye highlights to the actual lighting of each shot. Whenever the light direction was changed, the corresponding shot was updated in the chart and the animator knew how to re-align the eye highlight.
|
||||||
|
|
||||||
|
It is vital to match the color values for each type of light across shots. In recent productions we tried to simplify the light types down to four: Key, key-soft, fill and rim. Each type had a corresponding RGB value for each scene. We store these values in a library file. For Cycles they can simply be added to a node group which can be linked into each shot. Thus, whenever the value in the central file changes, all the shots automatically get the update and simply have to be re-rendered.
|
||||||
|
|
||||||
|
|
||||||
|
## File hierarchy
|
||||||
|
|
||||||
|
The lighting file is responsible for outputting the final rendered frames of a shot, either to be used as-is in the edit or as an image sequence in the compositing step. The lighting file therefore links in the output collection from the animation file, as well as from the corresponding effects files. When the lighting gets approved and a shot is marked for final rendering, the rendering quality settings should be changed from preview quality to the final output configuration before the shot gets submitted to the farm.
|
||||||
|
|
||||||
|
## Collection Layout
|
||||||
|
|
||||||
|
Lights and all other objects necessary to illuminate the shot are placed in a separate collection called *{shot number}.lighting*. Often there can be several additional elements in addition to the anim and effects data: Set extensions, matte paintings, high res assets, shot specific dressing.
|
||||||
|
|
||||||
|
## Syncing updates from animation
|
||||||
|
|
||||||
|
Whenever the animator updates their file, the changes automatically trickle down to the lighting file through the output collection. We have to ensure that all animated elements are included in the output at all times. Whenever the animator adds additional objects (helpers, extra props, etc) they need to be linked to it as well.
|
||||||
|
|
||||||
|
## Modifying linked data
|
||||||
|
|
||||||
|
Since a big part of the shot comes from the animation file, we often need to adjust the data that flows into the lighting file. This can be done in a variety of ways and depends on the project's setup and complexity.
|
||||||
|
|
||||||
|
The most basic level of control is changing visibility: Often a specific asset is made to be fast in animation playback, but we might want to see a more complex representation of it in the lighting file. In that case we want to split the asset's geometry into several different representations which could be controlled by switches in the rig or are simply placed in different collections.
|
||||||
|
The best way to hide a linked collection using the exclusion function in the Outliner. Exclusion is stored in the current (local) ViewLayer; unlike render and viewport visibility, which are stored on the collection and can not be written to a linked data block (unless the data is changed with library overrides). It's important to keep in mind that view and render visibility are directly read from the source file, so changing it in the asset (or the animation file) will directly affect the lighting file.
|
||||||
|
|
||||||
|
In the last productions we tried to move away from direct visibility control and opted for rig switches based on geometry nodes. But to change those, we still need to modify values on linked data. Using Blender's Python API we can modify pretty much any value, local or linked. We can write a set of instructions into a text data block (just make sure its name ends in .py) and make it auto-execute when the file is loaded. This is very powerful because we can even use loops to iterate through a large set of values. The downside is that changing values is pretty cumbersome, and we need to execute the script every time a value has been changed.
|
||||||
|
|
||||||
|
To automate the creation of RNA overrides we wrote the [Lighting Overrider](https://studio.blender.org/pipeline/addons/lighting_overrider) add-on. It automates the creation of the text data block and makes tweaking values much easier. Just hover over any value in the UI and press “O”, then choose the desired value and press “OK”.
|
||||||
|
|
||||||
|
A significant drawback of both methods is that animation is very hard to do. A hacky way is to create a driver on a linked RNA value with python and hook it up to a local ID property on a dummy object. This method was used on “Sprite Fright” to animate Ellie's bruising appearing in a shot. However, the easier method is to dive into the animation file and animate the value locally. Just make sure to communicate this with the animator of the shot.
|
||||||
|
|
||||||
|
## Debugging motion blur issues
|
||||||
|
|
||||||
|
Motion blur issues can happen quickly if there is a constraint switch in animation. For example, when a character picks up a prop from a table, the parenting hierarchy has to change over the course of a few frames. If done wrong, this can result in long motion blur streaks where the asset can travel between its rest position and the final position in the hand of the character. Often a solution can be to execute those constraint switches only in Constant keyframe interpolation, but sometimes this can also cause other issues.
|
||||||
|
The most visual way - apart from rendering the frames - to debug this is using Blender's subframe view. Just enable the Subframe toggle in the Playback popover of the timeline editor. This lets you scrub the timeline between frames which reveals what the renderer samples to create the motion blur. This needs some experimentation as we haven't found a way to eliminate the glitches in every case, but it should be a good starting point to give notes to the animator.
|
5
docs/pipeline-overview/shot-production/rendering.md
Normal file
5
docs/pipeline-overview/shot-production/rendering.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Rendering
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 6th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
54
docs/pipeline-overview/shot-production/shot-assembly.md
Normal file
54
docs/pipeline-overview/shot-production/shot-assembly.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Shot Assembly
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
October 10th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
Before Shot Assembly can begin two requirements need to be met. Firstly, shots to be assembled need to have corresponding Tasks on the Kitsu Server, and secondly those shots must have Assets ‘cast’ or associated with each shot. These will describe when the shot happens in the edit and what assets appear in the shot.
|
||||||
|
|
||||||
|
|
||||||
|
### Preparing Metastrips from a Previz Sequence
|
||||||
|
|
||||||
|
To begin Shot Assembly it is required to describe the name/length of each shot inside a “Video Sequence Editor” .blend file. Once a Previz Sequence has been assembled into the VSE, ['Metastrips’](https://studio.blender.org/pipeline/addons/blender_kitsu#metastrips) can be created for each shot. A metastrip is an empty video strip that is linked into the project management software Kitsu. Metastrips are used to push updates to the frame ranges of each shot to Kitsu, which are later used in the Shot Assembly process.
|
||||||
|
|
||||||
|
Metastrips are created from the Previz Strips, they are either linked to an existing Kitsu Shot, or a new Shot/Sequence can be pushed from the VSE to the Kitsu Server. The shot’s in, out and duration will be determined by the strip’s length. Multiple metastrips can be made out of a single previz strip if required, adjust the shot’s timing by simply trimming the metastrip in the timeline. These metastrips are then pushed to Kitsu Server to update the shot info on the server side. Below is a Previz Sequence with it’s metastrips.
|
||||||
|
|
||||||
|
|
||||||
|
![Metastrips](/media/pipeline-overview/shot-production/Pets_Previz__Meta_Strips.png)
|
||||||
|
|
||||||
|
|
||||||
|
For more information about metastrips and the processes involved in managing the edit of your film see the [Blender Kitsu Add-On documentation](https://studio.blender.org/pipeline/addons/blender_kitsu).
|
||||||
|
|
||||||
|
|
||||||
|
### Casting Assets in Kitsu
|
||||||
|
|
||||||
|
Assets or their place holders, need to be saved into the central location where assets are saved, on the [production directory](https://studio.blender.org/pipeline/td-guide/project-setup#project-directory). Once Assets or their place holders have been created, users can begin casting. Casting is completed on the Kitsu Server, [a demo of the asset casting process in Kitsu](https://forum.cg-wire.com/t/breakdown-casting-widget-for-kitsu/31) can be found on the CGWire Forum.
|
||||||
|
|
||||||
|
|
||||||
|
### Using Shot Builder
|
||||||
|
|
||||||
|
Now that all external requirements are met it is time to run the Shot Builder, which is a feature of the [Blender Kitsu Add-On](https://studio.blender.org/pipeline/addons/overview).
|
||||||
|
|
||||||
|
Inside your production’s directory the Shot Builder configuration files need to be created using the [examples](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs/examples) included in the Add-On's directory. See the Shot Builder Config [Directory Layout](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs#directory-layout) for details. The only configuration file that requires production specific edits is `assets.py`. This configuration file links the Kitsu Asset entries to their corresponding files in the production directory, please see the [Shot Builder API](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs#api) for more details.
|
||||||
|
|
||||||
|
|
||||||
|
![New Shot](/media/pipeline-overview/shot-production/new_shot_file.png)
|
||||||
|
|
||||||
|
|
||||||
|
The Shot Builder is started by navigating to File>New>Shot File. Then users can select a sequence, shot and type of shot to build. The Shot Builder is capable of assembling the following types of shots.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* <span style="text-decoration:underline;">Animation</span> contains all Assets with Rigs and a linked set, Animators work here, all data that needs to move down the pipeline is placed in a **shot_name.anim** collection.
|
||||||
|
* <span style="text-decoration:underline;">FX</span> contains any effects objects in a collection **shot_name.fx**. This file will have the **shot_name.anim** collection linked into it and the **shot_name.lighting** collection if available.
|
||||||
|
* <span style="text-decoration:underline;">Lighting</span> contains all lighting objects, like lights, shadow catchers and any other data related to lighting the scene in an **shot_name.lighting** collection. This .blend file will also contain a linked version of the **shot_name.anim** collection and the **shot_name.fx** collection when available.
|
||||||
|
* <span style="text-decoration:underline;">Compositing</span> contains linked copies of the **shot_name.anim**, **shot_name.fx**, **shot_name.lighting** collections. Compositing file contains a Compositing Node Group used for final render. The compositing file is usually used for rendering.
|
||||||
|
|
||||||
|
|
||||||
|
### Maintaining Existing Shots
|
||||||
|
|
||||||
|
After shots have been created in some cases the shot’s length or position may be adjusted by the editor. These changes will be tracked in Kitsu using the metastrips described above. If a metastrip for a given shot has been changed, those changes will be pushed into Kitsu. In this case when users open a shot file they will be warned within the Playblast Tools section of their Kitsu add-on to pull the new frame range into their .blend file.
|
||||||
|
|
||||||
|
|
||||||
|
![Frame Range Outdated](/media/pipeline-overview/shot-production/frame_range_out_of_date.png)
|
@ -1,79 +0,0 @@
|
|||||||
# Project Setup
|
|
||||||
|
|
||||||
::: warning Work in Progress
|
|
||||||
30 Apr. 2023 - The content of this page is currently being edited/updated.
|
|
||||||
:::
|
|
||||||
|
|
||||||
* Setup SVN (access for users)
|
|
||||||
* Setup Kitsu project
|
|
||||||
* Commit intial folder structure
|
|
||||||
|
|
||||||
## Project Directory
|
|
||||||
|
|
||||||
```python
|
|
||||||
.
|
|
||||||
├── config
|
|
||||||
│ └── asset_pipeline_config
|
|
||||||
├── previz # Anything related to early development or pre-production tests
|
|
||||||
└── pro # All files from the production
|
|
||||||
├── promo # Promotional material. Often created near the end of production
|
|
||||||
├── animation_test # For pre-production
|
|
||||||
├── shot_builder # Studio tool configs
|
|
||||||
├── lib # All assets from the production
|
|
||||||
│ ├── brushes
|
|
||||||
│ ├── cam # Camera rig & setup
|
|
||||||
│ ├── char # Characters & character variations
|
|
||||||
│ ├── env # Environment asset libraries
|
|
||||||
│ ├── fx # Effects
|
|
||||||
│ ├── lgt # Lighting setups
|
|
||||||
│ ├── maps # General textures and HDRIs
|
|
||||||
│ ├── matlib # Materials
|
|
||||||
│ ├── nodes # General Node groups
|
|
||||||
│ ├── poselib # Pose libraries for animation
|
|
||||||
│ ├── props
|
|
||||||
│ ├── scripts
|
|
||||||
│ └── sets
|
|
||||||
└── shots #Structured into sequences
|
|
||||||
```
|
|
||||||
|
|
||||||
## Render Directory
|
|
||||||
|
|
||||||
```
|
|
||||||
.
|
|
||||||
├── chaches
|
|
||||||
├── delivery
|
|
||||||
│ ├── audio
|
|
||||||
│ ├── color_preview
|
|
||||||
│ ├── mux
|
|
||||||
│ └── video
|
|
||||||
├── editorial
|
|
||||||
│ ├── edit
|
|
||||||
│ ├── export
|
|
||||||
│ ├── fonts
|
|
||||||
│ ├── score
|
|
||||||
│ └── sfx
|
|
||||||
├── farm_output
|
|
||||||
│ ├── lib
|
|
||||||
│ └── shots
|
|
||||||
├── plates
|
|
||||||
├── shot_frames
|
|
||||||
└── shot_previews
|
|
||||||
```
|
|
||||||
|
|
||||||
## Shared Directory
|
|
||||||
|
|
||||||
```python
|
|
||||||
.
|
|
||||||
├── bts # Behind the scenes
|
|
||||||
├── concepts # Concept art and paintings
|
|
||||||
├── development # Piches and boards
|
|
||||||
├── inspiration # Various inspirations & references
|
|
||||||
├── music
|
|
||||||
├── planning
|
|
||||||
├── pr
|
|
||||||
├── resources
|
|
||||||
├── script # Latest scripts for the movie
|
|
||||||
├── shot_packs # Shots for sharing online
|
|
||||||
├── training # Training produced for the production
|
|
||||||
└── videoref # Video shoots from animators
|
|
||||||
```
|
|
219
docs/td-guide/project-tools-setup.md
Normal file
219
docs/td-guide/project-tools-setup.md
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# Project Tools Setup
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
15 Oct. 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
In this guide, you will learn how to setup the Blender Studio Pipeline, the backbone of [Blender Open Movies](https://studio.blender.org/films/). This workflow relies on Blender, some Blender Add-Ons, and additional services like [Kitsu](https://www.cg-wire.com/kitsu) and [Flamenco](https://flamenco.blender.org/). Wether you are an individual with a single computer or a studio with a full network of workstations, this guide offers a straightforward approach to set up the pipeline, complete with easy-to-follow examples.
|
||||||
|
|
||||||
|
::: info Python Requirement
|
||||||
|
Running these scripts requires python 3.11+, please ensure python is installed on your system before running blender with the below instructions
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Clone Repository
|
||||||
|
The Blender Studio Pipeline git repository contains many tools and resources used in deploying and managing a Blender Studio Pipeline. To start our pipeline we will need to clone this repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /data # This directory is the root storage that will contain all projects
|
||||||
|
git lfs install # Ensure git-lfs is installed
|
||||||
|
git clone https://projects.blender.org/studio/blender-studio-pipeline.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generate Folder Structure
|
||||||
|
The first step in deploying the Blender Studio Pipeline is to create the correct folder structure. Many of the tools used in the Blender Studio require the following folder structure on each of your studio workstations.
|
||||||
|
|
||||||
|
|
||||||
|
1. Create project root directory
|
||||||
|
```bash
|
||||||
|
mkdir /data/{my_project}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
2. Navigate to the project-tools folder
|
||||||
|
```bash
|
||||||
|
cd /data/blender-studio-pipeline/scripts/project-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create base folder structure using
|
||||||
|
```bash
|
||||||
|
./init_project_folder_structure.py /data/{my_project}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
This will create the bones of your production's directories.
|
||||||
|
- `local` This is where the local copy of Blender and the add-ons will be installed.
|
||||||
|
- `shared` This is the folder that should be shared over the network.
|
||||||
|
- `svn` This the versioned controlled folder where the `.blend` production files will live.
|
||||||
|
|
||||||
|
|
||||||
|
3. Create render directory (Optional)
|
||||||
|
```bash
|
||||||
|
mkdir /data/{my_project}/render/
|
||||||
|
```
|
||||||
|
|
||||||
|
This directory is used for the temporary storage of render files generated by Flamenco. This storage must be accessible by all computers using Flamenco for rendering, commonly this is a Network Attached Storage of some kind. In this guide, our renders will live in the root of our project directory.
|
||||||
|
|
||||||
|
|
||||||
|
### Populating `shared`
|
||||||
|
This is the folder that should be shared over the network. (By using Syncthing, NFS shares, Samba, Dropbox, etc) Connect this folder to your sharing software of choice and create the following folder structure. More details about shared folder structure can be found [here](/naming-conventions/shared-folder-structure.md)
|
||||||
|
|
||||||
|
#### Initial Directory Set-Up
|
||||||
|
1. Create your Shared Folder directly in the target directory or symlink it to`/data/{my_project}/shared`.
|
||||||
|
2. Use the following commands to generate the below folder structure.
|
||||||
|
```bash
|
||||||
|
cd /data/blender-studio-pipeline/scripts/project-tools
|
||||||
|
init_project_folder_structure.py /data/{my_project}/shared --json_file folder_structure_shared.json
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Add Existing Directory to User Workstation
|
||||||
|
1. Clone your SVN Folder directly into the target directory `/data/{my_project}/shared`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
shared
|
||||||
|
├── artifacts # Where Global Blender & Add-Ons are stored
|
||||||
|
└── editorial
|
||||||
|
├── audio # Audio
|
||||||
|
├── deliver # Delivery for script
|
||||||
|
├── export # Renders coming out of edit
|
||||||
|
│ ├── _archive
|
||||||
|
└── footage
|
||||||
|
├── dev # Early Development
|
||||||
|
├── pre # Pre-Production steps like previs
|
||||||
|
├── pro # Playblast from Production
|
||||||
|
└── post # Image Sequences/Final Renders
|
||||||
|
```
|
||||||
|
|
||||||
|
### Populating `SVN`
|
||||||
|
This is the folder that should contain a version controlled file system to be shared over the network. (By using SVN, GIT-LFS, etc). Connect this folder to your version control software of choice and create the following folder structure. More details about shared folder structure can be found [here](/naming-conventions/svn-folder-structure.md)
|
||||||
|
|
||||||
|
#### Initial Directory Set-Up
|
||||||
|
1. Create your SVN Folder directly in the target directory `/data/{my_project}/svn`.
|
||||||
|
2. Use the following commands to generate the below folder structure.
|
||||||
|
```bash
|
||||||
|
cd /data/blender-studio-pipeline/scripts/project-tools
|
||||||
|
init_project_folder_structure.py /data/{my_project}/svn --json_file folder_structure_svn.json
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Add Existing Directory to User Workstation
|
||||||
|
1. Clone your SVN Folder directly into the target directory `/data/{my_project}/svn`.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.
|
||||||
|
└── svn /
|
||||||
|
├── dev / # Anything related to early development or tests
|
||||||
|
│ ├── boards
|
||||||
|
│ ├── concepts
|
||||||
|
│ └── tests
|
||||||
|
├── pre/ # For pre-production
|
||||||
|
│ ├── assets
|
||||||
|
│ └── shots
|
||||||
|
├── edit # Where the editorial .blend file lives
|
||||||
|
├── pro/ # All files from the production
|
||||||
|
│ ├── assets/ # All assets from the production
|
||||||
|
│ │ ├── cam # Camera rig & setup
|
||||||
|
│ │ ├── chars # Characters & character variations
|
||||||
|
│ │ ├── fx # Effects
|
||||||
|
│ │ ├── lgt # Lighting setups
|
||||||
|
│ │ ├── lib
|
||||||
|
│ │ ├── maps # General textures and HDRIs
|
||||||
|
│ │ ├── nodes # General Node groups
|
||||||
|
│ │ ├── poses # Pose libraries for animation
|
||||||
|
│ │ ├── props
|
||||||
|
│ │ ├── scripts
|
||||||
|
│ │ └── sets
|
||||||
|
│ ├── config
|
||||||
|
│ └── shots #Structured into sequences
|
||||||
|
└── tools
|
||||||
|
```
|
||||||
|
## Kitsu Server Setup
|
||||||
|
Kitsu is a project management software used by the Blender Studio for task management. The server is used to assign tasks to artists, as well as a track each shot as it moves through production The Kitsu server is required for automatically building shots.
|
||||||
|
|
||||||
|
1. Follow the Official [setup instructions](https://kitsu.cg-wire.com/installation/) for Kitsu Server
|
||||||
|
2. Upon entering the Kitsu web interface follow the [first production](https://kitsu.cg-wire.com/first_production/) guide
|
||||||
|
|
||||||
|
## Blender Setup
|
||||||
|
The next step is to deploy the required software onto each of the studio's workstations.
|
||||||
|
|
||||||
|
1. Download the latest Blender
|
||||||
|
```bash
|
||||||
|
cd /data/{my_project}/svn/tools
|
||||||
|
./update_blender.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will download the latest blender to `/data/{my_project}/local/blender`
|
||||||
|
|
||||||
|
::: info Choosing Branch to Install
|
||||||
|
You can specify a [daily build](https://builder.blender.org/download/daily/) branch to fetch by editing the `BLENDER_BRANCH` variable in the script file.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Install Blender Add-Ons
|
||||||
|
2. Download required Add-Ons
|
||||||
|
```bash
|
||||||
|
mkdir /data/{my_project}/shared/artifacts/addons
|
||||||
|
cd /data/blender-studio-pipeline/scripts/pipeline-release
|
||||||
|
./package_local.py /data/{my_project}/shared/artifacts/addons
|
||||||
|
```
|
||||||
|
### Install Blender Icon
|
||||||
|
|
||||||
|
If a desktop icon is preferred to launch blender
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /data/{my_project}/svn/tools
|
||||||
|
./install_desktop_file.sh
|
||||||
|
```
|
||||||
|
::: warning Linux only feature
|
||||||
|
October 19th 2023 -This feature is only available on linux at this time. Mac & Windows Users must launch Blender via the terminal.
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### Setup Blender Add-Ons
|
||||||
|
|
||||||
|
3. Follow the below instructions to correctly set the preferences of each Add-On
|
||||||
|
|
||||||
|
## Blender Kitsu Add-On Preferences
|
||||||
|
|
||||||
|
1. Open Blender and Select `Edit>Preferences>Add-Ons`
|
||||||
|
2. Search the 'Blender Kitsu' and use the checkbox to Enable the Add-On
|
||||||
|
3. Set the following settings in the add-on preferences
|
||||||
|
- Login
|
||||||
|
- Host: `{my_kitsu_server_url}` *Set during [Kitsu Server Setup](/td-guide/project-tools-setup.md#kitsu-server-setup)*
|
||||||
|
- Username: `{username@studio.org}`
|
||||||
|
- Password: `{user_password}`
|
||||||
|
- Project Settings
|
||||||
|
- Select Production: Choose the current Production
|
||||||
|
- Project Root Directory: `/data/{my_project}/svn`
|
||||||
|
- Animation Tools
|
||||||
|
- Playblast directory: `/data/{my_project}/shared/editorial/footage/pro/`
|
||||||
|
- Frames Directory: `/data/{my_project}/shared/editorial/footage/post/`
|
||||||
|
- Editorial Export Directory (Optional)
|
||||||
|
- `/data/{my_project}/shared/editorial/export/`
|
||||||
|
<!--
|
||||||
|
TODO Replace Image
|
||||||
|
-->
|
||||||
|
![Blender Kitsu Preferences](/media/td-guide/kitsu_pref.jpg)
|
||||||
|
|
||||||
|
## Render Review Add-On Preferences
|
||||||
|
1. Open Blender and Select `Edit>Preferences>Add-Ons`
|
||||||
|
2. Search the 'Render Review' and use the checkbox to Enable the Add-On
|
||||||
|
3. Set the following settings in the add-on preferences
|
||||||
|
- Ensure `Enable Blender Kitsu` is Enabled
|
||||||
|
- Render Farm: `/data/{my_project}/render/`
|
||||||
|
- Shot Frames: `/data/{my_project}/shared/editorial/footage/post/`
|
||||||
|
- Shot Previews: `/data/{my_project}/shared/editorial/footage/pro/`
|
||||||
|
|
||||||
|
![Render Review Preferences](/media/td-guide/render_review_pref.jpg)
|
||||||
|
<!--
|
||||||
|
TODO Replace Image
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Flamenco Setup
|
||||||
|
1. Create Flamenco shared storage directory
|
||||||
|
```bash
|
||||||
|
mkdir /data/flamenco_storage
|
||||||
|
```
|
||||||
|
1. Follow the instructions at https://flamenco.blender.org/usage/quickstart/ to setup Flamenco
|
||||||
|
2. During Flamenco Manager Setup use `/data/flamenco_storage` as the shared storage directory
|
||||||
|
3. During Flamenco Manager Setup use `/data/{my_project}/local/blender/{os}/blender` as the Blender Executable Path
|
5
docs/user-guide/organization/planning.md
Normal file
5
docs/user-guide/organization/planning.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Planning
|
||||||
|
|
||||||
|
::: work in progress
|
||||||
|
October 13th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
5
docs/user-guide/organization/task-review.md
Normal file
5
docs/user-guide/organization/task-review.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Task Review - in practice
|
||||||
|
|
||||||
|
::: Work in progress
|
||||||
|
October 13th 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
@ -1,13 +0,0 @@
|
|||||||
# Project Setup
|
|
||||||
|
|
||||||
::: warning Work in Progress
|
|
||||||
30 Apr. 2023 - The content of this page is currently being edited/updated.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Write how to set up a project in a fully configured workstation and production enviroment. Probably links to other parts of the reference.
|
|
||||||
|
|
||||||
* Ensure SVN access
|
|
||||||
* Ensure that Blender is configured with the 'shared' add-ons directory
|
|
||||||
* Ensure Kitsu access
|
|
||||||
* Checkout SVN project in the appropriate location
|
|
||||||
* ...
|
|
99
docs/user-guide/project_tools/project-blender.md
Normal file
99
docs/user-guide/project_tools/project-blender.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Project Blender
|
||||||
|
|
||||||
|
Project Tools will store a version of Blender within the `shared` directory. This version of Blender is internal to that project. This allows for multiple Blenders to be installed on your system, each with their own preferences tailored specifically to that project. The main advantage to running/managing Blender using the Project Tools scripts is that it will synchronize the Blender version and Shared Add-Ons across for all users contributing the the project. Project Tools also allows you to run a custom build of Blender with the Add-Ons and preferences set for your project.
|
||||||
|
|
||||||
|
## Create Blender Shortcut
|
||||||
|
|
||||||
|
Once your project has been setup using the "Project Tools" scripts Blender should be available inside your application's native application launcher. The run Blender script will take the correct blender version for your operating system from `{my_project}/shared/artifacts/blender` and extract it to the local directory. Along with any add-ons in the `{my_project}/shared/artifacts/addons` folder. Your Blender preferences are stored on a per project basis in `{directory-path}`
|
||||||
|
|
||||||
|
### Create Linux Shortcut
|
||||||
|
```bash
|
||||||
|
cd {my_project}/svn/tools
|
||||||
|
./install_desktop_file.sh
|
||||||
|
```
|
||||||
|
::: info Available on Gentoo
|
||||||
|
To learn more about running the Blender if you are on a Gentoo system please see the [Gentoo guide](/gentoo/user/running-blender.md), including how to run a [debug build](/gentoo/user/running-blender.md#debug-build).
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### Launch with Custom Build on Linux
|
||||||
|
You must run the Create Linux Shortcut step before running a custom build. This will launch blender using your custom binary, but with the Add-Ons and preferences of your project.
|
||||||
|
|
||||||
|
1. Navigate to your custom Blender binary
|
||||||
|
2. Right Click the binary
|
||||||
|
3. Select `Open with > Blender {my_project}`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!---
|
||||||
|
TODO Replace Image with Project-Tools version
|
||||||
|
|
||||||
|
![Image of Blender Icon in KDE Taskbar/Start Menu](/media/user-guide/launch_blender.mp4)
|
||||||
|
--->
|
||||||
|
|
||||||
|
### Create Windows Shortcut
|
||||||
|
|
||||||
|
1. Open the directory `{my_project}/svn/tools`
|
||||||
|
2. Create a shortcut to `launch_blender_win.bat` on your desktop
|
||||||
|
|
||||||
|
### Create Mac Shortcut
|
||||||
|
|
||||||
|
1. Open the directory `{my_project}/svn/tools`
|
||||||
|
2. In finder, select the `launch_blender_mac.command` and press `ctrl+shift+command+t` to add it to the dock.
|
||||||
|
|
||||||
|
|
||||||
|
## Launch Blender from Terminal
|
||||||
|
|
||||||
|
To launch Blender from the terminal, open the tools directory within your project folder, and from the terminal use the run Blender script.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /{my_project}/svn/tools
|
||||||
|
./run_blender.py
|
||||||
|
```
|
||||||
|
|
||||||
|
::: warning Command Line Arguments
|
||||||
|
Note: Command Line Arguments also known as Flags are not supported by the `run_blender.py` script.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Update Blender
|
||||||
|
|
||||||
|
This script will fetch the latest Blender download from https://builder.blender.org/download/ The Blender download for Linux, Mac, and Windows will be downloaded into the `{my_project}/shared/artifacts/blender` folder. It will keep up to 10 previous downloaded versions for backup. This Blender doesn't update automatically, at least one user in the project must manually initiate an update, all users will receive this update because blender is stored within the `shared` directory.
|
||||||
|
|
||||||
|
::: info Blender Studio Users
|
||||||
|
Internally to the Blender Studio only, the blender inside your project is automatically updated overnight, not manual update is required.
|
||||||
|
:::
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /{my_project}/svn/tools
|
||||||
|
./update_blender.py
|
||||||
|
```
|
||||||
|
## Rollback Blender
|
||||||
|
|
||||||
|
Use `rollback_blender.py` to switch the "current" version hosted in `{my_project}/shared/artifacts/blender` to one the older downloads, rolling back affects all users using your project. This is intended to be used to rollback to an older version in case of bugs in newer downloaded versions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /{my_project}/svn/tools
|
||||||
|
./rollback_blender.py
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Rollback Blender Locally
|
||||||
|
|
||||||
|
In some cases user may want to roll their machine's local blender to a previous version without affecting other users.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /{my_project}/svn/tools
|
||||||
|
./rollback_blender_local.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update Blender Add-Ons
|
||||||
|
Blender Add-ons can be packaged directly from the [Blender Studio Pipeline](https://projects.blender.org/studio/blender-studio-pipeline) repository. Personal Add-Ons can be installed [normally](https://docs.blender.org/manual/en/latest/editors/preferences/addons.html#installing-add-ons).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /blender-studio-pipeline/scripts/pipeline-release
|
||||||
|
./package_local.py /{my_project}/shared/artifacts/addons
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
::: info Blender Studio Users
|
||||||
|
Flamenco is installed and updated by the package manager of your Gentoo workstation. To learn more see [Update Local Add-Ons](/gentoo/td/maintaince#update-local-add-ons) in the Gentoo section.
|
||||||
|
:::
|
27
docs/user-guide/project_tools/project-overview.md
Normal file
27
docs/user-guide/project_tools/project-overview.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Project Overview
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
"Project Tools" is a collection of scripts included in the [Blender Studio Pipeline](https://projects.blender.org/studio/blender-studio-pipeline) repository, developed to assist you in running and managing one or more projects. These scripts automate and standardize many common operations involved in setting up a production pipeline.
|
||||||
|
|
||||||
|
It all starts with the directory layout, which should be deployed by a Technical Director following the [Project Tools Setup Guide](/td-guide/project-tools-setup.md). This standard directory layout defines where things like .blend files and playblasts are stored. It enables project tools the ability to ensure all users are running the same Blender and have a similar experience with minimal setup required by the individual users.
|
||||||
|
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
Typically projects are stored at the following path `/data/{my_project}` where `data` is at the root of the filesystem. This is for consistency between computers at the Blender Studio. External studios can use a different root folder for their projects for example a user's home folder.
|
||||||
|
|
||||||
|
The project folder contains all data related to a project including .blend files, playblasts, the blender that is used on the project for all operating systems and even preferences are stored within the project.
|
||||||
|
|
||||||
|
* `local` This is where the local copy of Blender and the add-ons will be installed. This directory is populated by the `run_blender.py` script with the Blender & Add-Ons from `shared`.
|
||||||
|
* `shared` This is the folder that should be shared over the network, it contains renders, playblast and other items that don't require version control. (By using Syncthing, NFS shares, Samba, Dropbox, etc)
|
||||||
|
* `svn` This the versioned controlled folder where the .blend production files will live.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.
|
||||||
|
└── my_project/
|
||||||
|
├── local # The local copy of Blender and the add-ons will be installed.
|
||||||
|
├── shared # Shared over the network (Syncthing, NFS, Dropbox, etc)
|
||||||
|
└── svn # Contains the `.blend` production files. (SVN, GIT-LFS, etc)
|
||||||
|
```
|
||||||
|
|
||||||
|
To learn the layout of the above directories, see the [`shared`](/naming-conventions/shared-folder-structure.md) and [`svn`](/naming-conventions/svn-folder-structure.md) directory overviews.
|
131
docs/user-guide/project_tools/project-usage.md
Normal file
131
docs/user-guide/project_tools/project-usage.md
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# Project Usage
|
||||||
|
|
||||||
|
Once your project is set-up there are several things that users can do when using the pipeline, including creating new shots on Kitsu directly from Blender, automatically building shots based on Kitsu data and updating the frame range of existing shots within the project.
|
||||||
|
|
||||||
|
::: warning Work in Progress
|
||||||
|
15 Oct. 2023 - The content of this page is currently being edited/updated.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Sync your Edit with Kitsu Server
|
||||||
|
Most productions begin with a previz or storyboard step, showing the overall direction and plan for the production. By inputting this as video strip(s) into a VSE file we can automatically create the corresponding shots on the Kitsu Server directly from the VSE.
|
||||||
|
|
||||||
|
1. If not already ensure your project settings are setup [Blender Kitsu Add-On Preferences](https://studio.blender.org/pipeline/addons/blender_kitsu#how-to-get-started)
|
||||||
|
2. At the directory `{my_project}/svn/edit` create a new "Video Editing" File.
|
||||||
|
3. Populate your new edit file with your previz video strips
|
||||||
|
4. With your first strip selected, in the Blender Kitsu side panel of the VSE select "Create Metastrip Active Shot” to create a new metastrip.
|
||||||
|
5. Next select “Init Active Shot”, and enter the Shot and Sequence names you would like to submit to Kitsu.
|
||||||
|
6. Finally select the “Submit New Shot Button” to submit this new Shot to the Kitsu Server.
|
||||||
|
|
||||||
|
Repeat steps 4-6 for each shot in the sequence. Multiple metastrips can be made out of a single previz strip if required, adjust the shot’s timing by simply trimming the metastrip in the timeline. Below is an example of a previz sequence with metastrips.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Creating your first Asset
|
||||||
|
The next step is to create an asset and store that information into the Kitsu Server.
|
||||||
|
|
||||||
|
1. Launch Blender via [Project Blender](/user-guide/project_tools/project-blender.md) Guide
|
||||||
|
2. Under `Edit>Preferences>Add-Ons` ensure `Asset Pipeline` is enabled
|
||||||
|
3. Follow the[ asset pipeline guide](https://studio.blender.org/pipeline/addons/asset_pipeline#how-to-get-started) to create a new asset (optional)
|
||||||
|
4. Save the above asset within the directory `{my_project}/svn/pro/assets/char`
|
||||||
|
5. Create a matching entry in Kitsu for the above asset via the [Create Assets guide](https://kitsu.cg-wire.com/first_production/#create-assets)
|
||||||
|
6. Follow the [casting guide](https://forum.cg-wire.com/t/breakdown-casting-widget-for-kitsu/31) in the breakdown section to assign assets to shots.
|
||||||
|
|
||||||
|
|
||||||
|
## Building your First Shot
|
||||||
|
Inside your production’s directory the Shot Builder configuration files need to be created using the [examples](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs/examples) included in the Add-On directory. See the Shot Builder Config [Directory Layout](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs#directory-layout) for details. The only configuration file that requires production specific edits is `assets.py`. This configuration file links the Kitsu Asset entries to their corresponding files in the production directory.
|
||||||
|
|
||||||
|
|
||||||
|
1. Copy Configuration Files from [examples](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs/examples) to `{my_project}/svn/pro/shot-builder/`
|
||||||
|
2. Update `assets.py` file with each asset you have in your `assets` folder see the [Shot Builder API](https://projects.blender.org/studio/blender-studio-pipeline/src/branch/main/scripts-blender/addons/blender_kitsu/shot_builder/docs#api)
|
||||||
|
3. Open a new Blender File, select `File>New>Shot File`
|
||||||
|
4. Select the desired Sequence/Shot from Kitsu and select OK to start Building
|
||||||
|
5. New file will be saved to `{my_project}/svn/pro/shots/{sequence}/{shot}/{shot}.blend`
|
||||||
|
|
||||||
|
## Playblast your First Shot
|
||||||
|
Once your first shot is animated you are ready to render a playblast of this shot, which will be later imported into your edit .blend file.
|
||||||
|
1. Launch Blender via [Launching Software] Guide and Open a Shot
|
||||||
|
2. In the Kitsu Sidepanel, under context, use the refresh icon to reload your file's context if it is not set already.
|
||||||
|
3. In the Kitsu Sidepanel under Playblast tools you are now ready to create a new playblast version, select `Create Playblast` which will render a preview of your shot and add it as a comment on your Kitsu Task for this shot
|
||||||
|
1. Status: select the status to set your Kitsu Task to
|
||||||
|
2. Comment: add any notes you would like to include in your comment
|
||||||
|
3. Use Current Viewport Shading: Enable this to render will the settings from your current viewport
|
||||||
|
4. Thumbnail Frame: Which frame in your current file should be the thumbnail for your preview file
|
||||||
|
|
||||||
|
## Loading First Playblast into the Edit
|
||||||
|
For each new task type, Anim/Layout etc needs to be added manually, then it can update the shot afterwards
|
||||||
|
|
||||||
|
Returning to your edit .blend file, we can now load the playblast from the animation file into the edit.
|
||||||
|
|
||||||
|
1. Open your edit .blend file inside the directory `/{my_project}/svn/edit`
|
||||||
|
2. From the Sequencer Header select `Add>Movie`
|
||||||
|
3. Navigate to the directory of the playblast for your shot's .blend file `{my_project}/shared/footage/pro/{sequence_name}/{shot_name}/{file_name}` and select the `.mp4` file
|
||||||
|
4. Place the new shot at the same timing as the corresponding metastrip
|
||||||
|
|
||||||
|
## Adjusting a Shot's Frame Range
|
||||||
|
During production in some cases the frame range of a shot will change, either adjusting a shot's length or adjusting it's position in the edit. Once adjusted, we can update the shot .blend file's frame range so new playblasts will match this updated frame range. Once a new playblast is available the shot can automatically be updated in the VSE via the Blender Kitsu Add-On
|
||||||
|
|
||||||
|
1. Open your edit .blend file inside the directory `{my_project}/svn/edit`
|
||||||
|
2. Select a shot and it's metastrip, adjust the timing of both strips so they remain in sync.
|
||||||
|
3. Select your metastrip, in the Kitsu Sidebar of the VSE Under Push select `Metadata 1 Shot` to push your shot's new frame range to the Kitsu Server
|
||||||
|
4. Open your shot .blend file inside the directory
|
||||||
|
`{my_project}/svn/pro/shots/{sequence_name}/{shot_name}/`
|
||||||
|
5. Inside the Kitsu Sidebar, under Playblast tools, if your frame range on Kitsu has changed you will see a red `Pull Frame Range` button. Select it to update the file's Frame Range
|
||||||
|
6. Adjust the shot's animation to accommodate the new frame range, then under Playblast use the `+` button to create a new version, then select `Create Playblast` to render a new playblast
|
||||||
|
7. Open your edit .blend file, and select the movie strip for the shot you would like to update.
|
||||||
|
8. In the Kitsu Sidebar under General Tools select, `^` to load the next playblast from that shot automatically
|
||||||
|
|
||||||
|
## Render Shot with Flamenco
|
||||||
|
<!--- TODO improve description --->
|
||||||
|
Once your shots are all ready to go, you can now render a final EXR from each of your shot files.
|
||||||
|
|
||||||
|
1. Open a shot file
|
||||||
|
2. In the properties panel navigate to Output, and set your file format to OpenEXR with Previews Enabled
|
||||||
|
3. In the properties panel navigate under Flamenco
|
||||||
|
1. Select `Fetch Job Types`
|
||||||
|
2. From the Dropdown select `Simple Blender Render`
|
||||||
|
3. Set Render Output Directory to `{my_project}/render/`
|
||||||
|
4. Set Add Path Components to `3`
|
||||||
|
5. Finally Select `Submit to Flamenco`
|
||||||
|
|
||||||
|
## Review and Approve Renders
|
||||||
|
Once your shot(s) have been rendered by Flamenco, you are ready to review your renders using the Render Review Add-On. This Add-On allows you to review different versions of your Render, assuming you rendered with Flamenco for multiple versions, and select the version you would like to use as your approved version.
|
||||||
|
|
||||||
|
1. Ensure Blender Kitsu Add-On is enabled and Logged In
|
||||||
|
2. From `File>New>Render Review`
|
||||||
|
3. From the dialogue box select a Sequence Name you would like to Review
|
||||||
|
4. Select the video strip for the render you are reviewing
|
||||||
|
5. Select `Push to Edit & Approve Render`
|
||||||
|
- Push to Edit will take the mp4 of your flamenco render and add it to the `{my_project}/shared/editorial/shots` directory
|
||||||
|
- Approve Render will copy the Image Sequence of your flamenco render to the `{my_project}/shared/editorial/frames` directory
|
||||||
|
|
||||||
|
## Import Approved Renders into VSE
|
||||||
|
Renders approved by the render review Add-On can be automatically imported into your edit using the same function used to update the playblast of shots in the edit.
|
||||||
|
|
||||||
|
1. Open your Edit .blend file
|
||||||
|
2. Select the video strip representing the shot that has an approved render. In the Kitsu Sidebar under General Tools select, `^` to load the next playblast from that shot automatically, which is an **mp4 preview** of your final render
|
||||||
|
3. Select the video strips representing all the shots you have approved renders for. Use `Shot as Image Sequence` to import the final image sequences for each shot as EXR or JPG and load it to a new channel in the VSE
|
||||||
|
|
||||||
|
## Final Render
|
||||||
|
Once the approved image sequences have been loaded into the main edit you are ready to create a final render of your film.
|
||||||
|
|
||||||
|
1. Open your Edit .blend file
|
||||||
|
2. Render Video as PNG Sequence
|
||||||
|
1. Under `Properties>Output` Set the output directory to `{my_project}/shared/editorial/deliver/frames/`
|
||||||
|
2. Set the File Format to `PNG`
|
||||||
|
3. Select `Render>Render Animation`
|
||||||
|
3. Render Audio
|
||||||
|
1. Select `Render>Render Audio`
|
||||||
|
2. In the Side Panel select Container `.wav`
|
||||||
|
3. Set the output directory to `{my_project}/shared/editorial/deliver/audio/`
|
||||||
|
4. Run Deliver script
|
||||||
|
1. Copy the `delivery.py` from `{my_project}/blender-studio-pipeline/film-delivery/` to the directory `{my_project}/shared/editorial/deliver/`
|
||||||
|
2. Enter delivery directory `cd /{my_project}/shared/editorial/deliver/
|
||||||
|
3. Encode audio with `./deliver.py --encode_audio audio/{name_of_audio}.wav`
|
||||||
|
4. Encode video with `.deliver.py --encode_video frames/`
|
||||||
|
5. Finally `.delivery.py --mux`
|
||||||
|
5. Final Render will be found in the `mux` directory
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
|||||||
# Blender Add-ons
|
# Blender Add-ons
|
||||||
|
|
||||||
Add-ons used by the Blender Studio pipeline. Download the latest addons releases from the table below. To review or report issues visit the [Blender-Studio-Pipeline](https://projects.blender.org/studio/blender-studio-pipeline/issues) issues board.
|
Add-ons used by the Blender Studio pipeline. To review or report issues visit the [Blender-Studio-Pipeline](https://projects.blender.org/studio/blender-studio-pipeline/issues) issues board.
|
||||||
|
|
||||||
|
| Add-on | Description |
|
||||||
|
|---|---|
|
||||||
|
|Anim Cupboard |Miscellaneous tools for animators.
|
||||||
|
|Asset Pipeline |Manages the Asset Pipeline, used by Modeling, Shading and Rigging departments.
|
||||||
|
|Blender Kitsu |Enforce conventions, build shots, manage production files and update data on kitsu server.
|
||||||
|
|Blender SVN |UI for the SVN (Subversion) file versioning system.
|
||||||
|
|Blender Gizmos |Attempt to prototype a system for using meshes for the manipulation of armatures.
|
||||||
|
|Cache Manager |Streamline the Alembic cache workflow of assets.
|
||||||
|
|Contact Sheet |Create a contactsheet from sequence editor strips.
|
||||||
|
|Easy Weights |Quality of life improvements for weight painting.
|
||||||
|
|Geonode Shapekeys |Enable animators to sculpt on linked and overridden meshes.
|
||||||
|
|Grease Converter |Convert annotations to Grease Pencil objects and vise versa.
|
||||||
|
|Lattice Magic |Lattice-based utilities.
|
||||||
|
|Lighting Overrider |Create, manage and apply python overrides in a flexible and reliable way.
|
||||||
|
|Pose Shape Keys |Manage and maintain shapekeys for rigging.
|
||||||
|
|Render Review |Review renders from Flamenco with the sequence editor.
|
||||||
|
|
||||||
| Add-on | Description | Latest Version | Checksum |
|
Download release packages of the above add-ons from the [Add-on Release Table](https://studio.blender.org/pipeline/addons/overview) page.
|
||||||
|---|---|---|---|
|
|
||||||
|[Anim Cupboard](../addons/anim_cupboard) |Miscellaneous tools for animators. |[↓ v0.0.4](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/anim_cupboard-0.0.4.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/anim_cupboard-0.0.4.sha256)|
|
Download previous release of the above add-ons from the [Releases](https://projects.blender.org/studio/blender-studio-pipeline/releases) page.
|
||||||
|[Asset Pipeline](../addons/asset_pipeline ) |Manages the Asset Pipeline, used by Modeling, Shading and Rigging departments. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/asset_pipeline-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/asset_pipeline-0.1.2.sha256)|
|
|
||||||
|[Blender Kitsu](../addons/blender_kitsu )|Enforce conventions, build shots, manage production files and update data on kitsu server. |[↓ v0.1.3](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/blender_kitsu-0.1.3.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/blender_kitsu-0.1.3.sha256)|
|
|
||||||
|[Blender SVN](../addons/blender_svn ) |UI for the SVN (Subversion) file versioning system. |[↓ v1.0.1](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/blender_svn-1.0.1.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/blender_svn-1.0.1.sha256)|
|
|
||||||
|[Blender Gizmos](../addons/bone_gizmos )|Attempt to prototype a system for using meshes for the manipulation of armatures. |[↓ v0.0.3](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/bone_gizmos-0.0.3.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/bone_gizmos-0.0.3.sha256)|
|
|
||||||
|[Cache Manager](../addons/cache_manager ) |Streamline the Alembic cache workflow of assets. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/cache_manager-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/cache_manager-0.1.2.sha256)|
|
|
||||||
|[Contact Sheet](../addons/contactsheet ) |Create a contactsheet from sequence editor strips. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/contactsheet-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/contactsheet-0.1.2.sha256)|
|
|
||||||
|[Easy Weights](../addons/easy_weights ) |Quality of life improvements for weight painting. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/easy_weights-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/easy_weights-0.1.2.sha256)|
|
|
||||||
|[Geonode Shapekeys](../addons/geonode_shapekeys ) |Enable animators to sculpt on linked and overridden meshes. |[↓ v0.0.3](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/geonode_shapekeys-0.0.3.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/geonode_shapekeys-0.0.3.sha256)|
|
|
||||||
|[Grease Converter](../addons/grease_converter ) |Convert annotations to Grease Pencil objects and vise versa. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/grease_converter-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/grease_converter-0.1.2.sha256)|
|
|
||||||
|[Lattice Magic](../addons/lattice_magic ) |Lattice-based utilities. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/lattice_magic-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/lattice_magic-0.1.2.sha256)|
|
|
||||||
|[Lighting Overrider](../addons/lighting_overrider ) |Create, manage and apply python overrides in a flexible and reliable way. |[↓ v0.1.2](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/lighting_overrider-0.1.2.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/lighting_overrider-0.1.2.sha256)|
|
|
||||||
|[Pose Shape Keys](../addons/pose_shape_keys )|Manage and maintain shapekeys for rigging. |[↓ v0.0.3](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/pose_shape_keys-0.0.3.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/pose_shape_keys-0.0.3.sha256)|
|
|
||||||
|[Render Review](../addons/render_review ) |Review renders from Flamenco with the sequence editor. |[↓ v0.1.3](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/render_review-0.1.3.zip) |[↓ SHA256](https://projects.blender.org/studio/blender-studio-pipeline/releases/download/0.0.1/render_review-0.1.3.sha256)|
|
|
@ -1,3 +1,16 @@
|
|||||||
|
## 0.0.5 - 2023-10-31
|
||||||
|
|
||||||
|
### ADDED
|
||||||
|
- Add "Instancer Empty to Collection" op
|
||||||
|
- Use consistent registration pattern
|
||||||
|
- Expose preference about library warning pop-ups
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
- Fix a crash in hotkeys.py
|
||||||
|
- Catch an error (not sure of cause yet)
|
||||||
|
- Prod file cleaning utils
|
||||||
|
- Update hotkey registration code
|
||||||
|
|
||||||
## 0.0.4 - 2023-08-02
|
## 0.0.4 - 2023-08-02
|
||||||
|
|
||||||
### ADDED
|
### ADDED
|
||||||
|
@ -12,7 +12,7 @@ from . import (
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
'name': "Animation Cupboard",
|
'name': "Animation Cupboard",
|
||||||
'author': "Demeter Dzadik",
|
'author': "Demeter Dzadik",
|
||||||
"version": (0, 0, 4),
|
"version": (0, 0, 5),
|
||||||
'blender': (3, 2, 0),
|
'blender': (3, 2, 0),
|
||||||
'description': "Tools to improve animation workflows",
|
'description': "Tools to improve animation workflows",
|
||||||
'location': "Various",
|
'location': "Various",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,27 @@
|
|||||||
|
## 0.1.4 - 2023-10-31
|
||||||
|
|
||||||
|
### ADDED
|
||||||
|
- Make Frames directory customizable
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
- Fix Drawing Sequence Line (#157)
|
||||||
|
- Fix Scene Name in Shot Builder Config File
|
||||||
|
- Fix EXR Colorspace Name
|
||||||
|
- Fix Bug "3d_start" missing on new shots
|
||||||
|
- Fix EXR Colorspace Name
|
||||||
|
- Fix Scene Name in Shot Builder Config File
|
||||||
|
- Fix Bug "3d_start" missing on new shots
|
||||||
|
- Fix Shot Builder example<6C>`assets.py`
|
||||||
|
- Fix Shot Builder example `shots.py`
|
||||||
|
- Fix Get Project ID from Kitsu Server in Shot Builder
|
||||||
|
- Fix Context Override Error in Shot Builder
|
||||||
|
- Update Shot Builder Hook Example
|
||||||
|
- Use New Shared Folder Structure
|
||||||
|
|
||||||
|
### REMOVED
|
||||||
|
- remove "boxed" ui for playblast
|
||||||
|
|
||||||
|
|
||||||
## 0.1.3 - 2023-08-02
|
## 0.1.3 - 2023-08-02
|
||||||
|
|
||||||
### ADDED
|
### ADDED
|
||||||
|
@ -195,6 +195,13 @@ The `Multi Edit` panel only appears when you select multiple metastrips that are
|
|||||||
|
|
||||||
It is meant to be way to quickly setup lots of shots if they don't exist on Kitsu yet. You specify the sequence all shots should belong to and adjust the `Shot Counter Start` value. In the preview property you can see how all shots will be named when you execute the `Multi Edit Strip` operator. <br/>
|
It is meant to be way to quickly setup lots of shots if they don't exist on Kitsu yet. You specify the sequence all shots should belong to and adjust the `Shot Counter Start` value. In the preview property you can see how all shots will be named when you execute the `Multi Edit Strip` operator. <br/>
|
||||||
|
|
||||||
|
##### Shot as Image Sequence
|
||||||
|
The `Shot as Image Sequence` Operator will replace a playblast from your Playblast Root directory with an image sequence located in the Frames Root directory. The Shots Directory and the Frames Directory should have matching folder structures. Typically the format is `/{sequence_name}/{shot_name}/{shot_name}-{shot_task}/`
|
||||||
|
|
||||||
|
![Shot as Image Sequence](/media/addons/blender_kitsu/Shot_as_Image_Sequence.jpg)
|
||||||
|
|
||||||
|
Use this operator to replace playblasts with image sequences that have been approved via the [Render Review Add-On](/addons/render_review) Image Sequences can be loaded as either `EXR` or `JPG` sequences.
|
||||||
|
|
||||||
###### Advanced Settings
|
###### Advanced Settings
|
||||||
If you check the `Advanced` checkbox next to the counter value, you have access to advance settings to customize the operator even more.
|
If you check the `Advanced` checkbox next to the counter value, you have access to advance settings to customize the operator even more.
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ bl_info = {
|
|||||||
"author": "Paul Golter",
|
"author": "Paul Golter",
|
||||||
"description": "Blender addon to interact with Kitsu",
|
"description": "Blender addon to interact with Kitsu",
|
||||||
"blender": (2, 93, 0),
|
"blender": (2, 93, 0),
|
||||||
"version": (0, 1, 3),
|
"version": (0, 1, 4),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
"doc_url": "",
|
"doc_url": "",
|
||||||
|
@ -26,7 +26,7 @@ VERSION_PATTERN = r"v\d\d\d"
|
|||||||
FRAME_START = 101
|
FRAME_START = 101
|
||||||
|
|
||||||
SHOT_DIR_NAME = "shots"
|
SHOT_DIR_NAME = "shots"
|
||||||
ASSET_DIR_NAME = "lib"
|
ASSET_DIR_NAME = "assets"
|
||||||
|
|
||||||
ASSET_TASK_MAPPING = {
|
ASSET_TASK_MAPPING = {
|
||||||
"geometry": "Geometry",
|
"geometry": "Geometry",
|
||||||
|
@ -39,7 +39,7 @@ class KITSU_PT_vi3d_playblast(bpy.types.Panel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
bl_category = "Kitsu"
|
bl_category = "Kitsu"
|
||||||
bl_label = "Playblast Tools"
|
bl_label = "Playblast"
|
||||||
bl_space_type = "VIEW_3D"
|
bl_space_type = "VIEW_3D"
|
||||||
bl_region_type = "UI"
|
bl_region_type = "UI"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
bl_options = {"DEFAULT_CLOSED"}
|
||||||
@ -47,9 +47,7 @@ class KITSU_PT_vi3d_playblast(bpy.types.Panel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context: bpy.types.Context) -> bool:
|
def poll(cls, context: bpy.types.Context) -> bool:
|
||||||
return bool(
|
return bool(prefs.session_auth(context))
|
||||||
prefs.session_auth(context)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll_error(cls, context: bpy.types.Context) -> bool:
|
def poll_error(cls, context: bpy.types.Context) -> bool:
|
||||||
@ -82,12 +80,8 @@ class KITSU_PT_vi3d_playblast(bpy.types.Panel):
|
|||||||
if self.poll_error(context):
|
if self.poll_error(context):
|
||||||
self.draw_error(context)
|
self.draw_error(context)
|
||||||
|
|
||||||
# Playblast box.
|
|
||||||
box = layout.box()
|
|
||||||
box.label(text="Playblast")
|
|
||||||
|
|
||||||
# Playblast version op.
|
# Playblast version op.
|
||||||
row = box.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator(
|
row.operator(
|
||||||
KITSU_OT_playblast_set_version.bl_idname,
|
KITSU_OT_playblast_set_version.bl_idname,
|
||||||
text=context.scene.kitsu.playblast_version,
|
text=context.scene.kitsu.playblast_version,
|
||||||
@ -101,12 +95,12 @@ class KITSU_PT_vi3d_playblast(bpy.types.Panel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Playblast op.
|
# Playblast op.
|
||||||
row = box.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator(KITSU_OT_playblast_create.bl_idname, icon="RENDER_ANIMATION")
|
row.operator(KITSU_OT_playblast_create.bl_idname, icon="RENDER_ANIMATION")
|
||||||
|
|
||||||
# Playblast path label.
|
# Playblast path label.
|
||||||
if Path(context.scene.kitsu.playblast_file).exists():
|
if Path(context.scene.kitsu.playblast_file).exists():
|
||||||
split = box.split(factor=1 - split_factor_small, align=True)
|
split = layout.split(factor=1 - split_factor_small, align=True)
|
||||||
split.label(icon="ERROR")
|
split.label(icon="ERROR")
|
||||||
sub_split = split.split(factor=split_factor_small)
|
sub_split = split.split(factor=split_factor_small)
|
||||||
sub_split.label(text=context.scene.kitsu.playblast_file)
|
sub_split.label(text=context.scene.kitsu.playblast_file)
|
||||||
@ -114,7 +108,7 @@ class KITSU_PT_vi3d_playblast(bpy.types.Panel):
|
|||||||
KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text=""
|
KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text=""
|
||||||
).filepath = context.scene.kitsu.playblast_file
|
).filepath = context.scene.kitsu.playblast_file
|
||||||
else:
|
else:
|
||||||
row = box.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text=context.scene.kitsu.playblast_file)
|
row.label(text=context.scene.kitsu.playblast_file)
|
||||||
row.operator(
|
row.operator(
|
||||||
KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text=""
|
KITSU_OT_open_path.bl_idname, icon="FILE_FOLDER", text=""
|
||||||
|
@ -203,6 +203,13 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
|
|||||||
update=init_playblast_file_model,
|
update=init_playblast_file_model,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
frames_root_dir: bpy.props.StringProperty( # type: ignore
|
||||||
|
name="Frames Root Directory",
|
||||||
|
description="Directory path to playblast root folder. Should point to: ./Blender Dropbox/render/sprites/shots/",
|
||||||
|
default="",
|
||||||
|
subtype="DIR_PATH",
|
||||||
|
)
|
||||||
|
|
||||||
project_root_dir: bpy.props.StringProperty( # type: ignore
|
project_root_dir: bpy.props.StringProperty( # type: ignore
|
||||||
name="Project Root Directory",
|
name="Project Root Directory",
|
||||||
description=(
|
description=(
|
||||||
@ -379,6 +386,7 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="Animation Tools", icon="RENDER_ANIMATION")
|
box.label(text="Animation Tools", icon="RENDER_ANIMATION")
|
||||||
box.row().prop(self, "playblast_root_dir")
|
box.row().prop(self, "playblast_root_dir")
|
||||||
|
box.row().prop(self, "frames_root_dir")
|
||||||
box.row().prop(self, "pb_open_webbrowser")
|
box.row().prop(self, "pb_open_webbrowser")
|
||||||
box.row().prop(self, "pb_open_vse")
|
box.row().prop(self, "pb_open_vse")
|
||||||
|
|
||||||
|
@ -6,24 +6,29 @@ from blender_kitsu import prefs
|
|||||||
from blender_kitsu import cache
|
from blender_kitsu import cache
|
||||||
|
|
||||||
|
|
||||||
def animation_workspace_vse_area_add(context:bpy.types.Context):
|
def animation_workspace_vse_area_add(context: bpy.types.Context):
|
||||||
"""Split smallest 3D View in current workspace"""
|
"""Split smallest 3D View in current workspace"""
|
||||||
for workspace in [workspace for workspace in bpy.data.workspaces if workspace.name == "Animation"]:
|
for workspace in [
|
||||||
|
workspace for workspace in bpy.data.workspaces if workspace.name == "Animation"
|
||||||
|
]:
|
||||||
context.window.workspace = workspace
|
context.window.workspace = workspace
|
||||||
context.view_layer.update()
|
context.view_layer.update()
|
||||||
areas = workspace.screens[0].areas
|
areas = workspace.screens[0].areas
|
||||||
view_3d_areas = sorted([area for area in areas if area.ui_type =="VIEW_3D"], key=lambda x: x.width, reverse=False)
|
view_3d_areas = sorted(
|
||||||
|
[area for area in areas if area.ui_type == "VIEW_3D"],
|
||||||
|
key=lambda x: x.width,
|
||||||
|
reverse=False,
|
||||||
|
)
|
||||||
small_view_3d = view_3d_areas[0]
|
small_view_3d = view_3d_areas[0]
|
||||||
with context.temp_override(window=context.window, area=small_view_3d):
|
with context.temp_override(window=context.window, area=small_view_3d):
|
||||||
bpy.ops.screen.area_split(direction='HORIZONTAL', factor=0.5)
|
bpy.ops.screen.area_split(direction='HORIZONTAL', factor=0.5)
|
||||||
small_view_3d.ui_type = "SEQUENCE_EDITOR"
|
small_view_3d.ui_type = "SEQUENCE_EDITOR"
|
||||||
small_view_3d.spaces[0].view_type = "PREVIEW"
|
small_view_3d.spaces[0].view_type = "PREVIEW"
|
||||||
|
|
||||||
|
|
||||||
def animation_workspace_delete_others():
|
def animation_workspace_delete_others():
|
||||||
"""Delete any workspace that is not an animation workspace"""
|
"""Delete any workspace that is not an animation workspace"""
|
||||||
for ws in bpy.data.workspaces:
|
for ws in bpy.data.workspaces:
|
||||||
if ws.name != "Animation":
|
if ws.name != "Animation":
|
||||||
bpy.ops.workspace.delete({"workspace": ws})
|
with bpy.context.temp_override(workspace=ws):
|
||||||
|
bpy.ops.workspace.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,10 +26,11 @@ class Asset:
|
|||||||
path: absolute path to the blend file containing this asset.
|
path: absolute path to the blend file containing this asset.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
asset_type = ""
|
asset_type = ""
|
||||||
code = ""
|
code = ""
|
||||||
name = ""
|
name = ""
|
||||||
path = "{production.path}/lib/{asset.asset_type}/{asset.code}/{asset.code}.blend"
|
path = "{production.path}/assets/{asset.asset_type}/{asset.code}/{asset.code}.blend"
|
||||||
collection = "{asset.code}"
|
collection = "{asset.code}"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
@ -143,7 +143,7 @@ class KitsuShotRef(ShotRef):
|
|||||||
|
|
||||||
|
|
||||||
class KitsuConnector(Connector):
|
class KitsuConnector(Connector):
|
||||||
PRODUCTION_KEYS = {'KITSU_PROJECT_ID'}
|
# PRODUCTION_KEYS = {'KITSU_PROJECT_ID'}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -212,7 +212,7 @@ settings about the production, including:
|
|||||||
``` bash
|
``` bash
|
||||||
└── project-name/ # Project Root Directory
|
└── project-name/ # Project Root Directory
|
||||||
└── pro/
|
└── pro/
|
||||||
├── lib/
|
├── assets/
|
||||||
├── shot-builder/
|
├── shot-builder/
|
||||||
│ ├── assets.py
|
│ ├── assets.py
|
||||||
│ ├── config.py
|
│ ├── config.py
|
||||||
|
@ -1,83 +1,30 @@
|
|||||||
from blender_kitsu.shot_builder.asset import Asset
|
from blender_kitsu.shot_builder.asset import Asset
|
||||||
|
|
||||||
|
|
||||||
class SpriteFrightAsset(Asset):
|
class ProductionAsset(Asset):
|
||||||
path = "{production.path}/lib/{asset.asset_type}/{asset.code}/{asset.code}.blend"
|
path = "{production.path}/assets/{asset.asset_type}/{asset.code}/{asset.code}.blend" # Path to most assets
|
||||||
|
color_tag = "NONE"
|
||||||
|
|
||||||
|
|
||||||
class Character(SpriteFrightAsset):
|
# Categories
|
||||||
asset_type = "char"
|
class Character(ProductionAsset):
|
||||||
collection = "CH-{asset.code}"
|
asset_type = "chars"
|
||||||
|
collection = "CH-{asset.code}" # Prefix for characters
|
||||||
|
|
||||||
|
|
||||||
class Ellie(Character):
|
class Prop(ProductionAsset):
|
||||||
name = "Ellie"
|
|
||||||
code = "ellie"
|
|
||||||
|
|
||||||
|
|
||||||
class Victoria(Character):
|
|
||||||
name = "Victoria"
|
|
||||||
code = "victoria"
|
|
||||||
|
|
||||||
|
|
||||||
class Phil(Character):
|
|
||||||
name = "Phil"
|
|
||||||
code = "phil"
|
|
||||||
|
|
||||||
|
|
||||||
class Rex(Character):
|
|
||||||
name = "Rex"
|
|
||||||
code = "rex"
|
|
||||||
|
|
||||||
|
|
||||||
class Jay(Character):
|
|
||||||
name = "Jay"
|
|
||||||
code = "jay"
|
|
||||||
|
|
||||||
# TODO: Bird character has no asset file yet.
|
|
||||||
# class Bird(Character):
|
|
||||||
# name = "Bird"
|
|
||||||
# code = "bird"
|
|
||||||
|
|
||||||
|
|
||||||
class Prop(SpriteFrightAsset):
|
|
||||||
asset_type = "props"
|
asset_type = "props"
|
||||||
collection = "PR-{asset.code}"
|
collection = "PR-{asset.code}" # Prefix for props
|
||||||
|
|
||||||
|
|
||||||
class Boombox(Prop):
|
# Assets
|
||||||
name = "Boombox"
|
class MyCharacter(Character):
|
||||||
code = "boombox"
|
name = "My Character" # Name on Kitsu Server
|
||||||
|
code = "mycharacter" # Name of Collection without prefix (e.g. CH-mycharacter)
|
||||||
|
path = "{production.path}/assets/{asset.asset_type}/mycharacter/publish/mycharacter.v001.blend" # This asset has a custom path
|
||||||
|
color_tag = "COLOR_01"
|
||||||
|
|
||||||
|
|
||||||
class BBQGrill(Prop):
|
class MyProp(Prop):
|
||||||
name = "BBQ Grill"
|
name = "MyProp"
|
||||||
code = "bbq_grill"
|
code = "myprop"
|
||||||
|
|
||||||
|
|
||||||
# NOTE: NotepadAndPencil is a combined asset. In Kitsu it is defined as a single asset. In the production
|
|
||||||
# reportitory it is stored as 2 collections in a single file. See `hooks.link_char_prop_for_anim`
|
|
||||||
# where this is handled.
|
|
||||||
class NotepadAndPencil(Prop):
|
|
||||||
name = "Notepad and pencil"
|
|
||||||
code = "notepad_pencil"
|
|
||||||
|
|
||||||
|
|
||||||
class Binoculars(Prop):
|
|
||||||
name = "Binoculars (Ellie)"
|
|
||||||
code = "binoculars"
|
|
||||||
|
|
||||||
|
|
||||||
class Backpack(Prop):
|
|
||||||
name = "Backpack (Phil)"
|
|
||||||
code = "backpack"
|
|
||||||
|
|
||||||
|
|
||||||
class Set(SpriteFrightAsset):
|
|
||||||
asset_type = "sets"
|
|
||||||
collection = "SE-{asset.code}"
|
|
||||||
|
|
||||||
|
|
||||||
class MushroomGrove(Set):
|
|
||||||
name = "Mushroom grove"
|
|
||||||
code = "mushroom_grove"
|
|
||||||
|
@ -5,10 +5,9 @@ SHOTS = KitsuConnector
|
|||||||
ASSETS = KitsuConnector
|
ASSETS = KitsuConnector
|
||||||
RENDER_SETTINGS = KitsuConnector
|
RENDER_SETTINGS = KitsuConnector
|
||||||
|
|
||||||
KITSU_PROJECT_ID = "fc77c0b9-bb76-41c3-b843-c9b156f9b3ec"
|
|
||||||
|
|
||||||
# Formatting rules
|
# Formatting rules
|
||||||
# ----------------
|
# ----------------
|
||||||
|
|
||||||
# The name of the scene in blender where the shot is build in.
|
# The name of the scene in blender where the shot is build in.
|
||||||
# SCENE_NAME_FORMAT = "{shot.sequence_code}_{shot.code}.{task_type}"
|
SCENE_NAME_FORMAT = "{shot.name}.{task_type}"
|
||||||
|
SHOT_NAME_FORMAT = "{shot.name}"
|
||||||
|
@ -3,7 +3,7 @@ from blender_kitsu.shot_builder.hooks import hook, Wildcard
|
|||||||
from blender_kitsu.shot_builder.asset import Asset
|
from blender_kitsu.shot_builder.asset import Asset
|
||||||
from blender_kitsu.shot_builder.shot import Shot
|
from blender_kitsu.shot_builder.shot import Shot
|
||||||
from blender_kitsu.shot_builder.project import Production
|
from blender_kitsu.shot_builder.project import Production
|
||||||
|
from pathlib import Path
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -11,6 +11,9 @@ logger = logging.getLogger(__name__)
|
|||||||
# ---------- Global Hook ----------
|
# ---------- Global Hook ----------
|
||||||
|
|
||||||
|
|
||||||
|
CAMERA_NAME = 'CAM-camera'
|
||||||
|
|
||||||
|
|
||||||
@hook()
|
@hook()
|
||||||
def set_cycles_render_engine(scene: bpy.types.Scene, **kwargs):
|
def set_cycles_render_engine(scene: bpy.types.Scene, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -21,6 +24,7 @@ def set_cycles_render_engine(scene: bpy.types.Scene, **kwargs):
|
|||||||
|
|
||||||
# ---------- Overrides for animation files ----------
|
# ---------- Overrides for animation files ----------
|
||||||
|
|
||||||
|
|
||||||
@hook(match_task_type='anim')
|
@hook(match_task_type='anim')
|
||||||
def task_type_anim_set_workbench(scene: bpy.types.Scene, **kwargs):
|
def task_type_anim_set_workbench(scene: bpy.types.Scene, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -28,6 +32,7 @@ def task_type_anim_set_workbench(scene: bpy.types.Scene, **kwargs):
|
|||||||
"""
|
"""
|
||||||
scene.render.engine = 'BLENDER_WORKBENCH'
|
scene.render.engine = 'BLENDER_WORKBENCH'
|
||||||
|
|
||||||
|
|
||||||
# ---------- Create output collection for animation files ----------
|
# ---------- Create output collection for animation files ----------
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +46,14 @@ def _add_camera_rig(
|
|||||||
of the shot and the camera will be set as active camera.
|
of the shot and the camera will be set as active camera.
|
||||||
"""
|
"""
|
||||||
# Load camera rig.
|
# Load camera rig.
|
||||||
path = f"{production.path}/lib/cam/camera_rig.blend"
|
path = f"{production.path}/assets/cam/camera_rig.blend"
|
||||||
|
|
||||||
|
if not Path(path).exists():
|
||||||
|
camera_data = bpy.data.cameras.new(name=CAMERA_NAME)
|
||||||
|
camera_object = bpy.data.objects.new(name=CAMERA_NAME, object_data=camera_data)
|
||||||
|
shot.output_collection.objects.link(camera_object)
|
||||||
|
return
|
||||||
|
|
||||||
collection_name = "CA-camera_rig"
|
collection_name = "CA-camera_rig"
|
||||||
bpy.ops.wm.link(
|
bpy.ops.wm.link(
|
||||||
filepath=path,
|
filepath=path,
|
||||||
@ -59,12 +71,14 @@ def _add_camera_rig(
|
|||||||
shot.output_collection.children.link(asset_collection)
|
shot.output_collection.children.link(asset_collection)
|
||||||
|
|
||||||
# Set the camera of the camera rig as active scene camera.
|
# Set the camera of the camera rig as active scene camera.
|
||||||
camera = bpy.data.objects['CAM-camera']
|
camera = bpy.data.objects[CAMERA_NAME]
|
||||||
scene.camera = camera
|
scene.camera = camera
|
||||||
|
|
||||||
|
|
||||||
@hook(match_task_type='anim')
|
@hook(match_task_type='anim')
|
||||||
def task_type_anim_output_collection(scene: bpy.types.Scene, production: Production, shot: Shot, task_type: str, **kwargs):
|
def task_type_anim_output_collection(
|
||||||
|
scene: bpy.types.Scene, production: Production, shot: Shot, task_type: str, **kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Animations are stored in an output collection. This collection will be linked
|
Animations are stored in an output collection. This collection will be linked
|
||||||
by the lighting file.
|
by the lighting file.
|
||||||
@ -72,15 +86,19 @@ def task_type_anim_output_collection(scene: bpy.types.Scene, production: Product
|
|||||||
Also loads the camera rig.
|
Also loads the camera rig.
|
||||||
"""
|
"""
|
||||||
output_collection = bpy.data.collections.new(
|
output_collection = bpy.data.collections.new(
|
||||||
name=shot.get_output_collection_name(shot=shot, task_type=task_type))
|
name=shot.get_output_collection_name(shot=shot, task_type=task_type)
|
||||||
|
)
|
||||||
shot.output_collection = output_collection
|
shot.output_collection = output_collection
|
||||||
output_collection.use_fake_user = True
|
output_collection.use_fake_user = True
|
||||||
|
scene.collection.children.link(output_collection)
|
||||||
|
|
||||||
_add_camera_rig(scene, production, shot)
|
_add_camera_rig(scene, production, shot)
|
||||||
|
|
||||||
|
|
||||||
@hook(match_task_type='lighting')
|
@hook(match_task_type='lighting')
|
||||||
def link_anim_output_collection(scene: bpy.types.Scene, production: Production, shot: Shot, **kwargs):
|
def link_anim_output_collection(
|
||||||
|
scene: bpy.types.Scene, production: Production, shot: Shot, **kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Link in the animation output collection from the animation file.
|
Link in the animation output collection from the animation file.
|
||||||
"""
|
"""
|
||||||
@ -88,13 +106,14 @@ def link_anim_output_collection(scene: bpy.types.Scene, production: Production,
|
|||||||
scene.collection.children.link(anim_collection)
|
scene.collection.children.link(anim_collection)
|
||||||
anim_file_path = shot.get_anim_file_path(production, shot)
|
anim_file_path = shot.get_anim_file_path(production, shot)
|
||||||
anim_output_collection_name = shot.get_output_collection_name(
|
anim_output_collection_name = shot.get_output_collection_name(
|
||||||
shot=shot, task_type="anim")
|
shot=shot, task_type="anim"
|
||||||
|
)
|
||||||
result = bpy.ops.wm.link(
|
result = bpy.ops.wm.link(
|
||||||
filepath=anim_file_path,
|
filepath=anim_file_path,
|
||||||
directory=anim_file_path + "/Collection",
|
directory=anim_file_path + "/Collection",
|
||||||
filename=anim_output_collection_name,
|
filename=anim_output_collection_name,
|
||||||
)
|
)
|
||||||
assert (result == {'FINISHED'})
|
assert result == {'FINISHED'}
|
||||||
|
|
||||||
# Move the anim output collection from scene collection to the animation collection.
|
# Move the anim output collection from scene collection to the animation collection.
|
||||||
anim_output_collection = bpy.data.objects[anim_output_collection_name]
|
anim_output_collection = bpy.data.objects[anim_output_collection_name]
|
||||||
@ -109,7 +128,7 @@ def link_anim_output_collection(scene: bpy.types.Scene, production: Production,
|
|||||||
# ---------- Asset loading and linking ----------
|
# ---------- Asset loading and linking ----------
|
||||||
|
|
||||||
|
|
||||||
@hook(match_task_type='anim', match_asset_type=['char', 'props'])
|
@hook(match_task_type='anim', match_asset_type=['chars', 'props'])
|
||||||
def link_char_prop_for_anim(scene: bpy.types.Scene, shot: Shot, asset: Asset, **kwargs):
|
def link_char_prop_for_anim(scene: bpy.types.Scene, shot: Shot, asset: Asset, **kwargs):
|
||||||
"""
|
"""
|
||||||
Loading a character or prop for an animation file.
|
Loading a character or prop for an animation file.
|
||||||
|
@ -2,28 +2,40 @@ from blender_kitsu.shot_builder.shot import Shot
|
|||||||
from blender_kitsu.shot_builder.project import Production
|
from blender_kitsu.shot_builder.project import Production
|
||||||
|
|
||||||
|
|
||||||
class SpriteFrightShot(Shot):
|
class ProductionShot(Shot):
|
||||||
def get_anim_file_path(self, production: Production, shot: Shot) -> str:
|
def get_anim_file_path(self, production: Production, shot: Shot) -> str:
|
||||||
"""
|
"""Get the animation file path for this given shot."""
|
||||||
Get the animation file path for this given shot.
|
return self.file_path_format.format_map(
|
||||||
"""
|
{'production': production, 'shot': shot, 'task_type': "anim"}
|
||||||
return self.file_path_format.format_map({
|
)
|
||||||
'production': production,
|
|
||||||
'shot': shot,
|
def get_lighting_file_path(self, production: Production, shot: Shot) -> str:
|
||||||
'task_type': "anim"
|
"""Get the lighting file path for this given shot."""
|
||||||
})
|
return self.file_path_format.format_map(
|
||||||
|
{'production': production, 'shot': shot, 'task_type': "lighting"}
|
||||||
|
)
|
||||||
|
|
||||||
def get_output_collection_name(self, shot: Shot, task_type: str) -> str:
|
def get_output_collection_name(self, shot: Shot, task_type: str) -> str:
|
||||||
|
"""Get the collection name where the output is stored."""
|
||||||
|
return f"{shot.name}.{task_type}.output"
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
"""Check if this shot contains all data, so it could be selected
|
||||||
|
for shot building.
|
||||||
"""
|
"""
|
||||||
Get the collection name where the output is stored.
|
if not super().is_valid():
|
||||||
"""
|
return False
|
||||||
return f"{shot.sequence_code}_{shot.code}.{task_type}.output"
|
return True
|
||||||
|
|
||||||
|
# Assuming path to file is in `project_name/svn/pro/shot/sequence_name/shot_name`
|
||||||
|
# Render Ouput path should be `project_name/shared/shot_frames/sequence_name/shot_name/`
|
||||||
|
|
||||||
|
def get_render_output_dir(self) -> str:
|
||||||
|
return f"//../../../../../shared/shot_frames/{self.sequence_code}/{self.name}/{self.name}.lighting"
|
||||||
|
|
||||||
|
def get_comp_output_dir(self) -> str:
|
||||||
|
return f"//../../../../../shared/shot_frames/{self.sequence_code}/{self.name}/{self.name}.comp"
|
||||||
|
|
||||||
|
|
||||||
class Sequence_0002(SpriteFrightShot):
|
class GenericShot(ProductionShot):
|
||||||
sequence_code = "0002"
|
is_generic = True
|
||||||
|
|
||||||
|
|
||||||
class Shot_0001_0001_A(Sequence_0002):
|
|
||||||
name = "001"
|
|
||||||
code = "0001"
|
|
||||||
|
@ -251,9 +251,11 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
|
|||||||
shot_builder.create_build_steps()
|
shot_builder.create_build_steps()
|
||||||
shot_builder.build()
|
shot_builder.build()
|
||||||
|
|
||||||
|
active_project = cache.project_active_get()
|
||||||
|
|
||||||
# Build Kitsu Context
|
# Build Kitsu Context
|
||||||
sequence = gazu.shot.get_sequence_by_name(
|
sequence = gazu.shot.get_sequence_by_name(
|
||||||
production.config['KITSU_PROJECT_ID'], self.seq_id
|
active_project.id, self.seq_id
|
||||||
)
|
)
|
||||||
shot = gazu.shot.get_shot_by_name(sequence, self.shot_id)
|
shot = gazu.shot.get_shot_by_name(sequence, self.shot_id)
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import bgl
|
|
||||||
import gpu
|
import gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ from gpu_extras.batch import batch_for_shader
|
|||||||
# Shaders and batches
|
# Shaders and batches
|
||||||
|
|
||||||
rect_coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
rect_coords = ((0, 0), (1, 0), (1, 1), (0, 1))
|
||||||
|
indices = ((0, 1, 2), (2, 3, 0))
|
||||||
# Setup shaders only if Blender runs in the foreground.
|
# Setup shaders only if Blender runs in the foreground.
|
||||||
# If running in the background, no handles are registered, as drawing extra UI
|
# If running in the background, no handles are registered, as drawing extra UI
|
||||||
# elements does not make sense.
|
# elements does not make sense.
|
||||||
@ -43,7 +42,7 @@ else:
|
|||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
ucolor_2d_shader = gpu.shader.from_builtin(color_key)
|
ucolor_2d_shader = gpu.shader.from_builtin(color_key)
|
||||||
ucolor_2d_rect_batch = batch_for_shader(
|
ucolor_2d_rect_batch = batch_for_shader(
|
||||||
ucolor_2d_shader, "TRI_FAN", {"pos": rect_coords}
|
ucolor_2d_shader, "TRIS", {"pos": rect_coords}, indices=indices
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -54,17 +53,15 @@ Float4 = typing.Tuple[float, float, float, float]
|
|||||||
|
|
||||||
def draw_line(position: Float2, size: Float2, color: Float4):
|
def draw_line(position: Float2, size: Float2, color: Float4):
|
||||||
with gpu.matrix.push_pop():
|
with gpu.matrix.push_pop():
|
||||||
bgl.glEnable(bgl.GL_BLEND)
|
gpu.state.blend_set("ALPHA")
|
||||||
|
|
||||||
gpu.matrix.translate(position)
|
gpu.matrix.translate(position)
|
||||||
gpu.matrix.scale(size)
|
gpu.matrix.scale(size)
|
||||||
|
|
||||||
# Render a colored rectangle
|
|
||||||
ucolor_2d_shader.bind()
|
|
||||||
ucolor_2d_shader.uniform_float("color", color)
|
ucolor_2d_shader.uniform_float("color", color)
|
||||||
ucolor_2d_rect_batch.draw(ucolor_2d_shader)
|
ucolor_2d_rect_batch.draw(ucolor_2d_shader)
|
||||||
|
|
||||||
bgl.glDisable(bgl.GL_BLEND)
|
gpu.state.blend_set("NONE")
|
||||||
|
|
||||||
|
|
||||||
def get_strip_rectf(strip) -> Float4:
|
def get_strip_rectf(strip) -> Float4:
|
||||||
|
@ -2269,13 +2269,19 @@ class KITSU_OT_shot_image_sequence(bpy.types.Operator):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def set_scene_colorspace(self, context):
|
def set_scene_colorspace(self, context):
|
||||||
|
if bpy.app.version_string.split('.')[0] == '3':
|
||||||
|
color_space_name = "Linear"
|
||||||
|
else:
|
||||||
|
color_space_name = "Linear Rec.709"
|
||||||
|
|
||||||
|
|
||||||
scene = context.scene
|
scene = context.scene
|
||||||
if self.file_type == ".jpg":
|
if self.file_type == ".jpg":
|
||||||
scene.sequencer_colorspace_settings.name = "sRGB"
|
scene.sequencer_colorspace_settings.name = "sRGB"
|
||||||
scene.view_settings.look = "None"
|
scene.view_settings.look = "None"
|
||||||
scene.view_settings.view_transform = 'Standard'
|
scene.view_settings.view_transform = 'Standard'
|
||||||
if self.file_type == ".exr":
|
if self.file_type == ".exr":
|
||||||
scene.sequencer_colorspace_settings.name = "Linear"
|
scene.sequencer_colorspace_settings.name = color_space_name
|
||||||
scene.view_settings.look = 'Medium High Contrast'
|
scene.view_settings.look = 'Medium High Contrast'
|
||||||
scene.view_settings.view_transform = 'Filmic'
|
scene.view_settings.view_transform = 'Filmic'
|
||||||
|
|
||||||
@ -2353,13 +2359,27 @@ class KITSU_OT_shot_image_sequence(bpy.types.Operator):
|
|||||||
new_strip.colorspace_settings.name = new_strip.colorspace_settings.name
|
new_strip.colorspace_settings.name = new_strip.colorspace_settings.name
|
||||||
|
|
||||||
def get_shot_seq_directory(self, context, strip):
|
def get_shot_seq_directory(self, context, strip):
|
||||||
|
addon_prefs = prefs.addon_prefs_get(context)
|
||||||
path_string = os.path.realpath(bpy.path.abspath(strip.filepath))
|
path_string = os.path.realpath(bpy.path.abspath(strip.filepath))
|
||||||
path = Path(path_string.replace("shot_previews", "shot_frames"))
|
path = Path(
|
||||||
|
path_string.replace(
|
||||||
|
addon_prefs.playblast_root_dir, addon_prefs.frames_root_dir
|
||||||
|
)
|
||||||
|
)
|
||||||
return path.parent
|
return path.parent
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||||
# Get closest empty channel
|
# Get closest empty channel
|
||||||
channel = int(self.channel_selection)
|
channel = int(self.channel_selection)
|
||||||
|
addon_prefs = prefs.addon_prefs_get(context)
|
||||||
|
if not (
|
||||||
|
Path(addon_prefs.frames_root_dir).is_dir()
|
||||||
|
and addon_prefs.frames_root_dir != ''
|
||||||
|
):
|
||||||
|
self.report(
|
||||||
|
{"ERROR"}, f"Frames Directory does not exist, check add-on preferences"
|
||||||
|
)
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if self.set_color_space:
|
if self.set_color_space:
|
||||||
self.set_scene_colorspace(context)
|
self.set_scene_colorspace(context)
|
||||||
|
@ -37,8 +37,7 @@ def shot_meta(strip: bpy.types.Sequence, shot: Shot) -> None:
|
|||||||
try:
|
try:
|
||||||
kitsu_3d_start = shot.data["3d_start"]
|
kitsu_3d_start = shot.data["3d_start"]
|
||||||
except:
|
except:
|
||||||
kitsu_3d_start = 101 # TODO REPLACE WITH BAKED GLOBAL VALUE
|
kitsu_3d_start = bkglobals.FRAME_START
|
||||||
|
|
||||||
shot.name = strip.kitsu.shot_name
|
shot.name = strip.kitsu.shot_name
|
||||||
shot.description = strip.kitsu.shot_description
|
shot.description = strip.kitsu.shot_description
|
||||||
shot.data["frame_in"] = strip.frame_final_start
|
shot.data["frame_in"] = strip.frame_final_start
|
||||||
@ -70,9 +69,7 @@ def new_shot(
|
|||||||
nb_frames=strip.frame_final_duration,
|
nb_frames=strip.frame_final_duration,
|
||||||
frame_in=frame_range[0],
|
frame_in=frame_range[0],
|
||||||
frame_out=frame_range[1],
|
frame_out=frame_range[1],
|
||||||
data={
|
data={"fps": bkglobals.FPS, "3d_start": bkglobals.FRAME_START},
|
||||||
"fps": bkglobals.FPS,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if add_tasks:
|
if add_tasks:
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
## 1.0.2 - 2023-10-31
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
- Fix some assumptions about SVN status
|
||||||
|
- Fix a crash in hotkeys.py
|
||||||
|
- Silly mistake (referenced before assignment)
|
||||||
|
- Use consistent registration pattern
|
||||||
|
|
||||||
## 1.0.1 - 2023-08-02
|
## 1.0.1 - 2023-08-02
|
||||||
|
|
||||||
### ADDED
|
### ADDED
|
||||||
|
@ -20,7 +20,7 @@ bl_info = {
|
|||||||
"author": "Demeter Dzadik, Paul Golter",
|
"author": "Demeter Dzadik, Paul Golter",
|
||||||
"description": "Blender Add-on to interact with Subversion.",
|
"description": "Blender Add-on to interact with Subversion.",
|
||||||
"blender": (3, 1, 0),
|
"blender": (3, 1, 0),
|
||||||
"version": (1, 0, 1),
|
"version": (1, 0, 2),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
"doc_url": "",
|
"doc_url": "",
|
||||||
|
@ -33,6 +33,7 @@ class SVN_Operator:
|
|||||||
|
|
||||||
class SVN_Operator_Single_File(SVN_Operator):
|
class SVN_Operator_Single_File(SVN_Operator):
|
||||||
"""Base class for SVN operators operating on a single file."""
|
"""Base class for SVN operators operating on a single file."""
|
||||||
|
|
||||||
file_rel_path: StringProperty()
|
file_rel_path: StringProperty()
|
||||||
|
|
||||||
# Flag to differentiate operators that require that the file exists pre-execute.
|
# Flag to differentiate operators that require that the file exists pre-execute.
|
||||||
@ -42,7 +43,9 @@ class SVN_Operator_Single_File(SVN_Operator):
|
|||||||
if not self.file_exists(context) and not type(self).missing_file_allowed:
|
if not self.file_exists(context) and not type(self).missing_file_allowed:
|
||||||
# If the operator requires the file to exist and it doesn't, cancel.
|
# If the operator requires the file to exist and it doesn't, cancel.
|
||||||
self.report(
|
self.report(
|
||||||
{'ERROR'}, f'File is no longer on the file system: "{self.file_rel_path}"')
|
{'ERROR'},
|
||||||
|
f'File is no longer on the file system: "{self.file_rel_path}"',
|
||||||
|
)
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
status = Processes.get('Status')
|
status = Processes.get('Status')
|
||||||
@ -65,7 +68,9 @@ class SVN_Operator_Single_File(SVN_Operator):
|
|||||||
return Path.joinpath(Path(repo.directory), Path(self.file_rel_path))
|
return Path.joinpath(Path(repo.directory), Path(self.file_rel_path))
|
||||||
|
|
||||||
def get_file(self, context) -> "SVN_file":
|
def get_file(self, context) -> "SVN_file":
|
||||||
return context.scene.svn.get_repo(context).get_file_by_svn_path(self.file_rel_path)
|
return context.scene.svn.get_repo(context).get_file_by_svn_path(
|
||||||
|
self.file_rel_path
|
||||||
|
)
|
||||||
|
|
||||||
def file_exists(self, context) -> bool:
|
def file_exists(self, context) -> bool:
|
||||||
exists = self.get_file_full_path(context).exists()
|
exists = self.get_file_full_path(context).exists()
|
||||||
@ -81,11 +86,12 @@ class Popup_Operator:
|
|||||||
popup_width = 400
|
popup_width = 400
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return context.window_manager.invoke_props_dialog(self, width=type(self).popup_width)
|
return context.window_manager.invoke_props_dialog(
|
||||||
|
self, width=type(self).popup_width
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Warning_Operator(Popup_Operator):
|
class Warning_Operator(Popup_Operator):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout.column(align=True)
|
layout = self.layout.column(align=True)
|
||||||
|
|
||||||
@ -100,7 +106,6 @@ class Warning_Operator(Popup_Operator):
|
|||||||
|
|
||||||
|
|
||||||
class May_Modifiy_Current_Blend(SVN_Operator_Single_File, Warning_Operator):
|
class May_Modifiy_Current_Blend(SVN_Operator_Single_File, Warning_Operator):
|
||||||
|
|
||||||
def file_is_current_blend(self, context) -> bool:
|
def file_is_current_blend(self, context) -> bool:
|
||||||
current_blend = context.scene.svn.get_repo(context).current_blend_file
|
current_blend = context.scene.svn.get_repo(context).current_blend_file
|
||||||
return current_blend and current_blend.svn_path == self.file_rel_path
|
return current_blend and current_blend.svn_path == self.file_rel_path
|
||||||
@ -132,7 +137,7 @@ class May_Modifiy_Current_Blend(SVN_Operator_Single_File, Warning_Operator):
|
|||||||
super().execute(context)
|
super().execute(context)
|
||||||
if self.reload_file:
|
if self.reload_file:
|
||||||
bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath, load_ui=False)
|
bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath, load_ui=False)
|
||||||
else:
|
elif self.file_is_current_blend(context):
|
||||||
context.scene.svn.file_is_outdated = True
|
context.scene.svn.file_is_outdated = True
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -140,26 +145,28 @@ class May_Modifiy_Current_Blend(SVN_Operator_Single_File, Warning_Operator):
|
|||||||
class SVN_OT_update_single(May_Modifiy_Current_Blend, Operator):
|
class SVN_OT_update_single(May_Modifiy_Current_Blend, Operator):
|
||||||
bl_idname = "svn.update_single"
|
bl_idname = "svn.update_single"
|
||||||
bl_label = "Update File"
|
bl_label = "Update File"
|
||||||
bl_description = "Download the latest available version of this file from the remote repository"
|
bl_description = (
|
||||||
|
"Download the latest available version of this file from the remote repository"
|
||||||
|
)
|
||||||
bl_options = {'INTERNAL'}
|
bl_options = {'INTERNAL'}
|
||||||
|
|
||||||
missing_file_allowed = True
|
missing_file_allowed = True
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
self.will_conflict = False
|
self.will_conflict = False
|
||||||
file_entry = context.scene.svn.get_repo(
|
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(
|
||||||
context).get_file_by_svn_path(self.file_rel_path)
|
self.file_rel_path
|
||||||
|
)
|
||||||
if file_entry.status not in ['normal', 'none']:
|
if file_entry.status not in ['normal', 'none']:
|
||||||
self.will_conflict = True
|
self.will_conflict = True
|
||||||
|
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(
|
||||||
context,
|
context,
|
||||||
["svn", "up", f"{self.file_rel_path}", "--accept", "postpone"],
|
["svn", "up", f"{self.file_rel_path}", "--accept", "postpone"],
|
||||||
use_cred=True
|
use_cred=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.report({'INFO'},
|
self.report({'INFO'}, f'Updated "{self.file_rel_path}" to the latest version.')
|
||||||
f'Updated "{self.file_rel_path}" to the latest version.')
|
|
||||||
|
|
||||||
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
||||||
if self.will_conflict:
|
if self.will_conflict:
|
||||||
@ -182,28 +189,34 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
|||||||
revision: IntProperty(default=0)
|
revision: IntProperty(default=0)
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
file_entry = context.scene.svn.get_repo(
|
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(
|
||||||
context).get_file_by_svn_path(self.file_rel_path)
|
self.file_rel_path
|
||||||
|
)
|
||||||
if self.file_is_current_blend(context) and file_entry.status != 'normal':
|
if self.file_is_current_blend(context) and file_entry.status != 'normal':
|
||||||
self.report({'ERROR'},
|
self.report(
|
||||||
'You must first revert or commit the changes to this file.')
|
{'ERROR'}, 'You must first revert or commit the changes to this file.'
|
||||||
|
)
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
return super().invoke(context, event)
|
return super().invoke(context, event)
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
file_entry = context.scene.svn.get_repo(
|
file_entry = context.scene.svn.get_repo(context).get_file_by_svn_path(
|
||||||
context).get_file_by_svn_path(self.file_rel_path)
|
self.file_rel_path
|
||||||
|
)
|
||||||
if file_entry.status == 'modified':
|
if file_entry.status == 'modified':
|
||||||
# If file has local modifications, let's avoid a conflict by cancelling
|
# If file has local modifications, let's avoid a conflict by cancelling
|
||||||
# and telling the user to resolve it in advance.
|
# and telling the user to resolve it in advance.
|
||||||
self.report({'ERROR'},
|
self.report(
|
||||||
"Cancelled: You have local modifications to this file. You must revert or commit it first!")
|
{'ERROR'},
|
||||||
|
"Cancelled: You have local modifications to this file. You must revert or commit it first!",
|
||||||
|
)
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
self.svn_download_file_revision(context, self.file_rel_path, self.revision)
|
self.svn_download_file_revision(context, self.file_rel_path, self.revision)
|
||||||
|
|
||||||
self.report({'INFO'},
|
self.report(
|
||||||
f"Checked out revision {self.revision} of {self.file_rel_path}")
|
{'INFO'}, f"Checked out revision {self.revision} of {self.file_rel_path}"
|
||||||
|
)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@ -212,11 +225,7 @@ class SVN_OT_download_file_revision(May_Modifiy_Current_Blend, Operator):
|
|||||||
if self.revision > 0:
|
if self.revision > 0:
|
||||||
commands.insert(2, f"-r{self.revision}")
|
commands.insert(2, f"-r{self.revision}")
|
||||||
|
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(context, commands, use_cred=True)
|
||||||
context,
|
|
||||||
commands,
|
|
||||||
use_cred=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
def set_predicted_file_status(self, repo, file_entry: "SVN_file"):
|
||||||
file_entry['revision'] = self.revision
|
file_entry['revision'] = self.revision
|
||||||
@ -238,10 +247,7 @@ class SVN_OT_restore_file(May_Modifiy_Current_Blend, Operator):
|
|||||||
missing_file_allowed = True
|
missing_file_allowed = True
|
||||||
|
|
||||||
def svn_revert(self, context, svn_file_path):
|
def svn_revert(self, context, svn_file_path):
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(context, ["svn", "revert", f"{svn_file_path}"])
|
||||||
context,
|
|
||||||
["svn", "revert", f"{svn_file_path}"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
self.svn_revert(context, self.file_rel_path)
|
self.svn_revert(context, self.file_rel_path)
|
||||||
@ -260,11 +266,15 @@ class SVN_OT_revert_file(SVN_OT_restore_file):
|
|||||||
missing_file_allowed = False
|
missing_file_allowed = False
|
||||||
|
|
||||||
def get_warning_text(self, context) -> str:
|
def get_warning_text(self, context) -> str:
|
||||||
return "You will irreversibly and permanently lose the changes you've made to this file:\n " + self.file_rel_path
|
return (
|
||||||
|
"You will irreversibly and permanently lose the changes you've made to this file:\n "
|
||||||
|
+ self.file_rel_path
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SVN_OT_revert_and_update(SVN_OT_download_file_revision, SVN_OT_revert_file):
|
class SVN_OT_revert_and_update(SVN_OT_download_file_revision, SVN_OT_revert_file):
|
||||||
"""Convenience operator for the "This file is outdated" warning message. Normally, these two operations should be done separately!"""
|
"""Convenience operator for the "This file is outdated" warning message. Normally, these two operations should be done separately!"""
|
||||||
|
|
||||||
bl_idname = "svn.revert_and_update_file"
|
bl_idname = "svn.revert_and_update_file"
|
||||||
bl_label = "Revert And Update File"
|
bl_label = "Revert And Update File"
|
||||||
bl_description = "A different version of this file was downloaded while it was open. This warning will persist until the file is updated and reloaded, or committed. Click to PERMANENTLY DISCARD local changes to this file and update it to the latest revision. Cannot be undone"
|
bl_description = "A different version of this file was downloaded while it was open. This warning will persist until the file is updated and reloaded, or committed. Click to PERMANENTLY DISCARD local changes to this file and update it to the latest revision. Cannot be undone"
|
||||||
@ -277,7 +287,10 @@ class SVN_OT_revert_and_update(SVN_OT_download_file_revision, SVN_OT_revert_file
|
|||||||
|
|
||||||
def get_warning_text(self, context) -> str:
|
def get_warning_text(self, context) -> str:
|
||||||
if self.get_file(context).status != 'normal':
|
if self.get_file(context).status != 'normal':
|
||||||
return "You will irreversibly and permanently lose the changes you've made to this file:\n " + self.file_rel_path
|
return (
|
||||||
|
"You will irreversibly and permanently lose the changes you've made to this file:\n "
|
||||||
|
+ self.file_rel_path
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return "File will be updated to latest revision."
|
return "File will be updated to latest revision."
|
||||||
|
|
||||||
@ -291,13 +304,14 @@ class SVN_OT_revert_and_update(SVN_OT_download_file_revision, SVN_OT_revert_file
|
|||||||
class SVN_OT_add_file(SVN_Operator_Single_File, Operator):
|
class SVN_OT_add_file(SVN_Operator_Single_File, Operator):
|
||||||
bl_idname = "svn.add_file"
|
bl_idname = "svn.add_file"
|
||||||
bl_label = "Add File"
|
bl_label = "Add File"
|
||||||
bl_description = "Mark this file for addition to the remote repository. It can then be committed"
|
bl_description = (
|
||||||
|
"Mark this file for addition to the remote repository. It can then be committed"
|
||||||
|
)
|
||||||
bl_options = {'INTERNAL'}
|
bl_options = {'INTERNAL'}
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
result = self.execute_svn_command(
|
result = self.execute_svn_command(
|
||||||
context,
|
context, ["svn", "add", f"{self.file_rel_path}"]
|
||||||
["svn", "add", f"{self.file_rel_path}"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
@ -316,8 +330,7 @@ class SVN_OT_unadd_file(SVN_Operator_Single_File, Operator):
|
|||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(
|
||||||
context,
|
context, ["svn", "rm", "--keep-local", f"{self.file_rel_path}"]
|
||||||
["svn", "rm", "--keep-local", f"{self.file_rel_path}"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
@ -336,7 +349,10 @@ class SVN_OT_trash_file(SVN_Operator_Single_File, Warning_Operator, Operator):
|
|||||||
missing_file_allowed = False
|
missing_file_allowed = False
|
||||||
|
|
||||||
def get_warning_text(self, context):
|
def get_warning_text(self, context):
|
||||||
return "Are you sure you want to move this file to the recycle bin?\n " + self.file_rel_path
|
return (
|
||||||
|
"Are you sure you want to move this file to the recycle bin?\n "
|
||||||
|
+ self.file_rel_path
|
||||||
|
)
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
send2trash([self.get_file_full_path(context)])
|
send2trash([self.get_file_full_path(context)])
|
||||||
@ -357,13 +373,14 @@ class SVN_OT_remove_file(SVN_Operator_Single_File, Warning_Operator, Operator):
|
|||||||
missing_file_allowed = True
|
missing_file_allowed = True
|
||||||
|
|
||||||
def get_warning_text(self, context):
|
def get_warning_text(self, context):
|
||||||
return "This file will be deleted for everyone:\n " + self.file_rel_path + "\nAre you sure?"
|
return (
|
||||||
|
"This file will be deleted for everyone:\n "
|
||||||
|
+ self.file_rel_path
|
||||||
|
+ "\nAre you sure?"
|
||||||
|
)
|
||||||
|
|
||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(context, ["svn", "remove", f"{self.file_rel_path}"])
|
||||||
context,
|
|
||||||
["svn", "remove", f"{self.file_rel_path}"]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@ -381,11 +398,17 @@ class SVN_OT_resolve_conflict(May_Modifiy_Current_Blend, Operator):
|
|||||||
name="Resolve Method",
|
name="Resolve Method",
|
||||||
description="Method to use to resolve the conflict",
|
description="Method to use to resolve the conflict",
|
||||||
items=[
|
items=[
|
||||||
('mine-full', 'Keep Mine',
|
(
|
||||||
'Overwrite the new changes downloaded from the remote, and keep the local changes instead'),
|
'mine-full',
|
||||||
('theirs-full', 'Keep Theirs',
|
'Keep Mine',
|
||||||
'Overwrite the local changes with those downloaded from the remote'),
|
'Overwrite the new changes downloaded from the remote, and keep the local changes instead',
|
||||||
]
|
),
|
||||||
|
(
|
||||||
|
'theirs-full',
|
||||||
|
'Keep Theirs',
|
||||||
|
'Overwrite the local changes with those downloaded from the remote',
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@ -402,7 +425,8 @@ class SVN_OT_resolve_conflict(May_Modifiy_Current_Blend, Operator):
|
|||||||
if self.resolve_method == 'mine-full':
|
if self.resolve_method == 'mine-full':
|
||||||
col.label(text="Local changes will be kept.")
|
col.label(text="Local changes will be kept.")
|
||||||
col.label(
|
col.label(
|
||||||
text="When committing, the changes someone else made will be overwritten.")
|
text="When committing, the changes someone else made will be overwritten."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
col.label(text="Local changes will be permanently lost.")
|
col.label(text="Local changes will be permanently lost.")
|
||||||
super().draw(context)
|
super().draw(context)
|
||||||
@ -410,8 +434,13 @@ class SVN_OT_resolve_conflict(May_Modifiy_Current_Blend, Operator):
|
|||||||
def _execute(self, context: Context) -> Set[str]:
|
def _execute(self, context: Context) -> Set[str]:
|
||||||
self.execute_svn_command(
|
self.execute_svn_command(
|
||||||
context,
|
context,
|
||||||
["svn", "resolve", f"{self.file_rel_path}",
|
[
|
||||||
"--accept", f"{self.resolve_method}"]
|
"svn",
|
||||||
|
"resolve",
|
||||||
|
f"{self.file_rel_path}",
|
||||||
|
"--accept",
|
||||||
|
f"{self.resolve_method}",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
@ -3,10 +3,14 @@
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from ..util import get_addon_prefs
|
||||||
|
|
||||||
|
|
||||||
def get_credential_commands(context) -> List[str]:
|
def get_credential_commands(context) -> List[str]:
|
||||||
repo = context.scene.svn.get_repo(context)
|
repo = context.scene.svn.get_repo(context)
|
||||||
assert (repo.is_cred_entered), "No username or password entered for this repository. The UI shouldn't have allowed you to get into a state where you can press an SVN operation button without having your credentials entered, so this is a bug!"
|
assert (
|
||||||
|
repo.is_cred_entered
|
||||||
|
), "No username or password entered for this repository. The UI shouldn't have allowed you to get into a state where you can press an SVN operation button without having your credentials entered, so this is a bug!"
|
||||||
return ["--username", f"{repo.username}", "--password", f"{repo.password}"]
|
return ["--username", f"{repo.username}", "--password", f"{repo.password}"]
|
||||||
|
|
||||||
|
|
||||||
@ -14,15 +18,22 @@ def execute_command(path: str, command: str) -> str:
|
|||||||
output_bytes = subprocess.check_output(
|
output_bytes = subprocess.check_output(
|
||||||
command,
|
command,
|
||||||
shell=False,
|
shell=False,
|
||||||
cwd=path+"/",
|
cwd=path + "/",
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
start_new_session=True
|
start_new_session=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
return output_bytes.decode(encoding='utf-8', errors='replace')
|
return output_bytes.decode(encoding='utf-8', errors='replace')
|
||||||
|
|
||||||
|
|
||||||
def execute_svn_command(context, command: List[str], *, ignore_errors=False, print_errors=True, use_cred=False) -> str:
|
def execute_svn_command(
|
||||||
|
context,
|
||||||
|
command: List[str],
|
||||||
|
*,
|
||||||
|
ignore_errors=False,
|
||||||
|
print_errors=True,
|
||||||
|
use_cred=False,
|
||||||
|
) -> str:
|
||||||
"""Execute an svn command in the root of the current svn repository.
|
"""Execute an svn command in the root of the current svn repository.
|
||||||
So any file paths that are part of the command should be relative to the
|
So any file paths that are part of the command should be relative to the
|
||||||
SVN root.
|
SVN root.
|
||||||
@ -36,6 +47,10 @@ def execute_svn_command(context, command: List[str], *, ignore_errors=False, pri
|
|||||||
|
|
||||||
command.append("--non-interactive")
|
command.append("--non-interactive")
|
||||||
|
|
||||||
|
prefs = get_addon_prefs(context)
|
||||||
|
if prefs.debug_mode:
|
||||||
|
print(" ".join(command))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if repo.is_valid_svn:
|
if repo.is_valid_svn:
|
||||||
return execute_command(repo.directory, command)
|
return execute_command(repo.directory, command)
|
||||||
@ -49,6 +64,7 @@ def execute_svn_command(context, command: List[str], *, ignore_errors=False, pri
|
|||||||
print(err_msg)
|
print(err_msg)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
|
|
||||||
def check_svn_installed():
|
def check_svn_installed():
|
||||||
code, message = subprocess.getstatusoutput('svn')
|
code, message = subprocess.getstatusoutput('svn')
|
||||||
return code != 127
|
return code != 127
|
@ -14,6 +14,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Union, Any, Set, Optional, Tuple
|
from typing import List, Dict, Union, Any, Set, Optional, Tuple
|
||||||
from .. import wheels
|
from .. import wheels
|
||||||
|
|
||||||
# This will load the xmltodict wheel file.
|
# This will load the xmltodict wheel file.
|
||||||
wheels.preload_dependencies()
|
wheels.preload_dependencies()
|
||||||
|
|
||||||
@ -85,9 +86,10 @@ def ensure_svn_of_current_file(_scene=None):
|
|||||||
|
|
||||||
# If file is in an existing repo, we should switch over to that repo.
|
# If file is in an existing repo, we should switch over to that repo.
|
||||||
for i, existing_repo in enumerate(prefs.repositories):
|
for i, existing_repo in enumerate(prefs.repositories):
|
||||||
if ( existing_repo.url == scene_svn.svn_url and
|
if (
|
||||||
existing_repo.directory == scene_svn.svn_directory and
|
existing_repo.url == scene_svn.svn_url
|
||||||
existing_repo != old_active_repo
|
and existing_repo.directory == scene_svn.svn_directory
|
||||||
|
and existing_repo != old_active_repo
|
||||||
):
|
):
|
||||||
prefs.active_repo_idx = i
|
prefs.active_repo_idx = i
|
||||||
else:
|
else:
|
||||||
@ -117,6 +119,7 @@ def set_scene_svn_info(context) -> bool:
|
|||||||
scene_svn.svn_url = base_url
|
scene_svn.svn_url = base_url
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
############## AUTOMATICALLY KEEPING FILE STATUSES UP TO DATE ##################
|
############## AUTOMATICALLY KEEPING FILE STATUSES UP TO DATE ##################
|
||||||
################################################################################
|
################################################################################
|
||||||
@ -137,7 +140,7 @@ class BGP_SVN_Status(BackgroundProcess):
|
|||||||
self.output = execute_svn_command(
|
self.output = execute_svn_command(
|
||||||
context,
|
context,
|
||||||
["svn", "status", "--show-updates", "--verbose", "--xml"],
|
["svn", "status", "--show-updates", "--verbose", "--xml"],
|
||||||
use_cred=True
|
use_cred=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_output(self, context, prefs):
|
def process_output(self, context, prefs):
|
||||||
@ -165,7 +168,12 @@ class BGP_SVN_Authenticate(BGP_SVN_Status):
|
|||||||
|
|
||||||
def acquire_output(self, context, prefs):
|
def acquire_output(self, context, prefs):
|
||||||
repo = context.scene.svn.get_repo(context)
|
repo = context.scene.svn.get_repo(context)
|
||||||
if not repo or not repo.is_valid_svn or not repo.is_cred_entered or repo.authenticated:
|
if (
|
||||||
|
not repo
|
||||||
|
or not repo.is_valid_svn
|
||||||
|
or not repo.is_cred_entered
|
||||||
|
or repo.authenticated
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
super().acquire_output(context, prefs)
|
super().acquire_output(context, prefs)
|
||||||
@ -202,9 +210,11 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
|
|||||||
svn_path = Path(filepath_str)
|
svn_path = Path(filepath_str)
|
||||||
svn_path_str = str(svn_path.as_posix())
|
svn_path_str = str(svn_path.as_posix())
|
||||||
suffix = svn_path.suffix
|
suffix = svn_path.suffix
|
||||||
if (suffix.startswith(".r") and suffix[2:].isdecimal()) \
|
if (
|
||||||
or (suffix.startswith(".blend") and suffix[6:].isdecimal()) \
|
(suffix.startswith(".r") and suffix[2:].isdecimal())
|
||||||
or suffix.endswith("blend@"):
|
or (suffix.startswith(".blend") and suffix[6:].isdecimal())
|
||||||
|
or suffix.endswith("blend@")
|
||||||
|
):
|
||||||
# Do not add certain file extensions, ever:
|
# Do not add certain file extensions, ever:
|
||||||
# .r### files are from SVN conflicts waiting to be resolved.
|
# .r### files are from SVN conflicts waiting to be resolved.
|
||||||
# .blend@ is the Blender filesave temp file.
|
# .blend@ is the Blender filesave temp file.
|
||||||
@ -222,13 +232,16 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
|
|||||||
file_entry = repo.external_files.add()
|
file_entry = repo.external_files.add()
|
||||||
file_entry.svn_path = svn_path_str
|
file_entry.svn_path = svn_path_str
|
||||||
file_entry.absolute_path = str(
|
file_entry.absolute_path = str(
|
||||||
repo.svn_to_absolute_path(svn_path).as_posix())
|
repo.svn_to_absolute_path(svn_path).as_posix()
|
||||||
|
)
|
||||||
|
|
||||||
file_entry['name'] = svn_path.name
|
file_entry['name'] = svn_path.name
|
||||||
if not file_entry.exists:
|
if not file_entry.exists:
|
||||||
new_files_on_repo.add((file_entry.svn_path, repos_status))
|
new_files_on_repo.add((file_entry.svn_path, repos_status))
|
||||||
|
|
||||||
if entry_existed and (file_entry.repos_status == 'none' and repos_status != 'none'):
|
if entry_existed and (
|
||||||
|
file_entry.repos_status == 'none' and repos_status != 'none'
|
||||||
|
):
|
||||||
new_files_on_repo.add((file_entry.svn_path, repos_status))
|
new_files_on_repo.add((file_entry.svn_path, repos_status))
|
||||||
|
|
||||||
file_entry.revision = revision
|
file_entry.revision = revision
|
||||||
@ -240,13 +253,12 @@ def update_file_list(context, file_statuses: Dict[str, Tuple[str, str, int]]):
|
|||||||
# File entry status has changed between local and repo.
|
# File entry status has changed between local and repo.
|
||||||
file_strings = []
|
file_strings = []
|
||||||
for svn_path, repos_status in new_files_on_repo:
|
for svn_path, repos_status in new_files_on_repo:
|
||||||
status_char = constants.SVN_STATUS_NAME_TO_CHAR.get(
|
status_char = constants.SVN_STATUS_NAME_TO_CHAR.get(repos_status, " ")
|
||||||
repos_status, " ")
|
|
||||||
file_strings.append(f"{status_char} {svn_path}")
|
file_strings.append(f"{status_char} {svn_path}")
|
||||||
print(
|
print(
|
||||||
"SVN: Detected file changes on remote:\n",
|
"SVN: Detected file changes on remote:\n",
|
||||||
"\n".join(file_strings),
|
"\n".join(file_strings),
|
||||||
"\nUpdating log...\n"
|
"\nUpdating log...\n",
|
||||||
)
|
)
|
||||||
Processes.start('Log')
|
Processes.start('Log')
|
||||||
|
|
||||||
@ -336,5 +348,7 @@ def unregister():
|
|||||||
bpy.app.handlers.save_post.remove(ensure_svn_of_current_file)
|
bpy.app.handlers.save_post.remove(ensure_svn_of_current_file)
|
||||||
bpy.app.handlers.save_post.remove(mark_current_file_as_modified)
|
bpy.app.handlers.save_post.remove(mark_current_file_as_modified)
|
||||||
|
|
||||||
|
Processes.kill('Status')
|
||||||
|
|
||||||
|
|
||||||
registry = [SVN_OT_explain_status]
|
registry = [SVN_OT_explain_status]
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
|
## 0.1.4 - 2023-10-31
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
- Fix a crash in hotkeys.py
|
||||||
|
- Upgrade to Blender 4.0
|
||||||
|
- EasyWeight console warnings & more
|
||||||
|
- Catch an error (not sure of cause yet)
|
||||||
|
- Update hotkey registration code
|
||||||
|
- Use consistent registration pattern
|
||||||
|
|
||||||
|
### REMOVED
|
||||||
|
- Remove hotkeys for painting.
|
||||||
|
|
||||||
## 0.1.2 - 2023-08-02
|
## 0.1.2 - 2023-08-02
|
||||||
|
|
||||||
### FIXED
|
### FIXED
|
||||||
|
@ -13,13 +13,16 @@
|
|||||||
|
|
||||||
|
|
||||||
from .utils import hotkeys
|
from .utils import hotkeys
|
||||||
from . import rogue_weights
|
from . import (
|
||||||
from . import vertex_group_menu
|
rogue_weights,
|
||||||
from . import vertex_group_operators
|
vertex_group_menu,
|
||||||
from . import weight_paint_context_menu
|
vertex_group_operators,
|
||||||
from . import toggle_weight_paint
|
weight_paint_context_menu,
|
||||||
from . import force_apply_mirror
|
toggle_weight_paint,
|
||||||
from . import smart_weight_transfer
|
force_apply_mirror,
|
||||||
|
smart_weight_transfer,
|
||||||
|
prefs,
|
||||||
|
)
|
||||||
import bpy
|
import bpy
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
@ -44,6 +47,7 @@ modules = [
|
|||||||
vertex_group_operators,
|
vertex_group_operators,
|
||||||
vertex_group_menu,
|
vertex_group_menu,
|
||||||
rogue_weights,
|
rogue_weights,
|
||||||
|
prefs,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -75,26 +79,29 @@ def register_unregister_modules(modules, register: bool):
|
|||||||
m.unregister()
|
m.unregister()
|
||||||
|
|
||||||
|
|
||||||
addon_keymaps = []
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
register_unregister_modules(modules, True)
|
register_unregister_modules(modules, True)
|
||||||
|
|
||||||
global addon_keymaps
|
prefs_class = bpy.types.AddonPreferences.bl_rna_get_subclass_py(
|
||||||
addon_keymaps.append(
|
'EASYWEIGHT_addon_preferences'
|
||||||
|
)
|
||||||
|
prefs_class.hotkeys.append(
|
||||||
hotkeys.addon_hotkey_register(
|
hotkeys.addon_hotkey_register(
|
||||||
op_idname='object.custom_weight_paint_context_menu',
|
op_idname='object.custom_weight_paint_context_menu',
|
||||||
keymap_name='Weight Paint',
|
keymap_name='Weight Paint',
|
||||||
key_id='W',
|
key_id='W',
|
||||||
add_on_conflict=False,
|
add_on_conflict=False,
|
||||||
warn_on_conflict=True,
|
warn_on_conflict=True,
|
||||||
error_on_conflict=False
|
error_on_conflict=False,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
register_unregister_modules(modules, False)
|
prefs_class = bpy.types.AddonPreferences.bl_rna_get_subclass_py(
|
||||||
|
'EASYWEIGHT_addon_preferences'
|
||||||
|
)
|
||||||
|
for py_kmi in prefs_class.hotkeys:
|
||||||
|
py_kmi.unregister()
|
||||||
|
|
||||||
for pykmi in addon_keymaps:
|
register_unregister_modules(modules, False)
|
||||||
pykmi.unregister()
|
|
||||||
|
8
scripts-blender/addons/easy_weights/prefs.py
Normal file
8
scripts-blender/addons/easy_weights/prefs.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
|
class EASYWEIGHT_addon_preferences(bpy.types.AddonPreferences):
|
||||||
|
hotkeys = []
|
||||||
|
|
||||||
|
|
||||||
|
registry = [EASYWEIGHT_addon_preferences]
|
218
scripts-blender/addons/easy_weights/transfer_vertex_groups.py
Normal file
218
scripts-blender/addons/easy_weights/transfer_vertex_groups.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
from typing import Dict, Tuple, List
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy.props import IntProperty
|
||||||
|
from mathutils import Vector, kdtree
|
||||||
|
|
||||||
|
|
||||||
|
class EASYWEIGHT_OT_transfer_vertex_groups(bpy.types.Operator):
|
||||||
|
"""Transfer vertex groups from active to selected meshes"""
|
||||||
|
|
||||||
|
bl_idname = "object.transfer_vertex_groups"
|
||||||
|
bl_label = "Transfer Vertex Groups"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
expand: IntProperty(
|
||||||
|
name="Expand",
|
||||||
|
default=2,
|
||||||
|
min=0,
|
||||||
|
max=5,
|
||||||
|
description="Expand selection of source vertices from the nearest one. Higher values give smoother weights but pre-calculation takes longer",
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_transfer_vertex_groups_op(self, context):
|
||||||
|
self.layout.operator(
|
||||||
|
EASYWEIGHT_OT_transfer_vertex_groups.bl_idname,
|
||||||
|
text=EASYWEIGHT_OT_transfer_vertex_groups.bl_label,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if not context.active_object or context.active_object.type != 'MESH':
|
||||||
|
cls.poll_message_set("Active object must be a mesh.")
|
||||||
|
return False
|
||||||
|
selected_meshes = [ob for ob in context.selected_objects if ob.type == 'MESH']
|
||||||
|
if len(selected_meshes) < 2:
|
||||||
|
cls.poll_message_set("At least two meshes must be selected.")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
source_obj = context.object
|
||||||
|
vgroups = source_obj.vertex_groups
|
||||||
|
kd_tree = build_kdtree(source_obj.data)
|
||||||
|
|
||||||
|
for target_obj in context.selected_objects:
|
||||||
|
if target_obj == source_obj or target_obj.type != 'MESH':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Remove groups from the target obj that we will be transferring.
|
||||||
|
for src_vg in vgroups:
|
||||||
|
tgt_vg = target_obj.vertex_groups.get(src_vg.name)
|
||||||
|
if tgt_vg:
|
||||||
|
target_obj.vertex_groups.remove(tgt_vg)
|
||||||
|
|
||||||
|
vert_influence_map = build_vert_influence_map(
|
||||||
|
source_obj, target_obj, kd_tree, self.expand
|
||||||
|
)
|
||||||
|
transfer_vertex_groups(source_obj, target_obj, vert_influence_map, vgroups)
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
def precalc_and_transfer_single_group(source_obj, target_obj, vgroup_name, expand=2):
|
||||||
|
"""Convenience function to transfer a single group. For transferring multiple groups,
|
||||||
|
this is very inefficient and shouldn't be used.
|
||||||
|
|
||||||
|
Instead, you should:
|
||||||
|
- build_kd_tree ONCE per source mesh.
|
||||||
|
- build_vert_influence_map and transfer_vertex_groups ONCE per object pair.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Remove group from the target obj if it already exists.
|
||||||
|
tgt_vg = target_obj.vertex_groups.get(vgroup_name)
|
||||||
|
if tgt_vg:
|
||||||
|
target_obj.vertex_groups.remove(tgt_vg)
|
||||||
|
|
||||||
|
kd_tree = build_kdtree(source_obj.data)
|
||||||
|
vert_influence_map = build_vert_influence_map(
|
||||||
|
source_obj, target_obj, kd_tree, expand
|
||||||
|
)
|
||||||
|
transfer_vertex_groups(
|
||||||
|
source_obj,
|
||||||
|
target_obj,
|
||||||
|
vert_influence_map,
|
||||||
|
vgroups=[source_obj.vertex_groups[vgroup_name]],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_kdtree(mesh):
|
||||||
|
kd = kdtree.KDTree(len(mesh.vertices))
|
||||||
|
for i, v in enumerate(mesh.vertices):
|
||||||
|
kd.insert(v.co, i)
|
||||||
|
kd.balance()
|
||||||
|
return kd
|
||||||
|
|
||||||
|
|
||||||
|
def build_vert_influence_map(obj_from, obj_to, kd_tree, expand=2):
|
||||||
|
verts_of_edge = {
|
||||||
|
i: (e.vertices[0], e.vertices[1]) for i, e in enumerate(obj_from.data.edges)
|
||||||
|
}
|
||||||
|
|
||||||
|
edges_of_vert: Dict[int, List[int]] = {}
|
||||||
|
for edge_idx, edge in enumerate(obj_from.data.edges):
|
||||||
|
for vert_idx in edge.vertices:
|
||||||
|
if vert_idx not in edges_of_vert:
|
||||||
|
edges_of_vert[vert_idx] = []
|
||||||
|
edges_of_vert[vert_idx].append(edge_idx)
|
||||||
|
|
||||||
|
# A mapping from target vertex index to a list of source vertex indicies and
|
||||||
|
# their influence.
|
||||||
|
# This can be pre-calculated once per object pair, to minimize re-calculations
|
||||||
|
# of subsequent transferring of individual vertex groups.
|
||||||
|
vert_influence_map: List[int, List[Tuple[int, float]]] = {}
|
||||||
|
for i, dest_vert in enumerate(obj_to.data.vertices):
|
||||||
|
vert_influence_map[i] = get_source_vert_influences(
|
||||||
|
dest_vert, obj_from, kd_tree, expand, edges_of_vert, verts_of_edge
|
||||||
|
)
|
||||||
|
|
||||||
|
return vert_influence_map
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_vert_influences(
|
||||||
|
target_vert, obj_from, kd_tree, expand=2, edges_of_vert={}, verts_of_edge={}
|
||||||
|
) -> List[Tuple[int, float]]:
|
||||||
|
_coord, idx, dist = get_nearest_vert(target_vert.co, kd_tree)
|
||||||
|
source_vert_indices = [idx]
|
||||||
|
|
||||||
|
if dist == 0:
|
||||||
|
# If the vertex position is a perfect match, just use that one vertex with max influence.
|
||||||
|
return [(idx, 1)]
|
||||||
|
|
||||||
|
for i in range(0, expand):
|
||||||
|
new_indices = []
|
||||||
|
for vert_idx in source_vert_indices:
|
||||||
|
for edge in edges_of_vert[vert_idx]:
|
||||||
|
vert_other = other_vert_of_edge(edge, vert_idx, verts_of_edge)
|
||||||
|
if vert_other not in source_vert_indices:
|
||||||
|
new_indices.append(vert_other)
|
||||||
|
source_vert_indices.extend(new_indices)
|
||||||
|
|
||||||
|
distances: List[Tuple[int, float]] = []
|
||||||
|
distance_total = 0
|
||||||
|
for src_vert_idx in source_vert_indices:
|
||||||
|
distance = (target_vert.co - obj_from.data.vertices[src_vert_idx].co).length
|
||||||
|
distance_total += distance
|
||||||
|
distances.append((src_vert_idx, distance))
|
||||||
|
|
||||||
|
# Calculate influences such that the total of all influences adds up to 1.0,
|
||||||
|
# and the influence is inversely correlated with the distance.
|
||||||
|
parts = [1 / (dist / distance_total) for idx, dist in distances]
|
||||||
|
parts_sum = sum(parts)
|
||||||
|
|
||||||
|
influences = [
|
||||||
|
(idx, 1 if dist == 0 else part / parts_sum)
|
||||||
|
for part, dist in zip(parts, distances)
|
||||||
|
]
|
||||||
|
|
||||||
|
return influences
|
||||||
|
|
||||||
|
|
||||||
|
def get_nearest_vert(
|
||||||
|
coords: Vector, kd_tree: kdtree.KDTree
|
||||||
|
) -> Tuple[Vector, int, float]:
|
||||||
|
"""Return coordinate, index, and distance of nearest vert to coords in kd_tree."""
|
||||||
|
return kd_tree.find(coords)
|
||||||
|
|
||||||
|
|
||||||
|
def other_vert_of_edge(
|
||||||
|
edge: int, vert: int, verts_of_edge: Dict[int, Tuple[int, int]]
|
||||||
|
) -> int:
|
||||||
|
verts = verts_of_edge[edge]
|
||||||
|
assert vert in verts, f"Vert {vert} not part of edge {edge}."
|
||||||
|
return verts[0] if vert == verts[1] else verts[1]
|
||||||
|
|
||||||
|
|
||||||
|
def transfer_vertex_groups(obj_from, obj_to, vert_influence_map, src_vgroups):
|
||||||
|
"""Transfer src_vgroups in obj_from to obj_to using a pre-calculated vert_influence_map."""
|
||||||
|
|
||||||
|
for src_vg in src_vgroups:
|
||||||
|
target_vg = obj_to.vertex_groups.get(src_vg.name)
|
||||||
|
if target_vg == None:
|
||||||
|
target_vg = obj_to.vertex_groups.new(name=src_vg.name)
|
||||||
|
|
||||||
|
for i, dest_vert in enumerate(obj_to.data.vertices):
|
||||||
|
source_verts = vert_influence_map[i]
|
||||||
|
|
||||||
|
# Vertex Group Name : Weight
|
||||||
|
vgroup_weights = {}
|
||||||
|
|
||||||
|
for src_vert_idx, influence in source_verts:
|
||||||
|
for group in obj_from.data.vertices[src_vert_idx].groups:
|
||||||
|
group_idx = group.group
|
||||||
|
vg = obj_from.vertex_groups[group_idx]
|
||||||
|
if vg not in src_vgroups:
|
||||||
|
continue
|
||||||
|
if vg.name not in vgroup_weights:
|
||||||
|
vgroup_weights[vg.name] = 0
|
||||||
|
vgroup_weights[vg.name] += vg.weight(src_vert_idx) * influence
|
||||||
|
|
||||||
|
# Assign final weights of this vertex in the vertex groups.
|
||||||
|
for vg_name in vgroup_weights.keys():
|
||||||
|
target_vg = obj_to.vertex_groups.get(vg_name)
|
||||||
|
target_vg.add([dest_vert.index], vgroup_weights[vg_name], 'REPLACE')
|
||||||
|
|
||||||
|
|
||||||
|
registry = [EASYWEIGHT_OT_transfer_vertex_groups]
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.types.VIEW3D_MT_object.append(
|
||||||
|
EASYWEIGHT_OT_transfer_vertex_groups.draw_transfer_vertex_groups_op
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.types.VIEW3D_MT_object.remove(
|
||||||
|
EASYWEIGHT_OT_transfer_vertex_groups.draw_transfer_vertex_groups_op
|
||||||
|
)
|
@ -187,13 +187,21 @@ class PyKeyMapItem:
|
|||||||
|
|
||||||
# Warn or raise error about conflicts.
|
# Warn or raise error about conflicts.
|
||||||
if conflicts and (warn_on_conflict or error_on_conflict):
|
if conflicts and (warn_on_conflict or error_on_conflict):
|
||||||
conflict_info = "\n".join(["Conflict: " + kmi_to_str(kmi) for kmi in conflicts])
|
conflict_info = "\n".join(
|
||||||
|
["Conflict: " + kmi_to_str(kmi) for kmi in conflicts]
|
||||||
|
)
|
||||||
|
|
||||||
if error_on_conflict:
|
if error_on_conflict:
|
||||||
raise KeyMapException("Failed to register KeyMapItem due to conflicting items:" + conflict_info)
|
raise KeyMapException(
|
||||||
|
"Failed to register KeyMapItem due to conflicting items:"
|
||||||
|
+ conflict_info
|
||||||
|
)
|
||||||
if warn_on_conflict:
|
if warn_on_conflict:
|
||||||
print(
|
print(
|
||||||
"Warning: Conflicting KeyMapItems: " + str(self) + "\n" + conflict_info
|
"Warning: Conflicting KeyMapItems: "
|
||||||
|
+ str(self)
|
||||||
|
+ "\n"
|
||||||
|
+ conflict_info
|
||||||
)
|
)
|
||||||
|
|
||||||
return keymap, kmi
|
return keymap, kmi
|
||||||
@ -455,6 +463,7 @@ def get_kmi_key_string(kmi) -> str:
|
|||||||
return "Unassigned"
|
return "Unassigned"
|
||||||
return final_string
|
return final_string
|
||||||
|
|
||||||
|
|
||||||
def get_keymap_of_config(keyconfig: KeyConfig, keymap_name: str) -> Optional[KeyMap]:
|
def get_keymap_of_config(keyconfig: KeyConfig, keymap_name: str) -> Optional[KeyMap]:
|
||||||
space_type, region_type = get_ui_types_of_keymap(keymap_name)
|
space_type, region_type = get_ui_types_of_keymap(keymap_name)
|
||||||
keymap = keyconfig.keymaps.find(
|
keymap = keyconfig.keymaps.find(
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
## 0.0.4 - 2023-10-31
|
||||||
|
|
||||||
|
### FIXED
|
||||||
|
- Use consistent registration pattern
|
||||||
|
|
||||||
## 0.0.3 - 2023-08-02
|
## 0.0.3 - 2023-08-02
|
||||||
|
|
||||||
### FIXED
|
### FIXED
|
||||||
|
@ -5,7 +5,7 @@ from . import operators, ui, props, prefs
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
'name': "GeoNode Shape Keys",
|
'name': "GeoNode Shape Keys",
|
||||||
'author': "Demeter Dzadik",
|
'author': "Demeter Dzadik",
|
||||||
"version": (0, 0, 3),
|
"version": (0, 0, 4),
|
||||||
'blender': (3, 5, 0),
|
'blender': (3, 5, 0),
|
||||||
'description': "Shape keys in the modifier stack",
|
'description': "Shape keys in the modifier stack",
|
||||||
'location': "Properties->Mesh->Shape Keys->GeoNode ShapeKeys, only on overridden meshes",
|
'location': "Properties->Mesh->Shape Keys->GeoNode ShapeKeys, only on overridden meshes",
|
||||||
|
@ -481,11 +481,16 @@ class RR_OT_sqe_inspect_exr_sequence(bpy.types.Operator):
|
|||||||
for img in img_to_rm:
|
for img in img_to_rm:
|
||||||
bpy.data.images.remove(img)
|
bpy.data.images.remove(img)
|
||||||
|
|
||||||
|
if bpy.app.version_string.split('.')[0] == '3':
|
||||||
|
color_space_name = "Linear"
|
||||||
|
else:
|
||||||
|
color_space_name = "Linear Rec.709"
|
||||||
|
|
||||||
# Create new image datablock.
|
# Create new image datablock.
|
||||||
image = bpy.data.images.load(exr_seq[0].as_posix(), check_existing=True)
|
image = bpy.data.images.load(exr_seq[0].as_posix(), check_existing=True)
|
||||||
image.name = exr_seq[0].parent.name + "_RENDER"
|
image.name = exr_seq[0].parent.name + "_RENDER"
|
||||||
image.source = "SEQUENCE"
|
image.source = "SEQUENCE"
|
||||||
image.colorspace_settings.name = "Linear"
|
image.colorspace_settings.name = color_space_name
|
||||||
|
|
||||||
# Set active image.
|
# Set active image.
|
||||||
image_editor.spaces.active.image = image
|
image_editor.spaces.active.image = image
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
# Scripts and utilities
|
|
||||||
|
|
||||||
## blender-purge
|
|
||||||
|
|
||||||
Command line tool to purge orphan data of many blend files via the console.
|
|
||||||
|
|
||||||
Author: Paul Golter
|
|
||||||
|
|
||||||
|
|
||||||
## freesound-credits
|
|
||||||
Snippet to generate credits for sounds taken from freesound.org in a VSE sequence.
|
|
||||||
|
|
||||||
Author & Maintainer: Francesco Siddi
|
|
58
scripts/bbatch/README.md
Normal file
58
scripts/bbatch/README.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# bbatch
|
||||||
|
`bbatch` is a command line tools to crawl directories for .blend files and execute a provied script.
|
||||||
|
## Table of Contents
|
||||||
|
- [Prerequisite](#prerequisite)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [How to get started](#how-to-get-started)
|
||||||
|
|
||||||
|
## Prerequisite
|
||||||
|
In order to use this tool you need:
|
||||||
|
- Python 3.5+
|
||||||
|
|
||||||
|
## Run
|
||||||
|
This folder contains a command line tool that doesn't require installation to use properly. This tool doesn't require installation to be run. To run `bbatch` without installation follow the steps below.
|
||||||
|
1. Clone this repository with `git clone https://projects.blender.org/studio/blender-studio-pipeline.git`
|
||||||
|
2. Run `cd blender-studio-pipeline/scripts/bbatch` to enter directory
|
||||||
|
3. Run program with `python bbatch /my-folder/`
|
||||||
|
|
||||||
|
|
||||||
|
## Installation (OPTIONAL)
|
||||||
|
Download or clone this repository. This repository is a command line tool that can be installed with the python packaging manager. Installation is an optional step only intended for advanced users.
|
||||||
|
This script does the following (follow this if you want to do this manually or on another platform):
|
||||||
|
|
||||||
|
1. Clone this repository with `git clone https://projects.blender.org/studio/blender-studio-pipeline.git`
|
||||||
|
2. Run `cd blender-studio-pipeline/scripts/bbatch` to enter directory
|
||||||
|
3. Install with `pip install .`
|
||||||
|
4. Run with `bbatch /my-folder/`
|
||||||
|
5. Get help with `bbatch -h`
|
||||||
|
|
||||||
|
## How to get started
|
||||||
|
Run directly out of repo folder or follow above installation instructions. Give `bbatch` a path to a .blend file or a folder as first argument, The detected blend files will be opened in the background, the python script will be executed, and the file closes. If blender is not installed at the default location of your computer, you need to provide a blender executable using the --exec flag.
|
||||||
|
|
||||||
|
If a script is provided, `bbatch` will automatically save after execution of the script, without saving any backups aka [save versions](https://docs.blender.org/manual/en/latest/editors/preferences/save_load.html#:~:text=of%20using%20characters.-,Save%20Versions,-Number%20of%20versions). If you already have saving logic in your provided script, skip this step using the "--nosave" flag.
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
| --script| Path to blender python script(s) to execute inside .blend files during crawl.|
|
||||||
|
| -n, --nosave|Don't save .blend after script execution.|
|
||||||
|
| -r, --recursive| If provided in combination with a folder path will perform recursive crawl|
|
||||||
|
| -f --filter| Provide a string to filter the found .blend files|
|
||||||
|
| -a, --ask| If provided there will be a prompt for confirmation before running script on .blend files.|
|
||||||
|
| -p, --purge| Run 'built-in function to purge data-blocks from all .blend files found in crawl, and saves them.|
|
||||||
|
| --exec| If provided user must provide blender executable path, OS default blender will not be used if found.|
|
||||||
|
| -h, --help| show the above help message and exit|
|
||||||
|
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
| Action | Command |
|
||||||
|
| ----------- | ----------- |
|
||||||
|
|Prints the names of .blends in Current Directory | `bbatch ./` |
|
||||||
|
|Print the names of .blends Recursively | `bbatch /my-folder/ --recursive` |
|
||||||
|
|Print only the names of .blends matching a provided string |`bbatch /my-folder/ --find string`|
|
||||||
|
|Run default 'Purge' script on .blends in Current Directory |`bbatch /my-folder/ --purge`|
|
||||||
|
|Run custom script on all .blends in Current Directory |`bbatch /my-folder/ --script /my-directory/my-script.py`|
|
||||||
|
|Ask/Prompt before script execution|`bbatch /my-folder/ --script /my-directory/my-script.py --ask`|
|
||||||
|
|Run script on .blends without saving |`bbatch /my-folder/ --script /my-directory/my-script.py --nosave` |
|
||||||
|
|Run with a custom blender executable|`bbatch /my-folder/ --exec /path-to-blender-executable/blender`|
|
||||||
|
|
@ -33,9 +33,7 @@ import uuid
|
|||||||
# Command line arguments.
|
# Command line arguments.
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"path",
|
"path", help="Path to a file(s) or folder(s) on which to perform crawl.", nargs='+'
|
||||||
help="Path to a file(s) or folder(s) on which to perform crawl.",
|
|
||||||
nargs='+'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -85,8 +83,6 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cancel_program(message: str):
|
def cancel_program(message: str):
|
||||||
print(message)
|
print(message)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -137,7 +133,8 @@ def check_file_exists(file_path_str: str, error_msg: str) -> Path:
|
|||||||
else:
|
else:
|
||||||
cancel_program(error_msg)
|
cancel_program(error_msg)
|
||||||
|
|
||||||
def script_append_save(script: Path, skip_save:bool):
|
|
||||||
|
def script_append_save(script: Path, skip_save: bool):
|
||||||
if skip_save:
|
if skip_save:
|
||||||
return script
|
return script
|
||||||
save_script = get_default_script("save.py", True)
|
save_script = get_default_script("save.py", True)
|
||||||
@ -150,22 +147,26 @@ def script_append_save(script: Path, skip_save:bool):
|
|||||||
script_data += "\n"
|
script_data += "\n"
|
||||||
script_data += save_data
|
script_data += save_data
|
||||||
temp_dir = Path(tempfile.TemporaryDirectory().name).parent
|
temp_dir = Path(tempfile.TemporaryDirectory().name).parent
|
||||||
new_temp_file = Path.joinpath(Path(temp_dir), f"blender_crawl_{uuid.uuid4()}.py")
|
new_temp_file = Path.joinpath(Path(temp_dir), f"bbatch_{uuid.uuid4()}.py")
|
||||||
with open(new_temp_file, "w") as new_file:
|
with open(new_temp_file, "w") as new_file:
|
||||||
new_file.write(script_data)
|
new_file.write(script_data)
|
||||||
return new_temp_file
|
return new_temp_file
|
||||||
|
|
||||||
|
|
||||||
def get_default_script(file_name:str, purge: bool):
|
def get_default_script(file_name: str, purge: bool):
|
||||||
# Cancel function if user has not supplied purge arg
|
# Cancel function if user has not supplied purge arg
|
||||||
if not purge:
|
if not purge:
|
||||||
return
|
return
|
||||||
scripts_directory = Path((os.path.dirname(__file__))).joinpath("default_scripts/")
|
scripts_directory = Path((os.path.dirname(__file__))).joinpath("default_scripts/")
|
||||||
purge_script = os.path.join(scripts_directory.resolve(), file_name)
|
purge_script = os.path.join(scripts_directory.resolve(), file_name)
|
||||||
return check_file_exists(str(purge_script), "Default scripts location may be invalid")
|
return check_file_exists(
|
||||||
|
str(purge_script), "Default scripts location may be invalid"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
"""Crawl blender files in a directory and run a provided scripts"""
|
"""Crawl blender files in a directory and run a provided scripts"""
|
||||||
# Parse arguments.
|
# Parse arguments.
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -208,11 +209,15 @@ def main() -> int:
|
|||||||
if file_path.is_dir():
|
if file_path.is_dir():
|
||||||
if recursive:
|
if recursive:
|
||||||
blend_files = [
|
blend_files = [
|
||||||
f for f in file_path.glob("**/*") if f.is_file() and f.suffix == ".blend"
|
f
|
||||||
|
for f in file_path.glob("**/*")
|
||||||
|
if f.is_file() and f.suffix == ".blend"
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
blend_files = [
|
blend_files = [
|
||||||
f for f in file_path.iterdir() if f.is_file() and f.suffix == ".blend"
|
f
|
||||||
|
for f in file_path.iterdir()
|
||||||
|
if f.is_file() and f.suffix == ".blend"
|
||||||
]
|
]
|
||||||
files.extend(blend_files)
|
files.extend(blend_files)
|
||||||
# If just one file.
|
# If just one file.
|
||||||
@ -247,9 +252,7 @@ def main() -> int:
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if not scripts:
|
if not scripts:
|
||||||
cancel_program(
|
cancel_program("No script files were provided to execute.")
|
||||||
"No script files were provided to execute."
|
|
||||||
)
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
for blend_file in files:
|
for blend_file in files:
|
||||||
@ -267,5 +270,6 @@ def main() -> int:
|
|||||||
cancel_program(f"Blender Crashed on file: {blend_file.as_posix()}")
|
cancel_program(f"Blender Crashed on file: {blend_file.as_posix()}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""The setup script for blender-crawl."""
|
"""The setup script for bbatch."""
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
@ -24,10 +24,13 @@ setup(
|
|||||||
description="Command line tool to perform recursive crawl blend files from the console",
|
description="Command line tool to perform recursive crawl blend files from the console",
|
||||||
long_description=readme,
|
long_description=readme,
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
keywords="blender_crawl",
|
keywords="bbatch",
|
||||||
name="blender_crawl",
|
name="bbatch",
|
||||||
packages=["blender_crawl", "blender_crawl.default_scripts",],
|
packages=[
|
||||||
|
"bbatch",
|
||||||
|
"bbatch.default_scripts",
|
||||||
|
],
|
||||||
version="0.1.1",
|
version="0.1.1",
|
||||||
entry_points={"console_scripts": ["blender_crawl = blender_crawl.__main__:main"]},
|
entry_points={"console_scripts": ["bbatch = bbatch.__main__:main"]},
|
||||||
package_data={'blender_crawl.default_scripts': ['*']}, #TODO Verify this is working correctly after install
|
package_data={'bbatch.default_scripts': ['*']},
|
||||||
)
|
)
|
@ -1,48 +0,0 @@
|
|||||||
# Blender Crawl
|
|
||||||
`blender_crawl` is a command line tools to crawl directories for .blend files and execute a provied script.
|
|
||||||
## Table of Contents
|
|
||||||
- [Prerequisite](#prerequisite)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [How to get started](#how-to-get-started)
|
|
||||||
|
|
||||||
## Prerequisite
|
|
||||||
In order to use this tool you need:
|
|
||||||
- Python 3.5+
|
|
||||||
|
|
||||||
## Run
|
|
||||||
This folder contains a command line tool that doesn't require installation to use properly. This tool doesn't require installation to be run. To run `blender-crawl` without installation follow the steps below.
|
|
||||||
1. Clone this repository with `git clone https://projects.blender.org/studio/blender-studio-pipeline.git`
|
|
||||||
2. Run `cd blender-studio-pipeline/scripts/blender-crawl` to enter directory
|
|
||||||
3. Run program with `python blender_crawl /my-folder/`
|
|
||||||
|
|
||||||
|
|
||||||
## How to get started
|
|
||||||
Run directly out of repo folder or follow above installation instructions. Give `blender_crawl` a path to a .blend file or a folder as first argument, The detected blend files will be opened in the background, the python script will be executed, and the file closes. If blender is not installed at the default location of your computer, you need to provide a blender executable using the --exec flag.
|
|
||||||
|
|
||||||
If a script is provided, `blender_crawl` will automatically save after execution of the script, without saving any backups aka [save versions](https://docs.blender.org/manual/en/latest/editors/preferences/save_load.html#:~:text=of%20using%20characters.-,Save%20Versions,-Number%20of%20versions). If you already have saving logic in your provided script, skip this step using the "--nosave" flag.
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
| ----------- | ----------- |
|
|
||||||
| --script| Path to blender python script(s) to execute inside .blend files during crawl.|
|
|
||||||
| -n, --nosave|Don't save .blend after script execution.|
|
|
||||||
| -r, --recursive| If provided in combination with a folder path will perform recursive crawl|
|
|
||||||
| -f --filter| Provide a string to filter the found .blend files|
|
|
||||||
| -a, --ask| If provided there will be a prompt for confirmation before running script on .blend files.|
|
|
||||||
| -p, --purge| Run 'built-in function to purge data-blocks from all .blend files found in crawl, and saves them.|
|
|
||||||
| --exec| If provided user must provide blender executable path, OS default blender will not be used if found.|
|
|
||||||
| -h, --help| show the above help message and exit|
|
|
||||||
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
| Action | Command |
|
|
||||||
| ----------- | ----------- |
|
|
||||||
|Prints the names of .blends in Current Directory | `blender_crawl ./` |
|
|
||||||
|Print the names of .blends Recursively | `blender_crawl /my-folder/ --recursive` |
|
|
||||||
|Print only the names of .blends matching a provided string |`blender_crawl /my-folder/ --find string`|
|
|
||||||
|Run default 'Purge' script on .blends in Current Directory |`blender_crawl /my-folder/ --purge`|
|
|
||||||
|Run custom script on all .blends in Current Directory |`blender_crawl /my-folder/ --script /my-directory/my-script.py`|
|
|
||||||
|Ask/Prompt before script execution|`blender_crawl /my-folder/ --script /my-directory/my-script.py --ask`|
|
|
||||||
|Run script on .blends without saving |`blender_crawl /my-folder/ --script /my-directory/my-script.py --nosave` |
|
|
||||||
|Run with a custom blender executable|`blender_crawl /my-folder/ --exec /path-to-blender-executable/blender`|
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user