From a5677155065e3cef6cc677ecc6ae8cf920f19386 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 20 Jul 2024 10:18:28 +0100 Subject: [PATCH 1/4] CSG library from Blocks --- .../Core/Runtime/PolyMesh.Geometry.cs | 10 + .../com.ixxy.polyhydra/Core/Runtime/csg.meta | 3 + .../Core/Runtime/csg/AssertOrThrow.cs | 64 + .../Core/Runtime/csg/AssertOrThrow.cs.meta | 3 + .../Core/Runtime/csg/CollisionSystem.cs | 104 ++ .../Core/Runtime/csg/CollisionSystem.cs.meta | 3 + .../Core/Runtime/csg/CsgContext.cs | 65 + .../Core/Runtime/csg/CsgContext.cs.meta | 11 + .../Core/Runtime/csg/CsgMath.cs | 92 ++ .../Core/Runtime/csg/CsgMath.cs.meta | 11 + .../Core/Runtime/csg/CsgObject.cs | 48 + .../Core/Runtime/csg/CsgObject.cs.meta | 11 + .../Core/Runtime/csg/CsgOperations.cs | 355 ++++++ .../Core/Runtime/csg/CsgOperations.cs.meta | 11 + .../Core/Runtime/csg/CsgPolygon.cs | 84 ++ .../Core/Runtime/csg/CsgPolygon.cs.meta | 11 + .../Core/Runtime/csg/CsgUtil.cs | 192 +++ .../Core/Runtime/csg/CsgUtil.cs.meta | 11 + .../Core/Runtime/csg/CsgVertex.cs | 55 + .../Core/Runtime/csg/CsgVertex.cs.meta | 11 + .../Core/Runtime/csg/Edge.cs | 64 + .../Core/Runtime/csg/Edge.cs.meta | 3 + .../Core/Runtime/csg/EdgeKey.cs | 75 ++ .../Core/Runtime/csg/EdgeKey.cs.meta | 3 + .../Core/Runtime/csg/Face.cs | 203 ++++ .../Core/Runtime/csg/Face.cs.meta | 3 + .../Core/Runtime/csg/FaceKey.cs | 52 + .../Core/Runtime/csg/FaceKey.cs.meta | 3 + .../Core/Runtime/csg/FaceProperties.cs | 30 + .../Core/Runtime/csg/FaceProperties.cs.meta | 3 + .../Core/Runtime/csg/FaceRecomposer.cs | 179 +++ .../Core/Runtime/csg/FaceRecomposer.cs.meta | 11 + .../Core/Runtime/csg/FaceTriangulator.cs | 387 ++++++ .../Core/Runtime/csg/FaceTriangulator.cs.meta | 3 + .../Core/Runtime/csg/Math3d.cs | 478 ++++++++ .../Core/Runtime/csg/Math3d.cs.meta | 3 + .../Core/Runtime/csg/MeshMath.cs | 865 +++++++++++++ .../Core/Runtime/csg/MeshMath.cs.meta | 3 + .../Core/Runtime/csg/OctreeImpl.cs | 356 ++++++ .../Core/Runtime/csg/OctreeImpl.cs.meta | 3 + .../Core/Runtime/csg/PolygonSplitter.cs | 1070 +++++++++++++++++ .../Core/Runtime/csg/PolygonSplitter.cs.meta | 11 + .../Core/Runtime/csg/SolidVertex.cs | 47 + .../Core/Runtime/csg/SolidVertex.cs.meta | 11 + .../Core/Runtime/csg/SpatialIndex.cs | 108 ++ .../Core/Runtime/csg/SpatialIndex.cs.meta | 3 + .../Core/Runtime/csg/Vertex.cs | 39 + .../Core/Runtime/csg/Vertex.cs.meta | 3 + .../Core/Runtime/csg/VertexKey.cs | 54 + .../Core/Runtime/csg/VertexKey.cs.meta | 3 + 50 files changed, 5231 insertions(+) create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs.meta create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs create mode 100644 Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs.meta diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs index 95b5d3b..3166866 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs @@ -3372,6 +3372,16 @@ public PolyMesh SplitEdges() return poly; } + public PolyMesh CsgSubtract(PolyMesh other) + { + var poly = Duplicate(); + CsgContext ctx = new CsgContext(GetBounds()); + CsgObject csgPoly = new CsgObject(this); + CsgObject csgPolyOther = new CsgObject(other); + var foo= CsgOperations.CsgSubtract(ctx, csgPoly, csgPolyOther); + return poly; + } + private PolyMesh SubdivideEdges(OpParams o) { int subdivisions = (int)o.GetValueA(this, 0); diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg.meta new file mode 100644 index 0000000..042ffc8 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1db8a3ebd85a461fba601485981450c3 +timeCreated: 1721327548 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs new file mode 100644 index 0000000..5652fc7 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs @@ -0,0 +1,64 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace com.google.apps.peltzer.client.model.util +{ + /// + /// Runtime assertions. + /// + public class AssertOrThrow + { + + /// + /// Verifies the condition is true. Otherwise throws an Exception. + /// + /// Condition to assert. + /// Message to include with exception, if thrown. + /// + /// Thrown when condition is false. + public static void True(bool condition, string msg) + { + if (!condition) + { + throw new Exception(msg); + } + } + + /// + /// Verifies the condition is false. Otherwise throws an Exception. + /// + /// Condition to assert. + /// Message to include with exception, if thrown. + /// + /// Thrown when condition is true. + public static void False(bool condition, string msg) + { + if (condition) + { + throw new Exception(msg); + } + } + + internal static T NotNull(T val, string msg) + { + if (val == null) + { + throw new Exception(msg); + } + return val; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs.meta new file mode 100644 index 0000000..6bb40cf --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/AssertOrThrow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 57292535523b4650898b227c7c28de23 +timeCreated: 1721327807 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs new file mode 100644 index 0000000..1766dd0 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs @@ -0,0 +1,104 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using System.Collections.Generic; +using System; +using com.google.apps.peltzer.client.model.core; + +namespace com.google.apps.peltzer.client.model.util +{ + + /// + /// Spatial index for objects based on their Bounds. + /// + public interface CollisionSystem + { + + + /// + /// Add an item to the CollisionSystem. + /// + /// The item to add + /// The item's initial bounds. + /// + /// Thrown when bounds is not contained by the CollisionSystem's bounds. + void Add(T item, Bounds bounds); + + /// + /// Update the bounds of an item. The item must already exist + /// in the index. + /// + /// The item to update. + /// The item's updated bounds. + /// + /// Thrown when the item isn't in the tree. + void UpdateItemBounds(T item, Bounds bounds); + + /// + /// Remove an item from the index. + /// + /// Item to remove. + /// + /// Thrown when the item isn't in the tree. + void Remove(T item); + + /// + /// Find items contained entirely within the given bounds. + /// This method will create a set when the number of items + /// is greater than zero. + /// + /// Containing bounds. + /// Set of items found. Null when this + /// method returns false. + /// Maximum number of items to find. + /// true if any items are found. + bool ContainedBy(Bounds bounds, out HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS); + + /// + /// Find items that intersect the given bounds. + /// This method will create a Set when the number of items + /// is greater than zero. + /// + /// Intersecting bounds. + /// Set of items found. Null when this + /// method returns false. + /// Maximum number of items to find. + /// true if any items are found. + bool IntersectedBy(Bounds bounds, out HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS); + + /// + /// Find items that intersect the given bounds. + /// This method will create a Set when the number of items + /// is greater than zero. + /// + /// Intersecting bounds. + /// Set of items found. Null when this + /// method returns false. + /// Maximum number of items to find. + /// true if any items are found. + bool IntersectedByPreallocated(Bounds bounds, ref HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS); + + /// + /// True if the given item is in the tree. + /// + bool HasItem(T item); + + // Return the bounds specified when the item was inserted or updated. + Bounds BoundsForItem(T item); + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs.meta new file mode 100644 index 0000000..1412708 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CollisionSystem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e911ab4ce7494547947ef72ec72451b0 +timeCreated: 1721327616 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs new file mode 100644 index 0000000..0536b4a --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs @@ -0,0 +1,65 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +using com.google.apps.peltzer.client.model.util; + +namespace Polyhydra.Core +{ + /// + /// Context for CSG operations. Unifies vertices. + /// + public class CsgContext + { + // We allow new points to be added if they are at least 3 epsilons away from existing points. + private readonly int WIGGLE_ROOM = 3; + private CollisionSystem tree; + + public CsgContext(Bounds bounds) + { + tree = new OctreeImpl(bounds); + } + + public CsgVertex CreateOrGetVertexAt(Vector3 loc) + { + Bounds bb = new Bounds(loc, Vector3.one * CsgMath.EPSILON * WIGGLE_ROOM); + CsgVertex closest = null; + HashSet vertices; + if (tree.IntersectedBy(bb, out vertices)) + { + float closestDist = 1000; + foreach (CsgVertex potential in vertices) + { + float d = Vector3.Distance(loc, potential.loc); + if (d < CsgMath.EPSILON && d < closestDist) + { + closest = potential; + closestDist = d; + } + } + } + if (closest == null) + { + closest = new CsgVertex(loc); + tree.Add(closest, new Bounds(loc, Vector3.zero)); + } + return closest; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs.meta new file mode 100644 index 0000000..1783405 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b47ddcc8bb7131d4596ba37ed25f0484 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs new file mode 100644 index 0000000..6776ed7 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs @@ -0,0 +1,92 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using com.google.apps.peltzer.client.model.core; + +namespace Polyhydra.Core +{ + public class CsgMath + { + // Floating point slop for testing things like coplanar, etc. + public const float EPSILON = 0.0001f; + + // Given a Unity Plane, find a point on that plane. + // public for testing. + public static Vector3 PointOnPlane(Plane plane) + { + return -(plane.normal * plane.distance); + } + + // Find the intersection of a ray with a plane. (Should return a value even if the plane is 'behind' the ray.) + public static void RayPlaneIntersection( + out Vector3 intersection, Vector3 rayStart, Vector3 rayNormal, Plane plane) + { + Ray ray = new Ray(rayStart, rayNormal); + float d; + plane.Raycast(ray, out d); + if (Math.Abs(d) > EPSILON) + { + intersection = ray.GetPoint(d); + } + else + { + intersection = rayStart; + } + } + + // Is the given point inside the given polygon. Assumes all points are coplanar. + // Returns 1 if inside, -1 if outside and 0 if on boundary. + // public for testing. + public static int IsInside(CsgPolygon poly, Vector3 point) + { + bool onEdge = false; + + for (int i = 0; i < poly.vertices.Count; i++) + { + Vector3 a = poly.vertices[i].loc; + Vector3 b = poly.vertices[(i + 1) % poly.vertices.Count].loc; + Vector3 c = poly.vertices[(i + 2) % poly.vertices.Count].loc; + int sameSide = SameSide(a, b, point, c); + if (sameSide < 0) + { + return -1; + } + if (sameSide == 0) + { + onEdge = true; + } + } + return onEdge ? 0 : 1; + } + + // Returns 1 if inside, -1 if outside and 0 if on boundary. + private static int SameSide(Vector3 a, Vector3 b, Vector3 check, Vector3 reference) + { + Vector3 checkSide = MeshMath.CalculateNormal(a, b, check); + Vector3 referenceSide = MeshMath.CalculateNormal(a, b, reference); + if (checkSide.magnitude < EPSILON) + { + return 0; + } + // Empirically, I've found that == is too lenient and distance is too restrictive. + // squared distance is not only more efficient, but it also produces better results. + return (referenceSide - checkSide).sqrMagnitude < EPSILON ? 1 : -1; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs.meta new file mode 100644 index 0000000..a76603e --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgMath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aef631201f62e02459daf025190d1f5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs new file mode 100644 index 0000000..8d318e8 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs @@ -0,0 +1,48 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace Polyhydra.Core +{ + /// + /// A collection of CsgPolygons that close a space. + /// + public class CsgObject + { + public List polygons { get; private set; } + public List vertices { get; private set; } + public Bounds bounds { get; private set; } + + public CsgObject(List polygons, List vertices) + { + this.polygons = polygons; + this.vertices = vertices; + Bounds bounds = new Bounds(vertices[0].loc, Vector3.zero); + for (int i = 1; i < vertices.Count; i++) + { + bounds.Encapsulate(vertices[i].loc); + } + this.bounds = bounds; + } + + public CsgObject(PolyMesh polyMesh) + { + throw new NotImplementedException(); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs.meta new file mode 100644 index 0000000..5e9d9c5 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 709b02782d67cca458a024be43329ce1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs new file mode 100644 index 0000000..837ceb8 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs @@ -0,0 +1,355 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +using com.google.apps.peltzer.client.model.core; +using com.google.apps.peltzer.client.model.util; + +namespace Polyhydra.Core +{ + + public class CsgOperations + { + private const float COPLANAR_EPS = 0.001f; + + + /// + /// Perform the subtract on CsgObjects. The implementation follows the paper: + /// http://vis.cs.brown.edu/results/videos/bib/pdf/Laidlaw-1986-CSG.pdf + /// + public static List CsgSubtract(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + { + SplitObject(ctx, leftObj, rightObj); + SplitObject(ctx, rightObj, leftObj); + SplitObject(ctx, leftObj, rightObj); + ClassifyPolygons(leftObj, rightObj); + ClassifyPolygons(rightObj, leftObj); + + FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; + List polys = SelectPolygons(leftObj, false, null, PolygonStatus.OUTSIDE, PolygonStatus.OPPOSITE); + polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.INSIDE)); + + return polys; + } + + /// + /// Perform union on CsgObjects + /// + private static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + { + SplitObject(ctx, leftObj, rightObj); + SplitObject(ctx, rightObj, leftObj); + SplitObject(ctx, leftObj, rightObj); + ClassifyPolygons(leftObj, rightObj); + ClassifyPolygons(rightObj, leftObj); + + FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; + List polys = SelectPolygons(leftObj, false, null, PolygonStatus.OUTSIDE, PolygonStatus.SAME); + polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.OUTSIDE)); + + return polys; + } + + /// + /// Perform intersection on CsgObjects + /// + private static List CsgIntersect(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + { + SplitObject(ctx, leftObj, rightObj); + SplitObject(ctx, rightObj, leftObj); + SplitObject(ctx, leftObj, rightObj); + ClassifyPolygons(leftObj, rightObj); + ClassifyPolygons(rightObj, leftObj); + + FaceProperties facePropertiesForNewFaces = leftObj.polygons[0].faceProperties; + List polys = SelectPolygons(leftObj, false, null, PolygonStatus.INSIDE, PolygonStatus.SAME); + polys.AddRange(SelectPolygons(rightObj, true, facePropertiesForNewFaces, PolygonStatus.INSIDE)); + + return polys; + + } + + /// + /// Select all of the polygons in the object with any of the given statuses. + /// + private static List SelectPolygons(CsgObject obj, bool invert, FaceProperties? overwriteFaceProperties, params PolygonStatus[] status) + { + HashSet selectedStatus = new HashSet(status); + List polys = new List(); + + foreach (CsgPolygon poly in obj.polygons) + { + if (selectedStatus.Contains(poly.status)) + { + CsgPolygon polyToAdd = poly; + if (invert) + { + polyToAdd = poly.Invert(); + } + if (overwriteFaceProperties.HasValue) + { + polyToAdd.faceProperties = overwriteFaceProperties.Value; + } + polys.Add(polyToAdd); + } + } + + return polys; + } + + // Section 7: Classify all polygons in the object. + private static void ClassifyPolygons(CsgObject obj, CsgObject wrt) + { + // Set up adjacency information. + foreach (CsgPolygon poly in obj.polygons) + { + for (int i = 0; i < poly.vertices.Count; i++) + { + int j = (i + 1) % poly.vertices.Count; + poly.vertices[i].neighbors.Add(poly.vertices[j]); + poly.vertices[j].neighbors.Add(poly.vertices[i]); + } + } + + // Classify polys. + foreach (CsgPolygon poly in obj.polygons) + { + if (HasUnknown(poly) || AllBoundary(poly)) + { + ClassifyPolygonUsingRaycast(poly, wrt); + if (poly.status == PolygonStatus.INSIDE || poly.status == PolygonStatus.OUTSIDE) + { + VertexStatus newStatus = poly.status == PolygonStatus.INSIDE ? VertexStatus.INSIDE : VertexStatus.OUTSIDE; + foreach (CsgVertex vertex in poly.vertices) + { + PropagateVertexStatus(vertex, newStatus); + } + } + } + else + { + // Use the status of the first vertex that is inside or outside. + foreach (CsgVertex vertex in poly.vertices) + { + if (vertex.status == VertexStatus.INSIDE) + { + poly.status = PolygonStatus.INSIDE; + break; + } + if (vertex.status == VertexStatus.OUTSIDE) + { + poly.status = PolygonStatus.OUTSIDE; + break; + } + } + AssertOrThrow.True(poly.status != PolygonStatus.UNKNOWN, "Should have classified polygon."); + } + } + } + + // Fig 8.1: Propagate vertex status. + private static void PropagateVertexStatus(CsgVertex vertex, VertexStatus newStatus) + { + if (vertex.status == VertexStatus.UNKNOWN) + { + vertex.status = newStatus; + foreach (CsgVertex neighbor in vertex.neighbors) + { + PropagateVertexStatus(neighbor, newStatus); + } + } + } + + // Fig 7.2: Classify a given polygon by raycasting from its barycenter into the faces of the other object. + // Public for testing. + public static void ClassifyPolygonUsingRaycast(CsgPolygon poly, CsgObject wrt) + { + Vector3 rayStart = poly.baryCenter; + Vector3 rayNormal = poly.plane.normal; + CsgPolygon closest = null; + float closestPolyDist = float.MaxValue; + + bool done; + int count = 0; + do + { + done = true; // Done unless we hit a special case. + foreach (CsgPolygon otherPoly in wrt.polygons) + { + float dot = Vector3.Dot(rayNormal, otherPoly.plane.normal); + bool perp = Mathf.Abs(dot) < CsgMath.EPSILON; + bool onOtherPlane = Mathf.Abs(otherPoly.plane.GetDistanceToPoint(rayStart)) < CsgMath.EPSILON; + Vector3 projectedToOtherPlane = Vector3.zero; + float signedDist = -1f; + if (!perp) + { + CsgMath.RayPlaneIntersection(out projectedToOtherPlane, rayStart, rayNormal, otherPoly.plane); + float dist = Vector3.Distance(projectedToOtherPlane, rayStart); + signedDist = dist * Mathf.Sign(Vector3.Dot(rayNormal, (projectedToOtherPlane - rayStart))); + } + + if (perp && onOtherPlane) + { + done = false; + break; + } + else if (perp && !onOtherPlane) + { + // no intersection + } + else if (!perp && onOtherPlane) + { + int isInside = CsgMath.IsInside(otherPoly, projectedToOtherPlane); + if (isInside >= 0) + { + closestPolyDist = 0; + closest = otherPoly; + break; + } + } + else if (!perp && signedDist > 0) + { + if (signedDist < closestPolyDist) + { + int isInside = CsgMath.IsInside(otherPoly, projectedToOtherPlane); + if (isInside > 0) + { + closest = otherPoly; + closestPolyDist = signedDist; + } + else if (isInside == 0) + { + // On segment, perturb and try again. + done = false; + break; + } + } + } + } + if (!done) + { + // Perturb the normal and try again. + rayNormal += new Vector3( + UnityEngine.Random.Range(-0.1f, 0.1f), + UnityEngine.Random.Range(-0.1f, 0.1f), + UnityEngine.Random.Range(-0.1f, 0.1f)); + rayNormal = rayNormal.normalized; + } + count++; + } while (!done && count < 5); + + if (closest == null) + { + // Didn't hit any polys, we are outside. + poly.status = PolygonStatus.OUTSIDE; + } + else + { + float dot = Vector3.Dot(poly.plane.normal, closest.plane.normal); + if (Mathf.Abs(closestPolyDist) < CsgMath.EPSILON) + { + poly.status = dot < 0 ? PolygonStatus.OPPOSITE : PolygonStatus.SAME; + } + else + { + poly.status = dot < 0 ? PolygonStatus.OUTSIDE : PolygonStatus.INSIDE; + } + } + } + + private static bool HasUnknown(CsgPolygon poly) + { + foreach (CsgVertex vertex in poly.vertices) + { + if (vertex.status == VertexStatus.UNKNOWN) + { + return true; + } + } + return false; + } + + private static bool AllBoundary(CsgPolygon poly) + { + foreach (CsgVertex vertex in poly.vertices) + { + if (vertex.status != VertexStatus.BOUNDARY) + { + return false; + } + } + return true; + } + + // Public for testing. + public static void SplitObject(CsgContext ctx, CsgObject toSplit, CsgObject splitBy) + { + bool splitPoly; + int count = 0; + HashSet alreadySplit = new HashSet(); + do + { + splitPoly = false; + // Temporary guard to prevent infinite loops while there are bugs. + // TODO(bug) figure out why csg creates so many rejected splits. + count++; + if (count > 100) + { + // This usually occurs when csg keeps trying to do the same invalid split over and over. + // If the algorithm has reached this point, it usually means that the two meshes are + // split enough to perform a pretty good looking csg subtraction. More investigation + // should be done on bug and we may be able to remove this guard. + return; + } + foreach (CsgPolygon toSplitPoly in toSplit.polygons) + { + if (alreadySplit.Contains(toSplitPoly)) + { + continue; + } + alreadySplit.Add(toSplitPoly); + if (toSplitPoly.bounds.Intersects(splitBy.bounds)) + { + foreach (CsgPolygon splitByPoly in splitBy.polygons) + { + if (toSplitPoly.bounds.Intersects(splitByPoly.bounds) + && !Coplanar(toSplitPoly.plane, splitByPoly.plane)) + { + splitPoly = PolygonSplitter.SplitPolys(ctx, toSplit, toSplitPoly, splitByPoly); + if (splitPoly) + { + break; + } + } + } + } + if (splitPoly) + { + break; + } + } + } while (splitPoly); + } + + private static bool Coplanar(Plane plane1, Plane plane2) + { + return Mathf.Abs(plane1.distance - plane2.distance) < COPLANAR_EPS + && Vector3.Distance(plane1.normal, plane2.normal) < COPLANAR_EPS; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs.meta new file mode 100644 index 0000000..6ba5bf1 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcddfe93d18f9a44c834ab20b211b9a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs new file mode 100644 index 0000000..bcc53ba --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs @@ -0,0 +1,84 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +using com.google.apps.peltzer.client.model.core; + +namespace Polyhydra.Core +{ + public enum PolygonStatus + { + UNKNOWN, + INSIDE, + OUTSIDE, + SAME, + OPPOSITE, + } + + /// + /// A convex, coplanar polygon. + /// + public class CsgPolygon + { + public List vertices { get; set; } + public Plane plane { get; private set; } + public FaceProperties faceProperties { get; set; } + public Bounds bounds { get; private set; } + public PolygonStatus status { get; set; } + public Vector3 baryCenter; + + public CsgPolygon(List vertices, FaceProperties faceProperties, Vector3? normal = null) + { + this.vertices = vertices; + this.faceProperties = faceProperties; + + if (normal != null) + { + plane = new Plane(normal.Value, vertices[0].loc); + } + else + { + plane = new Plane(vertices[0].loc, vertices[1].loc, vertices[2].loc); + } + + // Calc bounds and baryCenter + Bounds bounds = new Bounds(vertices[0].loc, Vector3.zero); + baryCenter = vertices[0].loc; + for (int i = 1; i < vertices.Count; i++) + { + bounds.Encapsulate(vertices[i].loc); + baryCenter += vertices[i].loc; + } + baryCenter /= (float)vertices.Count; + + // Expand a bit to cover floating point error. + bounds.Expand(0.002f); + this.bounds = bounds; + + // Status is UNKNOWN to start with. + status = PolygonStatus.UNKNOWN; + } + + internal CsgPolygon Invert() + { + List newVerts = new List(vertices); + newVerts.Reverse(); + return new CsgPolygon(newVerts, faceProperties, -plane.normal); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs.meta new file mode 100644 index 0000000..469cf09 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgPolygon.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce173431191e3534b95efbdd6ab4d99a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs new file mode 100644 index 0000000..25b087e --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs @@ -0,0 +1,192 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace Polyhydra.Core +{ + public class PolyEdge + { + CsgVertex a; + CsgVertex b; + + public PolyEdge(CsgVertex a, CsgVertex b) + { + this.a = a; + this.b = b; + } + + public PolyEdge Reversed() + { + return new PolyEdge(b, a); + } + + public override bool Equals(object obj) + { + if (obj is PolyEdge) + { + PolyEdge other = (PolyEdge)obj; + return a == other.a && b == other.b; + } + return false; + } + + public override int GetHashCode() + { + int hc = 13; + hc = (hc * 31) + a.GetHashCode(); + hc = (hc * 31) + b.GetHashCode(); + return hc; + } + } + + public class CsgUtil + { + + // Do some sanity checks on a polygon split: + // 1) All polys should have the same normal. + // 2) Each vertex from the original poly should be in at least one splitPoly + // 3) Each split poly should share at least one edge with one other (the edge should be reversed) + // 4) No edge should be in more than one poly (in the same order) + // 5) No vertex should be in the same poly more than once + // 6) Every edge in the initial polygon should be in a split, except those *edges* that were split. + // We pass in numSplitEdges to tell the test how many that should be. + public static bool IsValidPolygonSplit(CsgPolygon initialPoly, List splitPolys, int numSplitEdges) + { + List> vertsForPolys = new List>(); + List> edgesForPolys = new List>(); + + // Set up some datastructures, check normals while we are looping. + foreach (CsgPolygon poly in splitPolys) + { + vertsForPolys.Add(new HashSet(poly.vertices)); + edgesForPolys.Add(Edges(poly)); + if (Vector3.Distance(initialPoly.plane.normal, poly.plane.normal) > 0.001f) + { + Console.Write("Normals do not match: " + initialPoly.plane.normal + " vs " + poly.plane.normal); + return false; + } + } + + // Look for each vertex from the original poly: + foreach (CsgVertex vert in initialPoly.vertices) + { + bool found = false; + foreach (HashSet verts in vertsForPolys) + { + if (verts.Contains(vert)) + { + found = true; + break; + } + } + if (!found) + { + Console.Write("Vertex from original poly is missing from split polys"); + return false; + } + } + + // For each poly, find another poly with a matching edge (going the other direction) + for (int i = 0; i < edgesForPolys.Count; i++) + { + HashSet polyEdges = edgesForPolys[i]; + bool foundEdge = false; + for (int j = 0; j < edgesForPolys.Count; j++) + { + if (i == j) + { + continue; // Don't compare polygon to itself + } + foreach (PolyEdge edge in polyEdges) + { + if (edgesForPolys[j].Contains(edge.Reversed())) + { + foundEdge = true; + } + } + } + if (!foundEdge) + { + Console.Write("Poly " + i + " does not have any edges in other polys"); + return false; + } + } + + // Check that the total number of edges is the same as the sum of all edges in all splits + // i.e. there are no duplicate edges. + HashSet alledges = new HashSet(); + int sum = 0; + foreach (HashSet edges in edgesForPolys) + { + sum += edges.Count; + alledges.UnionWith(edges); + } + if (sum != alledges.Count) + { + Console.Write("Found duplicate edges."); + return false; + } + + // Check to make sure no polys have the same vertex more than once. + for (int i = 0; i < vertsForPolys.Count; i++) + { + // The 'Set' should have the same number of verts as the 'List' + if (vertsForPolys[i].Count != splitPolys[i].vertices.Count) + { + Console.Write("Found duplicate vertex"); + return false; + } + } + + // Look for all edges in the list above. The count should be the same number of edges + // in the initial poly minus the number of edges that were split. + int count = numSplitEdges; + HashSet initialEdges = Edges(initialPoly); + foreach (PolyEdge initialEdge in initialEdges) + { + if (alledges.Contains(initialEdge)) + { + count++; + } + } + if (initialEdges.Count != count) + { + Console.Write("Edges from initial poly are missing"); + return false; + } + + return true; + } + + private static HashSet Edges(CsgPolygon poly) + { + HashSet edges = new HashSet(); + + for (int i = 0; i < poly.vertices.Count; i++) + { + CsgVertex a = poly.vertices[i]; + CsgVertex b = poly.vertices[(i + 1) % poly.vertices.Count]; + edges.Add(new PolyEdge(a, b)); + } + + return edges; + } + } +} + diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs.meta new file mode 100644 index 0000000..39bddb2 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01ba1f473892cb343905ced5a6c2e13d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs new file mode 100644 index 0000000..7408d4c --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs @@ -0,0 +1,55 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace Polyhydra.Core +{ + public enum VertexStatus + { + UNKNOWN, + INSIDE, + OUTSIDE, + BOUNDARY + } + + /// + /// A vertex with an associated 'status'. The status determines whether a given vertex is inside another + /// object (or outside or on its boundary). + /// + [System.Diagnostics.DebuggerDisplay("{ToString()}")] + public class CsgVertex + { + public Vector3 loc { get; private set; } + public HashSet neighbors { get; private set; } + public VertexStatus status { get; set; } + private readonly String asString; + + public CsgVertex(Vector3 loc) + { + this.loc = loc; + this.neighbors = new HashSet(); + this.status = VertexStatus.UNKNOWN; + this.asString = "(" + loc.x.ToString("0.000") + ", " + loc.y.ToString("0.000") + ", " + loc.z.ToString("0.000") + ")"; + } + + public override string ToString() + { + return asString; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs.meta new file mode 100644 index 0000000..13aa66c --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgVertex.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57ebefaf7caae4c448f0bfdf2a9ecb21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs new file mode 100644 index 0000000..327ac85 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs @@ -0,0 +1,64 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace com.google.apps.peltzer.client.model.util +{ + /// + /// A pair that represents a line segment. Makes it easy to lookup segments in a Hashtable. + /// + internal struct Edge + { + private readonly int startId; + private readonly int endId; + + internal Edge(int startId, int endId) + { + this.startId = startId; + this.endId = endId; + } + + public override bool Equals(object obj) + { + if (obj is Edge) + { + Edge other = (Edge)obj; + return startId == other.startId && endId == other.endId; + } + return false; + } + + public Edge Reverse() + { + return new Edge(endId, startId); + } + + public override int GetHashCode() + { + int hc = 271; + hc = (hc * 257) + startId; + hc = (hc * 257) + endId; + return hc; + } + + public override string ToString() + { + return startId + " - " + endId; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs.meta new file mode 100644 index 0000000..ef67e7f --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Edge.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 19ef2a711492441e903669e1c752de1f +timeCreated: 1721327917 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs new file mode 100644 index 0000000..c4ab660 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs @@ -0,0 +1,75 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// A canonical id for an edge, which includes the id of the mesh it belongs to. + /// + public class EdgeKey + { + private readonly int _meshId; + private readonly int _vertexId1; + private readonly int _vertexId2; + private readonly int _hashCode; + + public EdgeKey(int meshId, int vertexId1, int vertexId2) + { + this._meshId = meshId; + if (vertexId1 < vertexId2) + { + this._vertexId1 = vertexId1; + this._vertexId2 = vertexId2; + } + else + { + this._vertexId1 = vertexId2; + this._vertexId2 = vertexId1; + } + // Hashcode suggested by Effective Java and Jon Skeet: + // http://stackoverflow.com/questions/11742593/what-is-the-hashcode-for-a-custom-class-having-just-two-int-properties + _hashCode = 17; + _hashCode = _hashCode * 31 + _meshId; + _hashCode = _hashCode * 31 + _vertexId1; + _hashCode = _hashCode * 31 + _vertexId2; + } + + public override bool Equals(object obj) + { + return Equals(obj as EdgeKey); + } + + public bool Equals(EdgeKey otherKey) + { + return otherKey != null + && _meshId == otherKey._meshId + && _vertexId1 == otherKey._vertexId1 + && _vertexId2 == otherKey._vertexId2; + } + + public override int GetHashCode() + { + return _hashCode; + } + + public bool ContainsVertex(int vertexId) + { + return _vertexId1 == vertexId || _vertexId2 == vertexId; + } + + public int meshId { get { return _meshId; } } + public int vertexId1 { get { return _vertexId1; } } + public int vertexId2 { get { return _vertexId2; } } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs.meta new file mode 100644 index 0000000..ebba005 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/EdgeKey.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 271d1e9e46274d89bc14a5a1ee227a4d +timeCreated: 1721328248 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs new file mode 100644 index 0000000..7de36cb --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs @@ -0,0 +1,203 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using com.google.apps.peltzer.client.model.render; +using UnityEngine; + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// A polygonal face of a MMesh. Vertices must be specified in clockwise + /// order. Edges may not cross. Each Vertex must have a normal relative + /// to this face. + /// + public class Face + { + // The id of this face (unique within the mesh) + private readonly int _id; + // The ordered collection of vertex ids that comprise the face, in clockwise order. + private readonly ReadOnlyCollection _vertexIds; + // We support two different triangulations as the renderer uses triangulation one way, and the modelling tools + // another. The renderer needs indices into its vertex buffer, and therefore wants triangulation to return triangle + // indices relative to the vertices in the face - it will apply an offset to those, and add them to the mesh's + // triangle array. More straightforwardly, modeling operations need to look up the relevant vertices from the + // MMesh, and hence require the vertex id. + + // This triangulation is a list of triangles whose indices are vertex ids. This is used by mesh validation among + // others. This representation is used to allow easy lookup of vertex data from the MMesh. + private List _modelTriangulation; + + // This triangulation is a list of triangles which index into this face's _vertexIds collection. This representation + // is required for adding triangles to a Unity Mesh, but isn't as helpful for general modelling operations. + private List _renderTriangulation; + + // The face normal. Prior to face being added to a mesh via committing a GeometryOperation, this may not be set + // (which is represented by having a value of Vector3.zero) + private Vector3 _normal; + + // The properties of the face - primarily the material. + private FaceProperties _properties; + + // Read-only getters. + public int id { get { return _id; } } + public ReadOnlyCollection vertexIds { get { return _vertexIds; } } + public Vector3 normal { get { return _normal; } } + public FaceProperties properties { get { return _properties; } } + + // Cached vertex data, used to optimize construction of full mesh data for rendering. + private List cachedMeshSpacePositions = null; + private List cachedRenderNormals = null; + + /// + /// Constructs a face with no normal. This constructor should only be used when it is certain that the normal will + /// be calculated later (ie, by Mesh.CommitOperation) + /// + /// Face id + /// Vertex ids for face in clockwise winding order. + /// Face properties + private Face(int id, ReadOnlyCollection vertexIds, FaceProperties properties) + { + _id = id; + _vertexIds = vertexIds; + _normal = Vector3.zero; + + _properties = properties; + _modelTriangulation = null; + _renderTriangulation = null; + + // Allocating capacity here to avoid needing to do it on every fetch - capacity is retained after Clear(). + cachedMeshSpacePositions = new List(vertexIds.Count); + cachedRenderNormals = new List(vertexIds.Count); + } + + // Constructs a face with an unset normal - the normal will be calculated + public static Face FaceWithPendingNormal(int id, ReadOnlyCollection vertexIds, FaceProperties properties) + { + return new Face(id, vertexIds, properties); + } + + // Constructs a face with an unset normal - the normal will be calculated + public static Face FaceWithPendingNormal(int id, List vertices, FaceProperties properties) + { + List indices = new List(vertices.Count); + for (int i = 0; i < vertices.Count; i++) + { + indices.Add(vertices[i].id); + } + return new Face(id, indices.AsReadOnly(), properties); + } + + /// + /// Constructs a face with the supplied normal. This constructor should only be used when a face is being created with + /// the same normal as a preexisting face - otherwise one of the normal calculating constructors should be used. + /// + /// Face id + /// Vertex ids for face in clockwise winding order. + /// Normal from another face this face should match + /// Face properties + public Face(int id, ReadOnlyCollection vertexIds, Vector3 normal, FaceProperties properties) + { + _id = id; + _vertexIds = vertexIds; + _normal = normal; + + _properties = properties; + _modelTriangulation = null; + _renderTriangulation = null; + + // Allocating capacity here to avoid needing to do it on every fetch - capacity is retained after Clear(). + cachedMeshSpacePositions = new List(vertexIds.Count); + cachedRenderNormals = new List(vertexIds.Count); + } + + /// + /// Constructs a face, calculating its normal. + /// + /// Face id + /// Vertex ids for face in clockwise winding order. + /// Dictionary of vertex ids to vertex data. + /// Face properties + public Face(int id, ReadOnlyCollection vertexIds, Dictionary verticesById, FaceProperties properties) + { + _id = id; + _vertexIds = vertexIds; + _normal = MeshMath.CalculateNormal(vertexIds, verticesById); + _properties = properties; + _modelTriangulation = null; + _renderTriangulation = null; + // Allocating capacity here to avoid needing to do it on every fetch - capacity is retained after Clear(). + cachedMeshSpacePositions = new List(vertexIds.Count); + cachedRenderNormals = new List(vertexIds.Count); + } + + /// + /// Private constructor - exists to support Clone(). + /// + private Face(int id, ReadOnlyCollection vertexIds, Vector3 normal, FaceProperties properties, + List modelTriangulation, List renderTriangulation, + List cachedMeshSpacePositions, List cachedRenderNormals) + { + _id = id; + _vertexIds = vertexIds; + _normal = normal; + _properties = properties; + _modelTriangulation = modelTriangulation; + _renderTriangulation = renderTriangulation; + + this.cachedMeshSpacePositions = cachedMeshSpacePositions; + this.cachedRenderNormals = cachedRenderNormals; + } + + /// + /// Recalculate the normal for this face, using the supplied vertex data. + /// + public void RecalculateNormal(Dictionary verticesById) + { + _normal = MeshMath.CalculateNormal(vertexIds, verticesById); + cachedRenderNormals.Clear(); + for (int i = 0; i < vertexIds.Count; i++) + { + cachedRenderNormals.Add(_normal); + } + } + + /// + /// Clears the vertex cache (because one of them has been modified). Cache will be recalculated next time the + /// vertices are accessed. + /// + public void InvalidateVertexCache() + { + cachedMeshSpacePositions.Clear(); + } + + + public Face Clone() + { + // Properties is a value object, so no need to clone. But we still need to + // make a new Face, so that the properties aren't shared. + return new Face( + _id, + _vertexIds, + _normal, + _properties, + _modelTriangulation, + _renderTriangulation, + new List(cachedMeshSpacePositions), + new List(cachedRenderNormals)); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs.meta new file mode 100644 index 0000000..7ccfb36 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Face.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8fa8c1bf27f04a878a52143aa70eb383 +timeCreated: 1721327992 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs new file mode 100644 index 0000000..f14d155 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs @@ -0,0 +1,52 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// A canonical id for a face, which includes the id of the mesh it belongs to. + /// + public class FaceKey + { + private readonly int _meshId; + private readonly int _faceId; + private readonly int _hashCode; + + public FaceKey(int meshId, int faceId) + { + _meshId = meshId; + _faceId = faceId; + // 31 is a good number: http://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier + _hashCode = (151 + meshId) * 31 + faceId; + } + + public override bool Equals(object obj) + { + return Equals(obj as FaceKey); + } + + public bool Equals(FaceKey otherKey) + { + return otherKey != null && _faceId == otherKey._faceId && _meshId == otherKey._meshId; + } + + public override int GetHashCode() + { + return _hashCode; + } + + public int meshId { get { return _meshId; } } + public int faceId { get { return _faceId; } } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs.meta new file mode 100644 index 0000000..bba59eb --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceKey.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2820227f8f4043cbbefbccc213510590 +timeCreated: 1721327969 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs new file mode 100644 index 0000000..fc59479 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs @@ -0,0 +1,30 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace com.google.apps.peltzer.client.model.core +{ + + /// + /// Properties for Faces. A value-type. + /// + public struct FaceProperties + { + public int materialId { get; private set; } + + public FaceProperties(int materialId) + { + this.materialId = materialId; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs.meta new file mode 100644 index 0000000..bb34e35 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceProperties.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 24f8ec0d4ab545b7874fe217ff36920c +timeCreated: 1721327782 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs new file mode 100644 index 0000000..6f1a275 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs @@ -0,0 +1,179 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +using com.google.apps.peltzer.client.model.util; + +namespace Polyhydra.Core +{ + public class FaceRecomposer + { + /// + /// Given a list of polygons (which are specified as a list of vertices) we try to merge together + /// as many polygons as possible. + /// + /// We assume that two vertices with the same id are the same vertex (semantically speaking). So our + /// strategy is to look for matching segments in two polygons. If two polygons have the same pair + /// (in reverse order from each other) within their vertex list, we can join them at that segment. + /// + /// If there are no overlapping segments, we also look for a shared vertex. If we find one, we + /// look for a possible T-junction. If we find one, we will join the polygons at that junction. + /// + public static List> RecomposeFace(List> startPieces) + { + List> joinedPieces = new List>(); + while (startPieces.Count > 0) + { + Dictionary curSegments = new Dictionary(); + Dictionary curVerts = new Dictionary(); + List curPiece = startPieces[0]; + startPieces.RemoveAt(0); + LoadSegments(curPiece, curSegments, curVerts); + + // Walk through all other pieces to see if one shares a segment. + bool foundJoin; + do + { + foundJoin = false; + for (int i = 0; i < startPieces.Count; i++) + { + List toJoin = startPieces[i]; + + for (int j = 0; j < toJoin.Count; j++) + { + Edge seg = new Edge(toJoin[(j + 1) % toJoin.Count].vertexId, toJoin[j].vertexId); + int joinAt; + if (curSegments.TryGetValue(seg, out joinAt)) + { + JoinPiecesAt(curPiece, joinAt, toJoin, j); + LoadSegments(curPiece, curSegments, curVerts); + startPieces.RemoveAt(i); + foundJoin = true; + break; + } + if (foundJoin) + { + break; + } + } + + if (!foundJoin) + { + // Look for a matching point. Maybe split the polygon at that point. + for (int j = 0; j < toJoin.Count; j++) + { + int idx; + if (curVerts.TryGetValue(toJoin[j].vertexId, out idx)) + { + int jMinusOne = (j - 1 + toJoin.Count) % toJoin.Count; + int jPlusOne = (j + 1) % toJoin.Count; + int idxPlusOne = (idx + 1) % curPiece.Count; + int idxMinusOne = (idx - 1 + curPiece.Count) % curPiece.Count; + + if (IsOnSegment(toJoin[jPlusOne], curPiece[idx], curPiece[idxMinusOne]) + || IsOnSegment(curPiece[idxMinusOne], toJoin[j], toJoin[jPlusOne])) + { + curPiece.RemoveAt(idx); + curPiece.InsertRange(idx, CycleAndRemoveN(toJoin, j + 1, 1)); + LoadSegments(curPiece, curSegments, curVerts); + startPieces.RemoveAt(i); + foundJoin = true; + break; + } + else if (IsOnSegment(toJoin[jMinusOne], curPiece[idx], curPiece[idxPlusOne]) + || IsOnSegment(curPiece[idxPlusOne], toJoin[j], toJoin[jMinusOne])) + { + curPiece.RemoveAt(idx); + curPiece.InsertRange(idx, CycleAndRemoveN(toJoin, j + 1, 1)); + LoadSegments(curPiece, curSegments, curVerts); + startPieces.RemoveAt(i); + foundJoin = true; + break; + } + } + } + } + if (foundJoin) + { + break; + } + } + } while (foundJoin); + joinedPieces.Add(curPiece); + } + + return joinedPieces; + } + + /// + /// Look for a T-junction. + /// + private static bool IsOnSegment(SolidVertex point, SolidVertex start, SolidVertex end) + { + Vector3 startLoc = start.position; + Vector3 lineVec = (end.position - start.position).normalized; + float lineLength = Vector3.Distance(start.position, end.position); + Vector3 startToPoint = point.position - startLoc; + float t = Vector3.Dot(startToPoint, lineVec); + Vector3 projected = startLoc + lineVec * t; + return t >= 0 && t <= lineLength && Vector3.Distance(projected, point.position) < 0.0005; + } + + /// + /// Join two polygons that share a segment. + /// + private static void JoinPiecesAt(List mainPiece, int joinAt, List toJoin, int joinTo) + { + List toInsert = new List(); + for (int i = 0; i < (toJoin.Count - 2); i++) + { + toInsert.Add(toJoin[(i + joinTo + 2) % toJoin.Count]); + } + mainPiece.InsertRange((joinAt + 1) % mainPiece.Count, toInsert); + } + + /// + /// Grab a sub-chain of a polygon. + /// + private static List CycleAndRemoveN(List orig, int offset, int n) + { + List copy = new List(); + for (int i = 0; i < (orig.Count - n); i++) + { + copy.Add(orig[(i + offset) % orig.Count]); + } + return copy; + } + + /// + /// Fill the indexes with data from the given polygon. + /// + private static void LoadSegments( + List curPiece, Dictionary curSegments, Dictionary curVerts) + { + curSegments.Clear(); + curVerts.Clear(); + for (int i = 0; i < curPiece.Count; i++) + { + curVerts[curPiece[i].vertexId] = i; + curSegments[new Edge(curPiece[i].vertexId, curPiece[(i + 1) % curPiece.Count].vertexId)] = i; + } + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs.meta new file mode 100644 index 0000000..0e37d26 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceRecomposer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a62138092bfcd04595c011ee9086673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs new file mode 100644 index 0000000..c82cda9 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs @@ -0,0 +1,387 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using com.google.apps.peltzer.client.model.core; +using com.google.apps.peltzer.client.model.util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace com.google.apps.peltzer.client.model.render +{ + public struct Triangle + { + public int vertId0 { get; private set; } + public int vertId1 { get; private set; } + public int vertId2 { get; private set; } + + public Triangle(int v1, int v2, int v3) + { + vertId0 = v1; + vertId1 = v2; + vertId2 = v3; + } + + public override string ToString() + { + return string.Format("[Triangle: vertId0={0}, vertId1={1}, vertId2={2}]", vertId0, vertId1, vertId2); + } + + public override bool Equals(object obj) + { + return obj is Triangle ? Equals((Triangle)obj) : base.Equals(obj); + } + + public bool Equals(Triangle other) + { + // Same vertices in same winding order. + return (vertId0 == other.vertId0 && vertId1 == other.vertId1 && vertId2 == other.vertId2) || + (vertId0 == other.vertId1 && vertId1 == other.vertId2 && vertId2 == other.vertId0) || + (vertId0 == other.vertId2 && vertId1 == other.vertId0 && vertId2 == other.vertId1); + } + + public override int GetHashCode() + { + // 10 bits for each id, ordered by size, beyond which collisions will occur. + int[] verts = { vertId0, vertId1, vertId2 }; + Array.Sort(verts); + return verts[2] << 20 + verts[1] << 10 + verts[0]; + } + } + + // Defines an extension to linked list to allow circular next operation and circular previous operation. + public static class CircularLinkedList + { + public static LinkedListNode CircularNext( + this LinkedListNode current) + { + if (current == current.List.Last) + { + return current.List.First; + } + else + { + return current.Next; + } + } + public static LinkedListNode CircularPrevious( + this LinkedListNode current) + { + if (current == current.List.First) + { + return current.List.Last; + } + else + { + return current.Previous; + } + } + } + + public class FaceTriangulator + { + + private struct HoleInfo + { + public LinkedList vertList; + public LinkedListNode bestCandidate; + public Vector3 bestPos; + public float bestMagnitude; + } + + /// + /// Triangulate a polygon with holes. The current implementation is a somewhat slow O(n^2) + /// ear-clipping algorithm due to its straight-forward implementation. + /// + /// Outside of the polygon -- in clockwise order. + /// List of triangles that fully cover the area defined by the border on the + /// outside and the holes on the inside. + public static List Triangulate(List border) + { + List triangles = new List(border.Count); + + // Assuming clockwise wind, take the plane normal of the first three vertices + // to determine intended face direction. + Vector3 faceNormal = MeshMath.CalculateNormal(border); + + // Store remaining vertices in a pseudo-circular linked list. + LinkedList remaining = new LinkedList(border); + + + // Initialize the three vertices to check for ears. + LinkedListNode prev = remaining.Last; + LinkedListNode current = remaining.First; + LinkedListNode next = current.Next; + + bool noMoreEars = true; + while (remaining.Count > 2) + { + if (Math3d.IsConvex(current.Value.loc, prev.Value.loc, next.Value.loc, faceNormal)) + { + bool cut = true; + // Check that none of the remaining vertices are inside. + LinkedListNode check = next.CircularNext(); + while (check != prev) + { + // Because of hole patching, the same vertex might be in multiple places in the list. + // We can safely skip repetitions of the three points. + if (check.Value.loc == prev.Value.loc || + check.Value.loc == current.Value.loc || + check.Value.loc == next.Value.loc) + { + check = check.CircularNext(); + continue; + } + if (Math3d.TriangleContainsPoint(prev.Value.loc, current.Value.loc, next.Value.loc, check.Value.loc)) + { + cut = false; + break; + } + check = check.CircularNext(); + } + + if (cut) + { + noMoreEars = false; + triangles.Add(new Triangle(prev.Value.id, current.Value.id, next.Value.id)); + remaining.Remove(current); + current = next; + next = next.CircularNext(); + + // Skip update logic which was handled above. + continue; + } + } + + // Update node pointers. + prev = current; + current = next; + next = next.CircularNext(); + + // If looped around and no ear was cut, fall back to simple. convex implementation when ear-clipping fails. + // NOTE: This should never happen provided we never have non-coplanar-faces, but we're just playing safe. + if (current == remaining.First) + { + if (noMoreEars) + { + // Fall back to simple. convex implementation when ear-clipping fails. + triangles = new List(border.Count); + for (int i = 2; i < border.Count; i++) + { + triangles.Add(new Triangle( + border[0].id, + border[i - 1].id, + border[i].id)); + } + return triangles; + } + else + { + noMoreEars = true; + } + } + } + return triangles; + } + + public static bool RemoveHoleAtVertex( + LinkedList border, LinkedList hole, Vertex bestCandidate, + Vector3 faceNormal, Vector3? origin = null, Vector3? axis = null) + { + // Initialize defaults for optional parameters. + LinkedListNode bestNode = hole.Find(bestCandidate); + Vector3 bestPos = bestCandidate.loc; + if (origin == null || axis == null) + { + origin = bestPos; + // Take as axis the normal of the vertex to minimize chance of occlusion. + Vector3 prevPos = bestNode.Previous.Value.loc; + Vector3 nextPos = bestNode.Next.Value.loc; + axis = Vector3.Lerp(prevPos - bestPos, nextPos - bestPos, 0.5f); + // If the vertex on the hole is concave, it's concave wrt the polygon and the axis + // will be pointing "inward", away from the border. Flip it. + if (!Math3d.IsConvex( + bestPos, + prevPos, + nextPos, + faceNormal)) + { + axis *= -1; + } + } + + // Find first visible point on arbitrary axis from chosen point in hole. + // This is probably by far the most computationally intense part of the algorithm. + LinkedListNode start = border.First; + LinkedListNode end = start.CircularNext(); + LinkedListNode bestOnBorder = null; + float bestMagnitude = float.MaxValue; + Vector3 intersectionPos = Vector3.zero; + do + { + // Find intersection projection on axis + Vector3 a, b; + Vector3 startVec = start.Value.loc; + Vector3 edge = end.Value.loc - startVec; + bool intersecting = + Math3d.ClosestPointsOnTwoLines(out a, out b, startVec, edge, bestPos, axis.Value) && + a == b; + + // Record whether intersection occured in the "forward" direction on the ray cast from + // bestOnHole. + bool forward = Vector3.Dot(b - bestPos, axis.Value) > 0; + + if (intersecting && forward) + { + // The two points are assumed to be the same but b is technically the one that should be + // used. + float coordVal = Vector3.Project(b - origin.Value, axis.Value).magnitude; + if (coordVal < bestMagnitude) + { + intersectionPos = b; + bestMagnitude = coordVal; + // Choose as "best" the vertex that is futher on the axis + // (to assure no visibility blockage wrt hole since vertex in hole + // was chosen with maximal on-axis projection magnitude) + if (Vector3.Project(start.Value.loc - origin.Value, axis.Value).magnitude > + Vector3.Project(end.Value.loc - origin.Value, axis.Value).magnitude) + { + bestOnBorder = start; + } + else + { + bestOnBorder = end; + } + } + } + + // Update to next pair. + start = end; + end = end.CircularNext(); + } while (start != border.First); + + if (bestOnBorder == null) + { + return false; + } + + // Finally, confirm visibility from chosen best vertices by checking other reflex vertices on + // the border are not contained + LinkedListNode onBorder = bestOnBorder.CircularNext(); + LinkedListNode occludingBestOnBorder = null; + double bestAngle = Math.PI * 2; + while (onBorder != bestOnBorder) + { + bool convex = Math3d.IsConvex( + onBorder.Value.loc, + onBorder.CircularPrevious().Value.loc, + onBorder.CircularNext().Value.loc, + faceNormal); + if (!convex && + Math3d.TriangleContainsPoint( + bestPos, intersectionPos, + bestOnBorder.Value.loc, + onBorder.Value.loc)) + { + // Find angle between intersection line and occluding vertex line. + Vector3 intersectionVec = intersectionPos - bestPos; + Vector3 occludingVertexVec = onBorder.Value.loc - bestPos; + float angle = Vector3.Dot(intersectionVec, occludingVertexVec); + if (angle < bestAngle) + { + occludingBestOnBorder = onBorder; + bestAngle = angle; + } + } + + onBorder = onBorder.CircularNext(); + } + + // Update bestOnBorder with the actual best if occluding vertex was found. + if (occludingBestOnBorder != null) + { + bestOnBorder = occludingBestOnBorder; + } + + // Add hole to border by cutting the shape. + LinkedListNode terminalOnBorder = bestOnBorder.CircularNext(); + LinkedListNode onHole = bestNode; + do + { + // Holes are wound counter-clockwise so adding them in order is appropriate to preserve + // overall polygon clockwise winding. + border.AddBefore(terminalOnBorder, onHole.Value); + onHole = onHole.CircularNext(); + } while (onHole != bestNode); + // Two additional vertices are added to complete loop. + border.AddBefore(terminalOnBorder, bestCandidate); + border.AddBefore(terminalOnBorder, bestOnBorder.Value); + + return true; + } + + public static bool RemoveHoles( + LinkedList border, List> holes, Vector3 faceNormal) + { + if (holes == null || holes.Count == 0) + { + return false; + } + + // Pick an arbitrary vertex as origin and edge as direction + Vector3 origin = border.First.Value.loc; + Vector3 axis = border.First.Next.Value.loc - origin; + + List holeInfos = new List(); + + // Record useful meta about each hole. + foreach (List hole in holes) + { + // Create a list of hole vertices that will later be connected to main list. + HoleInfo holeInfo; + holeInfo.vertList = new LinkedList(hole); + LinkedListNode holeCurrent = holeInfo.vertList.First; + holeInfo.bestCandidate = null; + holeInfo.bestMagnitude = float.MinValue; + + // Find maximal offset on the previously chosen arbitrary axis. + while (holeCurrent != null) + { + float coordVal = Vector3.Project(holeCurrent.Value.loc - origin, axis).magnitude; + if (coordVal > holeInfo.bestMagnitude) + { + holeInfo.bestMagnitude = coordVal; + holeInfo.bestCandidate = holeCurrent; + } + holeCurrent = holeCurrent.Next; + } + + holeInfo.bestPos = holeInfo.bestCandidate.Value.loc; + holeInfos.Add(holeInfo); + } + + // Order holes by largest magnitude in arbitrary axis. + holeInfos = holeInfos.OrderByDescending(h => h.bestMagnitude).ToList(); + + foreach (HoleInfo hole in holeInfos) + { + RemoveHoleAtVertex(border, hole.vertList, hole.bestCandidate.Value, faceNormal, origin, + axis); + } + return true; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs.meta new file mode 100644 index 0000000..8e40f1a --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/FaceTriangulator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2c7642cad774f2c906ea8d30aa7279a +timeCreated: 1721327907 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs new file mode 100644 index 0000000..0da25e9 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs @@ -0,0 +1,478 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +using com.google.apps.peltzer.client.model.core; + +namespace com.google.apps.peltzer.client.model.util +{ + /// + /// Some 3d math utilities. + /// Adapted from: http://wiki.unity3d.com/index.php/3d_Math_functions . + /// + public class Math3d + { + + public const float MERGE_DISTANCE = 0.008f; + public const float EPSILON = 0.0001f; + + /// + /// Resize a collection of vertices by using a scale vector. + /// + /// Original vertices. + /// Scale vector. + /// An IEnumerable to enumerate through the scaled vertex collection. + public static IEnumerable ScaleVertices(IEnumerable vertices, Vector3 scale) + { + return vertices.Select(v => new Vertex(v.id, Vector3.Scale(v.loc, scale))); + } + + /// + /// Convert a plane defined by 3 points to a plane defined by a vector and a point. + /// The plane point is the middle of the triangle defined by the 3 points. + /// + /// Normal of the output plane. + /// A point on the output plane. + /// A point on the plane. + /// A point on the plane. + /// A point on the plane. + public static void PlaneFrom3Points(out Vector3 planeNormal, out Vector3 planePoint, + Vector3 pointA, Vector3 pointB, Vector3 pointC) + { + + planeNormal = Vector3.zero; + planePoint = Vector3.zero; + + //Make two vectors from the 3 input points, originating from point A + Vector3 AB = pointB - pointA; + Vector3 AC = pointC - pointA; + + //Calculate the normal + planeNormal = Vector3.Normalize(Vector3.Cross(AB, AC)); + + //Get the points in the middle AB and AC + Vector3 middleAB = pointA + (AB / 2.0f); + Vector3 middleAC = pointA + (AC / 2.0f); + + //Get vectors from the middle of AB and AC to the point which is not on that line. + Vector3 middleABtoC = pointC - middleAB; + Vector3 middleACtoB = pointB - middleAC; + + //Calculate the intersection between the two lines. This will be the center + //of the triangle defined by the 3 points. + //We could use LineLineIntersection instead of ClosestPointsOnTwoLines but due to rounding errors + //this sometimes doesn't work. + Vector3 temp; + ClosestPointsOnTwoLines(out planePoint, out temp, middleAB, middleABtoC, middleAC, middleACtoB); + } + + /// + /// Two non-parallel lines which may or may not touch each other have a point on each line which are closest + /// to each other. This function finds those two points. If the lines are not parallel, the function + /// outputs true, otherwise false. + /// + /// Closest point on first line. + /// Closest point on second line. + /// Point on first line. + /// Direction of first line. + /// Point on second line. + /// Direction of second line. + /// True if lines are parallel. + public static bool ClosestPointsOnTwoLines(out Vector3 closestPointLine1, out Vector3 closestPointLine2, + Vector3 linePoint1, Vector3 lineVec1, Vector3 linePoint2, Vector3 lineVec2) + { + + closestPointLine1 = Vector3.zero; + closestPointLine2 = Vector3.zero; + + float a = Vector3.Dot(lineVec1, lineVec1); + float b = Vector3.Dot(lineVec1, lineVec2); + float e = Vector3.Dot(lineVec2, lineVec2); + + float d = a * e - b * b; + + //lines are not parallel + if (d != 0.0f) + { + + Vector3 r = linePoint1 - linePoint2; + float c = Vector3.Dot(lineVec1, r); + float f = Vector3.Dot(lineVec2, r); + + float s = (b * f - c * e) / d; + float t = (a * f - c * b) / d; + + closestPointLine1 = linePoint1 + lineVec1 * s; + closestPointLine2 = linePoint2 + lineVec2 * t; + + return true; + } + else + { + return false; + } + } + + /// + /// Returns a point which is a projection from a point to a plane. + /// + /// Plane's normal. + /// Point on plane. + /// The point to project. + /// The projection. + public static Vector3 ProjectPointOnPlane(Vector3 planeNormal, Vector3 planePoint, Vector3 point) + { + //First calculate the distance from the point to the plane: + float distance = SignedDistancePlanePoint(planeNormal, planePoint, point); + + //Reverse the sign of the distance + distance *= -1; + + //Get a translation vector + Vector3 translationVector = planeNormal.normalized * distance; + + //Translate the point to form a projection + return point + translationVector; + } + + /// + /// Get the shortest distance between a point and a plane. The output is signed so it holds information + /// as to which side of the plane normal the point is. + /// + /// Plane's normal. + /// Point on plane. + /// The point. + /// The (signed) distance. + public static float SignedDistancePlanePoint(Vector3 planeNormal, Vector3 planePoint, Vector3 point) + { + return Vector3.Dot(point - planePoint, planeNormal.normalized); + } + + /// + /// Check if a given polygon's vertex is a convex vertex, i.e. forms an acute + /// angle on the side bounding the area. + /// + /// The vertex to check. + /// The previous vertex in a clockwise wind. + /// The next vertex in a clockwise wind. + /// The normal vector of the polygon. + /// True if vertex is convex, false if it's reflex. + public static bool IsConvex(Vector3 check, Vector3 prev, Vector3 next, Vector3 faceNormal) + { + return Vector3.Dot(MeshMath.CalculateNormal(prev, check, next), faceNormal) > 0; + } + + /// + /// Simple check for arbitrary triangle in 3D being instersected by Ray. + /// The triangle must be given with the vertices in clockwise order. + /// + /// The ray to cast at the triangle. + /// The max distance for the ray. Minimum is assumed 0 + /// A vertex of the triangle. + /// A vertex of the triangle. + /// A vertex of the triangle. + /// The triangle's normal. + /// True if point is contained (and coplanar), false otherwise. + public static bool RayIntersectsTriangle(Ray ray, Vector3 a, Vector3 b, Vector3 c, Vector3 normal) + { + float distance; + if (!new Plane(a, b, c).Raycast(ray, out distance)) + { + return false; + } + // This is the intersection point between the ray and the plane that contains the triangle. + Vector3 point = ray.origin + ray.direction * distance; + // Now we have to check if the point is in the triangle. + // To do that, we check each segment (AB, BC, CA) and verify that the test point is on the same side of + // the line as the remaining vertex. Since we know the triangle's normal, we can speed up this calculation + // by doing the math directly inline instead of relying on SameSide(), etc: + return Vector3.Dot(MeshMath.CalculateNormal(a, b, point), normal) >= 0 && + Vector3.Dot(MeshMath.CalculateNormal(b, c, point), normal) >= 0 && + Vector3.Dot(MeshMath.CalculateNormal(c, a, point), normal) >= 0; + } + + /// + /// Simple check for arbitrary triangle in 3D containing an arbitrary point in 3D. + /// Uses barycentric coordinated to check that v >= 0, w >=0, and v + w <= 1 + /// + /// A vertex of the triangle. + /// A vertex of the triangle. + /// A vertex of the triangle. + /// A vertex to check. + /// True if point is contained (and coplanar), false otherwise. + public static bool TriangleContainsPoint(Vector3 a, Vector3 b, Vector3 c, Vector3 check) + { + Vector3 barycentricCoords = Bary(check, a, b, c); + return (barycentricCoords.x >= 0.0f + && barycentricCoords.y >= 0.0f + && (barycentricCoords.x + barycentricCoords.y) <= 1.0f); + } + + //Barycentric coordinate algorithm from Real Time Collision Detection + //Barycentric coordinates paramaterize space - in this space with respect to three points of a triangle. + //The u, v, and w coordinates allow you to calculate a point's position on a plane using the three points of + //a triangle ABC on that plane P = uA + vB + wC, and have the property that u + v + w = 1. Additionally, if the + //barycentric coordinates are such that 0 <= u, v, w <= 1 it implies that the point lies within the triangle. + //This can also be presented as v >= 0, w >= 0. and v + w <= 1. + public static Vector3 Bary(Vector3 point, Vector3 a, Vector3 b, Vector3 c) + { + Vector3 v0 = b - a; + Vector3 v1 = c - a; + Vector3 v2 = point - a; + float d00 = Vector3.Dot(v0, v0); + float d01 = Vector3.Dot(v0, v1); + float d11 = Vector3.Dot(v1, v1); + float d20 = Vector3.Dot(v2, v0); + float d21 = Vector3.Dot(v2, v1); + float denom = d00 * d11 - d01 * d01; + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + return new Vector3(v, w, 1.0f - v - w); + } + + /// + /// Check if a point is inside the border of a convex polygon (point must be coplanar with the + /// polygon already). + /// + public static bool IsInside(List poly, Vector3 point) + { + // This returns incorrect results for very small edges due to floating point precision. Multiplying by a big + // number fixes this (or at least makes the degerate case much harder to hit) + Vector3 p2 = point * 10000; + Vector3 baseVertex = poly[0] * 10000; + for (int i = 1; i < poly.Count - 1; i++) + { + Vector3 a = poly[i] * 10000; + Vector3 b = poly[(i + 1)] * 10000; + + // If point is in the triangle, return early bc it is therefore also in the polygon. + if (TriangleContainsPoint(baseVertex, a, b, p2)) + { + return true; + } + } + return false; + } + + /// + /// Check if three ordered points are colinear. + /// + public static bool AreColinear(Vector3 a, Vector3 b, Vector3 c) + { + return (b - a).normalized == (c - a).normalized; + } + + /// + /// Whether one set of bounds is contained by another. + /// + /// The presumed outer bounds. + /// The presumed inner bounds. + /// Whether the inner bounds are entirely contained by the outer bounds. + public static bool ContainsBounds(Bounds outer, Bounds inner) + { + return outer.Contains(inner.min) && outer.Contains(inner.max); + } + + // Returns the centroid of a group of vectors. + public static Vector3 FindCentroid(IEnumerable vectors) + { + Vector3 tally = Vector3.zero; + foreach (Vector3 vec in vectors) + { + tally += vec; + } + return tally / vectors.Count(); + } + + // Returns the centroid of a list of vectors. + public static Vector3 FindCentroid(List vectors) + { + Vector3 tally = Vector3.zero; + for (int i = 0; i < vectors.Count; i++) + { + tally += vectors[i]; + } + return tally / vectors.Count(); + } + + public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation) + { + Vector3 dir = point - pivot; + return rotation * dir + pivot; + } + + // Find the most common rotation of a group of rotations. + // Ties are broken deterministically by precedence in the passed collection. + // This method could probably be smarter. + public static Quaternion MostCommonRotation(IEnumerable rotations) + { + Dictionary rotationCounts = new Dictionary(); + int highestCount = 0; + Quaternion mostCommonRotation = Quaternion.identity; + + foreach (Quaternion rotation in rotations) + { + if (rotationCounts.ContainsKey(rotation)) + { + rotationCounts[rotation] = rotationCounts[rotation] + 1; + } + else + { + rotationCounts.Add(rotation, 1); + } + + if (rotationCounts[rotation] > highestCount) + { + highestCount = rotationCounts[rotation]; + mostCommonRotation = rotation; + } + } + + return mostCommonRotation; + } + + /// + /// Given a position and a list of points finds which point is nearest to position. + /// + /// The position being compared to the points. + /// The list of possible nearest points. + /// The nearest point. + public static Vector3 NearestPoint(Vector3 position, List points) + { + float nearestDistance = Mathf.Infinity; + Vector3 nearestPoint = new Vector3(); + + foreach (Vector3 point in points) + { + float distance = Vector3.SqrMagnitude(point - position); + + if (distance < nearestDistance) + { + nearestDistance = distance; + nearestPoint = point; + } + } + + return nearestPoint; + } + + /// + /// Takes a position and projects it onto a line. + /// + /// The point to project onto the line. + /// The line represented as a vector being projected onto. + /// A reference point on the line. + /// The toSnap position projected onto the line. + public static Vector3 ProjectPointOntoLine(Vector3 toSnap, Vector3 line, Vector3 origin) + { + // Find the distance from the origin to the toSnap position. + float projectedDistance = + Mathf.Cos(Vector3.Angle(toSnap - origin, line) * Mathf.Deg2Rad) * Vector3.Distance(origin, toSnap); + + return origin + (line.normalized * projectedDistance); + } + + /// + /// Compares two vectors for equality. + /// + /// The first vector. + /// The second vector. + /// The floating point error. + /// True if the vectors are equal. + public static bool CompareVectors(Vector3 v1, Vector3 v2, float epsilon) + { + if (!(Mathf.Abs(v1.x - v2.x) < epsilon)) + return false; + + if (!(Mathf.Abs(v1.y - v2.y) < epsilon)) + return false; + + if (!(Mathf.Abs(v1.z - v2.z) < epsilon)) + return false; + + return true; + } + + /// + /// Test if a quaternion is valid for rotation (ie, has a magnitude of 1). + /// + /// The quaternion to test + /// The acceptable amount or error. + /// True if the Quaternion is a valid rotation quaternion + public static bool QuaternionIsValidRotation(Quaternion testQuaternion, float epsilon = EPSILON) + { + return Mathf.Abs(testQuaternion.x * testQuaternion.x + + testQuaternion.y * testQuaternion.y + + testQuaternion.z * testQuaternion.z + + testQuaternion.w * testQuaternion.w - 1.0f) < epsilon; + } + + /// + /// Normalizes a quaternion. + /// + /// The Quaternion to normalize/ + /// The normalized Quaternion. + public static Quaternion Normalize(Quaternion q) + { + float mag = Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + return new Quaternion(q.x / mag, q.y / mag, q.z / mag, q.w / mag); + } + + public static Vector3 Normalize(Vector3 vec) + { + Vector3 scaledVec = 1000000f * vec; + return scaledVec / scaledVec.magnitude; + } + + /// + /// Compares two quaternions for equality. + /// + /// The first quaternion. + /// The second quaternion. + /// The floating point error. + /// True if the quaternions are equal. + public static bool CompareQuaternions(Quaternion q1, Quaternion q2, float epsilon) + { + Vector3 q1Euler = q1.eulerAngles; + Vector3 q2Euler = q2.eulerAngles; + + if (!(Mathf.Abs(q1Euler.x - q2Euler.x) < epsilon)) + return false; + + if (!(Mathf.Abs(q1Euler.y - q2Euler.y) < epsilon)) + return false; + + if (!(Mathf.Abs(q1Euler.z - q2Euler.z) < epsilon)) + return false; + + return true; + } + + /// + /// Returns a given value on a cubic bezier curve defined by A, B, C and D. + /// + /// WARNING: There seems to be unknown constraints for which this function doesn't work. It does work for the + /// current use cases. + /// + public static float CubicBezierEasing(float A, float B, float C, float D, float t) + { + return A + 3.0f * t * (B - A) + 3.0f * t * t * (C - 2.0f * B + A) + t * t * t * (D - 3.0f * C + 3.0f * B - A); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs.meta new file mode 100644 index 0000000..21b33a0 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Math3d.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7fc92b1e21544e67b311d7392351cf1c +timeCreated: 1721327890 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs new file mode 100644 index 0000000..7394a72 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs @@ -0,0 +1,865 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using com.google.apps.peltzer.client.model.util; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using UnityEngine; + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// Structure for holding a pair of edges and the separation between the edges. + /// + public struct EdgePair + { + internal float separation; + internal EdgeInfo fromEdge; + internal EdgeInfo toEdge; + } + + /// + /// Structure for holding a pair of faces and the separation between the faces. + /// + public struct FacePair + { + internal float separation; + internal float angle; + internal FaceKey fromFaceKey; + internal FaceKey toFaceKey; + internal Vector3 fromFaceModelSpaceCenter; + internal Vector3 toFaceModelSpaceCenter; + } + + /// + /// Structure for holding a vertex and a face and the separation between them. + /// + public struct FaceVertexPair + { + internal float separation; + internal VertexKey vertexKey; + internal FaceKey faceKey; + } + + /// + /// Math associated with meshes, faces and vertices. + /// + public class MeshMath + { + /// + /// Calculates a normal for a clockwise list of coplanar vertices. + /// This code is directly copied beneath to avoid an expensive Select. + /// + /// A clockwise list of Vertex objects. + /// The normal for this list of vertices. + public static Vector3 CalculateNormal(List vertices) + { + if (vertices.Count == 0) return Vector3.zero; + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = vertices.Count; + Vector3 thisPos = vertices[0].loc; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = vertices[next].loc; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + return Math3d.Normalize(normal); + } + + /// + /// Calculates a normal for a clockwise list of coplanar vertices. + /// This code is directly copied from above to avoid an expensive Select. + /// + /// A clockwise list of Vertex objects. + /// The normal for this list of vertices. + public static Vector3 CalculateNormal(List vertices) + { + if (vertices.Count == 0) return Vector3.zero; + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = vertices.Count; + Vector3 thisPos = vertices[0]; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = vertices[next]; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + return Math3d.Normalize(normal); + } + + /// + /// Calculates a normal from a clockwise wound array of vertices, + /// + /// + /// + /// + public static Vector3 CalculateNormal(ReadOnlyCollection vertices, Dictionary verticesById) + { + if (vertices.Count == 0) return Vector3.zero; + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = vertices.Count; + Vector3 thisPos = verticesById[vertices[0]].loc; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = verticesById[vertices[next]].loc; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + return Math3d.Normalize(normal); + } + + /// + /// Calculates a normal for a clockwise list of coplanar vertices. + /// This code is directly copied from above to avoid an expensive Select. + /// + /// A clockwise list of Vertex objects. + /// The normal for this list of vertices. + public static List CalculateNormals(List vertices, List> indices) + { + List outList = new List(); + if (vertices.Count == 0) return outList; + + for (int faceIndex = 0; faceIndex < indices.Count; faceIndex++) + { + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = indices[faceIndex].Count; + Vector3 thisPos = vertices[indices[faceIndex][0]]; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = vertices[indices[faceIndex][next]]; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + outList.Add(Math3d.Normalize(normal)); + } + return outList; + } + + /// + /// Calculates a normal for a clockwise list of coplanar vertices. + /// This code is directly copied from above to avoid an expensive Select. + /// + /// A clockwise list of Vertex objects. + /// The normal for this list of vertices. + public static List CalculateNormals(List vertices, List> indices) + { + List outList = new List(); + if (vertices.Count == 0) return outList; + + for (int faceIndex = 0; faceIndex < indices.Count; faceIndex++) + { + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = indices[faceIndex].Count; + Vector3 thisPos = vertices[indices[faceIndex][0]].loc; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = vertices[indices[faceIndex][next]].loc; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + outList.Add(Math3d.Normalize(normal)); + } + return outList; + } + + /// + /// Calculates a normal for a clockwise list of coplanar vertices. + /// This code is directly copied from above to avoid an expensive Select. + /// + /// A clockwise list of Vertex objects. + /// The normal for this list of vertices. + public static List CalculateNormals(Dictionary vertices, List> indices) + { + List outList = new List(); + if (vertices.Count == 0) return outList; + + for (int faceIndex = 0; faceIndex < indices.Count; faceIndex++) + { + // This uses Newell's method, which is proven to generate correct normal for any polygon. + Vector3 normal = Vector3.zero; + int count = indices[faceIndex].Count; + Vector3 thisPos = vertices[indices[faceIndex][0]].loc; + Vector3 nextPos; + for (int i = 0, next = 1; i < count; i++, next++) + { + // Note: this is cheaper than computing "next % count" at each iteration. + next = (next == count) ? 0 : next; + nextPos = vertices[indices[faceIndex][next]].loc; + normal.x += (thisPos.y - nextPos.y) * (thisPos.z + nextPos.z); + normal.y += (thisPos.z - nextPos.z) * (thisPos.x + nextPos.x); + normal.z += (thisPos.x - nextPos.x) * (thisPos.y + nextPos.y); + thisPos = nextPos; + } + outList.Add(normal.normalized); + } + return outList; + } + + /// + /// Calculates a normal for three vertices given in clockwise order. + /// + /// First vertex. + /// Second vertex. + /// Third vertex. + /// The normal for the given vertices. + public static Vector3 CalculateNormal(Vector3 v1, Vector3 v2, Vector3 v3) + { + // Note: we scale the vectors by 1000 before calculating the cross product because the vectors might be really + // tiny, so the cross product and normalization might run into floating point errors causing the result + // to be zero (bug). Pre-scaling the vectors by 1000 is mathematically equivalent, as we're normalizing + // anyway. + return Vector3.Cross((v1 - v2) * 1000f, (v1 - v3) * 1000f).normalized; + } + + /// + /// Tests whether a point p is on a coplanar convex face. + /// + /// The point to test. + /// The normal to the face (this is a flat face, so one normal) + /// The coplanar vertices of the face. + /// How close we must be to the face. + /// How far we must be from the vertex. + /// True if we are close, false if we're not close. + public static bool IsCloseToFaceInterior(Vector3 point, Vector3 faceNormal, + List faceVertices, float faceClosenessThreshold, float vertexDistanceThreshold) + { + // Don't accept points that are too close to vertices as being 'close to a face'. + foreach (Vector3 vertex in faceVertices) + { + if (Vector3.Distance(vertex, point) < vertexDistanceThreshold) + { + return false; + } + } + + // Short-circuit where we know a point is too far from a face. + if (Vector3.Distance(Vector3.Project(point, faceNormal), Vector3.Project(faceVertices[0], faceNormal)) + > faceClosenessThreshold) + { + return false; + } + + Vector3 prev = faceVertices[faceVertices.Count - 1]; + for (int i = 0; i < faceVertices.Count; i++) + { + Vector3 vertex = faceVertices[i]; + Vector3 edge = vertex - prev; + Vector3 normal = Vector3.Cross(faceNormal, edge); + float min = float.MaxValue; + float max = float.MinValue; + foreach (Vector3 faceVertex in faceVertices) + { + float dot = Vector3.Dot(faceVertex, normal); + if (dot < min) + { + min = dot; + } + if (dot > max) + { + max = dot; + } + } + float pointDot = Vector3.Dot(point, normal); + if (pointDot < min || pointDot > max) + { + return false; + } + prev = vertex; + } + return true; + } + + public static Vector3 CalculateGeometricCenter(List coplanarVertices) + { + List cornerVertices = FindCornerVertices(coplanarVertices); + return cornerVertices.Aggregate(Vector3.zero, (sum, vec) => sum + vec) / cornerVertices.Count; + } + + /// + /// Takes a set of bounds and returns a bounds that encapsulates all of them. + /// + /// The bounds to be encapsulated. + /// A bounds that encapsulates all the passed bounds. + public static Bounds FindEncapsulatingBounds(IEnumerable bounds) + { + // You have to start off with a bounds to encapsulate other bounds together. The first iteration of the for loop + // will encapsulate the first bounds again but it won't change the outcome of encapsulating bounds. + Bounds encapsulatingBounds = bounds.First(); + + foreach (Bounds bound in bounds) + { + encapsulatingBounds.Encapsulate(bound); + } + + return encapsulatingBounds; + } + + /// + /// Finds the edge bisectors in clockwise order around a face. + /// + /// Clockwise vertices representing a face. + /// Clockwise positions of edge bisector points. + public static List CalculateEdgeBisectors(IEnumerable coplanarVertices) + { + List edgeBisectors = new List(); + + // Find all the edge centers by iterating through the vertices which are stored clockwise. + for (int index = 0; index < coplanarVertices.Count(); index++) + { + Vector3 v1 = coplanarVertices.ElementAt(index); + Vector3 v2 = (index + 1 == coplanarVertices.Count()) ? coplanarVertices.ElementAt(0) : + coplanarVertices.ElementAt(index + 1); + + // Add the edge bisectors to verticesAndEdgeBisectors. + edgeBisectors.Add((v1 + v2) / 2.0f); + } + + return edgeBisectors; + } + + /// + /// Takes a list of coplanarVertices representing a face and removes any extraneous colinear vertices along the + /// edges returning only corner vertices. + /// + /// The list of clockwise coplanar vertices representing the face. + /// A list of clockwise coplanar vertices where no 3 vertices are colinear. + public static List FindCornerVertices(List coplanarVertices) + { + // Start by populating the cornerVertices with all coplanarVertices. We will remove colinear ones as we iterate. + int numElements = coplanarVertices.Count; + + if (numElements < 3) + { + // Nothing to do here, return a copy. + return new List(coplanarVertices); + } + + // Iterate through the coplanarVertices checking every Every vertex needs to be the middle vertex once. If the + // colinear check returns true we remove the middle vertex from the set of cornerVertices. + // Given that we are building a list, we need to ensure we preserve its order. + List cornerVertices = new List(numElements); + Vector3 previous = coplanarVertices[numElements - 1]; + Vector3 current = coplanarVertices[0]; + Vector3 next; + for (int i = 0; i < numElements; i++) + { + next = coplanarVertices[(i + 1) % numElements]; + + if (!Math3d.AreColinear(previous, current, next)) + { + cornerVertices.Add(current); + } + + previous = current; + current = next; + } + + return cornerVertices; + } + + /// + /// Finds which edge in a face a position is closest to by checking how far the position is from every edge in + /// the face. + /// + /// The position we want to find the closest edge to. + /// The clockwise vertices representing the face. + /// The two vertexIds that make the closest edge. + public static KeyValuePair FindClosestEdgeInFace(Vector3 position, + IEnumerable coplanarVertices) + { + float closestDistance = Mathf.Infinity; + KeyValuePair closestEdge = new KeyValuePair(); + + // Check each edge. Every vertex in vertexIds should be the first vertex on one edge to iterate through every + // edge we can iterate through the vertices which are stored in Face.vertexId in clockwise order and make this + // vertex the first and the subsequent vertex the second one for the edge. When we hit the end of the list the + // second vertex is the first one in the list. + for (int index = 0; index < coplanarVertices.Count(); index++) + { + // Find the vertices. + Vector3 v1 = coplanarVertices.ElementAt(index); + Vector3 v2 = (index + 1 == coplanarVertices.Count()) ? + coplanarVertices.ElementAt(0) : coplanarVertices.ElementAt(index + 1); + + // Find the distance from the position to the edge. + float currentDistance = DistanceFromEdge(position, v1, v2); + + if (currentDistance < closestDistance) + { + closestDistance = currentDistance; + closestEdge = new KeyValuePair(v1, v2); + } + } + + return closestEdge; + } + + + /// + /// Sorts all pairs of edges by increasing separation, where each edge belongs to a different face represented + /// by a list of clockwise coplanar vertices. + /// + /// The coplanar vertices of the first face. + /// The coplanar vertices of the second face. + /// + /// A sorted list of edgePair structs which contains each edge and the average distance between them. + /// + public static IEnumerable FindClosestEdgePairs(IEnumerable fromFaceVertices, + IEnumerable toFaceVertices) + { + List edgePairs = new List(); + + // Check each edge in fromFaceVertices against each edge in toFaceVertices. + for (int toIndex = 0; toIndex < toFaceVertices.Count(); toIndex++) + { + Vector3 toV1 = toFaceVertices.ElementAt(toIndex); + Vector3 toV2 = toFaceVertices.ElementAt((toIndex + 1) % toFaceVertices.Count()); + + for (int fromIndex = 0; fromIndex < fromFaceVertices.Count(); fromIndex++) + { + Vector3 fromV1 = fromFaceVertices.ElementAt(fromIndex); + Vector3 fromV2 = fromFaceVertices.ElementAt((fromIndex + 1) % fromFaceVertices.Count()); + + float separation; + bool edgesOverlap = CompareEdges(fromV1, fromV2, toV1, toV2, out separation); + + if (edgesOverlap) + { + // Create the EdgePair and add it to the list. + EdgeInfo fromEdge = new EdgeInfo(); + fromEdge.edgeStart = fromV1; + fromEdge.edgeVector = fromV2 - fromV1; + + EdgeInfo toEdge = new EdgeInfo(); + toEdge.edgeStart = toV1; + toEdge.edgeVector = toV2 - toV1; + + EdgePair edgePair = new EdgePair(); + edgePair.separation = separation; + edgePair.fromEdge = fromEdge; + edgePair.toEdge = toEdge; + + edgePairs.Add(edgePair); + } + } + } + + // Return edgePairs sorted in ascending order of separation. + return edgePairs.OrderBy(pair => pair.separation); + } + + /// + /// Finds the pair of edges with the smallest separation, where each edge belongs to a different face + /// represented by a list of clockwise coplanar vertices. + /// + /// The coplanar vertices of the first face. + /// The coplanar vertices of the second face. + /// The closest edge pair. + /// + /// Whether a closest edge pair was found. + /// + public static bool MaybeFindClosestEdgePair(IEnumerable fromFaceVertices, + IEnumerable toFaceVertices, out EdgePair closestEdgePair) + { + closestEdgePair = new EdgePair(); + float closestSeparation = Mathf.Infinity; + + // Check each edge in fromFaceVertices against each edge in toFaceVertices. + for (int toIndex = 0; toIndex < toFaceVertices.Count(); toIndex++) + { + Vector3 toV1 = toFaceVertices.ElementAt(toIndex); + Vector3 toV2 = toFaceVertices.ElementAt((toIndex + 1) % toFaceVertices.Count()); + + for (int fromIndex = 0; fromIndex < fromFaceVertices.Count(); fromIndex++) + { + Vector3 fromV1 = fromFaceVertices.ElementAt(fromIndex); + Vector3 fromV2 = fromFaceVertices.ElementAt((fromIndex + 1) % fromFaceVertices.Count()); + + float separation; + bool edgesOverlap = CompareEdges(fromV1, fromV2, toV1, toV2, out separation); + + if (edgesOverlap && separation < closestSeparation) + { + closestSeparation = separation; + // Create the EdgePair and add it to the list. + EdgeInfo fromEdge = new EdgeInfo(); + fromEdge.edgeStart = fromV1; + fromEdge.edgeVector = fromV2 - fromV1; + + EdgeInfo toEdge = new EdgeInfo(); + toEdge.edgeStart = toV1; + toEdge.edgeVector = toV2 - toV1; + + closestEdgePair.separation = separation; + closestEdgePair.fromEdge = fromEdge; + closestEdgePair.toEdge = toEdge; ; + } + } + } + + // Return edgePairs sorted in ascending order of separation. + return closestSeparation != Mathf.Infinity; + } + + /// + /// Compares two edges and determines how far apart they are. The separation between edges is calculated by + /// finding the average distance from each vertex in the first edge at a right angle to the second edge. We only + /// compare edges if they overlap or if at least one vertex of either edge is inside the other edge. + /// + /// See bug for a diagram. + /// + /// First vertex of an edge a. + /// Second vertex of an edge a. + /// First vertex of an edge b. + /// Second vertex of an edge b. + /// How far apart the edges are. + /// Whether the edges overlapped. + public static bool CompareEdges(Vector3 a1, Vector3 a2, Vector3 b1, Vector3 b2, out float separation) + { + float distanceA2 = DistanceFromEdge(a2, b1, b2); + bool a2InsideB = InsideEdge(a2, b1, b2); + + float distanceA1 = DistanceFromEdge(a1, b1, b2); + bool a1InsideB = InsideEdge(a1, b1, b2); + + // Find the separation which is the sum of the projections of a1 and a2 at 90 degree angles onto edge b. + separation = (distanceA1 + distanceA2) / 2.0f; + + // Check if either a1 or a2 was inside edge b. If they were we already know the edges are comparable. + if (a1InsideB || a2InsideB) + { + return true; + } + + // We already know the separation but we still don't know if the edges are actually comparable because a1 and a2 + // were not inside edge b. But we can still compare the edges and use the separation we already calculated if + // b1 or b2 are inside edge a. + float distanceB1 = DistanceFromEdge(b1, a1, a2); + bool b1InsideEdge = InsideEdge(b1, a1, a2); + float distanceB2 = DistanceFromEdge(b2, a1, a2); + bool b2InsideEdge = InsideEdge(b2, a1, a2); + + return b1InsideEdge || b2InsideEdge; + } + + /// + /// Finds the perpendicular distance from a position/vertex a to an edge b represented by two vertices b1 and b2. + /// Determines whether a is inside edge b. A vertex is inside an edge if the triangle formed by all three vertices + /// doesn't have obtuse angles at the corners defined by the edge's vertices. + /// + /// The position we are trying to find the distance to the edge for. + /// First vertex for the edge. + /// Second vertex for the edge. + /// Perpendicular distance from the position to the edge. + /// Whether a is inside edge b. + public static float DistanceFromEdge(Vector3 a, Vector3 b1, Vector3 b2) + { + // Find the angles of the corners of b2 in triangle ab2b1. + float thetaAB2B1 = Vector3.Angle(a - b2, b1 - b2) * Mathf.Deg2Rad; + + // Calculate the distance between a and edge b such that the projection of a onto edge b forms a right angle + // with edge b. + return Vector3.Distance(a, b2) * Mathf.Sin(Mathf.Min(Mathf.PI - thetaAB2B1, thetaAB2B1)); ; + } + + /// + /// Checks if a given vertex a is inside an edge b. A vertex is inside an edge if the triangle formed by all three + /// vertices doesn't have obtuse angles at the corners defined by the edge's vertices. + /// + /// The position we are trying to check is inside an edge. + /// First vertex for the edge. + /// Second vertex for the edge. + /// Whether a is inside edge b. + public static bool InsideEdge(Vector3 a, Vector3 b1, Vector3 b2) + { + // Find the angles of the corners of b1 and b2 in triangle ab2b1. + float thetaAB1B2 = Vector3.Angle(a - b1, b2 - b1) * Mathf.Deg2Rad; + float thetaAB2B1 = Vector3.Angle(a - b2, b1 - b2) * Mathf.Deg2Rad; + + // Check that b1 and b2 aren't obtuse angles in triangle a2b2b1. + return thetaAB1B2 < (Mathf.PI / 2.0f) && thetaAB2B1 < (Mathf.PI / 2.0f); + } + + /// + /// Finds which edge from a set of coplanarVertices is closest to a given edge. + /// + /// A set of vertices representing edges in clockwise order. + /// The edge being compared to. + /// A vector representation of the closestEdge. + public static Vector3 ClosestEdgeToEdge(IEnumerable coplanarVertices, EdgeInfo toEdge) + { + // We just want the edge endpoints. + Vector3 eV1 = toEdge.edgeStart; + Vector3 eV2 = eV1 + toEdge.edgeVector; + + float closestDistance = Mathf.Infinity; + Vector3 closestEdge = Vector3.zero; + + // Check each edge. To iterate through the edges we can iterate through the clockwise vertices in coplanar + // vertices allowing each vertex to be the first vertex in an edge. + for (int index = 0; index < coplanarVertices.Count(); index++) + { + // Find the vertices. + Vector3 v1 = coplanarVertices.ElementAt(index); + Vector3 v2 = coplanarVertices.ElementAt((index + 1) % coplanarVertices.Count()); + + Vector3 edge = v2 - v1; + + float d1 = DistanceFromEdge(v1, eV1, eV2); + float d2 = DistanceFromEdge(v2, eV1, eV2); + + float currentDistance = (d1 + d2) / 2.0f; + + if (currentDistance < closestDistance) + { + closestDistance = currentDistance; + closestEdge = edge; + } + } + + return closestEdge; + } + + /// + /// Checks to see if N vertices from a set of vertices are on a given mesh. + /// + /// The id for the mesh. + /// The set of vertices. + /// The minimum number of vertices on the same mesh required to return true. + /// True if N vertices from the set are on the mesh. + public static bool MultipleNearbyVerticesOnSameMesh(int meshId, IEnumerable vertexKeys, + int minSetSize) + { + ushort vertexCountOnSameMesh = 0; + + foreach (VertexKey vertexKey in vertexKeys) + { + if (meshId == vertexKey.meshId) + { + vertexCountOnSameMesh++; + if (vertexCountOnSameMesh >= minSetSize) + return true; + } + } + + return false; + } + + /// + /// Checks to see if N faces from a set of faces are on the same mesh. + /// + /// The faces, as a List for efficiency. + /// The minimum number of faces on the same mesh required to return true. + /// The mesh with the most nearby faces on it. + /// True if N faces from the set are on a mesh. + public static bool TryFindingNearestMeshGivenNearbyFaces(List> faces, int minSetSize, + out int nearestMeshId) + { + Dictionary faceCountByMeshId = new Dictionary(); + int currentMaxCount = 0; + nearestMeshId = -1; + + foreach (DistancePair faceKeyPair in faces) + { + int meshId = faceKeyPair.value.meshId; + + // Set the current face count for this mesh to 1, or increment it. + int currentFaceCount = 0; + faceCountByMeshId.TryGetValue(meshId, out currentFaceCount); + currentFaceCount++; + faceCountByMeshId[meshId] = currentFaceCount; + + // Update the current max count if the current mesh has more references. + if (currentFaceCount > currentMaxCount) + { + currentMaxCount = currentFaceCount; + nearestMeshId = meshId; + } + } + + return currentMaxCount >= minSetSize; + } + + + /// + /// Compares two faces. Finds the physical separation between the faces as the average distance of each vertex + /// in the fromFace to the plane created by the toFace. Also finds the angle between the normals of the face + /// which defines flushness. Two faces are defined as flush if they have an angle of 180 degrees between their + /// normals. + /// + /// The key of the face we are comparing the difference from, to the other face. + /// The normal of the fromFace. + /// The coplanar vertices that make up the fromFace. + /// The key of the face we are comparing the difference to, from the other face. + /// The plane defined by the toFace. + /// + /// A FairPair containing both faces, their separation and their angle from being flush. + /// + /// + /// Whether the faces were comparable. Faces are only comparable if Plane.Raycast() returns true. This happens + /// when the face normals have an angle > 90f. Or when they face each other. + /// + public static bool CompareFaces(FaceKey fromFaceKey, Vector3 fromFaceNormal, + List fromFaceVertices, FaceKey toFaceKey, FaceInfo toFaceInfo, out FacePair facePair) + { + Vector3 fromFaceCenter = MeshMath.CalculateGeometricCenter(fromFaceVertices); + + Ray normalRay = new Ray(fromFaceCenter, fromFaceNormal); + // The length of the Raycast before it enters the plane. + float intersectionLength; + + // The angle is calculated as the degrees from flush the normals are. Essentially the number of degrees the faces + // would have to be rotated to be flush. Faces are flush if there is 180 degrees between their normals. + facePair.angle = Vector3.Angle(fromFaceNormal, toFaceInfo.plane.normal); + + bool normalsPointTowardEachOther = toFaceInfo.plane.Raycast(normalRay, out intersectionLength); + float estimatedFaceSeparation = Vector3.Distance(fromFaceCenter, toFaceInfo.baryCenter); + + facePair.separation = (intersectionLength + estimatedFaceSeparation) / 2.0f; + facePair.fromFaceKey = fromFaceKey; + facePair.toFaceKey = toFaceKey; + facePair.toFaceModelSpaceCenter = toFaceInfo.baryCenter; + facePair.fromFaceModelSpaceCenter = fromFaceCenter; + + return normalsPointTowardEachOther; + } + + /// + /// Finds the average distance of a set of vertices from a plane. We use the signed difference from point to + /// plane so that faces that intersect each other are considered closest. + /// + /// The plane the vertices are separated from. + /// The vertices whose distance from the plane is being calculated. + /// The average distance for all vertices. + public static float AverageDistanceFromPlane(Plane plane, IEnumerable vertices) + { + // Avoid a divide by zero error. + if (vertices.Count() == 0) + return 0; + + float sum = 0; + foreach (Vector3 vertex in vertices) + { + // We want the distance from point to plane so we take the inverse. + sum += -plane.GetDistanceToPoint(vertex); + } + + return (sum / vertices.Count()); + } + + + /// + /// Compares a face and vertex. Calculates the distance from the faces center to the vertex. + /// + /// The key of the face being compared. + /// The center of the face being compared. + /// The key of the vertex being compared. + /// The position of the vertex being compared. + /// A FaceVertexPair with the face, the vertex and their separation. + public static FaceVertexPair CompareFaceAndVertex(FaceKey faceKey, Vector3 faceCenter, VertexKey vertexKey, + Vector3 vertex) + { + FaceVertexPair faceVertexPair = new FaceVertexPair(); + // Find the separation between the two as the distance from the vertex to the faces center. + faceVertexPair.separation = Vector3.Distance(vertex, faceCenter); + faceVertexPair.vertexKey = vertexKey; + faceVertexPair.faceKey = faceKey; + + return faceVertexPair; + } + + /// + /// Finds which edge in a face most represents the other edges. It does this by choosing an edge that is + /// perpendicular to the greatest number of other edges. + /// + /// The coplanar vertices representing the face. + /// The most representative edge. + public static EdgeInfo FindMostRepresentativeEdge(List coplanarFaceVertices) + { + // Start by cleaning out any colinear vertices (subedges). + IEnumerable cornerVertices = FindCornerVertices(coplanarFaceVertices); + // Create a dictionary that will hold an edge and the number of edges perpendicular to this edge. + Dictionary similarEdges = new Dictionary(); + + // Iterate through every edge. + for (int index = 0; index < cornerVertices.Count(); index++) + { + // Find the vertices. + Vector3 v1 = cornerVertices.ElementAt(index); + Vector3 v2 = cornerVertices.ElementAt((index + 1) % cornerVertices.Count()); ; + + // Find the current edge. + Vector3 currentEdge = (v2 - v1); + + bool foundSimilarEdge = false; + + // Check if the current edge is perpendicular to one of the edges in similarEdges. Two vectors are + // perpendicular if their dot product is 0. + foreach (KeyValuePair edges in similarEdges) + { + if (Mathf.Abs(Vector3.Dot(edges.Key.edgeVector, currentEdge.normalized) - 0) < Math3d.EPSILON) + { + // If they are similar, update the edgeCount and break to avoid iterating through the dictionary. + similarEdges[edges.Key] = similarEdges[edges.Key] + 1; + foundSimilarEdge = true; + break; + } + } + + // If we there was no similar edge, create a new edge entry in similarEdges. + if (!foundSimilarEdge) + { + EdgeInfo currentEdgeInfo = new EdgeInfo(); + currentEdgeInfo.edgeVector = currentEdge; + currentEdgeInfo.edgeStart = v1; + similarEdges[currentEdgeInfo] = 1; + } + } + + return similarEdges.OrderByDescending(pair => pair.Value).First().Key; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs.meta new file mode 100644 index 0000000..98af4ec --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/MeshMath.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 74fd8542ada44a8aa82481c26d8c1076 +timeCreated: 1721327668 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs new file mode 100644 index 0000000..098928c --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs @@ -0,0 +1,356 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using System.Collections.Generic; +using System; +using com.google.apps.peltzer.client.model.core; + +namespace com.google.apps.peltzer.client.model.util +{ + + /// + /// Spatial index for objects based on their Bounds. + /// + public class OctreeImpl : CollisionSystem + { + // Number of items stored in a Node before we decide to split that node. + private static readonly int SPLIT_SIZE = 10; + // Max depth of the tree. Since the tree divides the axis by two at + // each level, the size of the smallest node is initial_size/2^MAX_DEPTH. + // 10 is a reasonable default. Can be an Octree param if needed. + private static readonly int MAX_DEPTH = 10; + private readonly Bounds bounds; + private readonly OTNode root; + private readonly Dictionary itemBounds = + new Dictionary(); + private readonly Dictionary itemNode = + new Dictionary(); + + /// + /// Create an empty Octree. + /// + /// Bounds of the Octree. + /// All items added to this index must be within these bounds. + /// + public OctreeImpl(Bounds bounds) + { + this.bounds = bounds; + root = new OTNode(this, bounds, 0 /* Depth */); + } + + /// + /// Add an item to the Octree. + /// + /// The item to add + /// The item's initial bounds. + /// + /// Thrown when bounds is not contained by the Octree's bounds. + public void Add(T item, Bounds bounds) + { + AssertOrThrow.False(itemBounds.ContainsKey(item), + "Cannot re-add item using the same key. Use Update to change an item's bounds."); + itemBounds[item] = bounds; + OTNode node = root.Add(item, bounds); + itemNode[item] = node; + } + + /// + /// Update the bounds of an item. The item must already exist + /// in the index. + /// + /// The item to update. + /// The item's updated bounds. + /// + /// Thrown when the item isn't in the tree. + public void UpdateItemBounds(T item, Bounds bounds) + { + OTNode oldNode = itemNode[item]; + oldNode.Remove(item); + itemBounds[item] = bounds; + itemNode[item] = root.Add(item, bounds); + } + + /// + /// Remove an item from the index. + /// + /// Item to remove. + /// + /// Thrown when the item isn't in the tree. + public void Remove(T item) + { + AssertOrThrow.True(itemNode.ContainsKey(item), + "Item is specified for removal but is not in the tree."); + OTNode oldNode = itemNode[item]; + oldNode.Remove(item); + itemNode.Remove(item); + itemBounds.Remove(item); + } + + /// + /// Find items contained entirely within the given bounds. + /// This method will create a set when the number of items + /// is greater than zero. + /// + /// Containing bounds. + /// Set of items found. Null when this + /// method returns false. + /// Maximum number of items to find. + /// true if any items are found. + public bool ContainedBy(Bounds bounds, out HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS) + { + items = null; + return root.ContainedBy(bounds, ref items, limit); + } + + /// + /// Find items that intersect the given bounds. + /// This method will create a Set when the number of items + /// is greater than zero. + /// + /// Intersecting bounds. + /// Set of items found. Null when this + /// method returns false. + /// Maximum number of items to find. + /// true if any items are found. + public bool IntersectedBy(Bounds bounds, out HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS) + { + items = null; + return root.IntersectedBy(bounds, ref items, limit); + } + + /// + /// True if the given item is in the tree. + /// + public bool HasItem(T item) + { + return itemNode.ContainsKey(item); + } + + // Return the bounds specified when the item was inserted or updated. + public Bounds BoundsForItem(T item) + { + return itemBounds[item]; + } + + private void UpdateItemNode(T item, OTNode node) + { + itemNode[item] = node; + } + + /// + /// Checks whether the supplied Bounds intersects anything in the system, and fills the supplied preallocated Hashset + /// with intersected items. Returns true if there were any intersections. + /// + /// + /// + /// + /// + public bool IntersectedByPreallocated(Bounds bounds, ref HashSet items, + int limit = SpatialIndex.MAX_INTERSECT_RESULTS) + { + return true; + } + + // Public for testing. + public static Bounds SubBounds(Bounds parent, int idx) + { + Vector3 childSize = parent.size / 2.0f; + Vector3 extents = parent.extents / 2.0f; + Vector3 center = parent.center; + Vector3 childCenter = new Vector3( + (idx & 1) > 0 ? center.x - extents.x : center.x + extents.x, + (idx & 2) > 0 ? center.y - extents.y : center.y + extents.y, + (idx & 4) > 0 ? center.z - extents.z : center.z + extents.z); + return new Bounds(childCenter, childSize); + } + + // VisibleForTesting + public OTNode GetRootNode() + { + return root; + } + + // VisibleForTesting + public Bounds GetBounds() + { + return bounds; + } + + + // Tree structure to contain items. + public class OTNode + { + private readonly int depth; + private readonly OctreeImpl tree; + private readonly Bounds bounds; + private HashSet items = new HashSet(); + private OTNode[] childNodes = null; + + internal OTNode(OctreeImpl tree, Bounds bounds, int depth) + { + this.tree = tree; + this.bounds = bounds; + this.depth = depth; + } + + internal OTNode Add(T item, Bounds itemBounds) + { + AssertOrThrow.True(Math3d.ContainsBounds(bounds, itemBounds), + "Item has bounds outside of tree bounds"); + if (childNodes == null) + { + if (items.Count >= SPLIT_SIZE && depth < MAX_DEPTH) + { + SplitNode(); + // Recursively re-call this function, post-split + return Add(item, itemBounds); + } + else + { + items.Add(item); + return this; + } + } + else + { + for (int i = 0; i < 8; i++) + { + if (Math3d.ContainsBounds(SubBounds(bounds, i), itemBounds)) + { + if (childNodes[i] == null) + { + childNodes[i] = new OTNode( + tree, SubBounds(bounds, i), depth + 1); + } + return childNodes[i].Add(item, itemBounds); + } + } + // Wasn't bounded by any children, add it locally. + items.Add(item); + return this; + } + } + + internal void SplitNode() + { + childNodes = new OTNode[8]; + // Take all local items, remove them, then re-add them. + HashSet toAdd = items; + items = new HashSet(); + foreach (T item in toAdd) + { + OTNode addedTo = Add(item, tree.BoundsForItem(item)); + tree.UpdateItemNode(item, addedTo); + } + } + + internal void Remove(T item) + { + AssertOrThrow.True(items.Remove(item), + "Item is specified for removal but is not in the tree."); + } + + // Add items to the set from here and below within the tree. + // The resulting set is created on-demand. Returns 'true' + // if items match the query. + internal bool ContainedBy( + Bounds container, ref HashSet contained, int limit) + { + bool foundItems = false; + + foreach (T item in items) + { + if (contained != null && contained.Count >= limit) + { + return true; + } + if (Math3d.ContainsBounds(container, tree.BoundsForItem(item))) + { + EnsureSet(ref contained); + foundItems = true; + contained.Add(item); + } + } + + if (childNodes != null) + { + for (int i = 0; i < 8; i++) + { + if (childNodes[i] != null + && childNodes[i].bounds.Intersects(container)) + { + foundItems = childNodes[i].ContainedBy( + container, ref contained, limit) + || foundItems; + } + } + } + + return foundItems; + } + + // Add items to the set from here and below within the tree. + // The resulting set is created on-demand. Returns 'true' + // if items match the query. + internal bool IntersectedBy( + Bounds intersectBounds, ref HashSet intersected, int limit) + { + bool foundItems = false; + + foreach (T item in items) + { + if (intersected != null && intersected.Count >= limit) + { + return true; + } + if (tree.BoundsForItem(item).Intersects(intersectBounds)) + { + EnsureSet(ref intersected); + foundItems = true; + intersected.Add(item); + } + } + + if (childNodes != null) + { + for (int i = 0; i < 8; i++) + { + if (childNodes[i] != null + && childNodes[i].bounds.Intersects(intersectBounds)) + { + foundItems = childNodes[i].IntersectedBy( + intersectBounds, ref intersected, limit) + || foundItems; + } + } + } + return foundItems; + } + + private void EnsureSet(ref HashSet set) + { + set = set != null ? set : new HashSet(); + } + + // VisibleForTesting + public OTNode[] GetChildNodes() + { + return childNodes; + } + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs.meta new file mode 100644 index 0000000..9c622cf --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/OctreeImpl.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: adf791db561f41d6a76a4f0b5ef4e786 +timeCreated: 1721327589 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs new file mode 100644 index 0000000..c01a54b --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs @@ -0,0 +1,1070 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using com.google.apps.peltzer.client.model.core; + +namespace Polyhydra.Core +{ + + /// + /// Constants for segment endpoint type and segment types. + /// + public class Endpoint + { + public const int VERTEX = 1; + public const int FACE = 2; + public const int EDGE = 3; + + public const int V_V_V = VERTEX * 100 + VERTEX * 10 + VERTEX; + public const int V_E_V = VERTEX * 100 + EDGE * 10 + VERTEX; + public const int V_E_E = VERTEX * 100 + EDGE * 10 + EDGE; + public const int V_F_V = VERTEX * 100 + FACE * 10 + VERTEX; + public const int V_F_E = VERTEX * 100 + FACE * 10 + EDGE; + public const int V_F_F = VERTEX * 100 + FACE * 10 + FACE; + public const int E_E_V = EDGE * 100 + EDGE * 10 + VERTEX; + public const int E_E_E = EDGE * 100 + EDGE * 10 + EDGE; + public const int E_F_V = EDGE * 100 + FACE * 10 + VERTEX; + public const int E_F_E = EDGE * 100 + FACE * 10 + EDGE; + public const int E_F_F = EDGE * 100 + FACE * 10 + FACE; + public const int F_F_V = FACE * 100 + FACE * 10 + VERTEX; + public const int F_F_E = FACE * 100 + FACE * 10 + EDGE; + public const int F_F_F = FACE * 100 + FACE * 10 + FACE; + + public static int combine(int a, int b, int c) + { + return a * 100 + b * 10 + c; + } + } + + /// + /// Descriptor of segment which is the intersection of two polygons. + /// See Fig 5.1. + /// + public class SegmentDescriptor + { + public int start; + public int middle; + public int end; + + public int startVertIdx; + public int endVertIdx; + public float startDist; + public float endDist; + + public CsgVertex startVertex; + public CsgVertex endVertex; + + // Values after trimmed to the splitting poly. + public int finalStart; + public int finalMiddle; + public int finalEnd; + + public CsgVertex finalStartVertex; + public CsgVertex finalEndVertex; + } + + /// + /// Code to split a polygon with another one. The resulting polygons must always be convex without holes. + /// + public class PolygonSplitter + { + // Enable to get debug logging. + private const bool DEBUG = false; + // Enable to add polygon split checks. + private const bool VERIFY_POLY_SPLITS = false; + + /// Section 6: Split both of the polygons with respect to each other. + public static bool SplitPolys(CsgContext ctx, CsgObject objA, CsgPolygon polyA, CsgPolygon polyB) + { + float[] distAtoB = DistanceFromVertsToPlane(polyA, polyB.plane); + if (!CrossesPlane(distAtoB)) + { + // All verts on one side of plane, no intersection. + return false; + } + float[] distBtoA = DistanceFromVertsToPlane(polyB, polyA.plane); + if (!CrossesPlane(distAtoB)) + { + // All verts on one side of plane, no intersection. + return false; + } + + // Get the line that is the intersection of the planes. + Vector3 linePt; + Vector3 lineDir; + if (!PlanePlaneIntersection(out linePt, out lineDir, polyA.plane, polyB.plane)) + { + // Planes are parallel + return false; + } + + SegmentDescriptor segA = CalcSegmentDescriptor(ctx, linePt, lineDir, distAtoB, polyA); + SegmentDescriptor segB = CalcSegmentDescriptor(ctx, linePt, lineDir, distBtoA, polyB); + + if (segA == null || segB == null) + { + return false; + } + + // If the segments don't overlap, there is no intersection. + if (segA.endDist <= segB.startDist || segB.endDist <= segA.startDist) + { + return false; + } + + TrimTo(segA, segB); + + return SplitPolyOnSegment(objA, polyA, segA); + } + + // Trim segment A to segment B. This is where polygon A is cut by polygon B. + private static void TrimTo(SegmentDescriptor segA, SegmentDescriptor segB) + { + segA.finalMiddle = segA.middle; + if (segB.startDist > segA.startDist && Math.Abs(segA.startDist - segB.startDist) > CsgMath.EPSILON) + { + // Push segA start distance up. + segA.finalStart = segA.middle; + segA.finalStartVertex = segB.startVertex; + } + else + { + segA.finalStart = segA.start; + segA.finalStartVertex = segA.startVertex; + } + if (segB.endDist < segA.endDist && Math.Abs(segA.endDist - segB.endDist) > CsgMath.EPSILON) + { + // Pull segA end distance back. + segA.finalEnd = segA.middle; + segA.finalEndVertex = segB.endVertex; + } + else + { + segA.finalEnd = segA.end; + segA.finalEndVertex = segA.endVertex; + } + + if (segA.finalStartVertex == segA.finalEndVertex) + { + // Trimmed to a single vertex. + segA.finalStart = Endpoint.VERTEX; + segA.finalMiddle = Endpoint.VERTEX; + segA.finalEnd = Endpoint.VERTEX; + } + } + + // Given the exact segment where a polygon is split by the other, actually split the polygon. + // The way the polygon is split depends exactly on how the segment intersects the polygon. + // See Figure 6.3 for all the gory details. + // Public for testing. + public static bool SplitPolyOnSegment(CsgObject obj, CsgPolygon poly, SegmentDescriptor seg) + { + int splitType = Endpoint.combine(seg.finalStart, seg.finalMiddle, seg.finalEnd); + + if (DEBUG) + { + Console.Write("Split type: " + splitType + " seg = " + seg.startVertIdx + ", " + seg.endVertIdx + ", " + poly.vertices.Count); + } + + // For symmetrical cases, swap everything + if (splitType == Endpoint.F_F_V || splitType == Endpoint.E_E_V) + { + if (DEBUG) + { + Console.Write("Swapped"); + } + Swap(seg); + splitType = Endpoint.combine(seg.finalStart, seg.finalMiddle, seg.finalEnd); + } + + switch (splitType) + { + case Endpoint.E_E_E: + { + if (Vector3.Distance(seg.finalStartVertex.loc, seg.startVertex.loc) < CsgMath.EPSILON + && Vector3.Distance(seg.finalEndVertex.loc, seg.endVertex.loc) < CsgMath.EPSILON) + { + return false; + } + List> newPolys = new List>(); + int startInClockwiseOrder = seg.startVertIdx; + int endInClockwiseOrder = seg.endVertIdx; + CsgVertex startVertInClockwiseOrder = seg.finalStartVertex; + CsgVertex endVertInClockwiseOrder = seg.finalEndVertex; + bool pointsAreDifferent = startVertInClockwiseOrder != endVertInClockwiseOrder; + if ((seg.endVertIdx + 1) % poly.vertices.Count == seg.startVertIdx) + { + startInClockwiseOrder = seg.endVertIdx; + endInClockwiseOrder = seg.startVertIdx; + startVertInClockwiseOrder = seg.finalEndVertex; + endVertInClockwiseOrder = seg.finalStartVertex; + } + + newPolys.Add(new List() { + poly.vertices[(startInClockwiseOrder - 1 + poly.vertices.Count) % poly.vertices.Count], + poly.vertices[startInClockwiseOrder], + startVertInClockwiseOrder + }); + if (pointsAreDifferent) + { + newPolys.Add(new List() { + poly.vertices[(startInClockwiseOrder - 1 + poly.vertices.Count) % poly.vertices.Count], + startVertInClockwiseOrder, + endVertInClockwiseOrder + }); + } + List theRest = new List(); + for (int i = endInClockwiseOrder; i != startInClockwiseOrder; i = (i + 1) % poly.vertices.Count) + { + theRest.Add(poly.vertices[i]); + } + theRest.Add(endVertInClockwiseOrder); + newPolys.Add(theRest); + if (SafeReplacePolys(seg, obj, poly, newPolys.ToArray())) + { + obj.vertices.Add(startVertInClockwiseOrder); + if (pointsAreDifferent) + { + obj.vertices.Add(endVertInClockwiseOrder); + } + return true; + } + else + { + return false; + } + } + case Endpoint.V_V_V: + { + // Fig 6.3 (a) + seg.finalStartVertex.status = VertexStatus.BOUNDARY; + return false; + } + case Endpoint.V_E_V: + { + // Fig 6.3 (b) + seg.finalStartVertex.status = VertexStatus.BOUNDARY; + seg.finalEndVertex.status = VertexStatus.BOUNDARY; + return false; + } + case Endpoint.V_E_E: + { + // Fig 6.3 (c) + if (seg.endVertex == seg.finalEndVertex && seg.startVertex == seg.finalStartVertex) + { + //Debug.Log("no split is possible because segment is bad"); + return false; + } + List mainPart = new List(); + List triPart = new List(); + + for (int i = (seg.startVertIdx + 1) % poly.vertices.Count; i != seg.startVertIdx; i = (i + 1) % poly.vertices.Count) + { + mainPart.Add(poly.vertices[i]); + } + mainPart.Add(seg.finalEndVertex); + if (seg.startVertIdx == seg.endVertIdx) + { + // I don't think this ever happens. + triPart.Add(poly.vertices[(seg.startVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count]); + triPart.Add(poly.vertices[seg.startVertIdx]); + triPart.Add(seg.finalEndVertex); + } + else if (((seg.endVertIdx + 1) % poly.vertices.Count) == seg.startVertIdx) + { + triPart.Add(seg.finalEndVertex); + triPart.Add(poly.vertices[seg.startVertIdx]); + triPart.Add(poly.vertices[(seg.startVertIdx + 1) % poly.vertices.Count]); + } + else if ((seg.startVertIdx + 1) % poly.vertices.Count == seg.endVertIdx) + { + triPart.Add(poly.vertices[(seg.startVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count]); + triPart.Add(poly.vertices[seg.startVertIdx]); + triPart.Add(seg.finalEndVertex); + } + else + { + // This should never occur. + return false; + } + + if (SafeReplacePolys(seg, obj, poly, mainPart, triPart)) + { + obj.vertices.Add(seg.finalEndVertex); + + poly.vertices[seg.startVertIdx].status = VertexStatus.BOUNDARY; + return true; + } + else + { + return false; + } + } + case Endpoint.E_E_V: + { + // Fig 6.3 (c) + List mainPart = new List(); + List triPart = new List(); + + CsgVertex vertToAdd = null; + + if (seg.startVertIdx == seg.endVertIdx) + { + for (int i = (seg.startVertIdx + 1) % poly.vertices.Count; i != seg.startVertIdx; i = (i + 1) % poly.vertices.Count) + { + mainPart.Add(poly.vertices[i]); + } + mainPart.Add(seg.finalEndVertex); + triPart.Add(poly.vertices[seg.startVertIdx]); + triPart.Add(poly.vertices[(seg.startVertIdx + 1) % poly.vertices.Count]); + triPart.Add(seg.finalEndVertex); + vertToAdd = seg.finalEndVertex; + } + else if (((seg.endVertIdx + 1) % poly.vertices.Count) == seg.startVertIdx) + { + for (int i = seg.startVertIdx; i != seg.endVertIdx; i = (i + 1) % poly.vertices.Count) + { + mainPart.Add(poly.vertices[i]); + } + mainPart.Add(seg.finalStartVertex); + triPart.Add(poly.vertices[(seg.endVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count]); + triPart.Add(poly.vertices[seg.endVertIdx]); + triPart.Add(seg.finalStartVertex); + vertToAdd = seg.finalStartVertex; + } + else if ((seg.startVertIdx + 1) % poly.vertices.Count == seg.endVertIdx) + { + for (int i = (seg.endVertIdx + 1) % poly.vertices.Count; i != seg.endVertIdx; i = (i + 1) % poly.vertices.Count) + { + mainPart.Add(poly.vertices[i]); + } + mainPart.Add(seg.finalStartVertex); + triPart.Add(poly.vertices[seg.endVertIdx]); + triPart.Add(poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count]); + triPart.Add(seg.finalStartVertex); + vertToAdd = seg.finalStartVertex; + } + else + { + // Should not occur + return false; + } + + if (vertToAdd != null) + { + obj.vertices.Add(vertToAdd); + } + poly.vertices[seg.endVertIdx].status = VertexStatus.BOUNDARY; + + return SafeReplacePolys(seg, obj, poly, mainPart, triPart); + } + case Endpoint.V_F_V: + { + // Fig 6.3 (e) + List topPart = new List(); + for (int i = seg.startVertIdx; i != (seg.endVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[i]); + } + List bottomPart = new List(); + for (int i = seg.endVertIdx; i != (seg.startVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[i]); + } + + if (SafeReplacePolys(seg, obj, poly, topPart, bottomPart)) + { + poly.vertices[seg.startVertIdx].status = VertexStatus.BOUNDARY; + poly.vertices[seg.endVertIdx].status = VertexStatus.BOUNDARY; + return true; + } + else + { + return false; + } + } + case Endpoint.E_F_E: + { + // Fig 6.3 (k) + List topPart = new List(); + List bottomPart = new List(); + int vCount = poly.vertices.Count; + int startI = seg.startVertIdx; + int endI = seg.endVertIdx; + CsgVertex startV = seg.finalStartVertex; + CsgVertex endV = seg.finalEndVertex; + if ((endI + 1) % vCount == startI) + { + startI = seg.endVertIdx; + endI = seg.startVertIdx; + startV = seg.finalEndVertex; + endV = seg.finalStartVertex; + } + for (int i = (startI + 1) % vCount; i != (endI + 1) % vCount; i = (i + 1) % vCount) + { + topPart.Add(poly.vertices[i]); + } + topPart.Add(endV); + topPart.Add(startV); + for (int i = (endI + 1) % vCount; i != (startI + 1) % vCount; i = (i + 1) % vCount) + { + bottomPart.Add(poly.vertices[i]); + } + bottomPart.Add(startV); + bottomPart.Add(endV); + + if (SafeReplacePolys(seg, obj, poly, topPart, bottomPart)) + { + obj.vertices.Add(seg.finalStartVertex); + obj.vertices.Add(seg.finalEndVertex); + return true; + } + else + { + return false; + } + } + case Endpoint.V_F_E: + { + // Fig 6.3 (f) + List topPart = new List(); + List bottomPart = new List(); + topPart.Add(seg.finalEndVertex); + topPart.Add(seg.finalStartVertex); + + bottomPart.Add(seg.finalStartVertex); + bottomPart.Add(seg.finalEndVertex); + + bool topHalf = true; + for (int i = 1; i < poly.vertices.Count; i++) + { + int idx = (seg.startVertIdx + i) % poly.vertices.Count; + if (topHalf) + { + topPart.Add(poly.vertices[idx]); + } + else + { + bottomPart.Add(poly.vertices[idx]); + } + if (idx == seg.endVertIdx) + { + topHalf = false; + } + } + + if (SafeReplacePolys(seg, obj, poly, topPart, bottomPart)) + { + obj.vertices.Add(seg.finalStartVertex); + obj.vertices.Add(seg.finalEndVertex); + return true; + } + else + { + return false; + } + } + case Endpoint.E_F_V: + { + // Fig 6.3(f) + List topPart = new List(); + for (int i = seg.endVertIdx; i != (seg.startVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[i]); + } + topPart.Add(seg.finalStartVertex); + List bottomPart = new List(); + bottomPart.Add(seg.finalStartVertex); + for (int i = (seg.startVertIdx + 1) % poly.vertices.Count; i != (seg.endVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[i]); + } + + obj.vertices.Add(seg.finalStartVertex); + poly.vertices[seg.endVertIdx].status = VertexStatus.BOUNDARY; + + return SafeReplacePolys(seg, obj, poly, topPart, bottomPart); + } + case Endpoint.E_F_F: + { + // Fig 6.3 (l/m) + List topPart = new List(); + topPart.Add(seg.finalStartVertex); + int topEndIdx = seg.end == Endpoint.VERTEX ? (seg.endVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count + : seg.endVertIdx; + for (int idx = (seg.startVertIdx + 1) % poly.vertices.Count; idx != topEndIdx; + idx = (idx + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[idx]); + } + topPart.Add(poly.vertices[topEndIdx]); + topPart.Add(seg.finalEndVertex); + + List bottomPart = new List(); + bottomPart.Add(seg.finalEndVertex); + for (int idx = (seg.endVertIdx + 1) % poly.vertices.Count; idx != seg.startVertIdx; + idx = (idx + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[idx]); + } + bottomPart.Add(poly.vertices[seg.startVertIdx]); + bottomPart.Add(seg.finalStartVertex); + + bool result = false; + if (seg.end == Endpoint.VERTEX) + { + // Fig 6.3 (l) + CsgVertex prevVert = poly.vertices[(seg.endVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count]; + CsgVertex nextVert = poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count]; + + result = SafeReplacePolys(seg, obj, poly, topPart, bottomPart, + new List() { seg.finalEndVertex, prevVert, seg.endVertex }, + new List() { seg.finalEndVertex, seg.endVertex, nextVert }); + } + else + { + // Fig 6.3 (m) + CsgVertex nextVert = poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count]; + result = SafeReplacePolys(seg, obj, poly, topPart, bottomPart, + new List() { seg.finalEndVertex, poly.vertices[seg.endVertIdx], nextVert }); + } + if (result) + { + obj.vertices.Add(seg.finalStartVertex); + obj.vertices.Add(seg.finalEndVertex); + } + return result; + } + case Endpoint.F_F_E: + { + List> newPolys = new List>(); + newPolys.Add(new List() { + poly.vertices[seg.startVertIdx], + poly.vertices[(seg.startVertIdx + 1) % poly.vertices.Count], + seg.finalStartVertex + }); + + List topPart = new List(); + for (int i = (seg.startVertIdx + 1) % poly.vertices.Count; i != (seg.endVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[i]); + } + topPart.Add(seg.finalEndVertex); + topPart.Add(seg.finalStartVertex); + newPolys.Add(topPart); + + if (seg.start == Endpoint.VERTEX) + { + newPolys.Add(new List() { + poly.vertices[(seg.startVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count], + poly.vertices[seg.startVertIdx], + seg.finalStartVertex + }); + // bottom part goes until right before begin + List bottomPart = new List(); + for (int i = (seg.endVertIdx + 1) % poly.vertices.Count; i != seg.startVertIdx; i = (i + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[i]); + } + bottomPart.Add(seg.finalStartVertex); + bottomPart.Add(seg.finalEndVertex); + newPolys.Add(bottomPart); + } + else + { + List bottomPart = new List(); + for (int i = (seg.endVertIdx + 1) % poly.vertices.Count; i != (seg.startVertIdx + 1) % poly.vertices.Count; i = (i + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[i]); + } + bottomPart.Add(seg.finalStartVertex); + bottomPart.Add(seg.finalEndVertex); + newPolys.Add(bottomPart); + } + + obj.vertices.Add(seg.finalStartVertex); + obj.vertices.Add(seg.finalEndVertex); + + return SafeReplacePolys(seg, obj, poly, newPolys.ToArray()); + } + case Endpoint.V_F_F: + { + // Fig 6.3 (g/h) + obj.vertices.Add(seg.finalEndVertex); + List topPart = new List(); + int topEndIdx = seg.end == Endpoint.VERTEX ? (seg.endVertIdx - 1 + poly.vertices.Count) + % poly.vertices.Count : seg.endVertIdx; + topPart.Add(seg.finalEndVertex); + for (int idx = seg.startVertIdx; idx != topEndIdx; idx = (idx + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[idx]); + } + topPart.Add(poly.vertices[topEndIdx]); + + List bottomPart = new List(); + bottomPart.Add(seg.finalEndVertex); + for (int idx = (seg.endVertIdx + 1) % poly.vertices.Count; idx != seg.startVertIdx; + idx = (idx + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[idx]); + } + bottomPart.Add(poly.vertices[seg.startVertIdx]); + + if (seg.end == Endpoint.VERTEX) + { + // Fig 6.3 (g) + CsgVertex prevVert = poly.vertices[(seg.endVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count]; + CsgVertex nextVert = poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count]; + + return SafeReplacePolys(seg, obj, poly, topPart, bottomPart, + new List() { seg.finalEndVertex, prevVert, seg.endVertex }, + new List() { seg.finalEndVertex, seg.endVertex, nextVert }); + } + else + { + // Fig 6.3 (h) + CsgVertex nextVert = poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count]; + return SafeReplacePolys(seg, obj, poly, topPart, bottomPart, + new List() { seg.finalEndVertex, poly.vertices[seg.endVertIdx], nextVert }); + } + } + case Endpoint.F_F_F: + { + List> newPolys = new List>(); + + List interiorPoints = new List(); // may just be one point + interiorPoints.Add(seg.finalEndVertex); + if (Mathf.Abs(seg.finalEndVertex.loc.x - seg.finalStartVertex.loc.x) > CsgMath.EPSILON + || Mathf.Abs(seg.finalEndVertex.loc.y - seg.finalStartVertex.loc.y) > CsgMath.EPSILON + || Mathf.Abs(seg.finalEndVertex.loc.z - seg.finalStartVertex.loc.z) > CsgMath.EPSILON) + { + interiorPoints.Add(seg.finalStartVertex); + } + + // begin part + newPolys.Add(new List() { + poly.vertices[seg.startVertIdx], + poly.vertices[(seg.startVertIdx + 1) % poly.vertices.Count], + interiorPoints[interiorPoints.Count - 1], + }); + if (seg.start == Endpoint.VERTEX) + { + newPolys.Add(new List() { + poly.vertices[seg.startVertIdx], + interiorPoints[interiorPoints.Count - 1], + poly.vertices[(seg.startVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count], + }); + } + + // top part + List topPart = new List(); + int endIdx = seg.end == Endpoint.VERTEX ? seg.endVertIdx : (seg.endVertIdx + 1) % poly.vertices.Count; + for (int i = (seg.startVertIdx + 1) % poly.vertices.Count; i != endIdx; i = (i + 1) % poly.vertices.Count) + { + topPart.Add(poly.vertices[i]); + } + topPart.AddRange(interiorPoints); + newPolys.Add(topPart); + + // end part + newPolys.Add(new List() { + poly.vertices[seg.endVertIdx], + poly.vertices[(seg.endVertIdx + 1) % poly.vertices.Count], + interiorPoints[0], + }); + if (seg.end == Endpoint.VERTEX) + { + newPolys.Add(new List() { + poly.vertices[(seg.endVertIdx - 1 + poly.vertices.Count) % poly.vertices.Count], + poly.vertices[seg.endVertIdx], + interiorPoints[0], + }); + } + + // bottom part + List bottomPart = new List(); + endIdx = seg.start == Endpoint.VERTEX ? seg.startVertIdx : (seg.startVertIdx + 1) % poly.vertices.Count; + for (int i = (seg.endVertIdx + 1) % poly.vertices.Count; i != endIdx; i = (i + 1) % poly.vertices.Count) + { + bottomPart.Add(poly.vertices[i]); + } + bottomPart.AddRange(interiorPoints.Reverse().ToList()); + newPolys.Add(bottomPart); + + foreach (CsgVertex interiorPoint in interiorPoints) + { + obj.vertices.Add(interiorPoint); + } + + return SafeReplacePolys(seg, obj, poly, newPolys.ToArray()); + } + default: + if (DEBUG) + { + Console.Write("Unimplemented split type: " + splitType); + List[] otherPolys = new List[2]; + otherPolys[0] = new List() { seg.finalStartVertex, seg.finalEndVertex }; + otherPolys[1] = new List() { seg.startVertex, seg.endVertex }; + DumpPolygonsForDebug(poly, otherPolys); + } + Debug.Log("!!!!!>>>>> Unhandled case: " + splitType); + break; + } + return false; + } + + // Test helper, dumps info about a split to help generate a test case. + private static void DumpSplitInfo(CsgPolygon poly, SegmentDescriptor seg) + { + Console.Write("\nPoly:"); + foreach (CsgVertex vert in poly.vertices) + { + Console.Write(Str(vert)); + } + Console.Write("\nSeg:"); + Console.Write("descriptor.start = " + seg.start); + Console.Write("descriptor.middle = " + seg.middle); + Console.Write("descriptor.end = " + seg.end); + Console.Write("descriptor.finalStart = " + seg.finalStart); + Console.Write("descriptor.finalMiddle = " + seg.finalMiddle); + Console.Write("descriptor.finalEnd = " + seg.finalEnd); + Console.Write("descriptor.startVertex = " + Str(seg.startVertex)); + Console.Write("descriptor.endVertex = " + Str(seg.endVertex)); + Console.Write("descriptor.finalStartVertex = " + Str(seg.finalStartVertex)); + Console.Write("descriptor.finalEndVertex = " + Str(seg.finalEndVertex)); + Console.Write("descriptor.startVertIdx = " + seg.startVertIdx); + Console.Write("descriptor.endVertIdx = " + seg.endVertIdx); + } + + private static string Str(CsgVertex vert) + { + return vert.loc.x + "f, " + vert.loc.y + "f, " + vert.loc.z + "f"; + } + + + // Replace the polygon in the object with its components. If the components have zero-area, then skip. If the + // result would be identical, don't bother doing anything. + // SegmentDescriptor param is only here for debugging. + private static bool SafeReplacePolys(SegmentDescriptor seg, CsgObject obj, CsgPolygon oldPoly, + params List[] newPolyVerts) + { + + List newPolys = new List(); + foreach (List verts in newPolyVerts) + { + newPolys.Add(new CsgPolygon(verts, oldPoly.faceProperties, oldPoly.plane.normal)); + } + + bool dumpPolygons = DEBUG; + if (VERIFY_POLY_SPLITS) + { + int numSplitEdges = 0; + numSplitEdges += seg.finalStart == Endpoint.EDGE ? 1 : 0; + numSplitEdges += seg.finalEnd == Endpoint.EDGE ? 1 : 0; + if (numSplitEdges == 2 && seg.finalMiddle == Endpoint.EDGE) + { + // Same edge, so only one edge is split. + numSplitEdges = 1; + } + if (!CsgUtil.IsValidPolygonSplit(oldPoly, newPolys, numSplitEdges)) + { + Console.Write("Invalid split for case: " + Endpoint.combine(seg.finalStart, seg.finalMiddle, seg.finalEnd)); + dumpPolygons = true; + } + } + + // Dump polygon and split info out for debugging. + if (dumpPolygons) + { + List[] otherPolys = new List[newPolyVerts.Length + 2]; + for (int i = 0; i < newPolyVerts.Length; i++) + { + otherPolys[i] = newPolyVerts[i]; + } + otherPolys[newPolyVerts.Length] = new List() { seg.finalStartVertex, seg.finalEndVertex }; + otherPolys[newPolyVerts.Length + 1] = new List() { seg.startVertex, seg.endVertex }; + DumpPolygonsForDebug(oldPoly, otherPolys); + DumpSplitInfo(oldPoly, seg); + } + + // If we only had one valid polygon, it will be the same as the original, so do nothing. + if (newPolys.Count > 1) + { + obj.polygons.Remove(oldPoly); + obj.polygons.AddRange(newPolys); + return true; + } + + return false; + } + // Check if a polygon has a non-zero area and more than two vertices. + + // Helper method. Project the polygons on a plane and then write their 2d coords. + // Hacky: assumes last "polygon" is the split edge and labels it thusly + // (I always wanted to use 'thusly' in a code comment ;) + public static void DumpPolygonsForDebug(CsgPolygon oldPoly, params List[] polys) + { + Quaternion toPlane = Quaternion.Inverse(Quaternion.LookRotation(oldPoly.plane.normal, Vector3.right)); + Console.Write(""); + for (int i = -1; i < polys.Length; i++) + { + string name = "P" + (i + 1); + if (i == -1) + { + name = "Original"; + } + else if (i == (polys.Length - 2)) + { + name = "FinalSegment"; + } + else if (i == (polys.Length - 1)) + { + name = "Segment"; + } + List poly = i == -1 ? oldPoly.vertices : polys[i]; + foreach (CsgVertex vert in poly) + { + Vector3 projected = toPlane * vert.loc; + Console.Write(projected.x + ", " + projected.y + ", " + name); + } + } + } + + // Section 5: Calculate the segment descriptor for a given polygon and a line that splits it. + // Public for testing. + public static SegmentDescriptor CalcSegmentDescriptor(CsgContext ctx, + Vector3 linePt, Vector3 lineDir, float[] distToPlane, CsgPolygon poly) + { + SegmentDescriptor descriptor = new SegmentDescriptor(); + bool foundFirst = false; + bool foundSecond = false; + + for (int i = 0; i < distToPlane.Length; i++) + { + int j = (i + 1) % distToPlane.Length; + if (Math.Abs(distToPlane[i]) < CsgMath.EPSILON) + { + if (!foundFirst) + { + descriptor.startVertIdx = i; + descriptor.start = Endpoint.VERTEX; + descriptor.startDist = SignedDistance(linePt, lineDir, poly.vertices[i].loc); + descriptor.startVertex = poly.vertices[i]; + foundFirst = true; + } + else + { + descriptor.endVertIdx = i; + descriptor.end = Endpoint.VERTEX; + descriptor.endDist = SignedDistance(linePt, lineDir, poly.vertices[i].loc); + descriptor.endVertex = poly.vertices[i]; + foundSecond = true; + } + } + else if (Math.Abs(distToPlane[i]) > CsgMath.EPSILON && Math.Abs(distToPlane[j]) > CsgMath.EPSILON && Mathf.Sign(distToPlane[i]) != Mathf.Sign(distToPlane[j])) + { + // Crosses plane. + float t = distToPlane[i] / (distToPlane[i] - distToPlane[j]); + Vector3 midPoint = Vector3.Lerp(poly.vertices[i].loc, poly.vertices[j].loc, t); + // Project back onto our plane: + float dist = poly.plane.GetDistanceToPoint(midPoint); + midPoint -= (dist * poly.plane.normal); + if (!foundFirst) + { + descriptor.startVertIdx = i; + descriptor.start = Endpoint.EDGE; + descriptor.startDist = SignedDistance(linePt, lineDir, midPoint); + descriptor.startVertex = ctx.CreateOrGetVertexAt(midPoint); + descriptor.startVertex.status = VertexStatus.BOUNDARY; + foundFirst = true; + } + else + { + descriptor.endVertIdx = i; + descriptor.end = Endpoint.EDGE; + descriptor.endDist = SignedDistance(linePt, lineDir, midPoint); + descriptor.endVertex = ctx.CreateOrGetVertexAt(midPoint); + descriptor.endVertex.status = VertexStatus.BOUNDARY; + foundSecond = true; + } + } + } + + if (!foundFirst) + { + return null; + } + + if (!foundSecond) + { + descriptor.end = descriptor.start; + descriptor.endDist = descriptor.startDist; + descriptor.endVertIdx = descriptor.startVertIdx; + descriptor.endVertex = descriptor.startVertex; + } + + // Put the start and end in order of distance from linePt. + if (descriptor.startDist > descriptor.endDist) + { + int vertIdSave = descriptor.startVertIdx; + float distSave = descriptor.startDist; + int typeSave = descriptor.start; + CsgVertex vertSave = descriptor.startVertex; + + descriptor.startVertIdx = descriptor.endVertIdx; + descriptor.startDist = descriptor.endDist; + descriptor.start = descriptor.end; + descriptor.startVertex = descriptor.endVertex; + + descriptor.endVertIdx = vertIdSave; + descriptor.endDist = distSave; + descriptor.end = typeSave; + descriptor.endVertex = vertSave; + } + + if (descriptor.startVertIdx == descriptor.endVertIdx) + { + descriptor.middle = Endpoint.VERTEX; + } + else if (descriptor.start == Endpoint.VERTEX && descriptor.end == Endpoint.VERTEX + && (descriptor.startVertIdx == (descriptor.endVertIdx + 1) % distToPlane.Length + || (descriptor.startVertIdx + 1) % distToPlane.Length == descriptor.endVertIdx)) + { + descriptor.middle = Endpoint.EDGE; + } + else + { + descriptor.middle = Endpoint.FACE; + } + + // Mark endpoints as boundary. + descriptor.startVertex.status = VertexStatus.BOUNDARY; + descriptor.endVertex.status = VertexStatus.BOUNDARY; + + return descriptor; + } + + // Swap the endpoint descriptors. + private static void Swap(SegmentDescriptor descriptor) + { + int vertIdSave = descriptor.startVertIdx; + float distSave = descriptor.startDist; + int typeSave = descriptor.start; + int finalTypeSave = descriptor.finalStart; + CsgVertex vertSave = descriptor.startVertex; + CsgVertex finalVertSave = descriptor.finalStartVertex; + + descriptor.startVertIdx = descriptor.endVertIdx; + descriptor.startDist = descriptor.endDist; + descriptor.start = descriptor.end; + descriptor.finalStart = descriptor.finalEnd; + descriptor.startVertex = descriptor.endVertex; + descriptor.finalStartVertex = descriptor.finalEndVertex; + + descriptor.endVertIdx = vertIdSave; + descriptor.endDist = distSave; + descriptor.end = typeSave; + descriptor.finalEnd = finalTypeSave; + descriptor.endVertex = vertSave; + descriptor.finalEndVertex = finalVertSave; + } + + // Get the signed distance from a ray to a point. + private static float SignedDistance(Vector3 rayStart, Vector3 rayNormal, Vector3 point) + { + float d = Vector3.Distance(rayStart, point); + if (Vector3.Dot(rayNormal, point - rayStart) < 0) + { + return -d; + } + else + { + return d; + } + } + + // Calculate the ray that is the intersection of two planes. + private static bool PlanePlaneIntersection( + out Vector3 rayStart, out Vector3 rayNormal, Plane plane1, Plane plane2) + { + rayStart = Vector3.zero; + rayNormal = Vector3.Cross(plane1.normal, plane2.normal); + Vector3 ldir = Vector3.Cross(plane2.normal, rayNormal); + + float denominator = Vector3.Dot(plane1.normal, ldir); + + if (Mathf.Abs(denominator) > CsgMath.EPSILON) + { + Vector3 plane1Position = CsgMath.PointOnPlane(plane1); + Vector3 plane2Position = CsgMath.PointOnPlane(plane2); + Vector3 plane1ToPlane2 = plane1Position - plane2Position; + float t = Vector3.Dot(plane1.normal, plane1ToPlane2) / denominator; + rayStart = plane2Position + t * ldir; + return true; + } + else + { + return false; + } + } + + // Given the signed distances for a list of points to a plane, does the polygon cross the plane? + // It has if some points are positive and some are negative. Also considered true if some points + // are *on* the plane and others are not. + private static bool CrossesPlane(float[] dists) + { + bool hasAbove = false; + bool hasBelow = false; + bool hasOn = false; + foreach (float dist in dists) + { + if (dist < 0) + { + hasBelow = true; + } + else if (dist > 0) + { + hasAbove = true; + } + else + { + hasOn = true; + } + } + + int count = 0; + count += hasAbove ? 1 : 0; + count += hasBelow ? 1 : 0; + count += hasOn ? 1 : 0; + return count > 1; + } + + // Given a polygon, find the distance from each of its vertices to a given plane. + private static float[] DistanceFromVertsToPlane(CsgPolygon poly, Plane plane) + { + float[] dists = new float[poly.vertices.Count]; + for (int i = 0; i < poly.vertices.Count; i++) + { + float dist = plane.GetDistanceToPoint(poly.vertices[i].loc); + dists[i] = Mathf.Abs(dist) < CsgMath.EPSILON ? 0 : dist; + } + return dists; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs.meta new file mode 100644 index 0000000..19fff46 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/PolygonSplitter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b5645a7b3f19ce1478fc3aa7485c1b5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs new file mode 100644 index 0000000..1b7e2f9 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs @@ -0,0 +1,47 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Polyhydra.Core +{ + public struct SolidVertex + { + public int vertexId { get; private set; } + internal Vector3 position; + internal Vector3 normal; + + public SolidVertex(int vertexId, Vector3 pos, Vector3 norm) + { + this.vertexId = vertexId; + this.position = pos; + this.normal = norm; + } + + public SolidVertex Flip() + { + return new SolidVertex(vertexId, position, -normal); + } + + public SolidVertex Interpolate(int vertexId, SolidVertex other, float t) + { + return new SolidVertex( + vertexId, + Vector3.Lerp(position, other.position, t), + Vector3.Lerp(normal, other.normal, t).normalized); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs.meta new file mode 100644 index 0000000..f316d36 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SolidVertex.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ff36d2144337ac47b03cefef8fa27da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs new file mode 100644 index 0000000..ae49253 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs @@ -0,0 +1,108 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using com.google.apps.peltzer.client.model.util; +using UnityEngine; + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// Holder for calculated information about a Face. + /// + public struct FaceInfo + { + internal Bounds bounds; + internal Plane plane; + internal Vector3 baryCenter; + internal List border; + } + + /// + /// Holder for calculated information about an edge. + /// + public struct EdgeInfo + { + internal Bounds bounds; + internal float length; + internal Vector3 edgeStart; + internal Vector3 edgeVector; + } + + /// + /// Holder for an object along with a distance. Makes it easy to determine distance once for + /// a set of candidates and then sort on that. + /// + public struct DistancePair + { + public float distance; + public T value; + + internal DistancePair(float distance, T value) + { + this.distance = distance; + this.value = value; + } + } + + /// + /// Comparator for sorting DistancePairs. + /// + internal class DistancePairComparer : IComparer> + { + public int Compare(DistancePair left, DistancePair right) + { + return left.distance.CompareTo(right.distance); + } + } + + public class SpatialIndex + { + public const int MAX_INTERSECT_RESULTS = 100000; + + public CollisionSystem meshes { get; private set; } + private CollisionSystem faces; + private CollisionSystem edges; + private CollisionSystem vertices; + private CollisionSystem meshBounds; + private Dictionary faceInfo; + private Dictionary edgeInfo; + + public SpatialIndex(Bounds bounds) + { + Setup(bounds); + } + + private void Setup(Bounds bounds) + { + meshes = new OctreeImpl(bounds); + faces = new OctreeImpl(bounds); + edges = new OctreeImpl(bounds); + vertices = new OctreeImpl(bounds); + meshBounds = new OctreeImpl(bounds); + + faceInfo = new Dictionary(); + edgeInfo = new Dictionary(); + } + + + /// + /// Resets the entire state of the Spatial Index, using the given bounds. + /// + public void Reset(Bounds bounds) + { + Setup(bounds); + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs.meta new file mode 100644 index 0000000..701b60e --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/SpatialIndex.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2c145f64c3f446da83ee5bae238d0d8d +timeCreated: 1721327635 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs new file mode 100644 index 0000000..306f7f8 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs @@ -0,0 +1,39 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; + +namespace com.google.apps.peltzer.client.model.core +{ + + /// + /// A shared vertex. Represents a location in space that can be + /// shared by multiple faces in a single MMesh. + /// + public class Vertex + { + private readonly int _id; + private Vector3 _loc; + + // Read-only getters. + public int id { get { return _id; } } + public Vector3 loc { get { return _loc; } } + + public Vertex(int id, Vector3 loc) + { + _id = id; + _loc = loc; + } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs.meta new file mode 100644 index 0000000..5c1ead4 --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/Vertex.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4fc617a5717142328bd06057b136efe0 +timeCreated: 1721327866 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs new file mode 100644 index 0000000..e179e8f --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs @@ -0,0 +1,54 @@ +// Copyright 2020 The Blocks Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace com.google.apps.peltzer.client.model.core +{ + /// + /// A canonical id for a vertex, which includes the id of the mesh it belongs to. + /// + public class VertexKey + { + private readonly int _meshId; + private readonly int _vertexId; + private readonly int _hashCode; + + public VertexKey(int meshId, int vertexId) + { + _meshId = meshId; + _vertexId = vertexId; + // 31 is a good number: http://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier + _hashCode = (151 + meshId) * 31 + vertexId; + } + + public override bool Equals(object obj) + { + return Equals(obj as VertexKey); + } + + public bool Equals(VertexKey otherKey) + { + return otherKey != null + && _vertexId == otherKey._vertexId + && _meshId == otherKey._meshId; + } + + public override int GetHashCode() + { + return _hashCode; + } + + public int meshId { get { return _meshId; } } + public int vertexId { get { return _vertexId; } } + } +} diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs.meta b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs.meta new file mode 100644 index 0000000..018c9de --- /dev/null +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/VertexKey.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d12e69c52d2244f2b101addd2137aead +timeCreated: 1721327979 \ No newline at end of file From 1915f4710720999aea29da94304cc23d579614a7 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 20 Jul 2024 16:57:41 +0100 Subject: [PATCH 2/4] WIP --- .../Multi/CsgShapeSettings.cs | 55 +++++++++++++++++++ .../Multi/CsgShapeSettings.cs.meta | 3 + .../Core/Runtime/PolyMesh.Geometry.cs | 43 +++++++++++++-- .../Core/Runtime/csg/CsgObject.cs | 11 +++- .../Core/Runtime/csg/CsgOperations.cs | 4 +- 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs create mode 100644 Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs.meta diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs new file mode 100644 index 0000000..3a1fb53 --- /dev/null +++ b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Polyhydra.Core; +using UnityEngine; + +[CreateAssetMenu(fileName = "CsgShapeSettings", menuName = "PolyhydraMulti/CsgShapeSettings", order = 1)] +public class CsgShapeSettings : BaseSettings +{ + [Header(" ")] + public PolyMesh.CsgOp csgOp; + public BaseSettings Operand1; + public BaseSettings Operand2; + public PolyTransform Operand2Transform; + + public override PolyMesh BuildBaseShape() + { + return null; + } + + public override Mesh BuildAll(AppearanceSettings appearanceSettings) + { + PolyMesh finalPoly = new PolyMesh(); + var poly1 = Operand1.BuildBaseShape(); + poly1 = Operand1.ApplyModifiers(poly1); + var poly2 = Operand2.BuildBaseShape(); + poly2 = Operand2.ApplyModifiers(poly2); + poly2.Transform( + Operand2Transform.Translation, + Operand2Transform.Rotation, + Operand2Transform.NonUniformScale * Operand2Transform.Scale + ); + finalPoly = poly1.ApplyCsg(poly2, csgOp); + finalPoly = ApplyModifiers(finalPoly); + + var meshData = finalPoly.BuildMeshData( + colorMethod: GetColorMethod(appearanceSettings), + colors: CalculateColorList(appearanceSettings) + ); + return finalPoly.BuildUnityMesh(meshData); + } + + public override void AttachAction(Action settingsChanged, PolyhydraGenerator generator) + { + OnSettingsChanged += settingsChanged; + Operand1.AttachAction(settingsChanged, generator); + Operand2.AttachAction(settingsChanged, generator); + } + + public override void DetachAction(Action settingsChanged) + { + OnSettingsChanged -= settingsChanged; + Operand1.DetachAction(settingsChanged); + Operand2.DetachAction(settingsChanged); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs.meta b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs.meta new file mode 100644 index 0000000..25cd5c1 --- /dev/null +++ b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d216953ee452c43dd87a229fcb879dd4 +timeCreated: 1709036135 \ No newline at end of file diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs index 3166866..db44607 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs @@ -3372,14 +3372,49 @@ public PolyMesh SplitEdges() return poly; } - public PolyMesh CsgSubtract(PolyMesh other) + [Serializable] + public enum CsgOp + { + Union = 0, + Subtract = 1, + Intersect = 2, + } + + + public PolyMesh ApplyCsg(PolyMesh other, CsgOp op) { - var poly = Duplicate(); CsgContext ctx = new CsgContext(GetBounds()); CsgObject csgPoly = new CsgObject(this); CsgObject csgPolyOther = new CsgObject(other); - var foo= CsgOperations.CsgSubtract(ctx, csgPoly, csgPolyOther); - return poly; + List csgResult; + switch (op) + { + case CsgOp.Union: + csgResult = CsgOperations.CsgUnion(ctx, csgPoly, csgPolyOther); + break; + case CsgOp.Subtract: + csgResult = CsgOperations.CsgSubtract(ctx, csgPoly, csgPolyOther); + break; + case CsgOp.Intersect: + csgResult = CsgOperations.CsgIntersect(ctx, csgPoly, csgPolyOther); + break; + default: + csgResult = CsgOperations.CsgUnion(ctx, csgPoly, csgPolyOther); + break; + } + var newVerts = new List(); + var newFaces = new List>(); + foreach (var polygon in csgResult) + { + var face = new List(); + foreach (CsgVertex vertex in polygon.vertices) + { + newVerts.Add(vertex.loc); + face.Add(newVerts.Count - 1); + } + newFaces.Add(face); + } + return new PolyMesh(newVerts, newFaces); } private PolyMesh SubdivideEdges(OpParams o) diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs index 8d318e8..fcd92e6 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgObject.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.Linq; +using com.google.apps.peltzer.client.model.core; using UnityEngine; namespace Polyhydra.Core @@ -42,7 +43,15 @@ public CsgObject(List polygons, List vertices) public CsgObject(PolyMesh polyMesh) { - throw new NotImplementedException(); + var verts = polyMesh.ListVerticesByPoints().Select(v => new CsgVertex(v)).ToList(); + var faceIndices = polyMesh.ListFacesByVertexIndices().Select(f => + { + var faceVerts = f.Select(i => verts[i]).ToList(); + return new CsgPolygon(faceVerts, new FaceProperties()); + }).ToList(); + this.vertices = verts; + this.polygons = faceIndices; + this.bounds = polyMesh.GetBounds(); } } } diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs index 837ceb8..5f7a4bf 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/csg/CsgOperations.cs @@ -50,7 +50,7 @@ public static List CsgSubtract(CsgContext ctx, CsgObject leftObj, Cs /// /// Perform union on CsgObjects /// - private static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + public static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) { SplitObject(ctx, leftObj, rightObj); SplitObject(ctx, rightObj, leftObj); @@ -68,7 +68,7 @@ private static List CsgUnion(CsgContext ctx, CsgObject leftObj, CsgO /// /// Perform intersection on CsgObjects /// - private static List CsgIntersect(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) + public static List CsgIntersect(CsgContext ctx, CsgObject leftObj, CsgObject rightObj) { SplitObject(ctx, leftObj, rightObj); SplitObject(ctx, rightObj, leftObj); From 33d5ffdb7432c3396fb4ac5bbd0b4e3e754af7df Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 20 Jul 2024 17:37:44 +0100 Subject: [PATCH 3/4] CSG bounds fix --- Assets/Scripts/PolyhydraGenerator.cs | 2 +- .../com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/PolyhydraGenerator.cs b/Assets/Scripts/PolyhydraGenerator.cs index 7e49a2c..e283a74 100644 --- a/Assets/Scripts/PolyhydraGenerator.cs +++ b/Assets/Scripts/PolyhydraGenerator.cs @@ -58,7 +58,7 @@ private void Build() { var mf = gameObject.GetComponent(); mf.mesh = settings.BuildAll(appearanceSettings); - // TODO check if this is neccessary + // TODO check if this is necessary mf.mesh.RecalculateBounds(); } diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs index db44607..85e3684 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs @@ -3383,7 +3383,9 @@ public enum CsgOp public PolyMesh ApplyCsg(PolyMesh other, CsgOp op) { - CsgContext ctx = new CsgContext(GetBounds()); + var bounds = GetBounds(); + bounds.Encapsulate(other.GetBounds()); + CsgContext ctx = new CsgContext(bounds); CsgObject csgPoly = new CsgObject(this); CsgObject csgPolyOther = new CsgObject(other); List csgResult; @@ -3414,7 +3416,10 @@ public PolyMesh ApplyCsg(PolyMesh other, CsgOp op) } newFaces.Add(face); } - return new PolyMesh(newVerts, newFaces); + var result = new PolyMesh(newVerts, newFaces); + //result.Weld(0.01f); + result.MergeCoplanarFaces(0.01f); + return result; } private PolyMesh SubdivideEdges(OpParams o) From adbe7cfb6001625fa80cc118da4f358d1a28eb1a Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 21 Jul 2024 13:22:18 +0100 Subject: [PATCH 4/4] Refactor multi to allow nesting. Reorganise multi presets. CSG fixes --- .../Components.meta => Logo.meta} | 2 +- Assets/Logo/Logo CSG.asset | 50 + Assets/Logo/Logo CSG.asset.meta | 8 + Assets/Logo/Logo Element 1.asset | 35 + Assets/Logo/Logo Element 1.asset.meta | 8 + Assets/Logo/Logo Element 2.asset | 45 + Assets/Logo/Logo Element 2.asset.meta | 8 + Assets/Logo/Logo Scene.unity | 1789 +++++++++++++++++ Assets/Logo/Logo Scene.unity.meta | 7 + Assets/Logo/Materials.meta | 8 + Assets/Logo/Materials/Blue.mat | 127 ++ Assets/Logo/Materials/Blue.mat.meta | 8 + Assets/Logo/Materials/Cyan.mat | 126 ++ Assets/Logo/Materials/Cyan.mat.meta | 8 + Assets/Logo/Materials/Green.mat | 127 ++ Assets/Logo/Materials/Green.mat.meta | 8 + Assets/Logo/Materials/Magenta.mat | 127 ++ Assets/Logo/Materials/Magenta.mat.meta | 8 + Assets/Logo/Materials/Orange.mat | 127 ++ Assets/Logo/Materials/Orange.mat.meta | 8 + Assets/Logo/Materials/Red.mat | 127 ++ Assets/Logo/Materials/Red.mat.meta | 8 + Assets/Logo/Materials/White.mat | 127 ++ Assets/Logo/Materials/White.mat.meta | 8 + Assets/Presets - Multi/CSG 1.meta | 8 + .../CSG 1/CsgShapeSettings 1.asset | 40 + .../CSG 1/CsgShapeSettings 1.asset.meta | 8 + Assets/Presets - Multi/CSG 1/Operand 1.asset | 31 + .../CSG 1/Operand 1.asset.meta | 8 + Assets/Presets - Multi/CSG 1/Operand 2.asset | 31 + .../CSG 1/Operand 2.asset.meta | 8 + Assets/Presets - Multi/Layered 1.meta | 8 + .../DiPyramid.asset} | 2 +- .../DiPyramid.asset.meta} | 0 .../LayeredShapeSettings 1.asset | 0 .../LayeredShapeSettings 1.asset.meta | 0 .../Trapezohedron.asset} | 2 +- .../Trapezohedron.asset.meta} | 0 Assets/Presets - Multi/Layered 2.meta | 8 + .../Grid 1.asset} | 2 +- .../Grid 1.asset.meta} | 0 .../Grid 2.asset} | 2 +- .../Grid 2.asset.meta} | 0 .../Grid 3.asset} | 2 +- .../Grid 3.asset.meta} | 0 .../Grid 4.asset} | 2 +- .../Grid 4.asset.meta} | 0 .../LayeredShapeSettings 2.asset | 0 .../LayeredShapeSettings 2.asset.meta | 0 Assets/Presets - Multi/Layered 3.meta | 8 + .../Grid 1.asset} | 2 +- .../Grid 1.asset.meta} | 0 .../Grid 2.asset} | 2 +- .../Grid 2.asset.meta} | 0 .../LayeredShapeSettings 3.asset | 0 .../LayeredShapeSettings 3.asset.meta | 0 Assets/Presets - Multi/Layered 4.meta | 8 + .../Grid 1.asset} | 2 +- .../Grid 1.asset.meta} | 0 .../Grid 2.asset} | 2 +- .../Grid 2.asset.meta} | 0 .../LayeredShapeSettings 4.asset | 0 .../LayeredShapeSettings 4.asset.meta | 0 Assets/Presets - Multi/Nested.meta | 8 + .../Nested/PointSym Inner.asset | 29 + .../Nested/PointSym Inner.asset.meta | 8 + .../Nested/PointSym Outer.asset | 29 + .../Nested/PointSym Outer.asset.meta | 8 + Assets/Presets - Multi/Nested/Polygon.asset | 37 + .../Presets - Multi/Nested/Polygon.asset.meta | 8 + .../Nested/Wallpaper Outer.asset | 31 + .../Nested/Wallpaper Outer.asset.meta | 8 + Assets/Presets - Multi/PointSym 1.meta | 8 + .../Pentagon.asset} | 2 +- .../Pentagon.asset.meta} | 0 .../PointSymShapeSettings 1.asset | 0 .../PointSymShapeSettings 1.asset.meta | 0 Assets/Presets - Multi/PointSym 2.meta | 8 + .../PointSymShapeSettings 2.asset | 0 .../PointSymShapeSettings 2.asset.meta | 0 .../PointSym 2/PointSymShapeSettings.asset | 91 + .../PointSymShapeSettings.asset.meta | 8 + .../Square.asset} | 2 +- .../Square.asset.meta} | 0 Assets/Presets - Multi/WallpaperSym 1.meta | 8 + .../Grid.asset} | 2 +- .../Grid.asset.meta} | 0 .../WallpaperSymShapeSettings 1.asset | 0 .../WallpaperSymShapeSettings 1.asset.meta | 0 .../Multi/CsgShapeSettings.cs | 18 +- .../Multi/LayeredShapeSettings.cs | 12 +- .../Multi/PointSymShapeSettings.cs | 11 +- .../Multi/WallpaperSymShapeSettings.cs | 11 +- .../Core/Runtime/PolyMesh.Geometry.cs | 45 +- .../Core/Runtime/PolyMesh.cs | 13 + 95 files changed, 3455 insertions(+), 40 deletions(-) rename Assets/{Presets - Multi/Components.meta => Logo.meta} (77%) create mode 100644 Assets/Logo/Logo CSG.asset create mode 100644 Assets/Logo/Logo CSG.asset.meta create mode 100644 Assets/Logo/Logo Element 1.asset create mode 100644 Assets/Logo/Logo Element 1.asset.meta create mode 100644 Assets/Logo/Logo Element 2.asset create mode 100644 Assets/Logo/Logo Element 2.asset.meta create mode 100644 Assets/Logo/Logo Scene.unity create mode 100644 Assets/Logo/Logo Scene.unity.meta create mode 100644 Assets/Logo/Materials.meta create mode 100644 Assets/Logo/Materials/Blue.mat create mode 100644 Assets/Logo/Materials/Blue.mat.meta create mode 100644 Assets/Logo/Materials/Cyan.mat create mode 100644 Assets/Logo/Materials/Cyan.mat.meta create mode 100644 Assets/Logo/Materials/Green.mat create mode 100644 Assets/Logo/Materials/Green.mat.meta create mode 100644 Assets/Logo/Materials/Magenta.mat create mode 100644 Assets/Logo/Materials/Magenta.mat.meta create mode 100644 Assets/Logo/Materials/Orange.mat create mode 100644 Assets/Logo/Materials/Orange.mat.meta create mode 100644 Assets/Logo/Materials/Red.mat create mode 100644 Assets/Logo/Materials/Red.mat.meta create mode 100644 Assets/Logo/Materials/White.mat create mode 100644 Assets/Logo/Materials/White.mat.meta create mode 100644 Assets/Presets - Multi/CSG 1.meta create mode 100644 Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset create mode 100644 Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset.meta create mode 100644 Assets/Presets - Multi/CSG 1/Operand 1.asset create mode 100644 Assets/Presets - Multi/CSG 1/Operand 1.asset.meta create mode 100644 Assets/Presets - Multi/CSG 1/Operand 2.asset create mode 100644 Assets/Presets - Multi/CSG 1/Operand 2.asset.meta create mode 100644 Assets/Presets - Multi/Layered 1.meta rename Assets/Presets - Multi/{Components/Layered Shape 1a DiPyramid.asset => Layered 1/DiPyramid.asset} (95%) rename Assets/Presets - Multi/{Components/Layered Shape 1a DiPyramid.asset.meta => Layered 1/DiPyramid.asset.meta} (100%) rename Assets/Presets - Multi/{ => Layered 1}/LayeredShapeSettings 1.asset (100%) rename Assets/Presets - Multi/{ => Layered 1}/LayeredShapeSettings 1.asset.meta (100%) rename Assets/Presets - Multi/{Components/Layered Shape 1b Trapezohedron.asset => Layered 1/Trapezohedron.asset} (95%) rename Assets/Presets - Multi/{Components/Layered Shape 1b Trapezohedron.asset.meta => Layered 1/Trapezohedron.asset.meta} (100%) create mode 100644 Assets/Presets - Multi/Layered 2.meta rename Assets/Presets - Multi/{Components/Layered Shape 2a Grid.asset => Layered 2/Grid 1.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 2a Grid.asset.meta => Layered 2/Grid 1.asset.meta} (100%) rename Assets/Presets - Multi/{Components/Layered Shape 2b Grid.asset => Layered 2/Grid 2.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 2b Grid.asset.meta => Layered 2/Grid 2.asset.meta} (100%) rename Assets/Presets - Multi/{Components/Layered Shape 2c Grid.asset => Layered 2/Grid 3.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 2c Grid.asset.meta => Layered 2/Grid 3.asset.meta} (100%) rename Assets/Presets - Multi/{Components/Layered Shape 2d Grid.asset => Layered 2/Grid 4.asset} (96%) rename Assets/Presets - Multi/{Components/Layered Shape 2d Grid.asset.meta => Layered 2/Grid 4.asset.meta} (100%) rename Assets/Presets - Multi/{ => Layered 2}/LayeredShapeSettings 2.asset (100%) rename Assets/Presets - Multi/{ => Layered 2}/LayeredShapeSettings 2.asset.meta (100%) create mode 100644 Assets/Presets - Multi/Layered 3.meta rename Assets/Presets - Multi/{Components/Layered Shape 3a Grid.asset => Layered 3/Grid 1.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 3a Grid.asset.meta => Layered 3/Grid 1.asset.meta} (100%) rename Assets/Presets - Multi/{Components/Layered Shape 3b Grid.asset => Layered 3/Grid 2.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 3b Grid.asset.meta => Layered 3/Grid 2.asset.meta} (100%) rename Assets/Presets - Multi/{ => Layered 3}/LayeredShapeSettings 3.asset (100%) rename Assets/Presets - Multi/{ => Layered 3}/LayeredShapeSettings 3.asset.meta (100%) create mode 100644 Assets/Presets - Multi/Layered 4.meta rename Assets/Presets - Multi/{Components/Layered Shape 4a Grid.asset => Layered 4/Grid 1.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 4a Grid.asset.meta => Layered 4/Grid 1.asset.meta} (100%) rename Assets/Presets - Multi/{Components/Layered Shape 4b Grid.asset => Layered 4/Grid 2.asset} (97%) rename Assets/Presets - Multi/{Components/Layered Shape 4b Grid.asset.meta => Layered 4/Grid 2.asset.meta} (100%) rename Assets/Presets - Multi/{ => Layered 4}/LayeredShapeSettings 4.asset (100%) rename Assets/Presets - Multi/{ => Layered 4}/LayeredShapeSettings 4.asset.meta (100%) create mode 100644 Assets/Presets - Multi/Nested.meta create mode 100644 Assets/Presets - Multi/Nested/PointSym Inner.asset create mode 100644 Assets/Presets - Multi/Nested/PointSym Inner.asset.meta create mode 100644 Assets/Presets - Multi/Nested/PointSym Outer.asset create mode 100644 Assets/Presets - Multi/Nested/PointSym Outer.asset.meta create mode 100644 Assets/Presets - Multi/Nested/Polygon.asset create mode 100644 Assets/Presets - Multi/Nested/Polygon.asset.meta create mode 100644 Assets/Presets - Multi/Nested/Wallpaper Outer.asset create mode 100644 Assets/Presets - Multi/Nested/Wallpaper Outer.asset.meta create mode 100644 Assets/Presets - Multi/PointSym 1.meta rename Assets/Presets - Multi/{Components/PointSymShape 1a Pentagon.asset => PointSym 1/Pentagon.asset} (95%) rename Assets/Presets - Multi/{Components/PointSymShape 1a Pentagon.asset.meta => PointSym 1/Pentagon.asset.meta} (100%) rename Assets/Presets - Multi/{ => PointSym 1}/PointSymShapeSettings 1.asset (100%) rename Assets/Presets - Multi/{ => PointSym 1}/PointSymShapeSettings 1.asset.meta (100%) create mode 100644 Assets/Presets - Multi/PointSym 2.meta rename Assets/Presets - Multi/{ => PointSym 2}/PointSymShapeSettings 2.asset (100%) rename Assets/Presets - Multi/{ => PointSym 2}/PointSymShapeSettings 2.asset.meta (100%) create mode 100644 Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset create mode 100644 Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset.meta rename Assets/Presets - Multi/{Components/PointSymShape 2 Square.asset => PointSym 2/Square.asset} (94%) rename Assets/Presets - Multi/{Components/PointSymShape 2 Square.asset.meta => PointSym 2/Square.asset.meta} (100%) create mode 100644 Assets/Presets - Multi/WallpaperSym 1.meta rename Assets/Presets - Multi/{Components/WallpaperSymShape 1 Grid.asset => WallpaperSym 1/Grid.asset} (96%) rename Assets/Presets - Multi/{Components/WallpaperSymShape 1 Grid.asset.meta => WallpaperSym 1/Grid.asset.meta} (100%) rename Assets/Presets - Multi/{ => WallpaperSym 1}/WallpaperSymShapeSettings 1.asset (100%) rename Assets/Presets - Multi/{ => WallpaperSym 1}/WallpaperSymShapeSettings 1.asset.meta (100%) diff --git a/Assets/Presets - Multi/Components.meta b/Assets/Logo.meta similarity index 77% rename from Assets/Presets - Multi/Components.meta rename to Assets/Logo.meta index 16fbdf8..29f7606 100644 --- a/Assets/Presets - Multi/Components.meta +++ b/Assets/Logo.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 558072bb269402a42a36590176d0a7e7 +guid: 3d47312bb27c349f69dea656b3df1eab folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Logo/Logo CSG.asset b/Assets/Logo/Logo CSG.asset new file mode 100644 index 0000000..10f53b5 --- /dev/null +++ b/Assets/Logo/Logo CSG.asset @@ -0,0 +1,50 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d216953ee452c43dd87a229fcb879dd4, type: 3} + m_Name: Logo CSG + m_EditorClassIdentifier: + Operators: + - Active: 0 + OpType: 40 + Parameter1: 0 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 0 + OpType: 47 + Parameter1: 0.01 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 1 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + csgOp: 2 + Operand1: {fileID: 11400000, guid: 8f0b6d5c5d2fc456ea6b95d37059acd9, type: 2} + Operand2: {fileID: 11400000, guid: 5c356fbfc3d0d4d1fa72f188eeeed36f, type: 2} + Operand2Transform: + Translation: {x: -1, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0} + Scale: 1 + NonUniformScale: {x: 1, y: 1, z: 1} + WeldThreshold: 0 + MergeThreshold: 0 diff --git a/Assets/Logo/Logo CSG.asset.meta b/Assets/Logo/Logo CSG.asset.meta new file mode 100644 index 0000000..43d4808 --- /dev/null +++ b/Assets/Logo/Logo CSG.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39eb7eb0a6231415a8d0b286bc3a9abf +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Logo Element 1.asset b/Assets/Logo/Logo Element 1.asset new file mode 100644 index 0000000..4c11a6d --- /dev/null +++ b/Assets/Logo/Logo Element 1.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bcdd704e9dd94c86b0d2f7d8638f92e9, type: 3} + m_Name: Logo Element 1 + m_EditorClassIdentifier: + Operators: + - Active: 0 + OpType: 36 + Parameter1: 0.21 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + type: 0 + Sides: 4 + SetHeight: 1 + Height: 0.5 + CapHeight: 1 diff --git a/Assets/Logo/Logo Element 1.asset.meta b/Assets/Logo/Logo Element 1.asset.meta new file mode 100644 index 0000000..686c14b --- /dev/null +++ b/Assets/Logo/Logo Element 1.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f0b6d5c5d2fc456ea6b95d37059acd9 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Logo Element 2.asset b/Assets/Logo/Logo Element 2.asset new file mode 100644 index 0000000..1ed0edd --- /dev/null +++ b/Assets/Logo/Logo Element 2.asset @@ -0,0 +1,45 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bcdd704e9dd94c86b0d2f7d8638f92e9, type: 3} + m_Name: Logo Element 2 + m_EditorClassIdentifier: + Operators: + - Active: 1 + OpType: 102 + Parameter1: -0.21 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 0 + OpType: 118 + Parameter1: 5.9 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + type: 0 + Sides: 4 + SetHeight: 1 + Height: 0.15 + CapHeight: 1 diff --git a/Assets/Logo/Logo Element 2.asset.meta b/Assets/Logo/Logo Element 2.asset.meta new file mode 100644 index 0000000..8fa7bb6 --- /dev/null +++ b/Assets/Logo/Logo Element 2.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5c356fbfc3d0d4d1fa72f188eeeed36f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Logo Scene.unity b/Assets/Logo/Logo Scene.unity new file mode 100644 index 0000000..cd4a6fb --- /dev/null +++ b/Assets/Logo/Logo Scene.unity @@ -0,0 +1,1789 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 170076734} + m_IndirectSpecularColor: {r: 0.4475597, g: 0.49752253, b: 0.5753332, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 10 + m_AtlasSize: 512 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 256 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 1772668760} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &90555434 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 90555438} + - component: {fileID: 90555437} + - component: {fileID: 90555436} + - component: {fileID: 90555435} + m_Layer: 0 + m_Name: Tile 3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &90555435 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 90555434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &90555436 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 90555434} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 46c54a1d83fd44651a079089bd5a2af4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &90555437 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 90555434} + m_Mesh: {fileID: 0} +--- !u!4 &90555438 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 90555434} + m_LocalRotation: {x: -0, y: 0.3826836, z: -0, w: -0.92387944} + m_LocalPosition: {x: 0, y: 0, z: 1.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1674203136} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 315, z: 0} +--- !u!1 &170076733 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 170076735} + - component: {fileID: 170076734} + - component: {fileID: 170076736} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &170076734 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170076733} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 1e-45, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &170076735 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170076733} + m_LocalRotation: {x: 0.4512349, y: -0.12385471, z: -0.19826767, w: 0.86124164} + m_LocalPosition: {x: 0, y: 0.7828307, z: -2.8960624} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 534669905} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 46.73, y: -34.91, z: -41.4} +--- !u!114 &170076736 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170076733} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 1 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} +--- !u!1 &201366611 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 201366615} + - component: {fileID: 201366614} + - component: {fileID: 201366613} + - component: {fileID: 201366612} + m_Layer: 0 + m_Name: Tile 2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &201366612 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 201366611} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &201366613 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 201366611} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: acb7ded1b3e9c4f3daf4b171a6922358, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &201366614 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 201366611} + m_Mesh: {fileID: 0} +--- !u!4 &201366615 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 201366611} + m_LocalRotation: {x: -0, y: 0.92387956, z: -0, w: 0.38268343} + m_LocalPosition: {x: -1.41, y: 0, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1674203136} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 135, z: 0} +--- !u!1 &534669902 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 534669905} + - component: {fileID: 534669904} + - component: {fileID: 534669903} + - component: {fileID: 534669906} + - component: {fileID: 534669907} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &534669903 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 534669902} + m_Enabled: 1 +--- !u!20 &534669904 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 534669902} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.01 + far clip plane: 100 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &534669905 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 534669902} + m_LocalRotation: {x: 0.6013322, y: -0.116301194, z: 0.089038365, w: 0.78545904} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 170076735} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &534669906 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 534669902} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 0 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 +--- !u!114 &534669907 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 534669902} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bd4d0deaa0564b019f45068386d0ed6e, type: 3} + m_Name: + m_EditorClassIdentifier: + targetObject: {fileID: 832160059} + azimuthAngle: 0 + polarAngle: 45 + zoom: 0.8 +--- !u!1 &628800883 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 628800884} + - component: {fileID: 628800887} + - component: {fileID: 628800886} + - component: {fileID: 628800885} + m_Layer: 0 + m_Name: Tile 2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &628800884 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 628800883} + m_LocalRotation: {x: -0, y: 0.92387956, z: -0, w: 0.38268343} + m_LocalPosition: {x: -1.41, y: -0.01, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1095113823} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 135, z: 0} +--- !u!114 &628800885 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 628800883} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &628800886 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 628800883} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 891ea99868a504c5cb51e1fc5aaa4d7f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &628800887 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 628800883} + m_Mesh: {fileID: 0} +--- !u!1 &764618536 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 764618537} + - component: {fileID: 764618540} + - component: {fileID: 764618539} + - component: {fileID: 764618538} + m_Layer: 0 + m_Name: Gap + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &764618537 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 764618536} + m_LocalRotation: {x: -0, y: 0.3826836, z: -0, w: -0.92387944} + m_LocalPosition: {x: 0, y: -0.01, z: 1.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1095113823} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 315, z: 0} +--- !u!114 &764618538 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 764618536} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &764618539 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 764618536} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: b5f1799e9180c164dacd089c250950a7, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &764618540 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 764618536} + m_Mesh: {fileID: 0} +--- !u!1 &832160059 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 832160063} + - component: {fileID: 832160062} + - component: {fileID: 832160061} + - component: {fileID: 832160060} + m_Layer: 0 + m_Name: Tile 1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &832160060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832160059} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &832160061 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832160059} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8a14fbd27d2eb421a923c0fefa743635, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &832160062 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832160059} + m_Mesh: {fileID: 0} +--- !u!4 &832160063 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832160059} + m_LocalRotation: {x: -0, y: 0.38268343, z: -0, w: 0.92387956} + m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1674203136} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0} +--- !u!1 &836171138 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 836171139} + - component: {fileID: 836171142} + - component: {fileID: 836171141} + - component: {fileID: 836171140} + m_Layer: 0 + m_Name: Gap + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &836171139 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 836171138} + m_LocalRotation: {x: -0, y: 0.92387956, z: -0, w: 0.38268343} + m_LocalPosition: {x: -1.41, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2102651427} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 135, z: 0} +--- !u!114 &836171140 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 836171138} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &836171141 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 836171138} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: b5f1799e9180c164dacd089c250950a7, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &836171142 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 836171138} + m_Mesh: {fileID: 0} +--- !u!1 &1095113822 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1095113823} + m_Layer: 0 + m_Name: Right + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1095113823 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1095113822} + m_LocalRotation: {x: -0, y: -0, z: -0.7071068, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1412428143} + - {fileID: 628800884} + - {fileID: 1174532341} + - {fileID: 764618537} + m_Father: {fileID: 2119207517} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: -90} +--- !u!1 &1174532340 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1174532341} + - component: {fileID: 1174532344} + - component: {fileID: 1174532343} + - component: {fileID: 1174532342} + m_Layer: 0 + m_Name: Tile 3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1174532341 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1174532340} + m_LocalRotation: {x: -0, y: 0.9238796, z: -0, w: -0.38268325} + m_LocalPosition: {x: -1.41, y: -0.01, z: 1.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1095113823} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 225, z: 0} +--- !u!114 &1174532342 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1174532340} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1174532343 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1174532340} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 891ea99868a504c5cb51e1fc5aaa4d7f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1174532344 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1174532340} + m_Mesh: {fileID: 0} +--- !u!1 &1265634547 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1265634548} + - component: {fileID: 1265634551} + - component: {fileID: 1265634550} + - component: {fileID: 1265634549} + m_Layer: 0 + m_Name: Tile 3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1265634548 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265634547} + m_LocalRotation: {x: -0, y: 0.3826836, z: -0, w: -0.92387944} + m_LocalPosition: {x: 0, y: 0, z: 1.41} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2102651427} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 315, z: 0} +--- !u!114 &1265634549 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265634547} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1265634550 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265634547} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 891ea99868a504c5cb51e1fc5aaa4d7f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1265634551 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1265634547} + m_Mesh: {fileID: 0} +--- !u!1 &1404443488 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1404443489} + - component: {fileID: 1404443492} + - component: {fileID: 1404443491} + - component: {fileID: 1404443490} + m_Layer: 0 + m_Name: Tile 1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1404443489 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1404443488} + m_LocalRotation: {x: -0, y: 0.38268343, z: -0, w: 0.92387956} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2102651427} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0} +--- !u!114 &1404443490 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1404443488} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1404443491 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1404443488} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: f2e6846a87b3840a39858b3b2122613e, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1404443492 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1404443488} + m_Mesh: {fileID: 0} +--- !u!1 &1412428142 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1412428143} + - component: {fileID: 1412428146} + - component: {fileID: 1412428145} + - component: {fileID: 1412428144} + m_Layer: 0 + m_Name: Tile 1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1412428143 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1412428142} + m_LocalRotation: {x: -0, y: 0.38268343, z: -0, w: 0.92387956} + m_LocalPosition: {x: 0, y: -0.01, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1095113823} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 45, z: 0} +--- !u!114 &1412428144 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1412428142} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1412428145 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1412428142} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 891ea99868a504c5cb51e1fc5aaa4d7f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1412428146 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1412428142} + m_Mesh: {fileID: 0} +--- !u!1 &1674203135 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1674203136} + m_Layer: 0 + m_Name: Left + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1674203136 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1674203135} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.4, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 832160063} + - {fileID: 201366615} + - {fileID: 1831397075} + - {fileID: 90555438} + m_Father: {fileID: 2119207517} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!850595691 &1772668760 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Settings.lighting + serializedVersion: 4 + m_GIWorkflowMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 512 + m_BakeResolution: 10 + m_Padding: 2 + m_LightmapCompression: 3 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 256 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 0 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 +--- !u!1 &1831397071 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1831397075} + - component: {fileID: 1831397074} + - component: {fileID: 1831397073} + - component: {fileID: 1831397072} + m_Layer: 0 + m_Name: Gap + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!114 &1831397072 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831397071} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1831397073 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831397071} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: b5f1799e9180c164dacd089c250950a7, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1831397074 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831397071} + m_Mesh: {fileID: 0} +--- !u!4 &1831397075 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1831397071} + m_LocalRotation: {x: -0, y: 0.9238796, z: -0, w: -0.38268325} + m_LocalPosition: {x: -1.41, y: 0, z: 1.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1674203136} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 225, z: 0} +--- !u!1 &1994765191 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1994765192} + - component: {fileID: 1994765195} + - component: {fileID: 1994765194} + - component: {fileID: 1994765193} + m_Layer: 0 + m_Name: Tile 2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1994765192 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994765191} + m_LocalRotation: {x: -0, y: 0.9238796, z: -0, w: -0.38268325} + m_LocalPosition: {x: -1.41, y: 0, z: 1.41} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2102651427} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 225, z: 0} +--- !u!114 &1994765193 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994765191} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9933a06dcdbc4fc3b2e7cdafa2b7aa0f, type: 3} + m_Name: + m_EditorClassIdentifier: + settings: {fileID: 11400000, guid: 39eb7eb0a6231415a8d0b286bc3a9abf, type: 2} + appearanceSettings: {fileID: 11400000, guid: 405fee0d54aacb145b70464d8fa62f6f, type: 2} + debugFaces: 0 + debugEdges: 0 + debugVerts: 0 +--- !u!23 &1994765194 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994765191} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 891ea99868a504c5cb51e1fc5aaa4d7f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1994765195 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994765191} + m_Mesh: {fileID: 0} +--- !u!1 &2102651426 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2102651427} + m_Layer: 0 + m_Name: Top + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2102651427 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2102651426} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 1.4, y: 0, z: 1.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1404443489} + - {fileID: 836171139} + - {fileID: 1994765192} + - {fileID: 1265634548} + m_Father: {fileID: 2119207517} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0} +--- !u!1 &2119207516 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2119207517} + m_Layer: 0 + m_Name: Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2119207517 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2119207516} + m_LocalRotation: {x: -0.14644654, y: -0.35355324, z: -0.8535535, w: 0.3535534} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1674203136} + - {fileID: 2102651427} + - {fileID: 1095113823} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 225, y: 180, z: 45} diff --git a/Assets/Logo/Logo Scene.unity.meta b/Assets/Logo/Logo Scene.unity.meta new file mode 100644 index 0000000..7fcfdda --- /dev/null +++ b/Assets/Logo/Logo Scene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 315d4342e1b4046ed81a9e3774ba5682 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials.meta b/Assets/Logo/Materials.meta new file mode 100644 index 0000000..c2ec35c --- /dev/null +++ b/Assets/Logo/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 345b6d17fcf15456a9bb3303c2128dee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Blue.mat b/Assets/Logo/Materials/Blue.mat new file mode 100644 index 0000000..54f17e0 --- /dev/null +++ b/Assets/Logo/Materials/Blue.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blue + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0.23359394, b: 1, a: 1} + - _Color: {r: 0, g: 0.23359394, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Blue.mat.meta b/Assets/Logo/Materials/Blue.mat.meta new file mode 100644 index 0000000..6a657bb --- /dev/null +++ b/Assets/Logo/Materials/Blue.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8d787de27c18417989166d0f8c3172c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Cyan.mat b/Assets/Logo/Materials/Cyan.mat new file mode 100644 index 0000000..0637c54 --- /dev/null +++ b/Assets/Logo/Materials/Cyan.mat @@ -0,0 +1,126 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Cyan + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0.9806144, b: 1, a: 1} + - _Color: {r: 0, g: 0.9806144, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Cyan.mat.meta b/Assets/Logo/Materials/Cyan.mat.meta new file mode 100644 index 0000000..0622ea6 --- /dev/null +++ b/Assets/Logo/Materials/Cyan.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2e6846a87b3840a39858b3b2122613e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Green.mat b/Assets/Logo/Materials/Green.mat new file mode 100644 index 0000000..0bee367 --- /dev/null +++ b/Assets/Logo/Materials/Green.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Green + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0.9245283, b: 0.096612595, a: 1} + - _Color: {r: 0, g: 0.9245283, b: 0.096612595, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Green.mat.meta b/Assets/Logo/Materials/Green.mat.meta new file mode 100644 index 0000000..33c352a --- /dev/null +++ b/Assets/Logo/Materials/Green.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98ddb2049b7024858b092697a2e356aa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Magenta.mat b/Assets/Logo/Materials/Magenta.mat new file mode 100644 index 0000000..d932b14 --- /dev/null +++ b/Assets/Logo/Materials/Magenta.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Magenta + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 0, b: 0.87355375, a: 1} + - _Color: {r: 1, g: 0, b: 0.87355375, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Magenta.mat.meta b/Assets/Logo/Materials/Magenta.mat.meta new file mode 100644 index 0000000..bf64f29 --- /dev/null +++ b/Assets/Logo/Materials/Magenta.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46c54a1d83fd44651a079089bd5a2af4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Orange.mat b/Assets/Logo/Materials/Orange.mat new file mode 100644 index 0000000..1e1748a --- /dev/null +++ b/Assets/Logo/Materials/Orange.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Orange + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 0.7325124, b: 0, a: 1} + - _Color: {r: 1, g: 0.7325124, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Orange.mat.meta b/Assets/Logo/Materials/Orange.mat.meta new file mode 100644 index 0000000..9d94540 --- /dev/null +++ b/Assets/Logo/Materials/Orange.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a14fbd27d2eb421a923c0fefa743635 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/Red.mat b/Assets/Logo/Materials/Red.mat new file mode 100644 index 0000000..2b15832 --- /dev/null +++ b/Assets/Logo/Materials/Red.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Red + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 0, b: 0, a: 1} + - _Color: {r: 1, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/Red.mat.meta b/Assets/Logo/Materials/Red.mat.meta new file mode 100644 index 0000000..76fe492 --- /dev/null +++ b/Assets/Logo/Materials/Red.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: acb7ded1b3e9c4f3daf4b171a6922358 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Logo/Materials/White.mat b/Assets/Logo/Materials/White.mat new file mode 100644 index 0000000..572c1db --- /dev/null +++ b/Assets/Logo/Materials/White.mat @@ -0,0 +1,127 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8911944235226537853 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 5 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: White + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_ValidKeywords: + - _RECEIVE_SHADOWS_OFF + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 1 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AlphaClip: 0 + - _Blend: 0 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 0 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 0 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 1, b: 1, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Logo/Materials/White.mat.meta b/Assets/Logo/Materials/White.mat.meta new file mode 100644 index 0000000..a3e4679 --- /dev/null +++ b/Assets/Logo/Materials/White.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 891ea99868a504c5cb51e1fc5aaa4d7f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/CSG 1.meta b/Assets/Presets - Multi/CSG 1.meta new file mode 100644 index 0000000..fe5ad46 --- /dev/null +++ b/Assets/Presets - Multi/CSG 1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a300c15558244f5c8c3e3633b757447 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset b/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset new file mode 100644 index 0000000..5f2f53a --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset @@ -0,0 +1,40 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d216953ee452c43dd87a229fcb879dd4, type: 3} + m_Name: CsgShapeSettings 1 + m_EditorClassIdentifier: + Operators: + - Active: 0 + OpType: 1 + Parameter1: 0 + Parameter2: -0.05 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + csgOp: 0 + Operand1: {fileID: 11400000, guid: 4e45dd19641fb4a8083f981bf31c6b62, type: 2} + Operand2: {fileID: 11400000, guid: 21a8d0036ed45401ea8ba7bf8c682c96, type: 2} + Operand2Transform: + Translation: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0} + Scale: 0.71 + NonUniformScale: {x: 1, y: 1, z: 1} + WeldThreshold: 0 + MergeThreshold: 0 diff --git a/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset.meta b/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset.meta new file mode 100644 index 0000000..156076f --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/CsgShapeSettings 1.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cf7ad0a2e23eb40b5990252a5dfa7d86 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/CSG 1/Operand 1.asset b/Assets/Presets - Multi/CSG 1/Operand 1.asset new file mode 100644 index 0000000..be3c3b8 --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/Operand 1.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 63be2e6bb0184595ac92a61275f5a8f6, type: 3} + m_Name: Operand 1 + m_EditorClassIdentifier: + Operators: + - Active: 0 + OpType: 15 + Parameter1: 0.57 + Parameter2: 0.54 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + WythoffType: 11 diff --git a/Assets/Presets - Multi/CSG 1/Operand 1.asset.meta b/Assets/Presets - Multi/CSG 1/Operand 1.asset.meta new file mode 100644 index 0000000..2039d75 --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/Operand 1.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e45dd19641fb4a8083f981bf31c6b62 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/CSG 1/Operand 2.asset b/Assets/Presets - Multi/CSG 1/Operand 2.asset new file mode 100644 index 0000000..82fec5f --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/Operand 2.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 63be2e6bb0184595ac92a61275f5a8f6, type: 3} + m_Name: Operand 2 + m_EditorClassIdentifier: + Operators: + - Active: 1 + OpType: 1 + Parameter1: 0.78 + Parameter2: 0 + Iterations: 2 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + WythoffType: 11 diff --git a/Assets/Presets - Multi/CSG 1/Operand 2.asset.meta b/Assets/Presets - Multi/CSG 1/Operand 2.asset.meta new file mode 100644 index 0000000..4ccc1b0 --- /dev/null +++ b/Assets/Presets - Multi/CSG 1/Operand 2.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21a8d0036ed45401ea8ba7bf8c682c96 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Layered 1.meta b/Assets/Presets - Multi/Layered 1.meta new file mode 100644 index 0000000..260cf4f --- /dev/null +++ b/Assets/Presets - Multi/Layered 1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 51d7702099379495988a787701a7f8d5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/Layered Shape 1a DiPyramid.asset b/Assets/Presets - Multi/Layered 1/DiPyramid.asset similarity index 95% rename from Assets/Presets - Multi/Components/Layered Shape 1a DiPyramid.asset rename to Assets/Presets - Multi/Layered 1/DiPyramid.asset index ebec0e2..7f71025 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 1a DiPyramid.asset +++ b/Assets/Presets - Multi/Layered 1/DiPyramid.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: bcdd704e9dd94c86b0d2f7d8638f92e9, type: 3} - m_Name: Layered Shape 1a DiPyramid + m_Name: DiPyramid m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 1a DiPyramid.asset.meta b/Assets/Presets - Multi/Layered 1/DiPyramid.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 1a DiPyramid.asset.meta rename to Assets/Presets - Multi/Layered 1/DiPyramid.asset.meta diff --git a/Assets/Presets - Multi/LayeredShapeSettings 1.asset b/Assets/Presets - Multi/Layered 1/LayeredShapeSettings 1.asset similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 1.asset rename to Assets/Presets - Multi/Layered 1/LayeredShapeSettings 1.asset diff --git a/Assets/Presets - Multi/LayeredShapeSettings 1.asset.meta b/Assets/Presets - Multi/Layered 1/LayeredShapeSettings 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 1.asset.meta rename to Assets/Presets - Multi/Layered 1/LayeredShapeSettings 1.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 1b Trapezohedron.asset b/Assets/Presets - Multi/Layered 1/Trapezohedron.asset similarity index 95% rename from Assets/Presets - Multi/Components/Layered Shape 1b Trapezohedron.asset rename to Assets/Presets - Multi/Layered 1/Trapezohedron.asset index 8e6db2f..55ddf11 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 1b Trapezohedron.asset +++ b/Assets/Presets - Multi/Layered 1/Trapezohedron.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: bcdd704e9dd94c86b0d2f7d8638f92e9, type: 3} - m_Name: Layered Shape 1b Trapezohedron + m_Name: Trapezohedron m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 1b Trapezohedron.asset.meta b/Assets/Presets - Multi/Layered 1/Trapezohedron.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 1b Trapezohedron.asset.meta rename to Assets/Presets - Multi/Layered 1/Trapezohedron.asset.meta diff --git a/Assets/Presets - Multi/Layered 2.meta b/Assets/Presets - Multi/Layered 2.meta new file mode 100644 index 0000000..ca57b8d --- /dev/null +++ b/Assets/Presets - Multi/Layered 2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79b733a00154c45579876c4cd644b3f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/Layered Shape 2a Grid.asset b/Assets/Presets - Multi/Layered 2/Grid 1.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 2a Grid.asset rename to Assets/Presets - Multi/Layered 2/Grid 1.asset index 7db9738..4b4e17d 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 2a Grid.asset +++ b/Assets/Presets - Multi/Layered 2/Grid 1.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 2a Grid + m_Name: Grid 1 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 2a Grid.asset.meta b/Assets/Presets - Multi/Layered 2/Grid 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 2a Grid.asset.meta rename to Assets/Presets - Multi/Layered 2/Grid 1.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 2b Grid.asset b/Assets/Presets - Multi/Layered 2/Grid 2.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 2b Grid.asset rename to Assets/Presets - Multi/Layered 2/Grid 2.asset index 481e09c..32f4eb5 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 2b Grid.asset +++ b/Assets/Presets - Multi/Layered 2/Grid 2.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 2b Grid + m_Name: Grid 2 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 2b Grid.asset.meta b/Assets/Presets - Multi/Layered 2/Grid 2.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 2b Grid.asset.meta rename to Assets/Presets - Multi/Layered 2/Grid 2.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 2c Grid.asset b/Assets/Presets - Multi/Layered 2/Grid 3.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 2c Grid.asset rename to Assets/Presets - Multi/Layered 2/Grid 3.asset index 941c201..0abb719 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 2c Grid.asset +++ b/Assets/Presets - Multi/Layered 2/Grid 3.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 2c Grid + m_Name: Grid 3 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 2c Grid.asset.meta b/Assets/Presets - Multi/Layered 2/Grid 3.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 2c Grid.asset.meta rename to Assets/Presets - Multi/Layered 2/Grid 3.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 2d Grid.asset b/Assets/Presets - Multi/Layered 2/Grid 4.asset similarity index 96% rename from Assets/Presets - Multi/Components/Layered Shape 2d Grid.asset rename to Assets/Presets - Multi/Layered 2/Grid 4.asset index 449dc8c..3459e2c 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 2d Grid.asset +++ b/Assets/Presets - Multi/Layered 2/Grid 4.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 2d Grid + m_Name: Grid 4 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 2d Grid.asset.meta b/Assets/Presets - Multi/Layered 2/Grid 4.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 2d Grid.asset.meta rename to Assets/Presets - Multi/Layered 2/Grid 4.asset.meta diff --git a/Assets/Presets - Multi/LayeredShapeSettings 2.asset b/Assets/Presets - Multi/Layered 2/LayeredShapeSettings 2.asset similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 2.asset rename to Assets/Presets - Multi/Layered 2/LayeredShapeSettings 2.asset diff --git a/Assets/Presets - Multi/LayeredShapeSettings 2.asset.meta b/Assets/Presets - Multi/Layered 2/LayeredShapeSettings 2.asset.meta similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 2.asset.meta rename to Assets/Presets - Multi/Layered 2/LayeredShapeSettings 2.asset.meta diff --git a/Assets/Presets - Multi/Layered 3.meta b/Assets/Presets - Multi/Layered 3.meta new file mode 100644 index 0000000..e7cb613 --- /dev/null +++ b/Assets/Presets - Multi/Layered 3.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 287c229f9ad30452490c1f398f2fde98 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/Layered Shape 3a Grid.asset b/Assets/Presets - Multi/Layered 3/Grid 1.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 3a Grid.asset rename to Assets/Presets - Multi/Layered 3/Grid 1.asset index da1fda3..f3602c4 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 3a Grid.asset +++ b/Assets/Presets - Multi/Layered 3/Grid 1.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 3a Grid + m_Name: Grid 1 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 3a Grid.asset.meta b/Assets/Presets - Multi/Layered 3/Grid 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 3a Grid.asset.meta rename to Assets/Presets - Multi/Layered 3/Grid 1.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 3b Grid.asset b/Assets/Presets - Multi/Layered 3/Grid 2.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 3b Grid.asset rename to Assets/Presets - Multi/Layered 3/Grid 2.asset index 6c132bd..4404590 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 3b Grid.asset +++ b/Assets/Presets - Multi/Layered 3/Grid 2.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 3b Grid + m_Name: Grid 2 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 3b Grid.asset.meta b/Assets/Presets - Multi/Layered 3/Grid 2.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 3b Grid.asset.meta rename to Assets/Presets - Multi/Layered 3/Grid 2.asset.meta diff --git a/Assets/Presets - Multi/LayeredShapeSettings 3.asset b/Assets/Presets - Multi/Layered 3/LayeredShapeSettings 3.asset similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 3.asset rename to Assets/Presets - Multi/Layered 3/LayeredShapeSettings 3.asset diff --git a/Assets/Presets - Multi/LayeredShapeSettings 3.asset.meta b/Assets/Presets - Multi/Layered 3/LayeredShapeSettings 3.asset.meta similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 3.asset.meta rename to Assets/Presets - Multi/Layered 3/LayeredShapeSettings 3.asset.meta diff --git a/Assets/Presets - Multi/Layered 4.meta b/Assets/Presets - Multi/Layered 4.meta new file mode 100644 index 0000000..160c068 --- /dev/null +++ b/Assets/Presets - Multi/Layered 4.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f1a74f063ab543a3baae7dae6393bfb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/Layered Shape 4a Grid.asset b/Assets/Presets - Multi/Layered 4/Grid 1.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 4a Grid.asset rename to Assets/Presets - Multi/Layered 4/Grid 1.asset index 350be2e..f6fa248 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 4a Grid.asset +++ b/Assets/Presets - Multi/Layered 4/Grid 1.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 4a Grid + m_Name: Grid 1 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 4a Grid.asset.meta b/Assets/Presets - Multi/Layered 4/Grid 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 4a Grid.asset.meta rename to Assets/Presets - Multi/Layered 4/Grid 1.asset.meta diff --git a/Assets/Presets - Multi/Components/Layered Shape 4b Grid.asset b/Assets/Presets - Multi/Layered 4/Grid 2.asset similarity index 97% rename from Assets/Presets - Multi/Components/Layered Shape 4b Grid.asset rename to Assets/Presets - Multi/Layered 4/Grid 2.asset index 7adfd91..6e5e10d 100644 --- a/Assets/Presets - Multi/Components/Layered Shape 4b Grid.asset +++ b/Assets/Presets - Multi/Layered 4/Grid 2.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: Layered Shape 4b Grid + m_Name: Grid 2 m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/Layered Shape 4b Grid.asset.meta b/Assets/Presets - Multi/Layered 4/Grid 2.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/Layered Shape 4b Grid.asset.meta rename to Assets/Presets - Multi/Layered 4/Grid 2.asset.meta diff --git a/Assets/Presets - Multi/LayeredShapeSettings 4.asset b/Assets/Presets - Multi/Layered 4/LayeredShapeSettings 4.asset similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 4.asset rename to Assets/Presets - Multi/Layered 4/LayeredShapeSettings 4.asset diff --git a/Assets/Presets - Multi/LayeredShapeSettings 4.asset.meta b/Assets/Presets - Multi/Layered 4/LayeredShapeSettings 4.asset.meta similarity index 100% rename from Assets/Presets - Multi/LayeredShapeSettings 4.asset.meta rename to Assets/Presets - Multi/Layered 4/LayeredShapeSettings 4.asset.meta diff --git a/Assets/Presets - Multi/Nested.meta b/Assets/Presets - Multi/Nested.meta new file mode 100644 index 0000000..8febbb4 --- /dev/null +++ b/Assets/Presets - Multi/Nested.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b0b6da76fad04d5cbcc2918d0c6d17e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Nested/PointSym Inner.asset b/Assets/Presets - Multi/Nested/PointSym Inner.asset new file mode 100644 index 0000000..f1ecb54 --- /dev/null +++ b/Assets/Presets - Multi/Nested/PointSym Inner.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2e9a3bf926d4def994fb139a6407ded, type: 3} + m_Name: PointSym Inner + m_EditorClassIdentifier: + Operators: [] + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + ShapeSettings: {fileID: 11400000, guid: a116d5b326c454b13b7e46ae77d957db, type: 2} + Family: 11 + Transform: + Translation: {x: -0.5, y: 0.2, z: 0} + Rotation: {x: 90, y: 0, z: 90} + Scale: 0.14 + NonUniformScale: {x: 1.5, y: 1, z: 1} + Repeats: 3 + Radius: 0 diff --git a/Assets/Presets - Multi/Nested/PointSym Inner.asset.meta b/Assets/Presets - Multi/Nested/PointSym Inner.asset.meta new file mode 100644 index 0000000..0cad870 --- /dev/null +++ b/Assets/Presets - Multi/Nested/PointSym Inner.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b5b985155920240f09420dba117a72c2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Nested/PointSym Outer.asset b/Assets/Presets - Multi/Nested/PointSym Outer.asset new file mode 100644 index 0000000..7d16d4c --- /dev/null +++ b/Assets/Presets - Multi/Nested/PointSym Outer.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2e9a3bf926d4def994fb139a6407ded, type: 3} + m_Name: PointSym Outer + m_EditorClassIdentifier: + Operators: [] + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + ShapeSettings: {fileID: 11400000, guid: b5b985155920240f09420dba117a72c2, type: 2} + Family: 0 + Transform: + Translation: {x: 0.36, y: 0, z: 0} + Rotation: {x: 90, y: 0, z: 0} + Scale: 0.6 + NonUniformScale: {x: 1, y: 1, z: 1} + Repeats: 4 + Radius: 0 diff --git a/Assets/Presets - Multi/Nested/PointSym Outer.asset.meta b/Assets/Presets - Multi/Nested/PointSym Outer.asset.meta new file mode 100644 index 0000000..a5fd57a --- /dev/null +++ b/Assets/Presets - Multi/Nested/PointSym Outer.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47f1ca194549642ccafe273697b5aa89 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Nested/Polygon.asset b/Assets/Presets - Multi/Nested/Polygon.asset new file mode 100644 index 0000000..5e1818e --- /dev/null +++ b/Assets/Presets - Multi/Nested/Polygon.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2c2880ed5b584425b92968f04cae7e21, type: 3} + m_Name: Polygon + m_EditorClassIdentifier: + Operators: + - Active: 1 + OpType: 115 + Parameter1: 0.5 + Parameter2: 0 + Iterations: 2 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + type: 0 + method: 0 + A: 4 + B: 1.17 + C: 0 + Layers: 1 + LayerHeight: 0.5 diff --git a/Assets/Presets - Multi/Nested/Polygon.asset.meta b/Assets/Presets - Multi/Nested/Polygon.asset.meta new file mode 100644 index 0000000..bc0b4ed --- /dev/null +++ b/Assets/Presets - Multi/Nested/Polygon.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a116d5b326c454b13b7e46ae77d957db +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Nested/Wallpaper Outer.asset b/Assets/Presets - Multi/Nested/Wallpaper Outer.asset new file mode 100644 index 0000000..3b0249a --- /dev/null +++ b/Assets/Presets - Multi/Nested/Wallpaper Outer.asset @@ -0,0 +1,31 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1635e387b7294856aa9ca789699bae4a, type: 3} + m_Name: Wallpaper Outer + m_EditorClassIdentifier: + Operators: [] + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceInset: 0 + ColorMethod: 1 + ShapeSettings: {fileID: 11400000, guid: b5b985155920240f09420dba117a72c2, type: 2} + SymmetryGroup: 1 + Transform: + Translation: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 90, z: 90} + Scale: 1 + NonUniformScale: {x: 1, y: 1, z: 1} + RepeatX: 2 + RepeatY: 2 + GridScale: {x: 1, y: 1} + Skew: {x: 0, y: 0} diff --git a/Assets/Presets - Multi/Nested/Wallpaper Outer.asset.meta b/Assets/Presets - Multi/Nested/Wallpaper Outer.asset.meta new file mode 100644 index 0000000..5f326e7 --- /dev/null +++ b/Assets/Presets - Multi/Nested/Wallpaper Outer.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b9fb0b06f48cd4da8a566e69826e8010 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/PointSym 1.meta b/Assets/Presets - Multi/PointSym 1.meta new file mode 100644 index 0000000..82ebd33 --- /dev/null +++ b/Assets/Presets - Multi/PointSym 1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7e06d0cc55e5a447e93ff8fc11656c6f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/PointSymShape 1a Pentagon.asset b/Assets/Presets - Multi/PointSym 1/Pentagon.asset similarity index 95% rename from Assets/Presets - Multi/Components/PointSymShape 1a Pentagon.asset rename to Assets/Presets - Multi/PointSym 1/Pentagon.asset index 091e39f..2579143 100644 --- a/Assets/Presets - Multi/Components/PointSymShape 1a Pentagon.asset +++ b/Assets/Presets - Multi/PointSym 1/Pentagon.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2c2880ed5b584425b92968f04cae7e21, type: 3} - m_Name: PointSymShape 1a Pentagon + m_Name: Pentagon m_EditorClassIdentifier: Operators: - Active: 0 diff --git a/Assets/Presets - Multi/Components/PointSymShape 1a Pentagon.asset.meta b/Assets/Presets - Multi/PointSym 1/Pentagon.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/PointSymShape 1a Pentagon.asset.meta rename to Assets/Presets - Multi/PointSym 1/Pentagon.asset.meta diff --git a/Assets/Presets - Multi/PointSymShapeSettings 1.asset b/Assets/Presets - Multi/PointSym 1/PointSymShapeSettings 1.asset similarity index 100% rename from Assets/Presets - Multi/PointSymShapeSettings 1.asset rename to Assets/Presets - Multi/PointSym 1/PointSymShapeSettings 1.asset diff --git a/Assets/Presets - Multi/PointSymShapeSettings 1.asset.meta b/Assets/Presets - Multi/PointSym 1/PointSymShapeSettings 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/PointSymShapeSettings 1.asset.meta rename to Assets/Presets - Multi/PointSym 1/PointSymShapeSettings 1.asset.meta diff --git a/Assets/Presets - Multi/PointSym 2.meta b/Assets/Presets - Multi/PointSym 2.meta new file mode 100644 index 0000000..c44681d --- /dev/null +++ b/Assets/Presets - Multi/PointSym 2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ad52d4b79ede4746aff2efefe69ab92 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/PointSymShapeSettings 2.asset b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings 2.asset similarity index 100% rename from Assets/Presets - Multi/PointSymShapeSettings 2.asset rename to Assets/Presets - Multi/PointSym 2/PointSymShapeSettings 2.asset diff --git a/Assets/Presets - Multi/PointSymShapeSettings 2.asset.meta b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings 2.asset.meta similarity index 100% rename from Assets/Presets - Multi/PointSymShapeSettings 2.asset.meta rename to Assets/Presets - Multi/PointSym 2/PointSymShapeSettings 2.asset.meta diff --git a/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset new file mode 100644 index 0000000..efb74d7 --- /dev/null +++ b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2e9a3bf926d4def994fb139a6407ded, type: 3} + m_Name: PointSymShapeSettings + m_EditorClassIdentifier: + Operators: + - Active: 1 + OpType: 110 + Parameter1: 0.07 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 1 + OpType: 32 + Parameter1: 0.78 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 1 + OpType: 19 + Parameter1: 0.4 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 1 + OpType: 40 + Parameter1: 0.9 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 1 + OpType: 39 + Parameter1: 0.005 + Parameter2: 5.28 + Iterations: 1 + Parameter1Randomize: 1 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + - Active: 0 + OpType: 37 + Parameter1: 0.03 + Parameter2: 0 + Iterations: 1 + Parameter1Randomize: 0 + Parameter2Randomize: 0 + FilterType: 0 + FilterParam: 0 + FilterFlip: 0 + SpherizeAmount: 0 + FastConicalize: 1 + CanonicalizeIterations: 0 + PlanarizeIterations: 0 + FaceScale: 1 + FaceExtrude: 0 + FaceInset: 0 + ShapeSettings: {fileID: 11400000, guid: 5c04e85b2d7bc1d48a56a5152ad3d542, type: 2} + Family: 3 + Transform: + Translation: {x: 0, y: 0.015, z: 1.71} + Rotation: {x: 0, y: 0, z: 0} + Scale: 0.34 + NonUniformScale: {x: 1, y: 1, z: 1} + Repeats: 6 + Radius: 1.55 diff --git a/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset.meta b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset.meta new file mode 100644 index 0000000..c3e541d --- /dev/null +++ b/Assets/Presets - Multi/PointSym 2/PointSymShapeSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 95d09e17ecb074484a636fc6373e9497 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/PointSymShape 2 Square.asset b/Assets/Presets - Multi/PointSym 2/Square.asset similarity index 94% rename from Assets/Presets - Multi/Components/PointSymShape 2 Square.asset rename to Assets/Presets - Multi/PointSym 2/Square.asset index 43e40f7..743d4a8 100644 --- a/Assets/Presets - Multi/Components/PointSymShape 2 Square.asset +++ b/Assets/Presets - Multi/PointSym 2/Square.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2c2880ed5b584425b92968f04cae7e21, type: 3} - m_Name: PointSymShape 2 Square + m_Name: Square m_EditorClassIdentifier: Operators: [] SpherizeAmount: 0 diff --git a/Assets/Presets - Multi/Components/PointSymShape 2 Square.asset.meta b/Assets/Presets - Multi/PointSym 2/Square.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/PointSymShape 2 Square.asset.meta rename to Assets/Presets - Multi/PointSym 2/Square.asset.meta diff --git a/Assets/Presets - Multi/WallpaperSym 1.meta b/Assets/Presets - Multi/WallpaperSym 1.meta new file mode 100644 index 0000000..e911515 --- /dev/null +++ b/Assets/Presets - Multi/WallpaperSym 1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 44aa5dbdb4e5146f2a5a3256a83c37b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Presets - Multi/Components/WallpaperSymShape 1 Grid.asset b/Assets/Presets - Multi/WallpaperSym 1/Grid.asset similarity index 96% rename from Assets/Presets - Multi/Components/WallpaperSymShape 1 Grid.asset rename to Assets/Presets - Multi/WallpaperSym 1/Grid.asset index de7305f..66f5cb0 100644 --- a/Assets/Presets - Multi/Components/WallpaperSymShape 1 Grid.asset +++ b/Assets/Presets - Multi/WallpaperSym 1/Grid.asset @@ -10,7 +10,7 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5bf388923ca042dbaaa277a440745831, type: 3} - m_Name: WallpaperSymShape 1 Grid + m_Name: Grid m_EditorClassIdentifier: Operators: - Active: 1 diff --git a/Assets/Presets - Multi/Components/WallpaperSymShape 1 Grid.asset.meta b/Assets/Presets - Multi/WallpaperSym 1/Grid.asset.meta similarity index 100% rename from Assets/Presets - Multi/Components/WallpaperSymShape 1 Grid.asset.meta rename to Assets/Presets - Multi/WallpaperSym 1/Grid.asset.meta diff --git a/Assets/Presets - Multi/WallpaperSymShapeSettings 1.asset b/Assets/Presets - Multi/WallpaperSym 1/WallpaperSymShapeSettings 1.asset similarity index 100% rename from Assets/Presets - Multi/WallpaperSymShapeSettings 1.asset rename to Assets/Presets - Multi/WallpaperSym 1/WallpaperSymShapeSettings 1.asset diff --git a/Assets/Presets - Multi/WallpaperSymShapeSettings 1.asset.meta b/Assets/Presets - Multi/WallpaperSym 1/WallpaperSymShapeSettings 1.asset.meta similarity index 100% rename from Assets/Presets - Multi/WallpaperSymShapeSettings 1.asset.meta rename to Assets/Presets - Multi/WallpaperSym 1/WallpaperSymShapeSettings 1.asset.meta diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs index 3a1fb53..e77daed 100644 --- a/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs +++ b/Assets/Scripts/Geometry Preset Classes/Multi/CsgShapeSettings.cs @@ -11,15 +11,11 @@ public class CsgShapeSettings : BaseSettings public BaseSettings Operand1; public BaseSettings Operand2; public PolyTransform Operand2Transform; + public float WeldThreshold = 0; + public float MergeThreshold = 0; public override PolyMesh BuildBaseShape() - { - return null; - } - - public override Mesh BuildAll(AppearanceSettings appearanceSettings) - { - PolyMesh finalPoly = new PolyMesh(); + { PolyMesh finalPoly = new PolyMesh(); var poly1 = Operand1.BuildBaseShape(); poly1 = Operand1.ApplyModifiers(poly1); var poly2 = Operand2.BuildBaseShape(); @@ -29,9 +25,15 @@ public override Mesh BuildAll(AppearanceSettings appearanceSettings) Operand2Transform.Rotation, Operand2Transform.NonUniformScale * Operand2Transform.Scale ); - finalPoly = poly1.ApplyCsg(poly2, csgOp); + finalPoly = poly1.ApplyCsg(poly2, csgOp, WeldThreshold, MergeThreshold); finalPoly = ApplyModifiers(finalPoly); + + return finalPoly; + } + public override Mesh BuildAll(AppearanceSettings appearanceSettings) + { + var finalPoly = BuildBaseShape(); var meshData = finalPoly.BuildMeshData( colorMethod: GetColorMethod(appearanceSettings), colors: CalculateColorList(appearanceSettings) diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/LayeredShapeSettings.cs b/Assets/Scripts/Geometry Preset Classes/Multi/LayeredShapeSettings.cs index 50d8d86..0807209 100644 --- a/Assets/Scripts/Geometry Preset Classes/Multi/LayeredShapeSettings.cs +++ b/Assets/Scripts/Geometry Preset Classes/Multi/LayeredShapeSettings.cs @@ -35,11 +35,6 @@ public class ShapeLayer public List Layers; public override PolyMesh BuildBaseShape() - { - return null; - } - - public override Mesh BuildAll(AppearanceSettings appearanceSettings) { PolyMesh finalPoly = new PolyMesh(); for (var i = 0; i < Layers.Count; i++) @@ -61,6 +56,13 @@ public override Mesh BuildAll(AppearanceSettings appearanceSettings) } finalPoly = ApplyModifiers(finalPoly); + return finalPoly; + + } + + public override Mesh BuildAll(AppearanceSettings appearanceSettings) + { + var finalPoly = BuildBaseShape(); var meshData = finalPoly.BuildMeshData( colorMethod: GetColorMethod(appearanceSettings), colors: CalculateColorList(appearanceSettings) diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/PointSymShapeSettings.cs b/Assets/Scripts/Geometry Preset Classes/Multi/PointSymShapeSettings.cs index b501914..9118e72 100644 --- a/Assets/Scripts/Geometry Preset Classes/Multi/PointSymShapeSettings.cs +++ b/Assets/Scripts/Geometry Preset Classes/Multi/PointSymShapeSettings.cs @@ -13,11 +13,6 @@ public class PointSymShapeSettings : BaseSettings public float Radius = 1f; public override PolyMesh BuildBaseShape() - { - return null; - } - - public override Mesh BuildAll(AppearanceSettings appearanceSettings) { PolyMesh finalPoly = new PolyMesh(); var poly = ShapeSettings.BuildBaseShape(); @@ -31,6 +26,12 @@ public override Mesh BuildAll(AppearanceSettings appearanceSettings) } finalPoly = ApplyModifiers(finalPoly); + return finalPoly; + } + + public override Mesh BuildAll(AppearanceSettings appearanceSettings) + { + var finalPoly = BuildBaseShape(); var meshData = finalPoly.BuildMeshData( colorMethod: GetColorMethod(appearanceSettings), colors: CalculateColorList(appearanceSettings) diff --git a/Assets/Scripts/Geometry Preset Classes/Multi/WallpaperSymShapeSettings.cs b/Assets/Scripts/Geometry Preset Classes/Multi/WallpaperSymShapeSettings.cs index 2cb934c..ac38fe9 100644 --- a/Assets/Scripts/Geometry Preset Classes/Multi/WallpaperSymShapeSettings.cs +++ b/Assets/Scripts/Geometry Preset Classes/Multi/WallpaperSymShapeSettings.cs @@ -15,11 +15,6 @@ public class WallpaperSymShapeSettings : BaseSettings public Vector2 Skew; public override PolyMesh BuildBaseShape() - { - return null; - } - - public override Mesh BuildAll(AppearanceSettings appearanceSettings) { PolyMesh finalPoly = new PolyMesh(); var poly = ShapeSettings.BuildBaseShape(); @@ -37,7 +32,13 @@ public override Mesh BuildAll(AppearanceSettings appearanceSettings) finalPoly.Append(poly.Duplicate(mat)); } finalPoly = ApplyModifiers(finalPoly); + return finalPoly; + } + + public override Mesh BuildAll(AppearanceSettings appearanceSettings) + { + var finalPoly = BuildBaseShape(); var meshData = finalPoly.BuildMeshData( colorMethod: GetColorMethod(appearanceSettings), colors: CalculateColorList(appearanceSettings) diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs index 85e3684..7df44de 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.Geometry.cs @@ -88,6 +88,45 @@ public PolyMesh Stack(Vector3 axis, float offset, float scale, Filter filter = d return original; } + public PolyMesh Translate(OpParams o, int axis) + { + PolyMesh poly; + + if (o.filter == null) + { + poly = this; + } + else + { + poly = FaceRemove(new OpParams(o.filter), true); + } + + switch (axis) + { + case 0: + poly.Transform(new Vector3(o.OriginalParamA, 1, 1)); + break; + case 1: + poly.Transform(new Vector3(1, o.OriginalParamA, 1)); + break; + case 2: + poly.Transform(new Vector3(1, 1, o.OriginalParamA)); + break; + } + + PolyMesh result; + if (o.filter == null) + { + result = poly; + } + else + { + result = FaceRemove(new OpParams(o.filter), false); + result.Append(poly); + } + return result; + } + public PolyMesh Scale(OpParams o, int axis) { PolyMesh poly; @@ -3381,7 +3420,7 @@ public enum CsgOp } - public PolyMesh ApplyCsg(PolyMesh other, CsgOp op) + public PolyMesh ApplyCsg(PolyMesh other, CsgOp op, float weldThreshold = 0, float mergeThreshold = 0) { var bounds = GetBounds(); bounds.Encapsulate(other.GetBounds()); @@ -3417,8 +3456,8 @@ public PolyMesh ApplyCsg(PolyMesh other, CsgOp op) newFaces.Add(face); } var result = new PolyMesh(newVerts, newFaces); - //result.Weld(0.01f); - result.MergeCoplanarFaces(0.01f); + if (weldThreshold > 0) result = result.Weld(weldThreshold); + if (mergeThreshold > 0) result.MergeCoplanarFaces(mergeThreshold); return result; } diff --git a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.cs b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.cs index a3126fd..fe2fa58 100644 --- a/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.cs +++ b/Packages/Polyhydra/com.ixxy.polyhydra/Core/Runtime/PolyMesh.cs @@ -1313,6 +1313,9 @@ public enum Operation // Object Transforms + TranslateX = 116, + TranslateY = 117, + TranslateZ = 118, ScaleX = 98, ScaleY = 99, ScaleZ = 100, @@ -1562,6 +1565,16 @@ public PolyMesh AppyOperation(Operation op, OpParams p) // Object Transforms + case Operation.TranslateX: + polyMesh = polyMesh.Translate(p, (int)Axis.X); + break; + case Operation.TranslateY: + polyMesh = polyMesh.Translate(p, (int)Axis.Y); + break; + case Operation.TranslateZ: + polyMesh = polyMesh.Translate(p, (int)Axis.Z); + break; + case Operation.ScaleX: polyMesh = polyMesh.Scale(p, (int)Axis.X); break;