diff --git a/Addons/Fbx.cs b/Addons/Fbx.cs index acc835a14..0c30e65cc 100644 --- a/Addons/Fbx.cs +++ b/Addons/Fbx.cs @@ -1,9 +1,11 @@ -// todo Once we drop support for 2018.3, use optional assembly definitions -using System; +#if FBX_EXPORTER + using UnityEditor; using System.Reflection; using System.Linq; +using UnityEditor.Formats.Fbx.Exporter; using UnityEditor.ProBuilder; +using UnityEditor.ProBuilder.Actions; namespace UnityEngine.ProBuilder.Addons.FBX { @@ -26,29 +28,7 @@ class FbxOptions [InitializeOnLoad] static class Fbx { - private static Assembly FbxExporterAssembly - { - get - { - try - { - return Assembly.Load("Unity.Formats.Fbx.Editor"); - } - catch (System.IO.FileNotFoundException) - { - return null; - } - } - } - - static readonly Type[] k_ProBuilderTypes = new Type[] - { - typeof(BezierShape), - typeof(PolyShape), - typeof(Entity) - }; - - static FbxOptions m_FbxOptions = new FbxOptions() { + static FbxOptions s_FbxOptions = new FbxOptions() { quads = true }; @@ -59,52 +39,35 @@ static Fbx() static void TryLoadFbxSupport() { - if (FbxExporterAssembly == null) - { - return; - } - - var modelExporter = FbxExporterAssembly.GetType("UnityEditor.Formats.Fbx.Exporter.ModelExporter"); - var registerMeshCallback = modelExporter.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(x => x.Name == "RegisterMeshCallback").First(x => x.ContainsGenericParameters); - registerMeshCallback = registerMeshCallback.MakeGenericMethod(typeof(ProBuilderMesh)); - - var getMeshForComponent = FbxExporterAssembly.GetTypes() - .Where(t => t.BaseType == typeof(MulticastDelegate) && t.Name.StartsWith("GetMeshForComponent")) - .First(t => t.ContainsGenericParameters); - - getMeshForComponent = getMeshForComponent.MakeGenericType(typeof(ProBuilderMesh)); - var meshDelegate = Delegate.CreateDelegate(getMeshForComponent, typeof(Fbx).GetMethod("GetMeshForComponent", BindingFlags.NonPublic | BindingFlags.Static)); - - registerMeshCallback.Invoke(null, new object[] { meshDelegate, true }); - - m_FbxOptions.quads = ProBuilderSettings.Get("Export::m_FbxQuads", SettingsScope.User, true); + ModelExporter.RegisterMeshCallback(GetMeshForPBComponent, true); + s_FbxOptions.quads = ProBuilderSettings.Get("Export::m_FbxQuads", SettingsScope.User, true); } - static bool GetMeshForComponent(object exporter, ProBuilderMesh pmesh, object node) + static bool GetMeshForPBComponent(ModelExporter exporter, ProBuilderMesh pmesh, Autodesk.Fbx.FbxNode node) { Mesh mesh = new Mesh(); - MeshUtility.Compile(pmesh, mesh, m_FbxOptions.quads ? MeshTopology.Quads : MeshTopology.Triangles); + MeshUtility.Compile(pmesh, mesh, s_FbxOptions.quads ? MeshTopology.Quads : MeshTopology.Triangles); - // using reflection to call: exporter.ExportMesh(mesh, node, pmesh.GetComponent().sharedMaterials) var pMeshRenderer = pmesh.GetComponent(); var sharedMaterials = pMeshRenderer ? pMeshRenderer.sharedMaterials : null; - var exportMeshMethod = exporter.GetType().GetMethod("ExportMesh", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Mesh), node.GetType(), typeof(Material[]) }, null); - exportMeshMethod.Invoke(exporter, new object[] { mesh, node, sharedMaterials }); + + exporter.ExportMesh(mesh, node, sharedMaterials); Object.DestroyImmediate(mesh); - // probuilder can't handle mesh assets that may be externally reloaded, just strip pb stuff for now. - foreach (var type in k_ProBuilderTypes) + //Need to have ExportOptions accessible to remove this reflection + var exporterType = exporter.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .First(x => x.Name == "get_ExportOptions").Invoke(exporter, null).GetType(); + + if(exporterType == typeof(ConvertToPrefabSettingsSerialize)) { - var component = pmesh.GetComponent(type); - if (component != null) - Object.DestroyImmediate(component); + // probuilder can't handle mesh assets that may be externally reloaded, just strip pb stuff for now. + StripProBuilderScripts.DoStrip(pmesh); } - pmesh.preserveMeshAssetOnDestroy = true; - Object.DestroyImmediate(pmesh); - return true; } } } + +#endif diff --git a/Addons/Unity.ProBuilder.AddOns.Editor.asmdef b/Addons/Unity.ProBuilder.AddOns.Editor.asmdef index f1e18cd20..68b428d0b 100644 --- a/Addons/Unity.ProBuilder.AddOns.Editor.asmdef +++ b/Addons/Unity.ProBuilder.AddOns.Editor.asmdef @@ -1,10 +1,12 @@ { "name": "Unity.ProBuilder.AddOns.Editor", + "rootNamespace": "", "references": [ "Unity.ProBuilder", - "Unity.ProBuilder.Editor" + "Unity.ProBuilder.Editor", + "Unity.Formats.Fbx.Editor", + "Autodesk.Fbx" ], - "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], @@ -12,5 +14,14 @@ "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], - "autoReferenced": true -} + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.formats.fbx", + "expression": "4.0.0", + "define": "FBX_EXPORTER" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e36981fa..139b72c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [5.0.4] - 2021-06-08 + +### Bug Fixes + +- [case: 1334017] Fixed errors while exporting a PBShape using FBX Exporter and cleaning export. +- [case: 1332226] Fixed issue where some Gizmos menu items would be missing in projects that have ProBuilder package installed. +- [case: 1324374] Fixed incorrect vertex/edge/face rect selection when mesh's parent is rotated and/or scaled. + ## [5.0.3] - 2021-04-01 ### Bug Fixes diff --git a/Editor/EditorCore/HierarchyListener.cs b/Editor/EditorCore/HierarchyListener.cs index 51c609557..aa98dba16 100644 --- a/Editor/EditorCore/HierarchyListener.cs +++ b/Editor/EditorCore/HierarchyListener.cs @@ -12,14 +12,7 @@ static class HierarchyListener { static HierarchyListener() { - // The inspector icon for ProBuilderMesh is set in the component metadata. However, this also serves as the - // scene view gizmo icon, which we do not want. To avoid drawing an icon for every mesh in the Scene View, - // we simply tell the AnnotationManager to not render the icon. This _does_ put ProBuilderMesh in the - // "Recently Changed" list, but only when it is modified the first time. - // The alternative method of setting an icon is to place it in a folder named "Editor Default Resources/Icons", - // however that requires that the resources directory be in "Assets", which we do not want to do. - EditorUtility.SetGizmoIconEnabled(typeof(ProBuilderMesh), false); - + AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload; // When a prefab is updated, this is raised. For some reason it's // called twice? EditorApplication.hierarchyChanged += HierarchyWindowChanged; @@ -29,6 +22,17 @@ static HierarchyListener() PrefabUtility.prefabInstanceUpdated += PrefabInstanceUpdated; } + static void OnAfterAssemblyReload() + { + // The inspector icon for ProBuilderMesh is set in the component metadata. However, this also serves as the + // scene view gizmo icon, which we do not want. To avoid drawing an icon for every mesh in the Scene View, + // we simply tell the AnnotationManager to not render the icon. This _does_ put ProBuilderMesh in the + // "Recently Changed" list, but only when it is modified the first time. + // The alternative method of setting an icon is to place it in a folder named "Editor Default Resources/Icons", + // however that requires that the resources directory be in "Assets", which we do not want to do. + EditorApplication.delayCall += () => EditorUtility.SetGizmoIconEnabled(typeof(ProBuilderMesh), false); + } + static void PrefabInstanceUpdated(GameObject go) { if (EditorApplication.isPlayingOrWillChangePlaymode) diff --git a/README.md b/README.md index a0478fd6a..96f4f74d2 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ mesh.Optimize(); ## Third Party Licenses -[Third Party Licenses](https://github.com/Unity-Technologies/com.unity.probuilder/blob/master/com.unity.probuilder/Third%20Party%20Notices.md) +[Third Party Licenses](https://github.com/Unity-Technologies/com.unity.probuilder/blob/master/Third%20Party%20Notices.md) ## Contributing diff --git a/Runtime/Core/InternalUtility.cs b/Runtime/Core/InternalUtility.cs index 6ff34eecc..c8d446828 100644 --- a/Runtime/Core/InternalUtility.cs +++ b/Runtime/Core/InternalUtility.cs @@ -39,7 +39,7 @@ public static T[] GetComponents(this IEnumerable transforms) where public static GameObject EmptyGameObjectWithTransform(Transform t) { GameObject go = new GameObject(); - go.transform.position = t.position; + go.transform.localPosition = t.localPosition; go.transform.localRotation = t.localRotation; go.transform.localScale = t.localScale; @@ -50,6 +50,20 @@ public static GameObject EmptyGameObjectWithTransform(Transform t) return go; } + public static GameObject MeshGameObjectWithTransform(string name, Transform t, Mesh mesh, Material mat, bool inheritParent) + { + GameObject go = InternalUtility.EmptyGameObjectWithTransform(t); + go.name = name; + go.AddComponent().sharedMesh = mesh; + go.AddComponent().sharedMaterial = mat; + go.hideFlags = HideFlags.HideAndDontSave; + + if (inheritParent) + go.transform.SetParent(t.parent, false); + + return go; + } + public static T NextEnumValue(this T current) where T : IConvertible { Assert.IsTrue(current is Enum); diff --git a/Runtime/Core/SelectionPickerRenderer.cs b/Runtime/Core/SelectionPickerRenderer.cs index b1a263322..d4ba20a2a 100644 --- a/Runtime/Core/SelectionPickerRenderer.cs +++ b/Runtime/Core/SelectionPickerRenderer.cs @@ -540,9 +540,6 @@ static GameObject[] GenerateFacePickingObjects( { var pb = selection[i]; - GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform); - go.name = pb.name + " (Face Depth Test)"; - Mesh m = new Mesh(); m.vertices = pb.positionsInternal; m.triangles = pb.facesInternal.SelectMany(x => x.indexesInternal).ToArray(); @@ -559,8 +556,8 @@ static GameObject[] GenerateFacePickingObjects( m.colors32 = colors; - go.AddComponent().sharedMesh = m; - go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial; + GameObject go = InternalUtility.MeshGameObjectWithTransform(pb.name + " (Face Depth Test)", pb.transform, m, + BuiltinMaterials.facePickerMaterial, true); pickerObjects[i] = go; } @@ -587,10 +584,11 @@ static void GenerateVertexPickingObjects( { // build vertex billboards var pb = selection[i]; - GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform); - go.name = pb.name + " (Vertex Billboards)"; - go.AddComponent().sharedMesh = BuildVertexMesh(pb, map, ref index); - go.AddComponent().sharedMaterial = BuiltinMaterials.vertexPickerMaterial; + + var mesh = BuildVertexMesh(pb, map, ref index); + GameObject go = InternalUtility.MeshGameObjectWithTransform(pb.name + " (Vertex Billboards)", pb.transform, mesh, + BuiltinMaterials.vertexPickerMaterial, true); + pickerObjects[i] = go; } @@ -602,10 +600,10 @@ static void GenerateVertexPickingObjects( for (int i = 0; i < selectionCount; i++) { var pb = selection[i]; - GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform); - go.name = pb.name + " (Depth Mask)"; - go.AddComponent().sharedMesh = pb.mesh; - go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial; + + GameObject go = InternalUtility.MeshGameObjectWithTransform(pb.name + " (Depth Mask)", pb.transform, pb.mesh, + BuiltinMaterials.facePickerMaterial, true); + depthObjects[i] = go; } } @@ -632,10 +630,11 @@ static void GenerateEdgePickingObjects( { // build edge billboards var pb = selection[i]; - GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform); - go.name = pb.name + " (Edge Billboards)"; - go.AddComponent().sharedMesh = BuildEdgeMesh(pb, map, ref index); - go.AddComponent().sharedMaterial = BuiltinMaterials.edgePickerMaterial; + + var mesh = BuildEdgeMesh(pb, map, ref index); + GameObject go = InternalUtility.MeshGameObjectWithTransform(pb.name + " (Edge Billboards)", pb.transform, mesh, + BuiltinMaterials.edgePickerMaterial, true); + pickerObjects[i] = go; } @@ -646,11 +645,11 @@ static void GenerateEdgePickingObjects( for (int i = 0; i < selectionCount; i++) { var pb = selection[i]; + // copy the select gameobject just for z-write - GameObject go = InternalUtility.EmptyGameObjectWithTransform(pb.transform); - go.name = pb.name + " (Depth Mask)"; - go.AddComponent().sharedMesh = pb.mesh; - go.AddComponent().sharedMaterial = BuiltinMaterials.facePickerMaterial; + GameObject go = InternalUtility.MeshGameObjectWithTransform(pb.name + " (Depth Mask)", pb.transform, pb.mesh, + BuiltinMaterials.facePickerMaterial, true); + depthObjects[i] = go; } } diff --git a/Tests/Editor/Picking/RectSelection.cs b/Tests/Editor/Picking/RectSelection.cs index adc619be1..9e153c019 100644 --- a/Tests/Editor/Picking/RectSelection.cs +++ b/Tests/Editor/Picking/RectSelection.cs @@ -116,6 +116,41 @@ Dictionary> TestFacePick(PickerOptions options) } } + [Test] + public void PickVertices_RotatedParent_DepthTestOn() + { + // Create a parent container with -90 degree rotation around Z + var parent = new GameObject("Parent"); + parent.transform.position = new Vector3(0f, 0f, 0f); + parent.transform.rotation = Quaternion.Euler(0f, 0f, -90f); + + // Create a Cube such that when parented to the container has (6f, 0f, 0f) world position + var cube = ShapeFactory.Instantiate(); + cube.transform.position = new Vector3(0f, 6f, 0f); + cube.transform.SetParent(parent.transform, false); + + // Create a camera and point it to (6f, 0f, 0) looking directly at one of the Cube's faces + camera = new GameObject("Camera", typeof(Camera)).GetComponent(); + camera.transform.position = new Vector3(6f, 0, -6f); + camera.transform.forward = Vector3.forward; + + selectables = new ProBuilderMesh[] + { + cube + }; + + // Attempt full screen rect selection - this should select the 4 vertices of the quad that the camera's facing + var vertices = TestVertexPick(new PickerOptions() { depthTest = true }); + var selection = vertices.FirstOrDefault(); + Assert.IsNotNull(selection); + HashSet selectedElements = selection.Value; + Assert.That(selectedElements.Count, Is.EqualTo(4)); + + UObject.DestroyImmediate(cube.gameObject); + UObject.DestroyImmediate(parent); + UObject.DestroyImmediate(camera.gameObject); + } + [Test] public void PickVertices_DepthTestOn() { diff --git a/package.json b/package.json index 24ab8073e..3f60eb3c8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.probuilder", "displayName": "ProBuilder", - "version": "5.0.3", + "version": "5.0.4", "unity": "2019.4", "description": "Build, edit, and texture custom geometry in Unity. Use ProBuilder for in-scene level design, prototyping, collision meshes, all with on-the-fly play-testing.\n\nAdvanced features include UV editing, vertex colors, parametric shapes, and texture blending. With ProBuilder's model export feature it's easy to tweak your levels in any external 3D modelling suite.\n\nIf you are using URP/HDRP, be careful to also import the corresponding sample project in the section below to get the proper materials.", "keywords": [ diff --git a/validationExceptions.json b/validationExceptions.json new file mode 100644 index 000000000..a826c066c --- /dev/null +++ b/validationExceptions.json @@ -0,0 +1,9 @@ +{ + "Exceptions": + [ + { + "ValidationTest": "API Validation", + "PackageVersion": "5.0.4" + } + ] +} \ No newline at end of file diff --git a/validationExceptions.json.meta b/validationExceptions.json.meta new file mode 100644 index 000000000..4392f0186 --- /dev/null +++ b/validationExceptions.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a1430a41101d44a61a313b6897f438c0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: