This commit is contained in:
Marie Fetiveau 2019-04-10 16:23:07 -04:00
Родитель 25b419a9bc bc3958eb1e
Коммит 860bf9ce41
51 изменённых файлов: 1333 добавлений и 0 удалений

12
.gitignore поставляемый Executable file
Просмотреть файл

@ -0,0 +1,12 @@
artifacts/**
build/**
.build_script/**
node_modules/**
.DS_Store
.npmrc
!Documentation~
!.Documentation
npm-debug.log
build.sh.meta
build.bat.meta
.idea/

99
.yamato/upm-ci.yml Executable file
Просмотреть файл

@ -0,0 +1,99 @@
test_editors:
- version: 2018.3
- version: 2019.1
- version: 2019.2
test_platforms:
- name: win
type: Unity::VM
image: package-ci/win10:stable
flavor: m1.large
- name: mac
type: Unity::VM::osx
image: buildfarm/mac:stable
flavor: m1.mac
---
pack:
name: Pack
agent:
type: Unity::VM
image: package-ci/win10:stable
flavor: m1.large
commands:
- npm install upm-ci-utils -g --registry https://api.bintray.com/npm/unity/unity-npm
- upm-ci package pack --package-path com.unity.film-tv.toolbox
artifacts:
packages:
paths:
- "upm-ci~/packages/**/*"
{% for editor in test_editors %}
{% for platform in test_platforms %}
test_{{ platform.name }}_{{ editor.version }}:
name : Test {{ editor.version }} on {{ platform.name }}
agent:
type: {{ platform.type }}
image: {{ platform.image }}
flavor: {{ platform.flavor}}
commands:
- npm install upm-ci-utils -g --registry https://api.bintray.com/npm/unity/unity-npm
- upm-ci package test --unity-version {{ editor.version }} --package-path com.unity.film-tv.toolbox
artifacts:
logs.zip:
paths:
- "upm-ci~/test-results/**/*"
dependencies:
- .yamato/upm-ci.yml#pack
{% endfor %}
{% endfor %}
test_trigger:
name: Tests Trigger
agent:
type: Unity::VM
image: package-ci/win10:stable
flavor: m1.large
commands:
- dir
triggers:
branches:
only:
- "/.*/"
artifacts:
logs:
paths:
- "upm-ci~/test-results/**/*"
packages:
paths:
- "upm-ci~/packages/**/*"
dependencies:
- .yamato/upm-ci.yml#pack
{% for editor in test_editors %}
{% for platform in test_platforms %}
- .yamato/upm-ci.yml#test_{{platform.name}}_{{editor.version}}
{% endfor %}
{% endfor %}
publish:
name: Publish
agent:
type: Unity::VM
image: package-ci/win10:stable
flavor: m1.large
commands:
- npm install upm-ci-utils -g --registry https://api.bintray.com/npm/unity/unity-npm
- upm-ci package publish --package-path com.unity.film-tv.toolbox
triggers:
tags:
only:
- /^(v|V)\d+\.\d+\.\d+(-preview(\.\d+)?)?$/
artifacts:
artifacts.zip:
paths:
- "upm-ci~/packages/*.tgz"
dependencies:
- .yamato/upm-ci.yml#pack
{% for editor in test_editors %}
{% for platform in test_platforms %}
- .yamato/upm-ci.yml#test_{{ platform.name }}_{{ editor.version }}
{% endfor %}
{% endfor %}

30
ShotgunSamples/README.md Normal file
Просмотреть файл

@ -0,0 +1,30 @@
Shotgun CSV Import Export
--------------------------
This sample shows how to create a timeline in Unity using a shot sequence from a Shotgun CSV export and export back modifications.
**This package is ONLY needed if you are running Unity versions prior to 2019.1.**
If not, directly install `com.unity.film-tv.toolbox` via Package Manager window.
### How to install
* Download `ShotgunCSVImportExport.unitypackage` and import it in Unity
* This will import in your Assets folder :
* CSVImportExport.cs : a sample script demonstrating how to simply import / export a Shotgun CSV file
* shot.csv : a shotgun csv export sample, used in the import.
### How to test
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Import CSV`
* This creates a Timeline Asset (see shotgun_imported_timeline in the sample folder) from the `shot.csv` sample file :
![Package Manager UI](../com.unity.film-tv.toolbox/Documentation~/images/ShotgunCSVImportResultTimeline.png)
* Make edits to the Timeline asset
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Export CSV`:
* See export_from_unity.csv in the sample folder
### Limitations
* Having commas in fields (e.g.: `Opening sequence, part 2`) will confuse the parser
* Having duplicate column names will cause errors due to duplicate keys

