Added player character and step in/out of dialogue in scene 101

This commit is contained in:
2026-08-15 15:22:47 +02:00
parent a42f25d5fa
commit 38ba2b0eff
2019 changed files with 181227 additions and 265 deletions
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6e6cb02037fc4e95952dd035e688eb3d
folderAsset: yes
timeCreated: 1457452380
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,70 @@
using System;
using UnityEngine;
using UnityEngine.ProBuilder;
using ToolManager = UnityEditor.EditorTools.ToolManager;
namespace UnityEditor.ProBuilder.Actions
{
/// <summary>
/// Represents the [Cut tool](../manual/cut-tool.html) button on the [ProBuilder toolbar](../manual/toolbar.html) in the Editor.
/// </summary>
public class CutToolAction : MenuAction
{
/// <inheritdoc/>
public override ToolbarGroup group
{
get { return ToolbarGroup.Tool; }
}
/// <inheritdoc/>
public override string iconPath => "Toolbar/CutTool";
/// <inheritdoc/>
public override Texture2D icon => IconUtility.GetIcon(iconPath);
/// <inheritdoc/>
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
/// <inheritdoc/>
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
}
/// <inheritdoc/>
protected internal override bool hasFileMenuEntry
{
get { return false; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Cut Tool",
@"Create a cut in a face to subdivide it.",
keyCommandAlt, keyCommandShift, 'C'
);
/// <inheritdoc/>
public override bool enabled
{
get => MeshSelection.selectedObjectCount == 1;
}
/// <inheritdoc/>
protected override ActionResult PerformActionImplementation()
{
if(ToolManager.activeToolType == typeof(CutTool))
ToolManager.RestorePreviousTool();
else
ToolManager.SetActiveTool<CutTool>();
//Give the focus back to scene view to handle key inputs directly
SceneView.lastActiveSceneView.Focus();
return new ActionResult(ActionResult.Status.Success,"Cut Tool Starts");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3033ae68453f7e54fa868c2610c9bc7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,49 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class NewBezierShape : MenuAction
{
const string k_IconPath = "Toolbar/NewBezierSpline";
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => k_IconPath;
public override Texture2D icon => IconUtility.GetIcon(k_IconPath);
public override TooltipContent tooltip { get { return _tooltip; } }
public override string menuTitle { get { return "New Bezier Shape"; } }
public override int toolbarPriority { get { return 1; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"New Bezier Shape",
"Creates a new shape that is built by extruding along a bezier spline."
);
public override bool hidden
{
get { return !Experimental.experimentalFeaturesEnabled; }
}
public override bool enabled
{
get { return Experimental.experimentalFeaturesEnabled; }
}
protected override ActionResult PerformActionImplementation()
{
GameObject go = new GameObject();
var bezier = go.AddComponent<BezierShape>();
go.GetComponent<MeshRenderer>().sharedMaterial = EditorMaterialUtility.GetUserMaterial();
bezier.Init();
bezier.Refresh();
UndoUtility.RegisterCreatedObjectUndo(go, "Create Bezier Shape");
EditorUtility.InitObject(bezier.GetComponent<ProBuilderMesh>());
MeshSelection.SetSelection(go);
bezier.isEditing = true;
return new ActionResult(ActionResult.Status.Success, "Create Bezier Shape");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ed8e5003f1d14f718326765fbb042572
timeCreated: 1487278078
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,36 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenLightmapUVEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return s_Tooltip; } }
public override string menuTitle { get { return "Lightmap UV Editor"; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Lightmap UV Editor",
""
);
public override bool enabled
{
get { return true; }
}
public override bool hidden
{
get { return true; }
}
protected override ActionResult PerformActionImplementation()
{
EditorWindow.GetWindow<LightmapUVEditor>(true, "Lightmap UV Editor", true).position = LightmapUVEditor.desiredPosition;
return new ActionResult(ActionResult.Status.Success, "Open Lightmap UV Editor Window");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c851f089cbc73c141be8cc62b5dc057a
timeCreated: 1474046557
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenMaterialEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => "Toolbar/Panel_Materials";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Material Editor",
"Opens the Material Editor window.\n\nThe Material Editor window applies materials to selected faces or objects."
);
public override bool enabled => true;
protected override ActionResult PerformActionImplementation()
{
MaterialEditor.MenuOpenMaterialEditor();
return new ActionResult(ActionResult.Status.Success, "Open Materials Window");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2b43eb0a521a4a6a90bde45149e1c656
timeCreated: 1457452380
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenSmoothingEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => "Toolbar/Panel_Smoothing";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
public override string menuTitle { get { return "Smoothing"; } }
public override int toolbarPriority { get { return 2; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Smoothing Groups Editor",
@"Opens the Smoothing Group Editor.
Smoothing groups average the vertex normals with neighboring planes. This allows lighting to behave in a more realistic manner when dealing with edges that are intended to be smooth.
ProBuilder decides which edges should be smoothed by checking for neighboring faces that are in the same group. It also checks for Hard groups, which hardens edges of neighboring faces."
);
public override bool enabled => true;
protected override ActionResult PerformActionImplementation()
{
SmoothGroupEditor.MenuOpenSmoothGroupEditor();
return new ActionResult(ActionResult.Status.Success, "Open Smoothing Groups Editor");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 40af14f295f2434f9cfda219da2645a0
timeCreated: 1457452380
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenUVEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => "Toolbar/Panel_UVEditor";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"UV Editor",
"Opens the UV Editor window.\n\nThe UV Editor allows you to change how textures are rendered on this mesh."
);
public override bool enabled => true;
protected override ActionResult PerformActionImplementation()
{
UVEditor.MenuOpenUVEditor();
return new ActionResult(ActionResult.Status.Success, "Open UV Window");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e985069a91a74567a338b55beddbf74c
timeCreated: 1457452380
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenVertexColorEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => "Toolbar/Panel_VertColors";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
public override string menuTitle { get { return "Vertex Colors"; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Vertex Colors Editor",
"Opens the Vertex Color Palette.\n\nApply using Face mode for hard-edged colors.\nApply using Edge or Vertex mode for soft, blended colors."
);
public override bool enabled => true;
protected override ActionResult PerformActionImplementation()
{
VertexColorPalette.MenuOpenWindow();
return new ActionResult(ActionResult.Status.Success, "Open Vertex Color Window");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c8e7f2a60420444ba31322510285d625
timeCreated: 1457452380
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OpenVertexPositionEditor : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Tool; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return s_Tooltip; } }
public override string menuTitle { get { return "Vertex Editor"; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Vertex Position Editor",
"Opens the vertex positions editor window."
);
public override bool enabled => true;
public override bool hidden => true;
protected override ActionResult PerformActionImplementation()
{
VertexPositionEditor.MenuOpenVertexEditor();
return new ActionResult(ActionResult.Status.Success, "Open Vertex Editor Window");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0af7522e3732400388ebc019d869caf2
timeCreated: 1474046557
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 27d2963a2a5b4b0884f376976f4f34eb
folderAsset: yes
timeCreated: 1472831757
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,238 @@
using System;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
using UnityEngine.ProBuilder.Stl;
using UnityEngine.ProBuilder;
using Object = UnityEngine.Object;
namespace UnityEditor.ProBuilder.Actions
{
sealed class Export : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Object; } }
public override string iconPath => "Toolbar/Object_Export";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return m_Tooltip; } }
protected override internal bool hasFileMenuEntry { get { return false; } }
GUIContent gc_ExportFormat = new GUIContent("Export Format", "The type of file to export the current selection as.");
GUIContent gc_ExportRecursive = new GUIContent("Include Children", "Should the exporter include children of the current selection when searching for meshes to export?");
GUIContent gc_ObjExportRightHanded = new GUIContent("Right Handed", "Unity coordinate space is left handed, where most other major 3D modeling softwares are right handed. Usually this option should be left enabled.");
GUIContent gc_ExportAsGroup = new GUIContent("Export As Group", "If enabled all selected meshes will be combined to a single model. If not, each mesh will be exported individually.");
GUIContent gc_ObjApplyTransform = new GUIContent("Apply Transforms", "If enabled each mesh will have it's Transform applied prior to export. This is useful when you want to retain the correct placement of objects when re-importing to Unity (just set the imported mesh to { 0, 0, 0 }). If not enabled meshes are exported in local space.");
GUIContent gc_ObjExportCopyTextures = new GUIContent("Copy Textures", "With Copy Textures enabled the exporter will copy material textures to the destination directory. If false the material library will point to the texture path within the Unity project. If you're exporting models with the intention of editing in an external 3D modeler then re-importing, disable this option to avoid duplicate textures in your project.");
GUIContent gc_ObjExportVertexColors = new GUIContent("Vertex Colors", "Some 3D modeling applications will read and write vertex colors as an unofficial extension to the OBJ format.\n\nWarning! Enabling this can break compatibility with some other 3D modeling applications.");
GUIContent gc_ObjTextureOffsetScale = new GUIContent("Texture Offset, Scale", "Write texture map offset and scale to the material library. Not all 3D modeling applications support this specificiation, and some will fail to load materials that define these values.");
GUIContent gc_ObjQuads = new GUIContent("Export Quads", "Where possible, faces will be exported as quads instead of triangles. Note that this can result in a larger exported mesh (ProBuilder will not merge shared vertices with this option enabled).");
GUIContent gc_ExportAssetInPlace = new GUIContent("Replace Source", "Remove the ProBuilder component and replace the MeshFilter mesh with the exported asset.");
Pref<ExportFormat> m_ExportFormat = new Pref<ExportFormat>("export.format", k_DefaultFormat);
Pref<bool> m_ExportRecursive = new Pref<bool>("export.exportRecursive", false);
Pref<bool> m_ExportAsGroup = new Pref<bool>("export.exportAsGroup", false);
// obj specific
Pref<bool> m_ObjExportRightHanded = new Pref<bool>("export.objExportRightHanded", true);
Pref<bool> m_ObjExportCopyTextures = new Pref<bool>("export.objExportCopyTextures", true);
Pref<bool> m_ObjApplyTransform = new Pref<bool>("export.objApplyTransform", false);
Pref<bool> m_ObjExportVertexColors = new Pref<bool>("export.objExportVertexColors", false);
Pref<bool> m_ObjTextureOffsetScale = new Pref<bool>("export.objTextureOffsetScale", false);
Pref<bool> m_ObjQuads = new Pref<bool>("export.objQuads", true);
// stl specific
Pref<FileType> m_StlExportFormat = new Pref<FileType>("export.stlExportFormat", FileType.Ascii);
// ply specific
Pref<bool> m_PlyExportIsRightHanded = new Pref<bool>("export.plyExportIsRightHanded", true);
Pref<bool> m_PlyApplyTransform = new Pref<bool>("export.plyApplyTransform", true);
Pref<bool> m_PlyQuads = new Pref<bool>("export.plyQuads", true);
Pref<bool> m_PlyNGons = new Pref<bool>("export.plyNGons", false);
public enum ExportFormat
{
Obj,
Stl,
Ply,
Asset,
Prefab
}
const ExportFormat k_DefaultFormat = ExportFormat.Obj;
static readonly TooltipContent m_Tooltip = new TooltipContent
(
"Export",
"Export the selected ProBuilder objects as a model file."
);
public override bool hidden
{
get { return false; }
}
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Export Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUI.BeginChangeCheck();
m_ExportFormat.value = (ExportFormat)EditorGUILayout.EnumPopup(gc_ExportFormat, m_ExportFormat);
if (EditorGUI.EndChangeCheck())
{
if (m_ExportFormat.value == ExportFormat.Asset || m_ExportFormat.value == ExportFormat.Prefab)
{
var opt = ExportAsset.s_ExportAssetOptions.value;
opt.makePrefab = m_ExportFormat.value == ExportFormat.Prefab;
ExportAsset.s_ExportAssetOptions.SetValue(opt);
}
}
m_ExportRecursive.value = EditorGUILayout.Toggle(gc_ExportRecursive, m_ExportRecursive);
if (m_ExportFormat != ExportFormat.Asset
&& m_ExportFormat != ExportFormat.Prefab
&& m_ExportFormat != ExportFormat.Stl)
{
m_ExportAsGroup.value = EditorGUILayout.Toggle(gc_ExportAsGroup, m_ExportAsGroup);
}
if (m_ExportFormat == ExportFormat.Obj)
ObjExportOptions();
else if (m_ExportFormat == ExportFormat.Stl)
StlExportOptions();
else if (m_ExportFormat == ExportFormat.Ply)
PlyExportOptions();
else if (m_ExportFormat == ExportFormat.Asset || m_ExportFormat == ExportFormat.Prefab)
AssetExportOptions();
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Export"))
EditorUtility.ShowNotification(PerformAction().notification);
}
void ObjExportOptions()
{
EditorGUI.BeginDisabledGroup(m_ExportAsGroup);
if (m_ExportAsGroup)
EditorGUILayout.Toggle("Apply Transforms", true);
else
m_ObjApplyTransform.value = EditorGUILayout.Toggle(gc_ObjApplyTransform, m_ObjApplyTransform);
EditorGUI.EndDisabledGroup();
m_ObjExportRightHanded.value = EditorGUILayout.Toggle(gc_ObjExportRightHanded, m_ObjExportRightHanded);
m_ObjExportCopyTextures.value = EditorGUILayout.Toggle(gc_ObjExportCopyTextures, m_ObjExportCopyTextures);
m_ObjExportVertexColors.value = EditorGUILayout.Toggle(gc_ObjExportVertexColors, m_ObjExportVertexColors);
m_ObjTextureOffsetScale.value = EditorGUILayout.Toggle(gc_ObjTextureOffsetScale, m_ObjTextureOffsetScale);
m_ObjQuads.value = EditorGUILayout.Toggle(gc_ObjQuads, m_ObjQuads);
}
void StlExportOptions()
{
m_StlExportFormat.value = (UnityEngine.ProBuilder.Stl.FileType)EditorGUILayout.EnumPopup("Stl Format", m_StlExportFormat);
}
void PlyExportOptions()
{
EditorGUI.BeginDisabledGroup(m_ExportAsGroup);
if (m_ExportAsGroup)
EditorGUILayout.Toggle("Apply Transforms", true);
else
m_PlyApplyTransform.value = EditorGUILayout.Toggle(gc_ObjApplyTransform, m_PlyApplyTransform);
EditorGUI.EndDisabledGroup();
m_PlyExportIsRightHanded.value = EditorGUILayout.Toggle("Right Handed", m_PlyExportIsRightHanded);
m_PlyQuads.value = EditorGUILayout.Toggle("Quads", m_PlyQuads);
}
void AssetExportOptions()
{
var options = ExportAsset.s_ExportAssetOptions.value;
EditorGUI.BeginChangeCheck();
options.replaceOriginal = EditorGUILayout.Toggle(gc_ExportAssetInPlace, options.replaceOriginal);
if(EditorGUI.EndChangeCheck())
ExportAsset.s_ExportAssetOptions.SetValue(options);
}
protected override ActionResult PerformActionImplementation()
{
string res = null;
List<ProBuilderMesh> meshes = m_ExportRecursive ? MeshSelection.deep.ToList() : MeshSelection.topInternal;
if (meshes == null || meshes.Count == 0)
return new ActionResult(ActionResult.Status.Canceled, "No ProBuilder Mesh");
if (m_ExportFormat == ExportFormat.Obj)
{
res = ExportObj.ExportWithFileDialog(meshes,
m_ExportAsGroup,
m_ObjQuads,
new ObjOptions() {
handedness = m_ObjExportRightHanded ? ObjOptions.Handedness.Right : ObjOptions.Handedness.Left,
copyTextures = m_ObjExportCopyTextures,
applyTransforms = m_ExportAsGroup || m_ObjApplyTransform.value,
vertexColors = m_ObjExportVertexColors,
textureOffsetScale = m_ObjTextureOffsetScale
});
}
else if (m_ExportFormat == ExportFormat.Stl)
{
res = ExportStlAscii.ExportWithFileDialog(meshes.Select(x => x.gameObject).ToArray(), m_StlExportFormat);
}
else if (m_ExportFormat == ExportFormat.Ply)
{
res = ExportPly.ExportWithFileDialog(meshes, m_ExportAsGroup, new PlyOptions() {
isRightHanded = m_PlyExportIsRightHanded,
applyTransforms = m_PlyApplyTransform,
quads = m_PlyQuads,
ngons = m_PlyNGons
});
}
else if (m_ExportFormat == ExportFormat.Asset || m_ExportFormat == ExportFormat.Prefab)
{
res = ExportAsset.ExportWithFileDialog(meshes, ExportAsset.s_ExportAssetOptions.value);
}
if (string.IsNullOrEmpty(res))
return new ActionResult(ActionResult.Status.Canceled, "Canceled");
PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, "Exported " + m_ExportFormat.value);
}
internal static void PingExportedModel(string path)
{
var local = path.Replace("\\", "/");
var dataPath = Application.dataPath.Replace("\\", "/");
if (local.Contains(dataPath))
{
AssetDatabase.Refresh();
var assetPath = "Assets" + local.Replace(dataPath, "");
var o = AssetDatabase.LoadAssetAtPath<Object>(assetPath);
if (o != null)
EditorGUIUtility.PingObject(o);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 736319124efd40fda31c36ae2599181a
timeCreated: 1496775583
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,319 @@
using System;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.Shapes;
using Object = UnityEngine.Object;
namespace UnityEditor.ProBuilder.Actions
{
[Serializable]
struct ExportAssetOptions
{
public bool makePrefab;
public bool replaceOriginal;
public static readonly ExportAssetOptions defaults = new ExportAssetOptions()
{
makePrefab = false,
replaceOriginal = false
};
}
sealed class ExportAsset : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Export; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return s_Tooltip; } }
internal static Pref<ExportAssetOptions> s_ExportAssetOptions =new Pref<ExportAssetOptions>("export.assetOptions", ExportAssetOptions.defaults);
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Export Asset",
"Export a Unity mesh asset file."
);
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
public override bool hidden
{
get { return true; }
}
protected override ActionResult PerformActionImplementation()
{
var opt = s_ExportAssetOptions.value;
var res = ExportWithFileDialog(MeshSelection.topInternal, opt);
Export.PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, opt.makePrefab ? "Make Prefab" : "Make Asset");
}
/// <summary>
/// Export meshes to a Unity asset.
/// </summary>
/// <param name="meshes"></param>
/// <returns></returns>
public static string ExportWithFileDialog(IList<ProBuilderMesh> meshes, ExportAssetOptions options)
{
if (meshes == null || meshes.Count == 0)
return "";
string res = null;
if (meshes.Count < 2)
{
ProBuilderMesh first = meshes.FirstOrDefault();
if (first == null)
return null;
res = options.makePrefab
? ExportPrefab(first, options.replaceOriginal)
: ExportMesh(first, options.replaceOriginal);
}
else
{
string path = UnityEditor.EditorUtility.SaveFolderPanel("Export to Asset", "Assets", "");
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
return null;
meshes = meshes.ToList();
for (int i = 0, c = meshes.Count; i < c; i++)
{
var pb = meshes[i];
var assetPath = string.Format("{0}/{1}.asset", path, pb.name);
if (options.makePrefab)
{
res = ExportPrefab(assetPath, pb, options.replaceOriginal, true);
}
else
{
res = ExportMesh(assetPath, pb, options.replaceOriginal, true);
if (options.replaceOriginal)
StripProBuilderScripts.DestroyProBuilderMeshAndDependencies(pb.gameObject, pb, true, true);
}
}
}
ProBuilderEditor.Refresh();
AssetDatabase.Refresh();
return res;
}
static string AssetPathFromFullPath(string path, string extension)
{
if (string.IsNullOrEmpty(path)) return null;
string directory = Path.GetDirectoryName(path).Replace("\\", "/");
string name = Path.GetFileNameWithoutExtension(path);
string relativeDirectory = string.Format("Assets{0}", directory.Replace(Application.dataPath, ""));
return !string.IsNullOrEmpty(extension)
? string.Format("{0}/{1}.{2}", relativeDirectory, name, extension)
: string.Format("{0}/{1}", relativeDirectory, name);
}
static string AskForPath(string fileName, string extension)
{
return UnityEditor.EditorUtility.SaveFilePanel("Export to Asset", "Assets", fileName, extension);
}
static string AskForAssetPath(string fileName, string extension)
{
return AssetPathFromFullPath(AskForPath(fileName, extension), extension);
}
static string PickDifferentAssetPath(string previousAssetPath)
{
var extension = Path.GetExtension(previousAssetPath);
if (extension != null && extension.StartsWith("."))
{
extension = extension.Substring(1);
}
var newPath = AskForAssetPath(
Path.GetFileNameWithoutExtension(previousAssetPath),
extension
);
return !string.IsNullOrEmpty(newPath)
? newPath
: AssetDatabase.GenerateUniqueAssetPath(previousAssetPath);
}
static string ExportPrefab(ProBuilderMesh mesh, bool replaceOriginal)
{
string path = AskForPath(mesh.name, "prefab");
if (string.IsNullOrEmpty(path))
return null;
return ExportPrefab(path, mesh, replaceOriginal, false);
}
static string ExportMesh(ProBuilderMesh mesh, bool replaceOriginal)
{
string path = AskForPath(mesh.name, "asset");
if (string.IsNullOrEmpty(path))
return null;
ExportMesh(path, mesh, replaceOriginal, false);
if (replaceOriginal)
{
StripProBuilderScripts.DestroyProBuilderMeshAndDependencies(mesh.gameObject, mesh, true, true);
}
else
{
mesh.mesh = null;
EditorUtility.SynchronizeWithMeshFilter(mesh);
}
return path;
}
static string ExportPrefab(string path, ProBuilderMesh pb, bool replaceOriginal, bool batchExport)
{
string name = Path.GetFileNameWithoutExtension(path);
string basePath = AssetPathFromFullPath(path, null);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
string meshPath = string.Format("{0}.asset", basePath);
string prefabPath = string.Format("{0}.prefab", basePath);
if (batchExport)
{
// Never overwrite during batch export.
meshPath = AssetDatabase.GenerateUniqueAssetPath(meshPath);
prefabPath = AssetDatabase.GenerateUniqueAssetPath(prefabPath);
}
else
{
var existingMesh = AssetDatabase.LoadAssetAtPath<Mesh>(meshPath);
if (existingMesh)
{
// Overwriting mesh.
var existingPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (existingPrefab)
{
// Overwriting prefab as well.
var meshFilter = existingPrefab.GetComponent<MeshFilter>();
if (!meshFilter || meshFilter.sharedMesh != existingMesh)
{
// Prefab and mesh being overwritten are not related, pick different path.
meshPath = PickDifferentAssetPath(meshPath);
}
// Else allow overwrite both as they are related.
}
else
{
// Unrelated mesh is being overriden, pick different path.
meshPath = PickDifferentAssetPath(meshPath);
}
}
}
Mesh meshAsset = pb.mesh;
meshAsset.name = name;
meshAsset = CreateOrReplaceAsset(meshAsset, meshPath);
var go = replaceOriginal ? pb.gameObject : Object.Instantiate(pb.gameObject);
var component = go.GetComponent<ProBuilderMesh>();
Undo.RecordObject(component, "Export ProBuilderMesh as Replacement");
StripProBuilderScripts.DestroyProBuilderMeshAndDependencies(go, component, true, true);
go.GetComponent<MeshFilter>().sharedMesh = meshAsset;
var meshCollider = go.GetComponent<MeshCollider>();
if (meshCollider)
meshCollider.sharedMesh = meshAsset;
if (replaceOriginal)
PrefabUtility.SaveAsPrefabAssetAndConnect(go, prefabPath, InteractionMode.UserAction);
else
{
// if we're about to overwrite the prefab asset of the source mesh, first disconnect it so that we're not
// overwriting the instance
if (PrefabUtility.IsPartOfAnyPrefab(pb)
&& PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(pb) == prefabPath)
PrefabUtility.UnpackPrefabInstance(pb.gameObject, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
PrefabUtility.SaveAsPrefabAsset(go, prefabPath);
}
if (!replaceOriginal)
{
pb.mesh = null;
EditorUtility.SynchronizeWithMeshFilter(pb);
Object.DestroyImmediate(go);
}
return meshPath;
}
static string ExportMesh(string path, ProBuilderMesh mesh, bool replaceOriginal, bool batchExport)
{
var existing = AssetDatabase.GetAssetPath(mesh.mesh);
if (!string.IsNullOrEmpty(existing))
return existing;
string name = Path.GetFileNameWithoutExtension(path);
string meshPath = AssetPathFromFullPath(path, "asset");
if (batchExport)
{
// Never replace assets during batch export.
meshPath = AssetDatabase.GenerateUniqueAssetPath(meshPath);
}
mesh.ToMesh();
mesh.Refresh();
mesh.Optimize();
var meshAsset = mesh.mesh;
meshAsset.name = name;
meshAsset = CreateOrReplaceAsset(meshAsset, meshPath);
if (replaceOriginal)
{
mesh.GetComponent<MeshFilter>().sharedMesh = meshAsset;
var meshCollider = mesh.GetComponent<MeshCollider>();
if (meshCollider)
{
meshCollider.sharedMesh = meshAsset;
}
}
return meshPath;
}
static T CreateOrReplaceAsset<T>(T asset, string path) where T : Object
{
T existingAsset = AssetDatabase.LoadAssetAtPath<T>(path);
if (existingAsset == null)
{
AssetDatabase.CreateAsset(asset, path);
return asset;
}
var tempPath = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CreateAsset(asset, tempPath);
FileUtil.ReplaceFile(tempPath, path);
AssetDatabase.DeleteAsset(tempPath);
return existingAsset;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cc401b52b64c4b7b87078d8ae3e29b68
timeCreated: 1472836657
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,132 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExportObj : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Export; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return _tooltip; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Export Obj",
"Export a Wavefront OBJ file."
);
public override bool hidden
{
get { return true; }
}
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
string res = ExportWithFileDialog(MeshSelection.topInternal);
if (string.IsNullOrEmpty(res))
return new ActionResult(ActionResult.Status.Canceled, "User Canceled");
Export.PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, "Export OBJ");
}
// Prompt user for a save file location and export meshes as Obj.
public static string ExportWithFileDialog(IEnumerable<ProBuilderMesh> meshes, bool asGroup = true, bool allowQuads = true, ObjOptions options = null)
{
if (meshes == null || !meshes.Any())
return null;
IEnumerable<Model> models = allowQuads
? meshes.Select(x => new Model(x.gameObject.name, x))
: meshes.Select(x => new Model(x.gameObject.name, x.mesh, x.GetComponent<MeshRenderer>().sharedMaterials, x.transform.localToWorldMatrix));
string path = null, res = null;
if (asGroup || models.Count() < 2)
{
ProBuilderMesh first = meshes.FirstOrDefault();
string name = first != null ? first.name : "ProBuilderModel";
path = UnityEditor.EditorUtility.SaveFilePanel("Export to Obj", "Assets", name, "obj");
if (string.IsNullOrEmpty(path))
return null;
res = DoExport(path, models, options);
}
else
{
path = UnityEditor.EditorUtility.SaveFolderPanel("Export to Obj", "Assets", "");
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
return null;
foreach (Model model in models)
res = DoExport(string.Format("{0}/{1}.obj", path, model.name), new List<Model>() { model }, options);
}
return res;
}
internal static string DoExport(string path, IEnumerable<Model> models, ObjOptions options)
{
string name = Path.GetFileNameWithoutExtension(path);
string directory = Path.GetDirectoryName(path);
List<string> textures = null;
string obj, mat;
if (ObjExporter.Export(name, models, out obj, out mat, out textures, options))
{
try
{
FileUtility.WriteAllText(string.Format("{0}/{1}.obj", directory, name), obj);
FileUtility.WriteAllText(string.Format("{0}/{1}.mtl", directory, name.Replace(" ", "_")), mat);
CopyTextures(textures, directory);
}
catch (System.Exception e)
{
Debug.LogWarning(string.Format("Failed writing obj to path: {0}\n{1}", string.Format("{0}/{1}.obj", path, name), e.ToString()));
return null;
}
}
else
{
Debug.LogWarning("No meshes selected.");
return null;
}
return path;
}
// Copy files from their path to a destination directory.
static void CopyTextures(List<string> textures, string destination)
{
foreach (string path in textures)
{
string dest = string.Format("{0}/{1}", destination, Path.GetFileName(path));
if (!File.Exists(path))
{
Log.Warning("OBJ Export: Could not find texture \"" + path + ",\" it will not be copied.");
continue;
}
if (!File.Exists(dest))
File.Copy(path, dest);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23fb6d4a102f4c2188ef8afe61eb58e4
timeCreated: 1472833007
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,112 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using UnityEngine.ProBuilder.Stl;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using EditorUtility = UnityEditor.EditorUtility;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExportPly : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Export; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return _tooltip; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Export Ply",
"Export a Stanford PLY file."
);
public override bool hidden
{
get { return true; }
}
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
string res = ExportWithFileDialog(MeshSelection.topInternal);
if (string.IsNullOrEmpty(res))
return new ActionResult(ActionResult.Status.Canceled, "User Canceled");
Export.PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, "Export PLY");
}
/**
* Prompt user for a save file location and export meshes as Obj.
*/
public static string ExportWithFileDialog(IEnumerable<ProBuilderMesh> meshes, bool asGroup = true, PlyOptions options = null)
{
if (meshes == null || meshes.Count() < 1)
return null;
string path = null, res = null;
if (asGroup || meshes.Count() < 2)
{
ProBuilderMesh first = meshes.FirstOrDefault();
string name = first != null ? first.name : "ProBuilderModel";
path = UnityEditor.EditorUtility.SaveFilePanel("Export as PLY", "Assets", name, "ply");
if (string.IsNullOrEmpty(path))
return null;
res = DoExport(path, meshes, options);
}
else
{
path = UnityEditor.EditorUtility.SaveFolderPanel("Export to PLY", "Assets", "");
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
return null;
foreach (ProBuilderMesh model in meshes)
res = DoExport(string.Format("{0}/{1}.ply", path, model.name), new List<ProBuilderMesh>() { model }, options);
}
return res;
}
static string DoExport(string path, IEnumerable<ProBuilderMesh> models, PlyOptions options)
{
string name = Path.GetFileNameWithoutExtension(path);
string directory = Path.GetDirectoryName(path);
string ply;
if (PlyExporter.Export(models, out ply, options))
{
try
{
FileUtility.WriteAllText(string.Format("{0}/{1}.ply", directory, name), ply);
}
catch (System.Exception e)
{
Debug.LogWarning(string.Format("Failed writing PLY to path: {0}\n{1}", string.Format("{0}/{1}.ply", path, name), e.ToString()));
return null;
}
}
else
{
Debug.LogWarning("No meshes selected.");
return null;
}
return path;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ac7a7f49f26f45b29857e71b0d156fff
timeCreated: 1497030443
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,91 @@
using System;
using System.Globalization;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Threading;
using UnityEngine.ProBuilder.Stl;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using EditorUtility = UnityEditor.EditorUtility;
using Object = UnityEngine.Object;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExportStlAscii : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Export; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return _tooltip; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Export Stl",
@"Export an Stl model file."
);
public override bool hidden
{
get { return true; }
}
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
var res = ExportWithFileDialog(Selection.gameObjects, FileType.Ascii);
if (string.IsNullOrEmpty(res))
return new ActionResult(ActionResult.Status.Canceled, "User Canceled");
Export.PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, "Export STL");
}
public static string ExportWithFileDialog(GameObject[] gameObjects, FileType type)
{
GameObject first = gameObjects.FirstOrDefault(x => x.GetComponent<ProBuilderMesh>() != null);
string name = first != null ? first.name : "Mesh";
string path = UnityEditor.EditorUtility.SaveFilePanel("Save Mesh to STL", "", name, "stl");
var res = false;
var currentCulture = Thread.CurrentThread.CurrentCulture;
try
{
// pb_Stl is an external lib
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
res = pb_Stl_Exporter.Export(path, gameObjects, type);
}
finally
{
Thread.CurrentThread.CurrentCulture = currentCulture;
}
if (res)
{
string full = path.Replace("\\", "/");
// if the file was saved in project, ping it
if (full.Contains(Application.dataPath))
{
string relative = full.Replace(Application.dataPath, "Assets");
Object o = AssetDatabase.LoadAssetAtPath(relative, typeof(Object));
if (o != null)
EditorGUIUtility.PingObject(o);
AssetDatabase.Refresh();
}
return path;
}
return null;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 48b3897a5481441a8c9468e28fd54c53
timeCreated: 1472831854
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder.Stl;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExportStlBinary : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Export; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return _tooltip; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Export Stl",
@"Export an Stl model file."
);
public override bool hidden
{
get { return true; }
}
public override bool enabled
{
get { return MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
var res = ExportStlAscii.ExportWithFileDialog(Selection.gameObjects, FileType.Binary);
if (string.IsNullOrEmpty(res))
return new ActionResult(ActionResult.Status.Canceled, "User Canceled");
Export.PingExportedModel(res);
return new ActionResult(ActionResult.Status.Success, "Export STL");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b04279a9fe7348798c260ea6516c1a4c
timeCreated: 1472832591
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8dd93277e78748079ee5467a543c0416
folderAsset: yes
timeCreated: 1457564631
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,126 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UIElements;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class BevelEdges : MenuAction
{
const float k_MinBevelDistance = .0001f;
Pref<float> m_BevelSize = new Pref<float>("BevelEdges.size", .2f);
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Edge_Bevel";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
static readonly GUIContent gc_BevelDistance = EditorGUIUtility.TrTextContent("Distance", "The size of the bevel in meters. The value is clamped to the size of the smallest affected face.");
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Bevel Edges",
@"Smooth the selected edges by adding a slanted face connecting the two adjacent faces."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge | SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var floatField = new FloatField(gc_BevelDistance.text);
floatField.tooltip = gc_BevelDistance.tooltip;
floatField.isDelayed = PreviewActionManager.delayedPreview;
floatField.SetValueWithoutNotify(m_BevelSize.value);
floatField.RegisterCallback<ChangeEvent<float>>(evt =>
{
if (m_BevelSize.value != evt.newValue)
{
if (evt.newValue < k_MinBevelDistance)
{
m_BevelSize.SetValue(k_MinBevelDistance);
floatField.SetValueWithoutNotify(m_BevelSize);
}
else
m_BevelSize.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
}
});
System.Action onDelayedPreviewChanged = () =>
{
floatField.isDelayed = PreviewActionManager.delayedPreview;
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
root.Add(floatField);
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Bevel Edge Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Amount determines how much space the bevel occupies. The value is clamped to the size of the smallest affected face.", MessageType.Info);
EditorGUI.BeginChangeCheck();
m_BevelSize.value = EditorGUILayout.FloatField(gc_BevelDistance, m_BevelSize);
if (m_BevelSize < k_MinBevelDistance)
m_BevelSize.value = k_MinBevelDistance;
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Bevel Edges"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
ActionResult res = ActionResult.NoSelection;
UndoUtility.RecordSelection("Bevel Edges");
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
pb.ToMesh();
List<Face> faces = Bevel.BevelEdges(pb, pb.selectedEdges, m_BevelSize);
res = faces != null ? new ActionResult(ActionResult.Status.Success, "Bevel Edges") : new ActionResult(ActionResult.Status.Failure, "Failed Bevel Edges");
if (res)
pb.SetSelectedFaces(faces);
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b7bd43b2e3e547199420eec1d903ff30
timeCreated: 1466191810
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,75 @@
using UnityEngine;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class BridgeEdges : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Edge_Bridge";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Bridge Edges",
@"Add a new face connecting two edges.",
keyCommandAlt, 'B'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.topInternal.Any(x => x.selectedEdgeCount == 2); }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Bridge Edges");
bool success = false;
foreach (var mesh in MeshSelection.topInternal)
{
if (mesh.selectedEdgeCount == 2)
{
if (mesh.Bridge(mesh.selectedEdges[0], mesh.selectedEdges[1], ProBuilderEditor.s_AllowNonManifoldActions) != null)
{
success = true;
mesh.ToMesh();
mesh.Refresh();
mesh.Optimize();
}
}
}
if (success)
{
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Bridge Edges");
}
else
{
Debug.LogWarning("Failed Bridge Edges. Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold).");
return new ActionResult(ActionResult.Status.Failure, "Bridge Edges requires that only 2 edges be selected, and they must both only have one connecting face (non-manifold).");
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 066d50a96f4a480fac834bf3b64a8c26
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,122 @@
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class CollapseVertices : MenuAction
{
Pref<bool> m_CollapseToFirst = new Pref<bool>("CollapseVertices.collapseToFirst", false);
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Vert_Collapse";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Collapse Vertices",
@"Merge all selected vertices into a single vertex, centered at the first vertex or average position of all selected points."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedSharedVertexCountObjectMax > 1; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
root.style.minWidth = 150;
var toggle = new Toggle("Collapse to First");
toggle.tooltip = "Collapse To First setting decides where the collapsed vertex will be placed. If True, " +
"the new vertex will be placed at the position of the first selected vertex. If false, the new vertex " +
"is placed at the average position of all selected vertices.";
toggle.SetValueWithoutNotify(m_CollapseToFirst);
toggle.RegisterCallback<ChangeEvent<bool>>(evt =>
{
m_CollapseToFirst.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
});
root.Add(toggle);
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Collapse Vertices Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Collapse To First setting decides where the collapsed vertex will be placed.\n\nIf True, the new vertex will be placed at the position of the first selected vertex. If false, the new vertex is placed at the average position of all selected vertices.", MessageType.Info);
EditorGUI.BeginChangeCheck();
m_CollapseToFirst.value = EditorGUILayout.Toggle("Collapse To First", m_CollapseToFirst);
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Collapse Vertices"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
bool success = false;
bool collapseToFirst = m_CollapseToFirst;
UndoUtility.RecordSelection("Collapse Vertices");
foreach (var mesh in MeshSelection.topInternal)
{
if (mesh.selectedIndexesInternal.Length > 1)
{
int newIndex = mesh.MergeVertices(mesh.selectedIndexesInternal, collapseToFirst);
success = newIndex > -1;
if (success)
mesh.SetSelectedVertices(new int[] { newIndex });
mesh.ToMesh();
mesh.Refresh();
mesh.Optimize();
}
}
ProBuilderEditor.Refresh();
if (success)
return new ActionResult(ActionResult.Status.Success, "Collapse Vertices");
return new ActionResult(ActionResult.Status.Failure, "Collapse Vertices\nNo Vertices Selected");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d1e3d0e26dcfcb438a45fbbf9f23e92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ConformFaceNormals : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Face_ConformNormals";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_TooltipContent; } }
public override string menuTitle { get { return "Conform Face Normals"; } }
static readonly TooltipContent s_TooltipContent = new TooltipContent
(
"Conform Face Normals",
@"Orients all selected faces to face the same direction."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCountObjectMax > 1; }
}
protected override ActionResult PerformActionImplementation()
{
var selection = MeshSelection.topInternal;
UndoUtility.RecordSelection("Conform " + (MeshSelection.selectedFaceCount > 0 ? "Face" : "Object") + " Normals.");
ActionResult res = ActionResult.NoSelection;
foreach (ProBuilderMesh pb in selection)
{
var faces = pb.GetSelectedFaces();
if (faces == null)
continue;
res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, faces);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1fdc9faa599347a3b697e785ac4719b3
timeCreated: 1457564636
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ConnectEdges : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Edge_Connect";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
protected internal override bool hasFileMenuEntry { get { return false; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Connect Edges",
"Inserts a new edge connecting the center points of all selected edges. See also \"Subdivide.\"",
keyCommandAlt, 'E'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedEdgeCountObjectMax > 1; }
}
protected override ActionResult PerformActionImplementation()
{
ActionResult res = ActionResult.NoSelection;
UndoUtility.RecordSelection("Connect Edges");
foreach (var mesh in MeshSelection.topInternal)
{
Edge[] connections;
Face[] faces;
res = ConnectElements.Connect(mesh, mesh.selectedEdges, out faces, out connections, true, true);
if (connections != null)
{
if (connections.Length != 0)
mesh.SetSelectedEdges(connections);
mesh.Refresh();
mesh.Optimize();
}
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9eda47df4ab94d8a8174832a11359a8a
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ConnectVertices : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Vert_Connect";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return _tooltip; } }
protected internal override bool hasFileMenuEntry { get { return false; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Connect Vertices",
@"Adds edges connecting all selected vertices.",
keyCommandAlt, 'E'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedSharedVertexCount > 1; }
}
protected override ActionResult PerformActionImplementation()
{
ActionResult res = ActionResult.NoSelection;
UndoUtility.RecordSelection("Connect Vertices");
foreach (var mesh in MeshSelection.topInternal)
{
mesh.ToMesh();
int[] splits = mesh.Connect(mesh.selectedIndexesInternal);
if (splits != null)
{
mesh.Refresh();
mesh.Optimize();
mesh.SetSelectedVertices(splits);
res = new ActionResult(ActionResult.Status.Success, "Connect Edges");
}
else
{
res = new ActionResult(ActionResult.Status.Failure, "Failed Connecting Edges");
}
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6d73444bb8cc9b468d5ff190d7de759
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,75 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class DeleteFaces : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_Delete";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Delete Faces",
@"Delete all selected faces.",
keyCommandDelete
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Delete Face");
int count = 0;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
if (pb.selectedFaceCount == pb.facesInternal.Length)
{
Debug.LogWarning("Attempting to delete all faces on this mesh... I'm afraid I can't let you do that.");
continue;
}
Undo.RecordObject(pb.renderer, "Update Renderer");
pb.DeleteFaces(pb.selectedFacesInternal);
count += pb.selectedFaceCount;
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
MeshSelection.ClearElementSelection();
ProBuilderEditor.Refresh();
if (count > 0)
return new ActionResult(ActionResult.Status.Success, "Delete " + count + " Faces");
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b0c8bf4cfa4d461cb01b12bb9e3e42f9
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,182 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using System;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace UnityEditor.ProBuilder.Actions
{
sealed class DetachFaces : MenuAction
{
Pref<DetachSetting> m_DetachSetting = new Pref<DetachSetting>("DetachFaces.target", DetachSetting.GameObject);
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Face_Detach";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Detach Faces",
"Creates a new object (or submesh) from the selected faces."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
enum DetachSetting
{
GameObject,
Submesh
};
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var detachFace = new EnumField("Detach To", m_DetachSetting);
detachFace.tooltip = "You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.";
detachFace.RegisterCallback<ChangeEvent<string>>(evt =>
{
Enum.TryParse(evt.newValue, out DetachSetting newValue);
m_DetachSetting.SetValue(newValue);
});
root.Add(detachFace);
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Detach Face Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
m_DetachSetting.value = EditorGUILayout.Toggle("Separate GameObject", m_DetachSetting.value == DetachSetting.GameObject)
? DetachSetting.GameObject
: DetachSetting.Submesh;
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Detach Selection"))
EditorUtility.ShowNotification(PerformAction().notification);
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Detach Face(s)");
if (m_DetachSetting == DetachSetting.GameObject)
return DetachFacesToObject();
return DetachFacesToSubmesh();
}
static ActionResult DetachFacesToSubmesh()
{
int count = 0;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
pb.ToMesh();
List<Face> res = pb.DetachFaces(pb.selectedFacesInternal);
pb.Refresh();
pb.Optimize();
pb.SetSelectedFaces(res.ToArray());
count += pb.selectedFaceCount;
}
ProBuilderEditor.Refresh();
if (count > 0)
return new ActionResult(ActionResult.Status.Success, "Detach " + count + (count > 1 ? " Faces" : " Face"));
return new ActionResult(ActionResult.Status.Success, "Detach Faces");
}
static ActionResult DetachFacesToObject()
{
int detachedFaceCount = 0;
List<GameObject> detached = new List<GameObject>();
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
if (mesh.selectedFaceCount < 1 || mesh.selectedFaceCount == mesh.facesInternal.Length)
continue;
var primary = mesh.selectedFaceIndexes;
detachedFaceCount += primary.Count;
List<int> inverse = new List<int>();
for (int i = 0; i < mesh.facesInternal.Length; i++)
if (!primary.Contains(i))
inverse.Add(i);
ProBuilderMesh copy = Object.Instantiate(mesh.gameObject, mesh.transform.parent).GetComponent<ProBuilderMesh>();
copy.MakeUnique();
EditorUtility.SynchronizeWithMeshFilter(copy);
if (copy.transform.childCount > 0)
{
for (int i = copy.transform.childCount - 1; i > -1; i--)
Object.DestroyImmediate(copy.transform.GetChild(i).gameObject);
foreach (var child in mesh.transform.GetComponentsInChildren<ProBuilderMesh>())
EditorUtility.SynchronizeWithMeshFilter(child);
}
Undo.RecordObject(mesh.renderer, "Update Renderer");
mesh.DeleteFaces(primary);
copy.DeleteFaces(inverse);
mesh.Rebuild();
copy.Rebuild();
mesh.Optimize();
copy.Optimize();
mesh.ClearSelection();
copy.ClearSelection();
copy.SetSelectedFaces(copy.faces);
Undo.RegisterCreatedObjectUndo(copy.gameObject, "Detach Selection");
copy.gameObject.name = GameObjectUtility.GetUniqueNameForSibling(mesh.transform.parent, mesh.gameObject.name); ;
detached.Add(copy.gameObject);
}
MeshSelection.SetSelection(detached);
ProBuilderEditor.Refresh();
if (detachedFaceCount > 0)
return new ActionResult(ActionResult.Status.Success, "Detach " + detachedFaceCount + " faces to new Object");
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d9930631c9404fb0b63a91644558eb39
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using Object = UnityEngine.Object;
namespace UnityEditor.ProBuilder.Actions
{
sealed class DuplicateFaces : MenuAction
{
Pref<DuplicateFaceSetting> m_DuplicateFaceSetting = new Pref<DuplicateFaceSetting>("DuplicateFaces.target", DuplicateFaceSetting.GameObject);
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Face_Duplicate";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Duplicate Faces",
"Makes an exact copy of the selected faces, and either adds them to this mesh or creates a new Game Object"
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
internal enum DuplicateFaceSetting
{
GameObject,
Submesh
};
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var duplicateType = new EnumField("Duplicate To", m_DuplicateFaceSetting);
duplicateType.tooltip = "You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.";
duplicateType.RegisterCallback<ChangeEvent<string>>(evt =>
{
Enum.TryParse(evt.newValue, out DuplicateFaceSetting newValue);
if (m_DuplicateFaceSetting.value == newValue)
return;
m_DuplicateFaceSetting.value = newValue;
ProBuilderSettings.Save();
});
root.Add(duplicateType);
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Duplicate Face Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("You can create a new Game Object with the selected face(s), or keep them as part of this object by using a Submesh.", MessageType.Info);
EditorGUI.BeginChangeCheck();
m_DuplicateFaceSetting.value = (DuplicateFaceSetting)EditorGUILayout.EnumPopup("Duplicate To", m_DuplicateFaceSetting);
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Duplicate Selection"))
EditorUtility.ShowNotification(PerformAction().notification);
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Duplicate Face(s)");
if (m_DuplicateFaceSetting == DuplicateFaceSetting.GameObject)
return DuplicateFacesToObject();
return DuplicateFacesToSubmesh();
}
static ActionResult DuplicateFacesToSubmesh()
{
int count = 0;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
pb.ToMesh();
List<Face> res = pb.DetachFaces(pb.selectedFacesInternal, false);
pb.Refresh();
pb.Optimize();
pb.SetSelectedFaces(res.ToArray());
count += pb.selectedFaceCount;
}
ProBuilderEditor.Refresh();
if (count > 0)
return new ActionResult(ActionResult.Status.Success, "Duplicate " + count + (count > 1 ? " Faces" : " Face"));
return new ActionResult(ActionResult.Status.Success, "Duplicate Faces");
}
static ActionResult DuplicateFacesToObject()
{
int duplicatedFaceCount = 0;
List<GameObject> duplicated = new List<GameObject>();
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
if (mesh.selectedFaceCount < 1)
continue;
var primary = mesh.selectedFaceIndexes;
duplicatedFaceCount += primary.Count;
List<int> inverse = new List<int>();
for (int i = 0; i < mesh.facesInternal.Length; i++)
if (!primary.Contains(i))
inverse.Add(i);
ProBuilderMesh copy = Object.Instantiate(mesh.gameObject, mesh.transform.parent).GetComponent<ProBuilderMesh>();
copy.MakeUnique();
EditorUtility.SynchronizeWithMeshFilter(copy);
if (copy.transform.childCount > 0)
{
for (int i = copy.transform.childCount - 1; i > -1; i--)
Object.DestroyImmediate(copy.transform.GetChild(i).gameObject);
foreach (var child in mesh.transform.GetComponentsInChildren<ProBuilderMesh>())
EditorUtility.SynchronizeWithMeshFilter(child);
}
Undo.RegisterCreatedObjectUndo(copy.gameObject, "Duplicate Selection");
copy.DeleteFaces(inverse);
copy.Rebuild();
copy.Optimize();
mesh.ClearSelection();
copy.ClearSelection();
copy.SetSelectedFaces(copy.faces);
copy.gameObject.name = GameObjectUtility.GetUniqueNameForSibling(mesh.transform.parent, mesh.gameObject.name);
duplicated.Add(copy.gameObject);
}
MeshSelection.SetSelection(duplicated);
ProBuilderEditor.Refresh();
if (duplicatedFaceCount > 0)
return new ActionResult(ActionResult.Status.Success, "Duplicate " + duplicatedFaceCount + " faces to new Object");
return new ActionResult(ActionResult.Status.Failure, "No Faces Selected");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 172d21c82259a0c428b3924edd908358
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using System;
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
// The menu bar entry for Extrude delegates to Face/Edge as appropriate.
sealed class Extrude : MenuAction
{
public override ToolbarGroup @group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => String.Empty;
public override Texture2D icon => null;
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Extrude", "",
keyCommandSuper, 'E'
);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
public override SelectMode validSelectModes
{
get { return SelectMode.Face | SelectMode.Edge; }
}
public override bool enabled
{
get
{
switch (ProBuilderEditor.selectMode)
{
case SelectMode.Edge:
return EditorToolbarLoader.GetInstance<ExtrudeEdges>().enabled;
case SelectMode.Face:
return EditorToolbarLoader.GetInstance<ExtrudeFaces>().enabled;
default:
return false;
}
}
}
public override bool hidden
{
get { return true; }
}
protected override ActionResult PerformActionImplementation()
{
switch (ProBuilderEditor.selectMode)
{
case SelectMode.Edge:
return EditorToolbarLoader.GetInstance<ExtrudeEdges>().PerformAction();
case SelectMode.Face:
return EditorToolbarLoader.GetInstance<ExtrudeFaces>().PerformAction();
default:
return ActionResult.NoSelection;
}
}
internal override string GetMenuItemOverride()
{
return @" switch (ProBuilderEditor.selectMode)
{
case SelectMode.Edge:
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<ExtrudeEdges>(), true));
break;
case SelectMode.Face:
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<ExtrudeFaces>(), true));
break;
}";
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9b24935f60532d7428cbcb5df316e359
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,147 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExtrudeEdges : MenuAction
{
Pref<float> m_ExtrudeEdgeDistance = new Pref<float>("ExtrudeEdges.distance", .5f);
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Edge_Extrude";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return s_Tooltip; } }
protected internal override bool hasFileMenuEntry { get { return false; } }
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Extrude Edges",
@"Adds a new face extending from the currently selected edges. Edges must have an open side to be extruded.
NB : Allow non-manifold actions should be authorized in ProBuilder preferences to enable this action.",
keyCommandSuper, 'E'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedEdgeCount > 0 && ProBuilderEditor.s_AllowNonManifoldActions; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var toggle = new Toggle("As Group");
toggle.tooltip = "Extrude as Group determines whether or not adjacent faces stay attached to one another when extruding.";
toggle.SetValueWithoutNotify(VertexManipulationTool.s_ExtrudeEdgesAsGroup);
toggle.RegisterCallback<ChangeEvent<bool>>(OnEdgesAsGroupChanged);
root.Add(toggle);
var floatField = new FloatField("Distance");
floatField.isDelayed = PreviewActionManager.delayedPreview;
floatField.tooltip = "Extrude Amount determines how far an edge will be moved along it's normal when extruding. This value can be negative.";
floatField.SetValueWithoutNotify(m_ExtrudeEdgeDistance);
floatField.RegisterCallback<ChangeEvent<float>>(OnExtrudeChanged);
System.Action onDelayedPreviewChanged = () =>
{
floatField.isDelayed = PreviewActionManager.delayedPreview;
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
root.Add(floatField);
return root;
}
void OnEdgesAsGroupChanged(ChangeEvent<bool> evt)
{
VertexManipulationTool.s_ExtrudeEdgesAsGroup.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
}
void OnExtrudeChanged(ChangeEvent<float> evt)
{
m_ExtrudeEdgeDistance.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Extrude Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Extrude Amount determines how far an edge will be moved along it's normal when extruding. This value can be negative.\n\nExtrude as Group determines whether or not adjacent faces stay attached to one another when extruding.", MessageType.Info);
EditorGUI.BeginChangeCheck();
VertexManipulationTool.s_ExtrudeEdgesAsGroup.value = EditorGUILayout.Toggle("As Group", VertexManipulationTool.s_ExtrudeEdgesAsGroup);
m_ExtrudeEdgeDistance.value = EditorGUILayout.FloatField("Distance", m_ExtrudeEdgeDistance);
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Extrude Edges"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Extrude");
int extrudedFaceCount = 0;
bool success = false;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
pb.ToMesh();
pb.Refresh(RefreshMask.Normals);
if (pb.selectedEdgeCount < 1)
continue;
extrudedFaceCount += pb.selectedEdgeCount;
Edge[] newEdges = pb.Extrude(pb.selectedEdges,
m_ExtrudeEdgeDistance,
VertexManipulationTool.s_ExtrudeEdgesAsGroup,
ProBuilderEditor.s_AllowNonManifoldActions);
success |= newEdges != null;
if (success)
pb.SetSelectedEdges(newEdges);
else
extrudedFaceCount -= pb.selectedEdgeCount;
pb.Rebuild();
}
ProBuilderEditor.Refresh();
if (extrudedFaceCount > 0)
return new ActionResult(ActionResult.Status.Success, "Extrude");
return new ActionResult(ActionResult.Status.Canceled, "Extrude\nEmpty Selection");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 97626f417dcf4f68a68db312995f9e6b
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,186 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ExtrudeFaces : MenuAction
{
Pref<float> m_ExtrudeDistance = new Pref<float>("ExtrudeFaces.distance", .5f);
ExtrudeMethod extrudeMethod
{
get { return VertexManipulationTool.s_ExtrudeMethod; }
set { VertexManipulationTool.s_ExtrudeMethod.value = value; }
}
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_Extrude";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
protected internal override bool hasFileMenuEntry
{
get { return false; }
}
Texture2D[] m_Icons = null;
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Extrude Faces",
"Extrude selected faces, either as a group or individually.\n\nAlt + Click this button to show additional Extrude options.",
keyCommandSuper, 'E'
);
public ExtrudeFaces()
{
m_Icons = new Texture2D[3];
m_Icons[(int)ExtrudeMethod.IndividualFaces] = IconUtility.GetIcon("Toolbar/ExtrudeFace_Individual");
m_Icons[(int)ExtrudeMethod.VertexNormal] = IconUtility.GetIcon("Toolbar/ExtrudeFace_VertexNormals");
m_Icons[(int)ExtrudeMethod.FaceNormal] = IconUtility.GetIcon("Toolbar/ExtrudeFace_FaceNormals");
}
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var extrudeMethodLabel = new Label();
extrudeMethodLabel.tooltip = " You may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.";
extrudeMethodLabel.style.backgroundImage = m_Icons[(int)extrudeMethod];
extrudeMethodLabel.style.height = 22;
extrudeMethodLabel.style.width = 36;
var extrudeMethodField = new EnumField("Extrude By", extrudeMethod);
extrudeMethodField.tooltip = " You may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.";
extrudeMethodField.RegisterCallback<ChangeEvent<System.Enum>>(evt =>
{
var newEnumValue = (ExtrudeMethod)evt.newValue;
if (extrudeMethod != newEnumValue)
{
extrudeMethod = newEnumValue;
extrudeMethodLabel.style.backgroundImage = m_Icons[(int)extrudeMethod];
ProBuilderSettings.Save();
PreviewActionManager.UpdatePreview();
}
});
extrudeMethodField.style.flexGrow = 1;
var line = new VisualElement();
line.style.flexDirection = FlexDirection.Row;
line.Add(extrudeMethodField);
line.Add(extrudeMethodLabel);
root.Add(line);
var distanceField = new FloatField("Distance");
distanceField.tooltip = "Extrude Amount determines how far a face will be moved along it's normal when extruding. This value can be negative.";
distanceField.SetValueWithoutNotify(m_ExtrudeDistance.value);
distanceField.isDelayed = PreviewActionManager.delayedPreview;
distanceField.RegisterCallback<ChangeEvent<float>>(evt =>
{
m_ExtrudeDistance.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
});
root.Add(distanceField);
System.Action onDelayedPreviewChanged = () =>
{
distanceField.isDelayed = PreviewActionManager.delayedPreview;
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Extrude Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Extrude Amount determines how far a face will be moved along it's normal when extruding. This value can be negative.\n\nYou may also choose to Extrude by Face Normal, Vertex Normal, or as Individual Faces.", MessageType.Info);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Label(m_Icons[(int)extrudeMethod]);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
EditorGUI.BeginChangeCheck();
extrudeMethod = (ExtrudeMethod)EditorGUILayout.EnumPopup("Extrude By", extrudeMethod);
m_ExtrudeDistance.value = EditorGUILayout.FloatField("Distance", m_ExtrudeDistance);
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Extrude Faces"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Extrude");
int extrudedFaceCount = 0;
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
mesh.ToMesh();
mesh.Refresh(RefreshMask.Normals);
if (mesh.selectedFaceCount < 1)
continue;
extrudedFaceCount += mesh.selectedFaceCount;
var selectedFaces = mesh.GetSelectedFaces();
mesh.Extrude(selectedFaces,
VertexManipulationTool.s_ExtrudeMethod,
m_ExtrudeDistance);
mesh.SetSelectedFaces(selectedFaces);
mesh.Rebuild();
mesh.Optimize();
}
ProBuilderEditor.Refresh();
if (extrudedFaceCount > 0)
return new ActionResult(ActionResult.Status.Success, "Extrude");
return new ActionResult(ActionResult.Status.Canceled, "Extrude\nEmpty Selection");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d790e5dd7e634bac936d5dc0e026ae20
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,190 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Actions
{
sealed class FillHole : MenuAction
{
Pref<bool> m_SelectEntirePath = new Pref<bool>("FillHole.selectEntirePath", true);
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Edge_FillHole";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Fill Hole",
@"Create a new face connecting all selected vertices."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge | SelectMode.Vertex; }
}
public override bool enabled
{
get { return base.enabled && (MeshSelection.selectedEdgeCount > 0 || MeshSelection.selectedSharedVertexCount > 0); }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var toggle = new Toggle("Fill Entire Hole");
toggle.tooltip = "Fill Hole can optionally fill entire holes (default) or just the selected vertices on the hole edges. If no elements are selected, the entire object will be scanned for holes.";
toggle.style.flexGrow =1;
toggle.SetValueWithoutNotify(m_SelectEntirePath);
toggle.RegisterCallback<ChangeEvent<bool>>(OnFillHoleToggleChanged);
root.Add(toggle);
return root;
}
void OnFillHoleToggleChanged(ChangeEvent<bool> evt)
{
m_SelectEntirePath.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Fill Hole Settings", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Fill Hole can optionally fill entire holes (default) or just the selected vertices on the hole edges.\n\nIf no elements are selected, the entire object will be scanned for holes.", MessageType.Info);
EditorGUI.BeginChangeCheck();
m_SelectEntirePath.value = EditorGUILayout.Toggle("Fill Entire Hole", m_SelectEntirePath);
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Fill Hole"))
EditorUtility.ShowNotification(PerformAction().notification);
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Fill Hole");
ActionResult res = new ActionResult(ActionResult.Status.NoChange, "No Holes Found");
int filled = 0;
bool wholePath = m_SelectEntirePath;
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
bool selectAll = mesh.selectedIndexesInternal == null || mesh.selectedIndexesInternal.Length < 1;
IEnumerable<int> indexes = selectAll ? mesh.facesInternal.SelectMany(x => x.indexes) : mesh.selectedIndexesInternal;
mesh.ToMesh();
List<WingedEdge> wings = WingedEdge.GetWingedEdges(mesh);
HashSet<int> common = mesh.GetSharedVertexHandles(indexes);
List<List<WingedEdge>> holes = ElementSelection.FindHoles(wings, common);
HashSet<Face> appendedFaces = new HashSet<Face>();
foreach (List<WingedEdge> hole in holes)
{
List<int> holeIndexes;
Face face;
if (wholePath)
{
// if selecting whole path and in edge mode, make sure the path contains
// at least one complete edge from the selection.
if (ProBuilderEditor.selectMode == SelectMode.Edge &&
!hole.Any(x => common.Contains(x.edge.common.a) &&
common.Contains(x.edge.common.b)))
continue;
holeIndexes = hole.Select(x => x.edge.local.a).ToList();
face = AppendElements.CreatePolygon(mesh, holeIndexes, false);
}
else
{
IEnumerable<WingedEdge> selected = hole.Where(x => common.Contains(x.edge.common.a));
holeIndexes = selected.Select(x => x.edge.local.a).ToList();
face = AppendElements.CreatePolygon(mesh, holeIndexes, true);
}
if (face != null)
{
filled++;
appendedFaces.Add(face);
}
}
mesh.SetSelectedFaces(appendedFaces);
wings = WingedEdge.GetWingedEdges(mesh);
// make sure the appended faces match the first adjacent face found
// both in winding and face properties
foreach (var appendedFace in appendedFaces)
{
var wing = wings.FirstOrDefault(x => x.face == appendedFace);
if (wing == null)
continue;
using (var it = new WingedEdgeEnumerator(wing))
{
while (it.MoveNext())
{
if (it.Current == null)
continue;
var currentWing = it.Current;
var oppositeFace = it.Current.opposite != null ? it.Current.opposite.face : null;
if (oppositeFace != null && !appendedFaces.Contains(oppositeFace))
{
currentWing.face.submeshIndex = oppositeFace.submeshIndex;
currentWing.face.uv = new AutoUnwrapSettings(oppositeFace.uv);
SurfaceTopology.ConformOppositeNormal(currentWing.opposite);
break;
}
}
}
}
mesh.ToMesh();
mesh.Refresh();
mesh.Optimize();
}
ProBuilderEditor.Refresh();
if (filled > 0)
res = new ActionResult(ActionResult.Status.Success, filled > 1 ? string.Format("Filled {0} Holes", filled) : "Fill Hole");
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2871f338d8894523bcde8a078df337c2
timeCreated: 1466443265
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,70 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class FlipFaceEdge : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_FlipTri";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Flip Face Edge",
@"Reverses the direction of the middle edge in a quad. Use this to fix ridges in quads with varied height corners."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Flip Face Edges");
int success = 0;
int attempts = 0;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
foreach (Face face in pb.selectedFacesInternal)
{
if (pb.FlipEdge(face))
success++;
}
attempts++;
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
if (success > 0)
return new ActionResult(ActionResult.Status.Success, "Flipped " + success + " Edges");
return new ActionResult(ActionResult.Status.Failure, string.Format("Flip Edges\n{0}", attempts > 0 ? "Faces Must Be Quads" : "No Faces Selected"));
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4e7e1880876747d1823604bc506e3772
timeCreated: 1457564632
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class FlipFaceNormals : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_FlipNormals";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Flip Face Normals",
@"Reverses the direction of all faces in selection.",
keyCommandAlt, 'N'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Flip Face Normals");
int c = 0;
int faceCount = MeshSelection.selectedFaceCount;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
if (pb.selectedFaceCount < 1 && faceCount < 1)
{
foreach (var face in pb.facesInternal)
face.Reverse();
c += pb.facesInternal.Length;
}
else
{
foreach (var face in pb.GetSelectedFaces())
face.Reverse();
c += pb.selectedFaceCount;
}
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
if (c > 0)
return new ActionResult(ActionResult.Status.Success, "Flip " + c + (c > 1 ? " Face Normals" : " Face Normal"));
return new ActionResult(ActionResult.Status.Canceled, "Flip Normals\nNo Faces Selected");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 71cfb4d65045470facb2a2e1e41bec03
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,73 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class InsertEdgeLoop : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Edge_InsertLoop";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return _tooltip; }
}
static readonly TooltipContent _tooltip = new TooltipContent
(
"Insert Edge Loop",
@"Connects all edges in a ring around the object.",
keyCommandAlt, 'U'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
int success = 0;
UndoUtility.RecordSelection("Insert Edge Loop");
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
Edge[] edges = pb.Connect(ElementSelection.GetEdgeRing(pb, pb.selectedEdges)).item2;
if (edges != null)
{
pb.SetSelectedEdges(edges);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
success++;
}
}
ProBuilderEditor.Refresh();
if (success > 0)
return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop");
return new ActionResult(ActionResult.Status.Success, "Insert Edge Loop");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d3ba8385f1f540519a3524dceb13733b
timeCreated: 1457564632
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,72 @@
using UnityEngine;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class MergeFaces : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_Merge";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Merge Faces",
@"Tells ProBuilder to treat the selected faces as if they were a single face. Be careful not to use this with unconnected faces!"
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Merge Faces");
int success = 0;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
if (pb.selectedFaceCount > 1)
{
success += pb.selectedFaceCount;
Face face = MergeElements.Merge(pb, pb.selectedFacesInternal);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
pb.SetSelectedFaces(new Face[] { face });
}
}
ProBuilderEditor.Refresh();
if (success > 0)
return new ActionResult(ActionResult.Status.Success, "Merged " + success + " Faces");
return new ActionResult(ActionResult.Status.Failure, "Merge Faces\nNo Faces Selected");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 72b17ff6fd2249038e3d0e0d8969cef0
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.ProBuilder;
using ColorUtility = UnityEngine.ProBuilder.ColorUtility;
namespace UnityEditor.ProBuilder.Actions
{
sealed class OffsetElements : MenuAction
{
internal enum CoordinateSpace
{
World,
Local,
Element,
Handle
}
internal static Pref<Vector3> s_Translation = new Pref<Vector3>("MoveElements.s_Translation", Vector3.up);
internal static Pref<CoordinateSpace> s_CoordinateSpace = new Pref<CoordinateSpace>("MoveElements.s_CoordinateSpace", CoordinateSpace.World);
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/OffsetElements";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip => new TooltipContent ( "Offset Elements", "Move the selected elements by a set amount." );
public override SelectMode validSelectModes
{
get { return SelectMode.Face | SelectMode.Edge | SelectMode.Vertex; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedVertexCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var dist = s_Translation.value;
var coord = s_CoordinateSpace.value;
var spaceField = new EnumField("Coordinate Space", coord);
root.Add(spaceField);
spaceField.RegisterCallback<ChangeEvent<Enum>>(evt =>
{
CoordinateSpace newValue = (CoordinateSpace)evt.newValue;
if (s_CoordinateSpace.value == newValue)
return;
s_CoordinateSpace.SetValue(newValue);
PreviewActionManager.UpdatePreview();
});
var distField = new Vector3Field("Translate");
distField.SetValueWithoutNotify(dist);
distField.Query<FloatField>().ForEach(ff => ff.isDelayed = PreviewActionManager.delayedPreview);
root.Add(distField);
distField.RegisterCallback<ChangeEvent<Vector3>>(evt =>
{
s_Translation.SetValue(evt.newValue, true);
PreviewActionManager.UpdatePreview();
});
Action onDelayedPreviewChanged = () =>
{
distField.Query<FloatField>().ForEach(ff => ff.isDelayed = PreviewActionManager.delayedPreview);
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
return root;
}
public override void DoSceneGUI(SceneView sceneView)
=> MoveElementsSettings.OnSceneGUI(sceneView);
protected override void DoAlternateAction()
{
ConfigurableWindow.GetWindow<MoveElementsSettings>(true, "Offset Settings", true);
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Offset Elements(s)");
var handleRotation = MeshSelection.GetHandleRotation();
foreach (var group in MeshSelection.elementSelection)
{
var mesh = group.mesh;
var positions = mesh.positionsInternal;
var offset = s_Translation.value;
switch (s_CoordinateSpace.value)
{
case CoordinateSpace.World:
case CoordinateSpace.Handle:
{
var pre = mesh.transform.localToWorldMatrix;
var post = mesh.transform.worldToLocalMatrix;
if (s_CoordinateSpace.value == CoordinateSpace.Handle)
offset = handleRotation * offset;
foreach (var index in mesh.selectedCoincidentVertices)
{
var p = pre.MultiplyPoint3x4(positions[index]);
p += offset;
positions[index] = post.MultiplyPoint3x4(p);
}
break;
}
case CoordinateSpace.Local:
{
foreach (var index in mesh.selectedCoincidentVertices)
positions[index] += offset;
break;
}
case CoordinateSpace.Element:
{
foreach (var elements in group.elementGroups)
{
var rotation = Quaternion.Inverse(mesh.transform.rotation) * elements.rotation;
var o = rotation * offset;
foreach (var index in elements.indices)
positions[index] += o;
}
break;
}
}
mesh.Rebuild();
mesh.Optimize();
ProBuilderEditor.Refresh();
}
if(ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedEdgeCount + (MeshSelection.selectedEdgeCount > 1 ? " Edges" : " Edge"));
if(ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace))
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedFaceCount + (MeshSelection.selectedFaceCount > 1 ? " Faces" : " Face"));
return new ActionResult(ActionResult.Status.Success, "Move " + MeshSelection.selectedVertexCount + (MeshSelection.selectedVertexCount > 1 ? " Vertices" : " Vertex"));
}
}
class MoveElementsSettings : ConfigurableWindow
{
void OnEnable()
{
titleContent.text = L10n.Tr("Offset Element Settings");
SceneView.duringSceneGui += OnSceneGUI;
}
void OnDisable()
{
SceneView.duringSceneGui -= OnSceneGUI;
}
void OnGUI()
{
DoContextMenu();
EditorGUI.BeginChangeCheck();
var dist = OffsetElements.s_Translation.value;
var coord = OffsetElements.s_CoordinateSpace.value;
EditorGUI.BeginChangeCheck();
coord = (OffsetElements.CoordinateSpace) EditorGUILayout.EnumPopup("Coordinate Space", coord);
dist = EditorGUILayout.Vector3Field("Translate", dist);
if (EditorGUI.EndChangeCheck())
{
OffsetElements.s_Translation.SetValue(dist, true);
OffsetElements.s_CoordinateSpace.SetValue(coord);
}
GUILayout.FlexibleSpace();
if (GUILayout.Button(L10n.Tr("Offset Selection")))
{
var instance = EditorToolbarLoader.GetInstance<OffsetElements>();
EditorUtility.ShowNotification(instance.PerformAction().notification);
}
if (EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
}
static List<Vector3> s_Points = new List<Vector3>();
[InitializeOnEnterPlayMode]
static void ResetStaticsOnLoad()
{
s_Points.Clear();
}
internal static void OnSceneGUI(SceneView view)
{
s_Points.Clear();
var coord = OffsetElements.s_CoordinateSpace.value;
var offset = OffsetElements.s_Translation.value;
var handleRotation = MeshSelection.GetHandleRotation();
var camera = view.camera.transform.forward * -.01f;
foreach (var selection in MeshSelection.elementSelection)
{
var mesh = selection.mesh;
if (coord == OffsetElements.CoordinateSpace.Element)
{
foreach (var elements in selection.elementGroups)
{
s_Points.Add(elements.position + camera);
s_Points.Add(elements.rotation * offset);
}
}
else
{
var preview = offset;
if (coord == OffsetElements.CoordinateSpace.Handle)
preview = handleRotation * offset;
else if (coord == OffsetElements.CoordinateSpace.Local)
preview = mesh.transform.TransformDirection(offset);
foreach (var elements in selection.elementGroups)
{
s_Points.Add(elements.position + camera);
s_Points.Add(preview);
}
}
}
using (var lines = new EditorHandleDrawing.LineDrawingScope(ColorUtility.GetColor(offset)))
{
for (int i = 0; i < s_Points.Count; i += 2)
lines.DrawLine(s_Points[i], s_Points[i] + s_Points[i + 1]);
}
using (var points = new EditorHandleDrawing.PointDrawingScope(Color.gray))
{
for (int i = 0; i < s_Points.Count; i += 2)
points.Draw(s_Points[i]);
}
using(var points = new EditorHandleDrawing.PointDrawingScope(ColorUtility.GetColor(offset)))
{
for (int i = 0; i < s_Points.Count; i += 2)
points.Draw(s_Points[i] + s_Points[i+1]);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5fb3641deade84f238d59ad03971b64c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SetPivotToSelection : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => "Toolbar/Pivot_CenterOnElements";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip { get { return _tooltip; } }
public override string menuTitle { get { return "Set Pivot To Selection"; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Set Pivot to Center of Selection",
@"Moves the pivot point of each mesh to the average of all selected elements positions. This means the pivot point moves to where-ever the handle currently is."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
}
public override bool enabled
{
get
{
return base.enabled && (MeshSelection.selectedVertexCount > 0
|| MeshSelection.selectedEdgeCount > 0
|| MeshSelection.selectedFaceCount > 0);
}
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
Object[] objects = new Object[MeshSelection.selectedObjectCount * 2];
for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++)
{
objects[i] = MeshSelection.topInternal[i];
objects[i + c] = MeshSelection.topInternal[i].transform;
}
UndoUtility.RegisterCompleteObjectUndo(objects, "Set Pivot");
foreach (var mesh in MeshSelection.topInternal)
{
TransformUtility.UnparentChildren(mesh.transform);
mesh.CenterPivot(mesh.selectedIndexesInternal);
mesh.Optimize();
TransformUtility.ReparentChildren(mesh.transform);
}
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Set Pivot");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a6bc50937fe34666940ca23931be5344
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SmartConnect : MenuAction
{
public override ToolbarGroup group { get { return ToolbarGroup.Geometry; } }
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip { get { return _tooltip; } }
static readonly TooltipContent _tooltip = new TooltipContent
(
"Smart Connect",
"",
keyCommandAlt, 'E'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
}
public override bool enabled
{
get
{
return base.enabled && (MeshSelection.selectedVertexCount > 1
|| MeshSelection.selectedEdgeCount > 1
|| MeshSelection.selectedFaceCount > 1);
}
}
public override bool hidden
{
get { return true; }
}
protected override ActionResult PerformActionImplementation()
{
switch (ProBuilderEditor.selectMode)
{
case SelectMode.Vertex:
return EditorToolbarLoader.GetInstance<ConnectVertices>().PerformAction();
default:
return EditorToolbarLoader.GetInstance<ConnectEdges>().PerformAction();
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3e99f90a32784f4d9ff62732e1265f23
timeCreated: 1467037553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,73 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.ProBuilder.UI;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEditor.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SmartSubdivide : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => string.Empty;
public override Texture2D icon => null;
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Smart Subdivide",
"",
keyCommandAlt, 'S'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge | SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && (MeshSelection.selectedEdgeCount > 0 || MeshSelection.selectedFaceCount > 0); }
}
public override bool hidden
{
get { return true; }
}
protected override ActionResult PerformActionImplementation()
{
switch (ProBuilderEditor.selectMode)
{
case SelectMode.Edge:
return EditorToolbarLoader.GetInstance<SubdivideEdges>().PerformAction();
default:
return EditorToolbarLoader.GetInstance<SubdivideFaces>().PerformAction();
}
}
internal override string GetMenuItemOverride()
{
return @" switch (ProBuilderEditor.selectMode)
{
case SelectMode.Edge:
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<SubdivideEdges>(), true));
break;
default:
EditorAction.Start(new MenuActionSettings(EditorToolbarLoader.GetInstance<SubdivideFaces>(), true));
break;
}";
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: da5baec4bf414efb91d2253bef40dd19
timeCreated: 1469818738
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SplitVertices : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Vert_Split";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Split Vertices",
@"Disconnects vertices that share the same position in space so that they may be moved independently of one another.",
keyCommandAlt, 'X'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex; }
}
public override bool enabled
{
get
{
// This isn't completely accurate, because to check that each selected vertex has coincident vertices is
// unnecessarily expensive for the purposes of this property. So here we handle the most common case,
// where a single vertex is selected with no additional coincident vertices.
return base.enabled
&& MeshSelection.selectedVertexCountObjectMax > 0
&& !(MeshSelection.selectedVertexCountObjectMax == 1 && MeshSelection.selectedCoincidentVertexCountMax == 1);
}
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
int splitCount = 0;
UndoUtility.RecordSelection("Split Vertices");
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
var coincident = mesh.selectedCoincidentVertices;
splitCount += mesh.selectedSharedVerticesCount;
mesh.SplitVertices(coincident);
}
ProBuilderEditor.Refresh();
if (splitCount > 0)
return new ActionResult(ActionResult.Status.Success, "Split " + splitCount + (splitCount > 1 ? " Vertices" : " Vertex"));
return new ActionResult(ActionResult.Status.Failure, "Split Vertices\nInsuffient Vertices Selected");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c339ea7067e6fcd459cb4eac2f0dafb1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,272 @@
using System;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SubdivideEdges : MenuAction
{
Pref<int> m_SubdivisionCount = new Pref<int>("SubdivideEdges.subdivisions", 1);
Pref<int> m_SubdivisionUIMin = new Pref<int>("SubdivideEdges.subdivisionsUIMin", 1);
Pref<int> m_SubdivisionUIMax = new Pref<int>("SubdivideEdges.subdivisionsUIMax", 32);
Pref<bool> m_SubdivisionRangeExpanded = new Pref<bool>("SubdivideEdges.rangeExpanded", false);
const int m_SubdivisionMin = 1;
const int m_SubdivisionMax = 512;
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Edge_Subdivide";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
protected internal override bool hasFileMenuEntry
{
get { return false; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Subdivide Edges",
"Appends evenly spaced new vertices to the selected edges.",
keyCommandAlt, 'S'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Edge; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedEdgeCount > 0; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
Vector2IntField m_RangeField;
IntegerField m_SubdivCount;
SliderInt m_Slider;
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
var line = new VisualElement();
line.style.flexDirection = FlexDirection.Row;
var tooltip = "How many vertices to insert on each selected edge. Vertices will be equally spaced between " +
"one another and the boundaries of the edge.";
var foldout = new Foldout();
foldout.SetValueWithoutNotify(m_SubdivisionRangeExpanded.value);
foldout.RegisterCallback<ChangeEvent<bool>>(OnFoldoutChanged);
m_Slider = new SliderInt("Subdivisions", m_SubdivisionUIMin, m_SubdivisionUIMax);
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
m_Slider.tooltip = tooltip;
m_Slider.RegisterCallback<ChangeEvent<int>>(OnSliderChanged);
m_Slider.style.flexGrow = 1f;
m_SubdivCount = new IntegerField();
m_SubdivCount.isDelayed = PreviewActionManager.delayedPreview;
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
m_SubdivCount.tooltip = tooltip;
m_SubdivCount.RegisterCallback<ChangeEvent<int>>(OnCountChanged);
m_SubdivCount.style.width = 40;
Action onDelayedPreviewChanged = () =>
{
m_SubdivCount.isDelayed = PreviewActionManager.delayedPreview;
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
line.Add(foldout);
line.Add(m_Slider);
line.Add(m_SubdivCount);
root.Add(line);
m_RangeField = new Vector2IntField("Range");
m_RangeField.style.paddingLeft = new StyleLength(25);
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
m_RangeField.Q<Label>().style.flexGrow = 1;
var label = m_RangeField.Q<IntegerField>("unity-x-input").Q<Label>();
label.text = String.Empty;
label.style.flexGrow = 0;
label = m_RangeField.Q<IntegerField>("unity-y-input").Q<Label>();
label.text = String.Empty;
label.style.flexGrow = 0;
var field = m_RangeField.Q<IntegerField>("unity-x-input");
field.isDelayed = true;
field.RegisterCallback<ChangeEvent<int>>(OnMinChanged);
field.style.width = 40f;
var child = field[0];
child.style.display = DisplayStyle.None;
child = m_RangeField.Q<IntegerField>("unity-x-input")[1];
child.style.flexShrink = 0;
field = m_RangeField.Q<IntegerField>("unity-y-input");
field.isDelayed = true;
field.RegisterCallback<ChangeEvent<int>>(OnMaxChanged);
field.style.width = 40f;
child = field[0];
child.style.display = DisplayStyle.None;
child = m_RangeField.Q<IntegerField>("unity-y-input")[1];
child.style.flexShrink = 0;
child = m_RangeField[1][2];
child.style.display = DisplayStyle.None;
m_RangeField.style.display = m_SubdivisionRangeExpanded.value ? DisplayStyle.Flex : DisplayStyle.None;
root.Add(m_RangeField);
return root;
}
void OnFoldoutChanged(ChangeEvent<bool> evt)
{
if(m_SubdivisionRangeExpanded.value == evt.newValue)
return;
m_SubdivisionRangeExpanded.SetValue(evt.newValue);
m_RangeField.style.display = m_SubdivisionRangeExpanded.value ? DisplayStyle.Flex : DisplayStyle.None;
}
void OnSliderChanged(ChangeEvent<int> evt)
{
if(m_SubdivisionCount.value == evt.newValue)
return;
m_SubdivisionCount.SetValue(evt.newValue);
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
PreviewActionManager.UpdatePreview();
}
void OnCountChanged(ChangeEvent<int> evt)
{
if(m_SubdivisionCount.value == evt.newValue)
return;
m_SubdivisionCount.SetValue(evt.newValue < 1 ? 1 : evt.newValue);
if (m_SubdivisionCount.value < m_SubdivisionUIMin.value)
{
m_SubdivisionUIMin.SetValue(m_SubdivisionCount.value);
m_Slider.lowValue = m_SubdivisionUIMin.value;
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
}
if (m_SubdivisionCount.value > m_SubdivisionUIMax.value)
{
m_SubdivisionUIMax.SetValue(m_SubdivisionCount.value);
m_Slider.highValue = m_SubdivisionUIMax.value;
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
}
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
PreviewActionManager.UpdatePreview();
}
void OnMinChanged(ChangeEvent<int> evt)
{
if(m_SubdivisionUIMin.value == evt.newValue)
return;
m_SubdivisionUIMin.SetValue(evt.newValue < 1 ? 1 : evt.newValue);
m_Slider.lowValue = m_SubdivisionUIMin.value;
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
if (m_SubdivisionCount.value < m_SubdivisionUIMin.value)
{
m_SubdivisionCount.SetValue(m_SubdivisionUIMin.value);
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
PreviewActionManager.UpdatePreview();
}
}
void OnMaxChanged(ChangeEvent<int> evt)
{
if(m_SubdivisionUIMax.value == evt.newValue)
return;
m_SubdivisionUIMax.SetValue(evt.newValue);
m_Slider.highValue = m_SubdivisionUIMax.value;
m_RangeField.SetValueWithoutNotify(new Vector2Int(m_SubdivisionUIMin.value, m_SubdivisionUIMax.value));
if (m_SubdivisionCount.value > m_SubdivisionUIMax.value)
{
m_SubdivisionCount.SetValue(m_SubdivisionUIMax.value);
m_SubdivCount.SetValueWithoutNotify(m_SubdivisionCount.value);
m_Slider.SetValueWithoutNotify(m_SubdivisionCount.value);
PreviewActionManager.UpdatePreview();
}
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Subdivide Edge Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUILayout.HelpBox("How many vertices to insert on each selected edge.\n\nVertices will be equally spaced between one another and the boundaries of the edge.", MessageType.Info);
int minUIRange = m_SubdivisionUIMin.value;
int maxUIRange = m_SubdivisionUIMax.value;
bool expanded = m_SubdivisionRangeExpanded.value;
m_SubdivisionCount.value = (int)UI.EditorGUIUtility.FreeSliderWithRange("Subdivisions", (int)m_SubdivisionCount.value, m_SubdivisionMin, m_SubdivisionMax, ref minUIRange, ref maxUIRange, ref expanded);
m_SubdivisionUIMin.value = minUIRange;
m_SubdivisionUIMax.value = maxUIRange;
m_SubdivisionRangeExpanded.value = expanded;
if (EditorGUI.EndChangeCheck())
ProBuilderSettings.Save();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Subdivide Edges"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
int subdivisions = m_SubdivisionCount;
UndoUtility.RecordSelection("Subdivide Edges");
ActionResult result = ActionResult.NoSelection;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
List<Edge> newEdgeSelection = AppendElements.AppendVerticesToEdge(pb, pb.selectedEdges, subdivisions);
if (newEdgeSelection != null)
{
pb.SetSelectedEdges(newEdgeSelection);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
result = new ActionResult(ActionResult.Status.Success, "Subdivide Edge");
}
else
{
result = new ActionResult(ActionResult.Status.Failure, "Failed Subdivide Edge");
}
}
ProBuilderEditor.Refresh();
return result;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 48a7fe196a624a7bae17c8621899b11c
timeCreated: 1459347828
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class SubdivideFaces : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_Subdivide";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
protected internal override bool hasFileMenuEntry
{
get { return false; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Subdivide Faces",
@"Inserts a new vertex at the center of each selected face and creates a new edge from the center of each perimeter edge to the center vertex.",
keyCommandAlt, 'S'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
int success = 0;
UndoUtility.RecordSelection("Subdivide Faces");
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
Face[] faces = pb.Subdivide(pb.selectedFacesInternal);
pb.ToMesh();
if (faces != null)
{
success += pb.selectedFacesInternal.Length;
pb.SetSelectedFaces(faces);
pb.Refresh();
pb.Optimize();
}
}
if (success > 0)
{
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Subdivide " + success + ((success > 1) ? " faces" : " face"));
}
else
{
Debug.LogWarning("Subdivide faces failed - did you not have any faces selected?");
return new ActionResult(ActionResult.Status.Failure, "Subdivide Faces\nNo faces selected");
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bdc593ceb4be46bfab31b9c445ec89a2
timeCreated: 1457564631
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
using UnityEngine;
using System.Linq;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class TriangulateFaces : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Face_Triangulate";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Triangulate Faces",
"Break all selected faces down to triangles."
);
public override SelectMode validSelectModes
{
get { return SelectMode.Face; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedFaceCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
ActionResult res = ActionResult.NoSelection;
UndoUtility.RecordSelection("Triangulate Faces");
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
mesh.ToMesh();
Face[] triangulatedFaces = mesh.ToTriangles(mesh.selectedFacesInternal);
mesh.Refresh();
mesh.Optimize();
mesh.SetSelectedFaces(triangulatedFaces);
res = new ActionResult(ActionResult.Status.Success, string.Format("Triangulated {0} {1}", triangulatedFaces.Length, triangulatedFaces.Length < 2 ? "Face" : "Faces"));
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 729d0f7265af462a8b41492704404f59
timeCreated: 1470747992
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,187 @@
using System;
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.ProBuilder.MeshOperations;
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Actions
{
sealed class WeldVertices : MenuAction
{
Pref<float> m_WeldDistance = new Pref<float>("WeldVertices.weldDistance", .01f);
static readonly GUIContent gc_weldDistance = new GUIContent("Weld Distance", "The maximum distance between two vertices in order to be welded together.");
const float k_MinWeldDistance = .00001f;
public override ToolbarGroup group
{
get { return ToolbarGroup.Geometry; }
}
public override string iconPath => "Toolbar/Vert_Weld";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Weld Vertices",
@"Searches the current selection for vertices that are within the specified distance of on another and merges them into a single vertex.",
keyCommandAlt, 'V'
);
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex; }
}
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedSharedVertexCountObjectMax > 1; }
}
protected override MenuActionState optionsMenuState
{
get { return MenuActionState.VisibleAndEnabled; }
}
public override VisualElement CreateSettingsContent()
{
var root = new VisualElement();
root.style.minWidth = 150;
var floatField = new FloatField(gc_weldDistance.text);
floatField.isDelayed = PreviewActionManager.delayedPreview;
floatField.tooltip = gc_weldDistance.tooltip;
floatField.SetValueWithoutNotify(m_WeldDistance);
floatField.Q("unity-text-input").style.minWidth = 50;
floatField.RegisterCallback<ChangeEvent<float>>(evt =>
{
if (evt.newValue < k_MinWeldDistance)
{
m_WeldDistance.SetValue(k_MinWeldDistance);
floatField.SetValueWithoutNotify(m_WeldDistance);
}
else
m_WeldDistance.SetValue(evt.newValue);
PreviewActionManager.UpdatePreview();
});
Action onDelayedPreviewChanged = () =>
{
floatField.isDelayed = PreviewActionManager.delayedPreview;
};
PreviewActionManager.delayedPreviewChanged += onDelayedPreviewChanged;
root.RegisterCallback<DetachFromPanelEvent>(evt =>
{
PreviewActionManager.delayedPreviewChanged -= onDelayedPreviewChanged;
});
root.Add(floatField);
return root;
}
protected override void OnSettingsGUI()
{
GUILayout.Label("Weld Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
m_WeldDistance.value = EditorGUILayout.FloatField(gc_weldDistance, m_WeldDistance);
if (EditorGUI.EndChangeCheck())
{
if (m_WeldDistance < k_MinWeldDistance)
m_WeldDistance.value = k_MinWeldDistance;
ProBuilderSettings.Save();
}
GUILayout.FlexibleSpace();
if (GUILayout.Button("Weld Vertices"))
PerformAction();
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
ActionResult res = ActionResult.NoSelection;
UndoUtility.RecordSelection("Weld Vertices");
int weldCount = 0;
foreach (ProBuilderMesh mesh in MeshSelection.topInternal)
{
weldCount += mesh.sharedVerticesInternal.Length;
if (mesh.selectedIndexesInternal.Length > 1)
{
mesh.ToMesh();
var selectedVertices = mesh.GetCoincidentVertices(mesh.selectedVertices);
int[] welds = mesh.WeldVertices(mesh.selectedIndexesInternal, m_WeldDistance);
res = welds != null ? new ActionResult(ActionResult.Status.Success, "Weld Vertices") : new ActionResult(ActionResult.Status.Failure, "Failed Weld Vertices");
if (res)
{
var newSelection = welds ?? new int[0] { };
if (MeshValidation.ContainsDegenerateTriangles(mesh))
{
List<int> removedIndices = new List<int>();
var vertexCount = mesh.vertexCount;
if(MeshValidation.RemoveDegenerateTriangles(mesh, removedIndices))
{
if (removedIndices.Count < vertexCount)
{
var newlySelectedVertices = new List<int>();
selectedVertices.Sort();
removedIndices.Sort();
int count = 0;
for (int i = 0; i < selectedVertices.Count; i++)
{
if (count >= removedIndices.Count || selectedVertices[i] != removedIndices[count])
{
newlySelectedVertices.Add(selectedVertices[i] - UnityEngine.ProBuilder.ArrayUtility.NearestIndexPriorToValue(removedIndices, selectedVertices[i]) - 1);
}
else
{
++count;
}
}
newSelection = newlySelectedVertices.ToArray();
}
else
{
newSelection = new int[0];
}
}
mesh.ToMesh();
}
mesh.SetSelectedVertices(newSelection);
}
mesh.Refresh();
mesh.Optimize();
}
weldCount -= mesh.sharedVerticesInternal.Length;
}
ProBuilderEditor.Refresh();
if (res && weldCount > 0)
return new ActionResult(ActionResult.Status.Success, "Weld " + weldCount + (weldCount > 1 ? " Vertices" : " Vertex"));
return new ActionResult(ActionResult.Status.Failure, "Nothing to Weld");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a62bb434134faa6439b1998669525768
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 41e22c09cb0341bf81820025407faa1c
folderAsset: yes
timeCreated: 1457453107
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
using UnityEngine;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ToggleDragRectMode : MenuAction
{
RectSelectMode mode
{
get { return ProBuilderEditor.rectSelectMode; }
set { ProBuilderEditor.rectSelectMode = value; }
}
public override ToolbarGroup group
{
get { return ToolbarGroup.Selection; }
}
public override string iconPath => "Toolbar/DragSelect_Off";
public override Texture2D icon => m_Icons[mode == RectSelectMode.Partial ? 0 : 1];
internal Texture2D[] icons => m_Icons;
Texture2D[] m_Icons;
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
public override int toolbarPriority
{
get { return 0; }
}
public override SelectMode validSelectModes
{
get { return SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Set Drag Rect Mode",
"Sets whether or not a mesh element (edge or face) needs to be completely encompassed by a drag to be selected.\n\nThe default value is Intersect, meaning if any part of the elemnent is touched by the drag rectangle it will be selected."
);
public override string menuTitle { get { return mode == RectSelectMode.Complete ? "Rect: Complete" : "Rect: Intersect"; } }
public ToggleDragRectMode()
{
m_Icons = new Texture2D[]
{
IconUtility.GetIcon("Toolbar/DragSelect_Off"),
IconUtility.GetIcon("Toolbar/DragSelect_On")
};
}
internal override bool IsMenuItemChecked()
{
return mode == RectSelectMode.Complete;
}
protected override ActionResult PerformActionImplementation()
{
mode = InternalUtility.NextEnumValue(mode);
return new ActionResult(ActionResult.Status.Success,
"Set Drag Select\n" + (mode == RectSelectMode.Complete ? "Complete" : "Intersect"));
}
public override bool enabled
{
get
{
return ProBuilderEditor.instance != null
&& ProBuilderEditor.selectMode.ContainsFlag(SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 19fd4a6987194c66bf04e55e19aa466b
timeCreated: 1474401946
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,88 @@
using UnityEngine;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ToggleHandleOrientation : MenuAction
{
Texture2D[] m_Icons;
internal HandleOrientation handleOrientation
{
get { return VertexManipulationTool.handleOrientation; }
set
{
VertexManipulationTool.handleOrientation = value;
ProBuilderEditor.Refresh(false);
}
}
public override ToolbarGroup group
{
get { return ToolbarGroup.Selection; }
}
public override string iconPath => "Modes/ToolHandleElement";
public override Texture2D icon => m_Icons[(int)handleOrientation];
public override int toolbarPriority
{
get { return 0; }
}
public override TooltipContent tooltip
{
get { return k_Tooltips[(int)handleOrientation]; }
}
internal static TooltipContent[] tooltips => k_Tooltips;
static readonly TooltipContent[] k_Tooltips = new TooltipContent[]
{
new TooltipContent("Global", "The transform handle is oriented in a fixed direction."),
new TooltipContent("Local", "The transform handle is aligned with the active object rotation."),
new TooltipContent("Element", "The transform handle is aligned with the active element selection.")
};
public override string menuTitle
{
get { return "Orientation: " + k_Tooltips[(int)handleOrientation].title; }
}
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face; }
}
public override bool hidden
{
get { return false; }
}
const string k_NormalRotationIconPath = "Packages/com.unity.probuilder/Editor Default Resources/Icons/Modes/ToolHandleElement.png";
public ToggleHandleOrientation()
{
m_Icons = new Texture2D[]
{
IconUtility.GetIcon("Modes/ToolHandleGlobal"),
IconUtility.GetIcon("Modes/ToolHandleLocal"),
IconUtility.GetIcon("Modes/ToolHandleElement")
};
}
protected override ActionResult PerformActionImplementation()
{
handleOrientation = InternalUtility.NextEnumValue(handleOrientation);
return new ActionResult(ActionResult.Status.Success, "Set Handle Orientation\n" + k_Tooltips[(int)handleOrientation].title);
}
public override bool enabled
{
get { return ProBuilderEditor.instance != null; }
}
public override void RegisterChangedCallbacks() => VertexManipulationTool.pivotRotationChanged += ContentsChanged;
public override void UnregisterChangedCallbacks() => VertexManipulationTool.pivotRotationChanged -= ContentsChanged;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53d27af1247a7d547aa00100fae732a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using UnityEngine;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ToggleSelectBackFaces : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Selection; }
}
public override string iconPath => "Toolbar/Selection_SelectHidden-Off";
public override Texture2D icon => ProBuilderEditor.backfaceSelectionEnabled ? m_Icons[1] : m_Icons[0];
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
public override int toolbarPriority
{
get { return 1; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Set Hidden Element Selection",
"Setting Hidden Element Selection to On allows you to select faces that are either obscured by geometry or facing away from the scene camera (backfaces).\n\nThe default value is On.");
public override string menuTitle
{
get { return ProBuilderEditor.backfaceSelectionEnabled ? "Select Hidden: On" : "Select Hidden: Off"; }
}
public override SelectMode validSelectModes
{
get
{
return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace;
}
}
internal Texture2D[] icons => m_Icons;
Texture2D[] m_Icons;
public ToggleSelectBackFaces()
{
m_Icons = new Texture2D[]
{
IconUtility.GetIcon("Toolbar/Selection_SelectHidden-Off"),
IconUtility.GetIcon("Toolbar/Selection_SelectHidden-On")
};
}
internal override bool IsMenuItemChecked() => ProBuilderEditor.backfaceSelectionEnabled;
protected override ActionResult PerformActionImplementation()
{
ProBuilderEditor.backfaceSelectionEnabled = !ProBuilderEditor.backfaceSelectionEnabled;
return new ActionResult(ActionResult.Status.Success, "Set Hidden Element Selection\n" + (!ProBuilderEditor.backfaceSelectionEnabled ? "On" : "Off"));
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a61c5819f2b24f3fa336247228361725
timeCreated: 1457456290
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,66 @@
using UnityEngine;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ToggleXRay : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Selection; }
}
public override string iconPath => "Toolbar/Selection_SelectHidden-Off";
public override Texture2D icon => ProBuilderEditor.backfaceSelectionEnabled ? m_Icons[1] : m_Icons[0];
public override TooltipContent tooltip
{
get { return k_Tooltip; }
}
public override int toolbarPriority
{
get { return 1; }
}
// Menu bar item only access for now
public override bool hidden => true;
static readonly TooltipContent k_Tooltip = new TooltipContent
(
"Toggle X-Ray View",
@"When enabled, selected mesh elements that are occluded by geometry will be rendered with a faded appearance.",
keyCommandAlt, keyCommandShift, 'x'
);
public override string menuTitle
{
get { return EditorHandleDrawing.xRay ? "X-Ray: On" : "X-Ray: Off"; }
}
public override SelectMode validSelectModes
{
get { return SelectMode.Vertex | SelectMode.Edge | SelectMode.Face | SelectMode.TextureFace; }
}
readonly Texture2D[] m_Icons;
public ToggleXRay()
{
m_Icons = new Texture2D[]
{
IconUtility.GetIcon("Toolbar/Selection_SelectHidden-Off"),
IconUtility.GetIcon("Toolbar/Selection_SelectHidden-On")
};
}
internal override bool IsMenuItemChecked() => EditorHandleDrawing.xRay;
protected override ActionResult PerformActionImplementation()
{
EditorHandleDrawing.xRay = !EditorHandleDrawing.xRay;
SceneView.RepaintAll();
return new ActionResult(ActionResult.Status.Success, "Set X-Ray Vision\n" + (EditorHandleDrawing.xRay ? "On" : "Off"));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0baa0b710695b7441836813416aa41af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1138c079ebb24a988acb16b8d409db55
folderAsset: yes
timeCreated: 1457563002
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
using UnityEngine.ProBuilder;
using UnityEngine;
using UnityEngine.ProBuilder.MeshOperations;
namespace UnityEditor.ProBuilder.Actions
{
sealed class CenterPivot : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Object; }
}
public override string iconPath => "Toolbar/Pivot_CenterOnObject";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Center Pivot",
@"Set the pivot point of this object to the center of its bounds."
);
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
Object[] objects = new Object[MeshSelection.selectedObjectCount * 2];
for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++)
{
objects[i] = MeshSelection.topInternal[i];
objects[i + c] = MeshSelection.topInternal[i].transform;
}
UndoUtility.RegisterCompleteObjectUndo(objects, "Center Pivot");
foreach (var mesh in MeshSelection.topInternal)
{
TransformUtility.UnparentChildren(mesh.transform);
mesh.CenterPivot(null);
mesh.Optimize();
TransformUtility.ReparentChildren(mesh.transform);
}
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Center Pivot");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 460d1e5b4c054bfc8d012b87f9960bb9
timeCreated: 1461861662
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class ConformObjectNormals : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Object; }
}
public override string iconPath => "Toolbar/Object_ConformNormals";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
public override string menuTitle
{
get { return "Conform Normals"; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Conform Object Normals",
@"Check the object for faces that are flipped in the opposite direction of most other faces, then reverses any dissenters."
);
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
UndoUtility.RecordSelection("Conform Object Normals");
ActionResult res = ActionResult.NoSelection;
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
res = UnityEngine.ProBuilder.MeshOperations.SurfaceTopology.ConformNormals(pb, pb.faces);
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
return res;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 916d519062254e79b6adc87200d2c142
timeCreated: 1457563051
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class FlipObjectNormals : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Object; }
}
public override string iconPath => "Toolbar/Object_FlipNormals";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
public override string menuTitle
{
get { return "Flip Normals"; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Flip Object Normals",
@"Reverse the direction of all faces on the selected objects."
);
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedObjectCount > 0; }
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordSelection("Flip Object Normals");
foreach (ProBuilderMesh pb in MeshSelection.topInternal)
{
foreach (var face in pb.facesInternal)
face.Reverse();
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
return new ActionResult(ActionResult.Status.Success, "Flip Object Normals");
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: add8532bafe747b88ea53556bd2e74e8
timeCreated: 1457563051
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using UnityEngine.ProBuilder;
using UnityEngine;
namespace UnityEditor.ProBuilder.Actions
{
sealed class FreezeTransform : MenuAction
{
public override ToolbarGroup group
{
get { return ToolbarGroup.Object; }
}
public override string iconPath => "Toolbar/Pivot_FreezeTransform";
public override Texture2D icon => IconUtility.GetIcon(iconPath);
public override TooltipContent tooltip
{
get { return s_Tooltip; }
}
static readonly TooltipContent s_Tooltip = new TooltipContent
(
"Freeze Transform",
@"Set the pivot point to world coordinates (0,0,0) and clear all Transform values while keeping the mesh in place."
);
public override bool enabled
{
get { return base.enabled && MeshSelection.selectedObjectCount > 0; }
}
public bool ShouldFlipFaces(Vector3 scale)
{
var globalSign = Mathf.Sign(scale.x) * Mathf.Sign(scale.y) * Mathf.Sign(scale.z);
return globalSign < 0;
}
protected override ActionResult PerformActionImplementation()
{
if (MeshSelection.selectedObjectCount < 1)
return ActionResult.NoSelection;
UndoUtility.RecordMeshAndTransformSelection("Freeze Transforms");
var selection = MeshSelection.topInternal;
Vector3[][] positions = new Vector3[selection.Count][];
for (int i = 0, c = selection.Count; i < c; i++)
positions[i] = selection[i].VerticesInWorldSpace();
for (int i = 0, c = selection.Count; i < c; i++)
{
ProBuilderMesh pb = selection[i];
bool flipFaces = ShouldFlipFaces(pb.transform.localScale);
pb.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
pb.transform.localScale = Vector3.one;
foreach(Face face in pb.facesInternal)
{
face.manualUV = true;
if(flipFaces)
face.Reverse();
}
pb.positions = positions[i];
pb.ToMesh();
pb.Refresh();
pb.Optimize();
}
ProBuilderEditor.Refresh();
SceneView.RepaintAll();
return new ActionResult(ActionResult.Status.Success, "Freeze Transforms");
}
}
}

Some files were not shown because too many files have changed in this diff Show More