From 8303da8cafdf5746d54dbf67ddfc69eca68db9a7 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Sat, 12 May 2018 23:28:44 +0200 Subject: [PATCH 01/26] Introducing new primitive type: Capsule! And code cleanup. --- Gizmos/ButtonCapsule.png | Bin 0 -> 2403 bytes Gizmos/ButtonCapsule.png.meta | 77 + Scripts/Brushes/PrimitiveBrush.cs | 1356 +++++++++-------- .../Inspectors/PrimitiveBrushInspector.cs | 304 ++-- Scripts/Geometry/BrushFactory.cs | 1216 ++++++++------- Scripts/UI/SabreCSGResources.cs | 712 ++++----- 6 files changed, 1983 insertions(+), 1682 deletions(-) create mode 100644 Gizmos/ButtonCapsule.png create mode 100644 Gizmos/ButtonCapsule.png.meta diff --git a/Gizmos/ButtonCapsule.png b/Gizmos/ButtonCapsule.png new file mode 100644 index 0000000000000000000000000000000000000000..68e81710dfb0e71bda21903b6a0e42e7bf9c24b3 GIT binary patch literal 2403 zcmbVO3se(V8V*$|KCrAtOSRTvT+gC1nMa;85GW)F={A@J6nyN~$;>2~nq)#|FbP-- z%2LHztzyfuzFG?1qkycg$K!f{U3Is*#j0z&tKDi_RH_waEurlyt-S*wYg^m(?3^<* zckcb}_kaK6{>fTfUQYa&DPt50MZ6`~oDbfRe8)Wuo?=;97`#UNau*intg<(TbtHAP(&<~+Fr8ZC+?RTJ{YFJvL|0kGbD2L$p(7iXgvB{fpE`{mvEy1TqPG(yPU~1LM(T$5n+2v+t^#@g(1BnD25|gWo|-0E zf+jhHAxRd&NsR_E&^iaA)#wZk+Cdofb|8DO1gLp_9-K+qeJ`u5iUo`sf@bUn4TEr) z)_{WaQ?d!TO)NRkU~U%OutAk|7rCPmdznqKUDv{ z!u>zhM}ot+Xs?q4trmr4e^9w84W$PCe%1!p?rC7daL^UyVYD5Ck7z^Zynxyd+O@E9 zF8IwD@rcEoSy+7Z{Z~`nkIz?DcZfGH+`9eP-jcZGBde6>va(K}e$zMcOj7dSZAnSV zmYuewN~J3yD|b!xdJ zroD4GSO43T8Po>HVi=y(=Uw8vwB^!<^z@LsTdM!d`+3sd&d1w(;XB8B7ks$qtuq(D zI#zVHpl@u(u9K55$9(?KiTE+uouB+M=Gcg8<u*fX)Gw+!QO|xky}VvovGh?>`igg(YBpxR zSpM{e8;k4UUEAk(9aCQGTpOqyTYLQ=eRWT$so78}l=Ewro<^}m$)0D3(v+MZd!kN)~3ddpC*ifpv1;i zhpG%jUSjH|?iZ%tHoW&l@R$3;HH}kKcXl`YV&&W8?Ijo4kFU6;&zi&a;=Z>2 zy2jVeZsIN}lQNIrYM-|G&kc58IA>~l*!rt!|H-deao$t<%>a7N)bWs(wA3z{Q@Kz6 O8)V7OGq+?dUG*()7k4TE literal 0 HcmV?d00001 diff --git a/Gizmos/ButtonCapsule.png.meta b/Gizmos/ButtonCapsule.png.meta new file mode 100644 index 00000000..28251b86 --- /dev/null +++ b/Gizmos/ButtonCapsule.png.meta @@ -0,0 +1,77 @@ +fileFormatVersion: 2 +guid: 1a8135251802e5b4a939a5d911e9a716 +timeCreated: 1526153801 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Brushes/PrimitiveBrush.cs b/Scripts/Brushes/PrimitiveBrush.cs index 8b92f18c..5bd74380 100755 --- a/Scripts/Brushes/PrimitiveBrush.cs +++ b/Scripts/Brushes/PrimitiveBrush.cs @@ -1,4 +1,5 @@ #if UNITY_EDITOR || RUNTIME_CSG + using System; using System.Collections; using System.Collections.Generic; @@ -6,30 +7,32 @@ namespace Sabresaurus.SabreCSG { - public enum PrimitiveBrushType { - Cube, - Sphere, - Cylinder, - Prism, - Custom, - IcoSphere, + public enum PrimitiveBrushType + { + Cube, + Sphere, + Cylinder, + Prism, + Custom, + IcoSphere, Cone, - }; + Capsule + }; - /// - /// A simple brush that represents a single convex shape. - /// + /// + /// A simple brush that represents a single convex shape. + /// [ExecuteInEditMode] public class PrimitiveBrush : Brush { [SerializeField] - Polygon[] polygons; + private Polygon[] polygons; /// /// The prism side count. /// - [SerializeField,HideInInspector] - int prismSideCount = 6; + [SerializeField, HideInInspector] + private int prismSideCount = 6; /// /// Gets or sets the prism side count. @@ -40,8 +43,8 @@ public class PrimitiveBrush : Brush /// /// The cylinder side count. /// - [SerializeField,HideInInspector] - int cylinderSideCount = 20; + [SerializeField, HideInInspector] + private int cylinderSideCount = 20; /// /// Gets or sets the cylinder side count. @@ -53,7 +56,7 @@ public class PrimitiveBrush : Brush /// The cone side count. /// [SerializeField, HideInInspector] - int coneSideCount = 20; + private int coneSideCount = 20; /// /// Gets or sets the cone side count. @@ -61,11 +64,35 @@ public class PrimitiveBrush : Brush /// The cone side count. public int ConeSideCount { get { return coneSideCount; } set { coneSideCount = value; } } + /// + /// The capsule side count. + /// + [SerializeField, HideInInspector] + private int capsuleSideCount = 12; + + /// + /// Gets or sets the capsule side count. + /// + /// The capsule side count. + public int CapsuleSideCount { get { return capsuleSideCount; } set { capsuleSideCount = value; } } + + /// + /// The capsule height. + /// + [SerializeField, HideInInspector] + private float capsuleHeight = 2.0f; + + /// + /// Gets or sets the capsule height. + /// + /// The capsule height. + public float CapsuleHeight { get { return capsuleHeight; } set { capsuleHeight = value; } } + /// /// The sphere side count. /// - [SerializeField,HideInInspector] - int sphereSideCount = 6; + [SerializeField, HideInInspector] + private int sphereSideCount = 6; /// /// Gets or sets the sphere side count. @@ -76,8 +103,8 @@ public class PrimitiveBrush : Brush /// /// The icon sphere iteration count. /// - [SerializeField,HideInInspector] - int icoSphereIterationCount = 1; + [SerializeField, HideInInspector] + private int icoSphereIterationCount = 1; /// /// Gets or sets the icon sphere iteration count. @@ -85,38 +112,41 @@ public class PrimitiveBrush : Brush /// The icon sphere iteration count. public int IcoSphereIterationCount { get { return icoSphereIterationCount; } set { icoSphereIterationCount = value; } } - [SerializeField,HideInInspector] - PrimitiveBrushType brushType = PrimitiveBrushType.Cube; + [SerializeField, HideInInspector] + private PrimitiveBrushType brushType = PrimitiveBrushType.Cube; - [SerializeField,HideInInspector] - bool tracked = false; + [SerializeField, HideInInspector] + private bool tracked = false; - [SerializeField,HideInInspector] - BrushOrder cachedBrushOrder = null; + [SerializeField, HideInInspector] + private BrushOrder cachedBrushOrder = null; - [SerializeField] - BrushBase brushController = null; + [SerializeField] + private BrushBase brushController = null; - int cachedInstanceID = 0; + private int cachedInstanceID = 0; - private CSGModelBase parentCsgModel; + private CSGModelBase parentCsgModel; - [SerializeField] - WorldTransformData cachedWorldTransform; + [SerializeField] + private WorldTransformData cachedWorldTransform; - [SerializeField] - int objectVersionSerialized; + [SerializeField] + private int objectVersionSerialized; - int objectVersionUnserialized; + private int objectVersionUnserialized; - public PrimitiveBrushType BrushType { - get { - return brushType; - } - set { - brushType = value; - } - } + public PrimitiveBrushType BrushType + { + get + { + return brushType; + } + set + { + brushType = value; + } + } public BrushBase BrushController { @@ -128,13 +158,13 @@ public BrushBase BrushController } public bool IsReadOnly - { - get - { - // If this brush is being controlled by something else, it's read only - return (brushController != null); - } - } + { + get + { + // If this brush is being controlled by something else, it's read only + return (brushController != null); + } + } /// /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. @@ -148,18 +178,25 @@ public override string BeautifulBrushName { case PrimitiveBrushType.Cube: return "Cube Brush"; + case PrimitiveBrushType.Sphere: return "Sphere Brush"; + case PrimitiveBrushType.Cylinder: return "Cylinder Brush"; + case PrimitiveBrushType.Prism: return "Prism Brush"; + case PrimitiveBrushType.Custom: return "Custom Brush"; + case PrimitiveBrushType.IcoSphere: return "Icosphere Brush"; + case PrimitiveBrushType.Cone: return "Cone Brush"; + default: return base.BeautifulBrushName; } @@ -167,59 +204,61 @@ public override string BeautifulBrushName } public void SetBrushController(BrushBase brushController) - { - this.brushController = brushController; - } + { + this.brushController = brushController; + } - /// - /// Provide new polygons for the brush - /// - /// New polygons. - /// If the brush type has changed set this to true. For example if you change a cuboid into a wedge, the brush type should no longer be Cube. See BreakTypeRelation() for more details - public void SetPolygons(Polygon[] polygons, bool breakTypeRelation = true) + /// + /// Provide new polygons for the brush + /// + /// New polygons. + /// If the brush type has changed set this to true. For example if you change a cuboid into a wedge, the brush type should no longer be Cube. See BreakTypeRelation() for more details + public void SetPolygons(Polygon[] polygons, bool breakTypeRelation = true) { this.polygons = polygons; Invalidate(true); - if(breakTypeRelation) - { - BreakTypeRelation(); - } + if (breakTypeRelation) + { + BreakTypeRelation(); + } } - /// - /// Brushes retain knowledge of what they were made from, so it's easy to adjust the side count on a prism for example, while retaining some of its transform information. If you start cutting away at a prism using the clip tool for instance, it should stop tracking it as following the initial form. This method allows you to tell the brush it is no longer tracking a base form. - /// - public void BreakTypeRelation() - { - brushType = PrimitiveBrushType.Custom; - } + /// + /// Brushes retain knowledge of what they were made from, so it's easy to adjust the side count on a prism for example, while retaining some of its transform information. If you start cutting away at a prism using the clip tool for instance, it should stop tracking it as following the initial form. This method allows you to tell the brush it is no longer tracking a base form. + /// + public void BreakTypeRelation() + { + brushType = PrimitiveBrushType.Custom; + } #if UNITY_EDITOR - [UnityEditor.Callbacks.DidReloadScripts] - static void OnReloadedScripts() - { - PrimitiveBrush[] brushes = FindObjectsOfType(); - for (int i = 0; i < brushes.Length; i++) - { - brushes[i].UpdateVisibility(); - } - } + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnReloadedScripts() + { + PrimitiveBrush[] brushes = FindObjectsOfType(); + + for (int i = 0; i < brushes.Length; i++) + { + brushes[i].UpdateVisibility(); + } + } + #endif - void Start() + private void Start() { - cachedWorldTransform = new WorldTransformData(transform); - EnsureWellFormed(); + cachedWorldTransform = new WorldTransformData(transform); + EnsureWellFormed(); - Invalidate(false); + Invalidate(false); - if(brushCache == null || brushCache.Polygons == null || brushCache.Polygons.Length == 0) - { - RecachePolygons(true); - } + if (brushCache == null || brushCache.Polygons == null || brushCache.Polygons.Length == 0) + { + RecachePolygons(true); + } #if UNITY_EDITOR #if UNITY_5_5_OR_NEWER @@ -231,56 +270,56 @@ void Start() #endif #endif - objectVersionUnserialized = objectVersionSerialized; + objectVersionUnserialized = objectVersionSerialized; } - /// - /// Reset the polygons to those specified in the brush type. For example if the brush type is a cube, the polygons are reset to a cube. - /// - public void ResetPolygons() - { - if (brushType == PrimitiveBrushType.Cube) - { - polygons = BrushFactory.GenerateCube(); - } - else if (brushType == PrimitiveBrushType.Cylinder) - { - if(cylinderSideCount < 3) - { - cylinderSideCount = 3; - } - polygons = BrushFactory.GenerateCylinder(cylinderSideCount); - } - else if (brushType == PrimitiveBrushType.Sphere) - { - if(sphereSideCount < 3) - { - sphereSideCount = 3; - } - // Lateral only goes halfway around the sphere (180 deg), longitudinal goes all the way (360 deg) - polygons = BrushFactory.GeneratePolarSphere(sphereSideCount, sphereSideCount * 2); - } - else if (brushType == PrimitiveBrushType.IcoSphere) - { - if(icoSphereIterationCount < 0) - { - icoSphereIterationCount = 0; - } - else if(icoSphereIterationCount > 2) - { - icoSphereIterationCount = 2; - } - - polygons = BrushFactory.GenerateIcoSphere(icoSphereIterationCount); - } - else if (brushType == PrimitiveBrushType.Prism) - { - if(prismSideCount < 3) - { - prismSideCount = 3; - } - polygons = BrushFactory.GeneratePrism(prismSideCount); - } + /// + /// Reset the polygons to those specified in the brush type. For example if the brush type is a cube, the polygons are reset to a cube. + /// + public void ResetPolygons() + { + if (brushType == PrimitiveBrushType.Cube) + { + polygons = BrushFactory.GenerateCube(); + } + else if (brushType == PrimitiveBrushType.Cylinder) + { + if (cylinderSideCount < 3) + { + cylinderSideCount = 3; + } + polygons = BrushFactory.GenerateCylinder(cylinderSideCount); + } + else if (brushType == PrimitiveBrushType.Sphere) + { + if (sphereSideCount < 3) + { + sphereSideCount = 3; + } + // Lateral only goes halfway around the sphere (180 deg), longitudinal goes all the way (360 deg) + polygons = BrushFactory.GeneratePolarSphere(sphereSideCount, sphereSideCount * 2); + } + else if (brushType == PrimitiveBrushType.IcoSphere) + { + if (icoSphereIterationCount < 0) + { + icoSphereIterationCount = 0; + } + else if (icoSphereIterationCount > 2) + { + icoSphereIterationCount = 2; + } + + polygons = BrushFactory.GenerateIcoSphere(icoSphereIterationCount); + } + else if (brushType == PrimitiveBrushType.Prism) + { + if (prismSideCount < 3) + { + prismSideCount = 3; + } + polygons = BrushFactory.GeneratePrism(prismSideCount); + } else if (brushType == PrimitiveBrushType.Cone) { if (coneSideCount < 3) @@ -289,360 +328,372 @@ public void ResetPolygons() } polygons = BrushFactory.GenerateCone(coneSideCount); } - else if(brushType == Sabresaurus.SabreCSG.PrimitiveBrushType.Custom) - { - // Do nothing - Debug.LogError("PrimitiveBrushType.Custom is not a valid type for new brush creation"); - } - else - { - throw new NotImplementedException(); - } - } + else if (brushType == PrimitiveBrushType.Capsule) + { + if (capsuleHeight < 0.001f) + { + capsuleHeight = 0.001f; + } + if (capsuleSideCount < 3) + { + capsuleSideCount = 3; + } + polygons = BrushFactory.GenerateCapsule(capsuleHeight, 0.5f, capsuleSideCount); + } + else if (brushType == Sabresaurus.SabreCSG.PrimitiveBrushType.Custom) + { + // Do nothing + Debug.LogError("PrimitiveBrushType.Custom is not a valid type for new brush creation"); + } + else + { + throw new NotImplementedException(); + } + } - void DrawPolygons(Color color, params Polygon[] polygons) - { - GL.Begin(GL.TRIANGLES); - color.a = 0.7f; - GL.Color(color); - - for (int j = 0; j < polygons.Length; j++) - { - Polygon polygon = polygons[j]; - Vector3 position1 = polygon.Vertices[0].Position; - - for (int i = 1; i < polygon.Vertices.Length - 1; i++) - { - GL.Vertex(transform.TransformPoint(position1)); - GL.Vertex(transform.TransformPoint(polygon.Vertices[i].Position)); - GL.Vertex(transform.TransformPoint(polygon.Vertices[i + 1].Position)); - } - } - GL.End(); - } + private void DrawPolygons(Color color, params Polygon[] polygons) + { + GL.Begin(GL.TRIANGLES); + color.a = 0.7f; + GL.Color(color); + + for (int j = 0; j < polygons.Length; j++) + { + Polygon polygon = polygons[j]; + Vector3 position1 = polygon.Vertices[0].Position; + + for (int i = 1; i < polygon.Vertices.Length - 1; i++) + { + GL.Vertex(transform.TransformPoint(position1)); + GL.Vertex(transform.TransformPoint(polygon.Vertices[i].Position)); + GL.Vertex(transform.TransformPoint(polygon.Vertices[i + 1].Position)); + } + } + GL.End(); + } #if UNITY_EDITOR + public void OnRepaint(UnityEditor.SceneView sceneView, Event e) { // Selected brush green outline - if(!isBrushConvex) - { - SabreCSGResources.GetSelectedBrushMaterial().SetPass(0); - DrawPolygons(Color.red, polygons); - } + if (!isBrushConvex) + { + SabreCSGResources.GetSelectedBrushMaterial().SetPass(0); + DrawPolygons(Color.red, polygons); + } } + #endif - public override Polygon[] GenerateTransformedPolygons() - { - Polygon[] polygonsCopy = polygons.DeepCopy(); + public override Polygon[] GenerateTransformedPolygons() + { + Polygon[] polygonsCopy = polygons.DeepCopy(); - Vector3 center = transform.position; - Quaternion rotation = transform.rotation; - Vector3 scale = transform.lossyScale; + Vector3 center = transform.position; + Quaternion rotation = transform.rotation; + Vector3 scale = transform.lossyScale; - for (int i = 0; i < polygons.Length; i++) - { - for (int j = 0; j < polygons[i].Vertices.Length; j++) - { - polygonsCopy[i].Vertices[j].Position = rotation * polygonsCopy[i].Vertices[j].Position.Multiply(scale) + center; - polygonsCopy[i].Vertices[j].Normal = rotation * polygonsCopy[i].Vertices[j].Normal; - } - - // Just updated a load of vertex positions, so make sure the cached plane is updated - polygonsCopy[i].CalculatePlane(); - } + for (int i = 0; i < polygons.Length; i++) + { + for (int j = 0; j < polygons[i].Vertices.Length; j++) + { + polygonsCopy[i].Vertices[j].Position = rotation * polygonsCopy[i].Vertices[j].Position.Multiply(scale) + center; + polygonsCopy[i].Vertices[j].Normal = rotation * polygonsCopy[i].Vertices[j].Normal; + } - return polygonsCopy; - } + // Just updated a load of vertex positions, so make sure the cached plane is updated + polygonsCopy[i].CalculatePlane(); + } - public override void RecalculateBrushCache () - { - RecachePolygons(true); + return polygonsCopy; + } - RecalculateIntersections(); - } + public override void RecalculateBrushCache() + { + RecachePolygons(true); - public override void RecachePolygons(bool markUnbuilt) - { - if(brushCache == null) - { - brushCache = new BrushCache(); - } - Polygon[] cachedTransformedPolygons = GenerateTransformedPolygons(); - Bounds cachedTransformedBounds = GetBoundsTransformed(); - brushCache.Set(mode, cachedTransformedPolygons, cachedTransformedBounds, markUnbuilt); - } + RecalculateIntersections(); + } - public override void RecalculateIntersections() - { - CSGModelBase csgModel = GetCSGModel(); - if(csgModel != null) - { - List brushes = GetCSGModel().GetBrushes(); - - // Tracked brushes at edit time can be added in any order, so sort them - IComparer comparer = new BrushOrderComparer(); - for (int i = 0; i < brushes.Count; i++) - { - if(brushes[i] == null) - { - brushes.RemoveAt(i); - i--; - } - } - - for (int i = 0; i < brushes.Count; i++) - { - brushes[i].UpdateCachedBrushOrder(); - } - - brushes.Sort(comparer); - - RecalculateIntersections(brushes, true); - } - } + public override void RecachePolygons(bool markUnbuilt) + { + if (brushCache == null) + { + brushCache = new BrushCache(); + } + Polygon[] cachedTransformedPolygons = GenerateTransformedPolygons(); + Bounds cachedTransformedBounds = GetBoundsTransformed(); + brushCache.Set(mode, cachedTransformedPolygons, cachedTransformedBounds, markUnbuilt); + } - public override void RecalculateIntersections(List brushes, bool isRootChange) - { - List previousVisualIntersections = brushCache.IntersectingVisualBrushes; - List previousCollisionIntersections = brushCache.IntersectingCollisionBrushes; + public override void RecalculateIntersections() + { + CSGModelBase csgModel = GetCSGModel(); + if (csgModel != null) + { + List brushes = GetCSGModel().GetBrushes(); + + // Tracked brushes at edit time can be added in any order, so sort them + IComparer comparer = new BrushOrderComparer(); + for (int i = 0; i < brushes.Count; i++) + { + if (brushes[i] == null) + { + brushes.RemoveAt(i); + i--; + } + } - List intersectingVisualBrushes = CalculateIntersectingBrushes(this, brushes, false); - List intersectingCollisionBrushes = CalculateIntersectingBrushes(this, brushes, true); + for (int i = 0; i < brushes.Count; i++) + { + brushes[i].UpdateCachedBrushOrder(); + } - brushCache.SetIntersection(intersectingVisualBrushes, intersectingCollisionBrushes); + brushes.Sort(comparer); - if(isRootChange) - { - // Brushes that are either newly intersecting or no longer intersecting, they need to recalculate their - // intersections, but also rebuild - List brushesToRecalcAndRebuild = new List(); - - // Brushes that are still intersecting, these should recalculate their intersections any way in case - // sibling order has changed to make sure their intersection order is still correct - List brushesToRecalculateOnly = new List(); - - // Brushes that are either new or existing intersections - for (int i = 0; i < intersectingVisualBrushes.Count; i++) - { - if(intersectingVisualBrushes[i] != null) - { - if(!previousVisualIntersections.Contains(intersectingVisualBrushes[i])) - { - // It's a newly intersecting brush - if(!brushesToRecalcAndRebuild.Contains(intersectingVisualBrushes[i])) - { - brushesToRecalcAndRebuild.Add(intersectingVisualBrushes[i]); - } - } - else - { - // Intersection was already present - if(!brushesToRecalculateOnly.Contains(intersectingVisualBrushes[i])) - { - brushesToRecalculateOnly.Add(intersectingVisualBrushes[i]); - } - } - } - } - - // Find any brushes that no longer intersect - for (int i = 0; i < previousVisualIntersections.Count; i++) - { - if(previousVisualIntersections[i] != null && !intersectingVisualBrushes.Contains(previousVisualIntersections[i])) - { - if(!brushesToRecalcAndRebuild.Contains(previousVisualIntersections[i])) - { - brushesToRecalcAndRebuild.Add(previousVisualIntersections[i]); - } - } - } - - // Collision Pass - - // Brushes that are either new or existing intersections - for (int i = 0; i < intersectingCollisionBrushes.Count; i++) - { - if(intersectingCollisionBrushes[i] != null) - { - if(!previousCollisionIntersections.Contains(intersectingCollisionBrushes[i])) - { - // It's a newly intersecting brush - if(!brushesToRecalcAndRebuild.Contains(intersectingCollisionBrushes[i])) - { - brushesToRecalcAndRebuild.Add(intersectingCollisionBrushes[i]); - } - } - else - { - // Intersection was already present - if(!brushesToRecalculateOnly.Contains(intersectingCollisionBrushes[i])) - { - brushesToRecalculateOnly.Add(intersectingCollisionBrushes[i]); - } - } - } - } - - // Find any brushes that no longer intersect - for (int i = 0; i < previousCollisionIntersections.Count; i++) - { - if(previousCollisionIntersections[i] != null && !intersectingCollisionBrushes.Contains(previousCollisionIntersections[i])) - { - if(!brushesToRecalcAndRebuild.Contains(previousCollisionIntersections[i])) - { - brushesToRecalcAndRebuild.Add(previousCollisionIntersections[i]); - } - } - } - - // Notify brushes that are either newly intersecting or no longer intersecting that they need to recalculate and rebuild - for (int i = 0; i < brushesToRecalcAndRebuild.Count; i++) - { - // Brush intersection has changed - brushesToRecalcAndRebuild[i].RecalculateIntersections(brushes, false); - // Brush needs to be built - brushesToRecalcAndRebuild[i].BrushCache.SetUnbuilt(); - } - - // Brushes that remain intersecting should recalc their intersection lists just in case sibling order has changed - for (int i = 0; i < brushesToRecalculateOnly.Count; i++) - { - // Brush intersection has changed - brushesToRecalculateOnly[i].RecalculateIntersections(brushes, false); - } - } - } + RecalculateIntersections(brushes, true); + } + } + + public override void RecalculateIntersections(List brushes, bool isRootChange) + { + List previousVisualIntersections = brushCache.IntersectingVisualBrushes; + List previousCollisionIntersections = brushCache.IntersectingCollisionBrushes; + + List intersectingVisualBrushes = CalculateIntersectingBrushes(this, brushes, false); + List intersectingCollisionBrushes = CalculateIntersectingBrushes(this, brushes, true); + + brushCache.SetIntersection(intersectingVisualBrushes, intersectingCollisionBrushes); + + if (isRootChange) + { + // Brushes that are either newly intersecting or no longer intersecting, they need to recalculate their + // intersections, but also rebuild + List brushesToRecalcAndRebuild = new List(); + + // Brushes that are still intersecting, these should recalculate their intersections any way in case + // sibling order has changed to make sure their intersection order is still correct + List brushesToRecalculateOnly = new List(); + + // Brushes that are either new or existing intersections + for (int i = 0; i < intersectingVisualBrushes.Count; i++) + { + if (intersectingVisualBrushes[i] != null) + { + if (!previousVisualIntersections.Contains(intersectingVisualBrushes[i])) + { + // It's a newly intersecting brush + if (!brushesToRecalcAndRebuild.Contains(intersectingVisualBrushes[i])) + { + brushesToRecalcAndRebuild.Add(intersectingVisualBrushes[i]); + } + } + else + { + // Intersection was already present + if (!brushesToRecalculateOnly.Contains(intersectingVisualBrushes[i])) + { + brushesToRecalculateOnly.Add(intersectingVisualBrushes[i]); + } + } + } + } + + // Find any brushes that no longer intersect + for (int i = 0; i < previousVisualIntersections.Count; i++) + { + if (previousVisualIntersections[i] != null && !intersectingVisualBrushes.Contains(previousVisualIntersections[i])) + { + if (!brushesToRecalcAndRebuild.Contains(previousVisualIntersections[i])) + { + brushesToRecalcAndRebuild.Add(previousVisualIntersections[i]); + } + } + } + + // Collision Pass + + // Brushes that are either new or existing intersections + for (int i = 0; i < intersectingCollisionBrushes.Count; i++) + { + if (intersectingCollisionBrushes[i] != null) + { + if (!previousCollisionIntersections.Contains(intersectingCollisionBrushes[i])) + { + // It's a newly intersecting brush + if (!brushesToRecalcAndRebuild.Contains(intersectingCollisionBrushes[i])) + { + brushesToRecalcAndRebuild.Add(intersectingCollisionBrushes[i]); + } + } + else + { + // Intersection was already present + if (!brushesToRecalculateOnly.Contains(intersectingCollisionBrushes[i])) + { + brushesToRecalculateOnly.Add(intersectingCollisionBrushes[i]); + } + } + } + } + + // Find any brushes that no longer intersect + for (int i = 0; i < previousCollisionIntersections.Count; i++) + { + if (previousCollisionIntersections[i] != null && !intersectingCollisionBrushes.Contains(previousCollisionIntersections[i])) + { + if (!brushesToRecalcAndRebuild.Contains(previousCollisionIntersections[i])) + { + brushesToRecalcAndRebuild.Add(previousCollisionIntersections[i]); + } + } + } + + // Notify brushes that are either newly intersecting or no longer intersecting that they need to recalculate and rebuild + for (int i = 0; i < brushesToRecalcAndRebuild.Count; i++) + { + // Brush intersection has changed + brushesToRecalcAndRebuild[i].RecalculateIntersections(brushes, false); + // Brush needs to be built + brushesToRecalcAndRebuild[i].BrushCache.SetUnbuilt(); + } + // Brushes that remain intersecting should recalc their intersection lists just in case sibling order has changed + for (int i = 0; i < brushesToRecalculateOnly.Count; i++) + { + // Brush intersection has changed + brushesToRecalculateOnly[i].RecalculateIntersections(brushes, false); + } + } + } - // Fired by the CSG Model + // Fired by the CSG Model public override void OnUndoRedoPerformed() - { - if(objectVersionSerialized != objectVersionUnserialized) - { - Invalidate(true); - } + { + if (objectVersionSerialized != objectVersionUnserialized) + { + Invalidate(true); + } } - void EnsureWellFormed() + private void EnsureWellFormed() { if (polygons == null || polygons.Length == 0) { - // Reset custom brushes back to a cube - if(brushType == PrimitiveBrushType.Custom) - { - brushType = PrimitiveBrushType.Cube; - } - - ResetPolygons(); - } - } - -// public void OnDrawGizmosSelected() -// { -// // Ensure Edit Mode is on -// GetCSGModel().EditMode = true; -// } -// -// public void OnDrawGizmos() -// { -// EnsureWellFormed(); -// -// // Gizmos.color = Color.green; -// // for (int i = 0; i < PolygonFactory.hackyDisplay1.Count; i++) -// // { -// // Gizmos.DrawSphere(PolygonFactory.hackyDisplay1[i], 0.2f); -// // } -// // -// // Gizmos.color = Color.red; -// // for (int i = 0; i < PolygonFactory.hackyDisplay2.Count; i++) -// // { -// // Gizmos.DrawSphere(PolygonFactory.hackyDisplay2[i], 0.2f); -// // } -// } - - - void OnDisable() - { - // OnDisable is called on recompilation, so make sure we only process when needed - if(this.enabled == false || (gameObject.activeInHierarchy == false && transform.root.gameObject.activeInHierarchy == true)) - { - GetCSGModel().OnBrushDisabled(this); - // Copy the intersections list since the source list will change as we call recalculate on other brushes - List intersectingVisualBrushes = new List(brushCache.IntersectingVisualBrushes); - - for (int i = 0; i < intersectingVisualBrushes.Count; i++) - { - if(intersectingVisualBrushes[i] != null) - { - intersectingVisualBrushes[i].RecalculateIntersections(); - intersectingVisualBrushes[i].BrushCache.SetUnbuilt(); - } - } - } - } + // Reset custom brushes back to a cube + if (brushType == PrimitiveBrushType.Custom) + { + brushType = PrimitiveBrushType.Cube; + } - void UpdateTracking() - { - CSGModelBase parentCSGModel = GetCSGModel(); + ResetPolygons(); + } + } - // Make sure the CSG Model knows about this brush. If they duplicated a brush in the hierarchy then this - // allows us to make sure the CSG Model knows about it - if(parentCSGModel != null) - { - bool newBrush = parentCSGModel.TrackBrush(this); - - if(newBrush) - { - MeshFilter meshFilter = gameObject.AddOrGetComponent(); - - meshFilter.sharedMesh = new Mesh(); - brushCache = new BrushCache(); - EnsureWellFormed(); - RecalculateBrushCache(); - } - Invalidate(false); - tracked = true; - } - else - { - tracked = false; - } - } + // public void OnDrawGizmosSelected() + // { + // // Ensure Edit Mode is on + // GetCSGModel().EditMode = true; + // } + // + // public void OnDrawGizmos() + // { + // EnsureWellFormed(); + // + // // Gizmos.color = Color.green; + // // for (int i = 0; i < PolygonFactory.hackyDisplay1.Count; i++) + // // { + // // Gizmos.DrawSphere(PolygonFactory.hackyDisplay1[i], 0.2f); + // // } + // // + // // Gizmos.color = Color.red; + // // for (int i = 0; i < PolygonFactory.hackyDisplay2.Count; i++) + // // { + // // Gizmos.DrawSphere(PolygonFactory.hackyDisplay2[i], 0.2f); + // // } + // } + + private void OnDisable() + { + // OnDisable is called on recompilation, so make sure we only process when needed + if (this.enabled == false || (gameObject.activeInHierarchy == false && transform.root.gameObject.activeInHierarchy == true)) + { + GetCSGModel().OnBrushDisabled(this); + // Copy the intersections list since the source list will change as we call recalculate on other brushes + List intersectingVisualBrushes = new List(brushCache.IntersectingVisualBrushes); - void OnEnable() - { - UpdateTracking(); - } + for (int i = 0; i < intersectingVisualBrushes.Count; i++) + { + if (intersectingVisualBrushes[i] != null) + { + intersectingVisualBrushes[i].RecalculateIntersections(); + intersectingVisualBrushes[i].BrushCache.SetUnbuilt(); + } + } + } + } + + private void UpdateTracking() + { + CSGModelBase parentCSGModel = GetCSGModel(); + + // Make sure the CSG Model knows about this brush. If they duplicated a brush in the hierarchy then this + // allows us to make sure the CSG Model knows about it + if (parentCSGModel != null) + { + bool newBrush = parentCSGModel.TrackBrush(this); + + if (newBrush) + { + MeshFilter meshFilter = gameObject.AddOrGetComponent(); + + meshFilter.sharedMesh = new Mesh(); + brushCache = new BrushCache(); + EnsureWellFormed(); + RecalculateBrushCache(); + } + Invalidate(false); + tracked = true; + } + else + { + tracked = false; + } + } + + private void OnEnable() + { + UpdateTracking(); + } protected override void Update() { base.Update(); - if(!tracked) - { - UpdateTracking(); - } + if (!tracked) + { + UpdateTracking(); + } - // If the transform has changed, needs rebuild - if(cachedWorldTransform.SetFromTransform(transform)) - { - Invalidate(true); - } - } + // If the transform has changed, needs rebuild + if (cachedWorldTransform.SetFromTransform(transform)) + { + Invalidate(true); + } + } - /// - /// Tells the brush it has changed - /// - /// If set to true polygons will be recached. + /// + /// Tells the brush it has changed + /// + /// If set to true polygons will be recached. public override void Invalidate(bool polygonsChanged) { - base.Invalidate(polygonsChanged); - if(!gameObject.activeInHierarchy) - { - return; - } + base.Invalidate(polygonsChanged); + if (!gameObject.activeInHierarchy) + { + return; + } // previous versions of sabrecsg used to use mesh colliders for ray collision, but that's no longer the case so we clean them up. MeshCollider[] meshColliders = GetComponents(); @@ -650,7 +701,6 @@ public override void Invalidate(bool polygonsChanged) for (int i = 0; i < meshColliders.Length; i++) DestroyImmediate(meshColliders[i]); - // Make sure there is a mesh filter on this object MeshFilter meshFilter = gameObject.AddOrGetComponent(); MeshRenderer meshRenderer = gameObject.AddOrGetComponent(); @@ -715,34 +765,34 @@ public override void Invalidate(bool polygonsChanged) meshRenderer.sharedMaterial = material; } #endif -// isBrushConvex = GeometryHelper.IsBrushConvex(polygons); + // isBrushConvex = GeometryHelper.IsBrushConvex(polygons); - if(polygonsChanged) - { - RecalculateBrushCache(); - } + if (polygonsChanged) + { + RecalculateBrushCache(); + } - UpdateVisibility(); + UpdateVisibility(); - objectVersionSerialized++; - objectVersionUnserialized = objectVersionSerialized; + objectVersionSerialized++; + objectVersionUnserialized = objectVersionSerialized; - if(cachedWorldTransform == null) - { - cachedWorldTransform = new WorldTransformData(transform); - } - cachedWorldTransform.SetFromTransform(transform); + if (cachedWorldTransform == null) + { + cachedWorldTransform = new WorldTransformData(transform); + } + cachedWorldTransform.SetFromTransform(transform); } - public override void UpdateVisibility() + public override void UpdateVisibility() { - // Display brush if the CSG Model says to or if the brush isn't under a CSG Model - CSGModelBase csgModel = GetCSGModel(); - bool isVisible = false; - if(csgModel == null || csgModel.AreBrushesVisible) - { - isVisible = true; - } + // Display brush if the CSG Model says to or if the brush isn't under a CSG Model + CSGModelBase csgModel = GetCSGModel(); + bool isVisible = false; + if (csgModel == null || csgModel.AreBrushesVisible) + { + isVisible = true; + } MeshRenderer meshRenderer = GetComponent(); if (meshRenderer != null) { @@ -750,58 +800,58 @@ public override void UpdateVisibility() } } -// public Polygon GetPolygonFromTriangle(int triangleIndex) -// { -// int polygonIndex = polygonIndices[triangleIndex]; -// return polygons[polygonIndex]; -// } + // public Polygon GetPolygonFromTriangle(int triangleIndex) + // { + // int polygonIndex = polygonIndices[triangleIndex]; + // return polygons[polygonIndex]; + // } public override Bounds GetBounds() { - if (polygons != null && polygons.Length > 0) - { - Bounds bounds = new Bounds(polygons[0].Vertices[0].Position, Vector3.zero); - - for (int i = 0; i < polygons.Length; i++) - { - for (int j = 0; j < polygons[i].Vertices.Length; j++) - { - bounds.Encapsulate(polygons[i].Vertices[j].Position); - } - } - return bounds; - } - else - { - return new Bounds(Vector3.zero, Vector3.zero); - } + if (polygons != null && polygons.Length > 0) + { + Bounds bounds = new Bounds(polygons[0].Vertices[0].Position, Vector3.zero); + + for (int i = 0; i < polygons.Length; i++) + { + for (int j = 0; j < polygons[i].Vertices.Length; j++) + { + bounds.Encapsulate(polygons[i].Vertices[j].Position); + } + } + return bounds; + } + else + { + return new Bounds(Vector3.zero, Vector3.zero); + } } - public override void SetBounds (Bounds newBounds) - { - throw new NotImplementedException (); - } + public override void SetBounds(Bounds newBounds) + { + throw new NotImplementedException(); + } - public override Bounds GetBoundsTransformed() - { - if (polygons.Length > 0) - { - Bounds bounds = new Bounds(transform.TransformPoint(polygons[0].Vertices[0].Position), Vector3.zero); - - for (int i = 0; i < polygons.Length; i++) - { - for (int j = 0; j < polygons[i].Vertices.Length; j++) - { - bounds.Encapsulate(transform.TransformPoint(polygons[i].Vertices[j].Position)); - } - } - return bounds; - } - else - { - return new Bounds(Vector3.zero, Vector3.zero); - } - } + public override Bounds GetBoundsTransformed() + { + if (polygons.Length > 0) + { + Bounds bounds = new Bounds(transform.TransformPoint(polygons[0].Vertices[0].Position), Vector3.zero); + + for (int i = 0; i < polygons.Length; i++) + { + for (int j = 0; j < polygons[i].Vertices.Length; j++) + { + bounds.Encapsulate(transform.TransformPoint(polygons[i].Vertices[j].Position)); + } + } + return bounds; + } + else + { + return new Bounds(Vector3.zero, Vector3.zero); + } + } public override Bounds GetBoundsLocalTo(Transform otherTransform) { @@ -825,99 +875,99 @@ public override Bounds GetBoundsLocalTo(Transform otherTransform) } public float CalculateExtentsInAxis(Vector3 worldAxis) - { - // Transform the world axis direction to local - Vector3 localAxis = transform.InverseTransformDirection(worldAxis); + { + // Transform the world axis direction to local + Vector3 localAxis = transform.InverseTransformDirection(worldAxis); - float minDot = Vector3.Dot(polygons[0].Vertices[0].Position, localAxis); - float maxDot = minDot; + float minDot = Vector3.Dot(polygons[0].Vertices[0].Position, localAxis); + float maxDot = minDot; - for (int i = 0; i < polygons.Length; i++) - { - for (int j = 0; j < polygons[i].Vertices.Length; j++) - { - float dot = Vector3.Dot(polygons[i].Vertices[j].Position, localAxis); - minDot = Mathf.Min(dot, minDot); - maxDot = Mathf.Max(dot, maxDot); - } - } + for (int i = 0; i < polygons.Length; i++) + { + for (int j = 0; j < polygons[i].Vertices.Length; j++) + { + float dot = Vector3.Dot(polygons[i].Vertices[j].Position, localAxis); + minDot = Mathf.Min(dot, minDot); + maxDot = Mathf.Max(dot, maxDot); + } + } - return maxDot - minDot; - } - - public override int[] GetPolygonIDs () - { - int[] ids = new int[polygons.Length]; - for (int i = 0; i < polygons.Length; i++) - { - ids[i] = polygons[i].UniqueIndex; - } - return ids; - } + return maxDot - minDot; + } - public override Polygon[] GetPolygons () - { - return polygons; - } + public override int[] GetPolygonIDs() + { + int[] ids = new int[polygons.Length]; + for (int i = 0; i < polygons.Length; i++) + { + ids[i] = polygons[i].UniqueIndex; + } + return ids; + } - public override int AssignUniqueIDs (int startingIndex) - { - for (int i = 0; i < polygons.Length; i++) - { - int uniqueIndex = startingIndex + i; - polygons[i].UniqueIndex = uniqueIndex; - } + public override Polygon[] GetPolygons() + { + return polygons; + } - int assignedCount = polygons.Length; - - return assignedCount; - } + public override int AssignUniqueIDs(int startingIndex) + { + for (int i = 0; i < polygons.Length; i++) + { + int uniqueIndex = startingIndex + i; + polygons[i].UniqueIndex = uniqueIndex; + } - /// - /// Resets the pivot to the center of the brush. The world position of vertices remains unchanged, but the brush position and local vertex positions are updated so that the pivot is at the center. - /// - public void ResetPivot() - { - Vector3 delta = GetBounds().center; + int assignedCount = polygons.Length; - for (int i = 0; i < polygons.Length; i++) - { - for (int j = 0; j < polygons[i].Vertices.Length; j++) - { - polygons[i].Vertices[j].Position -= delta; - } - } + return assignedCount; + } - // Bounds is aligned with the object - transform.Translate(delta.Multiply(transform.localScale)); + /// + /// Resets the pivot to the center of the brush. The world position of vertices remains unchanged, but the brush position and local vertex positions are updated so that the pivot is at the center. + /// + public void ResetPivot() + { + Vector3 delta = GetBounds().center; - // Counter the delta offset - Transform[] childTransforms = transform.GetComponentsInChildren(true); + for (int i = 0; i < polygons.Length; i++) + { + for (int j = 0; j < polygons[i].Vertices.Length; j++) + { + polygons[i].Vertices[j].Position -= delta; + } + } - for (int i = 0; i < childTransforms.Length; i++) - { - if(childTransforms[i] != transform) - { - childTransforms[i].Translate(-delta); - } - } + // Bounds is aligned with the object + transform.Translate(delta.Multiply(transform.localScale)); - // Only invalidate if it's actually been realigned - if(delta != Vector3.zero) - { - Invalidate(true); - } - } - - /// - /// Duplicates the brush game object and returns the new object. - /// - /// The game object of the new brush. - public GameObject Duplicate() - { - GameObject newObject = Instantiate(this.gameObject); + // Counter the delta offset + Transform[] childTransforms = transform.GetComponentsInChildren(true); + + for (int i = 0; i < childTransforms.Length; i++) + { + if (childTransforms[i] != transform) + { + childTransforms[i].Translate(-delta); + } + } + + // Only invalidate if it's actually been realigned + if (delta != Vector3.zero) + { + Invalidate(true); + } + } + + /// + /// Duplicates the brush game object and returns the new object. + /// + /// The game object of the new brush. + public GameObject Duplicate() + { + GameObject newObject = Instantiate(this.gameObject); - newObject.name = this.gameObject.name; + newObject.name = this.gameObject.name; // copy the scale with checks for scaled parents and being of a scaled child. newObject.transform.parent = null; @@ -928,72 +978,72 @@ public GameObject Duplicate() // once parented they will end up at world position 0,0,0 if this step isn't done. newObject.transform.position = this.transform.position; - return newObject; - } + return newObject; + } - public override void PrepareToBuild(List brushes, bool forceRebuild) - { - if(forceRebuild) - { - brushCache.SetUnbuilt(); - RecachePolygons(true); - RecalculateIntersections(brushes, false); - } - } + public override void PrepareToBuild(List brushes, bool forceRebuild) + { + if (forceRebuild) + { + brushCache.SetUnbuilt(); + RecachePolygons(true); + RecalculateIntersections(brushes, false); + } + } - /// - /// Get the CSG Model that the brush is under - /// - /// The CSG Model. - public CSGModelBase GetCSGModel() - { - if (parentCsgModel == null) - { - CSGModelBase[] models = transform.GetComponentsInParent(true); - if(models.Length > 0) - { - parentCsgModel = models[0]; - } - } - return parentCsgModel; - } + /// + /// Get the CSG Model that the brush is under + /// + /// The CSG Model. + public CSGModelBase GetCSGModel() + { + if (parentCsgModel == null) + { + CSGModelBase[] models = transform.GetComponentsInParent(true); + if (models.Length > 0) + { + parentCsgModel = models[0]; + } + } + return parentCsgModel; + } - public override void UpdateCachedBrushOrder () - { - Transform csgModelTransform = GetCSGModel().transform; + public override void UpdateCachedBrushOrder() + { + Transform csgModelTransform = GetCSGModel().transform; - List reversePositions = new List(); + List reversePositions = new List(); - Transform traversedTransform = transform; + Transform traversedTransform = transform; - reversePositions.Add(traversedTransform.GetSiblingIndex()); + reversePositions.Add(traversedTransform.GetSiblingIndex()); - while(traversedTransform.parent != null && traversedTransform.parent != csgModelTransform) - { - traversedTransform = traversedTransform.parent; - reversePositions.Add(traversedTransform.GetSiblingIndex()); - } + while (traversedTransform.parent != null && traversedTransform.parent != csgModelTransform) + { + traversedTransform = traversedTransform.parent; + reversePositions.Add(traversedTransform.GetSiblingIndex()); + } - BrushOrder brushOrder = new BrushOrder(); - int count = reversePositions.Count; - brushOrder.Position = new int[count]; - for (int i = 0; i < count; i++) - { - brushOrder.Position[i] = reversePositions[count-1-i]; - } + BrushOrder brushOrder = new BrushOrder(); + int count = reversePositions.Count; + brushOrder.Position = new int[count]; + for (int i = 0; i < count; i++) + { + brushOrder.Position[i] = reversePositions[count - 1 - i]; + } - cachedBrushOrder = brushOrder; - } + cachedBrushOrder = brushOrder; + } - public override BrushOrder GetBrushOrder () - { - if(cachedBrushOrder == null) - { - UpdateCachedBrushOrder(); - } + public override BrushOrder GetBrushOrder() + { + if (cachedBrushOrder == null) + { + UpdateCachedBrushOrder(); + } - return cachedBrushOrder; - } + return cachedBrushOrder; + } #if (UNITY_5_0 || UNITY_5_1) void OnDrawGizmosSelected() diff --git a/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs b/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs index 603dd9a4..e12cb058 100644 --- a/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs +++ b/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs @@ -6,90 +6,95 @@ namespace Sabresaurus.SabreCSG { - [CanEditMultipleObjects] + [CanEditMultipleObjects] [CustomEditor(typeof(PrimitiveBrush))] - public class PrimitiveBrushInspector : BrushBaseInspector + public class PrimitiveBrushInspector : BrushBaseInspector { - string scaleString = "1"; - string resizeString = "1"; + private string scaleString = "1"; + private string resizeString = "1"; - Mesh sourceMesh = null; + private Mesh sourceMesh = null; - SerializedProperty prismSideCountProp; - SerializedProperty cylinderSideCountProp; - SerializedProperty sphereSideCountProp; - SerializedProperty icoSphereIterationCountProp; - SerializedProperty coneSideCountProp; + private SerializedProperty prismSideCountProp; + private SerializedProperty cylinderSideCountProp; + private SerializedProperty sphereSideCountProp; + private SerializedProperty icoSphereIterationCountProp; + private SerializedProperty coneSideCountProp; + private SerializedProperty capsuleSideCountProp; + private SerializedProperty capsuleHeightProp; - float shellDistance = 0; + private float shellDistance = 0; - protected override void OnEnable () - { - base.OnEnable (); - // Setup the SerializedProperties. - prismSideCountProp = serializedObject.FindProperty ("prismSideCount"); + protected override void OnEnable() + { + base.OnEnable(); + // Setup the SerializedProperties. + prismSideCountProp = serializedObject.FindProperty("prismSideCount"); + + cylinderSideCountProp = serializedObject.FindProperty("cylinderSideCount"); + + sphereSideCountProp = serializedObject.FindProperty("sphereSideCount"); + + coneSideCountProp = serializedObject.FindProperty("coneSideCount"); + + capsuleSideCountProp = serializedObject.FindProperty("capsuleSideCount"); + capsuleHeightProp = serializedObject.FindProperty("capsuleHeight"); + + icoSphereIterationCountProp = serializedObject.FindProperty("icoSphereIterationCount"); + } + + private void ChangeBrushesToType(PrimitiveBrushType newType) + { + Undo.RecordObjects(targets, "Change Brush Type"); + PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); + foreach (PrimitiveBrush brush in brushes) + { + Bounds localBounds = brush.GetBounds(); + brush.BrushType = newType; + brush.ResetPolygons(); - cylinderSideCountProp = serializedObject.FindProperty ("cylinderSideCount"); + if (localBounds.size != new Vector3(2, 2, 2)) + { + BrushUtility.Resize(brush, localBounds.size); + } + else + { + brush.Invalidate(true); + } + } + } - sphereSideCountProp = serializedObject.FindProperty ("sphereSideCount"); + private void ResetPolygonsKeepScale() + { + PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); + foreach (PrimitiveBrush brush in brushes) + { + Bounds localBounds = brush.GetBounds(); + brush.ResetPolygons(); - coneSideCountProp = serializedObject.FindProperty ("coneSideCount"); + if (localBounds.size != new Vector3(2, 2, 2)) + { + BrushUtility.Resize(brush, localBounds.size); + } + else + { + brush.Invalidate(true); + } + } + } - icoSphereIterationCountProp = serializedObject.FindProperty ("icoSphereIterationCount"); + private void ResetBounds() + { + // Reset the bounds to a 2,2,2 cube + Undo.RecordObjects(targets, "Reset Bounds"); + PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); + foreach (PrimitiveBrush brush in brushes) + { + BrushUtility.Resize(brush, new Vector3(2, 2, 2)); + } } - private void ChangeBrushesToType(PrimitiveBrushType newType) - { - Undo.RecordObjects(targets, "Change Brush Type"); - PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); - foreach (PrimitiveBrush brush in brushes) - { - Bounds localBounds = brush.GetBounds(); - brush.BrushType = newType; - brush.ResetPolygons(); - - if(localBounds.size != new Vector3(2, 2, 2)) - { - BrushUtility.Resize(brush, localBounds.size); - } - else - { - brush.Invalidate(true); - } - } - } - - private void ResetPolygonsKeepScale() - { - PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); - foreach (PrimitiveBrush brush in brushes) - { - Bounds localBounds = brush.GetBounds(); - brush.ResetPolygons(); - - if(localBounds.size != new Vector3(2, 2, 2)) - { - BrushUtility.Resize(brush, localBounds.size); - } - else - { - brush.Invalidate(true); - } - } - } - - private void ResetBounds() - { - // Reset the bounds to a 2,2,2 cube - Undo.RecordObjects(targets, "Reset Bounds"); - PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); - foreach (PrimitiveBrush brush in brushes) - { - BrushUtility.Resize(brush, new Vector3(2, 2, 2)); - } - } - - void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeType, GUIStyle brushButtonStyle, GUIStyle labelStyle, int width, int height, bool shortMode) + private void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeType, GUIStyle brushButtonStyle, GUIStyle labelStyle, int width, int height, bool shortMode) { GUI.enabled = !activeType.HasValue || activeType.Value != brushType; if (GUILayout.Button(new GUIContent(" ", SabreCSGResources.GetButtonTexture(brushType)), brushButtonStyle, GUILayout.Width(width), GUILayout.Height(height))) @@ -108,17 +113,22 @@ void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeTyp name = "Cyl"; } + if (shortMode && brushType == PrimitiveBrushType.Capsule) + { + name = "Cap"; + } + GUI.Label(lastRect, name, labelStyle); } public override void DoInspectorGUI() { float drawableWidth = EditorGUIUtility.currentViewWidth; - drawableWidth -= 42; // Take some off for scroll bars and padding + drawableWidth -= 42; // Take some off for scroll bars and padding - PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray(); + PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray(); - PrimitiveBrushType? activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null; + PrimitiveBrushType? activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null; using (new NamedVerticalScope("Type")) { @@ -144,11 +154,12 @@ public override void DoInspectorGUI() DrawBrushButton(PrimitiveBrushType.Cylinder, activeType, brushButtonStyle, labelStyle, stretchButtonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.Sphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); DrawBrushButton(PrimitiveBrushType.IcoSphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); - + GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); DrawBrushButton(PrimitiveBrushType.Cone, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); + DrawBrushButton(PrimitiveBrushType.Capsule, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode); GUI.enabled = true; // Reset GUI enabled so that the next items aren't disabled GUILayout.EndHorizontal(); @@ -189,6 +200,13 @@ public override void DoInspectorGUI() { EditorGUILayout.PropertyField(coneSideCountProp, new GUIContent("Sides")); } + else if (activeType.Value == PrimitiveBrushType.Capsule) + { + EditorGUILayout.BeginVertical(); + EditorGUILayout.PropertyField(capsuleHeightProp, new GUIContent("Height")); + EditorGUILayout.PropertyField(capsuleSideCountProp, new GUIContent("Sides")); + EditorGUILayout.EndVertical(); + } if (EditorGUI.EndChangeCheck()) { // One of the properties has changed @@ -273,42 +291,41 @@ public override void DoInspectorGUI() GUILayout.EndHorizontal(); } - using (new NamedVerticalScope("Rotation")) - { - - GUILayout.Label("Align up direction", EditorStyles.boldLabel); - GUILayout.BeginHorizontal(); - if(GUILayout.Button("X")) - { - AlignUpToAxis(new Vector3(1, 0, 0), false); - } - if(GUILayout.Button("Y")) - { - AlignUpToAxis(new Vector3(0, 1, 0), false); - } - if(GUILayout.Button("Z")) - { - AlignUpToAxis(new Vector3(0, 0, 1), false); - } - GUILayout.EndHorizontal(); - - GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel); - - GUILayout.BeginHorizontal(); - if(GUILayout.Button("X")) - { - AlignUpToAxis(new Vector3(1, 0, 0), true); - } - if(GUILayout.Button("Y")) - { - AlignUpToAxis(new Vector3(0, 1, 0), true); - } - if(GUILayout.Button("Z")) - { - AlignUpToAxis(new Vector3(0, 0, 1), true); - } - GUILayout.EndHorizontal(); - } + using (new NamedVerticalScope("Rotation")) + { + GUILayout.Label("Align up direction", EditorStyles.boldLabel); + GUILayout.BeginHorizontal(); + if (GUILayout.Button("X")) + { + AlignUpToAxis(new Vector3(1, 0, 0), false); + } + if (GUILayout.Button("Y")) + { + AlignUpToAxis(new Vector3(0, 1, 0), false); + } + if (GUILayout.Button("Z")) + { + AlignUpToAxis(new Vector3(0, 0, 1), false); + } + GUILayout.EndHorizontal(); + + GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("X")) + { + AlignUpToAxis(new Vector3(1, 0, 0), true); + } + if (GUILayout.Button("Y")) + { + AlignUpToAxis(new Vector3(0, 1, 0), true); + } + if (GUILayout.Button("Z")) + { + AlignUpToAxis(new Vector3(0, 0, 1), true); + } + GUILayout.EndHorizontal(); + } using (new NamedVerticalScope("Misc")) { @@ -389,7 +406,6 @@ public override void DoInspectorGUI() BrushUtility.SplitIntersecting(brushes); } - // BrushOrder brushOrder = BrushTarget.GetBrushOrder(); // string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray()); // GUILayout.Label(positionString, EditorStyles.boldLabel); @@ -403,7 +419,7 @@ public override void DoInspectorGUI() //} } - base.DoInspectorGUI(); + base.DoInspectorGUI(); } /// @@ -411,17 +427,17 @@ public override void DoInspectorGUI() /// /// Axis to match transform.up to /// If specified, when the brush is rotated the vertices will be counter rotated so they remain in their old positions and orientations - void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment) - { - PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); - foreach (PrimitiveBrush brush in brushes) - { + private void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment) + { + PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray(); + foreach (PrimitiveBrush brush in brushes) + { Vector3 perpendicularDirection = Vector3.up; - if(Mathf.Abs(Vector3.Dot(perpendicularDirection, newUpAxis)) > 0.9f) + if (Mathf.Abs(Vector3.Dot(perpendicularDirection, newUpAxis)) > 0.9f) { perpendicularDirection = Vector3.back; } - + // Calculate the new rotation for the brush so that it's new transform.up is at the axis. Note the parameter order Quaternion rotation = Quaternion.LookRotation(-perpendicularDirection, newUpAxis); Quaternion inverseRotation = Quaternion.Inverse(rotation * Quaternion.Inverse(brush.transform.localRotation)); @@ -433,28 +449,28 @@ void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment) // If they want to realign the brush but keep original world positions if (counterAlignment) - { - Polygon[] polygons = brush.GetPolygons(); - - for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) - { - Vertex[] vertices = polygons[polygonIndex].Vertices; - - // Rotate positions and vertices so they remain in their original place - for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++) - { - vertices[vertexIndex].Position = inverseRotation * vertices[vertexIndex].Position; - vertices[vertexIndex].Normal = inverseRotation * vertices[vertexIndex].Normal; - } - } - - // Polygon vertices have changed, so recalculate the planes - for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) - { - polygons[polygonIndex].CalculatePlane(); - } - } - } - } + { + Polygon[] polygons = brush.GetPolygons(); + + for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) + { + Vertex[] vertices = polygons[polygonIndex].Vertices; + + // Rotate positions and vertices so they remain in their original place + for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++) + { + vertices[vertexIndex].Position = inverseRotation * vertices[vertexIndex].Position; + vertices[vertexIndex].Normal = inverseRotation * vertices[vertexIndex].Normal; + } + } + + // Polygon vertices have changed, so recalculate the planes + for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++) + { + polygons[polygonIndex].CalculatePlane(); + } + } + } + } } } \ No newline at end of file diff --git a/Scripts/Geometry/BrushFactory.cs b/Scripts/Geometry/BrushFactory.cs index 4104e332..cd4b7d35 100644 --- a/Scripts/Geometry/BrushFactory.cs +++ b/Scripts/Geometry/BrushFactory.cs @@ -1,4 +1,5 @@ #if UNITY_EDITOR || RUNTIME_CSG + using UnityEngine; using System.Collections; using System.Collections.Generic; @@ -6,279 +7,278 @@ namespace Sabresaurus.SabreCSG { - /// - /// Provides easy methods for generating brushes (as Polygon sets) - /// - public static class BrushFactory - { - /// - /// Generates a cube (size 2,2,2) - /// - /// Polygons to be supplied to a brush. - public static Polygon[] GenerateCube() - { - Polygon[] polygons = new Polygon[6]; - - // Polygons and vertices are created in a clockwise order - - // Polygons are created in this order: back, left, front, right, bottom, top - - // Vertices with those polygons are created in this order: BR, BL, TL, TR (when viewed aligned with the UV) - - // Therefore to move the two bottom vertices of the left face, you would pick the second face (index 1) and - // then manipulate the first two vertices (indexes 0 and 1) - - // Back - polygons[0] = new Polygon(new Vertex[] { - new Vertex(new Vector3(-1, -1, 1), new Vector3(0, 0, 1), new Vector2(1,0)), - new Vertex(new Vector3(1, -1, 1), new Vector3(0, 0, 1), new Vector2(0,0)), - new Vertex(new Vector3(1, 1, 1), new Vector3(0, 0, 1), new Vector2(0,1)), - new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 0, 1), new Vector2(1,1)), - }, null, false, false); - - // Left - polygons[1] = new Polygon(new Vertex[] { - new Vertex(new Vector3(-1, -1, -1), new Vector3(-1, 0, 0), new Vector2(1,0)), - new Vertex(new Vector3(-1, -1, 1), new Vector3(-1, 0, 0), new Vector2(0,0)), - new Vertex(new Vector3(-1, 1, 1), new Vector3(-1, 0, 0), new Vector2(0,1)), - new Vertex(new Vector3(-1, 1, -1), new Vector3(-1, 0, 0), new Vector2(1,1)), - }, null, false, false); - - // Front - polygons[2] = new Polygon(new Vertex[] { - new Vertex(new Vector3(1, -1, 1), new Vector3(1, 0, 0), new Vector2(1,0)), - new Vertex(new Vector3(1, -1, -1), new Vector3(1, 0, 0), new Vector2(0,0)), - new Vertex(new Vector3(1, 1, -1), new Vector3(1, 0, 0), new Vector2(0,1)), - new Vertex(new Vector3(1, 1, 1), new Vector3(1, 0, 0), new Vector2(1,1)), - }, null, false, false); - - // Right - polygons[3] = new Polygon(new Vertex[] { - new Vertex(new Vector3(1, -1, -1), new Vector3(0, 0, -1), new Vector2(1,0)), - new Vertex(new Vector3(-1, -1, -1), new Vector3(0, 0, -1), new Vector2(0,0)), - new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 0, -1), new Vector2(0,1)), - new Vertex(new Vector3(1, 1, -1), new Vector3(0, 0, -1), new Vector2(1,1)), - }, null, false, false); - - // Bottom - polygons[4] = new Polygon(new Vertex[] { - new Vertex(new Vector3(-1, -1, -1), new Vector3(0, -1, 0), new Vector2(1,0)), - new Vertex(new Vector3(1, -1, -1), new Vector3(0, -1, 0), new Vector2(0,0)), - new Vertex(new Vector3(1, -1, 1), new Vector3(0, -1, 0), new Vector2(0,1)), - new Vertex(new Vector3(-1, -1, 1), new Vector3(0, -1, 0), new Vector2(1,1)), - }, null, false, false); - - // Top - polygons[5] = new Polygon(new Vertex[] { - new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,0)), - new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,0)), - new Vertex(new Vector3(1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,1)), - new Vertex(new Vector3(1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,1)), - }, null, false, false); - - return polygons; - } - - /// - /// Generates a cylinder of height and radius 2, unlike a prism sides have smooth normals - /// - /// Polygons to be supplied to a brush. - /// Side count for the cylinder. - public static Polygon[] GenerateCylinder(int sideCount = 20) - { - Polygon[] polygons = new Polygon[sideCount * 3]; - - float angleDelta = Mathf.PI * 2 / sideCount; - - for (int i = 0; i < sideCount; i++) - { - polygons[i] = new Polygon(new Vertex[] - { - new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)), - new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)), - new Vector2(i * (1f/sideCount),0)), - new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)), - new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)), - new Vector2((i+1) * (1f/sideCount),0)), - new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), - new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)), - new Vector2((i+1) * (1f/sideCount),1)), - new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), - new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)), - new Vector2(i * (1f/sideCount),1)), - }, null, false, false); - } - - Vertex capCenterVertex = new Vertex(new Vector3(0,1,0), Vector3.up, new Vector2(0,0)); - - for (int i = 0; i < sideCount; i++) - { - Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); - Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta))); - - Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; - polygons[sideCount + i] = new Polygon(capVertices, null, false, false); - } - - capCenterVertex = new Vertex(new Vector3(0,-1,0), Vector3.down, new Vector2(0,0)); - - for (int i = 0; i < sideCount; i++) - { - Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); - Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * -angleDelta), -1, Mathf.Cos((i+1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta))); - - Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; - polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false); - } - - return polygons; - } - - /// - /// Generates a prism of height and radius 2, unlike a cylinder sides have faceted normals - /// - /// Polygons to be supplied to a brush. - /// Side count for the prism. - public static Polygon[] GeneratePrism(int sideCount) - { - Polygon[] polygons = new Polygon[sideCount * 3]; - - float angleDelta = Mathf.PI * 2 / sideCount; - - for (int i = 0; i < sideCount; i++) - { - Vector3 normal = new Vector3(Mathf.Sin((i+0.5f) * angleDelta), 0, Mathf.Cos((i+0.5f) * angleDelta)); - polygons[i] = new Polygon(new Vertex[] { - - new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)), - normal, - new Vector2(0,0)), - new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)), - normal, - new Vector2(1,0)), - new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), - normal, - new Vector2(1,1)), - new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), - normal, - new Vector2(0,1)), - }, null, false, false); - } - - Vertex capCenterVertex = new Vertex(new Vector3(0,1,0), Vector3.up, new Vector2(0,0)); - - for (int i = 0; i < sideCount; i++) - { - Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); - Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta))); - - Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; - polygons[sideCount + i] = new Polygon(capVertices, null, false, false); - } - - capCenterVertex = new Vertex(new Vector3(0,-1,0), Vector3.down, new Vector2(0,0)); - - for (int i = 0; i < sideCount; i++) - { - Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); - Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * -angleDelta), -1, Mathf.Cos((i+1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta))); - - Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; - polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false); - } - - return polygons; - } - - [System.Obsolete("Use GeneratePolarSphere instead, or for a more isotropic geometry use GenerateIcoSphere")] - public static Polygon[] GenerateSphere(int lateralCount = 6, int longitudinalCount = 12) - { - return GeneratePolarSphere(lateralCount, longitudinalCount); - } - - /// - /// Generates an ico-sphere of radius 2. Unlike a polar-sphere this has a more even distribution of vertices. - /// - /// Polygons to be supplied to a brush. - /// Number of times the surface is subdivided, values of 1 or 2 are recommended. - public static Polygon[] GenerateIcoSphere(int iterationCount) - { - // Derived from http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html - float longestDimension = (1+Mathf.Sqrt(5f)) / 2f; - Vector3 sourceVector = new Vector3(0, 1, longestDimension); - - // Make the longest dimension 1, so the icosphere fits in a 2,2,2 cube - sourceVector.Normalize(); - - Vertex[] vertices = new Vertex[] - { - new Vertex(new Vector3(-sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(-sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), - - new Vertex(new Vector3(sourceVector.x,-sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(sourceVector.x,+sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(sourceVector.x,-sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(sourceVector.x,+sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero), - - new Vertex(new Vector3(+sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(+sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(-sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero), - new Vertex(new Vector3(-sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero), - }; - - Polygon[] polygons = new Polygon[] - { - new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[1].DeepCopy(),vertices[7].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[5].DeepCopy(),vertices[1].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[7].DeepCopy(),vertices[10].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[10].DeepCopy(),vertices[11].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[11].DeepCopy(),vertices[5].DeepCopy()}, null, false, false), - - new Polygon(new Vertex[] { vertices[7].DeepCopy(),vertices[1].DeepCopy(),vertices[8].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[1].DeepCopy(),vertices[5].DeepCopy(),vertices[9].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[10].DeepCopy(),vertices[7].DeepCopy(),vertices[6].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[11].DeepCopy(),vertices[10].DeepCopy(),vertices[2].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[5].DeepCopy(),vertices[11].DeepCopy(),vertices[4].DeepCopy()}, null, false, false), - - new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[2].DeepCopy(),vertices[6].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[4].DeepCopy(),vertices[2].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[6].DeepCopy(),vertices[8].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[8].DeepCopy(),vertices[9].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[9].DeepCopy(),vertices[4].DeepCopy()}, null, false, false), - - new Polygon(new Vertex[] { vertices[6].DeepCopy(),vertices[2].DeepCopy(),vertices[10].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[2].DeepCopy(),vertices[4].DeepCopy(),vertices[11].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[8].DeepCopy(),vertices[6].DeepCopy(),vertices[7].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[9].DeepCopy(),vertices[8].DeepCopy(),vertices[1].DeepCopy()}, null, false, false), - new Polygon(new Vertex[] { vertices[4].DeepCopy(),vertices[9].DeepCopy(),vertices[5].DeepCopy()}, null, false, false), - }; - - // Refine - for (int i = 0; i < iterationCount; i++) - { - Polygon[] newPolygons = new Polygon[polygons.Length*4]; - for (int j = 0; j < polygons.Length; j++) - { - Vertex a = Vertex.Lerp(polygons[j].Vertices[0], polygons[j].Vertices[1], 0.5f); - Vertex b = Vertex.Lerp(polygons[j].Vertices[1], polygons[j].Vertices[2], 0.5f); - Vertex c = Vertex.Lerp(polygons[j].Vertices[2], polygons[j].Vertices[0], 0.5f); - - a.Position = a.Position.normalized; - b.Position = b.Position.normalized; - c.Position = c.Position.normalized; - - newPolygons[j*4+0] = new Polygon(new Vertex[] { polygons[j].Vertices[0].DeepCopy(), a.DeepCopy(), c.DeepCopy()}, null, false, false); - newPolygons[j*4+1] = new Polygon(new Vertex[] { polygons[j].Vertices[1].DeepCopy(), b.DeepCopy(), a.DeepCopy()}, null, false, false); - newPolygons[j*4+2] = new Polygon(new Vertex[] { polygons[j].Vertices[2].DeepCopy(), c.DeepCopy(), b.DeepCopy()}, null, false, false); - newPolygons[j*4+3] = new Polygon(new Vertex[] { a.DeepCopy(), b.DeepCopy(), c.DeepCopy()}, null, false, false); - } - polygons = newPolygons; - } - - for (int i = 0; i < polygons.Length; i++) - { + /// + /// Provides easy methods for generating brushes (as Polygon sets) + /// + public static class BrushFactory + { + /// + /// Generates a cube (size 2,2,2) + /// + /// Polygons to be supplied to a brush. + public static Polygon[] GenerateCube() + { + Polygon[] polygons = new Polygon[6]; + + // Polygons and vertices are created in a clockwise order + + // Polygons are created in this order: back, left, front, right, bottom, top + + // Vertices with those polygons are created in this order: BR, BL, TL, TR (when viewed aligned with the UV) + + // Therefore to move the two bottom vertices of the left face, you would pick the second face (index 1) and + // then manipulate the first two vertices (indexes 0 and 1) + + // Back + polygons[0] = new Polygon(new Vertex[] { + new Vertex(new Vector3(-1, -1, 1), new Vector3(0, 0, 1), new Vector2(1,0)), + new Vertex(new Vector3(1, -1, 1), new Vector3(0, 0, 1), new Vector2(0,0)), + new Vertex(new Vector3(1, 1, 1), new Vector3(0, 0, 1), new Vector2(0,1)), + new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 0, 1), new Vector2(1,1)), + }, null, false, false); + + // Left + polygons[1] = new Polygon(new Vertex[] { + new Vertex(new Vector3(-1, -1, -1), new Vector3(-1, 0, 0), new Vector2(1,0)), + new Vertex(new Vector3(-1, -1, 1), new Vector3(-1, 0, 0), new Vector2(0,0)), + new Vertex(new Vector3(-1, 1, 1), new Vector3(-1, 0, 0), new Vector2(0,1)), + new Vertex(new Vector3(-1, 1, -1), new Vector3(-1, 0, 0), new Vector2(1,1)), + }, null, false, false); + + // Front + polygons[2] = new Polygon(new Vertex[] { + new Vertex(new Vector3(1, -1, 1), new Vector3(1, 0, 0), new Vector2(1,0)), + new Vertex(new Vector3(1, -1, -1), new Vector3(1, 0, 0), new Vector2(0,0)), + new Vertex(new Vector3(1, 1, -1), new Vector3(1, 0, 0), new Vector2(0,1)), + new Vertex(new Vector3(1, 1, 1), new Vector3(1, 0, 0), new Vector2(1,1)), + }, null, false, false); + + // Right + polygons[3] = new Polygon(new Vertex[] { + new Vertex(new Vector3(1, -1, -1), new Vector3(0, 0, -1), new Vector2(1,0)), + new Vertex(new Vector3(-1, -1, -1), new Vector3(0, 0, -1), new Vector2(0,0)), + new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 0, -1), new Vector2(0,1)), + new Vertex(new Vector3(1, 1, -1), new Vector3(0, 0, -1), new Vector2(1,1)), + }, null, false, false); + + // Bottom + polygons[4] = new Polygon(new Vertex[] { + new Vertex(new Vector3(-1, -1, -1), new Vector3(0, -1, 0), new Vector2(1,0)), + new Vertex(new Vector3(1, -1, -1), new Vector3(0, -1, 0), new Vector2(0,0)), + new Vertex(new Vector3(1, -1, 1), new Vector3(0, -1, 0), new Vector2(0,1)), + new Vertex(new Vector3(-1, -1, 1), new Vector3(0, -1, 0), new Vector2(1,1)), + }, null, false, false); + + // Top + polygons[5] = new Polygon(new Vertex[] { + new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,0)), + new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,0)), + new Vertex(new Vector3(1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,1)), + new Vertex(new Vector3(1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,1)), + }, null, false, false); + + return polygons; + } + + /// + /// Generates a cylinder of height and radius 2, unlike a prism sides have smooth normals + /// + /// Polygons to be supplied to a brush. + /// Side count for the cylinder. + public static Polygon[] GenerateCylinder(int sideCount = 20) + { + Polygon[] polygons = new Polygon[sideCount * 3]; + + float angleDelta = Mathf.PI * 2 / sideCount; + + for (int i = 0; i < sideCount; i++) + { + polygons[i] = new Polygon(new Vertex[] + { + new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)), + new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)), + new Vector2(i * (1f/sideCount),0)), + new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)), + new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)), + new Vector2((i+1) * (1f/sideCount),0)), + new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), + new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)), + new Vector2((i+1) * (1f/sideCount),1)), + new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), + new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)), + new Vector2(i * (1f/sideCount),1)), + }, null, false, false); + } + + Vertex capCenterVertex = new Vertex(new Vector3(0, 1, 0), Vector3.up, new Vector2(0, 0)); + + for (int i = 0; i < sideCount; i++) + { + Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); + Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * angleDelta), 1, Mathf.Cos((i + 1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta))); + + Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; + polygons[sideCount + i] = new Polygon(capVertices, null, false, false); + } + + capCenterVertex = new Vertex(new Vector3(0, -1, 0), Vector3.down, new Vector2(0, 0)); + + for (int i = 0; i < sideCount; i++) + { + Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); + Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * -angleDelta), -1, Mathf.Cos((i + 1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta))); + + Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; + polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false); + } + + return polygons; + } + + /// + /// Generates a prism of height and radius 2, unlike a cylinder sides have faceted normals + /// + /// Polygons to be supplied to a brush. + /// Side count for the prism. + public static Polygon[] GeneratePrism(int sideCount) + { + Polygon[] polygons = new Polygon[sideCount * 3]; + + float angleDelta = Mathf.PI * 2 / sideCount; + + for (int i = 0; i < sideCount; i++) + { + Vector3 normal = new Vector3(Mathf.Sin((i + 0.5f) * angleDelta), 0, Mathf.Cos((i + 0.5f) * angleDelta)); + polygons[i] = new Polygon(new Vertex[] { + new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)), + normal, + new Vector2(0,0)), + new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)), + normal, + new Vector2(1,0)), + new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), + normal, + new Vector2(1,1)), + new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), + normal, + new Vector2(0,1)), + }, null, false, false); + } + + Vertex capCenterVertex = new Vertex(new Vector3(0, 1, 0), Vector3.up, new Vector2(0, 0)); + + for (int i = 0; i < sideCount; i++) + { + Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); + Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * angleDelta), 1, Mathf.Cos((i + 1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta))); + + Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; + polygons[sideCount + i] = new Polygon(capVertices, null, false, false); + } + + capCenterVertex = new Vertex(new Vector3(0, -1, 0), Vector3.down, new Vector2(0, 0)); + + for (int i = 0; i < sideCount; i++) + { + Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); + Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * -angleDelta), -1, Mathf.Cos((i + 1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta))); + + Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; + polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false); + } + + return polygons; + } + + [System.Obsolete("Use GeneratePolarSphere instead, or for a more isotropic geometry use GenerateIcoSphere")] + public static Polygon[] GenerateSphere(int lateralCount = 6, int longitudinalCount = 12) + { + return GeneratePolarSphere(lateralCount, longitudinalCount); + } + + /// + /// Generates an ico-sphere of radius 2. Unlike a polar-sphere this has a more even distribution of vertices. + /// + /// Polygons to be supplied to a brush. + /// Number of times the surface is subdivided, values of 1 or 2 are recommended. + public static Polygon[] GenerateIcoSphere(int iterationCount) + { + // Derived from http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html + float longestDimension = (1 + Mathf.Sqrt(5f)) / 2f; + Vector3 sourceVector = new Vector3(0, 1, longestDimension); + + // Make the longest dimension 1, so the icosphere fits in a 2,2,2 cube + sourceVector.Normalize(); + + Vertex[] vertices = new Vertex[] + { + new Vertex(new Vector3(-sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(-sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero), + + new Vertex(new Vector3(sourceVector.x,-sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(sourceVector.x,+sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(sourceVector.x,-sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(sourceVector.x,+sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero), + + new Vertex(new Vector3(+sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(+sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(-sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero), + new Vertex(new Vector3(-sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero), + }; + + Polygon[] polygons = new Polygon[] + { + new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[1].DeepCopy(),vertices[7].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[5].DeepCopy(),vertices[1].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[7].DeepCopy(),vertices[10].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[10].DeepCopy(),vertices[11].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[11].DeepCopy(),vertices[5].DeepCopy()}, null, false, false), + + new Polygon(new Vertex[] { vertices[7].DeepCopy(),vertices[1].DeepCopy(),vertices[8].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[1].DeepCopy(),vertices[5].DeepCopy(),vertices[9].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[10].DeepCopy(),vertices[7].DeepCopy(),vertices[6].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[11].DeepCopy(),vertices[10].DeepCopy(),vertices[2].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[5].DeepCopy(),vertices[11].DeepCopy(),vertices[4].DeepCopy()}, null, false, false), + + new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[2].DeepCopy(),vertices[6].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[4].DeepCopy(),vertices[2].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[6].DeepCopy(),vertices[8].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[8].DeepCopy(),vertices[9].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[9].DeepCopy(),vertices[4].DeepCopy()}, null, false, false), + + new Polygon(new Vertex[] { vertices[6].DeepCopy(),vertices[2].DeepCopy(),vertices[10].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[2].DeepCopy(),vertices[4].DeepCopy(),vertices[11].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[8].DeepCopy(),vertices[6].DeepCopy(),vertices[7].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[9].DeepCopy(),vertices[8].DeepCopy(),vertices[1].DeepCopy()}, null, false, false), + new Polygon(new Vertex[] { vertices[4].DeepCopy(),vertices[9].DeepCopy(),vertices[5].DeepCopy()}, null, false, false), + }; + + // Refine + for (int i = 0; i < iterationCount; i++) + { + Polygon[] newPolygons = new Polygon[polygons.Length * 4]; + for (int j = 0; j < polygons.Length; j++) + { + Vertex a = Vertex.Lerp(polygons[j].Vertices[0], polygons[j].Vertices[1], 0.5f); + Vertex b = Vertex.Lerp(polygons[j].Vertices[1], polygons[j].Vertices[2], 0.5f); + Vertex c = Vertex.Lerp(polygons[j].Vertices[2], polygons[j].Vertices[0], 0.5f); + + a.Position = a.Position.normalized; + b.Position = b.Position.normalized; + c.Position = c.Position.normalized; + + newPolygons[j * 4 + 0] = new Polygon(new Vertex[] { polygons[j].Vertices[0].DeepCopy(), a.DeepCopy(), c.DeepCopy() }, null, false, false); + newPolygons[j * 4 + 1] = new Polygon(new Vertex[] { polygons[j].Vertices[1].DeepCopy(), b.DeepCopy(), a.DeepCopy() }, null, false, false); + newPolygons[j * 4 + 2] = new Polygon(new Vertex[] { polygons[j].Vertices[2].DeepCopy(), c.DeepCopy(), b.DeepCopy() }, null, false, false); + newPolygons[j * 4 + 3] = new Polygon(new Vertex[] { a.DeepCopy(), b.DeepCopy(), c.DeepCopy() }, null, false, false); + } + polygons = newPolygons; + } + + for (int i = 0; i < polygons.Length; i++) + { bool anyAboveHalf = false; for (int j = 0; j < polygons[i].Vertices.Length; j++) @@ -302,7 +302,7 @@ public static Polygon[] GenerateIcoSphere(int iterationCount) } } - if(u > 0.75f) + if (u > 0.75f) { anyAboveHalf = true; } @@ -325,154 +325,150 @@ public static Polygon[] GenerateIcoSphere(int iterationCount) uv.x += 1; } polygons[i].Vertices[j].UV = uv; + } + } + } + + return polygons; + } + + /// + /// Generates a sphere of radius 2 + /// + /// Polygons to be supplied to a brush. + /// Vertex count up from the south pole to the north pole. + /// Vertex count around the sphere equator. + public static Polygon[] GeneratePolarSphere(int lateralCount = 6, int longitudinalCount = 12) + { + Polygon[] polygons = new Polygon[lateralCount * longitudinalCount]; + + float angleDelta = 1f / lateralCount; + float longitudinalDelta = 1f / longitudinalCount; + + // Generate tris for the top and bottom, then quads for the rest + for (int i = 0; i < lateralCount; i++) + { + for (int j = 0; j < longitudinalCount; j++) + { + Vertex[] vertices; + + if (i == lateralCount - 1) + { + vertices = new Vertex[] { + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))), + }; + } + else if (i > 0) + { + vertices = new Vertex[] { + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))), + }; + } + else // i == 0 + { + vertices = new Vertex[] { + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * i * angleDelta), + Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) + ), + new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), + new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), + Mathf.Cos(Mathf.PI * (i+1) * angleDelta), + Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) + ), + new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))), + }; + } + for (int d = 0; d < vertices.Length; d++) + { + vertices[d].UV = new Vector2(vertices[d].UV.y, 1 - vertices[d].UV.x); } + + polygons[i + j * lateralCount] = new Polygon(vertices, null, false, false); } } - return polygons; - } - - /// - /// Generates a sphere of radius 2 - /// - /// Polygons to be supplied to a brush. - /// Vertex count up from the south pole to the north pole. - /// Vertex count around the sphere equator. - public static Polygon[] GeneratePolarSphere(int lateralCount = 6, int longitudinalCount = 12) - { - Polygon[] polygons = new Polygon[lateralCount * longitudinalCount]; - - float angleDelta = 1f / lateralCount; - float longitudinalDelta = 1f / longitudinalCount; - - // Generate tris for the top and bottom, then quads for the rest - for (int i = 0; i < lateralCount; i++) - { - for (int j = 0; j < longitudinalCount; j++) - { - Vertex[] vertices; - - if(i == lateralCount-1) - { - vertices = new Vertex[] { - - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))), - }; - } - else if(i > 0) - { - vertices = new Vertex[] { - - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))), - }; - } - else // i == 0 - { - vertices = new Vertex[] { - - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * i * angleDelta), - Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta) - ), - new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))), - new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), - Mathf.Cos(Mathf.PI * (i+1) * angleDelta), - Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) - ), - new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))), - }; - } - - for (int d = 0; d < vertices.Length; d++) - { - vertices[d].UV = new Vector2(vertices[d].UV.y, 1 - vertices[d].UV.x); - } - - polygons[i + j * lateralCount] = new Polygon(vertices, null, false, false); - } - } - - return polygons; - } + return polygons; + } /// /// Generates a cone of height and radius 2. If the is 3, generates a triangular-based pyramid. @@ -549,131 +545,278 @@ public static Polygon[] GenerateCone(int sideCount = 20) return polygons; } + /// + /// Generates a capsule. + /// + /// The height of the capsule. + /// The radius of the capsule. + /// The amount of segments of the capsule. + /// Special Thanks: Jay Kay for your capsule generation algorithm https://forum.unity.com/threads/solved-closed-procedurally-generated-capsule.406982/#post-2652094. + /// Polygons to be supplied to a brush. + public static Polygon[] GenerateCapsule(float height = 2.0f, float radius = 0.5f, int segments = 24) + { + // make segments an even number + if (segments % 2 != 0) + segments++; + + // extra vertex on the seam + int points = segments + 1; + + // calculate points around a circle + float[] pX = new float[points]; + float[] pZ = new float[points]; + float[] pY = new float[points]; + float[] pR = new float[points]; + + float calcH = 0f; + float calcV = 0f; + + for (int i = 0; i < points; i++) + { + pX[i] = Mathf.Sin(calcH * Mathf.Deg2Rad); + pZ[i] = Mathf.Cos(calcH * Mathf.Deg2Rad); + pY[i] = Mathf.Cos(calcV * Mathf.Deg2Rad); + pR[i] = Mathf.Sin(calcV * Mathf.Deg2Rad); + + calcH += 360f / (float)segments; + calcV += 180f / (float)segments; + } + + // - Vertices and UVs - + + Vector3[] vertices = new Vector3[points * (points + 1)]; + Vector2[] uvs = new Vector2[vertices.Length]; + int ind = 0; + + // Y-offset is half the height minus the diameter + float yOff = (height - (radius * 2f)) * 0.5f; + if (yOff < 0) + yOff = 0; + + // uv calculations + float stepX = 1f / ((float)(points - 1)); + float uvX, uvY; + + // Top Hemisphere + int top = Mathf.CeilToInt((float)points * 0.5f); + + for (int y = 0; y < top; y++) + { + for (int x = 0; x < points; x++) + { + vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius; + vertices[ind].y = yOff + vertices[ind].y; + + uvX = 1f - (stepX * (float)x); + uvY = (vertices[ind].y + (height * 0.5f)) / height; + uvs[ind] = new Vector2(uvX, uvY); + + ind++; + } + } + + // Bottom Hemisphere + int btm = Mathf.FloorToInt((float)points * 0.5f); + + for (int y = btm; y < points; y++) + { + for (int x = 0; x < points; x++) + { + vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius; + vertices[ind].y = -yOff + vertices[ind].y; + + uvX = 1f - (stepX * (float)x); + uvY = (vertices[ind].y + (height * 0.5f)) / height; + uvs[ind] = new Vector2(uvX, uvY); + + ind++; + } + } + + // - Triangles - + + int[] triangles = new int[(segments * (segments + 1) * 4)]; + + for (int y = 0, t = 0; y < segments + 1; y++) + { + for (int x = 0; x < segments; x++, t += 4) + { + triangles[t + 0] = ((y + 0) * (segments + 1)) + x + 0; + triangles[t + 1] = ((y + 1) * (segments + 1)) + x + 0; + triangles[t + 2] = ((y + 1) * (segments + 1)) + x + 1; + triangles[t + 3] = ((y + 0) * (segments + 1)) + x + 1; + } + } + + List polygons = new List(); + + int j = 0; + for (int i = 0; i < triangles.Length; i += 4) + { + Polygon p; + if (j < segments) + { + // top triangles. + polygons.Add(p = new Polygon(new Vertex[] + { + new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]), + new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]), + new Vertex(vertices[triangles[i+2]], Vector3.zero, uvs[triangles[i+2]]), + }, null, false, false)); + } + else if (i >= triangles.Length - (segments * 4)) + { + // bottom triangles. + polygons.Add(p = new Polygon(new Vertex[] + { + new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]), + new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]), + new Vertex(vertices[triangles[i+3]], Vector3.zero, uvs[triangles[i+3]]), + }, null, false, false)); + } + else + { + // everything else is quads. + polygons.Add(p = new Polygon(new Vertex[] + { + new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]), + new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]), + new Vertex(vertices[triangles[i+2]], Vector3.zero, uvs[triangles[i+2]]), + new Vertex(vertices[triangles[i+3]], Vector3.zero, uvs[triangles[i+3]]), + }, null, false, false)); + } + p.ResetVertexNormals(); + j += 1; + } + + return polygons.ToArray(); + } + /// /// Generates the polygons from a supplied convex mesh, preserving quads if the MeshImporter has keepQuads set. /// /// The polygons converted from the mesh. /// Source mesh. public static List GeneratePolygonsFromMesh(Mesh sourceMesh) - { - List generatedPolygons = new List(); - // Each sub mesh can have a different topology, i.e. triangles and quads - for (int subMeshIndex = 0; subMeshIndex < sourceMesh.subMeshCount; subMeshIndex++) - { - MeshTopology meshTopology = sourceMesh.GetTopology(subMeshIndex); - // The vertex count per polygon that we need to walk through the indices at - int stride = 1; - if(meshTopology == MeshTopology.Quads) - { - stride = 4; - } - else if(meshTopology == MeshTopology.Triangles) - { - stride = 3; - } - else - { - Debug.LogError("Unhandled sub mesh topology " + meshTopology + ". Ignoring sub mesh."); - continue; - } - - // Grab this sub mesh's index buffer - int[] indices = sourceMesh.GetIndices(subMeshIndex); - - // Walk through the polygons in the index buffer - for (int j = 0; j < indices.Length/stride; j++) - { - // Create a new vertex buffer for each polygon - Vertex[] vertices = new Vertex[stride]; - - // Pull out all the vertices for this source polygon - for (int k = 0; k < stride; k++) - { - int vertexIndex = indices[j*stride+k]; - - vertices[k] = new Vertex(sourceMesh.vertices[vertexIndex], - sourceMesh.normals[vertexIndex], - sourceMesh.uv[vertexIndex]); - } - // Generate a new polygon using these vertices and add it to the output polygon list - Polygon polygon = new Polygon(vertices, null, false, false); - generatedPolygons.Add(polygon); - } - } - // Finally return all the converted polygons - return generatedPolygons; - } - - /// - /// Generates a mesh from supplied polygons, particularly useful for visualising a brush's polygons on a MeshFilter - /// - /// Polygons. - /// Mesh to be written to. Prior to settings the mesh buffers - if the existing mesh is null it will be set to a new one, otherwise the existing mesh is cleared - /// Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex]; - public static void GenerateMeshFromPolygons(Polygon[] polygons, ref Mesh mesh, out List polygonIndices) - { - if(mesh == null) - { - mesh = new Mesh(); - } - mesh.Clear(); - // mesh = new Mesh(); - List vertices = new List(); - List normals = new List(); - List uvs = new List(); - List colors = new List(); - List triangles = new List(); - - // Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex]; - polygonIndices = new List(); - - // Set up an indexer that tracks unique vertices, so that we reuse vertex data appropiately - VertexList vertexList = new VertexList(); - - // Iterate through every polygon and triangulate - for (int i = 0; i < polygons.Length; i++) - { - Polygon polygon = polygons[i]; - List indices = new List(); - - for (int j = 0; j < polygon.Vertices.Length; j++) - { - // Each vertex must know about its shared data for geometry tinting - //polygon.Vertices[j].Shared = polygon.SharedBrushData; - // If the vertex is already in the indexer, fetch the index otherwise add it and get the added index - int index = vertexList.AddOrGet(polygon.Vertices[j]); - // Put each vertex index in an array for use in the triangle generation - indices.Add(index); - } - - // Triangulate the n-sided polygon and allow vertex reuse by using indexed geometry - for (int j = 2; j < indices.Count; j++) - { - triangles.Add(indices[0]); - triangles.Add(indices[j - 1]); - triangles.Add(indices[j]); - - // Map that this triangle is from the specified polygon (so we can map back from triangles to polygon) - polygonIndices.Add(i); - } - } - - // Create the relevant buffers from the vertex array - for (int i = 0; i < vertexList.Vertices.Count; i++) - { - vertices.Add(vertexList.Vertices[i].Position); - normals.Add(vertexList.Vertices[i].Normal); - uvs.Add(vertexList.Vertices[i].UV); - // colors.Add(((SharedBrushData)indexer.Vertices[i].Shared).BrushTintColor); - } - - // Set the mesh buffers - mesh.vertices = vertices.ToArray(); - mesh.normals = normals.ToArray(); - mesh.colors = colors.ToArray(); - mesh.uv = uvs.ToArray(); - mesh.triangles = triangles.ToArray(); - } + { + List generatedPolygons = new List(); + // Each sub mesh can have a different topology, i.e. triangles and quads + for (int subMeshIndex = 0; subMeshIndex < sourceMesh.subMeshCount; subMeshIndex++) + { + MeshTopology meshTopology = sourceMesh.GetTopology(subMeshIndex); + // The vertex count per polygon that we need to walk through the indices at + int stride = 1; + if (meshTopology == MeshTopology.Quads) + { + stride = 4; + } + else if (meshTopology == MeshTopology.Triangles) + { + stride = 3; + } + else + { + Debug.LogError("Unhandled sub mesh topology " + meshTopology + ". Ignoring sub mesh."); + continue; + } + + // Grab this sub mesh's index buffer + int[] indices = sourceMesh.GetIndices(subMeshIndex); + + // Walk through the polygons in the index buffer + for (int j = 0; j < indices.Length / stride; j++) + { + // Create a new vertex buffer for each polygon + Vertex[] vertices = new Vertex[stride]; + + // Pull out all the vertices for this source polygon + for (int k = 0; k < stride; k++) + { + int vertexIndex = indices[j * stride + k]; + + vertices[k] = new Vertex(sourceMesh.vertices[vertexIndex], + sourceMesh.normals[vertexIndex], + sourceMesh.uv[vertexIndex]); + } + // Generate a new polygon using these vertices and add it to the output polygon list + Polygon polygon = new Polygon(vertices, null, false, false); + generatedPolygons.Add(polygon); + } + } + // Finally return all the converted polygons + return generatedPolygons; + } + + /// + /// Generates a mesh from supplied polygons, particularly useful for visualising a brush's polygons on a MeshFilter + /// + /// Polygons. + /// Mesh to be written to. Prior to settings the mesh buffers - if the existing mesh is null it will be set to a new one, otherwise the existing mesh is cleared + /// Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex]; + public static void GenerateMeshFromPolygons(Polygon[] polygons, ref Mesh mesh, out List polygonIndices) + { + if (mesh == null) + { + mesh = new Mesh(); + } + mesh.Clear(); + // mesh = new Mesh(); + List vertices = new List(); + List normals = new List(); + List uvs = new List(); + List colors = new List(); + List triangles = new List(); + + // Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex]; + polygonIndices = new List(); + + // Set up an indexer that tracks unique vertices, so that we reuse vertex data appropiately + VertexList vertexList = new VertexList(); + + // Iterate through every polygon and triangulate + for (int i = 0; i < polygons.Length; i++) + { + Polygon polygon = polygons[i]; + List indices = new List(); + + for (int j = 0; j < polygon.Vertices.Length; j++) + { + // Each vertex must know about its shared data for geometry tinting + //polygon.Vertices[j].Shared = polygon.SharedBrushData; + // If the vertex is already in the indexer, fetch the index otherwise add it and get the added index + int index = vertexList.AddOrGet(polygon.Vertices[j]); + // Put each vertex index in an array for use in the triangle generation + indices.Add(index); + } + + // Triangulate the n-sided polygon and allow vertex reuse by using indexed geometry + for (int j = 2; j < indices.Count; j++) + { + triangles.Add(indices[0]); + triangles.Add(indices[j - 1]); + triangles.Add(indices[j]); + + // Map that this triangle is from the specified polygon (so we can map back from triangles to polygon) + polygonIndices.Add(i); + } + } + + // Create the relevant buffers from the vertex array + for (int i = 0; i < vertexList.Vertices.Count; i++) + { + vertices.Add(vertexList.Vertices[i].Position); + normals.Add(vertexList.Vertices[i].Normal); + uvs.Add(vertexList.Vertices[i].UV); + // colors.Add(((SharedBrushData)indexer.Vertices[i].Shared).BrushTintColor); + } + + // Set the mesh buffers + mesh.vertices = vertices.ToArray(); + mesh.normals = normals.ToArray(); + mesh.colors = colors.ToArray(); + mesh.uv = uvs.ToArray(); + mesh.triangles = triangles.ToArray(); + } /// /// Generates a mesh from the supplied polygons. particularly useful for visualising a @@ -741,4 +884,5 @@ public static void GenerateMeshFromPolygonsFast(Polygon[] polygons, ref Mesh mes } } } + #endif \ No newline at end of file diff --git a/Scripts/UI/SabreCSGResources.cs b/Scripts/UI/SabreCSGResources.cs index 26785fd7..a6a0af27 100644 --- a/Scripts/UI/SabreCSGResources.cs +++ b/Scripts/UI/SabreCSGResources.cs @@ -1,4 +1,5 @@ #if UNITY_EDITOR + using UnityEngine; using UnityEditor; using System.Collections.Generic; @@ -6,84 +7,84 @@ namespace Sabresaurus.SabreCSG { - public static class SabreCSGResources - { -#region Fields + public static class SabreCSGResources + { + #region Fields + + // Cached objects keyed by AssetDatabase path relative to SabreCSG folder + private static Dictionary loadedObjects = new Dictionary(); - // Cached objects keyed by AssetDatabase path relative to SabreCSG folder - static Dictionary loadedObjects = new Dictionary(); + // Textures generated rather than loaded + private static Texture2D clearTexture = null; // 0 alpha texture - // Textures generated rather than loaded - private static Texture2D clearTexture = null; // 0 alpha texture - private static Texture2D halfWhiteTexture = null; // white with 0.5 alpha - private static Texture2D halfBlackTexture = null; // black with 0.5 alpha + private static Texture2D halfWhiteTexture = null; // white with 0.5 alpha + private static Texture2D halfBlackTexture = null; // black with 0.5 alpha - // --------------------- + // --------------------- - private static Material selectedBrushMaterial = null; - private static Material selectedBrushDashedMaterial = null; - private static Material selectedBrushDashedAlphaMaterial = null; + private static Material selectedBrushMaterial = null; + private static Material selectedBrushDashedMaterial = null; + private static Material selectedBrushDashedAlphaMaterial = null; private static Material gizmoMaterial = null; - private static Material vertexMaterial = null; - private static Material circleMaterial = null; - private static Material circleOutlineMaterial = null; - private static Material planeMaterial = null; - private static Material previewMaterial = null; - private static Material excludedMaterial = null; - private static Material greyscaleUIMaterial = null; - -#endregion - - - /// - /// Loads an object from a path, or if the object is already loaded returns it - /// - /// Path local to the SabreCSG folder - /// - private static Object LoadObject(string sabrePath) - { - bool found = false; - - Object loadedObject = null; - - // First of all see if there's a cached record - if (loadedObjects.ContainsKey(sabrePath)) - { - found = true; - loadedObject = loadedObjects[sabrePath]; - - // Now make sure the cached record actually points to something - if (loadedObject != null) - { - return loadedObject; - } - } - - // Failed to load from cache, so load it from the Asset Database - loadedObject = AssetDatabase.LoadMainAssetAtPath(Path.Combine(CSGModel.GetSabreCSGPath(), sabrePath)); - if(loadedObject != null) - { - if (found) - { - // A cache record was found but empty, so set the existing record to the newly loaded object - loadedObjects[sabrePath] = loadedObject; - } - else - { - // We know that it's not already in the cache, so add it to the end - loadedObjects.Add(sabrePath, loadedObject); - } - } - return loadedObject; - } - - /// - /// Gets an icon for a primitive brush to be displayed on a button - /// - /// The icon texture. - /// Primitive Brush type. - public static Texture2D GetButtonTexture(PrimitiveBrushType brushType) - { + private static Material vertexMaterial = null; + private static Material circleMaterial = null; + private static Material circleOutlineMaterial = null; + private static Material planeMaterial = null; + private static Material previewMaterial = null; + private static Material excludedMaterial = null; + private static Material greyscaleUIMaterial = null; + + #endregion Fields + + /// + /// Loads an object from a path, or if the object is already loaded returns it + /// + /// Path local to the SabreCSG folder + /// + private static Object LoadObject(string sabrePath) + { + bool found = false; + + Object loadedObject = null; + + // First of all see if there's a cached record + if (loadedObjects.ContainsKey(sabrePath)) + { + found = true; + loadedObject = loadedObjects[sabrePath]; + + // Now make sure the cached record actually points to something + if (loadedObject != null) + { + return loadedObject; + } + } + + // Failed to load from cache, so load it from the Asset Database + loadedObject = AssetDatabase.LoadMainAssetAtPath(Path.Combine(CSGModel.GetSabreCSGPath(), sabrePath)); + if (loadedObject != null) + { + if (found) + { + // A cache record was found but empty, so set the existing record to the newly loaded object + loadedObjects[sabrePath] = loadedObject; + } + else + { + // We know that it's not already in the cache, so add it to the end + loadedObjects.Add(sabrePath, loadedObject); + } + } + return loadedObject; + } + + /// + /// Gets an icon for a primitive brush to be displayed on a button + /// + /// The icon texture. + /// Primitive Brush type. + public static Texture2D GetButtonTexture(PrimitiveBrushType brushType) + { if (brushType == PrimitiveBrushType.Prism) return ButtonPrismTexture; else if (brushType == PrimitiveBrushType.Cylinder) @@ -92,94 +93,97 @@ public static Texture2D GetButtonTexture(PrimitiveBrushType brushType) return ButtonSphereTexture; else if (brushType == PrimitiveBrushType.Cone) return ButtonConeTexture; + else if (brushType == PrimitiveBrushType.Capsule) + return ButtonCapsuleTexture; else return ButtonCubeTexture; - } - -#region Accessors - public static Texture2D ClearTexture - { - get - { - if(clearTexture == null) - { - clearTexture = new Texture2D(2,2, TextureFormat.RGBA32, false); - for (int x = 0; x < clearTexture.width; x++) - { - for (int y = 0; y < clearTexture.height; y++) - { - clearTexture.SetPixel(x,y,Color.clear); - } - } - clearTexture.Apply(); - } - return clearTexture; - } - } - - public static Texture2D HalfWhiteTexture - { - get - { - if(halfWhiteTexture == null) - { - halfWhiteTexture = new Texture2D(2,2, TextureFormat.RGBA32, false); - for (int x = 0; x < halfWhiteTexture.width; x++) - { - for (int y = 0; y < halfWhiteTexture.height; y++) - { - halfWhiteTexture.SetPixel(x,y,new Color(1,1,1,0.5f)); - } - } - halfWhiteTexture.Apply(); - } - return halfWhiteTexture; - } - } - - public static Texture2D HalfBlackTexture - { - get - { - if(halfBlackTexture == null) - { - halfBlackTexture = new Texture2D(2,2, TextureFormat.RGBA32, false); - for (int x = 0; x < halfBlackTexture.width; x++) - { - for (int y = 0; y < halfBlackTexture.height; y++) - { - halfBlackTexture.SetPixel(x,y,new Color(0,0,0,0.5f)); - } - } - halfBlackTexture.Apply(); - } - return halfBlackTexture; - } - } - - public static Texture2D AddIconTexture - { - get - { + } + + #region Accessors + + public static Texture2D ClearTexture + { + get + { + if (clearTexture == null) + { + clearTexture = new Texture2D(2, 2, TextureFormat.RGBA32, false); + for (int x = 0; x < clearTexture.width; x++) + { + for (int y = 0; y < clearTexture.height; y++) + { + clearTexture.SetPixel(x, y, Color.clear); + } + } + clearTexture.Apply(); + } + return clearTexture; + } + } + + public static Texture2D HalfWhiteTexture + { + get + { + if (halfWhiteTexture == null) + { + halfWhiteTexture = new Texture2D(2, 2, TextureFormat.RGBA32, false); + for (int x = 0; x < halfWhiteTexture.width; x++) + { + for (int y = 0; y < halfWhiteTexture.height; y++) + { + halfWhiteTexture.SetPixel(x, y, new Color(1, 1, 1, 0.5f)); + } + } + halfWhiteTexture.Apply(); + } + return halfWhiteTexture; + } + } + + public static Texture2D HalfBlackTexture + { + get + { + if (halfBlackTexture == null) + { + halfBlackTexture = new Texture2D(2, 2, TextureFormat.RGBA32, false); + for (int x = 0; x < halfBlackTexture.width; x++) + { + for (int y = 0; y < halfBlackTexture.height; y++) + { + halfBlackTexture.SetPixel(x, y, new Color(0, 0, 0, 0.5f)); + } + } + halfBlackTexture.Apply(); + } + return halfBlackTexture; + } + } + + public static Texture2D AddIconTexture + { + get + { return (Texture2D)LoadObject("Gizmos/Add.png"); - } - } - - public static Texture2D SubtractIconTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/Subtract.png"); - } - } - - public static Texture2D NoCSGIconTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/NoCSG.png"); - } - } + } + } + + public static Texture2D SubtractIconTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/Subtract.png"); + } + } + + public static Texture2D NoCSGIconTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/NoCSG.png"); + } + } public static Texture2D GroupIconTexture { @@ -189,53 +193,53 @@ public static Texture2D GroupIconTexture } } - public static Texture2D DialogOverlayTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/DialogOverlay75.png"); - } - } - - public static Texture2D DialogOverlayRetinaTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/DialogOverlay75@2x.png"); - } - } - - public static Texture2D ButtonCubeTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/ButtonCube.png"); - } - } - - public static Texture2D ButtonPrismTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/ButtonPrism.png"); - } - } - - public static Texture2D ButtonCylinderTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/ButtonCylinder.png"); - } - } - - public static Texture2D ButtonSphereTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/ButtonSphere.png"); - } - } + public static Texture2D DialogOverlayTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/DialogOverlay75.png"); + } + } + + public static Texture2D DialogOverlayRetinaTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/DialogOverlay75@2x.png"); + } + } + + public static Texture2D ButtonCubeTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonCube.png"); + } + } + + public static Texture2D ButtonPrismTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonPrism.png"); + } + } + + public static Texture2D ButtonCylinderTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonCylinder.png"); + } + } + + public static Texture2D ButtonSphereTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonSphere.png"); + } + } public static Texture2D ButtonIcoSphereTexture { @@ -245,13 +249,13 @@ public static Texture2D ButtonIcoSphereTexture } } - public static Texture2D ButtonStairsTexture - { - get - { - return (Texture2D)LoadObject("Gizmos/ButtonStairs.png"); - } - } + public static Texture2D ButtonStairsTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonStairs.png"); + } + } public static Texture2D ButtonConeTexture { @@ -260,7 +264,15 @@ public static Texture2D ButtonConeTexture return (Texture2D)LoadObject("Gizmos/ButtonCone.png"); } } - + + public static Texture2D ButtonCapsuleTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ButtonCapsule.png"); + } + } + public static Texture2D ButtonCurvedStairsTexture { get @@ -289,7 +301,7 @@ public static Texture2D GroupHeaderRetinaTexture { get { - return (Texture2D)LoadObject("Gizmos/GroupHeader@2x.png"); + return (Texture2D)LoadObject("Gizmos/GroupHeader@2x.png"); } } @@ -470,49 +482,49 @@ public static Texture2D ShapeEditorHomeTexture } public static Material GetExcludedMaterial() - { - if (excludedMaterial == null) - { - excludedMaterial = new Material(Shader.Find("SabreCSG/SeeExcluded")); - excludedMaterial.hideFlags = HideFlags.HideAndDontSave; - excludedMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - excludedMaterial.mainTexture = (Texture2D)LoadObject("Internal/Excluded.png"); - } - return excludedMaterial; - } - - public static Material GetGreyscaleUIMaterial() - { - if (greyscaleUIMaterial == null) - { - greyscaleUIMaterial = new Material(Shader.Find("Hidden/Grayscale-GUITexture")); - greyscaleUIMaterial.hideFlags = HideFlags.HideAndDontSave; - greyscaleUIMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - } - return greyscaleUIMaterial; - } - - public static Material GetSelectedBrushMaterial() - { - if (selectedBrushMaterial == null) - { - selectedBrushMaterial = new Material(Shader.Find("SabreCSG/Line")); - selectedBrushMaterial.hideFlags = HideFlags.HideAndDontSave; - selectedBrushMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - } - return selectedBrushMaterial; - } - - public static Material GetSelectedBrushDashedMaterial() - { - if (selectedBrushDashedMaterial == null) - { - selectedBrushDashedMaterial = new Material(Shader.Find("SabreCSG/Line Dashed")); - selectedBrushDashedMaterial.hideFlags = HideFlags.HideAndDontSave; - selectedBrushDashedMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - } - return selectedBrushDashedMaterial; - } + { + if (excludedMaterial == null) + { + excludedMaterial = new Material(Shader.Find("SabreCSG/SeeExcluded")); + excludedMaterial.hideFlags = HideFlags.HideAndDontSave; + excludedMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + excludedMaterial.mainTexture = (Texture2D)LoadObject("Internal/Excluded.png"); + } + return excludedMaterial; + } + + public static Material GetGreyscaleUIMaterial() + { + if (greyscaleUIMaterial == null) + { + greyscaleUIMaterial = new Material(Shader.Find("Hidden/Grayscale-GUITexture")); + greyscaleUIMaterial.hideFlags = HideFlags.HideAndDontSave; + greyscaleUIMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + } + return greyscaleUIMaterial; + } + + public static Material GetSelectedBrushMaterial() + { + if (selectedBrushMaterial == null) + { + selectedBrushMaterial = new Material(Shader.Find("SabreCSG/Line")); + selectedBrushMaterial.hideFlags = HideFlags.HideAndDontSave; + selectedBrushMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + } + return selectedBrushMaterial; + } + + public static Material GetSelectedBrushDashedMaterial() + { + if (selectedBrushDashedMaterial == null) + { + selectedBrushDashedMaterial = new Material(Shader.Find("SabreCSG/Line Dashed")); + selectedBrushDashedMaterial.hideFlags = HideFlags.HideAndDontSave; + selectedBrushDashedMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + } + return selectedBrushDashedMaterial; + } public static Material GetSelectedBrushDashedAlphaMaterial() { @@ -526,97 +538,99 @@ public static Material GetSelectedBrushDashedAlphaMaterial() } public static Material GetGizmoMaterial() - { - if (gizmoMaterial == null) - { - Shader shader = Shader.Find("SabreCSG/Handle"); - gizmoMaterial = new Material(shader); - gizmoMaterial.hideFlags = HideFlags.HideAndDontSave; - gizmoMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - gizmoMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/SquareGizmo8x8.png"); - } - return gizmoMaterial; - } - - public static Material GetVertexMaterial() - { - if (vertexMaterial == null) - { - Shader shader = Shader.Find("SabreCSG/Handle"); - vertexMaterial = new Material(shader); - vertexMaterial.hideFlags = HideFlags.HideAndDontSave; - vertexMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - vertexMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/CircleGizmo8x8.png"); - } - return vertexMaterial; - } - - public static Material GetCircleMaterial() - { - if (circleMaterial == null) - { - Shader shader = Shader.Find("SabreCSG/Handle"); - circleMaterial = new Material(shader); - circleMaterial.hideFlags = HideFlags.HideAndDontSave; - circleMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - circleMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/Circle.png"); - } - return circleMaterial; - } - - public static Material GetCircleOutlineMaterial() - { - if (circleOutlineMaterial == null) - { - Shader shader = Shader.Find("SabreCSG/Handle"); - circleOutlineMaterial = new Material(shader); - circleOutlineMaterial.hideFlags = HideFlags.HideAndDontSave; - circleOutlineMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - circleOutlineMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/CircleOutline.png"); - } - return circleOutlineMaterial; - } - - public static Material GetPreviewMaterial() - { - if(previewMaterial == null) - { - Shader shader = Shader.Find("SabreCSG/Preview"); - - previewMaterial = new Material(shader); - previewMaterial.hideFlags = HideFlags.HideAndDontSave; - previewMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - } - - return previewMaterial; - } - - public static Material GetNoCSGMaterial() - { - return (Material)LoadObject("Materials/NoCSG.mat"); - } - - public static Material GetAddMaterial() - { - return (Material)LoadObject("Materials/Add.mat"); - } - - public static Material GetSubtractMaterial() - { - return (Material)LoadObject("Materials/Subtract.mat"); - } - - public static Material GetPlaneMaterial() - { - if (planeMaterial == null) - { - planeMaterial = new Material(Shader.Find("SabreCSG/Plane")); - planeMaterial.hideFlags = HideFlags.HideAndDontSave; - planeMaterial.shader.hideFlags = HideFlags.HideAndDontSave; - } - return planeMaterial; - } -#endregion - } + { + if (gizmoMaterial == null) + { + Shader shader = Shader.Find("SabreCSG/Handle"); + gizmoMaterial = new Material(shader); + gizmoMaterial.hideFlags = HideFlags.HideAndDontSave; + gizmoMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + gizmoMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/SquareGizmo8x8.png"); + } + return gizmoMaterial; + } + + public static Material GetVertexMaterial() + { + if (vertexMaterial == null) + { + Shader shader = Shader.Find("SabreCSG/Handle"); + vertexMaterial = new Material(shader); + vertexMaterial.hideFlags = HideFlags.HideAndDontSave; + vertexMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + vertexMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/CircleGizmo8x8.png"); + } + return vertexMaterial; + } + + public static Material GetCircleMaterial() + { + if (circleMaterial == null) + { + Shader shader = Shader.Find("SabreCSG/Handle"); + circleMaterial = new Material(shader); + circleMaterial.hideFlags = HideFlags.HideAndDontSave; + circleMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + circleMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/Circle.png"); + } + return circleMaterial; + } + + public static Material GetCircleOutlineMaterial() + { + if (circleOutlineMaterial == null) + { + Shader shader = Shader.Find("SabreCSG/Handle"); + circleOutlineMaterial = new Material(shader); + circleOutlineMaterial.hideFlags = HideFlags.HideAndDontSave; + circleOutlineMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + circleOutlineMaterial.mainTexture = (Texture2D)LoadObject("Gizmos/CircleOutline.png"); + } + return circleOutlineMaterial; + } + + public static Material GetPreviewMaterial() + { + if (previewMaterial == null) + { + Shader shader = Shader.Find("SabreCSG/Preview"); + + previewMaterial = new Material(shader); + previewMaterial.hideFlags = HideFlags.HideAndDontSave; + previewMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + } + + return previewMaterial; + } + + public static Material GetNoCSGMaterial() + { + return (Material)LoadObject("Materials/NoCSG.mat"); + } + + public static Material GetAddMaterial() + { + return (Material)LoadObject("Materials/Add.mat"); + } + + public static Material GetSubtractMaterial() + { + return (Material)LoadObject("Materials/Subtract.mat"); + } + + public static Material GetPlaneMaterial() + { + if (planeMaterial == null) + { + planeMaterial = new Material(Shader.Find("SabreCSG/Plane")); + planeMaterial.hideFlags = HideFlags.HideAndDontSave; + planeMaterial.shader.hideFlags = HideFlags.HideAndDontSave; + } + return planeMaterial; + } + + #endregion Accessors + } } + #endif \ No newline at end of file From 8784d2dab71636f92d2675939eea7da964460d80 Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Fri, 1 Jun 2018 02:41:11 -0500 Subject: [PATCH 02/26] Add ReflectionProbeUsage settings Feature addition in reference to https://github.com/sabresaurus/SabreCSG/issues/82 + Added a group to CSGModelInspector "Common Fixes" + Added a toggle + enum to change ReflectionProbeUsage for the entire CSGModel --- Scripts/Core/BuildEngine/MeshGroupManager.cs | 23 ++++++++++++++---- Scripts/Core/CSGBuildSettings.cs | 10 +++++++- .../Editor/Inspectors/CSGModelInspector.cs | 24 ++++++++++++++++--- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Scripts/Core/BuildEngine/MeshGroupManager.cs b/Scripts/Core/BuildEngine/MeshGroupManager.cs index 8c71974d..89f2621f 100755 --- a/Scripts/Core/BuildEngine/MeshGroupManager.cs +++ b/Scripts/Core/BuildEngine/MeshGroupManager.cs @@ -6,6 +6,7 @@ using System.Reflection; using UnityEngine.SceneManagement; using System.IO; +using UnityEngine.Rendering; namespace Sabresaurus.SabreCSG { @@ -186,12 +187,24 @@ internal static void FinalizeVisualMesh(Transform meshGroupHolder, OnFinalizeVisualMesh(newGameObject, mesh); } - newGameObject.AddComponent().sharedMesh = mesh; - MeshRenderer meshRenderer = newGameObject.AddComponent(); + MeshRenderer meshRenderer = newGameObject.AddComponent(); meshRenderer.sharedMaterial = material; - meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode; -// newGameObject.transform.parent = meshGroupHolder; + meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode; + //newGameObject.transform.parent = meshGroupHolder; + + // we only want to use custom reflection probe settings for the visual model if we ask for them, + // otherwise we will just use the settings already assigned. + if( !buildSettings.UseModelReflectionProbes ) + { + meshRenderer.reflectionProbeUsage = buildSettings.ReflectionProbeUsage; + } + else + { + // use unity default + meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.BlendProbes; + } + newGameObject.transform.SetParent(meshGroupHolder, false); #if UNITY_EDITOR @@ -465,4 +478,4 @@ internal static void TriangulatePolygons(bool individualVertices, List } } } -#endif \ No newline at end of file +#endif diff --git a/Scripts/Core/CSGBuildSettings.cs b/Scripts/Core/CSGBuildSettings.cs index e96085c2..6805c921 100755 --- a/Scripts/Core/CSGBuildSettings.cs +++ b/Scripts/Core/CSGBuildSettings.cs @@ -76,6 +76,9 @@ block the incoming light and prevent the light leak artifact from appearing. public ShadowCastingMode ShadowCastingMode = ShadowCastingMode.TwoSided; + public bool UseModelReflectionProbes = true; + public ReflectionProbeUsage ReflectionProbeUsage = ReflectionProbeUsage.BlendProbes; + // What default physics material to use on collision meshes public PhysicMaterial DefaultPhysicsMaterial = null; @@ -147,7 +150,12 @@ public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings set if(settings1.ShadowCastingMode != settings2.ShadowCastingMode) { return true; - } + } + + if( settings1.UseModelReflectionProbes != settings2.UseModelReflectionProbes ) + { + return true; + } // Don't compare IsBuilt diff --git a/Scripts/Editor/Inspectors/CSGModelInspector.cs b/Scripts/Editor/Inspectors/CSGModelInspector.cs index 6c6abed7..41a3f0ba 100644 --- a/Scripts/Editor/Inspectors/CSGModelInspector.cs +++ b/Scripts/Editor/Inspectors/CSGModelInspector.cs @@ -24,6 +24,9 @@ public class CSGModelInspector : Editor private SerializedProperty shadowCastingModeProperty; + private SerializedProperty useModelReflectionProbesProperty; + private SerializedProperty reflectionProbeUsageProperty; + private SerializedProperty defaultPhysicsMaterialProperty; private SerializedProperty defaultVisualMaterialProperty; @@ -52,6 +55,9 @@ public void OnEnable() shadowCastingModeProperty = serializedObject.FindProperty("buildSettings.ShadowCastingMode"); + useModelReflectionProbesProperty = serializedObject.FindProperty( "buildSettings.UseModelReflectionProbes" ); + reflectionProbeUsageProperty = serializedObject.FindProperty( "buildSettings.ReflectionProbeUsage" ); + defaultPhysicsMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultPhysicsMaterial"); defaultVisualMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultVisualMaterial"); @@ -91,7 +97,8 @@ public override void OnInspectorGUI() EditorGUIUtility.labelWidth = 0; GUI.enabled = true; - EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode")); + + EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode")); // Experimental build settings to enable features that are not yet completely stable GUILayout.Label("Experimental", EditorStyles.boldLabel); @@ -100,7 +107,18 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(optimizeGeometryProperty, new GUIContent("Optimize Geometry")); EditorGUILayout.PropertyField(saveMeshesAsAssetsProperty, new GUIContent("Save Meshes As Assets")); EditorGUI.indentLevel = 0; - } + + GUILayout.Label( "Common Fixes", EditorStyles.boldLabel ); + EditorGUI.indentLevel = 1; + + EditorGUILayout.PropertyField( useModelReflectionProbesProperty, new GUIContent( "Default Reflection Probe Settings", "Use the default reflection probe usage settings (Blend Probes)?" ) ); + + GUI.enabled = !useModelReflectionProbesProperty.boolValue; + EditorGUILayout.PropertyField( reflectionProbeUsageProperty, new GUIContent( "Reflection Probes" ) ); + GUI.enabled = true; + + EditorGUI.indentLevel = 0; + } using (new NamedVerticalScope("Default Material")) { @@ -391,4 +409,4 @@ private void GuiLayoutEndImporterSection() EditorGUILayout.EndVertical(); } } -} \ No newline at end of file +} From 4fca65ee93cf0d6bc2832175b1da3e602f0c5482 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Fri, 1 Jun 2018 10:50:52 +0200 Subject: [PATCH 03/26] The checkbox will reset the reflection probes to Blend Probes at every rebuild, so I decided to remove it and just have this enum as the default way to manage it. Generic code cleanup of CSGBuildSettings and MeshGroupManager (sorry, I know that makes it hard to read, it was an accident!). --- Scripts/Core/BuildEngine/MeshGroupManager.cs | 856 +++++++++--------- Scripts/Core/CSGBuildSettings.cs | 193 ++-- .../Editor/Inspectors/CSGModelInspector.cs | 25 +- 3 files changed, 526 insertions(+), 548 deletions(-) diff --git a/Scripts/Core/BuildEngine/MeshGroupManager.cs b/Scripts/Core/BuildEngine/MeshGroupManager.cs index 89f2621f..ee2ae1fc 100755 --- a/Scripts/Core/BuildEngine/MeshGroupManager.cs +++ b/Scripts/Core/BuildEngine/MeshGroupManager.cs @@ -1,4 +1,5 @@ #if UNITY_EDITOR || RUNTIME_CSG + using UnityEngine; using System.Collections; using System.Collections.Generic; @@ -10,472 +11,459 @@ namespace Sabresaurus.SabreCSG { - internal static class MeshGroupManager - { - internal static Action OnFinalizeVisualMesh = null; - internal static Action OnFinalizeCollisionMesh = null; + internal static class MeshGroupManager + { + internal static Action OnFinalizeVisualMesh = null; + internal static Action OnFinalizeCollisionMesh = null; - private const int MESH_VERTEX_LIMIT = 65500; + private const int MESH_VERTEX_LIMIT = 65500; - internal static void Cleanup(Transform meshGroupHolder) - { - // Destroy all the old meshes to prevent them leaking - MeshFilter[] filters = meshGroupHolder.GetComponentsInChildren(); - MeshCollider[] colliders = meshGroupHolder.GetComponentsInChildren(); + internal static void Cleanup(Transform meshGroupHolder) + { + // Destroy all the old meshes to prevent them leaking + MeshFilter[] filters = meshGroupHolder.GetComponentsInChildren(); + MeshCollider[] colliders = meshGroupHolder.GetComponentsInChildren(); - for (int i = 0; i < filters.Length; i++) - { + for (int i = 0; i < filters.Length; i++) + { #if UNITY_EDITOR - if(filters[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(filters[i].sharedMesh)) -#endif - GameObject.DestroyImmediate(filters[i].sharedMesh); - } + if (filters[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(filters[i].sharedMesh)) +#endif + GameObject.DestroyImmediate(filters[i].sharedMesh); + } - for (int i = 0; i < colliders.Length; i++) - { + for (int i = 0; i < colliders.Length; i++) + { #if UNITY_EDITOR - if(colliders[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(colliders[i].sharedMesh)) -#endif - GameObject.DestroyImmediate(colliders[i].sharedMesh); - } - - // Finally destroy the game objects and components - meshGroupHolder.DestroyChildrenImmediate(); - } - - internal static void BuildVisual(Transform meshGroupHolder, - PolygonEntry[] polygonIndex, - CSGBuildSettings buildSettings, - CSGBuildContext.BuildContext buildContext, - MaterialMeshDictionary materialMeshDictionary) - { - materialMeshDictionary.Clear(); - - // Reset statistics - buildContext.buildMetrics.TotalMeshes = 0; - buildContext.buildMetrics.TotalVertices = 0; - buildContext.buildMetrics.TotalTriangles = 0; - - Dictionary> polygonMaterialTable = new Dictionary>(); - - for (int i = 0; i < polygonIndex.Length; i++) - { - PolygonEntry entry = polygonIndex[i]; - - if(PolygonEntry.IsValid(entry) - && entry.Positions.Length > 0 - && !entry.ExcludeFromBuild) // Skip polygons that weren't built - { - Material material = entry.Material; - - if(material == null) - { - material = buildSettings.DefaultVisualMaterial; - } - - if(polygonMaterialTable.ContainsKey(material)) - { - polygonMaterialTable[material].Add(entry); - } - else - { - polygonMaterialTable.Add(material, new List() { entry } ); - } - } - } - - foreach (KeyValuePair> row in polygonMaterialTable) - { - Mesh mesh = new Mesh(); - - List positionsList = new List(); - List normalsList = new List(); - List uvList = new List(); - List colorsList = new List(); - List trianglesList = new List(); - - for (int i = 0; i < row.Value.Count; i++) - { - int positionOffset = positionsList.Count; - int triangleOffset = trianglesList.Count; - - PolygonEntry polygonEntry = row.Value[i]; - if(polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) - { - FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); - mesh = new Mesh(); - positionsList.Clear(); - normalsList.Clear(); - uvList.Clear(); - colorsList.Clear(); - trianglesList.Clear(); - positionOffset = 0; - } - positionsList.AddRange(polygonEntry.Positions); - normalsList.AddRange(polygonEntry.Normals); - uvList.AddRange(polygonEntry.UV); - colorsList.AddRange(polygonEntry.Colors); - - for (int j = 0; j < polygonEntry.Triangles.Length; j++) - { - trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); - } - - row.Value[i].BuiltMesh = mesh; - row.Value[i].BuiltVertexOffset = positionOffset; - row.Value[i].BuiltTriangleOffset = triangleOffset; - } - - FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); - } - } - - internal static void FinalizeVisualMesh(Transform meshGroupHolder, - Mesh mesh, - Material material, - CSGBuildSettings buildSettings, - CSGBuildContext.BuildContext buildContext, - List positionsList, - List normalsList, - List uvList, - List colorsList, - List trianglesList, - MaterialMeshDictionary materialMeshDictionary) - { - Vector3[] positionsArray = new Vector3[positionsList.Count]; - Vector3[] normalsArray = new Vector3[normalsList.Count]; - Vector2[] uvArray = new Vector2[uvList.Count]; - Color[] colorsArray = new Color[colorsList.Count]; - int[] trianglesArray = new int[trianglesList.Count]; - - positionsList.CopyTo(positionsArray); - normalsList.CopyTo(normalsArray); - uvList.CopyTo(uvArray); - trianglesList.CopyTo(trianglesArray); - colorsList.CopyTo(colorsArray); - - mesh.vertices = positionsArray; - mesh.normals = normalsArray; - mesh.uv = uvArray; - mesh.colors = colorsArray; - - - if(meshGroupHolder.position != Vector3.zero - || meshGroupHolder.rotation != Quaternion.identity - || meshGroupHolder.lossyScale != Vector3.one) - { - mesh.LocalizeToTransform(meshGroupHolder); - } - - mesh.triangles = trianglesArray; - - if (buildSettings.GenerateTangents) - { - // Generate tangents, necessary for some shaders - mesh.GenerateTangents(); - } - - buildContext.buildMetrics.TotalMeshes++; - buildContext.buildMetrics.TotalVertices += positionsArray.Length; - buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3; - - GameObject newGameObject = new GameObject("MaterialMesh"); - - // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags - if(OnFinalizeVisualMesh != null) - { - OnFinalizeVisualMesh(newGameObject, mesh); - } - - newGameObject.AddComponent().sharedMesh = mesh; - MeshRenderer meshRenderer = newGameObject.AddComponent(); + if (colliders[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(colliders[i].sharedMesh)) +#endif + GameObject.DestroyImmediate(colliders[i].sharedMesh); + } + + // Finally destroy the game objects and components + meshGroupHolder.DestroyChildrenImmediate(); + } + + internal static void BuildVisual(Transform meshGroupHolder, + PolygonEntry[] polygonIndex, + CSGBuildSettings buildSettings, + CSGBuildContext.BuildContext buildContext, + MaterialMeshDictionary materialMeshDictionary) + { + materialMeshDictionary.Clear(); + + // Reset statistics + buildContext.buildMetrics.TotalMeshes = 0; + buildContext.buildMetrics.TotalVertices = 0; + buildContext.buildMetrics.TotalTriangles = 0; + + Dictionary> polygonMaterialTable = new Dictionary>(); + + for (int i = 0; i < polygonIndex.Length; i++) + { + PolygonEntry entry = polygonIndex[i]; + + if (PolygonEntry.IsValid(entry) + && entry.Positions.Length > 0 + && !entry.ExcludeFromBuild) // Skip polygons that weren't built + { + Material material = entry.Material; + + if (material == null) + { + material = buildSettings.DefaultVisualMaterial; + } + + if (polygonMaterialTable.ContainsKey(material)) + { + polygonMaterialTable[material].Add(entry); + } + else + { + polygonMaterialTable.Add(material, new List() { entry }); + } + } + } + + foreach (KeyValuePair> row in polygonMaterialTable) + { + Mesh mesh = new Mesh(); + + List positionsList = new List(); + List normalsList = new List(); + List uvList = new List(); + List colorsList = new List(); + List trianglesList = new List(); + + for (int i = 0; i < row.Value.Count; i++) + { + int positionOffset = positionsList.Count; + int triangleOffset = trianglesList.Count; + + PolygonEntry polygonEntry = row.Value[i]; + if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) + { + FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); + mesh = new Mesh(); + positionsList.Clear(); + normalsList.Clear(); + uvList.Clear(); + colorsList.Clear(); + trianglesList.Clear(); + positionOffset = 0; + } + positionsList.AddRange(polygonEntry.Positions); + normalsList.AddRange(polygonEntry.Normals); + uvList.AddRange(polygonEntry.UV); + colorsList.AddRange(polygonEntry.Colors); + + for (int j = 0; j < polygonEntry.Triangles.Length; j++) + { + trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); + } + + row.Value[i].BuiltMesh = mesh; + row.Value[i].BuiltVertexOffset = positionOffset; + row.Value[i].BuiltTriangleOffset = triangleOffset; + } + + FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary); + } + } + + internal static void FinalizeVisualMesh(Transform meshGroupHolder, + Mesh mesh, + Material material, + CSGBuildSettings buildSettings, + CSGBuildContext.BuildContext buildContext, + List positionsList, + List normalsList, + List uvList, + List colorsList, + List trianglesList, + MaterialMeshDictionary materialMeshDictionary) + { + Vector3[] positionsArray = new Vector3[positionsList.Count]; + Vector3[] normalsArray = new Vector3[normalsList.Count]; + Vector2[] uvArray = new Vector2[uvList.Count]; + Color[] colorsArray = new Color[colorsList.Count]; + int[] trianglesArray = new int[trianglesList.Count]; + + positionsList.CopyTo(positionsArray); + normalsList.CopyTo(normalsArray); + uvList.CopyTo(uvArray); + trianglesList.CopyTo(trianglesArray); + colorsList.CopyTo(colorsArray); + + mesh.vertices = positionsArray; + mesh.normals = normalsArray; + mesh.uv = uvArray; + mesh.colors = colorsArray; + + if (meshGroupHolder.position != Vector3.zero + || meshGroupHolder.rotation != Quaternion.identity + || meshGroupHolder.lossyScale != Vector3.one) + { + mesh.LocalizeToTransform(meshGroupHolder); + } + + mesh.triangles = trianglesArray; + + if (buildSettings.GenerateTangents) + { + // Generate tangents, necessary for some shaders + mesh.GenerateTangents(); + } + + buildContext.buildMetrics.TotalMeshes++; + buildContext.buildMetrics.TotalVertices += positionsArray.Length; + buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3; + + GameObject newGameObject = new GameObject("MaterialMesh"); + + // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags + if (OnFinalizeVisualMesh != null) + { + OnFinalizeVisualMesh(newGameObject, mesh); + } + + newGameObject.AddComponent().sharedMesh = mesh; + MeshRenderer meshRenderer = newGameObject.AddComponent(); meshRenderer.sharedMaterial = material; - meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode; - //newGameObject.transform.parent = meshGroupHolder; - - // we only want to use custom reflection probe settings for the visual model if we ask for them, - // otherwise we will just use the settings already assigned. - if( !buildSettings.UseModelReflectionProbes ) - { - meshRenderer.reflectionProbeUsage = buildSettings.ReflectionProbeUsage; - } - else - { - // use unity default - meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.BlendProbes; - } - - newGameObject.transform.SetParent(meshGroupHolder, false); + meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode; + meshRenderer.reflectionProbeUsage = buildSettings.ReflectionProbeUsage; + //newGameObject.transform.parent = meshGroupHolder; + + newGameObject.transform.SetParent(meshGroupHolder, false); #if UNITY_EDITOR - if(buildSettings.SaveMeshesAsAssets) + if (buildSettings.SaveMeshesAsAssets) { // Make sure the folder exists to save into string path = SceneManager.GetActiveScene().path; path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)); - if(!Directory.Exists(path)) + if (!Directory.Exists(path)) { - UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); + UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); } // Save to a file rather than leaving it as a scene asset UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "VisualMesh" + materialMeshDictionary.MeshCount + ".asset")); } #endif - materialMeshDictionary.Add(material, mesh, newGameObject); - } - - internal static void BuildCollision(Transform meshGroupHolder, - PolygonEntry[] polygonIndex, - CSGBuildSettings buildSettings, - List collisionMeshDictionary) - { - collisionMeshDictionary.Clear(); - - if(polygonIndex.Length > 0) - { - Mesh mesh = new Mesh(); - List positionsList = new List(); - List normalsList = new List(); - List uvList = new List(); - List trianglesList = new List(); - - for (int i = 0; i < polygonIndex.Length; i++) - { - if(polygonIndex[i] != null) - { - int positionOffset = positionsList.Count; - int triangleOffset = trianglesList.Count; - - PolygonEntry polygonEntry = polygonIndex[i]; - - if(PolygonEntry.IsValid(polygonEntry) - && polygonEntry.Positions.Length > 0 - && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built - { - if(polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) - { - FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); - mesh = new Mesh(); - positionsList.Clear(); - normalsList.Clear(); - uvList.Clear(); - trianglesList.Clear(); - positionOffset = 0; - } - positionsList.AddRange(polygonEntry.Positions); - normalsList.AddRange(polygonEntry.Normals); - uvList.AddRange(polygonEntry.UV); - - for (int j = 0; j < polygonEntry.Triangles.Length; j++) - { - trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); - } - - polygonEntry.BuiltMesh = mesh; - polygonEntry.BuiltVertexOffset = positionOffset; - polygonEntry.BuiltTriangleOffset = triangleOffset; - } - } - } - FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); - } - } - - internal static void FinalizeCollisionMesh(Transform meshGroupHolder, - Mesh mesh, - CSGBuildSettings buildSettings, - List positionsList, - List normalsList, - List uvList, - List trianglesList, - List collisionMeshDictionary) - { - Vector3[] positionsArray = new Vector3[positionsList.Count]; - Vector3[] normalsArray = new Vector3[normalsList.Count]; - Vector2[] uvArray = new Vector2[uvList.Count]; - int[] trianglesArray = new int[trianglesList.Count]; - - positionsList.CopyTo(positionsArray); - normalsList.CopyTo(normalsArray); - uvList.CopyTo(uvArray); - trianglesList.CopyTo(trianglesArray); - - mesh.vertices = positionsArray; - mesh.normals = normalsArray; - mesh.uv = uvArray; - - - if(meshGroupHolder.position != Vector3.zero - || meshGroupHolder.rotation != Quaternion.identity - || meshGroupHolder.lossyScale != Vector3.one) - { - mesh.LocalizeToTransform(meshGroupHolder); - } - - mesh.triangles = trianglesArray; - - GameObject newGameObject = new GameObject("CollisionMesh"); - - // Allow any editor dependent code to fire, e.g. assigning physics materials - if(OnFinalizeCollisionMesh != null) - { - OnFinalizeCollisionMesh(newGameObject, mesh); - } - - MeshCollider meshCollider = newGameObject.AddComponent(); - meshCollider.sharedMesh = mesh; - // Assign the physics material from build settings - meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial; - // Reparent - newGameObject.transform.SetParent(meshGroupHolder, false);//.parent = meshGroupHolder; + materialMeshDictionary.Add(material, mesh, newGameObject); + } + + internal static void BuildCollision(Transform meshGroupHolder, + PolygonEntry[] polygonIndex, + CSGBuildSettings buildSettings, + List collisionMeshDictionary) + { + collisionMeshDictionary.Clear(); + + if (polygonIndex.Length > 0) + { + Mesh mesh = new Mesh(); + List positionsList = new List(); + List normalsList = new List(); + List uvList = new List(); + List trianglesList = new List(); + for (int i = 0; i < polygonIndex.Length; i++) + { + if (polygonIndex[i] != null) + { + int positionOffset = positionsList.Count; + int triangleOffset = trianglesList.Count; + + PolygonEntry polygonEntry = polygonIndex[i]; + + if (PolygonEntry.IsValid(polygonEntry) + && polygonEntry.Positions.Length > 0 + && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built + { + if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT) + { + FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); + mesh = new Mesh(); + positionsList.Clear(); + normalsList.Clear(); + uvList.Clear(); + trianglesList.Clear(); + positionOffset = 0; + } + positionsList.AddRange(polygonEntry.Positions); + normalsList.AddRange(polygonEntry.Normals); + uvList.AddRange(polygonEntry.UV); + + for (int j = 0; j < polygonEntry.Triangles.Length; j++) + { + trianglesList.Add(polygonEntry.Triangles[j] + positionOffset); + } + + polygonEntry.BuiltMesh = mesh; + polygonEntry.BuiltVertexOffset = positionOffset; + polygonEntry.BuiltTriangleOffset = triangleOffset; + } + } + } + FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary); + } + } + + internal static void FinalizeCollisionMesh(Transform meshGroupHolder, + Mesh mesh, + CSGBuildSettings buildSettings, + List positionsList, + List normalsList, + List uvList, + List trianglesList, + List collisionMeshDictionary) + { + Vector3[] positionsArray = new Vector3[positionsList.Count]; + Vector3[] normalsArray = new Vector3[normalsList.Count]; + Vector2[] uvArray = new Vector2[uvList.Count]; + int[] trianglesArray = new int[trianglesList.Count]; + + positionsList.CopyTo(positionsArray); + normalsList.CopyTo(normalsArray); + uvList.CopyTo(uvArray); + trianglesList.CopyTo(trianglesArray); + + mesh.vertices = positionsArray; + mesh.normals = normalsArray; + mesh.uv = uvArray; + + if (meshGroupHolder.position != Vector3.zero + || meshGroupHolder.rotation != Quaternion.identity + || meshGroupHolder.lossyScale != Vector3.one) + { + mesh.LocalizeToTransform(meshGroupHolder); + } + + mesh.triangles = trianglesArray; + + GameObject newGameObject = new GameObject("CollisionMesh"); + + // Allow any editor dependent code to fire, e.g. assigning physics materials + if (OnFinalizeCollisionMesh != null) + { + OnFinalizeCollisionMesh(newGameObject, mesh); + } + + MeshCollider meshCollider = newGameObject.AddComponent(); + meshCollider.sharedMesh = mesh; + // Assign the physics material from build settings + meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial; + // Reparent + newGameObject.transform.SetParent(meshGroupHolder, false);//.parent = meshGroupHolder; #if UNITY_EDITOR - if(buildSettings.SaveMeshesAsAssets) + if (buildSettings.SaveMeshesAsAssets) { // Make sure the folder exists to save into string path = SceneManager.GetActiveScene().path; path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)); - if(!Directory.Exists(path)) + if (!Directory.Exists(path)) { - UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); + UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path)); } // Save to a file rather than leaving it as a scene asset UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "CollisionMesh" + collisionMeshDictionary.Count + ".asset")); } #endif - collisionMeshDictionary.Add(mesh); - } - - internal static void TriangulateNewPolygons(bool individualVertices, Dictionary> groupedPolygons, PolygonEntry[] polygonIndex) - { - foreach (KeyValuePair> row in groupedPolygons) - { - Vector3[] newPositions; - Vector3[] newNormals; - Vector2[] newUV; - Color[] newColors; - int[] newTriangles; - - List polygons = row.Value; - - TriangulatePolygons(individualVertices, polygons, out newTriangles, out newPositions, out newNormals, out newUV, out newColors); - - polygonIndex[row.Key] = new PolygonEntry(newPositions, newNormals, newUV, newColors, newTriangles, polygons[0].Material, polygons[0].UserExcludeFromFinal); - } - } - - internal static void TriangulatePolygons(bool individualVertices, List polygons, out int[] triangesToAppend, out Vector3[] positions, out Vector3[] normals, out Vector2[] uv, out Color[] colors) - { - if(individualVertices) - { - int totalTriangleCount = 0; - for (int i = 0; i < polygons.Count; i++) - { - totalTriangleCount += polygons[i].Vertices.Length - 2; - } - int totalVertexCount = totalTriangleCount * 3; - - positions = new Vector3[totalVertexCount]; - normals = new Vector3[totalVertexCount]; - uv = new Vector2[totalVertexCount]; - colors = new Color[totalVertexCount]; - - triangesToAppend = new int[totalTriangleCount * 3]; - - int triangleOffset = 0; - int vertexOffset = 0; - - // Calculate triangulation - for (int i = 0; i < polygons.Count; i++) - { - Polygon polygon = polygons[i]; - int triangleCount = polygons[i].Vertices.Length - 2; - - for (int j = 0; j < triangleCount; j++) - { - int sourceIndex = 0; - - positions[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Position; - normals[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Normal; - uv[vertexOffset + j*3] = polygon.Vertices[sourceIndex].UV; - colors[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Color; - - sourceIndex = j + 1; - - positions[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Position; - normals[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Normal; - uv[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].UV; - colors[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Color; - - sourceIndex = j + 2; - - positions[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Position; - normals[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Normal; - uv[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].UV; - colors[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Color; - } - - for (int j = 0; j < triangleCount; j++) - { - triangesToAppend[triangleOffset + 0] = triangleOffset + 0; - triangesToAppend[triangleOffset + 1] = triangleOffset + 1; - triangesToAppend[triangleOffset + 2] = triangleOffset + 2; - - triangleOffset += 3; - } - - vertexOffset += triangleCount * 3; - } - } - else - { - int totalVertexCount = 0; - for (int i = 0; i < polygons.Count; i++) - { - totalVertexCount += polygons[i].Vertices.Length; - } - - int totalTriangleCount = totalVertexCount - 2 * polygons.Count; - - positions = new Vector3[totalVertexCount]; - normals = new Vector3[totalVertexCount]; - uv = new Vector2[totalVertexCount]; - colors = new Color[totalVertexCount]; - - triangesToAppend = new int[totalTriangleCount * 3]; - - int triangleOffset = 0; - int vertexOffset = 0; - - // Calculate triangulation - for (int i = 0; i < polygons.Count; i++) - { - Polygon polygon = polygons[i]; - int vertexCount = polygon.Vertices.Length; - - for (int j = 0; j < vertexCount; j++) - { - positions[vertexOffset + j] = polygon.Vertices[j].Position; - normals[vertexOffset + j] = polygon.Vertices[j].Normal; - uv[vertexOffset + j] = polygon.Vertices[j].UV; - colors[vertexOffset + j] = polygon.Vertices[j].Color; - } - - for (int j = 2; j < vertexCount; j++) - { - triangesToAppend[triangleOffset + 0] = vertexOffset + (0); - triangesToAppend[triangleOffset + 1] = vertexOffset + (j - 1); - triangesToAppend[triangleOffset + 2] = vertexOffset + (j); - triangleOffset += 3; - } - - vertexOffset += vertexCount; - } - } - } - } + collisionMeshDictionary.Add(mesh); + } + + internal static void TriangulateNewPolygons(bool individualVertices, Dictionary> groupedPolygons, PolygonEntry[] polygonIndex) + { + foreach (KeyValuePair> row in groupedPolygons) + { + Vector3[] newPositions; + Vector3[] newNormals; + Vector2[] newUV; + Color[] newColors; + int[] newTriangles; + + List polygons = row.Value; + + TriangulatePolygons(individualVertices, polygons, out newTriangles, out newPositions, out newNormals, out newUV, out newColors); + + polygonIndex[row.Key] = new PolygonEntry(newPositions, newNormals, newUV, newColors, newTriangles, polygons[0].Material, polygons[0].UserExcludeFromFinal); + } + } + + internal static void TriangulatePolygons(bool individualVertices, List polygons, out int[] triangesToAppend, out Vector3[] positions, out Vector3[] normals, out Vector2[] uv, out Color[] colors) + { + if (individualVertices) + { + int totalTriangleCount = 0; + for (int i = 0; i < polygons.Count; i++) + { + totalTriangleCount += polygons[i].Vertices.Length - 2; + } + int totalVertexCount = totalTriangleCount * 3; + + positions = new Vector3[totalVertexCount]; + normals = new Vector3[totalVertexCount]; + uv = new Vector2[totalVertexCount]; + colors = new Color[totalVertexCount]; + + triangesToAppend = new int[totalTriangleCount * 3]; + + int triangleOffset = 0; + int vertexOffset = 0; + + // Calculate triangulation + for (int i = 0; i < polygons.Count; i++) + { + Polygon polygon = polygons[i]; + int triangleCount = polygons[i].Vertices.Length - 2; + + for (int j = 0; j < triangleCount; j++) + { + int sourceIndex = 0; + + positions[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Position; + normals[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Normal; + uv[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].UV; + colors[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Color; + + sourceIndex = j + 1; + + positions[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Position; + normals[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Normal; + uv[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].UV; + colors[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Color; + + sourceIndex = j + 2; + + positions[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Position; + normals[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Normal; + uv[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].UV; + colors[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Color; + } + + for (int j = 0; j < triangleCount; j++) + { + triangesToAppend[triangleOffset + 0] = triangleOffset + 0; + triangesToAppend[triangleOffset + 1] = triangleOffset + 1; + triangesToAppend[triangleOffset + 2] = triangleOffset + 2; + + triangleOffset += 3; + } + + vertexOffset += triangleCount * 3; + } + } + else + { + int totalVertexCount = 0; + for (int i = 0; i < polygons.Count; i++) + { + totalVertexCount += polygons[i].Vertices.Length; + } + + int totalTriangleCount = totalVertexCount - 2 * polygons.Count; + + positions = new Vector3[totalVertexCount]; + normals = new Vector3[totalVertexCount]; + uv = new Vector2[totalVertexCount]; + colors = new Color[totalVertexCount]; + + triangesToAppend = new int[totalTriangleCount * 3]; + + int triangleOffset = 0; + int vertexOffset = 0; + + // Calculate triangulation + for (int i = 0; i < polygons.Count; i++) + { + Polygon polygon = polygons[i]; + int vertexCount = polygon.Vertices.Length; + + for (int j = 0; j < vertexCount; j++) + { + positions[vertexOffset + j] = polygon.Vertices[j].Position; + normals[vertexOffset + j] = polygon.Vertices[j].Normal; + uv[vertexOffset + j] = polygon.Vertices[j].UV; + colors[vertexOffset + j] = polygon.Vertices[j].Color; + } + + for (int j = 2; j < vertexCount; j++) + { + triangesToAppend[triangleOffset + 0] = vertexOffset + (0); + triangesToAppend[triangleOffset + 1] = vertexOffset + (j - 1); + triangesToAppend[triangleOffset + 2] = vertexOffset + (j); + triangleOffset += 3; + } + + vertexOffset += vertexCount; + } + } + } + } } -#endif + +#endif \ No newline at end of file diff --git a/Scripts/Core/CSGBuildSettings.cs b/Scripts/Core/CSGBuildSettings.cs index 6805c921..492170ca 100755 --- a/Scripts/Core/CSGBuildSettings.cs +++ b/Scripts/Core/CSGBuildSettings.cs @@ -1,18 +1,19 @@ #if UNITY_EDITOR || RUNTIME_CSG + using UnityEngine; using System.Collections; using UnityEngine.Rendering; namespace Sabresaurus.SabreCSG { - [System.Serializable] - public class CSGBuildSettings - { - // Whether to also do a collision pass - public bool GenerateCollisionMeshes = true; + [System.Serializable] + public class CSGBuildSettings + { + // Whether to also do a collision pass + public bool GenerateCollisionMeshes = true; - // Also calculate tangents (needed for Unity's built in bump mapping) - public bool GenerateTangents = true; + // Also calculate tangents (needed for Unity's built in bump mapping) + public bool GenerateTangents = true; public bool OptimizeGeometry = true; @@ -20,16 +21,19 @@ public class CSGBuildSettings // Generate a UV2 channel for lightmapping [Tooltip("Note that when enabled this will have a non-trivial increase in build times")] - public bool GenerateLightmapUVs = false; - - // Unwrap settings, note these are different to what is displayed in the Model Importer, see http://docs.unity3d.com/401/Documentation/Manual/LightmappingUV.html - [Range(0f,1f)] - public float UnwrapAngleError = 0.08f; // 0 to 1 - [Range(0f,1f)] - public float UnwrapAreaError = 0.15f; // 0 to 1 - [Range(0f,180f)] - public float UnwrapHardAngle = 88f; // degrees, 0 to 180 - public float UnwrapPackMargin = 0.00390625f; // Assumes a 1024 texture, pack margin = PadPixels / 1024 + public bool GenerateLightmapUVs = false; + + // Unwrap settings, note these are different to what is displayed in the Model Importer, see http://docs.unity3d.com/401/Documentation/Manual/LightmappingUV.html + [Range(0f, 1f)] + public float UnwrapAngleError = 0.08f; // 0 to 1 + + [Range(0f, 1f)] + public float UnwrapAreaError = 0.15f; // 0 to 1 + + [Range(0f, 180f)] + public float UnwrapHardAngle = 88f; // degrees, 0 to 180 + + public float UnwrapPackMargin = 0.00390625f; // Assumes a 1024 texture, pack margin = PadPixels / 1024 /* Fix most of the light leaking that occurs after CSG operations. @@ -40,7 +44,7 @@ In Unity even a simple box placement like below will cause dynamic light to leak Dynamic Light Source - X + X X +------+ X | | X | | @@ -75,93 +79,92 @@ block the incoming light and prevent the light leak artifact from appearing. */ public ShadowCastingMode ShadowCastingMode = ShadowCastingMode.TwoSided; + public ReflectionProbeUsage ReflectionProbeUsage = ReflectionProbeUsage.BlendProbes; + + // What default physics material to use on collision meshes + public PhysicMaterial DefaultPhysicsMaterial = null; + + public Material DefaultVisualMaterial = null; + + [HideInInspector] + public bool IsBuilt = false; // Only really relevant to last build settings + + public CSGBuildSettings ShallowCopy() + { + return (CSGBuildSettings)this.MemberwiseClone(); + } - public bool UseModelReflectionProbes = true; - public ReflectionProbeUsage ReflectionProbeUsage = ReflectionProbeUsage.BlendProbes; - - // What default physics material to use on collision meshes - public PhysicMaterial DefaultPhysicsMaterial = null; - - public Material DefaultVisualMaterial = null; - - [HideInInspector] - public bool IsBuilt = false; // Only really relevant to last build settings - - public CSGBuildSettings ShallowCopy() - { - return (CSGBuildSettings)this.MemberwiseClone(); - } - - /// - /// Compares two build settings and returns if they are practially different. - /// - public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings settings2) - { - if(settings1.GenerateCollisionMeshes != settings2.GenerateCollisionMeshes) - { - return true; - } - if(settings1.GenerateTangents != settings2.GenerateTangents) - { - return true; - } + /// + /// Compares two build settings and returns if they are practially different. + /// + public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings settings2) + { + if (settings1.GenerateCollisionMeshes != settings2.GenerateCollisionMeshes) + { + return true; + } + if (settings1.GenerateTangents != settings2.GenerateTangents) + { + return true; + } if (settings1.OptimizeGeometry != settings2.OptimizeGeometry) { return true; } if (settings1.GenerateLightmapUVs != settings2.GenerateLightmapUVs) - { - return true; - } - - // Only compare UV unwrap settings if unwrapping is in use - if(settings1.GenerateLightmapUVs && settings1.GenerateLightmapUVs) - { - if(settings1.UnwrapAngleError != settings2.UnwrapAngleError) - { - return true; - } - - if(settings1.UnwrapAreaError != settings2.UnwrapAreaError) - { - return true; - } - - if(settings1.UnwrapHardAngle != settings2.UnwrapHardAngle) - { - return true; - } - - if(settings1.UnwrapPackMargin != settings2.UnwrapPackMargin) - { - return true; - } - } - - if(settings1.DefaultPhysicsMaterial != settings2.DefaultPhysicsMaterial) - { - return true; - } - if(settings1.DefaultVisualMaterial != settings2.DefaultVisualMaterial) - { - return true; - } - - if(settings1.ShadowCastingMode != settings2.ShadowCastingMode) { return true; - } + } + + // Only compare UV unwrap settings if unwrapping is in use + if (settings1.GenerateLightmapUVs && settings1.GenerateLightmapUVs) + { + if (settings1.UnwrapAngleError != settings2.UnwrapAngleError) + { + return true; + } + + if (settings1.UnwrapAreaError != settings2.UnwrapAreaError) + { + return true; + } + + if (settings1.UnwrapHardAngle != settings2.UnwrapHardAngle) + { + return true; + } + + if (settings1.UnwrapPackMargin != settings2.UnwrapPackMargin) + { + return true; + } + } + + if (settings1.DefaultPhysicsMaterial != settings2.DefaultPhysicsMaterial) + { + return true; + } + if (settings1.DefaultVisualMaterial != settings2.DefaultVisualMaterial) + { + return true; + } - if( settings1.UseModelReflectionProbes != settings2.UseModelReflectionProbes ) - { - return true; - } + if (settings1.ShadowCastingMode != settings2.ShadowCastingMode) + { + return true; + } + + if (settings1.ReflectionProbeUsage != settings2.ReflectionProbeUsage) + { + return true; + } - // Don't compare IsBuilt + // Don't compare IsBuilt - // No practical differences found - return false; - } - } + // No practical differences found + return false; + } + } } + #endif \ No newline at end of file diff --git a/Scripts/Editor/Inspectors/CSGModelInspector.cs b/Scripts/Editor/Inspectors/CSGModelInspector.cs index 41a3f0ba..e8745b12 100644 --- a/Scripts/Editor/Inspectors/CSGModelInspector.cs +++ b/Scripts/Editor/Inspectors/CSGModelInspector.cs @@ -24,8 +24,7 @@ public class CSGModelInspector : Editor private SerializedProperty shadowCastingModeProperty; - private SerializedProperty useModelReflectionProbesProperty; - private SerializedProperty reflectionProbeUsageProperty; + private SerializedProperty reflectionProbeUsageProperty; private SerializedProperty defaultPhysicsMaterialProperty; private SerializedProperty defaultVisualMaterialProperty; @@ -55,8 +54,7 @@ public void OnEnable() shadowCastingModeProperty = serializedObject.FindProperty("buildSettings.ShadowCastingMode"); - useModelReflectionProbesProperty = serializedObject.FindProperty( "buildSettings.UseModelReflectionProbes" ); - reflectionProbeUsageProperty = serializedObject.FindProperty( "buildSettings.ReflectionProbeUsage" ); + reflectionProbeUsageProperty = serializedObject.FindProperty("buildSettings.ReflectionProbeUsage"); defaultPhysicsMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultPhysicsMaterial"); defaultVisualMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultVisualMaterial"); @@ -97,8 +95,8 @@ public override void OnInspectorGUI() EditorGUIUtility.labelWidth = 0; GUI.enabled = true; - - EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode")); + EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode")); + EditorGUILayout.PropertyField(reflectionProbeUsageProperty, new GUIContent("Reflection Probes")); // Experimental build settings to enable features that are not yet completely stable GUILayout.Label("Experimental", EditorStyles.boldLabel); @@ -107,18 +105,7 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(optimizeGeometryProperty, new GUIContent("Optimize Geometry")); EditorGUILayout.PropertyField(saveMeshesAsAssetsProperty, new GUIContent("Save Meshes As Assets")); EditorGUI.indentLevel = 0; - - GUILayout.Label( "Common Fixes", EditorStyles.boldLabel ); - EditorGUI.indentLevel = 1; - - EditorGUILayout.PropertyField( useModelReflectionProbesProperty, new GUIContent( "Default Reflection Probe Settings", "Use the default reflection probe usage settings (Blend Probes)?" ) ); - - GUI.enabled = !useModelReflectionProbesProperty.boolValue; - EditorGUILayout.PropertyField( reflectionProbeUsageProperty, new GUIContent( "Reflection Probes" ) ); - GUI.enabled = true; - - EditorGUI.indentLevel = 0; - } + } using (new NamedVerticalScope("Default Material")) { @@ -409,4 +396,4 @@ private void GuiLayoutEndImporterSection() EditorGUILayout.EndVertical(); } } -} +} \ No newline at end of file From c56fea992a1a4f5b093fe9fc47bde4d32015103a Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Fri, 1 Jun 2018 20:19:13 +0200 Subject: [PATCH 04/26] Disabled CSG Models will no longer enable their MeshGroup during play. Fixes #131. --- Scripts/CSGModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Scripts/CSGModel.cs b/Scripts/CSGModel.cs index 05525081..bddfe89a 100644 --- a/Scripts/CSGModel.cs +++ b/Scripts/CSGModel.cs @@ -1980,6 +1980,8 @@ private static void CleanupForBuild(Transform csgModelTransform) Transform meshGroup = csgModelTransform.Find("MeshGroup"); if (meshGroup != null) { + // If the CSG Model is disabled we also disable the mesh group. + meshGroup.gameObject.SetActive(csgModelTransform.gameObject.activeInHierarchy); // Reanchor the meshes to the parent of the CSG Model meshGroup.SetParent(csgModelTransform.parent, true); } From c5026edd36abd17928364886306501eff5c5da04 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Fri, 1 Jun 2018 20:31:54 +0200 Subject: [PATCH 05/26] Now only looks at the CSG Model enabled checkbox so it works like a traditional game object where only a parent could be disabled yet the children are enabled. --- Scripts/CSGModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/CSGModel.cs b/Scripts/CSGModel.cs index bddfe89a..df20df61 100644 --- a/Scripts/CSGModel.cs +++ b/Scripts/CSGModel.cs @@ -1981,7 +1981,7 @@ private static void CleanupForBuild(Transform csgModelTransform) if (meshGroup != null) { // If the CSG Model is disabled we also disable the mesh group. - meshGroup.gameObject.SetActive(csgModelTransform.gameObject.activeInHierarchy); + meshGroup.gameObject.SetActive(csgModelTransform.gameObject.activeSelf); // Reanchor the meshes to the parent of the CSG Model meshGroup.SetParent(csgModelTransform.parent, true); } From b5644980636aee8c55f8c4dbd83cadfa3d09945d Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Fri, 1 Jun 2018 22:04:33 -0500 Subject: [PATCH 06/26] Add Hollow Box Brush + Add HollowBoxBrush + Add HollowBoxBrushInspector Simple compound brush for generating a hollow cube/box with wall thickness. --- .../Editor/HollowBoxBrushInspector.cs | 41 ++++++ .../Brushes/CompoundBrushes/HollowBoxBrush.cs | 131 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs create mode 100644 Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs new file mode 100644 index 00000000..8fcc9071 --- /dev/null +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs @@ -0,0 +1,41 @@ +namespace Sabresaurus.SabreCSG +{ + using UnityEditor; + + [CanEditMultipleObjects] + [CustomEditor( typeof( HollowBoxBrush ), true )] + public class HollowBoxBrushInspector : CompoundBrushInspector + { + private SerializedProperty wallThicknessProp; + + public override void DoInspectorGUI() + { + using( new NamedVerticalScope( "Hollow Box" ) ) + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField( wallThicknessProp ); + if( EditorGUI.EndChangeCheck() ) + { + ApplyAndInvalidate(); + } + + EditorGUILayout.Space(); + } + + base.DoInspectorGUI(); + } + + protected override void OnEnable() + { + base.OnEnable(); + // Setup the SerializedProperties. + wallThicknessProp = serializedObject.FindProperty( "wallThickness" ); + } + + private void ApplyAndInvalidate() + { + serializedObject.ApplyModifiedProperties(); + System.Array.ForEach( BrushTargets, item => item.Invalidate( true ) ); + } + } +} diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs new file mode 100644 index 00000000..356db559 --- /dev/null +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -0,0 +1,131 @@ +namespace Sabresaurus.SabreCSG +{ + using System.Collections.Generic; + using System; + using UnityEngine; + + public class HollowBoxBrush : CompoundBrush + { + /// + /// Gets or sets the thickness of the walls. + /// + /// The thickness of the walls. + public float WallThickness + { + get + { + return wallThickness; + } + set + { + wallThickness = value; + } + } + + /// + /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. + /// + /// The beautiful name of the brush. + public override string BeautifulBrushName + { + get + { + return "Hollow Box Brush"; + } + } + + public override int BrushCount + { + get + { + // If the brush is too small for walls, just default to a single brush to not break things ok! + return ( localBounds.size.x > wallThickness * 2.0f && + localBounds.size.y > wallThickness * 2.0f && + localBounds.size.z > wallThickness * 2.0f ) ? 2 : 1; + } + } + + /// + /// The thickness of the walls. + /// + [SerializeField] + private float wallThickness = 0.25f; + + public override void UpdateVisibility() + { + } + + public override void Invalidate( bool polygonsChanged ) + { + base.Invalidate( polygonsChanged ); + + for( int i = 0; i < BrushCount; i++ ) + { + generatedBrushes[i].Mode = this.Mode; + generatedBrushes[i].IsNoCSG = this.IsNoCSG; + generatedBrushes[i].IsVisible = this.IsVisible; + generatedBrushes[i].HasCollision = this.HasCollision; + } + + if( localBounds.size.x > wallThickness * 2.0f && + localBounds.size.y > wallThickness * 2.0f && + localBounds.size.z > wallThickness * 2.0f ) + { + Vector3 baseSize = localBounds.size; + + generatedBrushes[0].Mode = CSGMode.Add; + BrushUtility.Resize( generatedBrushes[0], baseSize ); + + generatedBrushes[1].Mode = CSGMode.Subtract; + BrushUtility.Resize( generatedBrushes[1], baseSize - new Vector3( wallThickness * 2, wallThickness * 2, wallThickness * 2 ) ); + + for( int i = 0; i < BrushCount; i++ ) + { + generatedBrushes[i].SetPolygons( GeneratePolys( i ) ); + generatedBrushes[i].Invalidate( true ); + } + } + } + + private Polygon[] GeneratePolys( int index ) + { + Polygon[] output = generatedBrushes[index].GetPolygons(); + + for( int i = 0; i < 6; i++ ) + { + GenerateNormals( output[i] ); + GenerateUvCoordinates( output[i] ); + } + + return output; + } + + /// + /// Generates the UV coordinates for a automatically. + /// + /// The polygon to be updated. + private void GenerateUvCoordinates( Polygon polygon ) + { + // stolen code from the surface editor "AutoUV". + Vector3 planeNormal = polygon.Plane.normal; + Quaternion cancellingRotation = Quaternion.Inverse( Quaternion.LookRotation( -planeNormal ) ); + // Sets the UV at each point to the position on the plane + for( int i = 0; i < polygon.Vertices.Length; i++ ) + { + Vector3 position = polygon.Vertices[i].Position; + Vector2 uv = ( cancellingRotation * position ) * 0.5f; + polygon.Vertices[i].UV = uv; + } + } + + private void GenerateNormals( Polygon polygon ) + { + Plane plane = new Plane( polygon.Vertices[1].Position, polygon.Vertices[2].Position, polygon.Vertices[3].Position ); + + foreach( Vertex vertex in polygon.Vertices ) + { + vertex.Normal = plane.normal; + } + } + } +} From b1bb6d3a9a4b6610cb1c5e4d8245e6a326f3ab4d Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Sun, 3 Jun 2018 23:46:44 -0500 Subject: [PATCH 07/26] Fix CS0252 Fix for CS0252 "Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'type' " in Toolbar.cs on .NET backend 4.5 Equivalent on unity 2017 and above. --- Scripts/UI/Toolbar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/UI/Toolbar.cs b/Scripts/UI/Toolbar.cs index 4a636d8d..f54215cb 100644 --- a/Scripts/UI/Toolbar.cs +++ b/Scripts/UI/Toolbar.cs @@ -219,7 +219,7 @@ static void CreateCompoundBrush(object compoundBrushType) { // Make sure we're actually being asked to create a compound brush if(compoundBrushType != null - && compoundBrushType == typeof(Type) + && (Type)compoundBrushType == typeof(Type) && !typeof(CompoundBrush).IsAssignableFrom((Type)compoundBrushType)) { throw new ArgumentException("Specified type must be derived from CompoundBrush"); From 838c07701aa083db155d2cf06bbea83268358965 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Mon, 4 Jun 2018 22:43:17 +0200 Subject: [PATCH 08/26] 2D Shape Editor can now extrude segments. --- Gizmos/ShapeEditorSegmentExtrude.png | Bin 0 -> 1739 bytes Gizmos/ShapeEditorSegmentExtrude.png.meta | 87 ++++++++++++++++++ .../Editor/ShapeEditorWindow.cs | 33 +++++++ Scripts/UI/SabreCSGResources.cs | 8 ++ 4 files changed, 128 insertions(+) create mode 100644 Gizmos/ShapeEditorSegmentExtrude.png create mode 100644 Gizmos/ShapeEditorSegmentExtrude.png.meta diff --git a/Gizmos/ShapeEditorSegmentExtrude.png b/Gizmos/ShapeEditorSegmentExtrude.png new file mode 100644 index 0000000000000000000000000000000000000000..5a3a9b5a0bf093b9cc4d77d2929b623f2f904c46 GIT binary patch literal 1739 zcmbVNU5MO79FM|jJv|ZYp&T59^re2}dpFsPjqNo#yGz}3dtJTrMLIh(*$ubJ#3XmS zcMl5Ziv_7*1)=!ji_n*feNeOyim%$1;*)|x!6&f@Do9TSXLhsewY}EEK$6MKZ+`#Z z|6~3+*;qI-H90dW2*OmY>MyeQ!~EWJ4|~3N>C_T?-Ir8f$^_y5z5KpQIP>0Pf-tca zHkb3|`f~u$NP(EPh|-S|h8Bba2m1*`DAR;*w`%yQ}K;M-Hynv1Qv?_@MNWS7qbKIb~TyKaTO^H}m zEE(w~L$n=5w=~BnJtZ2NZmXKD>ZYveK(m19h{Yu_Ybg%EqW^4Ri+#CLC(je0s=Z!M z>6r>m+p6w3j;a}|VaN<2XX|ke`*NJ^8!`AKLur`gA&o`O2wQYDcO|BD=t7i?Y2&O2 z6APHyhl#2y8h2>`#Au95R@3glI7TYzl8D54#<2Prn{;SSvktut^?3OP11z-l`q;*f zTB2xdBFpF3m>Wevc0^~*^@OO4B%`Y-BJ*oZ=05IC0=$&KoTg1myQ4%kMv_I(dfu)HP36LGzhzFAzM6hSgg%^mhM=V zY$*CbZoLj_ahAgvk(%#HEIvvYVgM@^DV23g)-i^13zx7Ab&O=ohNfOd$by87_WKmA z@+#o_hX(>v#5n$y=ajJRKw`=P3<5c*l*)|Fa99Kbn>c2r42dyfdp-@>nS|Z#tXvgl zjC$bMR?9GD8{?L&X-riGA}nK~Td<6YrJ*vf{6R5+YM8M!uNPHrHexdF0+gJbou={AVIX!#&#QU2MefeYf*P$1#T`P#&1!l&K?+3(js= + /// Called when the extrude segment button is pressed. Will extrude all selected segments. + /// + private void OnSegmentExtrude() + { + foreach (Segment segment in selectedSegments.ToArray()) // use .ToArray() to iterate a clone. + { + Segment next = GetNextSegment(segment); + // calculate extrude direction. + Vector2Int pos1 = next.position - segment.position; + Vector2 dir = new Vector2(pos1.y, -pos1.x).normalized * 2.0f; + // insert new segments at an extruded distance. + Shape parent = GetShapeOfSegment(segment); + Segment select; + parent.segments.Insert(parent.segments.IndexOf(next), select = new Segment(segment.position.x + Mathf.RoundToInt(dir.x), segment.position.y + Mathf.RoundToInt(dir.y))); + parent.segments.Insert(parent.segments.IndexOf(next), new Segment(next.position.x + Mathf.RoundToInt(dir.x), next.position.y + Mathf.RoundToInt(dir.y))); + // recalculate the pivot position of the shape. + parent.CalculatePivotPosition(); + // update the selection so we have the extruded segment selected, this improves the workflow experience. + selectedObjects.Remove(segment); + selectedObjects.Add(select); + } + } + /// /// Called when the delete button is pressed. Will delete all selected segments and shapes. /// diff --git a/Scripts/UI/SabreCSGResources.cs b/Scripts/UI/SabreCSGResources.cs index 30797d5c..25c4d1dc 100644 --- a/Scripts/UI/SabreCSGResources.cs +++ b/Scripts/UI/SabreCSGResources.cs @@ -447,6 +447,14 @@ public static Texture2D ShapeEditorSegmentInsertTexture } } + public static Texture2D ShapeEditorSegmentExtrudeTexture + { + get + { + return (Texture2D)LoadObject("Gizmos/ShapeEditorSegmentExtrude.png"); + } + } + public static Texture2D ShapeEditorSegmentLinearTexture { get From d97ef6c4d395bd2db231ce0462ac0fbf07aaa052 Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Mon, 4 Jun 2018 19:38:19 -0500 Subject: [PATCH 09/26] use #if NET_4_6 to detect if the project is on the .net 4.x backend --- Scripts/UI/Toolbar.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Scripts/UI/Toolbar.cs b/Scripts/UI/Toolbar.cs index f54215cb..b682add3 100644 --- a/Scripts/UI/Toolbar.cs +++ b/Scripts/UI/Toolbar.cs @@ -217,6 +217,7 @@ static void CreatePrimitiveBrush(PrimitiveBrushType brushType) static void CreateCompoundBrush(object compoundBrushType) { +#if NET_4_6 // Make sure we're actually being asked to create a compound brush if(compoundBrushType != null && (Type)compoundBrushType == typeof(Type) @@ -224,6 +225,15 @@ static void CreateCompoundBrush(object compoundBrushType) { throw new ArgumentException("Specified type must be derived from CompoundBrush"); } +#else + if(compoundBrushType != null + && compoundBrushType == typeof(Type) + && !typeof(CompoundBrush).IsAssignableFrom((Type)compoundBrushType)) + { + throw new ArgumentException("Specified type must be derived from CompoundBrush"); + } + +#endif Vector3 position = GetPositionForNewBrush(); GameObject newBrushObject = csgModel.CreateCompoundBrush((Type) compoundBrushType, position); @@ -575,4 +585,4 @@ private static void OnBottomToolbarGUI(int windowID) } } } -#endif \ No newline at end of file +#endif From 278094463a555d881ccc83d4407229dc4fc96298 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Tue, 5 Jun 2018 09:52:15 +0200 Subject: [PATCH 10/26] Now using (compoundBrushType is Type) check as discussed. --- Scripts/UI/Toolbar.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Scripts/UI/Toolbar.cs b/Scripts/UI/Toolbar.cs index b682add3..b9f0a31e 100644 --- a/Scripts/UI/Toolbar.cs +++ b/Scripts/UI/Toolbar.cs @@ -217,23 +217,13 @@ static void CreatePrimitiveBrush(PrimitiveBrushType brushType) static void CreateCompoundBrush(object compoundBrushType) { -#if NET_4_6 // Make sure we're actually being asked to create a compound brush if(compoundBrushType != null - && (Type)compoundBrushType == typeof(Type) + && compoundBrushType is Type && !typeof(CompoundBrush).IsAssignableFrom((Type)compoundBrushType)) { throw new ArgumentException("Specified type must be derived from CompoundBrush"); } -#else - if(compoundBrushType != null - && compoundBrushType == typeof(Type) - && !typeof(CompoundBrush).IsAssignableFrom((Type)compoundBrushType)) - { - throw new ArgumentException("Specified type must be derived from CompoundBrush"); - } - -#endif Vector3 position = GetPositionForNewBrush(); GameObject newBrushObject = csgModel.CreateCompoundBrush((Type) compoundBrushType, position); From d00358aa9184c3adffde0dafb88488cd60c6aa30 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Tue, 5 Jun 2018 12:54:17 +0200 Subject: [PATCH 11/26] Created a new icon for the Capsule. Added BeautifulBrushName. --- Gizmos/ButtonCapsule.png | Bin 2403 -> 2447 bytes Scripts/Brushes/PrimitiveBrush.cs | 3 +++ 2 files changed, 3 insertions(+) diff --git a/Gizmos/ButtonCapsule.png b/Gizmos/ButtonCapsule.png index 68e81710dfb0e71bda21903b6a0e42e7bf9c24b3..ac0441b1b375a186e17dcfe8cb3d3b530edfcf18 100644 GIT binary patch delta 826 zcmaDX)Gs{2f{E?aMyoC+UNc<-(-1==D-#1NBg@GbnH11O{xMam7^S5pCmN+C>zbOF z8tSH4nk4I5CL1Q}CYoEC8kra;CRvzUY@W^R!Zewm)y$O`)k+G*C9Y*9R^=I~dA3SM z28I^82Bb4z0DGK6tUghEBgCznOHYxu5Q=D zR-uFKA6;t%;*Yck{yDHnOvf>H>zgab+EU7&$t7oj4Dq+iH zz-GYo!OHL#*MooS{H}){n0+8GdU?Lk;-zV^410Wg_HExR*LgzxPjb?htQ*TYYJR1a z?0M?1^l9Q=h1&YYlBGhKq0LA$z@l`*`AbC3P*3yvZamhtK|a?v}_8X_7PMH4Jv8SPlz z6n!9#fpwpl{94uDMju`ZFdp$$Qg2wm5)sV&)U?RV=JR{^2GiukUSr{e8(Fp7ZOvUIK6DPALHmkEOpPZ*+t`jSpZbo;E4Kb;S*Zzh92j zYb;MVxYudx!yuzM?|(P7aIJEfEPh8rVe`%aE)A1X#_6*rab>xb+8In|n_2F#^pHSD zh%e(b(H$%Qo$OXw6sEwm^?#%Vb4p%r@@COChxLnllkPHZIil6QbFzD`aLa+`@oIfC z4%-(=pL)yTl&dj^zrD~vzGBmIUU!yHr)nFFcGmyh@kortY0c4#kNY^EwJ==xpZjR? z-`#D8Wn{P~SSp-vs4$?56q4>ljj+_h!&VN*>yLHzv(2U{#k_TT^Eo2&Q z3T%;Na9m%Wp=Yj~|6yzX(Z3H%W#sc7KlqkbRrSw#_Zg*KrR?tf?`E&Bl&cr-E}g%B Y<*O~KbA?WJ^DqE`r>mdKI;Vst0DMqYp8x;= delta 880 zcmV-$1CRWV6XOz)I06Owu{w$Z4K*z>GE_1#Ix{#rG&Pga0w4`FEip1wGBG+cI65>n zll}sElSKt4vz-G%0+X)>S$}VSMhXA`0}n|=K~z}7)t5VL990yCzjN;0@%kCBaR_z< zgcR69LeS8pa1+rXDAG_NAt*_oP$7X7Xb?h3h=vvkp$J73AP5aggP@3zf)s3tJnVSQ zdUw5k?0DwRJrw2@u&r6*CE;&obc;FP|DSV4Gh$}^RWE$8_{Y6rIDhfXFE<_;O_I}2 z!pTT@P&AGdidU}^Lt(BU5M>kdH=>}{5|#WE@_;a zo;^DE%vXy|y)Zp-=()WB^dCgxX>~R)wTk6aKQCN{ZbtM2n5$r->?>7Bs>G2Rv^p%F z|B$urwP)+?_P3=0R$A+4)@=1ey?z~um7Ry~rYL)Zn1S;!Fn>z(^H1p%ji;`yHeV|R zu)f`S=~n#)uBiOpliRPo$PASMR?mM#x4HCUDS%teU#e23n3RMNMY5EYt5+(e0Dw@c zVCh5*L_sB#1t`jAEIsr?Vp)Jajg&m>N!k~)eF3n8ci$Vpok!w&2k2Am2b4h1DtZU7 zdua&!)T289uz$O~hX3`8=&o;p^k+x^M+w+H*Mk17q3h2onwACdrD{nJ(OHCOz7&8( zT{uwl76By=jomB-5M$-_SR{z#w>$ke?G`~dT7=1?WcMF?qZEK(7ehXL&a8@%L5zD$ zkL_ESBA}gB;=~l`%!zk_3zX}(Vd$%QtgSe8Z+mHohJS4+!f$czqZlD%Xr2<2D%rzN zuzmc=v*!Gpnr=YZffFL2?*v!-tQZm&bn2`QO+Kw-TXV9#^_Zrc(~@Q5 z6d^&#@iD#~lfk88aP&tTKJ-md9r;p Date: Tue, 5 Jun 2018 16:15:12 +0200 Subject: [PATCH 12/26] MathHelper.cs PlaneEqualsLooserWithFlip is now even looser (less precise, allowing for more floating point errors and causing less false positives). --- Scripts/Extensions/MathHelper.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Scripts/Extensions/MathHelper.cs b/Scripts/Extensions/MathHelper.cs index ab2db6f8..d25d5283 100644 --- a/Scripts/Extensions/MathHelper.cs +++ b/Scripts/Extensions/MathHelper.cs @@ -312,18 +312,18 @@ public static bool PlaneEqualsLooser(Plane plane1, Plane plane2) public static bool PlaneEqualsLooserWithFlip(Plane plane1, Plane plane2) { if ( - Mathf.Abs(plane1.distance - plane2.distance) < EPSILON_LOWER_3 - && Mathf.Abs(plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_2 - && Mathf.Abs(plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_2 - && Mathf.Abs(plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_2) + Mathf.Abs(plane1.distance - plane2.distance) <= 0.02f + && Mathf.Abs(plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_3 + && Mathf.Abs(plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_3 + && Mathf.Abs(plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_3) { return true; } else if ( - Mathf.Abs(-plane1.distance - plane2.distance) < EPSILON_LOWER_3 - && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_2 - && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_2 - && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_2) + Mathf.Abs(-plane1.distance - plane2.distance) <= 0.02f + && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_3 + && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_3 + && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_3) { return true; } From 75e346d6becb885b2f43005ae73870c38d89652d Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Tue, 5 Jun 2018 19:11:39 +0200 Subject: [PATCH 13/26] Increased the floats slightly which took care of a lot of artifacts I was still able to create. --- Scripts/Extensions/MathHelper.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Scripts/Extensions/MathHelper.cs b/Scripts/Extensions/MathHelper.cs index d25d5283..c78e3609 100644 --- a/Scripts/Extensions/MathHelper.cs +++ b/Scripts/Extensions/MathHelper.cs @@ -313,17 +313,17 @@ public static bool PlaneEqualsLooserWithFlip(Plane plane1, Plane plane2) { if ( Mathf.Abs(plane1.distance - plane2.distance) <= 0.02f - && Mathf.Abs(plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_3 - && Mathf.Abs(plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_3 - && Mathf.Abs(plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_3) + && Mathf.Abs(plane1.normal.x - plane2.normal.x) < 0.006f + && Mathf.Abs(plane1.normal.y - plane2.normal.y) < 0.006f + && Mathf.Abs(plane1.normal.z - plane2.normal.z) < 0.006f) { return true; } else if ( Mathf.Abs(-plane1.distance - plane2.distance) <= 0.02f - && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_3 - && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_3 - && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_3) + && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < 0.006f + && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < 0.006f + && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < 0.006f) { return true; } From 4f551adfe53559cd0542b1cdb53dd78314cca673 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Tue, 5 Jun 2018 19:47:55 +0200 Subject: [PATCH 14/26] Additional fine-tuning to deal with extremely complex subtractive brushes. --- Scripts/Extensions/MathHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Extensions/MathHelper.cs b/Scripts/Extensions/MathHelper.cs index c78e3609..729ff92e 100644 --- a/Scripts/Extensions/MathHelper.cs +++ b/Scripts/Extensions/MathHelper.cs @@ -312,7 +312,7 @@ public static bool PlaneEqualsLooser(Plane plane1, Plane plane2) public static bool PlaneEqualsLooserWithFlip(Plane plane1, Plane plane2) { if ( - Mathf.Abs(plane1.distance - plane2.distance) <= 0.02f + Mathf.Abs(plane1.distance - plane2.distance) <= 0.08f && Mathf.Abs(plane1.normal.x - plane2.normal.x) < 0.006f && Mathf.Abs(plane1.normal.y - plane2.normal.y) < 0.006f && Mathf.Abs(plane1.normal.z - plane2.normal.z) < 0.006f) @@ -320,7 +320,7 @@ public static bool PlaneEqualsLooserWithFlip(Plane plane1, Plane plane2) return true; } else if ( - Mathf.Abs(-plane1.distance - plane2.distance) <= 0.02f + Mathf.Abs(-plane1.distance - plane2.distance) <= 0.08f && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < 0.006f && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < 0.006f && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < 0.006f) From ccf13b549b8b005e1a21ebabfa82be024d15cc44 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Tue, 5 Jun 2018 22:23:08 +0200 Subject: [PATCH 15/26] Faster and more accurate material searches for VMF and T3D. Fixed all runtime CSG game build compile errors. --- Scripts/Brushes/PrimitiveBrush.cs | 4 + Scripts/Importers/MaterialSearcher.cs | 66 +++++++++++++++ Scripts/Importers/MaterialSearcher.cs.meta | 13 +++ .../Importers/UnrealGold/T3dMapConverter.cs | 53 +++++------- .../ValveMapFormat2006/VmfWorldConverter.cs | 81 ++++++++++--------- 5 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 Scripts/Importers/MaterialSearcher.cs create mode 100644 Scripts/Importers/MaterialSearcher.cs.meta diff --git a/Scripts/Brushes/PrimitiveBrush.cs b/Scripts/Brushes/PrimitiveBrush.cs index a0c1c794..9916b61e 100644 --- a/Scripts/Brushes/PrimitiveBrush.cs +++ b/Scripts/Brushes/PrimitiveBrush.cs @@ -806,7 +806,11 @@ public override void UpdateVisibility() MeshRenderer meshRenderer = GetComponent(); if (meshRenderer != null) { +#if UNITY_EDITOR meshRenderer.enabled = isVisible && !CurrentSettings.ShowBrushesAsWireframes; +#else + meshRenderer.enabled = isVisible; +#endif } } diff --git a/Scripts/Importers/MaterialSearcher.cs b/Scripts/Importers/MaterialSearcher.cs new file mode 100644 index 00000000..42cc7bb4 --- /dev/null +++ b/Scripts/Importers/MaterialSearcher.cs @@ -0,0 +1,66 @@ +#if UNITY_EDITOR || RUNTIME_CSG + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Sabresaurus.SabreCSG.Importers +{ + /// + /// Searches for materials by name more precisely to prevent false positives and caches them for + /// increasingly faster search results. + /// + public class MaterialSearcher + { + /// + /// The material cache dictionary. + /// + private Dictionary m_MaterialCache = new Dictionary(); + + /// + /// Attempts to find a material in the project by name. + /// + /// The material names to search for, longest to shortest like 'PlayrShp.Ceiling.Hullwk' and 'Hullwk'. + /// The material if found or null. + public Material FindMaterial(string[] names) + { +#if UNITY_EDITOR + // iterate through all the names to search for, assuming it's longest to shortest. + for (int i = 0; i < names.Length; i++) + { + string name = names[i]; + // check whether we already have this material in our cache. + if (m_MaterialCache.ContainsKey(name)) + // if found immediately return this result. + return m_MaterialCache[name]; + + // have unity search for the asset (this can get very slow when there are a lot of materials in the project). + string[] guids = UnityEditor.AssetDatabase.FindAssets("t:Material " + name); + + // iterate through unity's search results: + for (int j = 0; j < guids.Length; j++) + { + // get the file system path of the file. + string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[j]); + string file = System.IO.Path.GetFileNameWithoutExtension(path); + + // make sure the file name matches the desired material name exactly to prevent false positives as much as possible. + if (file.Equals(name, StringComparison.InvariantCultureIgnoreCase)) + { + // we found a perfect match, load the material. + Material material = UnityEditor.AssetDatabase.LoadAssetAtPath(path); + // put the material in the cache. + m_MaterialCache.Add(name, material); + // return the material. + return material; + } + } + } + // we didn't find anything... +#endif + return null; + } + } +} + +#endif \ No newline at end of file diff --git a/Scripts/Importers/MaterialSearcher.cs.meta b/Scripts/Importers/MaterialSearcher.cs.meta new file mode 100644 index 00000000..982185c4 --- /dev/null +++ b/Scripts/Importers/MaterialSearcher.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b1acbf10a759e5642b1519c9f7654505 +timeCreated: 1528227179 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Importers/UnrealGold/T3dMapConverter.cs b/Scripts/Importers/UnrealGold/T3dMapConverter.cs index b53738c5..2419d385 100644 --- a/Scripts/Importers/UnrealGold/T3dMapConverter.cs +++ b/Scripts/Importers/UnrealGold/T3dMapConverter.cs @@ -18,12 +18,15 @@ public static class T3dMapConverter /// The model to import into. /// The map to be imported. /// The scale modifier. - public static void Import(CSGModel model, T3dMap map, int scale = 64) + public static void Import(CSGModelBase model, T3dMap map, int scale = 64) { try { model.BeginUpdate(); + // create a material searcher to associate materials automatically. + MaterialSearcher materialSearcher = new MaterialSearcher(); + List brushes = map.Brushes; Brush[] sabreBrushes = new Brush[brushes.Count]; @@ -43,7 +46,22 @@ public static void Import(CSGModel model, T3dMap map, int scale = 64) T3dPolygon tpolygon = tbrush.Polygons[i]; // find the material in the unity project automatically. - Material material = FindMaterial(tpolygon.Texture); + Material material; + if (tpolygon.Texture.Contains('.')) + { + // try finding both 'PlayrShp.Ceiling.Hullwk' and 'Hullwk'. + string tiny = tpolygon.Texture.Substring(tpolygon.Texture.LastIndexOf('.') + 1); + material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture, tiny }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' and also as '" + tiny + "' but it couldn't be found in the project."); + } + else + { + // only try finding 'Hullwk'. + material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' but it couldn't be found in the project."); + } Vertex[] vertices = new Vertex[tpolygon.Vertices.Count]; for (int j = 0; j < tpolygon.Vertices.Count; j++) @@ -187,37 +205,6 @@ private static Quaternion T3dRotatorToQuaternion(T3dRotator rotator) return quaternion; } - - /// - /// Attempts to find a material in the project by name. - /// - /// The material name to search for. - /// The material if found or null. - private static Material FindMaterial(string name) - { -#if UNITY_EDITOR - // first try finding the fully qualified texture name like 'PlayrShp.Ceiling.Hullwk'. - string texture = ""; - string guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + name).FirstOrDefault(); - if (guid == null) - { - // if it couldn't be found try a simplified name which UnrealEd typically exports like 'Hullwk'. - texture = name; - if (name.Contains('.')) - texture = name.Substring(name.LastIndexOf('.') + 1); - guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + texture).FirstOrDefault(); - } - // if a material could be found using either option: - if (guid != null) - { - // load the material. - string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid); - return UnityEditor.AssetDatabase.LoadAssetAtPath(path); - } - else { Debug.Log("SabreCSG: Tried to find material '" + name + "' and also as '" + texture + "' but it couldn't be found in the project."); } -#endif - return null; - } } } diff --git a/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs b/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs index 39d811c5..afcfb4d8 100644 --- a/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs +++ b/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs @@ -20,12 +20,15 @@ public static class VmfWorldConverter /// The model to import into. /// The world to be imported. /// The scale modifier. - public static void Import(CSGModel model, VmfWorld world) + public static void Import(CSGModelBase model, VmfWorld world) { try { model.BeginUpdate(); + // create a material searcher to associate materials automatically. + MaterialSearcher materialSearcher = new MaterialSearcher(); + // group all the brushes together. GroupBrush groupBrush = new GameObject("Source Engine Map").AddComponent(); groupBrush.transform.SetParent(model.transform); @@ -65,8 +68,26 @@ public static void Import(CSGModel model, VmfWorld world) // detect collision-only brushes. if (IsInvisibleMaterial(side.Material)) pr.IsVisible = false; - // try finding the material in the project. - polygon.Material = FindMaterial(side.Material); + // find the material in the unity project automatically. + Material material; + // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'. + string materialName = side.Material.Replace("/", "."); + if (materialName.Contains('.')) + { + // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'. + string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1); + material = materialSearcher.FindMaterial(new string[] { materialName, tiny }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project."); + } + else + { + // only try finding 'BRICKWALL052D'. + material = materialSearcher.FindMaterial(new string[] { materialName }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project."); + } + polygon.Material = material; // calculate the texture coordinates. int w = 256; int h = 256; @@ -145,8 +166,26 @@ public static void Import(CSGModel model, VmfWorld world) // detect collision-only brushes. if (IsInvisibleMaterial(side.Material)) pr.IsVisible = false; - // try finding the material in the project. - polygon.Material = FindMaterial(side.Material); + // find the material in the unity project automatically. + Material material; + // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'. + string materialName = side.Material.Replace("/", "."); + if (materialName.Contains('.')) + { + // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'. + string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1); + material = materialSearcher.FindMaterial(new string[] { materialName, tiny }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project."); + } + else + { + // only try finding 'BRICKWALL052D'. + material = materialSearcher.FindMaterial(new string[] { materialName }); + if (material == null) + Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project."); + } + polygon.Material = material; // calculate the texture coordinates. int w = 256; int h = 256; @@ -283,38 +322,6 @@ private static bool IsSpecialMaterial(string name) } return false; } - - /// - /// Attempts to find a material in the project by name. - /// - /// The material name to search for. - /// The material if found or null. - private static Material FindMaterial(string name) - { -#if UNITY_EDITOR - // first try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'. - name = name.Replace("/", "."); - string texture = ""; - string guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + name).FirstOrDefault(); - if (guid == null) - { - // if it couldn't be found try a simplified name like 'BRICKWALL052D'. - texture = name; - if (name.Contains('.')) - texture = name.Substring(name.LastIndexOf('.') + 1); - guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + texture).FirstOrDefault(); - } - // if a material could be found using either option: - if (guid != null) - { - // load the material. - string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid); - return UnityEditor.AssetDatabase.LoadAssetAtPath(path); - } - else { Debug.Log("SabreCSG: Tried to find material '" + name + "' and also as '" + texture + "' but it couldn't be found in the project."); } -#endif - return null; - } } } From 8b375d25d7147afef9f53869dbbadd54206c13a9 Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Tue, 5 Jun 2018 23:53:47 -0500 Subject: [PATCH 16/26] Add BrushSize feature. --- .../Editor/HollowBoxBrushInspector.cs | 20 +++++++++++++++++++ .../Brushes/CompoundBrushes/HollowBoxBrush.cs | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs index 8fcc9071..58e4311d 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs @@ -7,6 +7,9 @@ public class HollowBoxBrushInspector : CompoundBrushInspector { private SerializedProperty wallThicknessProp; + private SerializedProperty brushSizeProp; + + private float bSize; public override void DoInspectorGUI() { @@ -14,6 +17,18 @@ public override void DoInspectorGUI() { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField( wallThicknessProp ); + + EditorGUILayout.BeginHorizontal(); + + bSize = EditorGUILayout.FloatField("Brush Size", bSize ); + + if( SabreGUILayout.Button( "Set" ) ) + { + brushSizeProp.floatValue = bSize; + } + + EditorGUILayout.EndHorizontal(); + if( EditorGUI.EndChangeCheck() ) { ApplyAndInvalidate(); @@ -28,8 +43,13 @@ public override void DoInspectorGUI() protected override void OnEnable() { base.OnEnable(); + // Setup the SerializedProperties. wallThicknessProp = serializedObject.FindProperty( "wallThickness" ); + brushSizeProp = serializedObject.FindProperty( "brushSize" ); + + // Get the size of the brush for the inspector field value. + bSize = brushSizeProp.floatValue; } private void ApplyAndInvalidate() diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs index 356db559..85507ea0 100644 --- a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -22,6 +22,18 @@ public float WallThickness } } + public float BrushSize + { + get + { + return brushSize; + } + set + { + brushSize = value; + } + } + /// /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. /// @@ -50,6 +62,12 @@ public override int BrushCount /// [SerializeField] private float wallThickness = 0.25f; + + /// + /// The size of the brush bounds, set by inspector [set] button. + /// + [SerializeField] + private float brushSize = 2.0f; public override void UpdateVisibility() { @@ -71,6 +89,8 @@ public override void Invalidate( bool polygonsChanged ) localBounds.size.y > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f ) { + localBounds.size = Vector3.one * brushSize; + Vector3 baseSize = localBounds.size; generatedBrushes[0].Mode = CSGMode.Add; From 04d7df2badf91d30928f9054c67a17d90b50b3ac Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Tue, 5 Jun 2018 23:57:23 -0500 Subject: [PATCH 17/26] Removed unused namespaces. --- Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs index 85507ea0..227cdee6 100644 --- a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -1,7 +1,5 @@ namespace Sabresaurus.SabreCSG { - using System.Collections.Generic; - using System; using UnityEngine; public class HollowBoxBrush : CompoundBrush From 2dae65f3693d94df530ed0ee093cd460a9ecbed2 Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Wed, 6 Jun 2018 03:00:02 -0500 Subject: [PATCH 18/26] Allow individual dimention size setting for XYZ axis --- .../Editor/HollowBoxBrushInspector.cs | 9 +++++---- .../Brushes/CompoundBrushes/HollowBoxBrush.cs | 16 ++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs index 58e4311d..c96bfc95 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs @@ -1,6 +1,7 @@ namespace Sabresaurus.SabreCSG { using UnityEditor; + using UnityEngine; [CanEditMultipleObjects] [CustomEditor( typeof( HollowBoxBrush ), true )] @@ -9,7 +10,7 @@ public class HollowBoxBrushInspector : CompoundBrushInspector private SerializedProperty wallThicknessProp; private SerializedProperty brushSizeProp; - private float bSize; + private Vector3 bSize; public override void DoInspectorGUI() { @@ -20,11 +21,11 @@ public override void DoInspectorGUI() EditorGUILayout.BeginHorizontal(); - bSize = EditorGUILayout.FloatField("Brush Size", bSize ); + bSize = EditorGUILayout.Vector3Field( "Brush Size", bSize ); if( SabreGUILayout.Button( "Set" ) ) { - brushSizeProp.floatValue = bSize; + brushSizeProp.vector3Value = bSize; } EditorGUILayout.EndHorizontal(); @@ -49,7 +50,7 @@ protected override void OnEnable() brushSizeProp = serializedObject.FindProperty( "brushSize" ); // Get the size of the brush for the inspector field value. - bSize = brushSizeProp.floatValue; + bSize = brushSizeProp.vector3Value; } private void ApplyAndInvalidate() diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs index 227cdee6..1eecb96a 100644 --- a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -1,5 +1,7 @@ namespace Sabresaurus.SabreCSG { + using System.Collections.Generic; + using System; using UnityEngine; public class HollowBoxBrush : CompoundBrush @@ -20,7 +22,7 @@ public float WallThickness } } - public float BrushSize + public Vector3 BrushSize { get { @@ -60,12 +62,12 @@ public override int BrushCount /// [SerializeField] private float wallThickness = 0.25f; - + /// /// The size of the brush bounds, set by inspector [set] button. /// [SerializeField] - private float brushSize = 2.0f; + private Vector3 brushSize = new Vector3( 2, 2, 2 ); public override void UpdateVisibility() { @@ -87,15 +89,13 @@ public override void Invalidate( bool polygonsChanged ) localBounds.size.y > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f ) { - localBounds.size = Vector3.one * brushSize; - - Vector3 baseSize = localBounds.size; + localBounds.size = brushSize; generatedBrushes[0].Mode = CSGMode.Add; - BrushUtility.Resize( generatedBrushes[0], baseSize ); + BrushUtility.Resize( generatedBrushes[0], localBounds.size ); generatedBrushes[1].Mode = CSGMode.Subtract; - BrushUtility.Resize( generatedBrushes[1], baseSize - new Vector3( wallThickness * 2, wallThickness * 2, wallThickness * 2 ) ); + BrushUtility.Resize( generatedBrushes[1], localBounds.size - new Vector3( wallThickness * 2, wallThickness * 2, wallThickness * 2 ) ); for( int i = 0; i < BrushCount; i++ ) { From f6a45a1b8bd98145936779d4fc9b1824f66f7079 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Wed, 6 Jun 2018 23:11:13 +0200 Subject: [PATCH 19/26] 2DSE: Fixed inverted extrude direction on flipped projects. --- Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs index 999080c5..9368d6e3 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs @@ -1156,9 +1156,10 @@ private void OnSegmentExtrude() { foreach (Segment segment in selectedSegments.ToArray()) // use .ToArray() to iterate a clone. { + bool inverted = project.flipHorizontally ^ project.flipVertically; Segment next = GetNextSegment(segment); // calculate extrude direction. - Vector2Int pos1 = next.position - segment.position; + Vector2Int pos1 = (inverted ? segment.position - next.position : next.position - segment.position); Vector2 dir = new Vector2(pos1.y, -pos1.x).normalized * 2.0f; // insert new segments at an extruded distance. Shape parent = GetShapeOfSegment(segment); From ae72fac6270f9b2d8fdf8d3686c57a1d812b991e Mon Sep 17 00:00:00 2001 From: Daniel Cornelius Date: Wed, 6 Jun 2018 23:09:14 -0500 Subject: [PATCH 20/26] Code Cleanup + bugfix - Removed unused usings. - Removed redundant code - Removed scale setting feature. Possible addition to compound brush later. * Fixed brush not being manually scalable. * Made checking brush size easier. Now can simply use IsBrushXYZTooSmall to check the size of the brush. --- .../Editor/HollowBoxBrushInspector.cs | 18 ----- .../Brushes/CompoundBrushes/HollowBoxBrush.cs | 78 +++++-------------- 2 files changed, 20 insertions(+), 76 deletions(-) diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs index c96bfc95..b863b806 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs @@ -8,9 +8,6 @@ public class HollowBoxBrushInspector : CompoundBrushInspector { private SerializedProperty wallThicknessProp; - private SerializedProperty brushSizeProp; - - private Vector3 bSize; public override void DoInspectorGUI() { @@ -19,17 +16,6 @@ public override void DoInspectorGUI() EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField( wallThicknessProp ); - EditorGUILayout.BeginHorizontal(); - - bSize = EditorGUILayout.Vector3Field( "Brush Size", bSize ); - - if( SabreGUILayout.Button( "Set" ) ) - { - brushSizeProp.vector3Value = bSize; - } - - EditorGUILayout.EndHorizontal(); - if( EditorGUI.EndChangeCheck() ) { ApplyAndInvalidate(); @@ -47,10 +33,6 @@ protected override void OnEnable() // Setup the SerializedProperties. wallThicknessProp = serializedObject.FindProperty( "wallThickness" ); - brushSizeProp = serializedObject.FindProperty( "brushSize" ); - - // Get the size of the brush for the inspector field value. - bSize = brushSizeProp.vector3Value; } private void ApplyAndInvalidate() diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs index 1eecb96a..0bb06112 100644 --- a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -1,7 +1,5 @@ namespace Sabresaurus.SabreCSG { - using System.Collections.Generic; - using System; using UnityEngine; public class HollowBoxBrush : CompoundBrush @@ -22,18 +20,6 @@ public float WallThickness } } - public Vector3 BrushSize - { - get - { - return brushSize; - } - set - { - brushSize = value; - } - } - /// /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. /// @@ -50,24 +36,28 @@ public override int BrushCount { get { - // If the brush is too small for walls, just default to a single brush to not break things ok! - return ( localBounds.size.x > wallThickness * 2.0f && - localBounds.size.y > wallThickness * 2.0f && - localBounds.size.z > wallThickness * 2.0f ) ? 2 : 1; + // If the brush is too small for walls, just default to a single brush. + return ( IsBrushXYZTooSmall ) ? 2 : 1; } } /// - /// The thickness of the walls. + /// Is the size of the bounds X, Y, and Z above the minimum size? /// - [SerializeField] - private float wallThickness = 0.25f; + /// + public bool IsBrushXYZTooSmall + { + get + { + return localBounds.size.x > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f && localBounds.size.y > wallThickness * 2; + } + } /// - /// The size of the brush bounds, set by inspector [set] button. + /// The thickness of the walls. /// [SerializeField] - private Vector3 brushSize = new Vector3( 2, 2, 2 ); + private float wallThickness = 0.25f; public override void UpdateVisibility() { @@ -85,12 +75,8 @@ public override void Invalidate( bool polygonsChanged ) generatedBrushes[i].HasCollision = this.HasCollision; } - if( localBounds.size.x > wallThickness * 2.0f && - localBounds.size.y > wallThickness * 2.0f && - localBounds.size.z > wallThickness * 2.0f ) + if( IsBrushXYZTooSmall ) { - localBounds.size = brushSize; - generatedBrushes[0].Mode = CSGMode.Add; BrushUtility.Resize( generatedBrushes[0], localBounds.size ); @@ -103,6 +89,10 @@ public override void Invalidate( bool polygonsChanged ) generatedBrushes[i].Invalidate( true ); } } + else + { + BrushUtility.Resize( generatedBrushes[0], localBounds.size ); + } } private Polygon[] GeneratePolys( int index ) @@ -111,39 +101,11 @@ private Polygon[] GeneratePolys( int index ) for( int i = 0; i < 6; i++ ) { - GenerateNormals( output[i] ); - GenerateUvCoordinates( output[i] ); + output[i].ResetVertexNormals(); + output[i].GenerateUvCoordinates(); } return output; } - - /// - /// Generates the UV coordinates for a automatically. - /// - /// The polygon to be updated. - private void GenerateUvCoordinates( Polygon polygon ) - { - // stolen code from the surface editor "AutoUV". - Vector3 planeNormal = polygon.Plane.normal; - Quaternion cancellingRotation = Quaternion.Inverse( Quaternion.LookRotation( -planeNormal ) ); - // Sets the UV at each point to the position on the plane - for( int i = 0; i < polygon.Vertices.Length; i++ ) - { - Vector3 position = polygon.Vertices[i].Position; - Vector2 uv = ( cancellingRotation * position ) * 0.5f; - polygon.Vertices[i].UV = uv; - } - } - - private void GenerateNormals( Polygon polygon ) - { - Plane plane = new Plane( polygon.Vertices[1].Position, polygon.Vertices[2].Position, polygon.Vertices[3].Position ); - - foreach( Vertex vertex in polygon.Vertices ) - { - vertex.Normal = plane.normal; - } - } } } From cc9690195daa82af2ba511dc18bab46dd6772b7f Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 09:42:59 +0200 Subject: [PATCH 21/26] Updated SabreCSG version to 1.7.0. --- Scripts/CSGModelBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/CSGModelBase.cs b/Scripts/CSGModelBase.cs index 00d4468c..ed0efa21 100644 --- a/Scripts/CSGModelBase.cs +++ b/Scripts/CSGModelBase.cs @@ -15,7 +15,7 @@ namespace Sabresaurus.SabreCSG // [ExecuteInEditMode] public class CSGModelBase : MonoBehaviour { - public const string VERSION_STRING = "1.6.3"; + public const string VERSION_STRING = "1.7.0"; protected const string DEFAULT_FALLBACK_MATERIAL_PATH = "Materials/Default_Map"; // Limit to how many vertices a Unity mesh can hold, before it must be split into a second mesh (just under 2^16) From 62eb757a853f614eec12e5160e3f6884ee5a5756 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 12:19:40 +0200 Subject: [PATCH 22/26] Fixed HollowBoxBrush compilation errors. --- .../Editor/HollowBoxBrushInspector.cs.meta | 12 + .../Brushes/CompoundBrushes/HollowBoxBrush.cs | 226 +++++++++--------- .../CompoundBrushes/HollowBoxBrush.cs.meta | 12 + 3 files changed, 141 insertions(+), 109 deletions(-) create mode 100644 Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta create mode 100644 Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta new file mode 100644 index 00000000..28fb2868 --- /dev/null +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9a76fa0614387084cb85b598c0744270 +timeCreated: 1528366657 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs index 0bb06112..00fe96aa 100644 --- a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs @@ -1,111 +1,119 @@ -namespace Sabresaurus.SabreCSG +#if UNITY_EDITOR || RUNTIME_CSG + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Sabresaurus.SabreCSG { - using UnityEngine; - - public class HollowBoxBrush : CompoundBrush - { - /// - /// Gets or sets the thickness of the walls. - /// - /// The thickness of the walls. - public float WallThickness - { - get - { - return wallThickness; - } - set - { - wallThickness = value; - } - } - - /// - /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. - /// - /// The beautiful name of the brush. - public override string BeautifulBrushName - { - get - { - return "Hollow Box Brush"; - } - } - - public override int BrushCount - { - get - { - // If the brush is too small for walls, just default to a single brush. - return ( IsBrushXYZTooSmall ) ? 2 : 1; - } - } - - /// - /// Is the size of the bounds X, Y, and Z above the minimum size? - /// - /// - public bool IsBrushXYZTooSmall - { - get - { - return localBounds.size.x > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f && localBounds.size.y > wallThickness * 2; - } - } - - /// - /// The thickness of the walls. - /// - [SerializeField] - private float wallThickness = 0.25f; - - public override void UpdateVisibility() - { - } - - public override void Invalidate( bool polygonsChanged ) - { - base.Invalidate( polygonsChanged ); - - for( int i = 0; i < BrushCount; i++ ) - { - generatedBrushes[i].Mode = this.Mode; - generatedBrushes[i].IsNoCSG = this.IsNoCSG; - generatedBrushes[i].IsVisible = this.IsVisible; - generatedBrushes[i].HasCollision = this.HasCollision; - } - - if( IsBrushXYZTooSmall ) - { - generatedBrushes[0].Mode = CSGMode.Add; - BrushUtility.Resize( generatedBrushes[0], localBounds.size ); - - generatedBrushes[1].Mode = CSGMode.Subtract; - BrushUtility.Resize( generatedBrushes[1], localBounds.size - new Vector3( wallThickness * 2, wallThickness * 2, wallThickness * 2 ) ); - - for( int i = 0; i < BrushCount; i++ ) - { - generatedBrushes[i].SetPolygons( GeneratePolys( i ) ); - generatedBrushes[i].Invalidate( true ); - } - } - else - { - BrushUtility.Resize( generatedBrushes[0], localBounds.size ); - } - } - - private Polygon[] GeneratePolys( int index ) - { - Polygon[] output = generatedBrushes[index].GetPolygons(); - - for( int i = 0; i < 6; i++ ) - { - output[i].ResetVertexNormals(); - output[i].GenerateUvCoordinates(); - } - - return output; - } - } + [ExecuteInEditMode] + public class HollowBoxBrush : CompoundBrush + { + /// + /// Gets or sets the thickness of the walls. + /// + /// The thickness of the walls. + public float WallThickness + { + get + { + return wallThickness; + } + set + { + wallThickness = value; + } + } + + /// + /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name. + /// + /// The beautiful name of the brush. + public override string BeautifulBrushName + { + get + { + return "Hollow Box Brush"; + } + } + + public override int BrushCount + { + get + { + // If the brush is too small for walls, just default to a single brush. + return (IsBrushXYZTooSmall) ? 2 : 1; + } + } + + /// + /// Is the size of the bounds X, Y, and Z above the minimum size? + /// + /// + public bool IsBrushXYZTooSmall + { + get + { + return localBounds.size.x > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f && localBounds.size.y > wallThickness * 2; + } + } + + /// + /// The thickness of the walls. + /// + [SerializeField] + private float wallThickness = 0.25f; + + public override void UpdateVisibility() + { + } + + public override void Invalidate(bool polygonsChanged) + { + base.Invalidate(polygonsChanged); + + for (int i = 0; i < BrushCount; i++) + { + generatedBrushes[i].Mode = this.Mode; + generatedBrushes[i].IsNoCSG = this.IsNoCSG; + generatedBrushes[i].IsVisible = this.IsVisible; + generatedBrushes[i].HasCollision = this.HasCollision; + } + + if (IsBrushXYZTooSmall) + { + generatedBrushes[0].Mode = CSGMode.Add; + BrushUtility.Resize(generatedBrushes[0], localBounds.size); + + generatedBrushes[1].Mode = CSGMode.Subtract; + BrushUtility.Resize(generatedBrushes[1], localBounds.size - new Vector3(wallThickness * 2, wallThickness * 2, wallThickness * 2)); + + for (int i = 0; i < BrushCount; i++) + { + generatedBrushes[i].SetPolygons(GeneratePolys(i)); + generatedBrushes[i].Invalidate(true); + } + } + else + { + BrushUtility.Resize(generatedBrushes[0], localBounds.size); + } + } + + private Polygon[] GeneratePolys(int index) + { + Polygon[] output = generatedBrushes[index].GetPolygons(); + + for (int i = 0; i < 6; i++) + { + output[i].ResetVertexNormals(); + output[i].GenerateUvCoordinates(); + } + + return output; + } + } } + +#endif \ No newline at end of file diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta new file mode 100644 index 00000000..2e6895a0 --- /dev/null +++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: eed65674ef786f142a6e53243f320513 +timeCreated: 1528366664 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 94e3317fcf2be8b60ce62f6bd1bb7e21b94c98fb Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 12:39:11 +0200 Subject: [PATCH 23/26] Updated SabreCSG version to 1.7.1. --- Scripts/CSGModelBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/CSGModelBase.cs b/Scripts/CSGModelBase.cs index ed0efa21..5997e5a6 100644 --- a/Scripts/CSGModelBase.cs +++ b/Scripts/CSGModelBase.cs @@ -15,7 +15,7 @@ namespace Sabresaurus.SabreCSG // [ExecuteInEditMode] public class CSGModelBase : MonoBehaviour { - public const string VERSION_STRING = "1.7.0"; + public const string VERSION_STRING = "1.7.1"; protected const string DEFAULT_FALLBACK_MATERIAL_PATH = "Materials/Default_Map"; // Limit to how many vertices a Unity mesh can hold, before it must be split into a second mesh (just under 2^16) From be874bf6bb431bd12b6fb979d83e350b22617d96 Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 13:54:15 +0200 Subject: [PATCH 24/26] Optimize Geometry has been demoted and is now off by default. --- Scripts/Core/CSGBuildSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Core/CSGBuildSettings.cs b/Scripts/Core/CSGBuildSettings.cs index 492170ca..14b2919c 100755 --- a/Scripts/Core/CSGBuildSettings.cs +++ b/Scripts/Core/CSGBuildSettings.cs @@ -15,7 +15,7 @@ public class CSGBuildSettings // Also calculate tangents (needed for Unity's built in bump mapping) public bool GenerateTangents = true; - public bool OptimizeGeometry = true; + public bool OptimizeGeometry = false; public bool SaveMeshesAsAssets = false; From eae0faeef048125b58830f402b1a378e17ea070f Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 14:42:17 +0200 Subject: [PATCH 25/26] Fixed HollowBoxBrush inspector compilation errors. --- .../Editor/HollowBoxBrushInspector.cs | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs index b863b806..f28e346a 100644 --- a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs +++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs @@ -1,44 +1,47 @@ -namespace Sabresaurus.SabreCSG +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace Sabresaurus.SabreCSG { - using UnityEditor; - using UnityEngine; - - [CanEditMultipleObjects] - [CustomEditor( typeof( HollowBoxBrush ), true )] - public class HollowBoxBrushInspector : CompoundBrushInspector - { - private SerializedProperty wallThicknessProp; - - public override void DoInspectorGUI() - { - using( new NamedVerticalScope( "Hollow Box" ) ) - { - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField( wallThicknessProp ); - - if( EditorGUI.EndChangeCheck() ) - { - ApplyAndInvalidate(); - } - - EditorGUILayout.Space(); - } - - base.DoInspectorGUI(); - } - - protected override void OnEnable() - { - base.OnEnable(); - - // Setup the SerializedProperties. - wallThicknessProp = serializedObject.FindProperty( "wallThickness" ); - } - - private void ApplyAndInvalidate() - { - serializedObject.ApplyModifiedProperties(); - System.Array.ForEach( BrushTargets, item => item.Invalidate( true ) ); - } - } -} + [CanEditMultipleObjects] + [CustomEditor(typeof(HollowBoxBrush), true)] + public class HollowBoxBrushInspector : CompoundBrushInspector + { + SerializedProperty wallThicknessProp; + + public override void DoInspectorGUI() + { + using (new NamedVerticalScope("Hollow Box")) + { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(wallThicknessProp); + + if (EditorGUI.EndChangeCheck()) + { + ApplyAndInvalidate(); + } + + EditorGUILayout.Space(); + } + + base.DoInspectorGUI(); + } + + protected override void OnEnable() + { + base.OnEnable(); + + // Setup the SerializedProperties. + wallThicknessProp = serializedObject.FindProperty("wallThickness"); + } + + private void ApplyAndInvalidate() + { + serializedObject.ApplyModifiedProperties(); + System.Array.ForEach(BrushTargets, item => item.Invalidate(true)); + } + } +} \ No newline at end of file From ab9f8db88ee8a89cd96674b1a0014ce65b98794b Mon Sep 17 00:00:00 2001 From: Henry de Jongh Date: Thu, 7 Jun 2018 14:59:02 +0200 Subject: [PATCH 26/26] Updated SabreCSG version to 1.7.2. --- Scripts/CSGModelBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/CSGModelBase.cs b/Scripts/CSGModelBase.cs index 5997e5a6..377d0ee5 100644 --- a/Scripts/CSGModelBase.cs +++ b/Scripts/CSGModelBase.cs @@ -15,7 +15,7 @@ namespace Sabresaurus.SabreCSG // [ExecuteInEditMode] public class CSGModelBase : MonoBehaviour { - public const string VERSION_STRING = "1.7.1"; + public const string VERSION_STRING = "1.7.2"; protected const string DEFAULT_FALLBACK_MATERIAL_PATH = "Materials/Default_Map"; // Limit to how many vertices a Unity mesh can hold, before it must be split into a second mesh (just under 2^16)