Двоичные данные
ShotgunSamples/ShotgunCSVImportExport.unitypackage Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,6 @@
upm-ci~/**
.Editor/**
.yamato/**
*.zip*
TestRunnerOptions.json

Просмотреть файл

@ -0,0 +1,9 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.1.0-preview.0] - 2019-04-15
* Initialisation of the package
* Material Remapper

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 67b3fe034077744fabda8e32148aab07
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,15 @@
# Contributing
## If you are interested in contributing, here are some ground rules:
* First share your plan with us before starting to avoid redundant or disruptive work
* External contributors can use Github issues for that
* Branch of master for hot fixes, or dev for other works
* Please include the ticket ID in your commits (either Jira or Github issue)
## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/licenses/Unity_Contribution_Agreement)
By making a pull request, you are confirming agreement to the terms and conditions of the UCA, including that your Contributions are your original creation and that you have complete right and authority to make your Contributions.
## Once you have a change ready following these ground rules.
* Before submitting anything, please clean your history (squash, fix-up commits)
* Then create a pull request targeting dev branch and add the issue ID either in the title or the description of the PR.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 929c9cd4e3e8d475991b36dbf02edd61
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,62 @@
Unity Film / TV toolbox
=========================
This repository is a collection of experimental, proof of concept or helper tools for Film / TV productions.
Tools
=========================
Material Remapper
----------------------
Material Remapper allows to more easily assign material to models containing a lot of meshes (Alembic, etc).
It works with Legacy as well as SRP pipelines.
The tool was coded in large part by Mike Wutherick.
### How to Use
* Open the Material Remapper Window via `Window->Film-TV toolbox->Material Remapper`
* Select the root GameObject containing the MeshRenderers you want to set-up
* Click on "Update Scene Selection". This will load the list of unique MeshRenderers: ![import settings](images/WindowPopulated.png)
* Add more materials to the session library by clicking "Add new Material Entry". Each new entry is initialized to the default current pipeline material.
* Replace the default materials with the wanted materials.
* Chose material assignments from the popup (Multiple materials per MeshRenderer are supported).
* Once all assignments are configured, commit the changes to the Scene pressing "Apply Material Changes".
* If at any moment you wish to return to the default state of the Material Remapper, press ResetRemapper.
Samples
==========================
Since 2019.1, samples can be imported via the Package Manager window (Window > Package Manager) :
![Package Manager UI](images/SamplesImport.png)
If you are using a previous version, download the sample on [film-tv-toolbox repository](https://github.com/Unity-Technologies/film-tv-toolbox).
Shotgun CSV Import Export
--------------------------
This sample shows how to create a timeline in Unity using a shot sequence from a Shotgun CSV export and export back modifications.
### How to install
* Import the sample via Package Manager UI
* This will import in your Assets folder :
* CSVImportExport.cs : a sample script demonstrating how to simply import / export a Shotgun CSV file
* shot.csv : a shotgun csv export sample, used in the import.
### How to test
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Import CSV`
* This creates a Timeline Asset (see shotgun_imported_timeline in the sample folder) from the `shot.csv` sample file :
![Package Manager UI](images/ShotgunCSVImportResultTimeline.png)
* Make edits to the Timeline asset
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Export CSV`:
* See export_from_unity.csv in the sample folder
### Limitations
* Having commas in fields (e.g.: `Opening sequence, part 2`) will confuse the parser
* Having duplicate column names will cause errors due to duplicate keys

Двоичные данные
com.unity.film-tv.toolbox/Documentation~/images/SamplesImport.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 93 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 81 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 131 KiB

Просмотреть файл

@ -0,0 +1,91 @@
fileFormatVersion: 2
guid: 216ce80e76a3f4a848c2de909f9e6211
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 92d0e0de0c145404ca3404dc94023ca9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 94efd31d9b4904dc7a31e2065c0891db
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.FilmTV.Toolbox
{
[Serializable]
public class MaterialRemapperModel : ScriptableObject
{
[SerializeField]
public List<Material> materialList = new List<Material>();
[SerializeField]
public List<Transform> objectsToRemap = new List<Transform>();
[SerializeField]
List<MeshRenderer> meshToRemap = new List<MeshRenderer>();
[SerializeField]
public List<RemapList> remapList = new List<RemapList>(); // the remapper - stores mesh, material and selected index (popup index for the remapper)
/// <summary>
/// from our scene selection, prep the remapper, finding all of the mesh & materials that we'll want to remap
/// </summary>
/// <param name="objectList"></param>
public void FindMeshesAndMaterials(List<Transform> objectList)
{
objectsToRemap.Clear();
meshToRemap.Clear();
objectsToRemap = objectList;
foreach (var entry in objectList)
{
var meshList = entry.GetComponentsInChildren<MeshRenderer>();
meshToRemap.AddRange(meshList.ToList());
}
var uniqueMeshList = new List<MeshRenderer>();
remapList.Clear();
materialList = meshToRemap.SelectMany(m => m.sharedMaterials).Distinct().ToList();
// build our list of unique meshes
foreach (var mesh in meshToRemap)
{
// if we don't already have the name in our list, then add it
if (!uniqueMeshList.Contains(mesh))
{
var newRemap = new RemapList()
{
mesh = mesh,
selectedIndex = new List<int>()
};
// prep our remapper
newRemap.selectedIndex.Clear();
foreach (var mat in mesh.sharedMaterials)
{
var idx = materialList.IndexOf(mat);
newRemap.selectedIndex.Add(idx);
}
// build the list of things that we are going to remap
remapList.Add(newRemap);
uniqueMeshList.Add(mesh);
}
}
Debug.Log("MaterialRemapper:FindMeshesAndMaterials() - object count: " + objectList.Count + " Found : " + meshToRemap.Count + " meshes, " + uniqueMeshList.Count + " unique meshes");
}
/// <summary>
/// reset everything
/// </summary>
public void ResetData()
{
materialList.Clear();
objectsToRemap.Clear();
meshToRemap.Clear();
remapList.Clear();
}
public static Material GetDefaultMaterial()
{
Material defaultMat;
var pipelineAsset = GraphicsSettings.renderPipelineAsset;
if( pipelineAsset != null)
{
#if UNITY_2019_1_OR_NEWER
defaultMat = pipelineAsset.defaultMaterial;
#else
defaultMat = pipelineAsset.GetDefaultMaterial();
#endif
}
else
{
defaultMat = new Material(Shader.Find("Standard"));
}
return defaultMat;
}
/// <summary>
/// Do the remapping!
/// </summary>
public void ApplyMaterialChanges()
{
Debug.Log("MaterialRemapper:ApplyMaterialChanges() - " + meshToRemap.Count + " total meshes - " + remapList.Count + " unique meshes to remap to " + materialList.Count + " materials");
// iterate through all of the meshes
foreach (var mesh in meshToRemap)
{
var sourceMesh = mesh;
// for each mesh, find the remap equiv
var remap = remapList.FirstOrDefault(u => u.mesh.name == mesh.name);
Debug.Log("mesh: " + sourceMesh.name + " found remap mesh: " + remap.mesh.name);
// build our materials struct
Debug.Log("preparing to remap materials for mesh: " + sourceMesh.name);
var matList = new List<Material>();
foreach( var matIdx in remap.selectedIndex)
{
if( matIdx != -1)
{
var newMat = materialList[matIdx];
matList.Add(newMat);
Debug.Log(" - material: " + newMat.name);
}
else
{
Debug.Log(" - no material");
}
}
if( matList.Count > 0)
{
Undo.RegisterCompleteObjectUndo(sourceMesh.sharedMaterials.Cast<UnityEngine.Object>().ToArray(), "Assign Materials");
Debug.Log("Applying materials...");
sourceMesh.sharedMaterials = matList.ToArray();
}
}
}
/// <summary>
/// the remapping list of mesh to materials
/// </summary>
[Serializable]
public class RemapList
{
public MeshRenderer mesh;
public List<int> selectedIndex = new List<int>();
}
/********************************
* utility functions
********************************/
/// <summary>
/// get count of mesh in our selection
/// </summary>
/// <returns></returns>
public int GetMeshCount()
{
var meshCount = 0;
foreach (var entry in objectsToRemap)
{
var count = entry.GetComponentsInChildren<MeshRenderer>().Length;
meshCount += count;
}
return meshCount;
}
/// <summary>
/// figure out the count of unique mesh in our list
/// </summary>
public List<MeshRenderer> GetUniqueMeshList()
{
var ret = new List<MeshRenderer>();
foreach (var entry in objectsToRemap)
{
var meshList = entry.GetComponentsInChildren<MeshRenderer>();
foreach (var mesh in meshList)
{
if (!ret.FirstOrDefault(u => u.name == mesh.name))
{
ret.Add(mesh);
}
}
}
return ret;
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b4a85f04314e43debe43eae1ff22535
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,246 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace UnityEditor.FilmTV.Toolbox
{
[Serializable]
public class MaterialRemapperView : EditorWindow
{
[SerializeField]
Transform[] selection; // user scene selection
// UI States
bool showGlobalUI;
bool showMeshRemapList;
[SerializeField]
MaterialRemapperModel model;
static MaterialRemapperView instance;
Vector2 scrollPos;
/// <summary>
/// initialization
/// </summary>
[MenuItem(MaterialRemapperLocalization.menuItemLabel)]
public static void ShowWindow()
{
var thisWindow = GetWindow<MaterialRemapperView>(false, MaterialRemapperLocalization.windowLabel, true);
thisWindow.ResetData();
}
/// <summary>
/// reset ourself
/// </summary>
void ResetData()
{
model.ResetData();
}
void ResetSceneSelection()
{
model.ResetData();
}
void OnEnable()
{
instance = this;
if (model == null)
instance.model = CreateInstance<MaterialRemapperModel>();
instance.selection = Selection.GetTransforms(
SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
Undo.RegisterCompleteObjectUndo(instance.selection, "Replace Materials in Selection");
instance.model.objectsToRemap = instance.selection.ToList();
}
/// <summary>
/// Draw the UI
/// </summary>
void OnGUI()
{
var currentHeight = position.height;
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Width(EditorGUIUtility.currentViewWidth), GUILayout.Height(currentHeight));
{
GUILayout.Space(20);
EditorGUILayout.BeginVertical();
{
EditorGUILayout.BeginHorizontal();
{
if (GUILayout.Button(MaterialRemapperLocalization.updateSceneSelection, GUILayout.Width(300), GUILayout.Height(35)))
{
selection = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
Debug.Log("Selection updated, found : " + selection.Length + " entries");
// reset the scene selection (doesn't reset the material list)
ResetSceneSelection();
model.FindMeshesAndMaterials(selection.ToList()); // build a list of MeshRenderers from our selection
}
if (GUILayout.Button(MaterialRemapperLocalization.resetRemapper, GUILayout.Width(300), GUILayout.Height(35)))
{
// reset everything
ResetData();
}
}
EditorGUILayout.EndHorizontal();
showGlobalUI = model.objectsToRemap.Count > 0;
GUILayout.Space(20);
GUILayout.Label(MaterialRemapperLocalization.meshSelectLabel, EditorStyles.boldLabel, GUILayout.Width(500));
// hide the UI unless we have a selection
if (showGlobalUI)
{
EditorGUILayout.BeginHorizontal();
{
// list of objects that we want to remap
EditorGUILayout.BeginVertical(GUILayout.Width(250));
{
GUILayout.Space(10);
// list of objects
GUILayout.Label(MaterialRemapperLocalization.objectsToRemap, EditorStyles.boldLabel,
GUILayout.MinWidth(400));
// list the objects
foreach (var entry in model.objectsToRemap)
{
EditorGUILayout.ObjectField(entry.name, entry, typeof(Object), true,
GUILayout.MinWidth(400));
}
// temp debug values
GUILayout.Label(MaterialRemapperLocalization.selectionDetails, EditorStyles.boldLabel,
GUILayout.Width(250));
var meshCount = model.GetMeshCount();
GUILayout.Label(MaterialRemapperLocalization.meshCountInSelection + meshCount,
GUILayout.Width(250));
var uniqueMesh = model.GetUniqueMeshList().Count;
GUILayout.Label(MaterialRemapperLocalization.uniqueMeshCount + uniqueMesh,
GUILayout.Width(250));
}
EditorGUILayout.EndVertical();
}
{
// list of materials that we want to use for remapping
EditorGUILayout.BeginVertical(GUILayout.Width(250));
{
// list of materials
GUILayout.Label(MaterialRemapperLocalization.materialsLibrary, EditorStyles.boldLabel,
GUILayout.MinWidth(400));
EditorGUILayout.BeginHorizontal();
{
if (GUILayout.Button(MaterialRemapperLocalization.addNewMaterialEntry,
GUILayout.Width(400), GUILayout.Height(35)))
{
var newMat = MaterialRemapperModel.GetDefaultMaterial();
// add to our temp list of materials
model.materialList.Add(newMat);
newMat.name = "MaterialRemap";
}
}
EditorGUILayout.EndHorizontal();
for (var i = 0; i < model.materialList.Count; i++)
{
model.materialList[i] = (Material) EditorGUILayout.ObjectField(model.materialList[i].name,
model.materialList[i], typeof(Material), true, GUILayout.MinWidth(400));
}
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
{
GUILayout.Space(20);
// if we have any, show the remapper
showMeshRemapList = model.materialList.Count > 0;
if (showMeshRemapList)
{
GUILayout.Label(MaterialRemapperLocalization.meshRemapLabel, EditorStyles.boldLabel, GUILayout.Width(500));
// remapping UI
EditorGUILayout.BeginHorizontal(GUILayout.Width(500));
{
// mesh list to map to materials
EditorGUILayout.BeginVertical();
{
GUILayout.Label(MaterialRemapperLocalization.remapMesh, EditorStyles.boldLabel, GUILayout.Width(500));
// list the objects
foreach (var remapItem in model.remapList)
{
EditorGUILayout.BeginHorizontal();
// mesh that we want to remap
GUILayout.Label(remapItem.mesh.name, GUILayout.Width(200));
// show all of the materials in the mesh
for (var j = 0; j < remapItem.mesh.sharedMaterials.Length; j++)
{
// do we have any materials to remap
remapItem.selectedIndex[j] = EditorGUILayout.Popup(remapItem.selectedIndex[j], model.materialList.Select(x=>x.name).ToArray(), GUILayout.Width(200));
}
EditorGUILayout.EndHorizontal();
}
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
}
else
{
GUILayout.Label(MaterialRemapperLocalization.noMaterialToRemap, EditorStyles.boldLabel, GUILayout.Width(500));
}
}
if (showGlobalUI)
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("Apply Material Changes", GUILayout.Width(300), GUILayout.Height(35)))
{
model.ApplyMaterialChanges();
}
}
GUILayout.Space(20);
}
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
}
/// <summary>
/// Localization table for the MaterialRemapperView class
/// </summary>
static class MaterialRemapperLocalization
{
// UI Labels
public const string menuItemLabel = "Window/Film-TV toolbox/Material Remapper";
public const string windowLabel = "Material Remapper";
public const string meshSelectLabel = "Select Scene objects to remap";
public const string meshRemapLabel = "Remap materials to object meshes";
public const string updateSceneSelection = "Update Scene Selection";
public const string resetRemapper = "Reset Remapper";
public const string objectsToRemap = "Objects to Remap";
public const string selectionDetails = "Selection Details:";
public const string meshCountInSelection = " Mesh Count in selection: ";
public const string uniqueMeshCount = " Unique Mesh Count: ";
public const string materialsLibrary = "Materials Library";
public const string addNewMaterialEntry = "Add new Material Entry";
public const string remapMesh = "Mesh to Remap";
public const string noMaterialToRemap = "Add some materials to remap";
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79d012f0d5645e547b2f0d88513f2145
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
{
"name": "Unity.Film-TV.Toolbox.Editor",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 30d31d6520f4144ea9d168c2c6468e0a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f035652d61b6f48d88f1fea020a72b2c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace UnityEditor.FilmTV.Toolbox
{
public static class PackageUtils
{
/// <summary>
/// Utils function to get the relative path of a file considering a base folder
/// </summary>
public static string GetRelativeFolderPath(string baseFolderPath, string filePath = "")
{
const string universalSeparator = @"/";
const string windowsSeparator = @"\";
// Conforming paths to ease next steps
filePath = filePath.Replace(windowsSeparator, universalSeparator);
baseFolderPath = baseFolderPath.Replace(windowsSeparator, universalSeparator);
// baseFolderPath must end with a slash to indicate folder
baseFolderPath = baseFolderPath.TrimEnd();
if (!baseFolderPath.EndsWith(universalSeparator))
{
baseFolderPath += universalSeparator;
}
// Using URIs to find the relative path
var sampleFolderAbsoluteURI = new Uri(Path.GetDirectoryName(filePath));
var projectFolderAbsoluteURI = new Uri(baseFolderPath);
var sampleFolderRelativePath =
Uri.UnescapeDataString(
projectFolderAbsoluteURI.MakeRelativeUri(sampleFolderAbsoluteURI).ToString()
.Replace('/', Path.DirectorySeparatorChar)
);
return sampleFolderRelativePath;
}
/// <summary>
/// Utils function to get the relative path of the current caller with current Unity directory as a base folder
/// </summary>
public static string GetCallerRelativeToProjectFolderPath([CallerFilePath] string filePath = "")
{
return GetRelativeFolderPath(Directory.GetCurrentDirectory(), filePath);
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2085f19c60b074b2582a726e6d01f52e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,5 @@
film-tv-toolbox copyright © 2018-2019 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3faaf5cb4781e440ebd864b86032a91d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,25 @@
Unity Film / TV toolbox
=========================
This repository is a collection of experimental, proof of concept or helper tools for Film / TV productions.
Prerequistes
---------------
* This has been tested for `>= 2018.3`
* Windows / OSX, not tested on Linux
Content
----------------
### Tools
* Material Remapper : allows to more easily assign material to models containing a lot of meshes
### Samples
* Simple Shotgun CSV Import / Export : This shows how to create a timeline in Unity using a shot sequence from a Shotgun CSV export and export back modifications.
Required dependencies
---------------
No dependencies are required.

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 96baefe566d2d47938274840ec9c8f1e
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2f49bff734c204932a31359c8f8d0849
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b040897da15654d3e8621fe1dc146b3c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,4 @@
{
"displayName" : "ShotgunCSVImportExport",
"description" : "This shows how to create a timeline in Unity using a shot sequence from a Shotgun CSV export and export back modifications."
}

Просмотреть файл

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEditor;
using UnityEditor.FilmTV.Toolbox;
public static class CsvImportExport
{
const int k_FPS = 24;
const string k_ImportFileName = "shot.csv";
const string k_ExportFileName = "export_from_unity.csv";
const string k_TimelineAssetName = "shotgun_imported_timeline.asset";
static string s_PathToFile = PackageUtils.GetCallerRelativeToProjectFolderPath();
const string k_TimelineObjectName = "Sample Shotgun CSV timeline";
[MenuItem("Window/Film-TV toolbox/Samples/Shotgun/Import CSV")]
public static void ImportCsv ()
{
var timeline = ScriptableObject.CreateInstance("TimelineAsset") as TimelineAsset;
timeline.name = k_TimelineObjectName;
timeline.editorSettings.fps = k_FPS;
var track = timeline.CreateTrack<ControlTrack>(null, "");
foreach (var row in ReadCsvFile(Path.Combine(s_PathToFile, k_ImportFileName)))
{
var clip = track.CreateClip<ControlPlayableAsset>();
var clipAsset = clip.asset as ControlPlayableAsset;
clipAsset.name = row["Shot Code"];
clip.displayName = row["Shot Code"];
clip.start = (Convert.ToDouble(row["Cut In"]) / k_FPS);
// In Unity, the last frame is exclusive. frames goes : [start; end[
clip.duration = (Convert.ToDouble(row["Cut Out"]) - Convert.ToDouble(row["Cut In"]) + 1 ) / k_FPS;
}
Debug.Log($"Loaded {k_ImportFileName} from {s_PathToFile}.");
AssetDatabase.CreateAsset(timeline, Path.Combine(s_PathToFile, k_TimelineAssetName ));
Debug.Log($"Created Timeline Asset in {s_PathToFile}.");
}
[MenuItem("Window/Film-TV toolbox/Samples/Shotgun/Export CSV")]
public static void ExportCsv()
{
// Get the actual timeline object
var timelineAsset = AssetDatabase.LoadAssetAtPath<TimelineAsset>(Path.Combine(s_PathToFile, k_TimelineAssetName));
// Each dictionary in the list represents a row in the CSV file
// Each value in the row is addressable by its column name
var toWrite = new List<Dictionary<string, string>>();
var track = timelineAsset.GetOutputTrack(0);
foreach (var clip in track.GetClips())
{
var start = Convert.ToInt64(clip.start * k_FPS);
// In Shotgun, the last frame is inclusive. frames goes : [start; end]
// remove the "padding" we added
var end = Convert.ToInt64((clip.duration + clip.start) * k_FPS) - 1;
var clipAsset = clip.asset as ControlPlayableAsset;
var shotCode = clipAsset.name;
var dict = new Dictionary<string, string>
{
{"Shot Code", shotCode },
{"Cut In", start.ToString() },
{"Cut Out", end.ToString() }
};
toWrite.Add(dict);
}
var pathToOutputFile = Path.Combine(s_PathToFile, k_ExportFileName);
Debug.Log($"Exported clip updates in {pathToOutputFile}");
WriteCsv(toWrite, pathToOutputFile);
}
static IEnumerable<Dictionary<string, string>> ReadCsvFile(string filePath)
{
var output = new List<Dictionary<string, string>>();
using (var reader = new StreamReader(filePath))
{
var line = reader.ReadLine();
if (line == null)
return output;
var fieldNamesList = line.Split(',');
// Trim the double quotes
fieldNamesList = fieldNamesList.Select(x => x.Replace("\"", "")).ToArray();
line = reader.ReadLine();
while (null != line)
{
var dict = new Dictionary<string, string>();
var values = line.Split(',');
// Trim the double quotes
values = values.Select(x => x.Replace("\"", "")).ToArray();
for (long idx = 0; idx < fieldNamesList.Length; idx++)
{
dict.Add(fieldNamesList[idx], values[idx]);
}
output.Add(dict);
line = reader.ReadLine();
}
}
return output;
}
static void WriteCsv (IReadOnlyList<Dictionary<string, string>> input, string filePath)
{
//Keys should be uniform
var keys = input[0].Keys;
using (var writer = new StreamWriter(filePath))
{
writer.WriteLine(string.Join("\t", keys));
foreach (var item in input)
{
writer.WriteLine(string.Join("\t", item.Values));
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57946af1ff629459eb082256509c76d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,26 @@
Shotgun CSV Import Export
--------------------------
This sample shows how to create a timeline in Unity using a shot sequence from a Shotgun CSV export and export back modifications.
### How to install
* Import the sample via Package Manager UI
* This will import in your Assets folder :
* CSVImportExport.cs : a sample script demonstrating how to simply import / export a Shotgun CSV file
* shot.csv : a shotgun csv export sample, used in the import.
### How to test
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Import CSV`
* This creates a Timeline Asset (see shotgun_imported_timeline in the sample folder) from the `shot.csv` sample file
* Make edits to the Timeline asset
* Go to `Window > Film-TV toolbox > Samples > Shotgun` and select `Export CSV`:
* See export_from_unity.csv in the sample folder
### Limitations
* Having commas in fields (e.g.: `Opening sequence, part 2`) will confuse the parser
* Having duplicate column names will cause errors due to duplicate keys

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 266585094e33f4ce490787b46930126f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,6 @@
"Id","Shot Code","Thumbnail","Status","Description","Sequence","Open Notes Count","Cut In","Cut Out","Project",
"1171","Seq01_Shot01","","ip","Overview","Seq01","6","0","130","Sample Project",
"1172","Seq01_Shot02","","wtg","Closing In","Seq01","2","131","262","Sample Project",
"1173","Seq01_Shot03","","wtg","Character Shown","Seq01","0","263","433","Sample Project",
"1174","Seq01_Shot04","","wtg","Character Runs","Seq01","1","434","597","Sample Project",
"1175","Seq01_Shot05","","wtg","Character Jumps","Seq01","0","598","729","Sample Project",
1 Id Shot Code Thumbnail Status Description Sequence Open Notes Count Cut In Cut Out Project
2 1171 Seq01_Shot01 ip Overview Seq01 6 0 130 Sample Project
3 1172 Seq01_Shot02 wtg Closing In Seq01 2 131 262 Sample Project
4 1173 Seq01_Shot03 wtg Character Shown Seq01 0 263 433 Sample Project
5 1174 Seq01_Shot04 wtg Character Runs Seq01 1 434 597 Sample Project
6 1175 Seq01_Shot05 wtg Character Jumps Seq01 0 598 729 Sample Project

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a256f5e1e58be426883e6afdeab395ec
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,9 @@
{
"name": "Unity.Film-TV.Toolbox.Samples",
"references": [
"Unity.Film-TV.Toolbox.Editor",
"Unity.Timeline"
],
"includePlatforms": [],
"excludePlatforms": []
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 274e5bcbf8c1742a4b6bfe2052956bb7
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 22c3b412315274363a443efa33fcab44
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,3 @@
{
"createSeparatePackage": true
}

Просмотреть файл

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2bf2e1267c5454f6d97de33b29f69e92
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,37 @@
using UnityEngine;
using NUnit.Framework;
using System.Linq;
using UnityEditor.FilmTV.Toolbox;
using UnityEditor.SceneManagement;
class MaterialRemapperTest
{
MaterialRemapperModel m_Model;
[Test, Description("Test GetMeshCount returns the created meshes count")]
public void TestMeshListCount() {
Assert.That(m_Model.GetMeshCount() == 3);
}
[Test, Description("Test GetUniqueMeshList returns a list which size corresponds to unique meshes count")]
public void TestUniqueMeshListCount() {
Assert.That(m_Model.GetUniqueMeshList().Count == 2);
}
[SetUp]
public void SetUp()
{
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
// Create two GOs from the same source mesh
GameObject.CreatePrimitive(PrimitiveType.Cube);
GameObject.CreatePrimitive(PrimitiveType.Cube);
// Create a GO from another mesh
GameObject.CreatePrimitive(PrimitiveType.Sphere);
m_Model = ScriptableObject.CreateInstance<MaterialRemapperModel>();
m_Model.hideFlags = HideFlags.DontSave;
var transforms = GameObject.FindObjectsOfType<MeshRenderer>().Select(X => X.transform);
m_Model.FindMeshesAndMaterials(transforms.ToList());
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 253abc0de9c6d4c3aa344523585daff9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,54 @@
using System.IO;
using UnityEngine;
using NUnit.Framework;
using UnityEditor.FilmTV.Toolbox;
using UnityEngine.TestTools;
class PackageUtilsTest
{
[Test, Description("Test getting a relative path from a base with no trailing slash")]
[UnityPlatform(exclude = new[] {RuntimePlatform.WindowsEditor})] // URIs require a <drive>:\\ on Windows, see UriFormatException
public void TestRelativeFolderPathNoTrailingSlash()
{
var res = PackageUtils.GetRelativeFolderPath("/path/to/folder", "/path/to/folder/subfolder/file.ext");
Assert.That(res == "subfolder");
}
[Test, Description("Test getting a relative Windows path from a base with no trailing slash")]
public void TestRelativeFolderPathNoTrailingSlashWindows()
{
var res = PackageUtils.GetRelativeFolderPath("C:\\path\\to\\folder", "C:\\path\\to\\folder\\subfolder\\file.ext");
Assert.That(res == "subfolder");
}
[Test, Description("Test getting a relative path from a base with a trailing slash")]
[UnityPlatform(exclude = new[] {RuntimePlatform.WindowsEditor})] // URIs require a <drive>:\\ on Windows, see UriFormatException
public void TestRelativeFolderPathTrailingSlash()
{
var res = PackageUtils.GetRelativeFolderPath("/path/to/folder/", "/path/to/folder/subfolder/file.ext");
Assert.That(res == "subfolder");
}
[Test, Description("Test getting a relative Windows path from a base with a trailing slash")]
public void TestRelativeFolderPathTrailingSlashWindows()
{
var res = PackageUtils.GetRelativeFolderPath("C:\\path\\to\\folder\\", "C:\\path\\to\\folder\\subfolder\\file.ext");
Assert.That(res == "subfolder");
}
[Test, Description("Test Unrelated paths")]
[UnityPlatform(exclude = new[] {RuntimePlatform.WindowsEditor})] // URIs require a <drive>:\\ on Windows, see UriFormatException
public void TestUnrelatedPaths()
{
var res = PackageUtils.GetRelativeFolderPath("/another/path/to/folder/", "/path/to/folder/subfolder/file.ext");
Assert.That(res == "../../../../path/to/folder/subfolder");
}
[Test, Description("Test Unrelated paths on Windows")]
public void TestUnrelatedPathsWindows()
{
const string folderPath = "D:\\path\\to\\folder\\subfolder";
var res = PackageUtils.GetRelativeFolderPath("C:\\path\\to\\folder\\", folderPath + "\\file.ext");
Assert.That(res == folderPath.Replace('\\', Path.DirectorySeparatorChar));
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55ebf65f6978a4eeda8100cf270a4fd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,13 @@
{
"name": "Unity.Film-TV.Toolbox.EditorTests",
"references": [
"Unity.Film-TV.Toolbox.Editor"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 390d2ccf3930e4bb8b96baf024f9cecd
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,10 @@
{
"name": "com.unity.film-tv.toolbox",
"displayName":"Film and TV Toolbox",
"version": "0.1.0-preview.0",
"unity": "2018.3",
"description": "This package is a collection of experimental, proof of concept or helper tools for Film / TV productions.",
"keywords": ["film", "TV", "Tools", "Samples"],
"dependencies": {
}
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fea8078c0aae24ff89b99a182a1985db
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: