diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5a2b79b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Sebastian Lague + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/PathCreator/Core.meta b/PathCreator/Core.meta new file mode 100644 index 0000000..adea8be --- /dev/null +++ b/PathCreator/Core.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fded9f896ea32473eb30ae61b640a176 +folderAsset: yes +timeCreated: 1519645413 +licenseType: Pro +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor.meta b/PathCreator/Core/Editor.meta new file mode 100644 index 0000000..081c996 --- /dev/null +++ b/PathCreator/Core/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76c2f3171eae9954aa317277dbffd478 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor/Helper.meta b/PathCreator/Core/Editor/Helper.meta new file mode 100644 index 0000000..c7dd6da --- /dev/null +++ b/PathCreator/Core/Editor/Helper.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 366e41d4e3408334aad31e48f28757e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor/Helper/MouseUtility.cs b/PathCreator/Core/Editor/Helper/MouseUtility.cs new file mode 100644 index 0000000..0ae6b33 --- /dev/null +++ b/PathCreator/Core/Editor/Helper/MouseUtility.cs @@ -0,0 +1,43 @@ +using UnityEngine; +using UnityEditor; +using PathCreation; + +namespace PathCreationEditor +{ + public static class MouseUtility + { + /// + /// Determines mouse position in world. If PathSpace is xy/xz, the position will be locked to that plane. + /// If PathSpace is xyz, then depthFor3DSpace will be used as distance from scene camera. + /// + public static Vector3 GetMouseWorldPosition(PathSpace space, float depthFor3DSpace = 10) + { + Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); + Vector3 worldMouse = mouseRay.GetPoint(depthFor3DSpace); + + // Mouse can only move on XY plane + if (space == PathSpace.xy) + { + float zDir = mouseRay.direction.z; + if (zDir != 0) + { + float dstToXYPlane = Mathf.Abs(mouseRay.origin.z / zDir); + worldMouse = mouseRay.GetPoint(dstToXYPlane); + } + } + // Mouse can only move on XZ plane + else if (space == PathSpace.xz) + { + float yDir = mouseRay.direction.y; + if (yDir != 0) + { + float dstToXZPlane = Mathf.Abs(mouseRay.origin.y / yDir); + worldMouse = mouseRay.GetPoint(dstToXZPlane); + } + } + + return worldMouse; + } + + } +} \ No newline at end of file diff --git a/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta b/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta new file mode 100644 index 0000000..4b5ec6b --- /dev/null +++ b/PathCreator/Core/Editor/Helper/MouseUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd0de63ca5c52504a8d2063418a326c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor/Helper/PathHandle.cs b/PathCreator/Core/Editor/Helper/PathHandle.cs new file mode 100644 index 0000000..352b764 --- /dev/null +++ b/PathCreator/Core/Editor/Helper/PathHandle.cs @@ -0,0 +1,218 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using PathCreation; + +namespace PathCreationEditor +{ + public static class PathHandle + { + + public const float extraInputRadius = .005f; + + static Vector2 handleDragMouseStart; + static Vector2 handleDragMouseEnd; + static Vector3 handleDragWorldStart; + + static int selectedHandleID; + static bool mouseIsOverAHandle; + + public enum HandleInputType + { + None, + LMBPress, + LMBClick, + LMBDrag, + LMBRelease, + }; + + static readonly int hint; + static float dstMouseToDragPointStart; + + static List ids; + + static PathHandle() + { + hint = 109264; + ids = new List(); + + dstMouseToDragPointStart = float.MaxValue; + } + + public static Vector3 DrawHandle(Vector3 position, PathSpace space, bool isInteractive, float handleDiameter, Handles.CapFunction capFunc, HandleColours colours, out HandleInputType inputType, int handleIndex) + { + int id = GetID(handleIndex); + Vector3 screenPosition = Handles.matrix.MultiplyPoint(position); + Matrix4x4 cachedMatrix = Handles.matrix; + + inputType = HandleInputType.None; + + EventType eventType = Event.current.GetTypeForControl(id); + float handleRadius = handleDiameter / 2f; + float dstToHandle = HandleUtility.DistanceToCircle(position, handleRadius + extraInputRadius); + float dstToMouse = HandleUtility.DistanceToCircle(position, 0); + + // Handle input events + if (isInteractive) + { + // Repaint if mouse is entering/exiting handle (for highlight colour) + if (dstToHandle == 0) + { + if (!mouseIsOverAHandle) + { + HandleUtility.Repaint(); + mouseIsOverAHandle = true; + } + } + else + { + if (mouseIsOverAHandle) + { + HandleUtility.Repaint(); + mouseIsOverAHandle = false; + } + } + switch (eventType) + { + case EventType.MouseDown: + if (Event.current.button == 0 && Event.current.modifiers != EventModifiers.Alt) + { + if (dstToHandle == 0 && dstToMouse < dstMouseToDragPointStart) + { + dstMouseToDragPointStart = dstToMouse; + GUIUtility.hotControl = id; + handleDragMouseEnd = handleDragMouseStart = Event.current.mousePosition; + handleDragWorldStart = position; + selectedHandleID = id; + inputType = HandleInputType.LMBPress; + } + } + break; + + case EventType.MouseUp: + dstMouseToDragPointStart = float.MaxValue; + if (GUIUtility.hotControl == id && Event.current.button == 0) + { + GUIUtility.hotControl = 0; + selectedHandleID = -1; + Event.current.Use(); + + inputType = HandleInputType.LMBRelease; + + + if (Event.current.mousePosition == handleDragMouseStart) + { + inputType = HandleInputType.LMBClick; + } + } + break; + + case EventType.MouseDrag: + if (GUIUtility.hotControl == id && Event.current.button == 0) + { + handleDragMouseEnd += new Vector2(Event.current.delta.x, -Event.current.delta.y); + Vector3 position2 = Camera.current.WorldToScreenPoint(Handles.matrix.MultiplyPoint(handleDragWorldStart)) + + (Vector3)(handleDragMouseEnd - handleDragMouseStart); + inputType = HandleInputType.LMBDrag; + // Handle can move freely in 3d space + if (space == PathSpace.xyz) + { + position = Handles.matrix.inverse.MultiplyPoint(Camera.current.ScreenToWorldPoint(position2)); + } + // Handle is clamped to xy or xz plane + else + { + position = MouseUtility.GetMouseWorldPosition(space); + } + + GUI.changed = true; + Event.current.Use(); + } + break; + } + } + + switch (eventType) + { + case EventType.Repaint: + Color originalColour = Handles.color; + Handles.color = (isInteractive) ? colours.defaultColour : colours.disabledColour; + + if (id == GUIUtility.hotControl) + { + Handles.color = colours.selectedColour; + } + else if (dstToHandle == 0 && selectedHandleID == -1 && isInteractive) + { + Handles.color = colours.highlightedColour; + } + + + Handles.matrix = Matrix4x4.identity; + Vector3 lookForward = Vector3.up; + Camera cam = Camera.current; + if (cam != null) + { + if (cam.orthographic) + { + lookForward= -cam.transform.forward; + } + else + { + lookForward = (cam.transform.position - position); + } + } + + capFunc(id, screenPosition, Quaternion.LookRotation(lookForward), handleDiameter, EventType.Repaint); + Handles.matrix = cachedMatrix; + + Handles.color = originalColour; + break; + + case EventType.Layout: + Handles.matrix = Matrix4x4.identity; + HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(screenPosition, handleDiameter / 2f)); + Handles.matrix = cachedMatrix; + break; + } + + return position; + } + + public struct HandleColours + { + public Color defaultColour; + public Color highlightedColour; + public Color selectedColour; + public Color disabledColour; + + public HandleColours(Color defaultColour, Color highlightedColour, Color selectedColour, Color disabledColour) + { + this.defaultColour = defaultColour; + this.highlightedColour = highlightedColour; + this.selectedColour = selectedColour; + this.disabledColour = disabledColour; + } + } + + static void AddIDs(int upToIndex) + { + int numIDAtStart = ids.Count; + int numToAdd = (upToIndex - numIDAtStart) + 1; + for (int i = 0; i < numToAdd; i++) + { + ids.Add(GUIUtility.GetControlID(hint + numIDAtStart + i, FocusType.Passive));// + } + } + + static int GetID(int handleIndex) + { + if (handleIndex >= ids.Count) + { + AddIDs(handleIndex); + } + + return ids[handleIndex]; + } + } +} \ No newline at end of file diff --git a/PathCreator/Core/Editor/Helper/PathHandle.cs.meta b/PathCreator/Core/Editor/Helper/PathHandle.cs.meta new file mode 100644 index 0000000..4dee2f9 --- /dev/null +++ b/PathCreator/Core/Editor/Helper/PathHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 804ddb43548540a4ca3b0a534a75c4b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs b/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs new file mode 100644 index 0000000..14c5bc0 --- /dev/null +++ b/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using PathCreation; +using PathCreation.Utility; + +namespace PathCreationEditor +{ + public class ScreenSpacePolyLine + { + const int accuracyMultiplier = 10; + // dont allow vertices to be spaced too far apart, as screenspace-worldspace conversion can then be noticeably off + const float intermediaryThreshold = .2f; + + public readonly List verticesWorld; + // For each point in the polyline, says which bezier segment it belongs to + readonly List vertexToPathSegmentMap; + // Stores the index in the vertices list where the start point of each segment is + readonly int[] segmentStartIndices; + + readonly float pathLengthWorld; + readonly float[] cumululativeLengthWorld; + + Vector2[] points; + + Vector3 prevCamPos; + Quaternion prevCamRot; + bool premCamIsOrtho; + + public ScreenSpacePolyLine(BezierPath bezierPath, float maxAngleError, float minVertexDst, float accuracy = 1) + { + // Split path in vertices based on angle error + verticesWorld = new List(); + vertexToPathSegmentMap = new List(); + segmentStartIndices = new int[bezierPath.NumSegments+1]; + + verticesWorld.Add(bezierPath[0]); + vertexToPathSegmentMap.Add(0); + Vector3 prevPointOnPath = bezierPath[0]; + float dstSinceLastVertex = 0; + Vector3 lastAddedPoint = prevPointOnPath; + float dstSinceLastIntermediary = 0; + + for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) + { + Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); + verticesWorld.Add(segmentPoints[0]); + vertexToPathSegmentMap.Add(segmentIndex); + segmentStartIndices[segmentIndex] = verticesWorld.Count-1; + + prevPointOnPath = segmentPoints[0]; + lastAddedPoint = prevPointOnPath; + dstSinceLastVertex = 0; + dstSinceLastIntermediary = 0; + + float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); + int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * accuracyMultiplier); + float increment = 1f / divisions; + + for (float t = increment; t <= 1; t += increment) + { + Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t); + Vector3 nextPointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment); + + // angle at current point on path + float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); + // angle between the last added vertex, the current point on the path, and the next point on the path + float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); + float angleError = Mathf.Max(localAngle, angleFromPrevVertex); + + + if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) + { + dstSinceLastVertex = 0; + dstSinceLastIntermediary = 0; + verticesWorld.Add(pointOnPath); + vertexToPathSegmentMap.Add(segmentIndex); + lastAddedPoint = pointOnPath; + } + else + { + if (dstSinceLastIntermediary > intermediaryThreshold) + { + verticesWorld.Add(pointOnPath); + vertexToPathSegmentMap.Add(segmentIndex); + dstSinceLastIntermediary = 0; + } + else + { + dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude; + } + dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; + } + prevPointOnPath = pointOnPath; + } + } + + segmentStartIndices[bezierPath.NumSegments] = verticesWorld.Count; + + // ensure final point gets added (unless path is closed loop) + if (!bezierPath.IsClosed) + { + verticesWorld.Add(bezierPath[bezierPath.NumPoints - 1]); + } + else + { + verticesWorld.Add(bezierPath[0]); + } + + // Calculate length + cumululativeLengthWorld = new float[verticesWorld.Count]; + for (int i = 1; i < verticesWorld.Count; i++) + { + pathLengthWorld += (verticesWorld[i - 1] - verticesWorld[i]).magnitude; + cumululativeLengthWorld[i] = pathLengthWorld; + } + + } + + void ComputeScreenSpace() + { + if (Camera.current.transform.position != prevCamPos || Camera.current.transform.rotation != prevCamRot || Camera.current.orthographic != premCamIsOrtho) + { + points = new Vector2[verticesWorld.Count]; + for (int i = 0; i < verticesWorld.Count; i++) + { + points[i] = HandleUtility.WorldToGUIPoint(verticesWorld[i]); + } + + prevCamPos = Camera.current.transform.position; + prevCamRot = Camera.current.transform.rotation; + premCamIsOrtho = Camera.current.orthographic; + } + } + + public MouseInfo CalculateMouseInfo() + { + ComputeScreenSpace(); + + Vector2 mousePos = Event.current.mousePosition; + float minDst = float.MaxValue; + int closestPolyLineSegmentIndex = 0; + int closestBezierSegmentIndex = 0; + + for (int i = 0; i < points.Length - 1; i++) + { + float dst = HandleUtility.DistancePointToLineSegment(mousePos, points[i], points[i + 1]); + + if (dst < minDst) + { + minDst = dst; + closestPolyLineSegmentIndex = i; + closestBezierSegmentIndex = vertexToPathSegmentMap[i]; + } + } + + Vector2 closestPointOnLine = MathUtility.ClosestPointOnLineSegment(mousePos, points[closestPolyLineSegmentIndex], points[closestPolyLineSegmentIndex + 1]); + float dstToPointOnLine = (points[closestPolyLineSegmentIndex] - closestPointOnLine).magnitude; + float percentBetweenVertices = dstToPointOnLine / (points[closestPolyLineSegmentIndex] - points[closestPolyLineSegmentIndex + 1]).magnitude; + Vector3 closestPoint3D = Vector3.Lerp(verticesWorld[closestPolyLineSegmentIndex], verticesWorld[closestPolyLineSegmentIndex + 1], percentBetweenVertices); + + float distanceAlongPathWorld = cumululativeLengthWorld[closestPolyLineSegmentIndex] + Vector3.Distance(verticesWorld[closestPolyLineSegmentIndex], closestPoint3D); + float timeAlongPath = distanceAlongPathWorld / pathLengthWorld; + + // Calculate how far between the current bezier segment the closest point on the line is + + int bezierSegmentStartIndex = segmentStartIndices[closestBezierSegmentIndex]; + int bezierSegmentEndIndex = segmentStartIndices[closestBezierSegmentIndex+1]; + float bezierSegmentLength = cumululativeLengthWorld[bezierSegmentEndIndex] - cumululativeLengthWorld[bezierSegmentStartIndex]; + float distanceAlongBezierSegment = distanceAlongPathWorld - cumululativeLengthWorld[bezierSegmentStartIndex]; + float timeAlongBezierSegment = distanceAlongBezierSegment/bezierSegmentLength; + + return new MouseInfo(minDst, closestPoint3D, distanceAlongPathWorld, timeAlongPath, timeAlongBezierSegment, closestBezierSegmentIndex); + } + + + public struct MouseInfo + { + public readonly float mouseDstToLine; + public readonly Vector3 closestWorldPointToMouse; + public readonly float distanceAlongPathWorld; + public readonly float timeOnPath; + public readonly float timeOnBezierSegment; + public readonly int closestSegmentIndex; + + + public MouseInfo(float mouseDstToLine, Vector3 closestWorldPointToMouse, float distanceAlongPathWorld, float timeOnPath, float timeOnBezierSegment, int closestSegmentIndex) + { + this.mouseDstToLine = mouseDstToLine; + this.closestWorldPointToMouse = closestWorldPointToMouse; + this.distanceAlongPathWorld = distanceAlongPathWorld; + this.timeOnPath = timeOnPath; + this.timeOnBezierSegment = timeOnBezierSegment; + this.closestSegmentIndex = closestSegmentIndex; + } + } + } +} \ No newline at end of file diff --git a/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta b/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta new file mode 100644 index 0000000..d860ffe --- /dev/null +++ b/PathCreator/Core/Editor/Helper/ScreenSpacePolyLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05aed72a972059e45a62e31c2bb60fc4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Editor/PathEditor.cs b/PathCreator/Core/Editor/PathEditor.cs new file mode 100644 index 0000000..e7b545d --- /dev/null +++ b/PathCreator/Core/Editor/PathEditor.cs @@ -0,0 +1,834 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using PathCreation; +using PathCreation.Utility; +using UnityEditor.IMGUI.Controls; + +namespace PathCreationEditor +{ + /// Editor class for the creation of Bezier and Vertex paths + + [CustomEditor(typeof(PathCreator))] + public class PathEditor : Editor + { + + #region Fields + + // Interaction: + const float segmentSelectDistanceThreshold = 10f; + const float screenPolylineMaxAngleError = .3f; + const float screenPolylineMinVertexDst = .01f; + bool shareTransformsWithPath = false; // Should changes to pathcreator's transform affect the path (and vice versa) + + // Help messages: + const string helpInfo = "Shift-click to add or insert new points. Control-click to delete points. For more detailed infomation, please refer to the documentation."; + static readonly string[] spaceNames = { "3D (xyz)", "2D (xy)", "Top-down (xz)" }; + static readonly string[] tabNames = { "Bézier Path", "Vertex Path" }; + const string constantSizeTooltip = "If true, anchor and control points will keep a constant size when zooming in the editor."; + + // Display + const int inspectorSectionSpacing = 10; + const float constantHandleScale = .01f; + const float normalsSpacing = .1f; + GUIStyle boldFoldoutStyle; + + // References: + PathCreator creator; + Editor globalDisplaySettingsEditor; + ScreenSpacePolyLine screenSpaceLine; + ScreenSpacePolyLine.MouseInfo pathMouseInfo; + GlobalDisplaySettings globalDisplaySettings; + PathHandle.HandleColours splineAnchorColours; + PathHandle.HandleColours splineControlColours; + Dictionary capFunctions; + ArcHandle anchorAngleHandle = new ArcHandle(); + VertexPath normalsVertexPath; + + // State variables: + int selectedSegmentIndex; + int draggingHandleIndex; + int mouseOverHandleIndex; + int handleIndexToDisplayAsTransform; + + bool shiftLastFrame; + bool hasUpdatedScreenSpaceLine; + bool hasUpdatedNormalsVertexPath; + bool editingNormalsOld; + + Vector3 positionOld; + Quaternion rotationOld; + Vector3 scaleOld; + Quaternion currentHandleRot = Quaternion.identity; + Color handlesStartCol; + + // Constants + const int bezierPathTab = 0; + const int vertexPathTab = 1; + + #endregion + + #region Inspectors + + public override void OnInspectorGUI() + { + // Initialize GUI styles + if (boldFoldoutStyle == null) + { + boldFoldoutStyle = new GUIStyle(EditorStyles.foldout); + boldFoldoutStyle.fontStyle = FontStyle.Bold; + } + + Undo.RecordObject(creator, "Path settings changed"); + + // Draw Bezier and Vertex tabs + int tabIndex = GUILayout.Toolbar(data.tabIndex, tabNames); + if (tabIndex != data.tabIndex) + { + data.tabIndex = tabIndex; + TabChanged(); + } + + // Draw inspector for active tab + switch (data.tabIndex) + { + case bezierPathTab: + DrawBezierPathInspector(); + break; + case vertexPathTab: + DrawVertexPathInspector(); + break; + } + + // Notify of undo/redo that might modify the path + if (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") + { + data.PathModifiedByUndo(); + } + + // Update visibility of default transform tool + UpdateToolVisibility(); + } + + void DrawBezierPathInspector() + { + + using (var check = new EditorGUI.ChangeCheckScope()) + { + // Path options: + data.showPathOptions = EditorGUILayout.Foldout(data.showPathOptions, new GUIContent("Bézier Path Options"), true, boldFoldoutStyle); + if (data.showPathOptions) + { + bezierPath.Space = (PathSpace)EditorGUILayout.Popup("Space", (int)bezierPath.Space, spaceNames); + bezierPath.ControlPointMode = (BezierPath.ControlMode)EditorGUILayout.EnumPopup(new GUIContent("Control Mode"), bezierPath.ControlPointMode); + if (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) + { + bezierPath.AutoControlLength = EditorGUILayout.Slider(new GUIContent("Control Spacing"), bezierPath.AutoControlLength, 0, 1); + } + + bezierPath.IsClosed = EditorGUILayout.Toggle("Closed Path", bezierPath.IsClosed); + data.pathTransformationEnabled = EditorGUILayout.Toggle(new GUIContent("Enable Transforms"), data.pathTransformationEnabled); + + if (GUILayout.Button("Reset Path")) + { + Undo.RecordObject(creator, "Reset Path"); + bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; + data.ResetBezierPath(creator.transform.position, in2DEditorMode); + } + + GUILayout.Space(inspectorSectionSpacing); + } + + + data.showNormals = EditorGUILayout.Foldout(data.showNormals, new GUIContent("Normals Options"), true, boldFoldoutStyle); + if (data.showNormals) + { + bezierPath.FlipNormals = EditorGUILayout.Toggle(new GUIContent("Flip Normals"), bezierPath.FlipNormals); + if (bezierPath.Space == PathSpace.xyz) + { + bezierPath.GlobalNormalsAngle = EditorGUILayout.Slider(new GUIContent("Global Angle"), bezierPath.GlobalNormalsAngle, 0, 360); + + if (GUILayout.Button("Reset Normals")) + { + Undo.RecordObject(creator, "Reset Normals"); + bezierPath.FlipNormals = false; + bezierPath.ResetNormalAngles(); + } + } + GUILayout.Space(inspectorSectionSpacing); + } + + // Editor display options + data.showDisplayOptions = EditorGUILayout.Foldout(data.showDisplayOptions, new GUIContent("Display Options"), true, boldFoldoutStyle); + if (data.showDisplayOptions) + { + data.showPathBounds = GUILayout.Toggle(data.showPathBounds, new GUIContent("Show Path Bounds")); + data.showPerSegmentBounds = GUILayout.Toggle(data.showPerSegmentBounds, new GUIContent("Show Segment Bounds")); + data.displayAnchorPoints = GUILayout.Toggle(data.displayAnchorPoints, new GUIContent("Show Anchor Points")); + if (!(bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic && globalDisplaySettings.hideAutoControls)) + { + data.displayControlPoints = GUILayout.Toggle(data.displayControlPoints, new GUIContent("Show Control Points")); + } + data.keepConstantHandleSize = GUILayout.Toggle(data.keepConstantHandleSize, new GUIContent("Constant Point Size", constantSizeTooltip)); + data.bezierHandleScale = Mathf.Max(0, EditorGUILayout.FloatField(new GUIContent("Handle Scale"), data.bezierHandleScale)); + DrawGlobalDisplaySettingsInspector(); + } + + if (check.changed) + { + SceneView.RepaintAll(); + } + } + } + + void DrawVertexPathInspector() + { + data.showVertexPathOptions = EditorGUILayout.Foldout(data.showVertexPathOptions, new GUIContent("Vertex Path Options"), true, boldFoldoutStyle); + if (data.showVertexPathOptions) + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.vertexPathMaxAngleError = EditorGUILayout.Slider(new GUIContent("Max Angle Error"), data.vertexPathMaxAngleError, 0, 45); + data.vertexPathMinVertexSpacing = EditorGUILayout.Slider(new GUIContent("Min Vertex Dst"), data.vertexPathMinVertexSpacing, 0, 1); + + GUILayout.Space(inspectorSectionSpacing); + if (check.changed) + { + data.VertexPathSettingsChanged(); + SceneView.RepaintAll(); + } + } + } + + data.showVertexPathDisplayOptions = EditorGUILayout.Foldout(data.showVertexPathDisplayOptions, new GUIContent("Display Options"), true, boldFoldoutStyle); + if (data.showVertexPathDisplayOptions) + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.vertexHandleSize = EditorGUILayout.Slider(new GUIContent("Vertex Scale"), data.vertexHandleSize, 0, 1); + data.showNormalsInVertexMode = GUILayout.Toggle(data.showNormalsInVertexMode, new GUIContent("Show Normals")); + + if (check.changed) + { + SceneView.RepaintAll(); + } + } + DrawGlobalDisplaySettingsInspector(); + } + } + + void DrawGlobalDisplaySettingsInspector() + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + data.globalDisplaySettingsFoldout = EditorGUILayout.InspectorTitlebar(data.globalDisplaySettingsFoldout, globalDisplaySettings); + if (data.globalDisplaySettingsFoldout) + { + CreateCachedEditor(globalDisplaySettings, null, ref globalDisplaySettingsEditor); + globalDisplaySettingsEditor.OnInspectorGUI(); + } + if (check.changed) + { + UpdateGlobalDisplaySettings(); + SceneView.RepaintAll(); + } + } + } + + #endregion + + #region Scene GUI + + void OnSceneGUI() + { + handlesStartCol = Handles.color; + switch (data.tabIndex) + { + case bezierPathTab: + ProcessBezierPathInput(Event.current); + DrawBezierPathSceneEditor(); + break; + case vertexPathTab: + DrawVertexPathSceneEditor(); + break; + } + + + // Don't allow clicking over empty space to deselect the object + if (Event.current.type == EventType.Layout) + { + HandleUtility.AddDefaultControl(0); + } + } + + void DrawVertexPathSceneEditor() + { + + Color bezierCol = globalDisplaySettings.bezierPath; + bezierCol.a *= .5f; + + for (int i = 0; i < bezierPath.NumSegments; i++) + { + Vector3[] points = bezierPath.GetPointsInSegment(i); + Handles.DrawBezier(points[0], points[3], points[1], points[2], bezierCol, null, 2); + } + + Handles.color = globalDisplaySettings.vertexPath; + for (int i = 0; i < creator.path.NumVertices; i++) + { + int nextIndex = (i + 1) % creator.path.NumVertices; + if (nextIndex != 0 || bezierPath.IsClosed) + { + Handles.DrawLine(creator.path.vertices[i], creator.path.vertices[nextIndex]); + } + } + + if (data.showNormalsInVertexMode) + { + Handles.color = globalDisplaySettings.normals; + for (int i = 0; i < creator.path.NumVertices; i++) + { + Handles.DrawLine(creator.path.vertices[i], creator.path.vertices[i] + creator.path.normals[i] * globalDisplaySettings.normalsLength); + } + } + + Handles.color = globalDisplaySettings.vertex; + for (int i = 0; i < creator.path.NumVertices; i++) + { + Handles.SphereHandleCap(0, creator.path.vertices[i], Quaternion.identity, data.vertexHandleSize * .1f, EventType.Repaint); + } + } + + + void ProcessBezierPathInput(Event e) + { + + // Update path pivot point on mouse up + if (e.type == EventType.MouseUp) + { + currentHandleRot = Quaternion.identity; + bezierPath.Pivot = bezierPath.PathBounds.center; + } + + // Find which handle mouse is over. Start by looking at previous handle index first, as most likely to still be closest to mouse + int previousMouseOverHandleIndex = (mouseOverHandleIndex == -1) ? 0 : mouseOverHandleIndex; + mouseOverHandleIndex = -1; + for (int i = 0; i < bezierPath.NumPoints; i += 3) + { + int handleIndex = (previousMouseOverHandleIndex + i) % bezierPath.NumPoints; + float handleRadius = GetHandleDiameter(globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[handleIndex]) / 2f; + float dst = HandleUtility.DistanceToCircle(bezierPath[handleIndex], handleRadius); + if (dst == 0) + { + mouseOverHandleIndex = handleIndex; + break; + } + } + + // Shift-left click (when mouse not over a handle) to split or add segment + if (mouseOverHandleIndex == -1) + { + if (e.type == EventType.MouseDown && e.button == 0 && e.shift) + { + UpdatePathMouseInfo(); + // Insert point along selected segment + if (selectedSegmentIndex != -1 && selectedSegmentIndex < bezierPath.NumSegments) + { + Vector3 newPathPoint = pathMouseInfo.closestWorldPointToMouse; + Undo.RecordObject(creator, "Split segment"); + bezierPath.SplitSegment(newPathPoint, selectedSegmentIndex, pathMouseInfo.timeOnBezierSegment); + } + // If path is not a closed loop, add new point on to the end of the path + else if (!bezierPath.IsClosed) + { + // insert new point at same dst from scene camera as the point that comes before it (for a 3d path) + float dstCamToEndpoint = (Camera.current.transform.position - bezierPath[bezierPath.NumPoints - 1]).magnitude; + Vector3 newPathPoint = MouseUtility.GetMouseWorldPosition(bezierPath.Space, dstCamToEndpoint); + + Undo.RecordObject(creator, "Add segment"); + if (e.control || e.command) + { + bezierPath.AddSegmentToStart(newPathPoint); + } + else + { + bezierPath.AddSegmentToEnd(newPathPoint); + } + + } + + } + } + + // Control click or backspace/delete to remove point + if (e.keyCode == KeyCode.Backspace || e.keyCode == KeyCode.Delete || ((e.control || e.command) && e.type == EventType.MouseDown && e.button == 0)) + { + if (mouseOverHandleIndex != -1) + { + Undo.RecordObject(creator, "Delete segment"); + bezierPath.DeleteSegment(mouseOverHandleIndex); + if (mouseOverHandleIndex == handleIndexToDisplayAsTransform) + { + handleIndexToDisplayAsTransform = -1; + } + mouseOverHandleIndex = -1; + + } + } + + // Holding shift and moving mouse (but mouse not over a handle/dragging a handle) + if (draggingHandleIndex == -1 && mouseOverHandleIndex == -1) + { + bool shiftDown = e.shift && !shiftLastFrame; + if (shiftDown || ((e.type == EventType.MouseMove || e.type == EventType.MouseDrag) && e.shift)) + { + + UpdatePathMouseInfo(); + + if (pathMouseInfo.mouseDstToLine < segmentSelectDistanceThreshold) + { + if (pathMouseInfo.closestSegmentIndex != selectedSegmentIndex) + { + selectedSegmentIndex = pathMouseInfo.closestSegmentIndex; + HandleUtility.Repaint(); + } + } + else + { + selectedSegmentIndex = -1; + HandleUtility.Repaint(); + } + + } + } + + if (shareTransformsWithPath) + { + // Move bezier path if creator's transform position has changed + if (creator.transform.position != positionOld) + { + bezierPath.Position += (creator.transform.position - positionOld); + positionOld = creator.transform.position; + } + // Rotate bezier path if creator's transform rotation has changed + if (creator.transform.rotation != rotationOld) + { + bezierPath.Rotation = creator.transform.rotation; + creator.transform.rotation = bezierPath.Rotation; // set to constrained value + rotationOld = creator.transform.rotation; + } + // Scale bezier path if creator's transform scale has changed + if (creator.transform.localScale != scaleOld) + { + bezierPath.Scale = creator.transform.localScale; + creator.transform.localScale = bezierPath.Scale; // set to constrained value + scaleOld = creator.transform.localScale; + } + } + + shiftLastFrame = e.shift; + + } + + + + void DrawBezierPathSceneEditor() + { + bool displayControlPoints = data.displayControlPoints && (bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !globalDisplaySettings.hideAutoControls); + Bounds bounds = bezierPath.PathBounds; + + // Draw normals + if (data.showNormals) + { + if (!hasUpdatedNormalsVertexPath) + { + normalsVertexPath = new VertexPath(bezierPath, normalsSpacing); + hasUpdatedNormalsVertexPath = true; + } + + if (editingNormalsOld != data.showNormals) + { + editingNormalsOld = data.showNormals; + Repaint(); + } + + Handles.color = globalDisplaySettings.normals; + for (int i = 0; i < normalsVertexPath.NumVertices; i++) + { + Handles.DrawLine(normalsVertexPath.vertices[i], normalsVertexPath.vertices[i] + normalsVertexPath.normals[i] * globalDisplaySettings.normalsLength); + } + + } + + for (int i = 0; i < bezierPath.NumSegments; i++) + { + Vector3[] points = bezierPath.GetPointsInSegment(i); + + if (data.showPerSegmentBounds) + { + Bounds segmentBounds = CubicBezierUtility.CalculateBounds(points); + Handles.color = globalDisplaySettings.segmentBounds; + Handles.DrawWireCube(segmentBounds.center, segmentBounds.size); + } + + // Draw lines between control points + if (displayControlPoints) + { + Handles.color = (bezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? globalDisplaySettings.handleDisabled : globalDisplaySettings.controlLine; + Handles.DrawLine(points[1], points[0]); + Handles.DrawLine(points[2], points[3]); + } + + // Draw path + bool highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1); + Color segmentCol = (highlightSegment) ? globalDisplaySettings.highlightedPath : globalDisplaySettings.bezierPath; + Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2); + } + + + // Draw rotate/scale/move tool + if (data.pathTransformationEnabled && !Event.current.alt && !Event.current.shift) + { + if (Tools.current == Tool.Rotate) + { + Undo.RecordObject(creator, "Rotate Path"); + Quaternion newHandleRot = Handles.DoRotationHandle(currentHandleRot, bezierPath.Pivot); + Quaternion deltaRot = newHandleRot * Quaternion.Inverse(currentHandleRot); + currentHandleRot = newHandleRot; + + Quaternion newRot = deltaRot * bezierPath.Rotation; + bezierPath.Rotation = newRot; + if (shareTransformsWithPath) + { + creator.transform.rotation = newRot; + rotationOld = newRot; + } + } + else if (Tools.current == Tool.Scale) + { + Undo.RecordObject(creator, "Scale Path"); + bezierPath.Scale = Handles.DoScaleHandle(bezierPath.Scale, bezierPath.Pivot, Quaternion.identity, HandleUtility.GetHandleSize(bezierPath.Pivot)); + if (shareTransformsWithPath) + { + creator.transform.localScale = bezierPath.Scale; + scaleOld = bezierPath.Scale; + } + } + else + { + Undo.RecordObject(creator, "Move Path"); + + bezierPath.Pivot = bounds.center; + Vector3 newCentre = Handles.DoPositionHandle(bezierPath.Pivot, Quaternion.identity); + Vector3 deltaCentre = newCentre - bezierPath.Pivot; + bezierPath.Position += deltaCentre; + if (shareTransformsWithPath) + { + creator.transform.position = bezierPath.Position; + positionOld = bezierPath.Position; + } + } + + } + + if (data.showPathBounds) + { + Handles.color = globalDisplaySettings.bounds; + Handles.DrawWireCube(bounds.center, bounds.size); + } + + if (data.displayAnchorPoints) + { + for (int i = 0; i < bezierPath.NumPoints; i += 3) + { + DrawHandle(i); + } + } + if (displayControlPoints) + { + for (int i = 1; i < bezierPath.NumPoints - 1; i += 3) + { + DrawHandle(i); + DrawHandle(i + 1); + } + } + } + + void DrawHandle(int i) + { + Vector3 handlePosition = bezierPath[i]; + + float anchorHandleSize = GetHandleDiameter(globalDisplaySettings.anchorSize * data.bezierHandleScale, bezierPath[i]); + float controlHandleSize = GetHandleDiameter(globalDisplaySettings.controlSize * data.bezierHandleScale, bezierPath[i]); + + bool isAnchorPoint = i % 3 == 0; + bool isInteractive = isAnchorPoint || bezierPath.ControlPointMode != BezierPath.ControlMode.Automatic; + float handleSize = (isAnchorPoint) ? anchorHandleSize : controlHandleSize; + bool doTransformHandle = i == handleIndexToDisplayAsTransform; + + PathHandle.HandleColours handleColours = (isAnchorPoint) ? splineAnchorColours : splineControlColours; + var cap = capFunctions[(isAnchorPoint) ? globalDisplaySettings.anchorShape : globalDisplaySettings.controlShape]; + PathHandle.HandleInputType handleInputType; + handlePosition = PathHandle.DrawHandle(handlePosition, bezierPath.Space, isInteractive, handleSize, cap, handleColours, out handleInputType, i); + + if (doTransformHandle) + { + // Show normals rotate tool + if (data.showNormals && Tools.current == Tool.Rotate && isAnchorPoint && bezierPath.Space == PathSpace.xyz) + { + Handles.color = handlesStartCol; + + int attachedControlIndex = (i == bezierPath.NumPoints - 1) ? i - 1 : i + 1; + Vector3 dir = (bezierPath[attachedControlIndex] - handlePosition).normalized; + float handleRotOffset = (360 + bezierPath.GlobalNormalsAngle) % 360; + anchorAngleHandle.radius = handleSize * 3; + anchorAngleHandle.angle = handleRotOffset + bezierPath.GetAnchorNormalAngle(i / 3); + Vector3 handleDirection = Vector3.Cross(dir, Vector3.up); + Matrix4x4 handleMatrix = Matrix4x4.TRS( + handlePosition, + Quaternion.LookRotation(handleDirection, dir), + Vector3.one + ); + + using (new Handles.DrawingScope(handleMatrix)) + { + // draw the handle + EditorGUI.BeginChangeCheck(); + anchorAngleHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(creator, "Set angle"); + bezierPath.SetAnchorNormalAngle(i / 3, anchorAngleHandle.angle - handleRotOffset); + } + } + + } + else + { + handlePosition = Handles.DoPositionHandle(handlePosition, Quaternion.identity); + } + + } + + switch (handleInputType) + { + case PathHandle.HandleInputType.LMBDrag: + draggingHandleIndex = i; + handleIndexToDisplayAsTransform = -1; + break; + case PathHandle.HandleInputType.LMBRelease: + draggingHandleIndex = -1; + handleIndexToDisplayAsTransform = -1; + break; + case PathHandle.HandleInputType.LMBClick: + if (Event.current.shift) + { + handleIndexToDisplayAsTransform = -1; // disable move tool if new point added + } + else + { + if (handleIndexToDisplayAsTransform == i) + { + handleIndexToDisplayAsTransform = -1; // disable move tool if clicking on point under move tool + } + else + { + handleIndexToDisplayAsTransform = i; + } + } + break; + case PathHandle.HandleInputType.LMBPress: + if (handleIndexToDisplayAsTransform != i) + { + handleIndexToDisplayAsTransform = -1; + } + break; + } + + + if (bezierPath[i] != handlePosition) + { + Undo.RecordObject(creator, "Move point"); + bezierPath.MovePoint(i, handlePosition); + } + + } + + #endregion + + #region Internal methods + + void OnDisable() + { + Tools.hidden = false; + } + + void OnEnable() + { + creator = (PathCreator)target; + bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D; + creator.InitializeEditorData(in2DEditorMode); + positionOld = creator.transform.position; + rotationOld = creator.transform.rotation; + scaleOld = creator.transform.localScale; + + data.bezierCreated -= ResetState; + data.bezierCreated += ResetState; + Undo.undoRedoPerformed -= OnUndoRedo; + Undo.undoRedoPerformed += OnUndoRedo; + + LoadDisplaySettings(); + UpdateGlobalDisplaySettings(); + UpdateToolVisibility(); + ResetState(); + } + + void OnUndoRedo() + { + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + selectedSegmentIndex = -1; + + Repaint(); + } + + void TabChanged() + { + SceneView.RepaintAll(); + RepaintUnfocusedSceneViews(); + } + + + void LoadDisplaySettings() + { + // Global display settings: + string[] guids = AssetDatabase.FindAssets("t:GlobalDisplaySettings"); + if (guids.Length == 0) + { + Debug.LogWarning("Could not find DisplaySettings asset. Will use default settings instead."); + globalDisplaySettings = ScriptableObject.CreateInstance(); + } + else + { + string path = AssetDatabase.GUIDToAssetPath(guids[0]); + globalDisplaySettings = AssetDatabase.LoadAssetAtPath(path); + } + + capFunctions = new Dictionary(); + capFunctions.Add(GlobalDisplaySettings.HandleType.Circle, Handles.CylinderHandleCap); + capFunctions.Add(GlobalDisplaySettings.HandleType.Sphere, Handles.SphereHandleCap); + capFunctions.Add(GlobalDisplaySettings.HandleType.Square, Handles.CubeHandleCap); + } + + void UpdateGlobalDisplaySettings() + { + var gds = globalDisplaySettings; + splineAnchorColours = new PathHandle.HandleColours(gds.anchor, gds.anchorHighlighted, gds.anchorSelected, gds.handleDisabled); + splineControlColours = new PathHandle.HandleColours(gds.control, gds.controlHighlighted, gds.controlSelected, gds.handleDisabled); + + anchorAngleHandle.fillColor = new Color(1, 1, 1, .05f); + anchorAngleHandle.wireframeColor = Color.grey; + anchorAngleHandle.radiusHandleColor = Color.clear; + anchorAngleHandle.angleHandleColor = Color.white; + } + + + void ResetState() + { + selectedSegmentIndex = -1; + draggingHandleIndex = -1; + mouseOverHandleIndex = -1; + handleIndexToDisplayAsTransform = -1; + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + bezierPath.Pivot = bezierPath.PathBounds.center; + + bezierPath.OnModified -= OnPathModifed; + bezierPath.OnModified += OnPathModifed; + + SceneView.RepaintAll(); + } + + + void OnPathModifed() + { + hasUpdatedScreenSpaceLine = false; + hasUpdatedNormalsVertexPath = false; + + RepaintUnfocusedSceneViews(); + } + + void RepaintUnfocusedSceneViews() + { + // If multiple scene views are open, repaint those which do not have focus. + if (SceneView.sceneViews.Count > 1) + { + foreach (SceneView sv in SceneView.sceneViews) + { + if (EditorWindow.focusedWindow != (EditorWindow)sv) + { + sv.Repaint(); + } + } + } + } + + void UpdatePathMouseInfo() + { + if (!hasUpdatedScreenSpaceLine) + { + screenSpaceLine = new ScreenSpacePolyLine(bezierPath, screenPolylineMaxAngleError, screenPolylineMinVertexDst); + hasUpdatedScreenSpaceLine = true; + } + pathMouseInfo = screenSpaceLine.CalculateMouseInfo(); + } + + float GetHandleDiameter(float diameter, Vector3 handlePosition) + { + float scaledDiameter = diameter * constantHandleScale; + if (data.keepConstantHandleSize) + { + scaledDiameter *= HandleUtility.GetHandleSize(handlePosition) * 2.5f; + } + return scaledDiameter; + } + + BezierPath bezierPath + { + get + { + return data.bezierPath; + } + } + + PathCreatorData data + { + get + { + return creator.EditorData; + } + } + + bool editingNormals + { + get + { + return Tools.current == Tool.Rotate && handleIndexToDisplayAsTransform % 3 == 0 && bezierPath.Space == PathSpace.xyz; + } + } + + void UpdateToolVisibility() + { + // Hide/unhide tools depending on if inspector is folded + bool hideTools = UnityEditorInternal.InternalEditorUtility.GetIsInspectorExpanded(creator); + if (Tools.hidden != hideTools) + { + Tools.hidden = hideTools; + } + } + + #endregion + + } + +} \ No newline at end of file diff --git a/PathCreator/Core/Editor/PathEditor.cs.meta b/PathCreator/Core/Editor/PathEditor.cs.meta new file mode 100644 index 0000000..b13a53f --- /dev/null +++ b/PathCreator/Core/Editor/PathEditor.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 45d81a20743ff42a3b0dfbd499d4797e +timeCreated: 1516864234 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts.meta b/PathCreator/Core/Scripts.meta new file mode 100644 index 0000000..60c3a6d --- /dev/null +++ b/PathCreator/Core/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b462bfaafdf4ad042afec919e73883dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects.meta b/PathCreator/Core/Scripts/Objects.meta new file mode 100644 index 0000000..cdc1ed1 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba55af960111c724f97893d82237b2b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/BezierPath.cs b/PathCreator/Core/Scripts/Objects/BezierPath.cs new file mode 100644 index 0000000..67f62ce --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/BezierPath.cs @@ -0,0 +1,946 @@ +using System.Collections.Generic; +using UnityEngine; +using PathCreation.Utility; +using System.Linq; + +namespace PathCreation +{ + /// A bezier path is a path made by stitching together any number of (cubic) bezier curves. + /// A single cubic bezier curve is defined by 4 points: anchor1, control1, control2, anchor2 + /// The curve moves between the 2 anchors, and the shape of the curve is affected by the positions of the 2 control points + + /// When two curves are stitched together, they share an anchor point (end anchor of curve 1 = start anchor of curve 2). + /// So while one curve alone consists of 4 points, two curves are defined by 7 unique points. + + /// Apart from storing the points, this class also provides methods for working with the path. + /// For example, adding, inserting, and deleting points. + + [System.Serializable] + public class BezierPath + { + public event System.Action OnModified; + public enum ControlMode { Aligned, Mirrored, Free, Automatic }; + + #region Fields + + [SerializeField, HideInInspector] + List points; + [SerializeField, HideInInspector] + bool isClosed; + [SerializeField, HideInInspector] + Vector3 localPosition; + [SerializeField, HideInInspector] + PathSpace space; + [SerializeField, HideInInspector] + ControlMode controlMode; + [SerializeField, HideInInspector] + float autoControlLength = .3f; + [SerializeField, HideInInspector] + bool boundsUpToDate; + [SerializeField, HideInInspector] + Vector3 pivot; + [SerializeField, HideInInspector] + Bounds bounds; + [SerializeField, HideInInspector] + Quaternion rotation = Quaternion.identity; + [SerializeField, HideInInspector] + Vector3 scale = Vector3.one; + + // Normals settings + [SerializeField, HideInInspector] + List perAnchorNormalsAngle; + [SerializeField, HideInInspector] + float globalNormalsAngle; + [SerializeField, HideInInspector] + bool flipNormals; + + #endregion + + #region Constructors + + /// Creates a two-anchor path centred around the given centre point + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(Vector3 centre, bool isClosed = false, PathSpace space = PathSpace.xyz) + { + + Vector3 dir = (space == PathSpace.xz) ? Vector3.forward : Vector3.up; + float width = 2; + float controlHeight = .5f; + float controlWidth = 1f; + points = new List + { + centre + Vector3.left * width, + centre + Vector3.left * controlWidth + dir * controlHeight, + centre + Vector3.right * controlWidth - dir * controlHeight, + centre + Vector3.right * width + }; + + perAnchorNormalsAngle = new List() { 0, 0 }; + + Space = space; + IsClosed = isClosed; + } + + /// Creates a path from the supplied 3D points + /// List or array of points to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable points, bool isClosed = false, PathSpace space = PathSpace.xyz) + { + Vector3[] pointsArray = points.ToArray(); + + if (pointsArray.Length < 2) + { + Debug.LogError("Path requires at least 2 anchor points."); + } + else + { + controlMode = ControlMode.Automatic; + this.points = new List { pointsArray[0], Vector3.zero, Vector3.zero, pointsArray[1] }; + perAnchorNormalsAngle = new List(new float[] { 0, 0 }); + + for (int i = 2; i < pointsArray.Length; i++) + { + AddSegmentToEnd(pointsArray[i]); + perAnchorNormalsAngle.Add(0); + } + } + + this.Space = space; + this.IsClosed = isClosed; + } + + /// Creates a path from the positions of the supplied 2D points + /// List or array of transforms to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy) : + this(transforms.Select(p => new Vector3(p.x, p.y)), isClosed, space) + { } + + /// Creates a path from the positions of the supplied transforms + /// List or array of transforms to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable transforms, bool isClosed = false, PathSpace space = PathSpace.xy) : + this(transforms.Select(t => t.position), isClosed, space) + { } + + /// Creates a path from the supplied 2D points + /// List or array of 2d points to create the path from. + /// Should the end point connect back to the start point? + /// Determines if the path is in 3d space, or clamped to the xy/xz plane + public BezierPath(IEnumerable points, PathSpace space = PathSpace.xyz, bool isClosed = false) : + this(points.Select(p => new Vector3(p.x, p.y)), isClosed, space) + { } + + #endregion + + #region Public methods and accessors + + /// Get world space position of point + public Vector3 this[int i] + { + get + { + return GetPoint(i); + } + } + + /// Get world space position of point + public Vector3 GetPoint(int i) + { + return points[i] + localPosition; + } + + /// Total number of points in the path (anchors and controls) + public int NumPoints + { + get + { + return points.Count; + } + } + + /// Number of anchor points making up the path + public int NumAnchorPoints + { + get + { + return (IsClosed) ? points.Count / 3 : (points.Count + 2) / 3; + } + } + + /// Number of bezier curves making up this path + public int NumSegments + { + get + { + return points.Count / 3; + } + } + + /// Path can exist in 3D (xyz), 2D (xy), or Top-Down (xz) space + /// In xy or xz space, points will be clamped to that plane (so in a 2D path, for example, points will always be at 0 on z axis) + public PathSpace Space + { + get + { + return space; + } + set + { + if (value != space) + { + PathSpace previousSpace = space; + space = value; + UpdateToNewPathSpace(previousSpace); + } + } + } + + /// If closed, path will loop back from end point to start point + public bool IsClosed + { + get + { + return isClosed; + } + set + { + if (isClosed != value) + { + isClosed = value; + UpdateClosedState(); + } + } + } + + /// The control mode determines the behaviour of control points. + /// Possible modes are: + /// Aligned = controls stay in straight line around their anchor + /// Mirrored = controls stay in straight, equidistant line around their anchor + /// Free = no constraints (use this if sharp corners are needed) + /// Automatic = controls placed automatically to try make the path smooth + public ControlMode ControlPointMode + { + get + { + return controlMode; + } + set + { + if (controlMode != value) + { + controlMode = value; + if (controlMode == ControlMode.Automatic) + { + AutoSetAllControlPoints(); + NotifyPathModified(); + } + } + } + } + + /// Position of the path in the world + public Vector3 Position + { + get + { + return localPosition; + } + set + { + if (localPosition != value) + { + if (space == PathSpace.xy) + { + value.z = 0; + } + else if (space == PathSpace.xz) + { + value.y = 0; + } + localPosition = value; + NotifyPathModified(); + } + } + } + + /// When using automatic control point placement, this value scales how far apart controls are placed + public float AutoControlLength + { + get + { + return autoControlLength; + } + set + { + value = Mathf.Max(value, .01f); + if (autoControlLength != value) + { + autoControlLength = value; + AutoSetAllControlPoints(); + NotifyPathModified(); + } + } + } + + /// Add new anchor point to end of the path + public void AddSegmentToEnd(Vector3 anchorPos) + { + if (isClosed) + { + return; + } + + anchorPos -= localPosition; + + int lastAnchorIndex = points.Count - 1; + // Set position for new control to be mirror of its counterpart + Vector3 secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]); + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor + float dstPrevToNewAnchor = (points[lastAnchorIndex] - anchorPos).magnitude; + secondControlForOldLastAnchorOffset = (points[lastAnchorIndex] - points[lastAnchorIndex - 1]).normalized * dstPrevToNewAnchor * .5f; + } + Vector3 secondControlForOldLastAnchor = points[lastAnchorIndex] + secondControlForOldLastAnchorOffset; + Vector3 controlForNewAnchor = (anchorPos + secondControlForOldLastAnchor) * .5f; + + points.Add(secondControlForOldLastAnchor); + points.Add(controlForNewAnchor); + points.Add(anchorPos); + perAnchorNormalsAngle.Add(perAnchorNormalsAngle[perAnchorNormalsAngle.Count - 1]); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(points.Count - 1); + } + + NotifyPathModified(); + } + + /// Add new anchor point to start of the path + public void AddSegmentToStart(Vector3 anchorPos) + { + if (isClosed) + { + return; + } + + anchorPos -= localPosition; + + // Set position for new control to be mirror of its counterpart + Vector3 secondControlForOldFirstAnchorOffset = (points[0] - points[1]); + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set position for new control to be aligned with its counterpart, but with a length of half the distance from prev to new anchor + float dstPrevToNewAnchor = (points[0] - anchorPos).magnitude; + secondControlForOldFirstAnchorOffset = secondControlForOldFirstAnchorOffset.normalized * dstPrevToNewAnchor * .5f; + } + + Vector3 secondControlForOldFirstAnchor = points[0] + secondControlForOldFirstAnchorOffset; + Vector3 controlForNewAnchor = (anchorPos + secondControlForOldFirstAnchor) * .5f; + points.Insert(0, anchorPos); + points.Insert(1, controlForNewAnchor); + points.Insert(2, secondControlForOldFirstAnchor); + perAnchorNormalsAngle.Insert(0, perAnchorNormalsAngle[0]); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(0); + } + NotifyPathModified(); + } + + /// Insert new anchor point at given position. Automatically place control points around it so as to keep shape of curve the same + public void SplitSegment(Vector3 anchorPos, int segmentIndex, float splitTime) + { + + anchorPos -= localPosition; + + if (controlMode == ControlMode.Automatic) + { + points.InsertRange(segmentIndex * 3 + 2, new Vector3[] { Vector3.zero, anchorPos, Vector3.zero }); + AutoSetAllAffectedControlPoints(segmentIndex * 3 + 3); + } + else + { + // Split the curve to find where control points can be inserted to least affect shape of curve + // Curve will probably be deformed slightly since splitTime is only an estimate (for performance reasons, and so doesn't correspond exactly with anchorPos) + Vector3[][] splitSegment = CubicBezierUtility.SplitCurve(GetPointsInSegment(segmentIndex), splitTime); + points.InsertRange(segmentIndex * 3 + 2, new Vector3[] { splitSegment[0][2], splitSegment[1][0], splitSegment[1][1] }); + int newAnchorIndex = segmentIndex * 3 + 3; + MovePoint(newAnchorIndex - 2, splitSegment[0][1], true); + MovePoint(newAnchorIndex + 2, splitSegment[1][2], true); + MovePoint(newAnchorIndex, anchorPos, true); + + + if (controlMode == ControlMode.Mirrored) + { + float avgDst = ((splitSegment[0][2] - anchorPos).magnitude + (splitSegment[1][1] - anchorPos).magnitude) / 2; + MovePoint(newAnchorIndex + 1, anchorPos + (splitSegment[1][1] - anchorPos).normalized * avgDst, true); + } + } + + // Insert angle for new anchor (value should be set inbetween neighbour anchor angles) + int newAnchorAngleIndex = segmentIndex + 1; + int numAngles = perAnchorNormalsAngle.Count; + float anglePrev = (newAnchorAngleIndex > 0 || isClosed) ? perAnchorNormalsAngle[(newAnchorAngleIndex - 1 + numAngles) % numAngles] : 0; + float angleNext = (newAnchorAngleIndex < numAngles || isClosed) ? perAnchorNormalsAngle[(newAnchorAngleIndex + 1) % numAngles] : 0; + perAnchorNormalsAngle.Insert(newAnchorAngleIndex, (anglePrev + angleNext) / 2f); + + NotifyPathModified(); + } + + /// Delete the anchor point at given index, as well as its associated control points + public void DeleteSegment(int anchorIndex) + { + // Don't delete segment if its the last one remaining (or if only two segments in a closed path) + if (NumSegments > 2 || !isClosed && NumSegments > 1) + { + if (anchorIndex == 0) + { + if (isClosed) + { + points[points.Count - 1] = points[2]; + } + points.RemoveRange(0, 3); + } + else if (anchorIndex == points.Count - 1 && !isClosed) + { + points.RemoveRange(anchorIndex - 2, 3); + } + else + { + points.RemoveRange(anchorIndex - 1, 3); + } + + perAnchorNormalsAngle.RemoveAt(anchorIndex / 3); + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllControlPoints(); + } + + NotifyPathModified(); + } + } + + /// Returns an array of the 4 points making up the segment (anchor1, control1, control2, anchor2) + public Vector3[] GetPointsInSegment(int segmentIndex) + { + segmentIndex = Mathf.Clamp(segmentIndex, 0, NumSegments - 1); + return new Vector3[] { this[segmentIndex * 3], this[segmentIndex * 3 + 1], this[segmentIndex * 3 + 2], this[LoopIndex(segmentIndex * 3 + 3)] }; + } + + /// Move an existing point to a new position + public void MovePoint(int i, Vector3 pointPos, bool suppressPathModifiedEvent = false) + { + pointPos -= localPosition; + + if (space == PathSpace.xy) + { + pointPos.z = 0; + } + else if (space == PathSpace.xz) + { + pointPos.y = 0; + } + Vector3 deltaMove = pointPos - points[i]; + bool isAnchorPoint = i % 3 == 0; + + // Don't process control point if control mode is set to automatic + if (isAnchorPoint || controlMode != ControlMode.Automatic) + { + points[i] = pointPos; + + if (controlMode == ControlMode.Automatic) + { + AutoSetAllAffectedControlPoints(i); + } + else + { + // Move control points with anchor point + if (isAnchorPoint) + { + if (i + 1 < points.Count || isClosed) + { + points[LoopIndex(i + 1)] += deltaMove; + } + if (i - 1 >= 0 || isClosed) + { + points[LoopIndex(i - 1)] += deltaMove; + } + } + // If not in free control mode, then move attached control point to be aligned/mirrored (depending on mode) + else if (controlMode != ControlMode.Free) + { + bool nextPointIsAnchor = (i + 1) % 3 == 0; + int attachedControlIndex = (nextPointIsAnchor) ? i + 2 : i - 2; + int anchorIndex = (nextPointIsAnchor) ? i + 1 : i - 1; + + if (attachedControlIndex >= 0 && attachedControlIndex < points.Count || isClosed) + { + float distanceFromAnchor = 0; + // If in aligned mode, then attached control's current distance from anchor point should be maintained + if (controlMode == ControlMode.Aligned) + { + distanceFromAnchor = (points[LoopIndex(anchorIndex)] - points[LoopIndex(attachedControlIndex)]).magnitude; + } + // If in mirrored mode, then both control points should have the same distance from the anchor point + else if (controlMode == ControlMode.Mirrored) + { + distanceFromAnchor = (points[LoopIndex(anchorIndex)] - points[i]).magnitude; + + } + Vector3 dir = (points[LoopIndex(anchorIndex)] - pointPos).normalized; + points[LoopIndex(attachedControlIndex)] = points[LoopIndex(anchorIndex)] + dir * distanceFromAnchor; + } + } + } + + if (!suppressPathModifiedEvent) + { + NotifyPathModified(); + } + } + } + + /// Rotation of the path around current pivot + public Quaternion Rotation + { + get + { + return rotation; + } + set + { + if (space != PathSpace.xyz) + { + Vector3 axis = (space == PathSpace.xy) ? Vector3.forward : Vector3.up; + float angle = (space == PathSpace.xy) ? value.eulerAngles.z : value.eulerAngles.y; + value = Quaternion.AngleAxis(angle, axis); + } + if (rotation != value) + { + // Inverse of rotation takes us back to when there was no rotation applied, then multiply by new rotation + Quaternion rotFromOrigin = value * Quaternion.Inverse(rotation); + Vector3 localPivot = pivot - localPosition; + // Apply rotation to all points + for (int i = 0; i < points.Count; i++) + { + points[i] = rotFromOrigin * (points[i] - localPivot) + localPivot; + } + rotation = value; + NotifyPathModified(); + } + } + } + + /// Scale of the path around current pivot + public Vector3 Scale + { + get + { + return scale; + } + set + { + float minVal = 0.01f; + // Ensure scale is never exactly zero since information would be lost when scale is applied + if (value.x == 0) + { + value.x = minVal; + } + if (value.y == 0) + { + value.y = minVal; + } + if (value.z == 0) + { + value.z = minVal; + } + + // Set unused axis to zero + if (space == PathSpace.xy) + { + value.z = 0; + } + else if (space == PathSpace.xz) + { + value.y = 0; + } + + if (scale != value) + { + // Find scale required to go from current applied scale to new scale + Vector3 deltaScale = value; + if (scale.x != 0) + { + deltaScale.x /= scale.x; + } + if (scale.y != 0) + { + deltaScale.y /= scale.y; + } + if (scale.z != 0) + { + deltaScale.z /= scale.z; + } + + Vector3 localPivot = pivot - localPosition; + // Apply the scale to all points + for (int i = 0; i < points.Count; i++) + { + points[i] = Vector3.Scale(points[i] - localPivot, deltaScale) + localPivot; + } + + scale = value; + NotifyPathModified(); + } + } + } + + /// Current pivot point around which transformations occur + public Vector3 Pivot + { + get + { + return pivot; + } + set + { + pivot = value; + } + } + + /// Flip the normal vectors 180 degrees + public bool FlipNormals + { + get + { + return flipNormals; + } + set + { + if (flipNormals != value) + { + flipNormals = value; + NotifyPathModified(); + } + } + } + + /// Global angle that all normal vectors are rotated by (only relevant for paths in 3D space) + public float GlobalNormalsAngle + { + get + { + return globalNormalsAngle; + } + set + { + if (value != globalNormalsAngle) + { + globalNormalsAngle = value; + NotifyPathModified(); + } + } + } + + /// Get the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) + public float GetAnchorNormalAngle(int anchorIndex) + { + return perAnchorNormalsAngle[anchorIndex] % 360; + } + + + /// Set the desired angle of the normal vector at a particular anchor (only relevant for paths in 3D space) + public void SetAnchorNormalAngle(int anchorIndex, float angle) + { + angle = (angle + 360) % 360; + if (perAnchorNormalsAngle[anchorIndex] != angle) + { + perAnchorNormalsAngle[anchorIndex] = angle; + NotifyPathModified(); + } + } + + /// Reset global and anchor normal angles to 0 + public void ResetNormalAngles() + { + for (int i = 0; i < perAnchorNormalsAngle.Count; i++) + { + perAnchorNormalsAngle[i] = 0; + } + globalNormalsAngle = 0; + NotifyPathModified(); + } + + /// Bounding box containing the path + public Bounds PathBounds + { + get + { + if (!boundsUpToDate) + { + UpdateBounds(); + } + return bounds; + } + } + + #endregion + + #region Internal methods and accessors + + /// Update the bounding box of the path + void UpdateBounds() + { + if (boundsUpToDate) + { + return; + } + + // Loop through all segments and keep track of the minmax points of all their bounding boxes + MinMax3D minMax = new MinMax3D(); + + for (int i = 0; i < NumSegments; i++) + { + Vector3[] p = GetPointsInSegment(i); + minMax.AddValue(p[0]); + minMax.AddValue(p[3]); + + List extremePointTimes = CubicBezierUtility.ExtremePointTimes(p[0], p[1], p[2], p[3]); + foreach (float t in extremePointTimes) + { + minMax.AddValue(CubicBezierUtility.EvaluateCurve(p, t)); + } + } + + boundsUpToDate = true; + bounds = new Bounds((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); + } + + /// Determines good positions (for a smooth path) for the control points affected by a moved/inserted anchor point + void AutoSetAllAffectedControlPoints(int updatedAnchorIndex) + { + for (int i = updatedAnchorIndex - 3; i <= updatedAnchorIndex + 3; i += 3) + { + if (i >= 0 && i < points.Count || isClosed) + { + AutoSetAnchorControlPoints(LoopIndex(i)); + } + } + + AutoSetStartAndEndControls(); + } + + /// Determines good positions (for a smooth path) for all control points + void AutoSetAllControlPoints() + { + if (NumAnchorPoints > 2) + { + for (int i = 0; i < points.Count; i += 3) + { + AutoSetAnchorControlPoints(i); + } + } + + AutoSetStartAndEndControls(); + } + + /// Calculates good positions (to result in smooth path) for the controls around specified anchor + void AutoSetAnchorControlPoints(int anchorIndex) + { + // Calculate a vector that is perpendicular to the vector bisecting the angle between this anchor and its two immediate neighbours + // The control points will be placed along that vector + Vector3 anchorPos = points[anchorIndex]; + Vector3 dir = Vector3.zero; + float[] neighbourDistances = new float[2]; + + if (anchorIndex - 3 >= 0 || isClosed) + { + Vector3 offset = points[LoopIndex(anchorIndex - 3)] - anchorPos; + dir += offset.normalized; + neighbourDistances[0] = offset.magnitude; + } + if (anchorIndex + 3 >= 0 || isClosed) + { + Vector3 offset = points[LoopIndex(anchorIndex + 3)] - anchorPos; + dir -= offset.normalized; + neighbourDistances[1] = -offset.magnitude; + } + + dir.Normalize(); + + // Set the control points along the calculated direction, with a distance proportional to the distance to the neighbouring control point + for (int i = 0; i < 2; i++) + { + int controlIndex = anchorIndex + i * 2 - 1; + if (controlIndex >= 0 && controlIndex < points.Count || isClosed) + { + points[LoopIndex(controlIndex)] = anchorPos + dir * neighbourDistances[i] * autoControlLength; + } + } + } + + /// Determines good positions (for a smooth path) for the control points at the start and end of a path + void AutoSetStartAndEndControls() + { + if (isClosed) + { + // Handle case with only 2 anchor points separately, as will otherwise result in straight line () + if (NumAnchorPoints == 2) + { + Vector3 dirAnchorAToB = (points[3] - points[0]).normalized; + float dstBetweenAnchors = (points[0] - points[3]).magnitude; + Vector3 perp = Vector3.Cross(dirAnchorAToB, (space == PathSpace.xy) ? Vector3.forward : Vector3.up); + points[1] = points[0] + perp * dstBetweenAnchors / 2f; + points[5] = points[0] - perp * dstBetweenAnchors / 2f; + points[2] = points[3] + perp * dstBetweenAnchors / 2f; + points[4] = points[3] - perp * dstBetweenAnchors / 2f; + + } + else + { + AutoSetAnchorControlPoints(0); + AutoSetAnchorControlPoints(points.Count - 3); + } + } + else + { + // Handle case with 2 anchor points separately, as otherwise minor adjustments cause path to constantly flip + if (NumAnchorPoints == 2) + { + points[1] = points[0] + (points[3] - points[0]) * .25f; + points[2] = points[3] + (points[0] - points[3]) * .25f; + } + else + { + points[1] = (points[0] + points[2]) * .5f; + points[points.Count - 2] = (points[points.Count - 1] + points[points.Count - 3]) * .5f; + } + } + } + + /// Update point positions for new path space + /// (for example, if changing from xy to xz path, y and z axes will be swapped so the path keeps its shape in the new space) + void UpdateToNewPathSpace(PathSpace previousSpace) + { + // If changing from 3d to 2d space, first find the bounds of the 3d path. + // The axis with the smallest bounds will be discarded. + if (previousSpace == PathSpace.xyz) + { + Vector3 boundsSize = PathBounds.size; + float minBoundsSize = Mathf.Min(boundsSize.x, boundsSize.y, boundsSize.z); + + for (int i = 0; i < NumPoints; i++) + { + if (space == PathSpace.xy) + { + localPosition = new Vector3(localPosition.x, localPosition.y, 0); + float x = (minBoundsSize == boundsSize.x) ? points[i].z : points[i].x; + float y = (minBoundsSize == boundsSize.y) ? points[i].z : points[i].y; + points[i] = new Vector3(x, y, 0); + } + else if (space == PathSpace.xz) + { + localPosition = new Vector3(localPosition.x, 0, localPosition.z); + float x = (minBoundsSize == boundsSize.x) ? points[i].y : points[i].x; + float z = (minBoundsSize == boundsSize.z) ? points[i].y : points[i].z; + points[i] = new Vector3(x, 0, z); + } + } + } + else + { + // Nothing needs to change when going to 3d space + if (space != PathSpace.xyz) + { + for (int i = 0; i < NumPoints; i++) + { + // from xz to xy + if (space == PathSpace.xy) + { + localPosition = new Vector3(localPosition.x, localPosition.z); + points[i] = new Vector3(points[i].x, points[i].z, 0); + } + // from xy to xz + else if (space == PathSpace.xz) + { + localPosition = new Vector3(localPosition.x, 0, localPosition.y); + points[i] = new Vector3(points[i].x, 0, points[i].y); + } + } + } + } + + if (space != PathSpace.xyz) + { + Vector3 axis = (space == PathSpace.xy) ? Vector3.forward : Vector3.up; + float angle = (space == PathSpace.xy) ? rotation.eulerAngles.z : rotation.eulerAngles.y; + rotation = Quaternion.AngleAxis(angle, axis); + } + + NotifyPathModified(); + } + + /// Add/remove the extra 2 controls required for a closed path + void UpdateClosedState() + { + if (isClosed) + { + // Set positions for new controls to mirror their counterparts + Vector3 lastAnchorSecondControl = points[points.Count - 1] * 2 - points[points.Count - 2]; + Vector3 firstAnchorSecondControl = points[0] * 2 - points[1]; + if (controlMode != ControlMode.Mirrored && controlMode != ControlMode.Automatic) + { + // Set positions for new controls to be aligned with their counterparts, but with a length of half the distance between start/end anchor + float dstBetweenStartAndEndAnchors = (points[points.Count - 1] - points[0]).magnitude; + lastAnchorSecondControl = points[points.Count - 1] + (points[points.Count - 1] - points[points.Count - 2]).normalized * dstBetweenStartAndEndAnchors * .5f; + firstAnchorSecondControl = points[0] + (points[0] - points[1]).normalized * dstBetweenStartAndEndAnchors * .5f; + } + points.Add(lastAnchorSecondControl); + points.Add(firstAnchorSecondControl); + } + else + { + points.RemoveRange(points.Count - 2, 2); + + } + + if (controlMode == ControlMode.Automatic) + { + AutoSetStartAndEndControls(); + } + + if (OnModified != null) + { + OnModified(); + } + } + + /// Loop index around to start/end of points array if out of bounds (useful when working with closed paths) + int LoopIndex(int i) + { + return (i + points.Count) % points.Count; + } + + // Called internally when the path is modified + void NotifyPathModified() + { + boundsUpToDate = false; + if (OnModified != null) + { + OnModified(); + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Objects/BezierPath.cs.meta b/PathCreator/Core/Scripts/Objects/BezierPath.cs.meta new file mode 100644 index 0000000..39652fe --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/BezierPath.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 756209f371f304fb1920c53a8a25a5e2 +timeCreated: 1516786483 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs b/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs new file mode 100644 index 0000000..697e361 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs @@ -0,0 +1,3 @@ +namespace PathCreation { + public enum EndOfPathInstruction {Loop, Reverse, Stop}; +} diff --git a/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs.meta b/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs.meta new file mode 100644 index 0000000..6f25bdf --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/EndOfPathInstruction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c0d7fa70c03166e4180909b6e5d546c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs b/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs new file mode 100644 index 0000000..b790474 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs @@ -0,0 +1,50 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace PathCreationEditor +{ + //[CreateAssetMenu()] + public class GlobalDisplaySettings : ScriptableObject + { + + public enum HandleType { Sphere, Circle, Square }; + + [Header("Appearance")] + public float anchorSize = 10; + public float controlSize = 7f; + + [Tooltip("If true, control points will be hidden when the control point mode is set to automatic. Otherwise they will inactive, but still visible.")] + public bool hideAutoControls = true; + public HandleType anchorShape; + public HandleType controlShape; + + + [Header("Anchor Colours")] + public Color anchor = new Color(0.95f, 0.25f, 0.25f, 0.85f); + public Color anchorHighlighted = new Color(1, 0.4f, 0.4f); + public Color anchorSelected = Color.white; + + [Header("Control Colours")] + public Color control = new Color(0.35f, 0.6f, 1, 0.85f); + public Color controlHighlighted = new Color(0.6f, 0.6f, 1); + public Color controlSelected = Color.white; + public Color handleDisabled = new Color(1, 1, 1, 0.2f); + public Color controlLine = new Color(0, 0, 0, 0.35f); + + [Header("Bezier Path Colours")] + public Color bezierPath = Color.green; + public Color highlightedPath = new Color(1, 0.6f, 0); + public Color bounds = new Color(1, 1, 1, .4f); + public Color segmentBounds = new Color(1, 1, 1, .4f); + + [Header("Vertex Path Colours")] + public Color vertexPath = Color.white; + public Color vertex = Color.black; + + [Header("Normals")] + public Color normals = Color.yellow; + [Range(0,1)] + public float normalsLength = .1f; + } +} diff --git a/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs.meta b/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs.meta new file mode 100644 index 0000000..cd66ac9 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/GlobalDisplaySettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46d5e8d803e252f4499b52e657f8e1a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/MinMax3D.cs b/PathCreator/Core/Scripts/Objects/MinMax3D.cs new file mode 100644 index 0000000..4229602 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/MinMax3D.cs @@ -0,0 +1,21 @@ +using UnityEngine; + +namespace PathCreation { + public class MinMax3D { + + public Vector3 Min { get; private set; } + public Vector3 Max { get; private set; } + + public MinMax3D() + { + Min = Vector3.one * float.MaxValue; + Max = Vector3.one * float.MinValue; + } + + public void AddValue(Vector3 v) + { + Min = new Vector3(Mathf.Min(Min.x, v.x), Mathf.Min(Min.y,v.y), Mathf.Min(Min.z,v.z)); + Max = new Vector3(Mathf.Max(Max.x, v.x), Mathf.Max(Max.y,v.y), Mathf.Max(Max.z,v.z)); + } + } +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Objects/MinMax3D.cs.meta b/PathCreator/Core/Scripts/Objects/MinMax3D.cs.meta new file mode 100644 index 0000000..f96e783 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/MinMax3D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 194e1b7893dcbf940a71eb507d8b10f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/PathCreator.cs b/PathCreator/Core/Scripts/Objects/PathCreator.cs new file mode 100644 index 0000000..956cb28 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathCreator.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace PathCreation +{ + public class PathCreator : MonoBehaviour + { + + /// This class stores data for the path editor, and provides accessors to get the current vertex and bezier path. + /// Attach to a GameObject to create a new path editor. + + public event System.Action pathUpdated; + + [SerializeField, HideInInspector] + PathCreatorData editorData; + [SerializeField, HideInInspector] + bool initialized; + + // Vertex path created from the current bezier path + public VertexPath path + { + get + { + if (!initialized) + { + InitializeEditorData(false); + } + return editorData.vertexPath; + } + } + + // The bezier path created in the editor + public BezierPath bezierPath + { + get + { + if (!initialized) + { + InitializeEditorData(false); + } + return editorData.bezierPath; + } + set + { + if (!initialized) + { + InitializeEditorData(false); + } + editorData.bezierPath = value; + } + } + + #region Internal methods + + /// Used by the path editor to initialise some data + public void InitializeEditorData(bool in2DMode) + { + if (editorData == null) + { + editorData = new PathCreatorData(); + } + editorData.bezierOrVertexPathModified -= OnPathUpdated; + editorData.bezierOrVertexPathModified += OnPathUpdated; + + editorData.Initialize(transform.position, in2DMode); + initialized = true; + } + + public PathCreatorData EditorData + { + get + { + return editorData; + } + + } + + void OnPathUpdated() + { + if (pathUpdated != null) + { + pathUpdated(); + } + } + + #endregion + } +} diff --git a/PathCreator/Core/Scripts/Objects/PathCreator.cs.meta b/PathCreator/Core/Scripts/Objects/PathCreator.cs.meta new file mode 100644 index 0000000..730a1a2 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathCreator.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 8e5ac92bc18f545cc84cd886ece82b4d +timeCreated: 1516864223 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/PathCreatorData.cs b/PathCreator/Core/Scripts/Objects/PathCreatorData.cs new file mode 100644 index 0000000..f7b98a3 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathCreatorData.cs @@ -0,0 +1,156 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace PathCreation +{ + /// Stores state data for the path creator editor + + [System.Serializable] + public class PathCreatorData + { + public event System.Action bezierOrVertexPathModified; + public event System.Action bezierCreated; + + [SerializeField] + BezierPath _bezierPath; + VertexPath _vertexPath; + + [SerializeField] + bool vertexPathUpToDate; + + // vertex path settings + public float vertexPathMaxAngleError = .3f; + public float vertexPathMinVertexSpacing = 0.01f; + + // bezier display settings + public bool pathTransformationEnabled; + public bool showPathBounds; + public bool showPerSegmentBounds; + public bool displayAnchorPoints = true; + public bool displayControlPoints = true; + public float bezierHandleScale = 1; + public bool globalDisplaySettingsFoldout; + public bool keepConstantHandleSize; + + // vertex display settings + public float vertexHandleSize = .2f; + public bool showNormalsInVertexMode; + + // Editor display states + public bool showDisplayOptions; + public bool showPathOptions = true; + public bool showVertexPathDisplayOptions; + public bool showVertexPathOptions = true; + public bool showNormals; + public bool showNormalsHelpInfo; + public int tabIndex; + + public void Initialize(Vector3 centre, bool defaultIs2D) + { + if (_bezierPath == null) + { + CreateBezier(centre, defaultIs2D); + } + vertexPathUpToDate = false; + _bezierPath.OnModified -= BezierPathEdited; + _bezierPath.OnModified += BezierPathEdited; + } + + public void ResetBezierPath(Vector3 centre, bool defaultIs2D = false) + { + CreateBezier(centre, defaultIs2D); + } + + void CreateBezier(Vector3 centre, bool defaultIs2D = false) + { + if (_bezierPath != null) + { + _bezierPath.OnModified -= BezierPathEdited; + } + + var space = (defaultIs2D) ? PathSpace.xy : PathSpace.xyz; + _bezierPath = new BezierPath(centre, false, space); + + _bezierPath.OnModified += BezierPathEdited; + vertexPathUpToDate = false; + + if (bezierOrVertexPathModified != null) + { + bezierOrVertexPathModified(); + } + if (bezierCreated != null) + { + bezierCreated(); + } + } + + public BezierPath bezierPath + { + get + { + return _bezierPath; + } + set + { + _bezierPath.OnModified -= BezierPathEdited; + vertexPathUpToDate = false; + _bezierPath = value; + _bezierPath.OnModified += BezierPathEdited; + + if (bezierOrVertexPathModified != null) + { + bezierOrVertexPathModified(); + } + if (bezierCreated != null) + { + bezierCreated(); + } + + } + } + + // Get the current vertex path + public VertexPath vertexPath + { + get + { + // create new vertex path if path was modified since this vertex path was created + if (!vertexPathUpToDate || _vertexPath == null) + { + vertexPathUpToDate = true; + _vertexPath = new VertexPath(bezierPath, vertexPathMaxAngleError, vertexPathMinVertexSpacing); + } + return _vertexPath; + } + } + + + public void VertexPathSettingsChanged() + { + vertexPathUpToDate = false; + if (bezierOrVertexPathModified != null) + { + bezierOrVertexPathModified(); + } + } + + public void PathModifiedByUndo() + { + vertexPathUpToDate = false; + if (bezierOrVertexPathModified != null) + { + bezierOrVertexPathModified(); + } + } + + void BezierPathEdited() + { + vertexPathUpToDate = false; + if (bezierOrVertexPathModified != null) + { + bezierOrVertexPathModified(); + } + } + + } +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Objects/PathCreatorData.cs.meta b/PathCreator/Core/Scripts/Objects/PathCreatorData.cs.meta new file mode 100644 index 0000000..b626c46 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathCreatorData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 19547029ffb83484e90d00c182104817 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/PathSpace.cs b/PathCreator/Core/Scripts/Objects/PathSpace.cs new file mode 100644 index 0000000..8bb0052 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathSpace.cs @@ -0,0 +1,3 @@ +namespace PathCreation { + public enum PathSpace {xyz, xy, xz}; +} diff --git a/PathCreator/Core/Scripts/Objects/PathSpace.cs.meta b/PathCreator/Core/Scripts/Objects/PathSpace.cs.meta new file mode 100644 index 0000000..2c89861 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/PathSpace.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 407af8f85a5bae449bce4730c5d97d1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Objects/VertexPath.cs b/PathCreator/Core/Scripts/Objects/VertexPath.cs new file mode 100644 index 0000000..d8292cf --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/VertexPath.cs @@ -0,0 +1,312 @@ +using System.Collections.Generic; +using UnityEngine; +using PathCreation.Utility; + +namespace PathCreation +{ + /// A vertex path is a collection of points (vertices) that lie along a bezier path. + /// This allows one to do things like move at a constant speed along the path, + /// which is not possible with a bezier path directly due to how they're constructed mathematically. + + /// This class also provides methods for getting the position along the path at a certain distance or time + /// (where time = 0 is the start of the path, and time = 1 is the end of the path). + /// Other info about the path (tangents, normals, rotation) can also be retrieved in this manner. + + public class VertexPath + { + #region Fields + + public readonly PathSpace space; + public readonly bool isClosedLoop; + public readonly Vector3[] vertices; + public readonly Vector3[] tangents; + public readonly Vector3[] normals; + + /// Percentage along the path at each vertex (0 being start of path, and 1 being the end) + public readonly float[] times; + /// Total distance between the vertices of the polyline + public readonly float length; + /// Total distance from the first vertex up to each vertex in the polyline + public readonly float[] cumulativeLengthAtEachVertex; + /// Bounding box of the path + public readonly Bounds bounds; + /// Equal to (0,0,-1) for 2D paths, and (0,1,0) for XZ paths + public readonly Vector3 up; + + // Default values and constants: + const int accuracy = 10; // A scalar for how many times bezier path is divided when determining vertex positions + const float minVertexSpacing = .01f; + + #endregion + + #region Constructors + + /// Splits bezier path into array of vertices along the path. + ///How much can the angle of the path change before a vertex is added. This allows fewer vertices to be generated in straighter sections. + ///Vertices won't be added closer together than this distance, regardless of angle error. + public VertexPath(BezierPath bezierPath, float maxAngleError = 0.3f, float minVertexDst = 0) : + this(bezierPath, VertexPathUtility.SplitBezierPathByAngleError(bezierPath, maxAngleError, minVertexDst, VertexPath.accuracy)) + { } + + /// Splits bezier path into array of vertices along the path. + ///How much can the angle of the path change before a vertex is added. This allows fewer vertices to be generated in straighter sections. + ///Vertices won't be added closer together than this distance, regardless of angle error. + ///Higher value means the change in angle is checked more frequently. + public VertexPath(BezierPath bezierPath, float vertexSpacing) : + this(bezierPath, VertexPathUtility.SplitBezierPathEvenly(bezierPath, Mathf.Max(vertexSpacing, minVertexSpacing), VertexPath.accuracy)) + { } + + /// Internal contructor + VertexPath(BezierPath bezierPath, VertexPathUtility.PathSplitData pathSplitData) + { + space = bezierPath.Space; + isClosedLoop = bezierPath.IsClosed; + int numVerts = pathSplitData.vertices.Count; + length = pathSplitData.cumulativeLength[numVerts - 1]; + + vertices = new Vector3[numVerts]; + normals = new Vector3[numVerts]; + tangents = new Vector3[numVerts]; + cumulativeLengthAtEachVertex = new float[numVerts]; + times = new float[numVerts]; + bounds = new Bounds((pathSplitData.minMax.Min + pathSplitData.minMax.Max) / 2, pathSplitData.minMax.Max - pathSplitData.minMax.Min); + + // Figure out up direction for path + up = (bounds.size.z > bounds.size.y) ? Vector3.up : -Vector3.forward; + Vector3 lastRotationAxis = up; + + // Loop through the data and assign to arrays. + for (int i = 0; i < vertices.Length; i++) + { + vertices[i] = pathSplitData.vertices[i]; + tangents[i] = pathSplitData.tangents[i]; + cumulativeLengthAtEachVertex[i] = pathSplitData.cumulativeLength[i]; + times[i] = cumulativeLengthAtEachVertex[i] / length; + + // Calculate normals + if (space == PathSpace.xyz) + { + if (i == 0) + { + normals[0] = Vector3.Cross(lastRotationAxis, pathSplitData.tangents[0]).normalized; + } + else + { + // First reflection + Vector3 offset = (vertices[i] - vertices[i - 1]); + float sqrDst = offset.sqrMagnitude; + Vector3 r = lastRotationAxis - offset * 2 / sqrDst * Vector3.Dot(offset, lastRotationAxis); + Vector3 t = tangents[i - 1] - offset * 2 / sqrDst * Vector3.Dot(offset, tangents[i - 1]); + + // Second reflection + Vector3 v2 = tangents[i] - t; + float c2 = Vector3.Dot(v2, v2); + + Vector3 finalRot = r - v2 * 2 / c2 * Vector3.Dot(v2, r); + Vector3 n = Vector3.Cross(finalRot, tangents[i]).normalized; + normals[i] = n; + lastRotationAxis = finalRot; + } + } + else + { + normals[i] = Vector3.Cross(tangents[i], up) * ((bezierPath.FlipNormals) ? 1 : -1); + } + } + + // Apply correction for 3d normals along a closed path + if (space == PathSpace.xyz && isClosedLoop) + { + // Get angle between first and last normal (if zero, they're already lined up, otherwise we need to correct) + float normalsAngleErrorAcrossJoin = Vector3.SignedAngle(normals[normals.Length - 1], normals[0], tangents[0]); + // Gradually rotate the normals along the path to ensure start and end normals line up correctly + if (Mathf.Abs(normalsAngleErrorAcrossJoin) > 0.1f) // don't bother correcting if very nearly correct + { + for (int i = 1; i < normals.Length; i++) + { + float t = (i / (normals.Length - 1f)); + float angle = normalsAngleErrorAcrossJoin * t; + Quaternion rot = Quaternion.AngleAxis(angle, tangents[i]); + normals[i] = rot * normals[i] * ((bezierPath.FlipNormals) ? -1 : 1); + } + } + } + + // Rotate normals to match up with user-defined anchor angles + if (space == PathSpace.xyz) + { + for (int anchorIndex = 0; anchorIndex < pathSplitData.anchorVertexMap.Count - 1; anchorIndex++) + { + int nextAnchorIndex = (isClosedLoop) ? (anchorIndex + 1) % bezierPath.NumSegments : anchorIndex + 1; + + float startAngle = bezierPath.GetAnchorNormalAngle(anchorIndex) + bezierPath.GlobalNormalsAngle; + float endAngle = bezierPath.GetAnchorNormalAngle(nextAnchorIndex) + bezierPath.GlobalNormalsAngle; + float deltaAngle = Mathf.DeltaAngle(startAngle, endAngle); + + int startVertIndex = pathSplitData.anchorVertexMap[anchorIndex]; + int endVertIndex = pathSplitData.anchorVertexMap[anchorIndex + 1]; + + int num = endVertIndex - startVertIndex; + if (anchorIndex == pathSplitData.anchorVertexMap.Count - 2) + { + num += 1; + } + for (int i = 0; i < num; i++) + { + int vertIndex = startVertIndex + i; + float t = i / (num - 1f); + float angle = startAngle + deltaAngle * t; + Quaternion rot = Quaternion.AngleAxis(angle, tangents[vertIndex]); + normals[vertIndex] = (rot * normals[vertIndex]) * ((bezierPath.FlipNormals) ? -1 : 1); + } + } + } + } + + #endregion + + #region Public methods and accessors + public int NumVertices + { + get + { + return vertices.Length; + } + } + + /// Gets point on path based on distance travelled. + public Vector3 GetPointAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + float t = dst / length; + return GetPoint(t, endOfPathInstruction); + } + + /// Gets forward direction on path based on distance travelled. + public Vector3 GetDirectionAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + float t = dst / length; + return GetDirection(t, endOfPathInstruction); + } + + /// Gets normal vector on path based on distance travelled. + public Vector3 GetNormalAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + float t = dst / length; + return GetNormal(t, endOfPathInstruction); + } + + /// Gets a rotation that will orient an object in the direction of the path at this point, with local up point along the path's normal + public Quaternion GetRotationAtDistance(float dst, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + float t = dst / length; + return GetRotation(t, endOfPathInstruction); + } + + /// Gets point on path based on 'time' (where 0 is start, and 1 is end of path). + public Vector3 GetPoint(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + var data = CalculatePercentOnPathData(t, endOfPathInstruction); + return Vector3.Lerp(vertices[data.previousIndex], vertices[data.nextIndex], data.percentBetweenIndices); + } + + /// Gets forward direction on path based on 'time' (where 0 is start, and 1 is end of path). + public Vector3 GetDirection(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + var data = CalculatePercentOnPathData(t, endOfPathInstruction); + return Vector3.Lerp(tangents[data.previousIndex], tangents[data.nextIndex], data.percentBetweenIndices); + } + + /// Gets normal vector on path based on 'time' (where 0 is start, and 1 is end of path). + public Vector3 GetNormal(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + var data = CalculatePercentOnPathData(t, endOfPathInstruction); + return Vector3.Lerp(normals[data.previousIndex], normals[data.nextIndex], data.percentBetweenIndices); + } + + /// Gets a rotation that will orient an object in the direction of the path at this point, with local up point along the path's normal + public Quaternion GetRotation(float t, EndOfPathInstruction endOfPathInstruction = EndOfPathInstruction.Loop) + { + var data = CalculatePercentOnPathData(t, endOfPathInstruction); + Vector3 direction = Vector3.Lerp(tangents[data.previousIndex], tangents[data.nextIndex], data.percentBetweenIndices); + Vector3 normal = Vector3.Lerp(normals[data.previousIndex], normals[data.nextIndex], data.percentBetweenIndices); + return Quaternion.LookRotation(direction, normal); + } + + #endregion + + #region Internal methods + + /// For a given value 't' between 0 and 1, calculate the indices of the two vertices before and after t. + /// Also calculate how far t is between those two vertices as a percentage between 0 and 1. + TimeOnPathData CalculatePercentOnPathData(float t, EndOfPathInstruction endOfPathInstruction) + { + // Constrain t based on the end of path instruction + switch (endOfPathInstruction) + { + case EndOfPathInstruction.Loop: + // If t is negative, make it the equivalent value between 0 and 1 + if (t < 0) + { + t += Mathf.CeilToInt(Mathf.Abs(t)); + } + t %= 1; + break; + case EndOfPathInstruction.Reverse: + t = Mathf.PingPong(t, 1); + break; + case EndOfPathInstruction.Stop: + t = Mathf.Clamp01(t); + break; + } + + + int prevIndex = 0; + int nextIndex = NumVertices - 1; + int i = Mathf.RoundToInt(t * (NumVertices - 1)); // starting guess + + // Starts by looking at middle vertex and determines if t lies to the left or to the right of that vertex. + // Continues dividing in half until closest surrounding vertices have been found. + while (true) + { + // t lies to left + if (t <= times[i]) + { + nextIndex = i; + } + // t lies to right + else + { + prevIndex = i; + } + i = (nextIndex + prevIndex) / 2; + + if (nextIndex - prevIndex <= 1) + { + break; + } + } + + float abPercent = Mathf.InverseLerp(times[prevIndex], times[nextIndex], t); + return new TimeOnPathData(prevIndex, nextIndex, abPercent); + } + + struct TimeOnPathData + { + public readonly int previousIndex; + public readonly int nextIndex; + public readonly float percentBetweenIndices; + + public TimeOnPathData(int prev, int next, float percentBetweenIndices) + { + this.previousIndex = prev; + this.nextIndex = next; + this.percentBetweenIndices = percentBetweenIndices; + } + } + + #endregion + + } + + +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Objects/VertexPath.cs.meta b/PathCreator/Core/Scripts/Objects/VertexPath.cs.meta new file mode 100644 index 0000000..b8cf388 --- /dev/null +++ b/PathCreator/Core/Scripts/Objects/VertexPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 223183313f20d4049a397d83fab3151e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Utility.meta b/PathCreator/Core/Scripts/Utility.meta new file mode 100644 index 0000000..cebbeaa --- /dev/null +++ b/PathCreator/Core/Scripts/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8ad8bc51ab46e854089e8d55e0648a19 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs b/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs new file mode 100644 index 0000000..76ff556 --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs @@ -0,0 +1,182 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace PathCreation.Utility +{ + + /// Collection of functions related to cubic bezier curves + /// (a curve with a start and end 'anchor' point, and two 'control' points to define the shape of the curve between the anchors) + public static class CubicBezierUtility + { + /// Returns point at time 't' (between 0 and 1) along bezier curve defined by 4 points (anchor_1, control_1, control_2, anchor_2) + public static Vector3 EvaluateCurve(Vector3[] points, float t) + { + Debug.Assert(points.Length == 4, "Incorrect number of points supplied to cubic bezier function. Expected 4, but received " + points.Length); + if (points.Length >= 4) + { + return EvaluateCurve(points[0], points[1], points[2], points[3], t); + } + return Vector3.zero; + } + + /// Returns point at time 't' (between 0 and 1) along bezier curve defined by 4 points (anchor_1, control_1, control_2, anchor_2) + public static Vector3 EvaluateCurve(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t) + { + t = Mathf.Clamp01(t); + return (1 - t) * (1 - t) * (1 - t) * a1 + 3 * (1 - t) * (1 - t) * t * c1 + 3 * (1 - t) * t * t * c2 + t * t * t * a2; + } + + /// Returns a vector tangent to the point at time 't' + /// This is the vector tangent to the curve at that point + public static Vector3 EvaluateCurveDerivative(Vector3[] points, float t) + { + Debug.Assert(points.Length == 4, "Incorrect number of points supplied to cubic bezier function. Expected 4, but received " + points.Length); + if (points.Length >= 4) + { + return EvaluateCurveDerivative(points[0], points[1], points[2], points[3], t); + } + return Vector3.zero; + } + + /// Calculates the derivative of the curve at time 't' + /// This is the vector tangent to the curve at that point + public static Vector3 EvaluateCurveDerivative(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t) + { + t = Mathf.Clamp01(t); + return 3 * (1 - t) * (1 - t) * (c1 - a1) + 6 * (1 - t) * t * (c2 - c1) + 3 * t * t * (a2 - c2); + } + + /// Returns the second derivative of the curve at time 't' + public static Vector3 EvaluateCurveSecondDerivative(Vector3[] points, float t) + { + Debug.Assert(points.Length == 4, "Incorrect number of points supplied to cubic bezier function. Expected 4, but received " + points.Length); + if (points.Length >= 4) + { + return EvaluateCurveSecondDerivative(points[0], points[1], points[2], points[3], t); + } + return Vector3.zero; + } + + ///Returns the second derivative of the curve at time 't' + public static Vector3 EvaluateCurveSecondDerivative(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t) + { + t = Mathf.Clamp01(t); + return 6 * (1 - t) * (c2 - 2 * c1 + a1) + 6 * t * (a2 - 2 * c2 + c1); + } + + + /// Calculates the normal vector (vector perpendicular to the curve) at specified time + public static Vector3 Normal(Vector3[] points, float t) + { + Debug.Assert(points.Length == 4, "Incorrect number of points supplied to cubic bezier function. Expected 4, but received " + points.Length); + if (points.Length >= 4) + { + return Normal(points[0], points[1], points[2], points[3], t); + } + return Vector3.zero; + } + + /// Calculates the normal vector (vector perpendicular to the curve) at specified time + public static Vector3 Normal(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t) + { + Vector3 tangent = EvaluateCurveDerivative(a1, c1, c2, a2, t); + Vector3 nextTangent = EvaluateCurveSecondDerivative(a1, c1, c2, a2, t); + Vector3 c = Vector3.Cross(nextTangent, tangent); + return Vector3.Cross(c, tangent).normalized; + } + + public static Bounds CalculateBounds(Vector3[] points) + { + Debug.Assert(points.Length == 4, "Incorrect number of points supplied to cubic bezier function. Expected 4, but received " + points.Length); + MinMax3D minMax = new MinMax3D(); + if (points.Length >= 4) + { + minMax.AddValue(points[0]); + minMax.AddValue(points[3]); + + List extremePointTimes = ExtremePointTimes(points[0], points[1], points[2], points[3]); + foreach (float t in extremePointTimes) + { + minMax.AddValue(CubicBezierUtility.EvaluateCurve(points, t)); + } + } + + return new Bounds((minMax.Min + minMax.Max) / 2, minMax.Max - minMax.Min); + } + + /// Splits curve into two curves at time t. Returns 2 arrays of 4 points. + public static Vector3[][] SplitCurve(Vector3[] points, float t) + { + Vector3 a1 = Vector3.Lerp(points[0], points[1], t); + Vector3 a2 = Vector3.Lerp(points[1], points[2], t); + Vector3 a3 = Vector3.Lerp(points[2], points[3], t); + Vector3 b1 = Vector3.Lerp(a1, a2, t); + Vector3 b2 = Vector3.Lerp(a2, a3, t); + Vector3 pointOnCurve = Vector3.Lerp(b1, b2, t); + + return new Vector3[][] { + new Vector3[] { points[0], a1, b1, pointOnCurve }, + new Vector3[] { pointOnCurve, b2, a3, points[3] } + }; + } + + // Crude, but fast estimation of curve length. + public static float EstimateCurveLength(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) + { + float controlNetLength = (p0 - p1).magnitude + (p1 - p2).magnitude + (p2 - p3).magnitude; + float estimatedCurveLength = (p0 - p3).magnitude + controlNetLength / 2f; + return estimatedCurveLength; + } + + /// Times of stationary points on curve (points where derivative is zero on any axis) + public static List ExtremePointTimes(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) + { + // coefficients of derivative function + Vector3 a = 3 * (-p0 + 3 * p1 - 3 * p2 + p3); + Vector3 b = 6 * (p0 - 2 * p1 + p2); + Vector3 c = 3 * (p1 - p0); + + List times = new List(); + times.AddRange(StationaryPointTimes(a.x, b.x, c.x)); + times.AddRange(StationaryPointTimes(a.y, b.y, c.y)); + times.AddRange(StationaryPointTimes(a.z, b.z, c.z)); + return times; + } + + + // Finds times of stationary points on curve defined by ax^2 + bx + c. + // Only times between 0 and 1 are considered as Bezier only uses values in that range + static IEnumerable StationaryPointTimes(float a, float b, float c) + { + List times = new List(); + + // from quadratic equation: y = [-b +- sqrt(b^2 - 4ac)]/2a + if (a != 0) + { + float discriminant = b * b - 4 * a * c; + if (discriminant >= 0) + { + float s = Mathf.Sqrt(discriminant); + float t1 = (-b + s) / (2 * a); + if (t1 >= 0 && t1 <= 1) + { + times.Add(t1); + } + + if (discriminant != 0) + { + float t2 = (-b - s) / (2 * a); + + if (t2 >= 0 && t2 <= 1) + { + times.Add(t2); + } + } + } + } + return times; + } + + } +} diff --git a/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs.meta b/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs.meta new file mode 100644 index 0000000..df965d8 --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/CubicBezierUtility.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: c66a6536bb2d641558729d6da1ec7594 +timeCreated: 1519565538 +licenseType: Pro +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Utility/MathUtility.cs b/PathCreator/Core/Scripts/Utility/MathUtility.cs new file mode 100644 index 0000000..3fac05c --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/MathUtility.cs @@ -0,0 +1,100 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace PathCreation.Utility +{ + public static class MathUtility + { + + public static bool LineSegmentsIntersect(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2) + { + float d = (b2.x - b1.x) * (a1.y - a2.y) - (a1.x - a2.x) * (b2.y - b1.y); + if (d == 0) + return false; + float t = ((b1.y - b2.y) * (a1.x - b1.x) + (b2.x - b1.x) * (a1.y - b1.y)) / d; + float u = ((a1.y - a2.y) * (a1.x - b1.x) + (a2.x - a1.x) * (a1.y - b1.y)) / d; + + return t >= 0 && t <= 1 && u >= 0 && u <= 1; + } + + public static bool LinesIntersect(Vector2 a1, Vector2 a2, Vector2 a3, Vector2 a4) + { + return (a1.x - a2.x) * (a3.y - a4.y) - (a1.y - a2.y) * (a3.x - a4.x) != 0; + } + + public static Vector2 PointOfLineLineIntersection(Vector2 a1, Vector2 a2, Vector2 a3, Vector2 a4) + { + float d = (a1.x - a2.x) * (a3.y - a4.y) - (a1.y - a2.y) * (a3.x - a4.x); + if (d == 0) + { + Debug.LogError("Lines are parallel, please check that this is not the case before calling line intersection method"); + return Vector2.zero; + } + else + { + float n = (a1.x - a3.x) * (a3.y - a4.y) - (a1.y - a3.y) * (a3.x - a4.x); + float t = n / d; + return a1 + (a2 - a1) * t; + } + } + + + + public static Vector2 ClosestPointOnLineSegment(Vector2 p, Vector2 a, Vector2 b) + { + Vector2 aB = b - a; + Vector2 aP = p - a; + float sqrLenAB = aB.sqrMagnitude; + + if (sqrLenAB == 0) + return a; + + float t = Mathf.Clamp01(Vector2.Dot(aP, aB) / sqrLenAB); + return a + aB * t; + } + + public static float DistanceToLineSegment(Vector2 p, Vector2 a, Vector2 b) + { + Vector2 aB = b - a; + Vector2 aP = p - a; + float sqrLenAB = aB.sqrMagnitude; + + if (sqrLenAB == 0) + return aP.magnitude; + + float t = Mathf.Clamp01(Vector2.Dot(aP, aB) / sqrLenAB); + return Vector2.Distance(p, a + aB * t); + } + + public static int SideOfLine(Vector2 a, Vector2 b, Vector2 c) + { + return (int)Mathf.Sign((c.x - a.x) * (-b.y + a.y) + (c.y - a.y) * (b.x - a.x)); + } + + /// returns the smallest angle between ABC. Never greater than 180 + public static float MinAngle(Vector3 a, Vector3 b, Vector3 c) + { + return Vector3.Angle((a - b), (c - b)); + } + + public static bool PointInTriangle(Vector2 a, Vector2 b, Vector2 c, Vector2 p) + { + float area = 0.5f * (-b.y * c.x + a.y * (-b.x + c.x) + a.x * (b.y - c.y) + b.x * c.y); + float s = 1 / (2 * area) * (a.y * c.x - a.x * c.y + (c.y - a.y) * p.x + (a.x - c.x) * p.y); + float t = 1 / (2 * area) * (a.x * b.y - a.y * b.x + (a.y - b.y) * p.x + (b.x - a.x) * p.y); + return s >= 0 && t >= 0 && (s + t) <= 1; + } + + public static bool PointsAreClockwise(Vector2[] points) + { + float signedArea = 0; + for (int i = 0; i < points.Length; i++) + { + int nextIndex = (i + 1) % points.Length; + signedArea += (points[nextIndex].x - points[i].x) * (points[nextIndex].y + points[i].y); + } + + return signedArea >= 0; + } + } +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Utility/MathUtility.cs.meta b/PathCreator/Core/Scripts/Utility/MathUtility.cs.meta new file mode 100644 index 0000000..277ff19 --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/MathUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 096bae1d1ed960d4bb7605dce06767c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs b/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs new file mode 100644 index 0000000..69ab950 --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs @@ -0,0 +1,139 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace PathCreation.Utility +{ + public static class VertexPathUtility + { + + public static PathSplitData SplitBezierPathByAngleError(BezierPath bezierPath, float maxAngleError, float minVertexDst, float accuracy) + { + PathSplitData splitData = new PathSplitData(); + + splitData.vertices.Add(bezierPath[0]); + splitData.tangents.Add(CubicBezierUtility.EvaluateCurveDerivative(bezierPath.GetPointsInSegment(0), 0).normalized); + splitData.cumulativeLength.Add(0); + splitData.anchorVertexMap.Add(0); + splitData.minMax.AddValue(bezierPath[0]); + + Vector3 prevPointOnPath = bezierPath[0]; + Vector3 lastAddedPoint = bezierPath[0]; + + float currentPathLength = 0; + float dstSinceLastVertex = 0; + + // Go through all segments and split up into vertices + for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) + { + Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); + float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); + int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy); + float increment = 1f / divisions; + + for (float t = increment; t <= 1; t += increment) + { + bool isLastPointOnPath = (t + increment > 1 && segmentIndex == bezierPath.NumSegments - 1); + if (isLastPointOnPath) + { + t = 1; + } + Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints, t); + Vector3 nextPointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints, t + increment); + + // angle at current point on path + float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath); + // angle between the last added vertex, the current point on the path, and the next point on the path + float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath); + float angleError = Mathf.Max(localAngle, angleFromPrevVertex); + + + if ((angleError > maxAngleError && dstSinceLastVertex >= minVertexDst) || isLastPointOnPath) + { + + currentPathLength += (lastAddedPoint - pointOnPath).magnitude; + splitData.cumulativeLength.Add(currentPathLength); + splitData.vertices.Add(pointOnPath); + splitData.tangents.Add(CubicBezierUtility.EvaluateCurveDerivative(segmentPoints, t).normalized); + splitData.minMax.AddValue(pointOnPath); + dstSinceLastVertex = 0; + lastAddedPoint = pointOnPath; + } + else + { + dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; + } + prevPointOnPath = pointOnPath; + } + splitData.anchorVertexMap.Add(splitData.vertices.Count - 1); + } + return splitData; + } + + public static PathSplitData SplitBezierPathEvenly(BezierPath bezierPath, float spacing, float accuracy) + { + PathSplitData splitData = new PathSplitData(); + + splitData.vertices.Add(bezierPath[0]); + splitData.tangents.Add(CubicBezierUtility.EvaluateCurveDerivative(bezierPath.GetPointsInSegment(0), 0).normalized); + splitData.cumulativeLength.Add(0); + splitData.anchorVertexMap.Add(0); + splitData.minMax.AddValue(bezierPath[0]); + + Vector3 prevPointOnPath = bezierPath[0]; + Vector3 lastAddedPoint = bezierPath[0]; + + float currentPathLength = 0; + float dstSinceLastVertex = 0; + + // Go through all segments and split up into vertices + for (int segmentIndex = 0; segmentIndex < bezierPath.NumSegments; segmentIndex++) + { + Vector3[] segmentPoints = bezierPath.GetPointsInSegment(segmentIndex); + float estimatedSegmentLength = CubicBezierUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]); + int divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy); + float increment = 1f / divisions; + + for (float t = increment; t <= 1; t += increment) + { + bool isLastPointOnPath = (t + increment > 1 && segmentIndex == bezierPath.NumSegments - 1); + if (isLastPointOnPath) + { + t = 1; + } + Vector3 pointOnPath = CubicBezierUtility.EvaluateCurve(segmentPoints, t); + dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude; + + // If vertices are now too far apart, go back by amount we overshot by + if (dstSinceLastVertex > spacing) { + float overshootDst = dstSinceLastVertex - spacing; + pointOnPath += (prevPointOnPath-pointOnPath).normalized * overshootDst; + t-=increment; + } + + if (dstSinceLastVertex >= spacing || isLastPointOnPath) + { + currentPathLength += (lastAddedPoint - pointOnPath).magnitude; + splitData.cumulativeLength.Add(currentPathLength); + splitData.vertices.Add(pointOnPath); + splitData.tangents.Add(CubicBezierUtility.EvaluateCurveDerivative(segmentPoints, t).normalized); + splitData.minMax.AddValue(pointOnPath); + dstSinceLastVertex = 0; + lastAddedPoint = pointOnPath; + } + prevPointOnPath = pointOnPath; + } + splitData.anchorVertexMap.Add(splitData.vertices.Count - 1); + } + return splitData; + } + + + public class PathSplitData { + public List vertices = new List(); + public List tangents = new List(); + public List cumulativeLength = new List(); + public List anchorVertexMap = new List(); + public MinMax3D minMax = new MinMax3D(); + } + } +} \ No newline at end of file diff --git a/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs.meta b/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs.meta new file mode 100644 index 0000000..a36c3a7 --- /dev/null +++ b/PathCreator/Core/Scripts/Utility/VertexPathUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5505526f3c472e8499fa7ce169410ace +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Settings.meta b/PathCreator/Core/Settings.meta new file mode 100644 index 0000000..8ecf40c --- /dev/null +++ b/PathCreator/Core/Settings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f9b1508dc64040469b4b1ee117d27a9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Core/Settings/GlobalDisplaySettings.asset b/PathCreator/Core/Settings/GlobalDisplaySettings.asset new file mode 100644 index 0000000..89ea830 --- /dev/null +++ b/PathCreator/Core/Settings/GlobalDisplaySettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 46d5e8d803e252f4499b52e657f8e1a2, type: 3} + m_Name: GlobalDisplaySettings + m_EditorClassIdentifier: + anchorSize: 10 + controlSize: 7 + hideAutoControls: 1 + anchorShape: 0 + controlShape: 0 + anchor: {r: 0.95, g: 0.25, b: 0.25, a: 0.85} + anchorHighlighted: {r: 1, g: 0.4, b: 0.4, a: 1} + anchorSelected: {r: 1, g: 1, b: 1, a: 1} + control: {r: 0.35, g: 0.6, b: 1, a: 0.85} + controlHighlighted: {r: 0.6, g: 0.6, b: 1, a: 1} + controlSelected: {r: 1, g: 1, b: 1, a: 1} + handleDisabled: {r: 1, g: 1, b: 1, a: 0.2} + controlLine: {r: 0, g: 0, b: 0, a: 0.7254902} + bezierPath: {r: 0, g: 1, b: 0, a: 1} + highlightedPath: {r: 1, g: 0.6, b: 0, a: 1} + bounds: {r: 1, g: 1, b: 1, a: 0.4} + segmentBounds: {r: 1, g: 1, b: 1, a: 0.4} + vertexPath: {r: 1, g: 1, b: 1, a: 1} + vertex: {r: 0, g: 0, b: 0, a: 1} + normals: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + normalsLength: 0.1 diff --git a/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta b/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta new file mode 100644 index 0000000..b4c86c8 --- /dev/null +++ b/PathCreator/Core/Settings/GlobalDisplaySettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 35fab21375bf12f4b96eba4dafa4ad30 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples.meta b/PathCreator/Examples.meta new file mode 100644 index 0000000..5bffe72 --- /dev/null +++ b/PathCreator/Examples.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 856133338e5935c4ea7215ea837b00da +folderAsset: yes +timeCreated: 1546380202 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials.meta b/PathCreator/Examples/Materials.meta new file mode 100644 index 0000000..4b6d20e --- /dev/null +++ b/PathCreator/Examples/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba68311e0a74e694fbd8f64185d03175 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Black.mat b/PathCreator/Examples/Materials/Black.mat new file mode 100644 index 0000000..11377dd --- /dev/null +++ b/PathCreator/Examples/Materials/Black.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Black + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.2 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.103773594, g: 0.103773594, b: 0.103773594, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Black.mat.meta b/PathCreator/Examples/Materials/Black.mat.meta new file mode 100644 index 0000000..5c81f46 --- /dev/null +++ b/PathCreator/Examples/Materials/Black.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69561553b86bde34da494d1ffed3ba45 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Blue.mat b/PathCreator/Examples/Materials/Blue.mat new file mode 100644 index 0000000..16b3943 --- /dev/null +++ b/PathCreator/Examples/Materials/Blue.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Blue + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.2 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.30584726, g: 0.5791927, b: 0.8207547, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Blue.mat.meta b/PathCreator/Examples/Materials/Blue.mat.meta new file mode 100644 index 0000000..dff321d --- /dev/null +++ b/PathCreator/Examples/Materials/Blue.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5c270a73328a99e439fec44a0cf3a17d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Green.mat b/PathCreator/Examples/Materials/Green.mat new file mode 100644 index 0000000..ad0bc71 --- /dev/null +++ b/PathCreator/Examples/Materials/Green.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Green + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.2 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.46839345, g: 0.81960785, b: 0.30588233, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Green.mat.meta b/PathCreator/Examples/Materials/Green.mat.meta new file mode 100644 index 0000000..cfbf57e --- /dev/null +++ b/PathCreator/Examples/Materials/Green.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9ca80467a033e954a8b9ad8601d6f4c7 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Red.mat b/PathCreator/Examples/Materials/Red.mat new file mode 100644 index 0000000..c792f37 --- /dev/null +++ b/PathCreator/Examples/Materials/Red.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Red + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.2 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.9528302, g: 0.23820755, b: 0.26590508, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Red.mat.meta b/PathCreator/Examples/Materials/Red.mat.meta new file mode 100644 index 0000000..a21a9ae --- /dev/null +++ b/PathCreator/Examples/Materials/Red.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01763a1c279a5cd4d8f897f6b6cdbae5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Road Texture.png b/PathCreator/Examples/Materials/Road Texture.png new file mode 100644 index 0000000..1e6076f Binary files /dev/null and b/PathCreator/Examples/Materials/Road Texture.png differ diff --git a/PathCreator/Examples/Materials/Road Texture.png.meta b/PathCreator/Examples/Materials/Road Texture.png.meta new file mode 100644 index 0000000..310bf25 --- /dev/null +++ b/PathCreator/Examples/Materials/Road Texture.png.meta @@ -0,0 +1,74 @@ +fileFormatVersion: 2 +guid: eef7def8a6d89134284369bbe0145515 +timeCreated: 1546381451 +licenseType: Pro +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Road Underside.mat b/PathCreator/Examples/Materials/Road Underside.mat new file mode 100644 index 0000000..13a6dc3 --- /dev/null +++ b/PathCreator/Examples/Materials/Road Underside.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Road Underside + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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: 10.66} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 10.66} + 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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.047169805, g: 0.047169805, b: 0.047169805, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Road Underside.mat.meta b/PathCreator/Examples/Materials/Road Underside.mat.meta new file mode 100644 index 0000000..feab809 --- /dev/null +++ b/PathCreator/Examples/Materials/Road Underside.mat.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c1d8d882244c5344fa86a8fd23246f81 +timeCreated: 1519840206 +licenseType: Pro +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Materials/Road.mat b/PathCreator/Examples/Materials/Road.mat new file mode 100644 index 0000000..3af20a1 --- /dev/null +++ b/PathCreator/Examples/Materials/Road.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Road + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _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: 2800000, guid: eef7def8a6d89134284369bbe0145515, type: 3} + m_Scale: {x: 1, y: 8} + 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} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.153 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/PathCreator/Examples/Materials/Road.mat.meta b/PathCreator/Examples/Materials/Road.mat.meta new file mode 100644 index 0000000..6ccc41c --- /dev/null +++ b/PathCreator/Examples/Materials/Road.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b5df4735192845740bd11076dc9cdc99 +timeCreated: 1546381442 +licenseType: Pro +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scenes.meta b/PathCreator/Examples/Scenes.meta new file mode 100644 index 0000000..e53a358 --- /dev/null +++ b/PathCreator/Examples/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fa8a76a6a31dba43b3f224aa4c8ef31 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scenes/Follow Path.unity b/PathCreator/Examples/Scenes/Follow Path.unity new file mode 100644 index 0000000..81e63e8 --- /dev/null +++ b/PathCreator/Examples/Scenes/Follow Path.unity @@ -0,0 +1,813 @@ +%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: 0} + m_IndirectSpecularColor: {r: 0.44657844, g: 0.49641222, b: 0.57481694, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 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: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !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 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &178586474 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1261160551110262, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 178586475} + - component: {fileID: 178586477} + - component: {fileID: 178586476} + m_Layer: 0 + m_Name: Up + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &178586475 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4332630432681102, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 178586474} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.75, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 260492340} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &178586476 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 23762714413822036, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 178586474} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 9ca80467a033e954a8b9ad8601d6f4c7, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &178586477 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 33471940085706574, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 178586474} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &184727957 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 184727959} + - component: {fileID: 184727958} + m_Layer: 0 + m_Name: Path + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &184727958 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 184727957} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e5ac92bc18f545cc84cd886ece82b4d, type: 3} + m_Name: + m_EditorClassIdentifier: + editorData: + _bezierPath: + points: + - {x: -4.350084, y: -0.6192361, z: 0.36958665} + - {x: -4.3119984, y: -0.27224493, z: 2.7627096} + - {x: -2.4967203, y: 1.7331059, z: 2.2348938} + - {x: 0.1517717, y: 1.7120849, z: 2.2182245} + - {x: 2.800264, y: 1.6910639, z: 2.2015553} + - {x: 4.16408, y: -0.44785511, z: 2.1293974} + - {x: 4.3446193, y: -0.6192361, z: 0.36958665} + - {x: 4.5251584, y: -0.7906171, z: -1.390224} + - {x: 3.3285677, y: 1.0417786, z: -2.7135065} + - {x: 0.8092283, y: 1.108709, z: -2.8377376} + - {x: -1.7101113, y: 1.1756394, z: -2.9619687} + - {x: -4.3881693, y: -0.9662273, z: -2.0235362} + isClosed: 1 + localPosition: {x: 0, y: 0, z: 0} + space: 0 + controlMode: 1 + autoControlLength: 0.3 + boundsUpToDate: 1 + pivot: {x: 0.0059502125, y: 0.5274788, z: -0.2848444} + bounds: + m_Center: {x: 0.0059502125, y: 0.5274788, z: -0.2848444} + m_Extent: {x: 4.356436, y: 1.1847702, z: 2.5637088} + rotation: {x: 0, y: 0, z: 0, w: 1} + scale: {x: 2.1736755, y: 2.1736755, z: 2.1736755} + perAnchorNormalsAngle: + - 0 + - 0 + - 0 + - 0 + globalNormalsAngle: 69 + flipNormals: 0 + vertexPathUpToDate: 0 + vertexPathMaxAngleError: 0.3 + vertexPathMinVertexSpacing: 0 + pathTransformationEnabled: 1 + showPathBounds: 0 + showPerSegmentBounds: 0 + displayAnchorPoints: 1 + displayControlPoints: 1 + bezierHandleScale: 1 + globalDisplaySettingsFoldout: 0 + keepConstantHandleSize: 0 + vertexHandleSize: 0.2 + showNormalsInVertexMode: 0 + showDisplayOptions: 0 + showPathOptions: 1 + showVertexPathDisplayOptions: 0 + showVertexPathOptions: 1 + showNormals: 1 + showNormalsHelpInfo: 0 + tabIndex: 0 +--- !u!4 &184727959 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 184727957} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &260492339 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1898765153087064, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 260492340} + m_Layer: 0 + m_Name: Model + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &260492340 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4613831018837782, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 260492339} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: + - {fileID: 777118616} + - {fileID: 1637570425} + - {fileID: 178586475} + - {fileID: 1058266275} + - {fileID: 447391218} + m_Father: {fileID: 920376881} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &447391217 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1040639682956682, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 447391218} + - component: {fileID: 447391220} + - component: {fileID: 447391219} + m_Layer: 0 + m_Name: Right + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &447391218 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4684888935946578, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 447391217} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.75, y: 0, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 260492340} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &447391219 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 23606280284518030, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 447391217} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 01763a1c279a5cd4d8f897f6b6cdbae5, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &447391220 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 33200099712053450, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 447391217} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &777118615 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1503969683355944, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 777118616} + - component: {fileID: 777118618} + - component: {fileID: 777118617} + m_Layer: 0 + m_Name: Body + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &777118616 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4464063548295190, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 777118615} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 260492340} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &777118617 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 23731417647594800, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 777118615} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 69561553b86bde34da494d1ffed3ba45, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &777118618 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 33383125268350062, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 777118615} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &920376879 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1203775041438820, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 920376881} + - component: {fileID: 920376880} + m_Layer: 0 + m_Name: Character + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &920376880 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 920376879} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 33aabe1a2dec93349b06e6e5dc51bc06, type: 3} + m_Name: + m_EditorClassIdentifier: + pathCreator: {fileID: 184727958} + endOfPathInstruction: 0 + speed: 5 +--- !u!4 &920376881 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4420301013693702, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 920376879} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 260492340} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1058266274 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1198493738150434, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1058266275} + - component: {fileID: 1058266277} + - component: {fileID: 1058266276} + m_Layer: 0 + m_Name: Left + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1058266275 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4190488380692570, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1058266274} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.75, y: 0, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 260492340} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1058266276 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 23009601051369370, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1058266274} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 01763a1c279a5cd4d8f897f6b6cdbae5, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &1058266277 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 33555401627542824, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1058266274} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1308570505 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1308570508} + - component: {fileID: 1308570507} + - component: {fileID: 1308570506} + 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 &1308570506 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_Enabled: 1 +--- !u!20 &1308570507 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + 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.3 + far clip plane: 1000 + 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 &1308570508 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1308570505} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1637570424 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 1517205665874768, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1637570425} + - component: {fileID: 1637570427} + - component: {fileID: 1637570426} + m_Layer: 0 + m_Name: Forward + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1637570425 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 4341172907228852, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1637570424} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.75} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 260492340} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1637570426 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 23060902572419204, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1637570424} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 5c270a73328a99e439fec44a0cf3a17d, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &1637570427 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 33794067100287920, guid: fddcd04ee5c9ef84a90dff56078b017c, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1637570424} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1844901808 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1844901810} + - component: {fileID: 1844901809} + 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 &1844901809 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1844901808} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1844901810 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1844901808} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} diff --git a/PathCreator/Examples/Scenes/Follow Path.unity.meta b/PathCreator/Examples/Scenes/Follow Path.unity.meta new file mode 100644 index 0000000..5ccda69 --- /dev/null +++ b/PathCreator/Examples/Scenes/Follow Path.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c1e712ad623461e418f182144cf61384 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scenes/Object Path.unity b/PathCreator/Examples/Scenes/Object Path.unity new file mode 100644 index 0000000..79c4667 --- /dev/null +++ b/PathCreator/Examples/Scenes/Object Path.unity @@ -0,0 +1,800 @@ +%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: 0} + m_IndirectSpecularColor: {r: 0.44657844, g: 0.49641222, b: 0.57481694, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 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: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !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 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &315902406 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 315902409} + - component: {fileID: 315902408} + - component: {fileID: 315902407} + m_Layer: 0 + m_Name: Sphere (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &315902407 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 315902406} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &315902408 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 315902406} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &315902409 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 315902406} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 6.54, y: -1.75, z: 6.2} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 762964052} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &729862344 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 729862347} + - component: {fileID: 729862346} + - component: {fileID: 729862345} + m_Layer: 0 + m_Name: Sphere (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &729862345 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 729862344} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &729862346 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 729862344} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &729862347 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 729862344} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 9.1, y: 6.39, z: -3.51} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 762964052} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &762964051 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 762964052} + m_Layer: 0 + m_Name: Waypoints + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &762964052 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 762964051} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1295418884} + - {fileID: 2107402768} + - {fileID: 1544419622} + - {fileID: 729862347} + - {fileID: 315902409} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &839433805 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 839433807} + - component: {fileID: 839433808} + - component: {fileID: 839433806} + m_Layer: 0 + m_Name: Path + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!96 &839433806 +TrailRenderer: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 839433805} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 0 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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_Time: 5 + m_Parameters: + serializedVersion: 2 + widthMultiplier: 1 + widthCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + colorGradient: + serializedVersion: 2 + key0: {r: 0.16470587, g: 0.47597936, b: 1, a: 1} + key1: {r: 0.7216981, g: 0.89121073, b: 1, a: 0} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + numCornerVertices: 0 + numCapVertices: 8 + alignment: 0 + textureMode: 0 + generateLightingData: 0 + m_MinVertexDistance: 0.1 + m_Autodestruct: 0 + m_Emitting: 1 +--- !u!4 &839433807 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 839433805} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &839433808 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 839433805} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f417e74782fffcd40b77b0fdbe82af04, type: 3} + m_Name: + m_EditorClassIdentifier: + waypoints: + - {fileID: 1295418884} + - {fileID: 2107402768} + - {fileID: 1544419622} + - {fileID: 729862347} + - {fileID: 315902409} + speed: 10 +--- !u!1 &1295418881 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1295418884} + - component: {fileID: 1295418883} + - component: {fileID: 1295418882} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1295418882 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1295418881} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &1295418883 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1295418881} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1295418884 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1295418881} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 762964052} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1407856702 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1407856705} + - component: {fileID: 1407856704} + - component: {fileID: 1407856703} + 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 &1407856703 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1407856702} + m_Enabled: 1 +--- !u!20 &1407856704 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1407856702} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + 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.3 + far clip plane: 1000 + 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 &1407856705 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1407856702} + m_LocalRotation: {x: 0.12969545, y: -0.1184572, z: 0.015607949, w: 0.9843289} + m_LocalPosition: {x: 3, y: 8.92, z: -18.23} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 15.012001, y: -13.724001, z: 0} +--- !u!1 &1544419619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1544419622} + - component: {fileID: 1544419621} + - component: {fileID: 1544419620} + m_Layer: 0 + m_Name: Sphere (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1544419620 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1544419619} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &1544419621 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1544419619} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1544419622 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1544419619} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -6.17, y: 6.39, z: -10.04} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 762964052} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1612519935 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1612519937} + - component: {fileID: 1612519936} + 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 &1612519936 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1612519935} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1612519937 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1612519935} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &2107402765 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2107402768} + - component: {fileID: 2107402767} + - component: {fileID: 2107402766} + m_Layer: 0 + m_Name: Sphere (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &2107402766 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2107402765} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + 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 +--- !u!33 &2107402767 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2107402765} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &2107402768 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2107402765} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -6.17, y: 1.4746776, z: 2.67} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 762964052} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/PathCreator/Examples/Scenes/Object Path.unity.meta b/PathCreator/Examples/Scenes/Object Path.unity.meta new file mode 100644 index 0000000..d2ff05c --- /dev/null +++ b/PathCreator/Examples/Scenes/Object Path.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3145d476fb2c7f242875d23b77320de4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scenes/Road.unity b/PathCreator/Examples/Scenes/Road.unity new file mode 100644 index 0000000..753466a --- /dev/null +++ b/PathCreator/Examples/Scenes/Road.unity @@ -0,0 +1,584 @@ +%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: 8 + 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: 0} + m_IndirectSpecularColor: {r: 0.44657844, g: 0.49641222, b: 0.57481694, a: 1} +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 9 + m_Resolution: 2 + m_BakeResolution: 40 + m_TextureWidth: 1024 + m_TextureHeight: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 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: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !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 + m_NavMeshData: {fileID: 0} +--- !u!43 &732243162 +Mesh: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: + serializedVersion: 8 + m_SubMeshes: + - serializedVersion: 2 + firstByte: 0 + indexCount: 1440 + topology: 0 + firstVertex: 0 + vertexCount: 1922 + localAABB: + m_Center: {x: 1.6215771, y: 0.65933514, z: -2.181013} + m_Extent: {x: 3.8682241, y: 1.1189339, z: 4.323511} + - serializedVersion: 2 + firstByte: 2880 + indexCount: 1440 + topology: 0 + firstVertex: 2 + vertexCount: 1922 + localAABB: + m_Center: {x: 1.6072979, y: 0.5470645, z: -2.1961308} + m_Extent: {x: 3.9050364, y: 1.1205606, z: 4.3664474} + - serializedVersion: 2 + firstByte: 5760 + indexCount: 2880 + topology: 0 + firstVertex: 4 + vertexCount: 1924 + localAABB: + m_Center: {x: 1.6072979, y: 0.6023864, z: -2.1961308} + m_Extent: {x: 3.9050364, y: 1.1758826, z: 4.3664474} + m_Shapes: + vertices: [] + shapes: [] + channels: [] + fullWeights: [] + m_BindPose: [] + m_BoneNameHashes: + m_RootBoneNameHash: 0 + m_MeshCompression: 0 + m_IsReadable: 1 + m_KeepVertices: 0 + m_KeepIndices: 0 + m_IndexBuffer: 0000080001000100080009000800100009000900100011001000180011001100180019001800200019001900200021002000280021002100280029002800300029002900300031003000380031003100380039003800400039003900400041004000480041004100480049004800500049004900500051005000580051005100580059005800600059005900600061006000680061006100680069006800700069006900700071007000780071007100780079007800800079007900800081008000880081008100880089008800900089008900900091009000980091009100980099009800a00099009900a000a100a000a800a100a100a800a900a800b000a900a900b000b100b000b800b100b100b800b900b800c000b900b900c000c100c000c800c100c100c800c900c800d000c900c900d000d100d000d800d100d100d800d900d800e000d900d900e000e100e000e800e100e100e800e900e800f000e900e900f000f100f000f800f100f100f800f900f8000001f900f900000101010001080101010101080109010801100109010901100111011001180111011101180119011801200119011901200121012001280121012101280129012801300129012901300131013001380131013101380139013801400139013901400141014001480141014101480149014801500149014901500151015001580151015101580159015801600159015901600161016001680161016101680169016801700169016901700171017001780171017101780179017801800179017901800181018001880181018101880189018801900189018901900191019001980191019101980199019801a00199019901a001a101a001a801a101a101a801a901a801b001a901a901b001b101b001b801b101b101b801b901b801c001b901b901c001c101c001c801c101c101c801c901c801d001c901c901d001d101d001d801d101d101d801d901d801e001d901d901e001e101e001e801e101e101e801e901e801f001e901e901f001f101f001f801f101f101f801f901f8010002f901f901000201020002080201020102080209020802100209020902100211021002180211021102180219021802200219021902200221022002280221022102280229022802300229022902300231023002380231023102380239023802400239023902400241024002480241024102480249024802500249024902500251025002580251025102580259025802600259025902600261026002680261026102680269026802700269026902700271027002780271027102780279027802800279027902800281028002880281028102880289028802900289028902900291029002980291029102980299029802a00299029902a002a102a002a802a102a102a802a902a802b002a902a902b002b102b002b802b102b102b802b902b802c002b902b902c002c102c002c802c102c102c802c902c802d002c902c902d002d102d002d802d102d102d802d902d802e002d902d902e002e102e002e802e102e102e802e902e802f002e902e902f002f102f002f802f102f102f802f902f8020003f902f902000301030003080301030103080309030803100309030903100311031003180311031103180319031803200319031903200321032003280321032103280329032803300329032903300331033003380331033103380339033803400339033903400341034003480341034103480349034803500349034903500351035003580351035103580359035803600359035903600361036003680361036103680369036803700369036903700371037003780371037103780379037803800379037903800381038003880381038103880389038803900389038903900391039003980391039103980399039803a00399039903a003a103a003a803a103a103a803a903a803b003a903a903b003b103b003b803b103b103b803b903b803c003b903b903c003c103c003c803c103c103c803c903c803d003c903c903d003d103d003d803d103d103d803d903d803e003d903d903e003e103e003e803e103e103e803e903e803f003e903e903f003f103f003f803f103f103f803f903f8030004f903f903000401040004080401040104080409040804100409040904100411041004180411041104180419041804200419041904200421042004280421042104280429042804300429042904300431043004380431043104380439043804400439043904400441044004480441044104480449044804500449044904500451045004580451045104580459045804600459045904600461046004680461046104680469046804700469046904700471047004780471047104780479047804800479047904800481048004880481048104880489048804900489048904900491049004980491049104980499049804a00499049904a004a104a004a804a104a104a804a904a804b004a904a904b004b104b004b804b104b104b804b904b804c004b904b904c004c104c004c804c104c104c804c904c804d004c904c904d004d104d004d804d104d104d804d904d804e004d904d904e004e104e004e804e104e104e804e904e804f004e904e904f004f104f004f804f104f104f804f904f8040005f904f904000501050005080501050105080509050805100509050905100511051005180511051105180519051805200519051905200521052005280521052105280529052805300529052905300531053005380531053105380539053805400539053905400541054005480541054105480549054805500549054905500551055005580551055105580559055805600559055905600561056005680561056105680569056805700569056905700571057005780571057105780579057805800579057905800581058005880581058105880589058805900589058905900591059005980591059105980599059805a00599059905a005a105a005a805a105a105a805a905a805b005a905a905b005b105b005b805b105b105b805b905b805c005b905b905c005c105c005c805c105c105c805c905c805d005c905c905d005d105d005d805d105d105d805d905d805e005d905d905e005e105e005e805e105e105e805e905e805f005e905e905f005f105f005f805f105f105f805f905f8050006f905f905000601060006080601060106080609060806100609060906100611061006180611061106180619061806200619061906200621062006280621062106280629062806300629062906300631063006380631063106380639063806400639063906400641064006480641064106480649064806500649064906500651065006580651065106580659065806600659065906600661066006680661066106680669066806700669066906700671067006780671067106780679067806800679067906800681068006880681068106880689068806900689068906900691069006980691069106980699069806a00699069906a006a106a006a806a106a106a806a906a806b006a906a906b006b106b006b806b106b106b806b906b806c006b906b906c006c106c006c806c106c106c806c906c806d006c906c906d006d106d006d806d106d106d806d906d806e006d906d906e006e106e006e806e106e106e806e906e806f006e906e906f006f106f006f806f106f106f806f906f8060007f906f906000701070007080701070107080709070807100709070907100711071007180711071107180719071807200719071907200721072007280721072107280729072807300729072907300731073007380731073107380739073807400739073907400741074007480741074107480749074807500749074907500751075007580751075107580759075807600759075907600761076007680761076107680769076807700769076907700771077007780771077107780779077807800779077907800781070b000a00030003000a000200130012000b000b0012000a001b001a00130013001a001200230022001b001b0022001a002b002a00230023002a002200330032002b002b0032002a003b003a00330033003a003200430042003b003b0042003a004b004a00430043004a004200530052004b004b0052004a005b005a00530053005a005200630062005b005b0062005a006b006a00630063006a006200730072006b006b0072006a007b007a00730073007a007200830082007b007b0082007a008b008a00830083008a008200930092008b008b0092008a009b009a00930093009a009200a300a2009b009b00a2009a00ab00aa00a300a300aa00a200b300b200ab00ab00b200aa00bb00ba00b300b300ba00b200c300c200bb00bb00c200ba00cb00ca00c300c300ca00c200d300d200cb00cb00d200ca00db00da00d300d300da00d200e300e200db00db00e200da00eb00ea00e300e300ea00e200f300f200eb00eb00f200ea00fb00fa00f300f300fa00f20003010201fb00fb000201fa000b010a01030103010a010201130112010b010b0112010a011b011a01130113011a011201230122011b011b0122011a012b012a01230123012a012201330132012b012b0132012a013b013a01330133013a013201430142013b013b0142013a014b014a01430143014a014201530152014b014b0152014a015b015a01530153015a015201630162015b015b0162015a016b016a01630163016a016201730172016b016b0172016a017b017a01730173017a017201830182017b017b0182017a018b018a01830183018a018201930192018b018b0192018a019b019a01930193019a019201a301a2019b019b01a2019a01ab01aa01a301a301aa01a201b301b201ab01ab01b201aa01bb01ba01b301b301ba01b201c301c201bb01bb01c201ba01cb01ca01c301c301ca01c201d301d201cb01cb01d201ca01db01da01d301d301da01d201e301e201db01db01e201da01eb01ea01e301e301ea01e201f301f201eb01eb01f201ea01fb01fa01f301f301fa01f20103020202fb01fb010202fa010b020a02030203020a020202130212020b020b0212020a021b021a02130213021a021202230222021b021b0222021a022b022a02230223022a022202330232022b022b0232022a023b023a02330233023a023202430242023b023b0242023a024b024a02430243024a024202530252024b024b0252024a025b025a02530253025a025202630262025b025b0262025a026b026a02630263026a026202730272026b026b0272026a027b027a02730273027a027202830282027b027b0282027a028b028a02830283028a028202930292028b028b0292028a029b029a02930293029a029202a302a2029b029b02a2029a02ab02aa02a302a302aa02a202b302b202ab02ab02b202aa02bb02ba02b302b302ba02b202c302c202bb02bb02c202ba02cb02ca02c302c302ca02c202d302d202cb02cb02d202ca02db02da02d302d302da02d202e302e202db02db02e202da02eb02ea02e302e302ea02e202f302f202eb02eb02f202ea02fb02fa02f302f302fa02f20203030203fb02fb020203fa020b030a03030303030a030203130312030b030b0312030a031b031a03130313031a031203230322031b031b0322031a032b032a03230323032a032203330332032b032b0332032a033b033a03330333033a033203430342033b033b0342033a034b034a03430343034a034203530352034b034b0352034a035b035a03530353035a035203630362035b035b0362035a036b036a03630363036a036203730372036b036b0372036a037b037a03730373037a037203830382037b037b0382037a038b038a03830383038a038203930392038b038b0392038a039b039a03930393039a039203a303a2039b039b03a2039a03ab03aa03a303a303aa03a203b303b203ab03ab03b203aa03bb03ba03b303b303ba03b203c303c203bb03bb03c203ba03cb03ca03c303c303ca03c203d303d203cb03cb03d203ca03db03da03d303d303da03d203e303e203db03db03e203da03eb03ea03e303e303ea03e203f303f203eb03eb03f203ea03fb03fa03f303f303fa03f20303040204fb03fb030204fa030b040a04030403040a040204130412040b040b0412040a041b041a04130413041a041204230422041b041b0422041a042b042a04230423042a042204330432042b042b0432042a043b043a04330433043a043204430442043b043b0442043a044b044a04430443044a044204530452044b044b0452044a045b045a04530453045a045204630462045b045b0462045a046b046a04630463046a046204730472046b046b0472046a047b047a04730473047a047204830482047b047b0482047a048b048a04830483048a048204930492048b048b0492048a049b049a04930493049a049204a304a2049b049b04a2049a04ab04aa04a304a304aa04a204b304b204ab04ab04b204aa04bb04ba04b304b304ba04b204c304c204bb04bb04c204ba04cb04ca04c304c304ca04c204d304d204cb04cb04d204ca04db04da04d304d304da04d204e304e204db04db04e204da04eb04ea04e304e304ea04e204f304f204eb04eb04f204ea04fb04fa04f304f304fa04f20403050205fb04fb040205fa040b050a05030503050a050205130512050b050b0512050a051b051a05130513051a051205230522051b051b0522051a052b052a05230523052a052205330532052b052b0532052a053b053a05330533053a053205430542053b053b0542053a054b054a05430543054a054205530552054b054b0552054a055b055a05530553055a055205630562055b055b0562055a056b056a05630563056a056205730572056b056b0572056a057b057a05730573057a057205830582057b057b0582057a058b058a05830583058a058205930592058b058b0592058a059b059a05930593059a059205a305a2059b059b05a2059a05ab05aa05a305a305aa05a205b305b205ab05ab05b205aa05bb05ba05b305b305ba05b205c305c205bb05bb05c205ba05cb05ca05c305c305ca05c205d305d205cb05cb05d205ca05db05da05d305d305da05d205e305e205db05db05e205da05eb05ea05e305e305ea05e205f305f205eb05eb05f205ea05fb05fa05f305f305fa05f20503060206fb05fb050206fa050b060a06030603060a060206130612060b060b0612060a061b061a06130613061a061206230622061b061b0622061a062b062a06230623062a062206330632062b062b0632062a063b063a06330633063a063206430642063b063b0642063a064b064a06430643064a064206530652064b064b0652064a065b065a06530653065a065206630662065b065b0662065a066b066a06630663066a066206730672066b066b0672066a067b067a06730673067a067206830682067b067b0682067a068b068a06830683068a068206930692068b068b0692068a069b069a06930693069a069206a306a2069b069b06a2069a06ab06aa06a306a306aa06a206b306b206ab06ab06b206aa06bb06ba06b306b306ba06b206c306c206bb06bb06c206ba06cb06ca06c306c306ca06c206d306d206cb06cb06d206ca06db06da06d306d306da06d206e306e206db06db06e206da06eb06ea06e306e306ea06e206f306f206eb06eb06f206ea06fb06fa06f306f306fa06f20603070207fb06fb060207fa060b070a07030703070a070207130712070b070b0712070a071b071a07130713071a071207230722071b071b0722071a072b072a07230723072a072207330732072b072b0732072a073b073a07330733073a073207430742073b073b0742073a074b074a07430743074a074207530752074b074b0752074a075b075a07530753075a075207630762075b075b0762075a076b076a07630763076a076207730772076b076b0772076a077b077a07730773077a077207830782077b077b0782077a07040006000e000c0004000e0005000f0007000d000f0005000c000e00160014000c0016000d0017000f00150017000d00140016001e001c0014001e0015001f0017001d001f0015001c001e00260024001c0026001d0027001f00250027001d00240026002e002c0024002e0025002f0027002d002f0025002c002e00360034002c0036002d0037002f00350037002d00340036003e003c0034003e0035003f0037003d003f0035003c003e00460044003c0046003d0047003f00450047003d00440046004e004c0044004e0045004f0047004d004f0045004c004e00560054004c0056004d0057004f00550057004d00540056005e005c0054005e0055005f0057005d005f0055005c005e00660064005c0066005d0067005f00650067005d00640066006e006c0064006e0065006f0067006d006f0065006c006e00760074006c0076006d0077006f00750077006d00740076007e007c0074007e0075007f0077007d007f0075007c007e00860084007c0086007d0087007f00850087007d00840086008e008c0084008e0085008f0087008d008f0085008c008e00960094008c0096008d0097008f00950097008d00940096009e009c0094009e0095009f0097009d009f0095009c009e00a600a4009c00a6009d00a7009f00a500a7009d00a400a600ae00ac00a400ae00a500af00a700ad00af00a500ac00ae00b600b400ac00b600ad00b700af00b500b700ad00b400b600be00bc00b400be00b500bf00b700bd00bf00b500bc00be00c600c400bc00c600bd00c700bf00c500c700bd00c400c600ce00cc00c400ce00c500cf00c700cd00cf00c500cc00ce00d600d400cc00d600cd00d700cf00d500d700cd00d400d600de00dc00d400de00d500df00d700dd00df00d500dc00de00e600e400dc00e600dd00e700df00e500e700dd00e400e600ee00ec00e400ee00e500ef00e700ed00ef00e500ec00ee00f600f400ec00f600ed00f700ef00f500f700ed00f400f600fe00fc00f400fe00f500ff00f700fd00ff00f500fc00fe0006010401fc000601fd000701ff0005010701fd00040106010e010c0104010e0105010f0107010d010f0105010c010e01160114010c0116010d0117010f01150117010d01140116011e011c0114011e0115011f0117011d011f0115011c011e01260124011c0126011d0127011f01250127011d01240126012e012c0124012e0125012f0127012d012f0125012c012e01360134012c0136012d0137012f01350137012d01340136013e013c0134013e0135013f0137013d013f0135013c013e01460144013c0146013d0147013f01450147013d01440146014e014c0144014e0145014f0147014d014f0145014c014e01560154014c0156014d0157014f01550157014d01540156015e015c0154015e0155015f0157015d015f0155015c015e01660164015c0166015d0167015f01650167015d01640166016e016c0164016e0165016f0167016d016f0165016c016e01760174016c0176016d0177016f01750177016d01740176017e017c0174017e0175017f0177017d017f0175017c017e01860184017c0186017d0187017f01850187017d01840186018e018c0184018e0185018f0187018d018f0185018c018e01960194018c0196018d0197018f01950197018d01940196019e019c0194019e0195019f0197019d019f0195019c019e01a601a4019c01a6019d01a7019f01a501a7019d01a401a601ae01ac01a401ae01a501af01a701ad01af01a501ac01ae01b601b401ac01b601ad01b701af01b501b701ad01b401b601be01bc01b401be01b501bf01b701bd01bf01b501bc01be01c601c401bc01c601bd01c701bf01c501c701bd01c401c601ce01cc01c401ce01c501cf01c701cd01cf01c501cc01ce01d601d401cc01d601cd01d701cf01d501d701cd01d401d601de01dc01d401de01d501df01d701dd01df01d501dc01de01e601e401dc01e601dd01e701df01e501e701dd01e401e601ee01ec01e401ee01e501ef01e701ed01ef01e501ec01ee01f601f401ec01f601ed01f701ef01f501f701ed01f401f601fe01fc01f401fe01f501ff01f701fd01ff01f501fc01fe0106020402fc010602fd010702ff0105020702fd01040206020e020c0204020e0205020f0207020d020f0205020c020e02160214020c0216020d0217020f02150217020d02140216021e021c0214021e0215021f0217021d021f0215021c021e02260224021c0226021d0227021f02250227021d02240226022e022c0224022e0225022f0227022d022f0225022c022e02360234022c0236022d0237022f02350237022d02340236023e023c0234023e0235023f0237023d023f0235023c023e02460244023c0246023d0247023f02450247023d02440246024e024c0244024e0245024f0247024d024f0245024c024e02560254024c0256024d0257024f02550257024d02540256025e025c0254025e0255025f0257025d025f0255025c025e02660264025c0266025d0267025f02650267025d02640266026e026c0264026e0265026f0267026d026f0265026c026e02760274026c0276026d0277026f02750277026d02740276027e027c0274027e0275027f0277027d027f0275027c027e02860284027c0286027d0287027f02850287027d02840286028e028c0284028e0285028f0287028d028f0285028c028e02960294028c0296028d0297028f02950297028d02940296029e029c0294029e0295029f0297029d029f0295029c029e02a602a4029c02a6029d02a7029f02a502a7029d02a402a602ae02ac02a402ae02a502af02a702ad02af02a502ac02ae02b602b402ac02b602ad02b702af02b502b702ad02b402b602be02bc02b402be02b502bf02b702bd02bf02b502bc02be02c602c402bc02c602bd02c702bf02c502c702bd02c402c602ce02cc02c402ce02c502cf02c702cd02cf02c502cc02ce02d602d402cc02d602cd02d702cf02d502d702cd02d402d602de02dc02d402de02d502df02d702dd02df02d502dc02de02e602e402dc02e602dd02e702df02e502e702dd02e402e602ee02ec02e402ee02e502ef02e702ed02ef02e502ec02ee02f602f402ec02f602ed02f702ef02f502f702ed02f402f602fe02fc02f402fe02f502ff02f702fd02ff02f502fc02fe0206030403fc020603fd020703ff0205030703fd02040306030e030c0304030e0305030f0307030d030f0305030c030e03160314030c0316030d0317030f03150317030d03140316031e031c0314031e0315031f0317031d031f0315031c031e03260324031c0326031d0327031f03250327031d03240326032e032c0324032e0325032f0327032d032f0325032c032e03360334032c0336032d0337032f03350337032d03340336033e033c0334033e0335033f0337033d033f0335033c033e03460344033c0346033d0347033f03450347033d03440346034e034c0344034e0345034f0347034d034f0345034c034e03560354034c0356034d0357034f03550357034d03540356035e035c0354035e0355035f0357035d035f0355035c035e03660364035c0366035d0367035f03650367035d03640366036e036c0364036e0365036f0367036d036f0365036c036e03760374036c0376036d0377036f03750377036d03740376037e037c0374037e0375037f0377037d037f0375037c037e03860384037c0386037d0387037f03850387037d03840386038e038c0384038e0385038f0387038d038f0385038c038e03960394038c0396038d0397038f03950397038d03940396039e039c0394039e0395039f0397039d039f0395039c039e03a603a4039c03a6039d03a7039f03a503a7039d03a403a603ae03ac03a403ae03a503af03a703ad03af03a503ac03ae03b603b403ac03b603ad03b703af03b503b703ad03b403b603be03bc03b403be03b503bf03b703bd03bf03b503bc03be03c603c403bc03c603bd03c703bf03c503c703bd03c403c603ce03cc03c403ce03c503cf03c703cd03cf03c503cc03ce03d603d403cc03d603cd03d703cf03d503d703cd03d403d603de03dc03d403de03d503df03d703dd03df03d503dc03de03e603e403dc03e603dd03e703df03e503e703dd03e403e603ee03ec03e403ee03e503ef03e703ed03ef03e503ec03ee03f603f403ec03f603ed03f703ef03f503f703ed03f403f603fe03fc03f403fe03f503ff03f703fd03ff03f503fc03fe0306040404fc030604fd030704ff0305040704fd03040406040e040c0404040e0405040f0407040d040f0405040c040e04160414040c0416040d0417040f04150417040d04140416041e041c0414041e0415041f0417041d041f0415041c041e04260424041c0426041d0427041f04250427041d04240426042e042c0424042e0425042f0427042d042f0425042c042e04360434042c0436042d0437042f04350437042d04340436043e043c0434043e0435043f0437043d043f0435043c043e04460444043c0446043d0447043f04450447043d04440446044e044c0444044e0445044f0447044d044f0445044c044e04560454044c0456044d0457044f04550457044d04540456045e045c0454045e0455045f0457045d045f0455045c045e04660464045c0466045d0467045f04650467045d04640466046e046c0464046e0465046f0467046d046f0465046c046e04760474046c0476046d0477046f04750477046d04740476047e047c0474047e0475047f0477047d047f0475047c047e04860484047c0486047d0487047f04850487047d04840486048e048c0484048e0485048f0487048d048f0485048c048e04960494048c0496048d0497048f04950497048d04940496049e049c0494049e0495049f0497049d049f0495049c049e04a604a4049c04a6049d04a7049f04a504a7049d04a404a604ae04ac04a404ae04a504af04a704ad04af04a504ac04ae04b604b404ac04b604ad04b704af04b504b704ad04b404b604be04bc04b404be04b504bf04b704bd04bf04b504bc04be04c604c404bc04c604bd04c704bf04c504c704bd04c404c604ce04cc04c404ce04c504cf04c704cd04cf04c504cc04ce04d604d404cc04d604cd04d704cf04d504d704cd04d404d604de04dc04d404de04d504df04d704dd04df04d504dc04de04e604e404dc04e604dd04e704df04e504e704dd04e404e604ee04ec04e404ee04e504ef04e704ed04ef04e504ec04ee04f604f404ec04f604ed04f704ef04f504f704ed04f404f604fe04fc04f404fe04f504ff04f704fd04ff04f504fc04fe0406050405fc040605fd040705ff0405050705fd04040506050e050c0504050e0505050f0507050d050f0505050c050e05160514050c0516050d0517050f05150517050d05140516051e051c0514051e0515051f0517051d051f0515051c051e05260524051c0526051d0527051f05250527051d05240526052e052c0524052e0525052f0527052d052f0525052c052e05360534052c0536052d0537052f05350537052d05340536053e053c0534053e0535053f0537053d053f0535053c053e05460544053c0546053d0547053f05450547053d05440546054e054c0544054e0545054f0547054d054f0545054c054e05560554054c0556054d0557054f05550557054d05540556055e055c0554055e0555055f0557055d055f0555055c055e05660564055c0566055d0567055f05650567055d05640566056e056c0564056e0565056f0567056d056f0565056c056e05760574056c0576056d0577056f05750577056d05740576057e057c0574057e0575057f0577057d057f0575057c057e05860584057c0586057d0587057f05850587057d05840586058e058c0584058e0585058f0587058d058f0585058c058e05960594058c0596058d0597058f05950597058d05940596059e059c0594059e0595059f0597059d059f0595059c059e05a605a4059c05a6059d05a7059f05a505a7059d05a405a605ae05ac05a405ae05a505af05a705ad05af05a505ac05ae05b605b405ac05b605ad05b705af05b505b705ad05b405b605be05bc05b405be05b505bf05b705bd05bf05b505bc05be05c605c405bc05c605bd05c705bf05c505c705bd05c405c605ce05cc05c405ce05c505cf05c705cd05cf05c505cc05ce05d605d405cc05d605cd05d705cf05d505d705cd05d405d605de05dc05d405de05d505df05d705dd05df05d505dc05de05e605e405dc05e605dd05e705df05e505e705dd05e405e605ee05ec05e405ee05e505ef05e705ed05ef05e505ec05ee05f605f405ec05f605ed05f705ef05f505f705ed05f405f605fe05fc05f405fe05f505ff05f705fd05ff05f505fc05fe0506060406fc050606fd050706ff0505060706fd05040606060e060c0604060e0605060f0607060d060f0605060c060e06160614060c0616060d0617060f06150617060d06140616061e061c0614061e0615061f0617061d061f0615061c061e06260624061c0626061d0627061f06250627061d06240626062e062c0624062e0625062f0627062d062f0625062c062e06360634062c0636062d0637062f06350637062d06340636063e063c0634063e0635063f0637063d063f0635063c063e06460644063c0646063d0647063f06450647063d06440646064e064c0644064e0645064f0647064d064f0645064c064e06560654064c0656064d0657064f06550657064d06540656065e065c0654065e0655065f0657065d065f0655065c065e06660664065c0666065d0667065f06650667065d06640666066e066c0664066e0665066f0667066d066f0665066c066e06760674066c0676066d0677066f06750677066d06740676067e067c0674067e0675067f0677067d067f0675067c067e06860684067c0686067d0687067f06850687067d06840686068e068c0684068e0685068f0687068d068f0685068c068e06960694068c0696068d0697068f06950697068d06940696069e069c0694069e0695069f0697069d069f0695069c069e06a606a4069c06a6069d06a7069f06a506a7069d06a406a606ae06ac06a406ae06a506af06a706ad06af06a506ac06ae06b606b406ac06b606ad06b706af06b506b706ad06b406b606be06bc06b406be06b506bf06b706bd06bf06b506bc06be06c606c406bc06c606bd06c706bf06c506c706bd06c406c606ce06cc06c406ce06c506cf06c706cd06cf06c506cc06ce06d606d406cc06d606cd06d706cf06d506d706cd06d406d606de06dc06d406de06d506df06d706dd06df06d506dc06de06e606e406dc06e606dd06e706df06e506e706dd06e406e606ee06ec06e406ee06e506ef06e706ed06ef06e506ec06ee06f606f406ec06f606ed06f706ef06f506f706ed06f406f606fe06fc06f406fe06f506ff06f706fd06ff06f506fc06fe0606070407fc060607fd060707ff0605070707fd06040706070e070c0704070e0705070f0707070d070f0705070c070e07160714070c0716070d0717070f07150717070d07140716071e071c0714071e0715071f0717071d071f0715071c071e07260724071c0726071d0727071f07250727071d07240726072e072c0724072e0725072f0727072d072f0725072c072e07360734072c0736072d0737072f07350737072d07340736073e073c0734073e0735073f0737073d073f0735073c073e07460744073c0746073d0747073f07450747073d07440746074e074c0744074e0745074f0747074d074f0745074c074e07560754074c0756074d0757074f07550757074d07540756075e075c0754075e0755075f0757075d075f0755075c075e07660764075c0766075d0767075f07650767075d07640766076e076c0764076e0765076f0767076d076f0765076c076e07760774076c0776076d0777076f07750777076d07740776077e077c0774077e0775077f0777077d077f0775077c077e07860784077c0786077d0787077f07850787077d07 + m_Skin: [] + m_VertexData: + m_CurrentChannels: 11 + m_VertexCount: 1928 + m_Channels: + - stream: 0 + offset: 0 + format: 0 + dimension: 3 + - stream: 0 + offset: 12 + format: 0 + dimension: 3 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 24 + format: 0 + dimension: 2 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + - stream: 0 + offset: 0 + format: 0 + dimension: 0 + m_DataSize: 61696 + _typelessdata: 11c90fc0529ee33fc5e0613fb2f9cd3eb6075f3ffd0a90be00000000000000006efcdbbfcb0f783ff5655ebfb2f9cd3eb6075f3ffd0a90be0000803f00000000260e13c0be74d53f55066b3fb2f9cdbeb6075fbffd0a903e00000000000000009986e2bfa3bc5b3f654055bfb2f9cdbeb6075fbffd0a903e000000000000000011c90fc0529ee33fc5e0613f672b87beda2ccf3e5d23603f00000000000000006efcdbbfcb0f783ff5655ebf672b873eda2ccfbe5d2360bf0000000000000000260e13c0be74d53f55066b3f672b87beda2ccf3e5d23603f00000000000000009986e2bfa3bc5b3f654055bf672b873eda2ccfbe5d2360bf0000000000000000564ed3bfbf27cb3f9602933f6550cc3ed87c5f3f62928fbe0000000066201d3d840a91bf9a2f493fe8fb1bbf6550cc3ed87c5f3f62928fbe0000803f66201d3d00cbd9bfbbf6bc3f8a91973f6550ccbed87c5fbf62928f3e00000000000000002e8797bf92cd2c3f01de12bf6550ccbed87c5fbf62928f3e0000000000000000564ed3bfbf27cb3f9602933fa28784bee31fcd3e8a00613f0000000000000000840a91bf9a2f493fe8fb1bbfa287843ee31fcdbe8a0061bf000000000000000000cbd9bfbbf6bc3f8a91973fa28784bee31fcd3e8a00613f00000000000000002e8797bf92cd2c3f01de12bfa287843ee31fcdbe8a0061bf0000000000000000270cb1bfa808c03f6202a23f6b87ca3e6ff65f3fc1238fbe000000007380623dee8860bf9718353f51a5ffbe6b87ca3e6ff65f3fc1238fbe0000803f7380623d4f7ab7bfebcfb13fd28da63f6b87cabe6ff65fbfc1238f3e00000000000000003e656dbf1ea7183f8f77edbe6b87cabe6ff65fbfc1238f3e0000000000000000270cb1bfa808c03f6202a23f5f8f81bebaf8ca3eb6eb613f0000000000000000ee8860bf9718353f51a5ffbe5f8f813ebaf8cabeb6eb61bf00000000000000004f7ab7bfebcfb13fd28da63f5f8f81bebaf8ca3eb6eb613f00000000000000003e656dbf1ea7183f8f77edbe5f8f813ebaf8cabeb6eb61bf0000000000000000f23897bf9899b73f6c14ad3fc6c0c83e1b6f603f00b08ebe00000000bf1b8b3d8ce02fbfc762263f1dfbd6bec6c0c83e1b6f603f00b08ebe0000803fbf1b8b3daa989dbf3259a93f309cb13fc6c0c8be1b6f60bf00b08e3e0000000000000000fd9f3cbffae1093f0fdcc4bec6c0c8be1b6f60bf00b08e3e0000000000000000f23897bf9899b73f6c14ad3fae227dbe6ad0c83e33d3623f00000000000000008ce02fbfc762263f1dfbd6beae227d3e6ad0c8be33d362bf0000000000000000aa989dbf3259a93f309cb13fae227dbe6ad0c83e33d3623f0000000000000000fd9f3cbffae1093f0fdcc4beae227d3e6ad0c8be33d362bf000000000000000076867abf8826af3f68e9b73ffba8c63ef3f3603f03598ebe00000000fccba43d7d7fffbe85e4173f28d8afbefba8c63ef3f3603f03598ebe0000803ffccba43df19183bfb2dda03f696ebc3ffba8c6bef3f360bf03598e3e00000000000000002a5d0cbfb3a5f63e26c49dbefba8c6bef3f360bf03598e3e000000000000000076867abf8826af3f68e9b73f6e8d75be8a68c63e72df633f00000000000000007d7fffbe85e4173f28d8afbe6e8d753e8a68c6be72df63bf0000000000000000f19183bfb2dda03f696ebc3f6e8d75be8a68c63e72df633f00000000000000002a5d0cbfb3a5f63e26c49dbe6e8d753e8a68c6be72df63bf00000000000000007dd757bf306ca93fa1ffbe3ff5fcc43e5967613f70d18dbe0000000071d0b53d22cebfbe708a0e3ffcd296bef5fcc43e5967613f70d18dbe0000803f71d0b53dba5964bf061c9b3f5480c33ff5fcc4be596761bf70d18d3e00000000000000009cd2d8be3ad4e33e32d084bef5fcc4be596761bf70d18d3e00000000000000007dd757bf306ca93fa1ffbe3fd9e06fbef04dc43e60b4643f000000000000000022cebfbe708a0e3ffcd296bed9e06f3ef04dc4be60b464bf0000000000000000ba5964bf061c9b3f5480c33fd9e06fbef04dc43e60b4643f00000000000000009cd2d8be3ad4e33e32d084bed9e06f3ef04dc4be60b464bf0000000000000000b50935bf1bb1a33f0aefc53fd92bc33efbdf613f90548dbe0000000049bcc63d1ea880be984d053f704a7dbed92bc33efbdf613f90548dbe0000803f49bcc63d696e41bf4859953fc66bca3fd92bc3befbdf61bf90548d3e0000000000000000877199bee53bd13e936459bed92bc3befbdf61bf90548d3e0000000000000000b50935bf1bb1a33f0aefc53f4b6b69be9e14c23e5898653f00000000000000001ea880be984d053f704a7dbe4b6b693e9e14c2be589865bf0000000000000000696e41bf4859953fc66bca3f4b6b69be9e14c23e5898653f0000000000000000877199bee53bd13e936459be4b6b693e9e14c2be589865bf0000000000000000cb1c12bfa4f59d3fe1b2cc3fa534c13ef95d623f76e18cbe00000000848dd73dd12a04bec45ff83e58be4ebea534c13ef95d623f76e18cbe0000803f848dd73d8b611ebfd1958f3ff52bd13fa534c1bef95d62bf76e18c3e0000000000000000d23d35be79e0be3eb8f52abea534c1bef95d62bf76e18c3e0000000000000000cb1c12bfa4f59d3fe1b2cc3f2d2462be66bbbf3eac8a663f0000000000000000d12a04bec45ff83e58be4ebe2d24623e66bbbfbeac8a66bf00000000000000008b611ebfd1958f3ff52bd13f2d2462be66bbbf3eac8a663f0000000000000000d23d35be79e0be3eb8f52abe2d24623e66bbbfbeac8a66bf0000000000000000b5b700bf6ff59a3fa00dd03f9e08c03eedbb623fcb1e8cbe0000000090ebdf3d282e8bbd6de4ef3e648538be9e08c03eedbb623fcb1e8cbe0000803f90ebdf3d68e90cbfa58f8c3f8680d43f9e08c0beedbb62bfcb1e8c3e0000000000000000c0bbecbd454db63e36ee14be9e08c0beedbb62bfcb1e8c3e0000000000000000b5b700bf6ff59a3fa00dd03fe0a35ebea7f8bd3e4c1e673f0000000000000000282e8bbd6de4ef3e648538bee0a35e3ea7f8bdbe4c1e67bf000000000000000068e90cbfa58f8c3f8680d43fe0a35ebea7f8bd3e4c1e673f0000000000000000c0bbecbd454db63e36ee14bee0a35e3ea7f8bdbe4c1e67bf00000000000000009094debe2ef5973f935bd33fddd3be3ecc1a633f925d8bbe000000003f42e83d809beabb727ae73ea0cb22beddd3be3ecc1a633f925d8bbe0000803f3f42e83dc0d0f6be5e89893f56c8d73fddd3bebecc1a63bf925d8b3e0000000000000000ef345fbd31cbad3e0bcbfebdddd3bebecc1a63bf925d8b3e00000000000000009094debe2ef5973f935bd33f22ea5abe222dbc3e07b5673f0000000000000000809beabb727ae73ea0cb22be22ea5a3e222dbcbe07b567bf0000000000000000c0d0f6be5e89893f56c8d73f22ea5abe222dbc3e07b5673f0000000000000000ef345fbd31cbad3e0bcbfebd22ea5a3e222dbcbe07b567bf0000000000000000d0a8bbbee6f4943f1f9cd63f4c96bd3e967a633f8c9d8abe000000005c91f03d84665a3d6222df3ef0940dbe4c96bd3e967a633f8c9d8abe0000803f5c91f03dabbcd3be0183863fca02db3f4c96bdbe967a63bf8c9d8a3e0000000000000000583dce3bcc5aa53e38bfd4bd4c96bdbe967a63bf8c9d8a3e0000000000000000d0a8bbbee6f4943f1f9cd63fa0f556be9a58ba3ebd4e683f000000000000000084665a3d6222df3ef0940dbea0f5563e9a58babebd4e68bf0000000000000000abbcd3be0183863fca02db3fa0f556be9a58ba3ebd4e683f0000000000000000583dce3bcc5aa53e38bfd4bda0f5563e9a58babebd4e68bf0000000000000000d0ab98beadf4913fa2ced93fea4fbc3e44db633fbcde89be00000000b1d8f83dbe63e83d98dcd63e10caf1bdea4fbc3e44db633fbcde89be0000803fb1d8f83d3896b0bea47c833f3e2fde3fea4fbcbe44db63bfbcde893e00000000000000001fba883d74fc9c3e56c0abbdea4fbcbe44db63bfbcde893e0000000000000000d0ab98beadf4913fa2ced93fc0c452be0e7bb83e43eb683f0000000000000000be63e83d98dcd63e10caf1bdc0c4523e0e7bb8be43eb68bf00000000000000003896b0bea47c833f3e2fde3fc0c452be0e7bb83e43eb683f00000000000000001fba883d74fc9c3e56c0abbdc0c4523e0e7bb8be43eb68bf0000000000000000c93a6bbe84f48e3f7cf2dc3fa000bb3ee13c643fce2089be00000000048c003e8f71313eb8a9ce3e987fc9bda000bb3ee13c643fce2089be0000803f048c003e375d8dbe4876803f104de13fa000bbbee13c64bfce20893e0000000000000000e9f1013ec8b0943e5ad683bda000bbbee13c64bfce20893e0000000000000000c93a6bbe84f48e3f7cf2dc3f2c564ebe2c94b63e758a693f00000000000000008f71313eb8a9ce3e987fc9bd2c564e3e2c94b6be758a69bf0000000000000000375d8dbe4876803f104de13f2c564ebe2c94b63e758a693f0000000000000000e9f1013ec8b0943e5ad683bd2c564e3e2c94b6be758a69bf00000000000000005cfa24be76f48b3f0607e03f5da8b93e669f643f996388be0000000098a7043e32566e3e408ac63eb851a2bd5da8b93e669f643f996388be0000803f98a7043e902254bef1df7a3f985be43f5da8b9be669f64bf9963883e0000000000000000fe2d3f3e4a788c3e30113abd5da8b9be669f64bf9963883e00000000000000005cfa24be76f48b3f0607e03f47a849becca3b43e212c6a3f000000000000000032566e3e408ac63eb851a2bd47a8493ecca3b4be212c6abf0000000000000000902254bef1df7a3f985be43f47a849becca3b43e212c6a3f0000000000000000fe2d3f3e4a788c3e30113abd47a8493ecca3b4be212c6abf0000000000000000742bbdbd8ef4883f9c0be33f1347b83ed402653ff2a687be00000000febe083e9f6e953ea87ebe3e808f78bd1347b83ed402653ff2a687be0000803ffebe083e32640dbe80d3743f315ae73f1347b8bed40265bff2a6873e0000000000000000c60e7c3e7153843ed879ddbc1347b8bed40265bff2a6873e0000000000000000742bbdbd8ef4883f9c0be33f7cb944bec8a9b23e18d06a3f00000000000000009f6e953ea87ebe3e808f78bd7cb9443ec8a9b2be18d06abf000000000000000032640dbe80d3743f315ae73f7cb944bec8a9b23e18d06a3f0000000000000000c60e7c3e7153843ed879ddbc7cb9443ec8a9b2be18d06abf0000000000000000b860c0bcd3f4853f94ffe53fb1dcb63e2e67653fa0ea86be000000001ed20c3e1482b33e7b87b63eb0d12ebdb1dcb63e2e67653fa0ea86be0000803f1ed20c3e07fd8cbd4cc76e3f2e48ea3fb1dcb6be2e6765bfa0ea863e0000000000000000de489c3e8e85783ec8f916bcb1dcb6be2e6765bfa0ea863e0000000000000000b860c0bcd3f4853f94ffe53f1f883fbee9a5b03e22766b3f00000000000000001482b33e7b87b63eb0d12ebd1f883f3ee9a5b0be22766bbf000000000000000007fd8cbd4cc76e3f2e48ea3f1f883fbee9a5b03e22766b3f0000000000000000de489c3e8e85783ec8f916bc1f883f3ee9a5b0be22766bbf0000000000000000088d3a3d50f5823f43e2e83f2469b53e6fcc653f6e2e86be00000000e4e0103e2064d13e3ea5ae3e40efcebc2469b53e6fcc653f6e2e86be0000803fe4e0103e40350f3a6abb683fe324ed3f2469b5be6fcc65bf6e2e863e00000000000000001a5aba3ea48d683ea871033c2469b5be6fcc65bf6e2e863e0000000000000000088d3a3d50f5823f43e2e83f7f123abe0298ae3e001e6c3f00000000000000002064d13e3ea5ae3e40efcebc7f123a3e0298aebe001e6cbf000000000000000040350f3a6abb683fe324ed3f7f123abe0298ae3e001e6c3f00000000000000001a5aba3ea48d683ea871033c7f123a3e0298aebe001e6cbf00000000000000005ef2ea3d23ec7f3ffab2eb3f61ecb33e9732663f2e7285be000000003deb143e7013ef3e66d8a63ec03a0abc61ecb33e9732663f2e7285be0000803f3deb143eb28b8f3df4af623fa0efef3f61ecb3be973266bf2e72853e0000000000000000c539d83e0ec0583e2e0cca3c61ecb3be973266bf2e72853e00000000000000005ef2ea3d23ec7f3ffab2eb3fd95634bef07fac3e70c76c3f00000000000000007013ef3e66d8a63ec03a0abcd956343ef07facbe70c76cbf0000000000000000b28b8f3df4af623fa0efef3fd95634bef07fac3e70c76c3f0000000000000000c539d83e0ec0583e2e0cca3cd956343ef07facbe70c76cbf000000000000000084763c3e32ee793f0a71ee3f4d66b23ea999663f87b584be0000000014f1183e5847063f9c219f3e80dbfe3b4d66b23ea999663f87b584be0000803f14f1183e42260f3eeca45c3fb3a7f23f4d66b2bea99966bf87b5843e00000000000000008fe6f53e1e1e493e8bb0263d4d66b2bea99966bf87b5843e000000000000000084763c3e32ee793f0a71ee3f6e532ebe645daa3e2f726d3f00000000000000005847063f9c219f3e80dbfe3b6e532e3e645daabe2f726dbf000000000000000042260f3eeca45c3fb3a7f23f6e532ebe645daa3e2f726d3f00000000000000008fe6f53e1e1e493e8bb0263d6e532e3e645daabe2f726dbf00000000000000000ece813ee2f0733fbc1bf13fdcd6b03ea301673f45f883be000000005bf21c3e3dea143f4f81973ea074bf3cdcd6b03ea301673f45f883be0000803f5bf21c3e50b1563e679a563f634cf53fdcd6b0bea30167bf45f8833e00000000000000008aaf093fb2a8393e22cf653ddcd6b0bea30167bf45f8833e00000000000000000ece813ee2f0733fbc1bf13f6c0628be3b30a83eea1d6e3f00000000000000003dea143f4f81973ea074bf3c6c06283e3b30a8beea1d6ebf000000000000000050b1563e679a563f634cf53f6c0628be3b30a83eea1d6e3f00000000000000008aaf093fb2a8393e22cf653d6c06283e3b30a8beea1d6ebf00000000000000008075a53e50f46d3f5ab2f33f013eaf3e836a673f383a83be0000000001ef203eb371233ff8f78f3ea0011d3d013eaf3e836a673f383a83be0000803f01ef203e06348f3e8390503ff8dcf73f013eafbe836a67bf383a833e0000000000000000f650183fbe602a3ead2a913d013eafbe836a67bf383a833e00000000000000008075a53e50f46d3f5ab2f33fe66d21be54f8a53e4dca6e3f0000000000000000b371233ff8f78f3ea0011d3de66d213e54f8a5be4dca6ebf000000000000000006348f3e8390503ff8dcf73fe66d21be54f8a53e4dca6e3f0000000000000000f650183fbe602a3ead2a913de66d213e54f8a5be4dca6ebf00000000000000000a32c93e7ff8673f2834f63fa79bad3e48d4673ff67a82be00000000fbe6243e0bdd313f3e86883e70a5573da79bad3e48d4673ff67a82be0000803ffbe6243eb225b33e44874a3fb358fa3fa79badbe48d467bff67a823e0000000000000000dfd6263f8e471b3e6c1bae3da79badbe48d467bff67a823e00000000000000000a32c93e7ff8673f2834f63f0c881abe60b5a33efc766f3f00000000000000000bdd313f3e86883e70a5573d0c881a3e60b5a3befc766fbf0000000000000000b225b33e44874a3fb358fa3f0c881abe60b5a33efc766f3f0000000000000000dfd6263f8e471b3e6c1bae3d0c881a3e60b5a3befc766fbf00000000000000002e04ed3e86fd613f66a0f83fc1efab3ef23e683f41ba81be000000003bda283e8f2b403f982c813e20cd873dc1efab3ef23e683f41ba81be0000803f3bda283e2e2ed73ebf7e443fd3befc3fc1efabbef23e68bf41ba813e00000000000000008f40353f135e0c3eefb3c93dc1efabbef23e68bf41ba813e00000000000000002e04ed3e86fd613f66a0f83fef5213be3a67a13e9423703f00000000000000008f2b403f982c813e20cd873def52133e3a67a1be942370bf00000000000000002e2ed73ebf7e443fd3befc3fef5213be3a67a13e9423703f00000000000000008f40353f135e0c3eefb3c93def52133e3a67a1be942370bf00000000000000003776083f77035c3f52f6fa3f463aaa3e7eaa683fccf780be00000000bac82c3e855c4e3f0cd7733ea86aa23d463aaa3e7eaa683fccf780be0000803fbac82c3efd4dfb3e07773e3f920eff3f463aaabe7eaa68bfccf7803e00000000000000004d8d433f9a4afb3daeeee33d463aaabe7eaa68bfccf7803e00000000000000003776083f77035c3f52f6fa3f9bcc0bbeb40d9f3ea7cf703f0000000000000000855c4e3f0cd7733ea86aa23d9bcc0b3eb40d9fbea7cf70bf0000000000000000fd4dfb3e07773e3f920eff3f9bcc0bbeb40d9f3ea7cf703f00000000000000004d8d433f9a4afb3daeeee33d9bcc0b3eb40d9fbea7cf70bf0000000000000000a7751a3f6e0a563f2235fd3f2b7ba83ee616693f543380be000000006db2303e316f5c3ffa86653e40a6bb3d2b7ba83ee616693f543380be0000803f6db2303ed3c20f3f3a70383f93a300402b7ba8bee61669bf5433803e00000000000000005dbc513f523cde3d78c6fc3d2b7ba8bee61669bf5433803e0000000000000000a7751a3f6e0a563f2235fd3f15f303beafa89c3ebe7a713f0000000000000000316f5c3ffa86653e40a6bb3d15f3033eafa89cbebe7a71bf0000000000000000d3c20f3f3a70383f93a3004015f303beafa89c3ebe7a713f00000000000000005dbc513f523cde3d78c6fc3d15f3033eafa89cbebe7a71bf0000000000000000a4802c3f7012503f0c5cff3f62b2a63e2d84693fc5d87ebe000000005097343ee4626a3f426a573ec87ad33d62b2a63e2d84693fc5d87ebe0000803f5097343ed1ea213f5b6a323fdfb3014062b2a6be2d8469bfc5d87e3e000000000000000011cd5f3fdb93c13df81a0a3e62b2a6be2d8469bfc5d87e3e0000000000000000a4802c3f7012503f0c5cff3f0289f7bddf379a3e5f24723f0000000000000000e4626a3f426a573ec87ad33d0289f73ddf379abe5f2472bf0000000000000000d1ea213f5b6a323fdfb301400289f7bddf379a3e5f24723f000000000000000011cd5f3fdb93c13df81a0a3e0289f73ddf379abe5f2472bf000000000000000074973e3f991b4a3f1fb50040ebdfa43e4cf2693f54457dbe000000005f77383ee436783fc481493ee0e3e93debdfa43e4cf2693f54457dbe0000803f5f77383e401f343f88652c3fc4b70240ebdfa4be4cf269bf54457d3e0000000000000000b0be6d3ffd52a53d471c153eebdfa4be4cf269bf54457d3e000000000000000074973e3f991b4a3f1fb50040c17de6bd28bb973e00cc723f0000000000000000e436783fc481493ee0e3e93dc17de63d28bb97be00cc72bf0000000000000000401f343f88652c3fc4b70240c17de6bd28bb973e00cc723f0000000000000000b0be6d3ffd52a53d471c153ec17de63d28bb97be00cc72bf000000000000000057ba503f0526443f71af0140c303a33e3f616a3fb7ab7bbe0000000099523c3e3cf5823f50ce3b3e40ddfe3dc303a33e3f616a3fb7ab7bbe0000803f99523c3e5f60463fdc61263fd6ae0340c303a3be3f616abfb7ab7b3e000000000000000080907b3f5b7b893df2641f3ec303a3be3f616abfb7ab7b3e000000000000000057ba503f0526443f71af014080c0d4bd7132953e0e71733f00000000000000003cf5823f50ce3b3e40ddfe3d80c0d43d713295be0e7173bf00000000000000005f60463fdc61263fd6ae034080c0d4bd7132953e0e71733f000000000000000080907b3f5b7b893df2641f3e80c0d43d713295be0e7173bf000000000000000082e9623fb6313e3f8f9c0240e61da13e07d16a3fc50a7abe00000000fd28403e77be893f42512e3e5c31093ee61da13e07d16a3fc50a7abe0000803ffd28403e65ae583f5b5f203fa5980440e61da1be07d16abfc50a7a3e0000000000000000e8a0843f5c1f5c3dbaf2283ee61da1be07d16abfc50a7a3e000000000000000082e9623fb6313e3f8f9c0240b24dc2bd659d923ef312743f000000000000000077be893f42512e3e5c31093eb24dc23d659d92bef31274bf000000000000000065ae583f5b5f203fa5980440b24dc2bd659d923ef312743f0000000000000000e8a0843f5c1f5c3dbaf2283eb24dc23d659d92bef31274bf00000000000000003d25753fda3e383f077c03405e2e9f3e93416b3f0a6278be0000000090fa433ecc76903f240b213e7c38123e5e2e9f3e93416b3f0a6278be0000803f90fa433e97096b3f345e1a3fbe7405405e2e9fbe93416bbf0a62783e0000000000000000f9688b3f3222263de9c3313e5e2e9fbe93416bbf0a62783e00000000000000003d25753fda3e383f077c03406a21afbd11fc8f3effb0743f0000000000000000cc76903f240b213e7c38123e6a21af3d11fc8fbeffb074bf000000000000000097096b3f345e1a3fbe7405406a21afbd11fc8f3effb0743f0000000000000000f9688b3f3222263de9c3313e6a21af3d11fc8fbeffb074bf0000000000000000dcb6833f884d323f674d044038359d3ee6b26b3f93b076be0000000057c7473ee01d973ffefc133e4c821a3e38359d3ee6b26b3f93b076be0000803f57c7473e26727d3f7e5e143fad42064038359dbee6b26bbf93b0763e00000000000000001720923fa406e23cacd6393e38359dbee6b26bbf93b0763e0000000000000000dcb6833f884d323f674d044023389bbd494e8d3e854a753f0000000000000000e01d973ffefc133e4c821a3e23389b3d494e8dbe854a75bf000000000000000026727d3f7e5e143fad42064023389bbd494e8d3e854a753f00000000000000001720923fa406e23cacd6393e23389b3d494e8dbe854a75bf000000000000000092e18c3fe45d2c3f3910054082329b3ef2246c3f93f574be000000005a8f4b3e5eb39d3f9227073e740d223e82329b3ef2246c3f93f574be0000803f5a8f4b3e20f4873f5e600e3ffb01074082329bbef2246cbf93f5743e0000000000000000ecc5983f9017733c9229413e82329bbef2246cbf93f5743e000000000000000092e18c3fe45d2c3f39100540648e86bdff938a3ec3de753f00000000000000005eb39d3f9227073e740d223e648e863dff938abec3de75bf000000000000000020f4873f5e600e3ffb010740648e86bdff938a3ec3de753f0000000000000000ecc5983f9017733c9229413e648e863dff938abec3de75bf0000000000000000d912963fff6f263f02c405405126993eb4976c3fec2f73be00000000a5524f3efb36a43fc017f53d90d8283e5126993eb4976c3fec2f73be0000803fa5524f3e0c36913fe663083f2ab20740512699beb4976cbfec2f733e00000000000000002e5a9f3fc0de163b11bb473e512699beb4976cbfec2f733e0000000000000000d912963fff6f263f02c405402d4262bd07cd873ef26c763f0000000000000000fb36a43fc017f53d90d8283e2d42623d07cd87bef26c76bf00000000000000000c36913fe663083f2ab207402d4262bd07cd873ef26c763f00000000000000002e5a9f3fc0de163b11bb473e2d42623d07cd87bef26c76bf0000000000000000c24a9f3f0584203f48680640c410973e200b6d3fbe5e71be000000004411533e66a8aa3f3855dc3da8e22e3ec410973e200b6d3fbe5e71be0000803f4411533ee67e9a3f4369023fbf520840c41097be200b6dbfbe5e713e00000000000000008adca53fb80624bc158a4d3ec41097be200b6dbfbe5e713e0000000000000000c24a9f3f0584203f486806403fda35bd5ef9843e3cf4763f000000000000000066a8aa3f3855dc3da8e22e3e3fda353d5ef984be3cf476bf0000000000000000e67e9a3f4369023fbf5208403fda35bd5ef9843e3cf4763f00000000000000008adca53fb80624bc158a4d3e3fda353d5ef984be3cf476bf00000000000000005b89a83f1a9a1a3f8cfc0640fef1943e297f6d3f13816fbe000000004acb563e5307b13fe008c43dd82a343efef1943e297f6d3f13816fbe0000803f4acb563ebacea33f37e1f83e38e30840fef194be297f6dbf13816f3e0000000000000000b24cac3f4c0cb5bc9b95523efef194be297f6dbf13816f3e00000000000000005b89a83f1a9a1a3f8cfc064080df07bdfe18823ebc73773f00000000000000005307b13fe008c43dd82a343e80df073dfe1882bebc7377bf0000000000000000bacea33f37e1f83e38e3084080df07bdfe18823ebc73773f0000000000000000b24cac3f4c0cb5bc9b95523e80df073dfe1882bebc7377bf0000000000000000afceb13f6db2143f488007402bca923ec6f36d3feb956dbe00000000c9805a3e8153b73f0a34ac3d5cb0383e2bca923ec6f36d3feb956dbe0000803fc9805a3e9425ad3f3ef4ec3e0e6309402bca92bec6f36dbfeb956d3e000000000000000066aab23fc81c0bbdbedc563e2bca92bec6f36dbfeb956d3e0000000000000000afceb13f6db2143f488007404e9ab0bcd7577e3e85ea773f00000000000000008153b73f0a34ac3d5cb0383e4e9ab03cd7577ebe85ea77bf00000000000000009425ad3f3ef4ec3e0e6309404e9ab0bcd7577e3e85ea773f000000000000000066aab23fc81c0bbdbedc563e4e9ab03cd7577ebe85ea77bf0000000000000000bf1abb3f2ccd0e3ffcf207408499903ee6686e3f379c6bbe00000000d7315e3eaf8cbd3ff4d7943dd8723c3e8499903ee6686e3f379c6bbe0000803fd7315e3e7183b63ffd0be13ebfd10940849990bee6686ebf379c6b3e000000000000000061f5b83ff2c23abd015f5a3e849990bee6686ebf379c6b3e0000000000000000bf1abb3f2ccd0e3ffcf20740ed7b1cbc5a64783e9c57783f0000000000000000af8cbd3ff4d7943dd8723c3eed7b1c3c5a6478be9c5778bf00000000000000007183b63ffd0be13ebfd10940ed7b1cbc5a64783e9c57783f000000000000000061f5b83ff2c23abd015f5a3eed7b1c3c5a6478be9c5778bf00000000000000008a6dc43f89ea083f1b5408404c608e3e7ede6e3ff09269be0000000091de613ea6b2c33f0ceb7b3df0713f3e4c608e3e7ede6e3ff09269be0000803f91de613e4ee8bf3fd828d53eba2e0a404c608ebe7ede6ebff092693e00000000000000006a2dbf3fc27669bde51b5d3e4c608ebe7ede6ebff092693e00000000000000008a6dc43f89ea083f1b54084033e33a3bb157723ef8b9783f0000000000000000a6b2c33f0ceb7b3df0713f3e33e33abbb15772bef8b978bf00000000000000004ee8bf3fd828d53eba2e0a4033e33a3bb157723ef8b9783f00000000000000006a2dbf3fc27669bde51b5d3e33e33abbb15772bef8b978bf000000000000000006c7cd3fca0a033f1ca30840c91e8c3e78546f3f257967be000000001387653e36c5c93fc41a4f3d88ad413ec91e8c3e78546f3f257967be0000803f1387653e2054c93f634bc93e77790a40c91e8cbe78546fbf2579673e00000000000000005052c53f629b8bbd30135f3ec91e8cbe78546fbf2579673e000000000000000006c7cd3fca0a033f1ca30840e139803c3b326c3e8610793f000000000000000036c5c93fc41a4f3d88ad413ee13980bc3b326cbe861079bf00000000000000002054c93f634bc93e77790a40e139803c3b326c3e8610793f00000000000000005052c53f629b8bbd30135f3ee13980bc3b326cbe861079bf00000000000000001a27d73f345cfa3e70df084054d5893ec9ca6f3f794d65be000000007d2b693e38c4cf3f6441233da025433e54d5893ec9ca6f3f794d65be0000803f7d2b693ecbc6d23ff673bd3e61b10a4054d589bec9ca6fbf794d653e0000000000000000e963cb3f4800a2bdb644603e54d589bec9ca6fbf794d653e00000000000000001a27d73f345cfa3e70df08405a5cec3c07f4653e2c5a793f000000000000000038c4cf3f6441233da025433e5a5cecbc07f465be2c5a79bf0000000000000000cbc6d23ff673bd3e61b10a405a5cec3c07f4653e2c5a793f0000000000000000e963cb3f4800a2bdb644603e5a5cecbc07f465be2c5a79bf0000000000000000b08de03f8fa9ee3e880809404f84873e5741703ff70e63be00000000f5cb6c3e8cafd53fe0bef03c84da433e4f84873e5741703ff70e63be0000803ff5cb6c3e3540dc3f34a3b13eead50a404f8487be574170bff70e633e00000000000000001162d13fb5e9b7bda4b0603e4f8487be574170bff70e633e0000000000000000b08de03f8fa9ee3e8808094036e22d3da19d5f3ebf95793f00000000000000008cafd53fe0bef03c84da433e36e22dbda19d5fbebf9579bf00000000000000003540dc3f34a3b13eead50a4036e22d3da19d5f3ebf95793f00000000000000001162d13fb5e9b7bda4b0603e36e22dbda19d5fbebf9579bf00000000000000009dfae93f4efee23ece1d09402b2c853e0db8703fa9bc60be00000000a368703e1d87db3fc8e99c3c8ccc433e2b2c853e0db8703fa9bc60be0000803fa368703e30c0e53fccd9a53e78e60a402b2c85be0db870bfa9bc603e0000000000000000b04cd73f9857cdbd3257603e2b2c85be0db870bfa9bc603e00000000000000009dfae93f4efee23ece1d0940f137673db12f593e0bc2793f00000000000000001d87db3fc8e99c3c8ccc433ef13767bdb12f59be0bc279bf000000000000000030c0e53fccd9a53e78e60a40f137673db12f593e0bc2793f0000000000000000b04cd73f9857cdbd3257603ef13767bdb12f59be0bc279bf0000000000000000ad6df33ff65ad73eb01e094065cd823ed22e713f36555ebe00000000b101743ee74ae13f900b163c30fc423e65cd823ed22e713f36555ebe0000803fb101743e8446ef3f49189a3e78e20a4065cd82bed22e71bf36555e3e0000000000000000be23dd3f4349e2bdac385f3e65cd82bed22e71bf36555e3e0000000000000000ad6df33ff65ad73eb01e09403716913d9aaa523edadd793f0000000000000000e74ae13f900b163c30fc423e371691bd9aaa52bedadd79bf00000000000000008446ef3f49189a3e78e20a403716913d9aaa523edadd793f0000000000000000be23dd3f4349e2bdac385f3e371691bd9aaa52bedadd79bf0000000000000000a6e6fc3f4ec0cb3e950a09408a68803e91a5713fb3d75bbe000000005197773eeafae63f00df1dba0c6a413e8a68803e91a5713fb3d75bbe0000803f5197773ef2d2f83f785f8e3e4dc90a408a6880be91a571bfb3d75b3e000000000000000036e7e23f18bff6bd92555d3e8a6880be91a571bfb3d75b3e0000000000000000a6e6fc3f4ec0cb3e950a0940dc5daf3d3d0f4c3ee9e7793f0000000000000000eafae63f00df1dba0c6a413edc5dafbd3d0f4cbee9e779bf0000000000000000f2d2f83f785f8e3e4dc90a40dc5daf3d3d0f4c3ee9e7793f000000000000000036e7e23f18bff6bd92555d3edc5dafbd3d0f4cbee9e779bf00000000000000009c320340272fc03ee6e0084068fc7b3e281c723f2f4359be00000000b5297b3e3897ec3f20ee25bce0163f3e68fc7b3e281c723f2f4359be0000803fb5297b3e9332014031b0823e609a0a4068fc7bbe281c72bf2f43593e00000000000000002697e83fce5c05be83ae5a3e68fc7bbe281c72bf2f43593e00000000000000009c320340272fc03ee6e008400570ce3d985e453ef0de793f00000000000000003897ec3f20ee25bce0163f3e0570cebd985e45bef0de79bf00000000000000009332014031b0823e609a0a400570ce3d985e453ef0de793f00000000000000002697e83fce5c05be83ae5a3e0570cebd985e45bef0de79bf000000000000000084f4074038a8b43e09a10840211e773e8092723f6b9656be0000000015b97e3ef01ff23f60139fbc40033c3e211e773e8092723f6b9656be0000803f15b97e3e5ffe054066166e3e14550a40211e77be809272bf6b96563e0000000000000000a733ee3f761c0fbeec43573e211e77be809272bf6b96563e000000000000000084f4074038a8b43e09a10840bc48ee3d6e993e3eaac1793f0000000000000000f01ff23f60139fbc40033c3ebc48eebd6e993ebeaac179bf00000000000000005ffe054066166e3e14550a40bc48ee3d6e993e3eaac1793f0000000000000000a733ee3f761c0fbeec43573ebc48eebd6e993ebeaac179bf0000000000000000d6b80c40672ca93e674a0840b937723e7908733f72d053be00000000d722813e4495f73fc047e9bc0c30383eb937723e7908733f72d053be0000803fd722813ea6cc0a40d6e2563ecff80940b93772be790873bf72d0533e0000000000000000e5bcf33ff09e18be8d16533eb93772be790873bf72d0533e0000000000000000d6b80c40672ca93e674a0840a471073ee3c0373ecd8e793f00000000000000004495f73fc047e9bc0c30383ea47107bee3c037becd8e79bf0000000000000000a6cc0a40d6e2563ecff80940a471073ee3c0373ecd8e793f0000000000000000e5bcf33ff09e18be8d16533ea47107bee3c037becd8e79bf0000000000000000577f1140ccbc9d3e64dc0740b94a6d3eec7d733f9ff050be00000000dfe7823e6df7fc3f74cd18bdf89d333eb94a6d3eec7d733f9ff050be0000803fdfe7823e2a9d0f40f6c73f3ef5840940b94a6dbeec7d73bf9ff0503e00000000000000001233f93fffe421be06274e3eb94a6dbeec7d73bf9ff0503e0000000000000000577f1140ccbc9d3e64dc0740051d183e7bd6303e0a45793f00000000000000006df7fc3f74cd18bdf89d333e051d18be7bd630be0a4579bf00000000000000002a9d0f40f6c73f3ef5840940051d183e7bd6303e0a45793f00000000000000001233f93fffe421be06274e3e051d18be7bd630be0a4579bf0000000000000000c14716405b5a923e6a560740db58683eb9f2733ff1f54dbe00000000c6ab843e5f230140ce083cbdb04d2e3edb58683eb9f2733ff1f54dbe0000803fc6ab843ea06f1440bec7283eedf80840db5868beb9f273bff1f54d3e00000000000000007c96fe3f2cef2abee275483edb5868beb9f273bff1f54d3e0000000000000000c14716405b5a923e6a5607401123293e75db293e1ee3783f00000000000000005f230140ce083cbdb04d2e3e112329be75db29be1ee378bf0000000000000000a06f1440bec7283eedf808401123293e75db293e1ee3783f00000000000000007c96fe3f2cef2abee275483e112329be75db29be1ee378bf0000000000000000c6111b404006873ee2b70640ef63633eba66743fd3df4abe00000000af6e863ecac103401c5a5ebdd03f283eef63633eba66743fd3df4abe0000803faf6e863eb74319409ae4113e20540840ef6363beba6674bfd3df4a3e0000000000000000bbf301406dbe33beaa03423eef6363beba6674bfd3df4a3e0000000000000000c6111b404006873ee2b70640e17f3a3e84d1223eca67783f0000000000000000cac103401c5a5ebdd03f283ee17f3abe84d122beca6778bf0000000000000000b74319409ae4113e20540840e17f3a3e84d1223eca67783f0000000000000000bbf301406dbe33beaa03423ee17f3abe84d122beca6778bf00000000000000000fdd1f401883773e3a000640f26d5e3eccd9743f60ad47be00000000c130883e3157064078c47fbda874213ef26d5e3eccd9743f60ad47be0000803fc130883e15191e407b41f63df9950740f26d5ebeccd974bf60ad473e00000000000000003793044078533cbe90d03a3ef26d5ebeccd974bf60ad473e00000000000000000fdd1f401883773e3a000640f62e4c3e1bba1b3eded1773f00000000000000003157064078c47fbda874213ef62e4cbe1bba1bbeded177bf000000000000000015191e407b41f63df9950740f62e4c3e1bba1b3eded1773f00000000000000003793044078533cbe90d03a3ef62e4cbe1bba1bbeded177bf00000000000000003ea92440081b613ede2e0540f178593ec24b753f225e44be0000000024f2893ed6e30840692690bd78ec193ef178593ec24b753f225e44be0000803f24f2893e57ef224092fdc83de3bd0640f17859bec24b75bf225e443e0000000000000000ef29074074af44bec7dc323ef17859bec24b75bf225e443e00000000000000003ea92440081b613ede2e0540472b5e3e1e97143e2e20773f0000000000000000d6e30840692690bd78ec193e472b5ebe1e9714be2e2077bf000000000000000057ef224092fdc83de3bd0640472b5e3e1e97143e2e20773f0000000000000000ef29074074af44bec7dc323e472b5ebe1e9714be2e2077bf0000000000000000ec75294014d74a3e464304400f87543e75bc753fd8f140be0000000002b38b3e02680b4081fc9fbd2ca7113e0f87543e75bc753fd8f140be0000803f02b38b3e11c6274029039c3d56cb05400f8754be75bc75bfd8f1403e000000000000000027b80940c0d34cbe31282a3e0f8754be75bc75bfd8f1403e0000000000000000ec75294014d74a3e46430440496f703eaa6a0d3ea751763f000000000000000002680b4081fc9fbd2ca7113e496f70beaa6a0dbea75176bf000000000000000011c6274029039c3d56cb0540496f703eaa6a0d3ea751763f000000000000000027b80940c0d34cbe31282a3e496f70beaa6a0dbea75176bf0000000000000000a5422e40b2b9343eea3c0340a29a4f3ec02b763ff7673dbe0000000089738d3e0be40d40c566afbd48a4083ea29a4f3ec02b763ff7673dbe0000803f89738d3ecb9c2c40a6ae5e3dcabd0440a29a4fbec02b76bff7673d3e0000000000000000313e0c406bc154be41b2203ea29a4fbec02b76bff7673d3e0000000000000000a5422e40b2b9343eea3c0340677a813e8a36063e4a65753f00000000000000000be40d40c566afbd48a4083e677a81be8a3606be4a6575bf0000000000000000cb9c2c40a6ae5e3dcabd0440677a813e8a36063e4a65753f0000000000000000313e0c406bc154be41b2203e677a81be8a3606be4a6575bf0000000000000000f20e3340d8c51e3e481b024003b64a3e7599763f7ac039be00000000e8338f3e4a5810407868bebd38c6fd3d03b64a3e7599763f7ac039be0000803fe8338f3e097331405000063dbb94034003b64abe759976bf7ac0393e000000000000000061bc0e40007a5cbe467a163e03b64abe759976bf7ac0393e0000000000000000f20e3340d8c51e3e481b02409cda8a3e14fafd3d2d5a743f00000000000000004a5810407868bebd38c6fd3d9cda8abe14fafdbd2d5a74bf0000000000000000097331405000063dbb9403409cda8a3e14fafd3d2d5a743f000000000000000061bc0e40007a5cbe467a163e9cda8abe14fafdbd2d5a74bf000000000000000050da374070fe083ee8dd00409cdb453e6705773f62fb35be000000004ff4903e24c51240cd04cdbde8c4e83d9cdb453e6705773f62fb35be0000803f4ff4903e44483640601d383cb24f02409cdb45be670577bf62fb353e00000000000000001833114000ff63be0d7f0b3e9cdb45be670577bf62fb353e000000000000000050da374070fe083ee8dd0040ac54943ed780ef3d812f733f000000000000000024c51240cd04cdbde8c4e83dac5494bed780efbd812f73bf000000000000000044483640601d383cb24f0240ac54943ed780ef3d812f733f00000000000000001833114000ff63be0d7f0b3eac5494bed780efbd812f73bf000000000000000037a43c4013cde63dac08ff3ff00d413e6d6f773fe71832be00000000f2b4923e052b15403f3fdbbd2041d23df00d413e6d6f773fe71832be0000803ff2b4923eee1b3b40c8be24bc3bee0040f00d41be6d6f77bfe718323e0000000000000000bca2134016526bbeb97dff3df00d41be6d6f77bfe718323e000000000000000037a43c4013cde63dac08ff3fc7e49d3e2906e13d9ae4713f0000000000000000052b15403f3fdbbd2041d23dc7e49dbe2906e1bd9ae471bf0000000000000000ee1b3b40c8be24bc3bee0040c7e49d3e2906e13d9ae4713f0000000000000000bca2134016526bbeb97dff3dc7e49dbe2906e1bd9ae471bf0000000000000000186c41407702bc3d541cfc3f874f3c3e5dd7773f68192ebe000000000576943e5e8a1740571be9bd7836ba3d874f3c3e5dd7773f68192ebe0000803f0576943e72ed3f403c30ffbcdedffe3f874f3cbe5dd777bf68192e3e0000000000000000b80b1640ee7472be196fe63d874f3cbe5dd777bf68192e3e0000000000000000186c41407702bc3d541cfc3fe486a73ee78ed23ded78703f00000000000000005e8a1740571be9bd7836ba3de486a7bee78ed2bded7870bf000000000000000072ed3f403c30ffbcdedffe3fe486a73ee78ed23ded78703f0000000000000000b80b1640ee7472be196fe63de486a7bee78ed2bded7870bf00000000000000005b31464088a3913d06f6f83fe6a2373e093d783f85fd29be00000000c037963ea9e31940ee9cf6bd589fa03de6a2373e093d783f85fd29be0000803fc037963e35bc4440942455bddda8fb3fe6a237be093d78bf85fd293e0000000000000000836e1840606979becbcccb3de6a237be093d78bf85fd293e00000000000000005b31464088a3913d06f6f83fc536b13e3b20c43d11ec6e3f0000000000000000a9e31940ee9cf6bd589fa03dc536b1be3b20c4bd11ec6ebf000000000000000035bc4440942455bddda8fb3fc536b13e3b20c43d11ec6e3f0000000000000000836e1840606979becbcccb3dc536b1be3b20c4bd11ec6ebf000000000000000064f34a40586c4f3d1795f53fb50a333e4ca0783fc1c525be000000005cfa973e64371c40a0e301beb074853db50a333e4ca0783fc1c525be0000803f5cfa973e9487494080e494bdca36f83fb50a33be4ca078bfc1c5253e000000000000000094cb1a407b1880bee08faf3db50a33be4ca078bfc1c5253e000000000000000064f34a40586c4f3d1795f53f00f0ba3eb6beb53dcc3d6d3f000000000000000064371c40a0e301beb074853d00f0babeb6beb5bdcc3d6dbf00000000000000009487494080e494bdca36f83f00f0ba3eb6beb53dcc3d6d3f000000000000000094cb1a407b1880bee08faf3d00f0babeb6beb5bdcc3d6dbf000000000000000094b14f40f803f93cf0f8f13f7b892e3efd00793f227321be0000000012be993e10861e40254f08bef05c513d7b892e3efd00793f227321be0000803f12be993eeb4e4e40ebbbbebd1289f43f7b892ebefd0079bf2273213e000000000000000067231d40cd6683be94b0913d7b892ebefd0079bf2273213e000000000000000094b14f40f803f93cf0f8f13f11aec43ea46fa73d096e6b3f000000000000000010861e40254f08bef05c513d11aec4bea46fa7bd096e6bbf0000000000000000eb4e4e40ebbbbebd1289f43f11aec43ea46fa73d096e6b3f000000000000000067231d40cd6683be94b0913d11aec4bea46fa7bd096e6bbf0000000000000000486b544058532a3c1121ee3fb7212a3ef75e793fc8061dbe0000000021839b3e34d02040ea920ebee085143db7212a3ef75e793fc8061dbe0000803f21839b3e93115340f911e8bd399ff03fb7212abef75e79bfc8061d3e00000000000000007f761f408ea086bedc4a643db7212abef75e79bfc8061d3e0000000000000000486b544058532a3c1121ee3f526cce3e2038993de27c693f000000000000000034d02040ea920ebee085143d526ccebe203899bde27c69bf000000000000000093115340f911e8bd399ff03f526cce3e2038993de27c693f00000000000000007f761f408ea086bedc4a643d526ccebe203899bde27c69bf0000000000000000dc1f5940103819bc0c0dea3fd0d5253e13ba793f128218be00000000c7499d3e58162340e1b014bee09ca83cd0d5253e13ba793f128218be0000803fc7499d3ee2ce5740fc6f08bed778ec3fd0d525be13ba79bf1282183e00000000000000005ec52140aec689bed0c7213dd0d525be13ba79bf1282183e0000000000000000dc1f5940103819bc0c0dea3f1026d83e601d8b3d996a673f000000000000000058162340e1b014bee09ca83c1026d8be601d8bbd996a67bf0000000000000000e2ce5740fc6f08bed778ec3f1026d83e601d8b3d996a673f00000000000000005ec52140aec689bed0c7213d1026d8be601d8bbd996a67bf0000000000000000a7ce5d403e36ecbc93bce53f46a8213e32127a3f42e613be0000000043129f3e0359254096aa1abe00ed843b46a8213e32127a3f42e613be0000803f43129f3e2a865c4006901cbea315e83f46a821be32127abf42e6133e000000000000000086102440ead98cbe4e7fb73c46a821be32127abf42e6133e0000000000000000a7ce5d403e36ecbc93bce53f8ed6e13e9d477a3da637653f00000000000000000359254096aa1abe00ed843b8ed6e1be9d477abda63765bf00000000000000002a865c4006901cbea315e83f8ed6e13e9d477a3da637653f000000000000000086102440ead98cbe4e7fb73c8ed6e1be9d477abda63765bf00000000000000000377624058c644bd652fe13f599b1d3e35677a3ff1340fbe00000000d5dca03ebf982740be8120be80a15abc599b1d3e35677a3ff1340fbe0000803fd5dca03ec1366140046630be6375e33f599b1dbe35677abff1340f3e00000000000000007d58264016db8fbe14bb903b599b1dbe35677abff1340f3e00000000000000000377624058c644bd652fe13f1279eb3e50a05e3da8e4623f0000000000000000bf982740be8120be80a15abc1279ebbe50a05ebda8e462bf0000000000000000c1366140046630be6375e33f1279eb3e50a05e3da8e4623f00000000000000007d58264016db8fbe14bb903b1279ebbe50a05ebda8e462bf000000000000000050186740752189bd5e65dc3f18b1193e01b97a3f26700abe00000000c3a9a23e10d62940383826bef0a101bd18b1193e01b97a3f26700abe0000803fc3a9a23e03e06540b6ee43befb97de3f18b119be01b97abf26700a3e0000000000000000c39d28401acb92be5e396dbc18b119be01b97abf26700a3e000000000000000050186740752189bd5e65dc3ffd08f53efa4e433d6d72603f000000000000000010d62940383826bef0a101bdfd08f5befa4e43bd6d7260bf000000000000000003e06540b6ee43befb97de3ffd08f53efa4e433d6d72603f0000000000000000c39d28401acb92be5e396dbcfd08f5befa4e43bd6d7260bf0000000000000000eab16b40c942afbd6b5ed73f7deb153e7c077b3fc59905be000000004f79a43e7a112c4082cf2bbe207050bd7deb153e7c077b3fc59905be0000803f4f79a43e47816a403e2757be5f7dd93f7deb15be7c077bbfc599053e0000000000000000d7e02a40aeaa95bea0910cbd7deb15be7c077bbfc599053e0000000000000000eab16b40c942afbd6b5ed73fbd81fe3e3a5c283dece15d3f00000000000000007a112c4082cf2bbe207050bdbd81febe3a5c28bdece15dbf000000000000000047816a403e2757be5f7dd93fbd81fe3e3a5c283dece15d3f0000000000000000d7e02a40aeaa95bea0910cbdbd81febe3a5c28bdece15dbf00000000000000003443704047c2d4bd931ad23f814c123e93527b3fa1b300be00000000be4ba63e7c4b2e40bc4831be909b91bd814c123e93527b3fa1b300be0000803fbe4ba63eed196f40220d6abe9e25d43f814c12be93527bbfa1b3003e000000000000000035222d405e7a98bebad561bd814c12be93527bbfa1b3003e00000000000000003443704047c2d4bd931ad23f72ef033f32cf0d3d4c345b3f00000000000000007c4b2e40bc4831be909b91bd72ef03bf32cf0dbd4c345bbf0000000000000000ed196f40220d6abe9e25d43f72ef033f32cf0d3d4c345b3f000000000000000035222d405e7a98bebad561bd72ef03bf32cf0dbd4c345bbf00000000000000009ccb74409a9af9bdeb99cc3fc3d50e3e359a7b3ff67ff7bd000000005721a83e908430403da536be700ebdbdc3d50e3e359a7b3ff67ff7bd0000803f5721a83e5ea97340b09d7cbed690ce3fc3d50ebe359a7bbff67ff73d000000000000000052622f40d03a9bbeb99f9dbdc3d50ebe359a7bbff67ff73d00000000000000009ccb74409a9af9bdeb99cc3f178e083fbe5fe73cd26a583f0000000000000000908430403da536be700ebdbd178e08bfbe5fe7bcd26a58bf00000000000000005ea97340b09d7cbed690ce3f178e083fbe5fe73cd26a583f000000000000000052622f40d03a9bbeb99f9dbd178e08bfbe5fe7bcd26a58bf00000000000000008e4a794063e30ebe9edcc63fac880b3e55de7b3f5582edbd0000000062faa93e26bd324033e63bbe08a5eabdac880b3e55de7b3f5582edbd0000803f62faa93e062f7840316b87be3cbfc83fac880bbe55de7bbf5582ed3d00000000000000009ea1314099ec9dbe227bccbdac880bbe55de7bbf5582ed3d00000000000000008e4a794063e30ebe9edcc63fce1a0d3f430bb43cee86553f000000000000000026bd324033e63bbe08a5eabdce1a0dbf430bb4bcee8655bf0000000000000000062f7840316b87be3cbfc83fce1a0d3f430bb43cee86553f00000000000000009ea1314099ec9dbe227bccbdce1a0dbf430bb4bcee8655bf000000000000000082bf7d4073a120bee4e2c03fa666083ee91e7c3f7872e3bd0000000025d7ab3eacf534403b0c41be403a0dbea666083ee91e7c3f7872e3bd0000803f25d7ab3e57aa7c40a05a90be10b1c23fa66608bee91e7cbf7872e33d000000000000000081e033400490a0bebd91fdbda66608bee91e7cbf7872e33d000000000000000082bf7d4073a120bee4e2c03faa93113f1fab813c2c8a523f0000000000000000acf534403b0c41be403a0dbeaa9311bf1fab81bc2c8a52bf000000000000000057aa7c40a05a90be10b1c23faa93113f1fab813c2c8a523f000000000000000081e033400490a0bebd91fdbdaa9311bf1fab81bc2c8a52bf0000000000000000fc1481405d0532be04adba3fb470053eea5b7c3f4455d9bd00000000e9b7ad3e832e3740251846be744926beb470053eea5b7c3f4455d9bd0000803fe9b7ad3e698d8040141c99bea366bc3fb47005beea5b7cbf4455d93d00000000000000005c1f36407825a3be7d7c18beb47005beea5b7cbf4455d93d0000000000000000fc1481405d0532be04adba3fe9f6153f3f96203c33764f3f0000000000000000832e3740251846be744926bee9f615bf3f9620bc33764fbf0000000000000000698d8040141c99bea366bc3fe9f6153f3f96203c33764f3f00000000000000005c1f36407825a3be7d7c18bee9f615bf3f9620bc33764fbf0000000000000000bc448340530d43be513bb43fbaa7023e58957c3f652fcfbd00000000f89caf3e046839406b0a4bbe908b40bebaa7023e58957c3f652fcfbd0000803ff89caf3efdbf8240a5aea1be51e0b53fbaa702be58957cbf652fcf3d0000000000000000865e384031ada5be906333bebaa702be58957cbf652fcf3d0000000000000000bc448340530d43be513bb43fe9421a3ff2a27f3bc34c4c3f0000000000000000046839406b0a4bbe908b40bee9421abff2a27fbbc34c4cbf0000000000000000fdbf8240a5aea1be51e0b53fe9421a3ff2a27f3bc34c4c3f0000000000000000865e384031ada5be906333bee9421abff2a27fbbc34c4cbf0000000000000000cc6e854090b753be288ead3f4a0c003e36cb7c3fd205c5bd000000009c86b13e7da23b4080e34fbe400c5cbe4a0c003e36cb7c3fd205c5bd0000803f9c86b13eb3ec84407211aabe821eaf3f4a0c00be36cb7cbfd205c53d00000000000000004b9e3a406a27a8be73894fbe4a0c00be36cb7cbfd205c53d0000000000000000cc6e854090b753be288ead3f37761e3f0404f5bab00f493f00000000000000007da23b4080e34fbe400c5cbe37761ebf0404f53ab00f49bf0000000000000000b3ec84407211aabe821eaf3f37761e3f0404f5bab00f493f00000000000000004b9e3a406a27a8be73894fbe37761ebf0404f53ab00f49bf00000000000000005a9587409e9b61bebf9ea63f1627f83d86fe7c3f8a9ebebd000000001d75b33e78d93d408c0a57be209e78be1627f83d86fe7c3f8a9ebebd0000803f1d75b33e4a1787408210b1be1622a83f1627f8bd86fe7cbf8a9ebe3d000000000000000058dd3c40f8c7abbe6b836cbe1627f8bd86fe7cbf8a9ebe3d00000000000000005a9587409e9b61bebf9ea63f7aa2223f2111a9bb83b2453f000000000000000078d93d408c0a57be209e78be7aa222bf2111a93b83b245bf00000000000000004a1787408210b1be1622a83f7aa2223f2111a9bb83b2453f000000000000000058dd3c40f8c7abbe6b836cbe7aa222bf2111a93b83b245bf000000000000000077e88940bf1a6bbe4cb89e3fb8bfde3da0767d3f08e9b5bd00000000cd5cb53e717f3f40692261bee28887beb8bfde3da0767d3f08e9b5bd0000803fcd5cb53e4f77894094eeb5bef029a03fb8bfdebda0767dbf08e9b53d0000000000000000219d3e4068f2b0be51c281beb8bfdebda0767dbf08e9b53d000000000000000077e88940bf1a6bbe4cb89e3ff9a2283f62859fbb859a403f0000000000000000717f3f40692261bee28887bef9a228bf62859f3b859a40bf00000000000000004f77894094eeb5bef029a03ff9a2283f62859fbb859a403f0000000000000000219d3e4068f2b0be51c281bef9a228bf62859f3b859a40bf000000000000000022298c40ab4e73beed93963fafb3c53df0e37d3f9280acbd00000000c941b73ec9194140f1c56abe9b2493beafb3c53df0e37d3f9280acbd0000803fc941b73eb3c48b404e24babe73f2973fafb3c5bdf0e37dbf9280ac3d0000000000000000ec504040f0dfb5be82aa8dbeafb3c5bdf0e37dbf9280ac3d000000000000000022298c40ab4e73beed93963ff5702e3faf8b88bb145d3b3f0000000000000000c9194140f1c56abe9b2493bef5702ebfaf8b883b145d3bbf0000000000000000b3c48b404e24babe73f2973ff5702e3faf8b88bb145d3b3f0000000000000000ec504040f0dfb5be82aa8dbef5702ebfaf8b883b145d3bbf00000000000000000e578e40f13d7abef8348e3f4c11ad3d57467e3f7d73a2bd000000007e24b93e6aa94240ebf973be07279fbe4c11ad3d57467e3f7d73a2bd0000803f7e24b93e23ff8d40efb4bdbe127f8f3f4c11adbd57467ebf7d73a23d000000000000000094f94140ec92babea0fe99be4c11adbd57467ebf7d73a23d00000000000000000e578e40f13d7abef8348e3f6309343fa48048bbbafe353f00000000000000006aa94240ebf973be07279fbe630934bfa480483bbafe35bf000000000000000023ff8d40efb4bdbe127f8f3f6309343fa48048bbbafe353f000000000000000094f94140ec92babea0fe99be630934bfa480483bbafe35bf0000000000000000fe71904062ef7fbed09e853f9be4943dc79d7e3faad197bd000000005405bb3e1d2f4440c0c27cbefe94abbe9be4943dc79d7e3faad197bd0000803f5405bb3e5b269040dda3c0be4fd3863f9be494bdc79d7ebfaad1973d0000000000000000d79743408c0dbfbe02c3a6be9be494bdc79d7ebfaad1973d0000000000000000fe71904062ef7fbed09e853fbd69393f6528cbba1084303f00000000000000001d2f4440c0c27cbefe94abbebd6939bf6528cb3a108430bf00000000000000005b269040dda3c0be4fd3863fbd69393f6528cbba1084303f0000000000000000d79743408c0dbfbe02c3a6bebd6939bf6528cb3a108430bf0000000000000000c47992407e3582bebea9793f0f717a3d51ea7e3febaa8cbd00000000abe4bc3e88ab4540da9182be6773b8be0f717a3d51ea7e3febaa8cbd0000803fabe4bc3e273a92401bf5c2be6ae57b3f0f717abd51ea7ebfebaa8c3d00000000000000004f2c45407751c3be0efcb3be0f717abd51ea7ebfebaa8c3d0000000000000000c47992407e3582bebea9793fff8f3e3f10b7b839b9f12a3f000000000000000088ab4540da9182be6773b8beff8f3ebf10b7b8b9b9f12abf0000000000000000273a92401bf5c2be6ae57b3fff8f3e3f10b7b839b9f12a3f00000000000000004f2c45407751c3be0efcb3beff8f3ebf10b7b8b9b9f12abf0000000000000000406e9440a0dc83be14b5673f242c4c3d1b2c7f3f940f81bd00000000e0c2be3e2f1f4740c28f86be23c7c5be242c4c3d1b2c7f3f940f81bd0000803fe0c2be3e643a9440f3acc4be95c1693f242c4cbd1b2c7fbf940f813d000000000000000077b746401560c7be21aec1be242c4cbd1b2c7fbf940f813d0000000000000000406e9440a0dc83be14b5673fa47a433fb0c82c3b534c253f00000000000000002f1f4740c28f86be23c7c5bea47a43bfb0c82cbb534c25bf0000000000000000643a9440f3acc4be95c1693fa47a433fb0c82c3b534c253f000000000000000077b746401560c7be21aec1bea47a43bfb0c82cbb534c25bf0000000000000000674f9640cdf184be5466553f180a1f3d5b637f3ffb1e6abd0000000047a0c03e7c8a4840fb5b8abed594d3be180a1f3d5b637f3ffb1e6abd0000803f47a0c03e0227964028d0c5be1042573f180a1fbd5b637fbffb1e6a3d0000000000000000b1394840563acbbe5dddcfbe180a1fbd5b637fbffb1e6a3d0000000000000000674f9640cdf184be5466553fa528483fa645ad3b5f981f3f00000000000000007c8a4840fb5b8abed594d3bea52848bfa645adbb5f981fbf00000000000000000227964028d0c5be1042573fa528483fa645ad3b5f981f3f0000000000000000b1394840563acbbe5dddcfbea52848bfa645adbb5f981fbf0000000000000000391d9840b77985befcc3423f992ae63c59907f3f497651bd00000000347dc23eb4ed49407bf78dbecbe0e1be992ae63c59907f3f497651bd0000803f347dc23efeff97408063c6be9c6d443f992ae6bc59907fbf4976513d00000000000000003eb3494044e1cebe8a8ddebe992ae6bc59907fbf4976513d0000000000000000391d9840b77985befcc3423f7b994c3f43dc073c31da193f0000000000000000b4ed49407bf78dbecbe0e1be7b994cbf43dc07bc31da19bf0000000000000000feff97408063c6be9c6d443f7b994c3f43dc073c31da193f00000000000000003eb3494044e1cebe8a8ddebe7b994cbf43dc07bc31da19bf0000000000000000c2d799406d7985be61d42f3f79ac903c6bb37f3f174438bd00000000ee59c43e04494b40c76291beeeaef0be79ac903c6bb37f3f174438bd0000803fee59c43e62c599401e6cc6becf4a313f79ac90bc6bb37fbf1744383d000000000000000045244b407855d2be13c2edbe79ac90bc6bb37fbf1744383d0000000000000000c2d799406d7985be61d42f3f02cd503f9b953e3cec15143f000000000000000004494b40c76291beeeaef0be02cd50bf9b953ebcec1514bf000000000000000062c599401e6cc6becf4a313f02cd503f9b953e3cec15143f000000000000000045244b407855d2be13c2edbe02cd50bf9b953ebcec1514bf0000000000000000237f9b4020f684be8e9d1c3fc58ef63bf7cc7f3fb6a61ebd00000000bd36c63e7f9c4c40589e94be4e0100bfc58ef63bf7cc7f3fb6a61ebd0000803fbd36c63e4f779b404eefc5beefdf1d3fc58ef6bbf7cc7fbfb6a61e3d0000000000000000d78c4c408697d5beda7dfdbec58ef6bbf7cc7fbfb6a61e3d0000000000000000237f9b4020f684be8e9d1c3f8dc3543f72837a3c6e4f0e3f00000000000000007f9c4c40589e94be4e0100bf8dc354bf72837abc6e4f0ebf00000000000000004f779b404eefc5beefdf1d3f8dc3543f72837a3c6e4f0e3f0000000000000000d78c4c408697d5beda7dfdbe8dc354bf72837abc6e4f0ebf000000000000000081139d4008f583be4e25093ff16017bb69dd7f3f3abb04bd00000000e013c83e21e84d408eaa97be5aef07bff16017bb69dd7f3f3abb04bd0000803fe013c83ee8159d4064f2c4be04330a3ff160173b69dd7fbf3abb043d0000000000000000efec4d40eaa7d8bea4e106bff160173b69dd7fbf3abb043d000000000000000081139d4008f583be4e25093fc37d583f31ac9d3c548a083f000000000000000021e84d408eaa97be5aef07bfc37d58bf31ac9dbc548a08bf0000000000000000e8159d4064f2c4be04330a3fc37d583f31ac9d3c548a083f0000000000000000efec4d40eaa7d8bea4e106bfc37d58bf31ac9dbc548a08bf000000000000000010959e40b07b82be31e2ea3e200242bc33e57f3fee37d5bc0000000091f1c93ed02b4f407a879abeb32210bf200242bc33e57f3fee37d5bc0000803f91f1c93e62a19e40067bc3be7493ec3e2002423c33e57fbfee37d53c000000000000000074444f40d086dbbe124a0fbf2002423c33e57fbfee37d53c000000000000000010959e40b07b82be31e2ea3e9efc5b3f555ec03ce6c9023f0000000000000000d02b4f407a879abeb32210bf9efc5bbf555ec0bce6c902bf000000000000000062a19e40067bc3be7493ec3e9efc5b3f555ec03ce6c9023f000000000000000074444f40d086dbbe124a0fbf9efc5bbf555ec0bce6c902bf00000000000000000804a0404d8f80be2f0cc33efaa2acbcd0e47f3f4ec6a0bc0000000005d0cb3e606750408d359dbe2a9c18bffaa2acbcd0e47f3f4ec6a0bc0000803f05d0cb3ef519a0408a8ec1bee152c43efaa2ac3cd0e47fbf4ec6a03c00000000000000003a935040ca34debed1f817bffaa2ac3cd0e47fbf4ec6a03c00000000000000000804a0404d8f80be2f0cc33e61415f3ffb31e53c4222fa3e0000000000000000606750408d359dbe2a9c18bf61415fbffb31e5bc4222fabe0000000000000000f519a0408a8ec1bee152c43e61415f3ffb31e53c4222fa3e00000000000000003a935040ca34debed1f817bf61415fbffb31e5bc4222fabe0000000000000000ac60a140a56a7cbe42d29a3e3edaf5bcbfdc7f3fc49f58bc000000006cafcd3e949a5140f0b49fbe375c21bf3edaf5bcbfdc7f3fc49f58bc0000803f6cafcd3ee57fa1408332bfbe59ae9b3e3edaf53cbfdc7fbfc49f583c000000000000000006d9514020b2e0be2bee20bf3edaf53cbfdc7fbfc49f583c0000000000000000ac60a140a56a7cbe42d29a3e874d623f74fe053d58c5ee3e0000000000000000949a5140f0b49fbe375c21bf874d62bf74fe05bd58c5eebe0000000000000000e57fa1408332bfbe59ae9b3e874d623f74fe053d58c5ee3e000000000000000006d9514020b2e0be2bee20bf874d62bf74fe05bd58c5eebe000000000000000045aba24021e676bee27a643ef9591ebd7acd7f3f73ffdfbb00000000ee8fcf3e2ac55240f005a2be00632abff9591ebd7acd7f3f73ffdfbb0000803fee8fcf3e7ed3a240606cbcbe775e653ef9591e3d7acd7fbf73ffdf3b00000000000000009b15534040ffe2be1b2a2abff9591e3d7acd7fbf73ffdf3b000000000000000045aba24021e676bee27a643ebe22653f7d4b1a3db981e33e00000000000000002ac55240f005a2be00632abfbe2265bf7d4b1abdb981e3be00000000000000007ed3a240606cbcbe775e653ebe22653f7d4b1a3db981e33e00000000000000009b15534040ffe2be1b2a2abfbe2265bf7d4b1abdb981e3be000000000000000020e4a340a49b70be46ac123ec69f40bd7bb77f3f9948ffb900000000b371d13ed5e65340e028a4be5eb033bfc69f40bd7bb77f3f9948ffb90000803fb371d13e0d15a4408c41b9be7cbc123ec69f403d7bb77fbf9948ff390000000000000000af4854409a1ce5be51ac33bfc69f403d7bb77fbf9948ff39000000000000000020e4a340a49b70be46ac123ed4c2673f366c2f3d6f5bd83e0000000000000000d5e65340e028a4be5eb033bfd4c267bf366c2fbd6f5bd8be00000000000000000d15a4408c41b9be7cbc123ed4c2673f366c2f3d6f5bd83e0000000000000000af4854409a1ce5be51ac33bfd4c267bf366c2fbd6f5bd8be00000000000000008a0ba540ae9569be9490803d3dc761bd439b7f3f0c56be3b00000000d754d33e38ff5440211ea6bed8433dbf3dc761bd439b7f3f0c56be3b0000803fd754d33ee344a54066b7b5bea11b7e3d3dc7613d439b7fbf0c56bebb0000000000000000ea715540b00ae7be30743dbf3dc7613d439b7fbf0c56bebb00000000000000008a0ba540ae9569be9490803db72f6a3f2a4d453deb55cd3e000000000000000038ff5440211ea6bed8433dbfb72f6abf2a4d45bdeb55cdbe0000000000000000e344a54066b7b5bea11b7e3db72f6a3f2a4d453deb55cd3e0000000000000000ea715540b00ae7be30743dbfb72f6abf2a4d45bdeb55cdbe0000000000000000d921a6400cde61bef01595bcdbed80bd3f797f3f352f453c000000007939d53e010e56406ae6a7beaa1c47bfdbed80bd3f797f3f352f453c0000803f7939d53e5863a640f1d2b1be5e9ba1bcdbed803d3f797fbf352f45bc0000000000000000ff90564055cae8bed58047bfdbed803d3f797fbf352f45bc0000000000000000d921a6400cde61bef01595bc636b6c3f8fdd5b3dfa73c23e0000000000000000010e56406ae6a7beaa1c47bf636b6cbf8fdd5bbdfa73c2be00000000000000005863a640f1d2b1be5e9ba1bc636b6c3f8fdd5b3dfa73c23e0000000000000000ff90564055cae8bed58047bf636b6cbf8fdd5bbdfa73c2be00000000000000006227a740197f59befc0eccbd497390bded517f3fe5ee943c00000000af1fd73ed01257402c82a9bec83951bf497390bded517f3fe5ee943c0000803faf1fd73ec370a7407a99adbe83c9d0bd4973903ded517fbfe5ee94bc000000000000000093a557401a5ceabe19d151bf4973903ded517fbfe5ee94bc00000000000000006227a740197f59befc0eccbde6776e3f7c0a733de8b7b73e0000000000000000d01257402c82a9bec83951bfe6776ebf7c0a73bde8b7b7be0000000000000000c370a7407a99adbe83c9d0bde6776e3f7c0a733de8b7b73e000000000000000093a557401a5ceabe19d151bfe6776ebf7c0a73bde8b7b7be0000000000000000731ca840568250beb8d939bef6799fbdb4257f3f9582c63c000000008a07d93e420d584049f2aabee8995bbff6799fbdb4257f3f9582c63c0000803f8a07d93e776da840de0fa9be77003dbef6799f3db4257fbf9582c6bc000000000000000049af5840fcc0ebbe98635cbff6799f3db4257fbf9582c6bc0000000000000000731ca840568250beb8d939be4857703f3b62853d7a23ad3e0000000000000000420d584049f2aabee8995bbf485770bf3b6285bd7a23adbe0000000000000000776da840de0fa9be77003dbe4857703f3b62853d7a23ad3e000000000000000049af5840fcc0ebbe98635cbf485770bf3b6285bd7a23adbe00000000000000006601a94092f146be120787be0707aebd00f57e3ff03ef73c000000001af1da3e00fd58407337acbe7f3b66bf0707aebd00f57e3ff03ef73c0000803f1af1da3ece59a9401c3ba4be79fd88be0707ae3d00f57ebff03ef7bc0000000000000000d0ad5940c6f9ecbeb33667bf0707ae3d00f57ebff03ef7bc00000000000000006601a94092f146be120787be990b723f557d913df6b7a23e000000000000000000fd58407337acbe7f3b66bf990b72bf557d91bdf6b7a2be0000000000000000ce59a9401c3ba4be79fd88be990b723f557d913df6b7a23e0000000000000000d0ad5940c6f9ecbeb33667bf990b72bf557d91bdf6b7a2be00000000000000008bd6a940add53cbe264db1be0821bcbd33c07e3fae88133d0000000068dcdc3eaae15940da52adbecb1c71bf0821bcbd33c07e3fae88133d0000803f68dcdc3e1d36aa40c11f9fbebaa4b3be0821bc3d33c07ebfae8813bd0000000000000000cea05a40c407eebe954872bf0821bc3d33c07ebfae8813bd00000000000000008bd6a940add53cbe264db1bed796733f06d09d3d3876983e0000000000000000aae15940da52adbecb1c71bfd79673bf06d09dbd387698be00000000000000001d36aa40c11f9fbebaa4b3bed796733f06d09d3d3876983e0000000000000000cea05a40c407eebe954872bfd79673bf06d09dbd387698be0000000000000000369caa40b43732be4fbadbbec4cdc9bdac877e3f99f52a3d000000007bc9de3eedba5a409845aebedc3b7cbfc4cdc9bdac877e3f99f52a3d0000803f7bc9de3eba02ab4068c299be1671debec4cdc93dac877ebf99f52abd0000000000000000f5875b4026eceebe40977dbfc4cdc93dac877ebf99f52abd0000000000000000369caa40b43732be4fbadbbefcfa743f7d53aa3db55e8e3e0000000000000000edba5a409845aebedc3b7cbffcfa74bf7d53aabdb55e8ebe0000000000000000ba02ab4068c299be1671debefcfa743f7d53aa3db55e8e3e0000000000000000f5875b4026eceebe40977dbffcfa74bf7d53aabdb55e8ebe0000000000000000b252ab406c2027be082503bffa12d7bdc04b7e3f36e0413d0000000054b8e03e6c885b40e610afbe45cb83bffa12d7bdc04b7e3f36e0413d0000803f54b8e03ef4bfab408c2794befdae04bffa12d73dc04b7ebf36e041bd0000000000000000f0625c403ca8efbe3f9084bffa12d73dc04b7ebf36e041bd0000000000000000b252ab406c2027be082503bfef39763f6001b73d8371843e00000000000000006c885b40e610afbe45cb83bfef3976bf6001b7bd837184be0000000000000000f4bfab408c2794befdae04bfef39763f6001b73d8371843e0000000000000000f0625c403ca8efbe3f9084bfef3976bf6001b7bd837184be000000000000000050faab4065981bbe227c18bf5bf6e3bdc30c7e3f9f43583d00000000f6a8e23edf495c401eb6afbe489589bf5bf6e3bdc30c7e3f9f43583d0000803ff6a8e23e1e6eac4089538ebe95331abf5bf6e33dc30c7ebf9f4358bd00000000000000007b315d40743df0be01718abf5bf6e33dc30c7ebf9f4358bd000000000000000050faab4065981bbe227c18bf8455773fd8d3c33ddb5c753e0000000000000000df495c401eb6afbe489589bf845577bfd8d3c3bddb5c75be00000000000000001e6eac4089538ebe95331abf8455773fd8d3c33ddb5c753e00000000000000007b315d40743df0be01718abf845577bfd8d3c3bddb5c75be00000000000000005a93ac40a0a70fbe8ce02dbf067ef0bdfbca7d3fb11b6e3d00000000599be43ef8fe5c40de36b0bebd7a8fbf067ef0bdfbca7d3fb11b6e3d0000803f599be43e860dad40714a88be62c42fbf067ef03dfbca7dbfb11b6ebd00000000000000004ff35d407fadf0bea86c90bf067ef03dfbca7dbfb11b6ebd00000000000000005a93ac40a0a70fbe8ce02dbf7a4f783f1cc6d03ddb29623e0000000000000000f8fe5c40de36b0bebd7a8fbf7a4f78bf1cc6d0bddb2962be0000000000000000860dad40714a88be62c42fbf7a4f783f1cc6d03ddb29623e00000000000000004ff35d407fadf0bea86c90bf7a4f78bf1cc6d0bddb2962be0000000000000000181ead40225603be755043bf8caffcbdb2867d3fc2b2813d00000000798fe63e70a75d40c394b0be5e7a95bf8caffcbdb2867d3fc2b2813d0000803f798fe63e759ead405a1082be8d5f45bf8caffc3db2867dbfc2b281bd00000000000000002ba85e400cfaf0beea8196bf8caffc3db2867dbfc2b281bd0000000000000000181ead40225603be755043bf7f29793f64d3dd3d90484f3e000000000000000070a75d40c394b0be5e7a95bf7f2979bf64d3ddbd90484fbe0000000000000000759ead405a1082be8d5f45bf7f29793f64d3dd3d90484f3e00000000000000002ba85e400cfaf0beea8196bf7f2979bf64d3ddbd90484fbe0000000000000000ce9aad40e557edbd2cca58bfeb4704be25407d3f960f8c3d000000004c85e83e0b435e4064d1b0bed6929bbfeb4704be25407d3f960f8c3d0000803f4c85e83e3421ae40ae5277be61035bbfeb47043e25407dbf960f8cbd0000000000000000d64f5f40c224f1be71af9cbfeb47043e25407dbf960f8cbd0000000000000000ce9aad40e557edbd2cca58bf23e5793fd5f6ea3dfeb63c3e00000000000000000b435e4064d1b0bed6929bbf23e579bfd5f6eabdfeb63cbe00000000000000003421ae40ae5277be61035bbf23e5793fd5f6ea3dfeb63c3e0000000000000000d64f5f40c224f1be71af9cbf23e579bfd5f6eabdfeb63cbe0000000000000000be09ae401f61d3bd194c6ebf2d120abe8ff77c3f8f23963d00000000c57cea3e8cd15e40a5eeb0bec2c2a1bf2d120abe8ff77c3f8f23963d0000803fc57cea3e0696ae406a326abe43ae70bf2d120a3e8ff77cbf8f2396bd00000000000000001bea5f40922ff1bed7f3a2bf2d120a3e8ff77cbf8f2396bd0000000000000000be09ae401f61d3bd194c6ebfe2837a3fbb2cf83dd4722a3e00000000000000008cd15e40a5eeb0bec2c2a1bfe2837abfbb2cf8bdd4722abe00000000000000000696ae406a326abe43ae70bfe2837a3fbb2cf83dd4722a3e00000000000000001bea5f40922ff1bed7f3a2bfe2837abfbb2cf8bdd4722abe0000000000000000296bae40fcd6b8bd5bea81bff0b80fbe25ad7c3f5eee9f3d00000000d575ec3ec2525f405feeb0beb508a8bff0b80fbe25ad7c3f5eee9f3d0000803fd575ec3e2ffdae408cc75cbe562f83bff0b80f3e25ad7cbf5eee9fbd0000000000000000cd766040661cf1beb04da9bff0b80f3e25ad7cbf5eee9fbd0000000000000000296bae40fcd6b8bd5bea81bf20077b3fa0b8023e6679183e0000000000000000c2525f405feeb0beb508a8bf20077bbfa0b802be667918be00000000000000002ffdae408cc75cbe562f83bf20077b3fa0b8023e6679183e0000000000000000cd766040661cf1beb04da9bf20077bbfa0b802be667918be000000000000000048bfae409ac79dbd4ab18cbfd03e15be18617c3fd16fa93d000000006d70ee3e7dc65f40aed2b0be3e63aebfd03e15be18617c3fd16fa93d0000803f6d70ee3eea56af4039194fbe96098ebfd03e153e18617cbfd16fa9bd0000000000000000c1f5604064edf0be8abbafbfd03e153e18617cbfd16fa9bd000000000000000048bfae409ac79dbd4ab18cbf25707b3fc760093eccc7063e00000000000000007dc65f40aed2b0be3e63aebf25707bbfc76009beccc706be0000000000000000ea56af4039194fbe96098ebf25707b3fc760093eccc7063e0000000000000000c1f5604064edf0be8abbafbf25707bbfc76009beccc706be00000000000000005506af40384182bd257a97bf0da61abe8f137c3f15a8b23d00000000766cf03e9a2c6040999db0bedbd0b4bf0da61abe8f137c3f15a8b23d0000803f766cf03e74a3af40a42e41be2de598bf0da61a3e8f137cbf15a8b2bd0000000000000000d96661409da4f0bee33bb6bf0da61a3e8f137cbf15a8b2bd00000000000000005506af40384182bd257a97bf20c07b3f4b0d103eaeb5ea3d00000000000000009a2c6040999db0bedbd0b4bf20c07bbf4b0d10beaeb5eabd000000000000000074a3af40a42e41be2de598bf20c07b3f4b0d103eaeb5ea3d0000000000000000d96661409da4f0bee33bb6bf20c07bbf4b0d10beaeb5eabd00000000000000008540af4064a34cbd4344a2bfe7f01fbeb6c47b3f7f97bb3d00000000dc69f23ef68460405051b0be0d50bbbfe7f01fbeb6c47b3f7f97bb3d0000803fdc69f23e05e3af40d40e33be73c1a3bfe7f01f3eb6c47bbf7f97bbbd0000000000000000f6c961404d44f0be3dcdbcbfe7f01f3eb6c47bbf7f97bbbd00000000000000008540af4064a34cbd4344a2bf2af87b3fe3bc163e4d5ec83d0000000000000000f68460405051b0be0d50bbbf2af87bbfe3bc16be4d5ec8bd000000000000000005e3af40d40e33be73c1a3bf2af87b3fe3bc163e4d5ec83d0000000000000000f6c961404d44f0be3dcdbcbf2af87bbfe3bc16be4d5ec8bd00000000000000000c6eaf40a00e14bdff0eadbf202125beb0747b3f9f3ec43d000000008868f43e74cf6040daefafbe49dfc1bf202125beb0747b3f9f3ec43d0000803f8868f43ed115b040fcc024bec49daebf2021253eb0747bbf9f3ec4bd0000000000000000ff1e624084ceefbe0e6ec3bf2021253eb0747bbf9f3ec4bd00000000000000000c6eaf40a00e14bdff0eadbf48197c3f066e1d3e5382a63d000000000000000074cf6040daefafbe49dfc1bf48197cbf066e1dbe5382a6bd0000000000000000d115b040fcc024bec49daebf48197c3f066e1d3e5382a63d0000000000000000ff1e624084ceefbe0e6ec3bf48197cbf066e1dbe5382a6bd0000000000000000198faf4000bbb5bcb5d9b7bf19392abe97237b3f099ecc3d000000006168f63e030c6140ad7bafbe037dc8bf19392abe97237b3f099ecc3d0000803f6168f63e0b3cb040814b16be7d79b9bf19392a3e97237bbf099eccbd0000000000000000e8656240be45efbecb1ccabf19392a3e97237bbf099eccbd0000000000000000198faf4000bbb5bcb5d9b7bf5d247c3ffd1f243e741a853d0000000000000000030c6140ad7bafbe037dc8bf5d247cbffd1f24be741a85bd00000000000000000b3cb040814b16be7d79b9bf5d247c3ffd1f243e741a853d0000000000000000e8656240be45efbecb1ccabf5d247cbffd1f24be741a85bd0000000000000000d8a3af4010aa04bcc9a3c2bfb73a2fbe8cd17a3f8bb6d43d000000004e69f83e913a614018f7aebeab27cfbfb73a2fbe8cd17a3f8bb6d43d0000803f4e69f83ee055b04014b507be0554c4bfb73a2f3e8cd17abf8bb6d4bd0000000000000000a29e624052aceebee7d7d0bfb73a2f3e8cd17abf8bb6d4bd0000000000000000d8a3af4010aa04bcc9a3c2bf3d1a7c3fc8d12a3e263e483d0000000000000000913a614018f7aebeab27cfbf3d1a7cbfc8d12abe263e48bd0000000000000000e055b04014b507be0554c4bf3d1a7c3fc8d12a3e263e483d0000000000000000a29e624052aceebee7d7d0bf3d1a7cbfc8d12abe263e48bd000000000000000074acaf406076c73ba06ccdbf682734bea57e7a3f0289dc3d00000000316bfa3e155b61404664aebeacddd5bf682734bea57e7a3f0289dc3d0000803f316bfa3e7d63b0404609f2bdc12ccfbf6827343ea57e7abf0289dcbd000000000000000028c962407104eebecd9dd7bf6827343ea57e7abf0289dcbd000000000000000074acaf406076c73ba06ccdbfa4fb7b3f2082313eb410073d0000000000000000155b61404664aebeacddd5bfa4fb7bbf208231beb41007bd00000000000000007d63b0404609f2bdc12ccfbfa4fb7b3f2082313eb410073d000000000000000028c962407104eebecd9dd7bfa4fb7bbf208231beb41007bd000000000000000014a9af4010aca63ca333d8bf420139bef92a7a3f5a16e43d00000000ed6dfc3e8f6d6140ccc5adbe719ddcbf420139bef92a7a3f5a16e43d0000803fed6dfc3e0b65b040a680d4bd1c03dabf4201393ef92a7abf5a16e4bd00000000000000007de56240b650edbeea6cdebf4201393ef92a7abf5a16e4bd000000000000000014a9af4010aca63ca333d8bf34c97b3f8d30383eaa398d3c00000000000000008f6d6140ccc5adbe719ddcbf34c97bbf8d3038beaa398dbc00000000000000000b65b040a680d4bd1c03dabf34c97b3f8d30383eaa398d3c00000000000000007de56240b650edbeea6cdebf34c97bbf8d3038beaa398dbc0000000000000000dc99af40e0f00d3d39f8e2bfb4c93dbe98d6793f8e5feb3d000000006371fe3ef9716140fd1dadbe5f65e3bfb4c93dbe98d6793f8e5feb3d0000803f6371fe3eaf5ab0407fddb6bd80d6e4bfb4c93d3e98d679bf8e5febbd00000000000000009ff362407993ecbea643e5bfb4c93d3e98d679bf8e5febbd0000000000000000dc99af40e0f00d3d39f8e2bf7d837b3f19dc3e3e2b4bda3a0000000000000000f9716140fd1dadbe5f65e3bf7d837bbf19dc3ebe2b4bdaba0000000000000000af5ab0407fddb6bd80d6e4bf7d837b3f19dc3e3e2b4bda3a00000000000000009ff362407993ecbea643e5bf7d837bbf19dc3ebe2b4bdaba0000000000000000ee7eaf4058a7483dd1b9edbfa38242be8f81793faa65f23d00000000bb3a003f60686140796facbee333eabfa38242be8f81793faa65f23d0000803fbb3a003f8d44b040de2b99bd5ea6efbfa382423e8f8179bfaa65f2bd00000000000000009ff362405ccfebbe7020ecbfa382423e8f8179bfaa65f2bd0000000000000000ee7eaf4058a7483dd1b9edbff72a7b3f6484453e777b61bc000000000000000060686140796facbee333eabff72a7bbf648445be777b613c00000000000000008d44b040de2b99bd5ea6efbff72a7b3f6484453e777b61bc00000000000000009ff362405ccfebbe7020ecbff72a7bbf648445be777b613c00000000000000006958af407aaf813dd177f8bf362d47bef12b793fba29f93d00000000003d013fce5061408ebcabbe5b07f1bf362d47bef12b793fba29f93d0000803f003d013fc622b04026f276bd1e72fabf362d473ef12b79bfba29f9bd000000000000000088e56240b106ebbea801f3bf362d473ef12b79bfba29f9bd00000000000000006958af407aaf813dd177f8bf09c07a3f6c284c3ecd0eeebc0000000000000000ce5061408ebcabbe5b07f1bf09c07abf6c284cbecd0eee3c0000000000000000c622b04026f276bd1e72fabf09c07a3f6c284c3ecd0eeebc000000000000000088e56240b106ebbea801f3bf09c07abf6c284cbecd0eee3c00000000000000006a26af40c0ff9e3dd69801c00dcb4bbec4d5783fd7acff3d00000000733f023f532b6140d607abbe33def7bf0dcb4bbec4d5783fd7acff3d0000803f733f023f78f5af407ea23bbd9a9c02c00dcb4b3ec4d578bfd7acffbd00000000000000006fc96240163ceabebbe5f9bf0dcb4b3ec4d578bfd7acffbd00000000000000006a26af40c0ff9e3dd69801c003437a3fc6c7523e9f3735bd0000000000000000532b6140d607abbe33def7bf03437abfc6c752be9f37353d000000000000000078f5af407ea23bbd9a9c02c003437a3fc6c7523e9f3735bd00000000000000006fc96240163ceabebbe5f9bf03437abfc6c752be9f37353d00000000000000000de9ae406437bc3d67f306c0315d50be167f783f04f8023e000000000242033f07f86040a753aabecab6febf315d50be167f783f04f8023e0000803f0242033fc0bcaf40148300bd88fd07c0315d503e167f78bf04f802be00000000000000006c9f6240e271e9be866500c0315d503e167f78bf04f802be00000000000000000de9ae406437bc3d67f306c025b4793f8061593e4f0073bd000000000000000007f86040a753aabecab6febf25b479bf806159be4f00733d0000000000000000c0bcaf40148300bd88fd07c025b4793f8061593e4f0073bd00000000000000006c9f6240e271e9be866500c025b479bf806159be4f00733d000000000000000067a0ae40a54ad93d524b0cc048e554bee927783f39fa053e000000009b44043f02b76040b1a2a9bec2c702c048e554bee927783f39fa053e0000803f9b44043fb478af40dc568bbc905b0dc048e5543ee92778bf39fa05be00000000000000009d676240c8aae8be00d803c048e5543ee92778bf39fa05be000000000000000067a0ae40a54ad93d524b0cc09613793f5af55f3ef23898bd000000000000000002b76040b1a2a9bec2c702c0961379bf5af55fbef238983d0000000000000000b478af40dc568bbc905b0dc09613793f5af55f3ef23898bd00000000000000009d676240c8aae8be00d803c0961379bf5af55fbef238983d00000000000000008f4cae40dd2cf63d4ca011c0796459be42d0773f91dd083e000000002b47053f676860406af7a8be643306c0796459be42d0773f91dd083e0000803f2b47053f6e29af40204e33bb68b612c07964593e42d077bf91dd08be0000000000000000252262403ee9e7be804907c07964593e42d077bf91dd08be00000000000000008f4cae40dd2cf63d4ca011c06d61783fa182663e84ceb6bd0000000000000000676860406af7a8be643306c06d6178bfa18266be84ceb63d00000000000000006e29af40204e33bb68b612c06d61783fa182663e84ceb6bd0000000000000000252262403ee9e7be804907c06d6178bfa18266be84ceb63d000000000000000098edad40c268093e10f216c0eadb5dbe2178773f94a20b3e00000000a349063f590c60404d54a8be7c9d09c0eadb5dbe2178773f94a20b3e0000803fa349063f01cfae40001e3b3ccd0d18c0eadb5d3e217877bf94a20bbe00000000000000002acf6140be2fe7be39b90ac0eadb5d3e217877bf94a20bbe000000000000000098edad40c268093e10f216c0ac9d773fae086d3e3b49d5bd0000000000000000590c60404d54a8be7c9d09c0ac9d77bfae086dbe3b49d53d000000000000000001cfae40001e3b3ccd0d18c0ac9d773fae086d3e3b49d5bd00000000000000002acf6140be2fe7be39b90ac0ac9d77bfae086dbe3b49d53d00000000000000009083ad401e96173e52401cc0da4c62be8d1f773fca490e3e00000000f04b073ffda25f40e1bba7be3c050dc0da4c62be8d1f773fca490e3e0000803ff04b073f7c69ae40dc61d03c73611dc0da4c623e8d1f77bfca490ebe0000000000000000d56e6140d280e6be5d260ec0da4c623e8d1f77bfca490ebe00000000000000009083ad401e96173e52401cc044c8763ff086733e53b1f3bd0000000000000000fda25f40e1bba7be3c050dc044c876bff08673be53b1f33d00000000000000007c69ae40dc61d03c73611dc044c8763ff086733e53b1f3bd0000000000000000d56e6140d280e6be5d260ec044c876bff08673be53b1f33d0000000000000000890ead400e98253ec98a21c038b866be82c6763fb1d3103e00000000ff4d083f862c5f408d30a7bed76910c038b866be82c6763fb1d3103e0000803fff4d083ff2f8ad409eed203d13b122c038b8663e82c676bfb1d310be000000000000000058016140e0dee5be219011c038b8663e82c676bfb1d310be0000000000000000890ead400e98253ec98a21c016e1753f94fc793e930709be0000000000000000862c5f408d30a7bed76910c016e175bf94fc79be9307093e0000000000000000f2f8ad409eed203d13b122c016e1753f94fc793e930709be000000000000000058016140e0dee5be219011c016e175bf94fc79be9307093e00000000000000008c8eac40ac68333e2cd126c0871f6bbef66c763fda40133e00000000c04f093f27a95e40fab4a6be7eca13c0871f6bbef66c763fda40133e0000803fc04f093f6f7dad400ae6583d64fc27c0871f6b3ef66c76bfda4013be0000000000000000ec8660408f4ce5beb6f514c0871f6b3ef66c76bfda4013be00000000000000008c8eac40ac68333e2cd126c0e3e7743fa834803e703518be000000000000000027a95e40fab4a6be7eca13c0e3e774bfa83480be7035183e00000000000000006f7dad400ae6583d64fc27c0e3e7743fa834803e703518be0000000000000000ec8660408f4ce5beb6f514c0e3e774bfa83480be7035183e0000000000000000a503ac403401413e33132cc073836fbeef12763fb891153e000000001f510a3f15195e40664ba6be652617c073836fbeef12763fb891153e0000803f1f510a3ffef6ac408dff873d20432dc073836f3eef1276bfb89115be0000000000000000c6ff5f401dcce4be525618c073836f3eef1276bfb89115be0000000000000000a503ac403401413e33132cc06bdc733f0066833e696627be000000000000000015195e40664ba6be652617c06bdc73bf006683be6966273e0000000000000000fef6ac408dff873d20432dc06bdc733f0066833e696627be0000000000000000c6ff5f401dcce4be525618c06bdc73bf006683be6966273e0000000000000000da6dab40645b4e3e8f5031c034e573be62b8753fcdc6173e000000000b520b3f8d7c5d4050f6a5bebd7c1ac034e573be62b8753fcdc6173e0000803f0b520b3fa665ac40ed0fa33df88432c034e5733e62b875bfcdc617be0000000000000000256c5f400760e4be26b11bc034e5733e62b875bfcdc617be0000000000000000da6dab40645b4e3e8f5031c04dbe723f0192863e8e9e36be00000000000000008d7c5d4050f6a5bebd7c1ac04dbe72bf019286be8e9e363e0000000000000000a665ac40ed0fa33df88432c04dbe723f0192863e8e9e36be0000000000000000256c5f400760e4be26b11bc04dbe72bf019286be8e9e363e000000000000000030cdaa4099705b3efa8836c0aa4578be475d753f8de0193e0000000071520c3fd0d35c4003b8a5bebacc1dc0aa4578be475d753f8de0193e0000803f71520c3f6fc9ab40e796bd3da8c137c0aa45783e475d75bf8de019be00000000000000004dcc5e40960ae4be68051fc0aa45783e475d75bf8de019be000000000000000030cdaa4099705b3efa8836c01e8d713f28b8893ef8e145be0000000000000000d0d35c4003b8a5bebacc1dc01e8d71bf28b889bef8e1453e00000000000000006fc9ab40e796bd3da8c137c01e8d713f28b8893ef8e145be00000000000000004dcc5e40960ae4be68051fc01e8d71bf28b889bef8e1453e0000000000000000a821aa40753a683e27bc3bc012a67cbe9201753f7edf1b3e0000000042520d3f251f5c40f092a5be931521c012a67cbe9201753f7edf1b3e0000803f42520d3f5922ab40cc87d73de3f83cc012a67c3e920175bf7edf1bbe000000000000000087205e4038cee3be4f5222c012a67c3e920175bf7edf1bbe0000000000000000a821aa40753a683e27bc3bc05448703f15d88c3ea43455be0000000000000000251f5c40f092a5be931521c0544870bf15d88cbea434553e00000000000000005922ab40cc87d73de3f83cc05448703f15d88c3ea43455be000000000000000087205e4038cee3be4f5222c0544870bf15d88cbea434553e0000000000000000416ba94035b2743ecbe940c0ad8380be39a5743f11c41d3e0000000069510e3fd65e5b405389a5be795624c0ad8380be39a5743f11c41d3e0000803f69510e3f6570aa401fd5f03d602a42c0ad83803e39a574bf11c41dbe00000000000000001e695d4026ade3be0e9725c0ad83803e39a574bf11c41dbe0000000000000000416ba94035b2743ecbe940c05aef6e3f37f18f3e949a64be0000000000000000d65e5b405389a5be795624c05aef6ebf37f18fbe949a643e00000000000000006570aa401fd5f03d602a42c05aef6e3f37f18f3e949a64be00000000000000001e695d4026ade3be0e9725c05aef6ebf37f18fbe949a643e0000000000000000fca9a8409668803e991146c04eb582be2f48743fc98e1f3e00000000d84f0f3f35935a40769da5bea38e27c04eb582be2f48743fc98e1f3e0000803fd84f0f3f95b3a940cab8043ed25547c04eb5823e2f4874bfc98e1fbe000000000000000068a65c40a7a9e3bedcd228c04eb5823e2f4874bfc98e1fbe0000000000000000fca9a8409668803e991146c084816d3f0603933eb51774be000000000000000035935a40769da5bea38e27c084816dbf060393beb517743e000000000000000095b3a940cab8043ed25547c084816d3f0603933eb51774be000000000000000068a65c40a7a9e3bedcd228c084816dbf060393beb517743e0000000000000000cfdda7401a48863e44334bc04ee884be63ea733f0d40213e000000007d4d103f96bc594064d1a5be46bd2ac04ee884be63ea733f0d40213e0000803f7d4d103fe0eba84078a7103eed7a4cc04ee8843e63ea73bf0d4021be0000000000000000b9d85b40c2c5e3beef042cc04ee8843e63ea73bf0d4021be0000000000000000cfdda7401a48863e44334bc010fe6b3fbf0c963efad781be000000000000000096bc594064d1a5be46bd2ac010fe6bbfbf0c96befad7813e0000000000000000e0eba84078a7103eed7a4cc010fe6b3fbf0c963efad781be0000000000000000b9d85b40c2c5e3beef042cc010fe6bbfbf0c96befad7813e0000000000000000b306a74023f48b3e7d4e50c0321d87bec58b733f55d8223e00000000474a113f52db58403727a6be99e12dc0321d87bec58b733f55d8223e0000803f474a113f4019a8409b2f1c3e649951c0321d873ec58b73bf55d822be00000000000000006c005b408c03e4be802c2fc0321d873ec58b73bf55d822be0000000000000000b306a74023f48b3e7d4e50c027646a3fad0d993e8fb389be000000000000000052db58403727a6be99e12dc027646abfad0d99be8fb3893e00000000000000004019a8409b2f1c3e649951c027646a3fad0d993e8fb389be00000000000000006c005b408c03e4be802c2fc027646abfad0d99be8fb3893e00000000000000009e24a6403c69913ef46255c09f5489be3c2c733f3358243e000000002746123fd0ef57401aa1a6bed8fa30c09f5489be3c2c733f3358243e0000803f2746123fac3ba740554a273ee7b056c09f54893e3c2c73bf335824be0000000000000000ed1d5a402c65e4becb4832c09f54893e3c2c73bf335824be00000000000000009e24a6403c69913ef46255c0d7b2683f2b059c3e70a091be0000000000000000d0ef57401aa1a6bed8fa30c0d7b268bf2b059cbe70a0913e0000000000000000ac3ba740554a273ee7b056c0d7b2683f2b059c3e70a091be0000000000000000ed1d5a402c65e4becb4832c0d7b268bf2b059cbe70a0913e00000000000000008237a54081a3963e58705ac0e48e8bbeb9cb723f05c0253e000000000d41133f71fa5640c740a7be3a0834c0e48e8bbeb9cb723f05c0253e0000803f0d41133f1753a640e6ef313e26c15bc0e48e8b3eb9cb72bf05c025be00000000000000009b31594055ece4be085935c0e48e8b3eb9cb72bf05c025be00000000000000008237a54081a3963e58705ac024e9663f24f29e3e74a099be000000000000000071fa5640c740a7be3a0834c024e966bf24f29ebe74a0993e00000000000000001753a640e6ef313e26c15bc024e9663f24f29e3e74a099be00000000000000009b31594055ece4be085935c024e966bf24f29ebe74a0993e00000000000000004c3fa4405d9f9b3e53765fc0b9cc8dbe1f6a723f7210273e00000000ec3a143fa4fb55404908a8befd0837c0b9cc8dbe1f6a723f7210273e0000803fec3a143f6f5fa54033193c3eccc960c0b9cc8d3e1f6a72bf721027be0000000000000000ea3b58400c9be5be765c38c0b9cc8d3e1f6a72bf721027be00000000000000004c3fa4405d9f9b3e53765fc0e805653fd3d3a13e57b5a1be0000000000000000a4fb55404908a8befd0837c0e80565bfd3d3a1be57b5a13e00000000000000006f5fa54033193c3eccc960c0e805653fd3d3a13e57b5a1be0000000000000000ea3b58400c9be5be765c38c0e80565bfd3d3a1be57b5a13e0000000000000000ea3ba340b458a03e937464c06a0e90be5607723fd849283e00000000b433153fd8f3544022f9a8be61fc39c06a0e90be5607723fd849283e0000803fb433153fa360a44010be453e89ca65c06a0e903e560772bfd84928be00000000000000004a3d5740ce72e6be57523bc06a0e903e560772bfd84928be0000000000000000ea3ba340b458a03e937464c0f907633feba8a43ecbe0a9be0000000000000000d8f3544022f9a8be61fc39c0f90763bfeba8a4becbe0a93e0000000000000000a360a44010be453e89ca65c0f907633feba8a43ecbe0a9be00000000000000004a3d5740ce72e6be57523bc0f90763bfeba8a4becbe0a93e0000000000000000442da240b2cba43ebd6a69c0ad5492be3ea3713fea6c293e00000000582b163f87e353401e15aabea5e13cc0ad5492be3ea3713fea6c293e0000803f582b163f9c56a340e5d64e3e03c36ac0ad54923e3ea371bfea6c29be0000000000000000373656405e75e7beeb393ec0ad54923e3ea371bfea6c29be0000000000000000442da240b2cba43ebd6a69c003ee603f6870a73e6024b2be000000000000000087e353401e15aabea5e13cc003ee60bf6870a7be6024b23e00000000000000009c56a340e5d64e3e03c36ac003ee603f6870a73e6024b2be0000000000000000373656405e75e7beeb393ec003ee60bf6870a7be6024b23e00000000000000004113a1402df4a83e74586ec0fe9f94beb53d713f397a2a3e00000000cd21173f31cb5240af5dabbe10b83fc0fe9f94beb53d713f397a2a3e0000803fcd21173f4341a240705b573eddb26fc0fe9f943eb53d71bf397a2abe00000000000000003427554024a4e8be791241c0fe9f943eb53d71bf397a2abe00000000000000004113a1402df4a83e74586ec0a3b65e3fee28aa3e9281babe000000000000000031cb5240af5dabbe10b83fc0a3b65ebfee28aabe9281ba3e00000000000000004341a240705b573eddb26fc0a3b65e3fee28aa3e9281babe00000000000000003427554024a4e8be791241c0a3b65ebfee28aabe9281ba3e000000000000000044f99f40277eaa3e6e4a73c07bd394bef23f713f4e93293e000000000617183f5d9451407784aabed67142c07bd394bef23f713f4e93293e0000803f0617183fae27a140406e5a3e02a374c07bd3943ef23f71bf4e9329be000000000000000031f153407ecbe7be6aca43c07bd3943ef23f71bf4e9329be000000000000000044f99f40277eaa3e6e4a73c055bc5c3f4f81aa3e6162c3be00000000000000005d9451407784aabed67142c055bc5cbf4f81aabe6162c33e0000000000000000ae27a140406e5a3e02a374c055bc5c3f4f81aa3e6162c3be000000000000000031f153407ecbe7be6aca43c055bc5cbf4f81aabe6162c33e00000000000000007fc79e40add8ac3e174978c0881b95be680b713f98372d3e00000000b90f193f3e5b5040b5d4aabe1b2a45c0881b95be680b713f98372d3e0000803fb90f193f7cf69f40fd3d5f3e11a979c0881b953e680b71bf98372dbe000000000000000037b95240640ee8be158a46c0881b953e680b71bf98372dbe00000000000000007fc79e40add8ac3e174978c080675a3fb1d6ab3ef07bccbe00000000000000003e5b5040b5d4aabe1b2a45c080675abfb1d6abbef07bcc3e00000000000000007cf69f40fd3d5f3e11a979c080675a3fb1d6ab3ef07bccbe000000000000000037b95240640ee8be158a46c080675abfb1d6abbef07bcc3e0000000000000000e9899d400807af3efe377dc09c5295be0bd7703fb0fc303e0000000039081a3f7e134f40623babbe14de47c09c5295be0bd7703fb0fc303e0000803f39081a3f55b99e404cb5633ea19f7ec09c52953e0bd770bfb0fc30be000000000000000057725140c467e8beb74549c09c52953e0bd770bfb0fc30be0000000000000000e9899d400807af3efe377dc0aa00583f3521ad3ea467d5be00000000000000007e134f40623babbe14de47c0aa0058bf3521adbea467d53e000000000000000055b99e404cb5633ea19f7ec0aa00583f3521ad3ea467d5be000000000000000057725140c467e8beb74549c0aa0058bf3521adbea467d53e0000000000000000c5409c40010bb13e720b81c0427895bee5a2703f68e1343e0000000090001b3f2abd4d40d5b7abbea98d4ac0427895bee5a2703f68e1343e0000803f90001b3f7e709d40bcd7673e38c381c04278953ee5a270bf68e134be00000000000000009c1c5040f8d6e8be36fd4bc04278953ee5a270bf68e134be0000000000000000c5409c40010bb13e720b81c0c088553f6b61ae3eea24debe00000000000000002abd4d40d5b7abbea98d4ac0c08855bf6b61aebeea24de3e00000000000000007e709d40bcd7673e38c381c0c088553f6b61ae3eea24debe00000000000000009c1c5040f8d6e8be36fd4bc0c08855bf6b61aebeea24de3e000000000000000056ec9a4061e6b23ec77283c00d8c95be036f703f9be4383e00000000c5f81b3f58584c406549acbeba384dc00d8c95be036f703f9be4383e0000803fc5f81b3f371c9c40d8a86b3ea12e84c00d8c953e036f70bf9be438be00000000000000001ab84e405a5be9be6eb04ec00d8c953e036f70bf9be438be000000000000000056ec9a4061e6b23ec77283c0a800533fe397af3e4cb3e6be000000000000000058584c406549acbeba384dc0a80053bfe397afbe4cb3e63e0000000000000000371c9c40d8a86b3ea12e84c0a800533fe397af3e4cb3e6be00000000000000001ab84e405a5be9be6eb04ec0a80053bfe397afbe4cb3e63e0000000000000000dc8c9940bb9ab43ee4d185c0648d95be713b703feb043d3e00000000e3f01c3f13e54a4033efacbe26df4fc0648d95be713b703feb043d3e0000803fe3f01c3fc0bc9a40be2b6f3eef9186c0648d953e713b70bfeb043dbe0000000000000000db444d400ff4e9be3c5f51c0648d953e713b70bfeb043dbe0000000000000000dc8c9940bb9ab43ee4d185c04869503ff7c4b03e8512efbe000000000000000013e54a4033efacbe26df4fc0486950bff7c4b0be8512ef3e0000000000000000c0bc9a40be2b6f3eef9186c04869503ff7c4b03e8512efbe0000000000000000db444d400ff4e9be3c5f51c0486950bff7c4b0be8512ef3e00000000000000009a2298400b2ab63eb02888c0117c95be3008703f6141413e00000000eee81d3f7c634940c5a8adbecd8052c0117c95be3008703f6141413e0000803feee81d3f5b5299406864723e09ed88c0117c953e300870bf614141be0000000000000000fdc24b409ca0eabe7f0954c0117c953e300870bf614141be00000000000000009a2298400b2ab63eb02888c072c34d3f68e9b13e4e42f7be00000000000000007c634940c5a8adbecd8052c072c34dbf68e9b1be4e42f73e00000000000000005b5299406864723e09ed88c072c34d3f68e9b13e4e42f7be0000000000000000fdc24b409ca0eabe7f0954c072c34dbf68e9b1be4e42f73e0000000000000000d0ad9640db95b73e15778ac0845795be50d56f3f9c98453e00000000efe01e3fa1d347403775aebe821d55c0845795be50d56f3f9c98453e0000803fefe01e3f46dd9740e055753ed73f8bc08457953e50d56fbf9c9845be00000000000000008e324a402260ebbe06af56c08457953e50d56fbf9c9845be0000000000000000d0ad9640db95b73e15778ac0ff0f4b3f8905b33e9e42ffbe0000000000000000a1d347403775aebe821d55c0ff0f4bbf8905b3be9e42ff3e000000000000000046dd9740e055753ed73f8bc0ff0f4b3f8905b33e9e42ffbe00000000000000008e324a402260ebbe06af56c0ff0f4bbf8905b3be9e42ff3e0000000000000000be2e9540eadfb83efdbc8cc05e1f95bed4a26f3f70094a3e00000000ecd81f3fa0354640d453afbe1db557c05e1f95bed4a26f3f70094a3e0000803fecd81f3fc25d9640a403783e428a8dc05e1f953ed4a26fbf70094abe0000000000000000a9934840ec31ecbea74f59c05e1f953ed4a26fbf70094abe0000000000000000be2e9540eadfb83efdbc8cc0b94f483fdf19b43eb98903bf0000000000000000a0354640d453afbe1db557c0b94f48bfdf19b4beb989033f0000000000000000c25d9640a403783e428a8dc0b94f483fdf19b43eb98903bf0000000000000000a9934840ec31ecbea74f59c0b94f48bfdf19b4beb989033f0000000000000000a2a59340160aba3e54fa8ec060d394bebb706f3fdb924e3e00000000ead0203f958944400644b0be71475ac060d394bebb706f3fdb924e3e0000803fead0203f0cd494406e717a3e35cc8fc060d3943ebb706fbfdb924ebe000000000000000069e646406515edbe33eb5bc060d3943ebb706fbfdb924ebe0000000000000000a2a59340160aba3e54fa8ec05d83453f0e27b53e6d5a07bf0000000000000000958944400644b0be71475ac05d8345bf0e27b5be6d5a073f00000000000000000cd494406e717a3e35cc8fc05d83453f0e27b53e6d5a07bf000000000000000069e646406515edbe33eb5bc05d8345bf0e27b5be6d5a073f0000000000000000b8129240e015bb3e062f91c0047394be143f6f3f7633533e00000000edc8213f99cf4240dc44b1be49d45cc0047394be143f6f3f7633533e0000803fedc8213f5e4093403ca27c3e9b0592c00473943e143f6fbf763353be0000000000000000e52a45409e09eebe72815ec00473943e143f6fbf763353be0000000000000000b8129240e015bb3e062f91c0afab423f5e2db63e88130bbf000000000000000099cf4240dc44b1be49d45cc0afab42bf5e2db6be88130b3f00000000000000005e4093403ca27c3e9b0592c0afab423f5e2db63e88130bbf0000000000000000e52a45409e09eebe72815ec0afab42bf5e2db6be88130b3f00000000000000003d7690402c05bc3e045b93c01cfe93bed50d6f3f59ea573e00000000fbc0223fd0074140ca55b2be755b5fc01cfe93bed50d6f3f59ea573e0000803ffbc0223ff6a29140d8997e3e633694c01cfe933ed50d6fbf59ea57be0000000000000000416143400a0eefbe321261c01cfe933ed50d6fbf59ea57be00000000000000003d7690402c05bc3e045b93c053c93f3f7b2db73e24b50ebf0000000000000000d0074140ca55b2be755b5fc053c93fbf7b2db7be24b50e3f0000000000000000f6a29140d8997e3e633694c053c93f3f7b2db73e24b50ebf0000000000000000416143400a0eefbe321261c053c93fbf7b2db7be24b50e3f00000000000000006cd08e4085d9bc3e3d7e95c0347493be0add6e3f34b65c3e0000000018b9233f5a323f40e575b3bec3dc61c0347493be0add6e3f34b65c3e0000803f18b9233f0cfc8f40aa2d803e7b5e96c03474933e0add6ebf34b65cbe00000000000000009b894140c021f0be3f9d63c03474933e0add6ebf34b65cbe00000000000000006cd08e4085d9bc3e3d7e95c0fcdc3c3fb527b83e6d3f12bf00000000000000005a323f40e575b3bec3dc61c0fcdc3cbfb527b8be6d3f123f00000000000000000cfc8f40aa2d803e7b5e96c0fcdc3c3fb527b83e6d3f12bf00000000000000009b894140c021f0be3f9d63c0fcdc3cbfb527b8be6d3f123f00000000000000007f218d40ab94bd3ea09897c008d592beabac6e3fff95613e0000000045b1243f5c4f3d407da4b4bef55764c008d592beabac6e3fff95613e0000803f45b1243fdc4b8e4019f5803ed27d98c008d5923eabac6ebfff9561be000000000000000016a43f400f44f1be592266c008d5923eabac6ebfff9561be00000000000000007f218d40ab94bd3ea09897c043e7393f941cb93e94b215bf00000000000000005c4f3d407da4b4bef55764c043e739bf941cb9be94b2153f0000000000000000dc4b8e4019f5803ed27d98c043e7393f941cb93e94b215bf000000000000000016a43f400f44f1be592266c043e739bf941cb9be94b2153f0000000000000000ae698b402138be3e1faa99c02d2092bec47c6e3f7488663e0000000087a9253ff95e3b40a1e0b5bed6cc66c02d2092bec47c6e3f7488663e0000803f87a9253f9b928c40baa4813e58949ac02d20923ec47c6ebf748866be0000000000000000d4b03d400874f2be47a168c02d20923ec47c6ebf748866be0000000000000000ae698b402138be3e1faa99c0c4e8363f610cba3ecf0e19bf0000000000000000f95e3b40a1e0b5bed6cc66c0c4e836bf610cbabecf0e193f00000000000000009b928c40baa4813e58949ac0c4e8363f610cba3ecf0e19bf0000000000000000d4b03d400874f2be47a168c0c4e836bf610cbabecf0e193f000000000000000032a98940b9c5be3eacb29bc0745591be4a4d6e3fb18c6b3e00000000dfa1263f60613940b329b7be2b3b69c0745591be4a4d6e3fb18c6b3e0000803fdfa1263f83d08a40613e823efea19cc07455913e4a4d6ebfb18c6bbe000000000000000003b03b400bb1f3bece196bc07455913e4a4d6ebfb18c6bbe000000000000000032a98940b9c5be3eacb29bc006e2333fb6f7ba3e5b541cbf000000000000000060613940b329b7be2b3b69c006e233bfb6f7babe5b541c3f000000000000000083d08a40613e823efea19cc006e2333fb6f7ba3e5b541cbf000000000000000003b03b400bb1f3bece196bc006e233bfb6f7babe5b541c3f00000000000000003ee08740113fbf3e3cb29dc0887490be3a1e6e3f9ea1703e00000000509a273fb6563740db7eb8bebca26bc0887490be3a1e6e3f9ea1703e0000803f509a273fc6058940adc3823eb7a69ec08874903e3a1e6ebf9ea170be0000000000000000c7a139403ffaf4beb28b6dc08874903e3a1e6ebf9ea170be00000000000000003ee08740113fbf3e3cb29dc08cd3303ff6debb3e78831fbf0000000000000000b6563740db7eb8bebca26bc08cd330bff6debbbe78831f3f0000000000000000c6058940adc3823eb7a69ec08cd3303ff6debb3e78831fbf0000000000000000c7a139403ffaf4beb28b6dc08cd330bff6debbbe78831f3f00000000000000000b0f86409ca5bf3ebfa89fc0fe7c8fbe98ef6d3ff3c5753e00000000da92283f2b3f35401cdfb9be46036ec0fe7c8fbe98ef6d3ff3c5753e0000803fda92283f9c3287401036833e74a2a0c0fe7c8f3e98ef6dbff3c575be00000000000000004e863740a84ef6beaff66fc0fe7c8f3e98ef6dbff3c575be00000000000000000b0f86409ca5bf3ebfa89fc0d7bd2d3f5cc2bc3e6f9c22bf00000000000000002b3f35401cdfb9be46036ec0d7bd2dbf5cc2bcbe6f9c223f00000000000000009c3287401036833e74a2a0c0d7bd2d3f5cc2bc3e6f9c22bf00000000000000004e863740a84ef6beaff66fc0d7bd2dbf5cc2bcbe6f9c223f0000000000000000cd35844020fbbf3e2b96a1c0a76e8ebe5bc16d3fdbf87a3e00000000808b293ff01a3340ca49bbbe935c70c0a76e8ebe5bc16d3fdbf87a3e0000803f808b293f395785405397833e2895a2c0a76e8e3e5bc16dbfdbf87abe0000000000000000c85d354097adf7be8d5a72c0a76e8e3e5bc16dbfdbf87abe0000000000000000cd35844020fbbf3e2b96a1c054a12a3f75a2bd3e859f25bf0000000000000000f01a3340ca49bbbe935c70c054a12abf75a2bdbe859f253f0000000000000000395785405397833e2895a2c054a12a3f75a2bd3e859f25bf0000000000000000c85d354097adf7be8d5a72c054a12abf75a2bdbe859f253f0000000000000000b45482402741c03e717aa3c028498dbe7e936d3f9d1c803e0000000041842a3f32ea3040fbbdbcbe5eae72c028498dbe7e936d3f9d1c803e0000803f41842a3fcc73834000e9833ec47ea4c028498d3e7e936dbf9d1c80be0000000000000000612833402216f9be03b774c028498d3e7e936dbf9d1c80be0000000000000000b45482402741c03e717aa3c06d7e273f917fbe3e078d28bf000000000000000032ea3040fbbdbcbe5eae72c06d7e27bf917fbebe078d283f0000000000000000cc73834000e9833ec47ea4c06d7e273f917fbe3e078d28bf0000000000000000612833402216f9be03b774c06d7e27bf917fbebe078d283f0000000000000000f66b80405779c03e8855a5c0420c8cbefa656d3f11c3823e000000001e7d2b3f29ad2e40e33abebe70f874c0420c8cbefa656d3f11c3823e0000803f1e7d2b3f8a888140c02c843e3d5fa6c0420c8c3efa656dbf11c382be000000000000000051e630407a87fabedb0b77c0420c8c3efa656dbf11c382be0000000000000000f66b80405779c03e8855a5c08555243f1d5abf3e3f652bbf000000000000000029ad2e40e33abebe70f874c0855524bf1d5abfbe3f652b3f00000000000000008a888140c02c843e3d5fa6c08555243f1d5abf3e3f652bbf000000000000000051e630407a87fabedb0b77c0855524bf1d5abfbe3f652b3f000000000000000083f77c400fa5c03e6427a7c080b78abed1386d3f1f6f853e0000000016762c3f05642c406dbfbfbe873a77c080b78abed1386d3f1f6f853e0000803f16762c3f422b7f40f063843e8736a8c080b78a3ed1386dbf1f6f85be0000000000000000c4972e408c00fcbece5879c080b78a3ed1386dbf1f6f85be000000000000000083f77c400fa5c03e6427a7c0fb26213f3e32c03e83282ebf000000000000000005642c406dbfbfbe873a77c0fb2621bf3e32c0be83282e3f0000000000000000422b7f40f063843e8736a8c0fb26213f3e32c03e83282ebf0000000000000000c4972e408c00fcbece5879c0fb2621bf3e32c0be83282e3f00000000000000009008794021c6c03ef7efa8c0c14a89beef0b6d3f7b20883e00000000296f2d3f040f2a40fb4ac1be627479c0c14a89beef0b6d3f7b20883e0000803f296f2d3f84367b406990843e9304aac0c14a893eef0b6dbf7b2088be0000000000000000f83c2c40b380fdbe9a9d7bc0c14a893eef0b6dbf7b2088be00000000000000009008794021c6c03ef7efa8c016f31d3f8e08c13e19d730bf0000000000000000040f2a40fb4ac1be627479c016f31dbf8e08c1be19d7303f000000000000000084367b406990843e9304aac016f31d3f8e08c13e19d730bf0000000000000000f83c2c40b380fdbe9a9d7bc016f31dbf8e08c1be19d7303f0000000000000000700b7540e0ddc03e39afaac08dc587be55df6c3f78d68a3e0000000057682e3f5cae27406edcc2bec6a57bc08dc587be55df6c3f78d68a3e0000803f57682e3f373377407cb3843e57c9abc08dc5873e55df6cbf78d68abe000000000000000023d62940d206ffbe03da7dc08dc5873e55df6cbf78d68abe0000000000000000700b7540e0ddc03e39afaac028ba1a3f27ddc13e587133bf00000000000000005cae27406edcc2bec6a57bc028ba1abf27ddc1be5871333f0000000000000000373377407cb3843e57c9abc028ba1a3f27ddc13e587133bf000000000000000023d62940d206ffbe03da7dc028ba1abf27ddc1be5871333f000000000000000082007140e4edc03e1c65acc09b2786bef8b26c3f9f908d3e000000009f612f3f46422540e872c4be73ce7dc09b2786bef8b26c3f9f908d3e0000803f9f612f3fb6217340c5ce843ec584adc09b27863ef8b26cbf9f908dbe00000000000000007a632740044900bfe20680c09b27863ef8b26cbf9f908dbe000000000000000082007140e4edc03e1c65acc0767c173f66b0c23e8bf735bf000000000000000046422540e872c4be73ce7dc0767c17bf66b0c2be8bf7353f0000000000000000b6217340c5ce843ec584adc0767c173f66b0c23e8bf735bf00000000000000007a632740044900bfe20680c0767c17bf66b0c2be8bf7353f00000000000000001ee86c40d4f7c03e9411aec0ad7084bec8866c3f894e903e00000000005b303f02cb2240a00dc6be29ee7fc0ad7084bec8866c3f894e903e0000803f005b303f5b026f40eee3843ecf36afc0ad70843ec8866cbf894e90be00000000000000003fe52440c31001bf501c81c0ad70843ec8866cbf894e90be00000000000000001ee86c40d4f7c03e9411aec0383a143fba82c33efc6938bf000000000000000002cb2240a00dc6be29ee7fc0383a14bfba82c3befc69383f00000000000000005b026f40eee3843ecf36afc0383a143fba82c33efc6938bf00000000000000003fe52440c31001bf501c81c0383a14bfba82c3befc69383f0000000000000000a2c26840fdfcc03e96b4afc049a082bec55a6c3f880f933e000000007754313fcc48204071abc7be560281c049a082bec55a6c3f880f933e0000803f7754313f7fd56a4045f4843e6adfb0c049a0823ec55a6cbf880f93be0000000000000000a95b224015da01bf2a2d82c049a0823ec55a6cbf880f93be0000000000000000a2c26840fdfcc03e96b4afc0adf3103f3754c43effc83abf0000000000000000cc48204071abc7be560281c0adf310bf3754c4beffc83a3f00000000000000007fd56a4045f4843e6adfb0c0adf3103f3754c43effc83abf0000000000000000a95b224015da01bf2a2d82c0adf310bf3754c4beffc83a3f0000000000000000629064401fffc03e174eb1c03fb680bed72e6c3f4ed3953e00000000044e323fe4bb1d40a74bc9bee10882c03fb680bed72e6c3f4ed3953e0000803f044e323f789b66408f01853e897eb2c03fb6803ed72e6cbf4ed395be0000000000000000fac61f409ba402bf533983c03fb6803ed72e6cbf4ed395be0000000000000000629064401fffc03e174eb1c0fba80d3f6325c53ed7143dbf0000000000000000e4bb1d40a74bc9bee10882c0fba80dbf6325c5bed7143d3f0000000000000000789b66408f01853e897eb2c0fba80d3f6325c53ed7143dbf0000000000000000fac61f409ba402bf533983c0fba80dbf6325c5bed7143d3f0000000000000000b851604087ffc03e07deb2c029647dbefa026c3f2e99983e00000000a447333f8c241b401dedcabe930a83c029647dbefa026c3f2e99983e0000803fa447333f9c5462401b0d853e1b14b4c029647d3efa026cbf2e9998be000000000000000070271d40c46f03bfa74084c029647d3efa026cbf2e9998be0000000000000000b851604087ffc03e07deb2c0565a0a3f52f6c53ed24d3fbf00000000000000008c241b401dedcabe930a83c0565a0abf52f6c5bed24d3f3f00000000000000009c5462401b0d853e1b14b4c0565a0a3f52f6c53ed24d3fbf000000000000000070271d40c46f03bfa74084c0565a0abf52f6c5bed24d3f3f0000000000000000f8065c40d7ffc03e5c64b4c0082779be1ad76b3fbe609b3e000000005741343f06831840038fccbe500784c0082779be1ad76b3fbe609b3e0000803f5741343f3f015e409018853e16a0b5c00827793e1ad76bbfbe609bbe00000000000000004d7d1a40253b04bf0a4385c00827793e1ad76bbfbe609bbe0000000000000000f8065c40d7ffc03e5c64b4c0e207073f6dc7c63e337441bf000000000000000006831840038fccbe500784c0e20707bf6dc7c6be3374413f00000000000000003f015e409018853e16a0b5c0e207073f6dc7c63e337441bf00000000000000004d7d1a40253b04bf0a4385c0e20707bf6dc7c6be3374413f000000000000000076b057405a01c13e0be1b5c026b474be32ab6b3f54299e3e00000000193b353f96d715403230cebef9fe84c026b474be32ab6b3f54299e3e0000803f193b353fb3a159403a25853e6d22b7c026b4743e32ab6bbf54299ebe0000000000000000d3c81740290605bf5b4086c026b4743e32ab6bbf54299ebe000000000000000076b057405a01c13e0be1b5c0c1b1033fc698c73e458843bf000000000000000096d715403230cebef9fe84c0c1b103bfc698c7be4588433f0000000000000000b3a159403a25853e6d22b7c0c1b1033fc698c73e458843bf0000000000000000d3c81740290605bf5b4086c0c1b103bfc698c7be4588433f0000000000000000854e5340c705c13e0154b7c0070b70be237f6b3f95f2a03e00000000e734363f7f221340edcfcfbe71f185c0070b70be237f6b3f95f2a03e0000803fe734363f49365540d834853e0d9bb8c0070b703e237f6bbf95f2a0be0000000000000000430a15406ed005bf7d3887c0070b703e237f6bbf95f2a0be0000000000000000854e5340c705c13e0154b7c00b58003fda6ac83e3d8a45bf00000000000000007f221340edcfcfbe71f185c00b5800bfda6ac8be3d8a453f000000000000000049365540d834853e0d9bb8c00b58003fda6ac83e3d8a45bf0000000000000000430a15406ed005bf7d3887c00b5800bfda6ac8be3d8a453f000000000000000077e14e406e0ec13e34bdb8c0bd2a6bbeed526b3fd7bba33e00000000c22e373f07641040126dd1be9cde86c0bd2a6bbeed526b3fd7bba33e0000803fc22e373f53bf5040ba48853ee909bac0bd2a6b3eed526bbfd7bba3be0000000000000000e3411240639906bf512b88c0bd2a6b3eed526bbfd7bba3be000000000000000077e14e406e0ec13e34bdb8c0bef5f93ec03dc93e637a47bf000000000000000007641040126dd1be9cde86c0bef5f9bec03dc9be637a473f000000000000000053bf5040ba48853ee909bac0bef5f93ec03dc93e637a47bf0000000000000000e3411240639906bf512b88c0bef5f9bec03dc9be637a473f0000000000000000a0694a40c61cc13e961cbac07f1266be76266b3f8884a63e00000000a528383f769c0d40a806d3be5ac687c07f1266be76266b3f8884a63e0000803fa528383f223d4c405d62853ef36ebbc07f12663e76266bbf8884a6be0000000000000000f86f0f40886007bfb71889c07f12663e76266bbf8884a6be0000000000000000a0694a40c61cc13e961cbac0a634f33eb711ca3eee5849bf0000000000000000769c0d40a806d3be5ac687c0a634f3beb711cabeee58493f0000000000000000223d4c405d62853ef36ebbc0a634f33eb711ca3eee5849bf0000000000000000f86f0f40886007bfb71889c0a634f3beb711cabeee58493f00000000000000004ae745407232c13e1772bbc0abc160bea5f96a3f3d4ca93e000000008e22393f12cc0a40da9bd4be93a888c0abc160bea5f96a3f3d4ca93e0000803f8e22393fffaf47406b83853e1acabcc0abc1603ea5f96abf3d4ca9be0000000000000000c7940c40702508bf96008ac0abc1603ea5f96abf3d4ca9be00000000000000004ae745407232c13e1772bbc0e06cec3e26e7ca3e0d264bbf000000000000000012cc0a40da9bd4be93a888c0e06cecbe26e7cabe0d264b3f0000000000000000ffaf47406b83853e1acabcc0e06cec3e26e7ca3e0d264bbf0000000000000000c7940c40702508bf96008ac0e06cecbe26e7cabe0d264b3f0000000000000000cd5a4140ac50c13ea7bdbcc03e375bbe75cc6a3f2f12ac3e000000007b1c3a3f25f30740762bd6be278589c03e375bbe75cc6a3f2f12ac3e0000803f7b1c3a3f3f18434020ad853e4d1bbec03e375b3e75cc6abf2f12acbe000000000000000097b0094081e708bfcde28ac03e375b3e75cc6abf2f12acbe0000000000000000cd5a4140ac50c13ea7bdbcc09e9ee53e11becb3effe14cbf000000000000000025f30740762bd6be278589c09e9ee5be11becbbeffe14c3f00000000000000003f18434020ad853e4d1bbec09e9ee53e11becb3effe14cbf000000000000000097b0094081e708bfcde28ac09e9ee5be11becbbeffe14c3f000000000000000071c43c402a79c13e39ffbdc0a27255bec39e6a3ffbd5ae3e0000000069163b3ff7110540bcb4d7beff5b8ac0a27255bec39e6a3ffbd5ae3e0000803f69163b3f2b763e4039e1853e7d62bfc0a272553ec39e6abffbd5aebe0000000000000000b1c3064057a609bf43bf8bc0a272553ec39e6abffbd5aebe000000000000000071c43c402a79c13e39ffbdc0e7c9de3ef396cc3ee88c4ebf0000000000000000f7110540bcb4d7beff5b8ac0e7c9debef396ccbee88c4e3f00000000000000002b763e4039e1853e7d62bfc0e7c9de3ef396cc3ee88c4ebf0000000000000000b1c3064057a609bf43bf8bc0e7c9debef396ccbee88c4e3f00000000000000008924384030adc13eba36bfc0cc724fbe7e706a3fd596b13e0000000056103c3fd32802407c36d9befc2c8bc0cc724fbe7e706a3fd596b13e0000803f56103c3f12ca39400021863e969fc0c0cc724f3e7e706abfd596b1be00000000000000005cce034056610abfd8958cc0cc724f3e7e706abfd596b1be00000000000000008924384030adc13eba36bfc0d9eed73ed671cd3efa2650bf0000000000000000d32802407c36d9befc2c8bc0d9eed7bed671cdbefa26503f000000000000000012ca39400021863e969fc0c0d9eed73ed671cd3efa2650bf00000000000000005cce034056610abfd8958cc0d9eed7bed671cdbefa26503f00000000000000005f7b33403eeec13e1c64c0c0fc3649be8f416a3f2854b43e00000000400a3d3ffe6ffe3fc6afdabe06f88bc0fc3649be8f416a3f2854b43e0000803f400a3d3f3d143540f96d863e8ad2c1c0fc36493e8f416abf2854b4be0000000000000000ddd0004005180bbf74668dc0fc36493e8f416abf2854b4be00000000000000005f7b33403eeec13e1c64c0c0810dd13e024fce3e59b051bf0000000000000000fe6ffe3fc6afdabe06f88bc0810dd1be024fcebe59b0513f00000000000000003d143540f96d863e8ad2c1c0810dd13e024fce3e59b051bf0000000000000000ddd0004005180bbf74668dc0810dd1be024fcebe59b0513f000000000000000043c92e40c73dc23e4c87c1c053be42bedf116a3f480db73e0000000024043e3f8e7ff83f9b1fdcbe02bd8cc053be42bedf116a3f480db73e0000803f24043e3ffb5430409fc9863e42fbc2c053be423edf116abf480db7be0000000000000000fe96fb3fe1c90bbff8308ec053be423edf116abf480db7be000000000000000043c92e40c73dc23e4c87c1c0ef25ca3eb12ecf3e2b2953bf00000000000000008e7ff83f9b1fdcbe02bd8cc0ef25cabeb12ecfbe2b29533f0000000000000000fb5430409fc9863e42fbc2c0ef25ca3eb12ecf3e2b2953bf0000000000000000fe96fb3fe1c90bbff8308ec0ef25cabeb12ecfbe2b29533f0000000000000000830e2a40609dc23e39a0c2c019083cbe4ee1693fa7c1b93e0000000000fe3e3ff280f23f1e85ddbed77b8dc019083cbe4ee1693fa7c1b93e0000803f00fe3e3f988c2b408e35873eae19c4c019083c3e4ee169bfa7c1b9be00000000000000001b7df53f78760cbf4cf58ec019083c3e4ee169bfa7c1b9be0000000000000000830e2a40609dc23e39a0c2c02638c33e3f11d03e879154bf0000000000000000f280f23f1e85ddbed77b8dc02638c3be3f11d0be8791543f0000000000000000988c2b408e35873eae19c4c02638c33e3f11d03e879154bf00000000000000001b7df53f78760cbf4cf58ec02638c3be3f11d0be8791543f0000000000000000684b25405b0ec33ed1aec3c0521335bec2af693f6d70bc3e00000000d1f73f3fb774ec3f2bdfdebe6f348ec0521335bec2af693f6d70bc3e0000803fd1f73f3f5abb26401fb3873eba2dc5c05213353ec2af69bf6d70bcbe00000000000000009b54ef3f341d0dbf58b38fc05213353ec2af69bf6d70bcbe0000000000000000684b25405b0ec33ed1aec3c03344bc3ec3f6d03e8be955bf0000000000000000b774ec3f2bdfdebe6f348ec03344bcbec3f6d0be8be9553f00000000000000005abb26401fb3873eba2dc5c03344bc3ec3f6d03e8be955bf00000000000000009b54ef3f341d0dbf58b38fc03344bcbec3f6d0be8be9553f0000000000000000448020402392c33e02b3c4c023df2dbe247d693fd618bf3e0000000097f1403f755be63fbd2ce0beaee68ec023df2dbe247d693fd618bf3e0000803f97f1403f93e12140c243883e5137c6c023df2d3e247d69bfd618bfbe0000000000000000121ee93f8fbd0dbffd6a90c023df2d3e247d69bfd618bfbe0000000000000000448020402392c33e02b3c4c0254ab53e70dfd13e513157bf0000000000000000755be63fbd2ce0beaee68ec0254ab5be70dfd1be5131573f000000000000000093e12140c243883e5137c6c0254ab53e70dfd13e513157bf0000000000000000121ee93f8fbd0dbffd6a90c0254ab5be70dfd1be5131573f00000000000000005fad1b40642ac43eb6acc5c0dd6a26be4549693f53bac13e000000004eeb413fc235e03f0c6de1be7e928fc0dd6a26be4549693f53bac13e0000803f4eeb413f88ff1c4030e9883e5e36c7c0dd6a263e454969bf53bac1be000000000000000014dae23f20570ebf261c91c0dd6a263e454969bf53bac1be00000000000000005fad1b40642ac43eb6acc5c0f849ae3eb8cbd23ede6858bf0000000000000000c235e03f0c6de1be7e928fc0f849aebeb8cbd2bede68583f000000000000000088ff1c4030e9883e5e36c7c0f849ae3eb8cbd23ede6858bf000000000000000014dae23f20570ebf261c91c0f849aebeb8cbd2bede68583f00000000000000000bd316405ed8c43eda9bc6c089b51ebe1514693fea53c43e00000000f5e4423f3704da3fe89ee2bec83790c089b51ebe1514693fea53c43e0000803ff5e4423f8a151840aca4893eca2ac8c089b51e3e151469bfea53c4be00000000000000003589dc3f4de90ebfb8c691c089b51e3e151469bfea53c4be00000000000000000bd316405ed8c43eda9bc6c0bd43a73ea3bbd33e499059bf00000000000000003704da3fe89ee2bec83790c0bd43a7bea3bbd3be4990593f00000000000000008a151840aca4893eca2ac8c0bd43a73ea3bbd33e499059bf00000000000000003589dc3f4de90ebfb8c691c0bd43a7bea3bbd3be4990593f000000000000000091f11140aa9dc53e5a80c7c06bbe16be67dd683fe8e4c63e000000008bde433f64c7d33f76c1e3be76d690c06bbe16be67dd683fe8e4c63e0000803f8bde433fe1231340dc778a3e8114c9c06bbe163e67dd68bfe8e4c6be0000000000000000032cd63fa2730fbf9d6a92c06bbe163e67dd68bfe8e4c6be000000000000000091f11140aa9dc53e5a80c7c07c37a03e90afd43e94a75abf000000000000000064c7d33f76c1e3be76d690c07c37a0be90afd4be94a75a3f0000000000000000e1231340dc778a3e8114c9c07c37a03e90afd43e94a75abf0000000000000000032cd63fa2730fbf9d6a92c07c37a0be90afd4be94a75a3f000000000000000042090d40957bc63e235ac8c0a3840ebe23a5683f4e6cc93e000000000fd8443fde7fcd3f93d3e4be716e91c0a3840ebe23a5683f4e6cc93e0000803f0fd8443fdb2a0e4012648b3e6ef3c9c0a3840e3e23a568bf4e6cc9be000000000000000010c3cf3f8bf50fbfbc0793c0a3840e3e23a568bf4e6cc9be000000000000000042090d40957bc63e235ac8c04b25993e94a7d53ecbae5bbf0000000000000000de7fcd3f93d3e4be716e91c04b2599be94a7d5becbae5b3f0000000000000000db2a0e4012648b3e6ef3c9c04b25993e94a7d53ecbae5bbf000000000000000010c3cf3f8bf50fbfbc0793c04b2599be94a7d5becbae5b3f00000000000000006a1a0840ae73c73e1d29c9c0780706be1b6b683f4be9cb3e000000007fd1453f3b2ec73f56d4e5bea3ff91c0780706be1b6b683f4be9cb3e0000803f7fd1453fc32a0940e86a8c3e76c7cac07807063e1b6b68bf4be9cbbe0000000000000000ed4ec93f8e6e10bffc9d93c07807063e1b6b68bf4be9cbbe00000000000000006a1a0840ae73c73e1d29c9c0300d923e02a4d63eeaa55cbf00000000000000003b2ec73f56d4e5bea3ff91c0300d92be02a4d6beeaa55c3f0000000000000000c32a0940e86a8c3e76c7cac0300d923e02a4d63eeaa55cbf0000000000000000ed4ec93f8e6e10bffc9d93c0300d92be02a4d6beeaa55c3f0000000000000000572503409087c83e31edc9c0948cfabd212f683f1a5bce3e00000000daca463f0ed3c03fecc2e6bef98992c0948cfabd212f683f1a5bce3e0000803fdaca463fe6230440068e8d3e8290cbc0948cfa3d212f68bf1a5bcebe00000000000000002cd0c23f3bde10bf4a2d94c0948cfa3d212f68bf1a5bcebe0000000000000000572503409087c83e31edc9c042ef8a3e3ea5d73ee28c5dbf00000000000000000ed3c03fecc2e6bef98992c042ef8abe3ea5d7bee28c5d3f0000000000000000e6230440068e8d3e8290cbc042ef8a3e3ea5d73ee28c5dbf00000000000000002cd0c23f3bde10bf4a2d94c042ef8abe3ea5d7bee28c5d3f0000000000000000b554fc3f83b8c93e4ba6cac08c80e8bd15f1673f94c0d03e0000000021c4473fe76eba3f2b9ee7be5d0d93c08c80e8bd15f1673f94c0d03e0000803f21c4473f272dfe3fbcce8e3e7a4eccc08c80e83d15f167bf94c0d0be00000000000000005947bc3ff94311bf8cb594c08c80e83d15f167bf94c0d0be0000000000000000b554fc3f83b8c93e4ba6cac09dcb833e57abd83eb7635ebf0000000000000000e76eba3f2b9ee7be5d0d93c09dcb83be57abd8beb7635e3f0000000000000000272dfe3fbcce8e3e7a4eccc09dcb833e57abd83eb7635ebf00000000000000005947bc3ff94311bf8cb594c09dcb83be57abd8beb7635e3f00000000000000008553f23f2008cb3e5254cbc0b7e9d5bdc9b0673fdd18d33e0000000053bd483f5502b43f3665e8bebc8993c0b7e9d5bdc9b0673fdd18d33e0000803f53bd483f3106f43fad2e903e4501cdc0b7e9d53dc9b067bfdd18d3be000000000000000001b5b53f549f11bfaf3695c0b7e9d53dc9b067bfdd18d3be00000000000000008553f23f2008cb3e5254cbc0be44793eabb6d93e552a5fbf00000000000000005502b43f3665e8bebc8993c0be4479beabb6d9be552a5f3f00000000000000003106f43fad2e903e4501cdc0be44793eabb6d93e552a5fbf000000000000000001b5b53f549f11bfaf3695c0be4479beabb6d9be552a5f3f0000000000000000c147e83fb977cc3e30f7cbc0b4c6c2bd146e673fc562d53e0000000070b6493fe98dad3fef16e9be04ff93c0b4c6c2bd146e673fc562d53e0000803f70b6493f8ad3e93f38af913ecaa8cdc0b4c6c23d146e67bfc562d5be0000000000000000b219af3fb8ef11bf9eb095c0b4c6c23d146e67bfc562d5be0000000000000000c147e83fb977cc3e30f7cbc060e76a3e54c7da3eb2e05fbf0000000000000000e98dad3fef16e9be04ff93c060e76abe54c7dabeb2e05f3f00000000000000008ad3e93f38af913ecaa8cdc060e76a3e54c7da3eb2e05fbf0000000000000000b219af3fb8ef11bf9eb095c060e76abe54c7dabeb2e05f3f00000000000000000a32de3f0309ce3ecb8eccc0b616afbdbd28673f739dd73e0000000079af4a3f2c12a73f97b2e9be1f6d94c0b616afbdbd28673f739dd73e0000803f79af4a3fd295df3f1f52933eec44cec0b616af3dbd2867bf739dd7be0000000000000000f475a83fbe3412bf402396c0b616af3dbd2867bf739dd7be00000000000000000a32de3f0309ce3ecb8eccc0767f5c3ecddddb3eb18660bf00000000000000002c12a73f97b2e9be1f6d94c0767f5cbecddddbbeb186603f0000000000000000d295df3f1f52933eec44cec0767f5c3ecddddb3eb18660bf0000000000000000f475a83fbe3412bf402396c0767f5cbecddddbbeb186603f00000000000000000713d43f47bdcf3e0c1bcdc09ad89abd9be0663f9cc7d93e000000006ea84b3fab8fa03f0737eabefcd394c09ad89abd9be0663f9cc7d93e0000803f6ea84b3fad4dd53fb518953e93d5cec09ad89a3d9be066bf9cc7d9be000000000000000051caa13fcc6d12bf838e96c09ad89a3d9be066bf9cc7d9be00000000000000000713d43f47bdcf3e0c1bcdc06f0d4e3e27fadc3e411c61bf0000000000000000ab8fa03f0737eabefcd394c06f0d4ebe27fadcbe411c613f0000000000000000ad4dd53fb518953e93d5cec06f0d4e3e27fadc3e411c61bf000000000000000051caa13fcc6d12bf838e96c06f0d4ebe27fadcbe411c613f00000000000000005febc93f2096d13eda9bcdc0b80b86bd7595663f38e0db3e0000000050a14c3fed069a3f64a3eabe8a3395c0b80b86bd7595663f38e0db3e0000803f50a14c3fc1fbca3fa404973ea45acfc0b80b863d759566bf38e0dbbe00000000000000004f179b3f709a12bf54f296c0b80b863d759566bf38e0dbbe00000000000000005febc93f2096d13eda9bcdc0c8913f3ec21cde3e41a161bf0000000000000000ed069a3f64a3eabe8a3395c0c8913fbec21cdebe41a1613f0000000000000000c1fbca3fa404973ea45acfc0c8913f3ec21cde3e41a161bf00000000000000004f179b3f709a12bf54f296c0c8913fbec21cdebe41a1613f0000000000000000bdbbbf3ff694d33e1c11cec0de5e61bd1c47663f09e6dd3e00000000239a4d3f7778933fa8f6eabeb68b95c0de5e61bd1c47663f09e6dd3e0000803f239a4d3fb7a0c03f6117993e02d4cfc0de5e613d1c4766bf09e6ddbe0000000000000000715d943f1eba12bf9c4e97c0de5e613d1c4766bf09e6ddbe0000000000000000bdbbbf3ff694d33e1c11cec0170d313ecf45df3e981562bf00000000000000007778933fa8f6eabeb68b95c0170d31becf45dfbe9815623f0000000000000000b7a0c03f6117993e02d4cfc0170d313ecf45df3e981562bf0000000000000000715d943f1eba12bf9c4e97c0170d31becf45dfbe9815623f0000000000000000ce84b53f51bbd53eb67acec0b78635bd55f5653fdfd7df3e00000000e6924e3fcee48c3fe52febbe6edc95c0b78635bd55f5653fdfd7df3e0000803fe6924e3f3c3db63f82529b3e8f41d0c0b786353d55f565bfdfd7dfbe00000000000000003c9d8d3f5acc12bf47a397c0b786353d55f565bfdfd7dfbe0000000000000000ce84b53f51bbd53eb67acec00080223e9b75e03e237962bf0000000000000000cee48c3fe52febbe6edc95c0008022be9b75e0be2379623f00000000000000003c3db63f82529b3e8f41d0c00080223e9b75e03e237962bf00000000000000003c9d8d3f5acc12bf47a397c0008022be9b75e0be2379623f00000000000000004147ab3fb70ad83e92d8cec0b28e08bde59f653f83b4e13e000000009f8b4f3f714c863f2f4eebbea22596c0b28e08bde59f653f83b4e13e0000803f9f8b4f3fffd1ab3f9bb79d3e34a3d0c0b28e083de59f65bf83b4e1be00000000000000002fd7863fa6d012bf44f097c0b28e083de59f65bf83b4e1be00000000000000004147ab3fb70ad83e92d8cec040eb133e73ace13ebccb62bf0000000000000000714c863f2f4eebbea22596c040eb13be73ace1bebccb623f0000000000000000ffd1ab3f9bb79d3e34a3d0c040eb133e73ace13ebccb62bf00000000000000002fd7863fa6d012bf44f097c040eb13be73ace1bebccb623f0000000000000000c803a13f9a84da3e922acfc0c1edb4bc9446653fa17ae33e000000004e84503fb45f7f3f8650ebbe426796c0c1edb4bc9446653fa17ae33e0000803f4e84503fb15fa13f2e48a03ecff8d0c0c1edb43c944665bfa17ae3be0000000000000000c30b803f79c612bf7f3598c0c1edb43c944665bfa17ae3be0000000000000000c803a13f9a84da3e922acfc0b74f053e90eae23e400d63bf0000000000000000b45f7f3f8650ebbe426796c0b74f05be90eae2be400d633f0000000000000000b15fa13f2e48a03ecff8d0c0b74f053e90eae23e400d63bf0000000000000000c30b803f79c612bf7f3598c0b74f05be90eae2be400d633f00000000000000001bbb963f802add3e9e70cfc0ccff2cbc28e9643ff328e53e00000000f97c513f0a1f723f0036ebbe3ca196c0ccff2cbc28e9643ff328e53e0000803ff97c513f0ce7963fce05a33e4542d1c0ccff2c3c28e964bff328e5be0000000000000000ec76723f59ad12bfe37298c0ccff2c3c28e964bff328e5be00000000000000001bbb963f802add3e9e70cfc0ad5ced3d4030e43e893d63bf00000000000000000a1f723f0036ebbe3ca196c0ad5cedbd4030e4be893d633f00000000000000000ce7963fce05a33e4542d1c0ad5ced3d4030e43e893d63bf0000000000000000ec76723f59ad12bfe37298c0ad5cedbd4030e4be893d633f0000000000000000f36d8c3fe2fddf3e9aaacfc06f9da23a5a87643f15bee63e00000000a375523fd0d7643f9efdeabe80d396c06f9da23a5a87643f15bee63e0000803fa375523fc9688c3f08f2a53e787fd1c06f9da2ba5a8764bf15bee6be00000000000000007dcd643fbc8412bf5ea898c06f9da2ba5a8764bf15bee6be0000000000000000f36d8c3fe2fddf3e9aaacfc05a10d03dc07de53e6c5c63bf0000000000000000d0d7643f9efdeabe80d396c05a10d0bdc07de5be6c5c633f0000000000000000c9688c3f08f2a53e787fd1c05a10d03dc07de53e6c5c63bf00000000000000007dcd643fbc8412bf5ea898c05a10d0bdc07de5be6c5c633f00000000000000000e1d823f4c00e33e6ed8cfc065175a3cee20643fbe38e83e00000000516e533fe68a573f7ea6eabefefd96c065175a3cee20643fbe38e83e0000803f516e533fa9e5813f760ea93e4eb0d1c065175abcee2064bfbe38e8be00000000000000001c1c573f2a4c12bfded598c065175abcee2064bfbe38e8be00000000000000000e1d823f4c00e33e6ed8cfc0d6bcb23d65d3e63ebe6963bf0000000000000000e68a573f7ea6eabefefd96c0d6bcb2bd65d3e6bebe69633f0000000000000000a9e5813f760ea93e4eb0d1c0d6bcb23d65d3e63ebe6963bf00000000000000001c1c573f2a4c12bfded598c0d6bcb2bd65d3e6bebe69633f00000000000000005a926f3f1033e63efdf9cfc0ff20d23caab5633f5d97e93e000000000967543f2c394a3f802feabea52097c0ff20d23caab5633f5d97e93e0000803f0967543fdcbc6e3f795cac3ea5d4d1c0ff20d2bcaab563bf5d97e9be0000000000000000ae63493f0c0312bf4dfb98c0ff20d2bcaab563bf5d97e9be00000000000000005a926f3f1033e63efdf9cfc0b464953d4831e83e606563bf00000000000000002c394a3f802feabea52097c0b46495bd4831e8be6065633f0000000000000000dcbc6e3f795cac3ea5d4d1c0b464953d4831e83e606563bf0000000000000000ae63493f0c0312bf4dfb98c0b46495bd4831e8be6065633f0000000000000000d2e05a3ffd9fe83e2717d0c04d341d3dc484633fcde0e93e00000000cf5f553fcee83c3ffd9fe8be693397c04d341d3dc484633fcde0e93e0000803fcf5f553f62a1593fd1d5ae3e65f2d1c04d341dbdc48463bfcde0e9be00000000000000005ea93b3f143511bfa70e99c04d341dbdc48463bfcde0e9be0000000000000000d2e05a3ffd9fe83e2717d0c022c06f3dfd9fe83efa8e63bf0000000000000000cee83c3ffd9fe8be693397c022c06fbdfd9fe8befa8e633f000000000000000062a1593fd1d5ae3e65f2d1c022c06f3dfd9fe83efa8e63bf00000000000000005ea93b3f143511bfa70e99c022c06fbdfd9fe8befa8e633f0000000000000000f867423f1ab1eb3e1025d0c0b157683d6f39633f700bea3e000000000d5b563f7204333fa662e7be0c3997c0b157683d6f39633f700bea3e0000803f0d5b563fd98f403f11fab13ea400d2c0b15768bd6f3963bf700beabe0000000000000000532c313fd88c10bfa01499c0b15768bd6f3963bf700beabe0000000000000000f867423f1ab1eb3e1025d0c06e38f63ce089e93e13b063bf00000000000000007204333fa662e7be0c3997c06e38f6bce089e9be13b0633f0000000000000000d98f403f11fab13ea400d2c06e38f63ce089e93e13b063bf0000000000000000532c313fd88c10bfa01499c06e38f6bce089e9be13b0633f00000000000000000f232a3ff8e4ee3e8f18d0c0b8b9993d24ea623fb1e4e93e000000006b53573f8b52293f261be6be0f3497c0b8b9993d24ea623fb1e4e93e0000803f6b53573f51b2273f1242b53ed4f3d1c0b8b999bd24ea62bfb1e4e9be0000000000000000cde1263f06df0fbf540f99c0b8b999bd24ea62bfb1e4e9be00000000000000000f232a3ff8e4ee3e8f18d0c0e484d03a0f80ea3e009263bf00000000000000008b52293f261be6be0f3497c0e484d0ba0f80eabe0092633f000000000000000051b2273f1242b53ed4f3d1c0e484d03a0f80ea3e009263bf0000000000000000cde1263f06df0fbf540f99c0e484d0ba0f80eabe0092633f0000000000000000df18123ff039f23e21f2cfc0be1abf3d3e97623f286de93e000000004049583ff5c91f3fe4c8e4bed32497c0be1abf3d3e97623f286de93e0000803f4049583f39100f3f19acb83e74ccd1c0be1abfbd3e9762bf286de9be00000000000000004fc11c3f5e2b0fbf26ff98c0be1abfbd3e9762bf286de9be0000000000000000df18123ff039f23e21f2cfc05711dbbc6a81eb3e373563bf0000000000000000f5c91f3fe4c8e4bed32497c05711db3c6a81ebbe3735633f000000000000000039100f3f19acb83e74ccd1c05711dbbc6a81eb3e373563bf00000000000000004fc11c3f5e2b0fbf26ff98c05711db3c6a81ebbe3735633f00000000000000001ea0f43e39aef53e5eb2cfc07326e43d2a41623f89a6e83e00000000e53c593fa161163f236be3be9a0b97c07326e43d2a41623f89a6e83e0000803fe53c593fb661ed3e3f36bc3e1d8bd1c07326e4bd2a4162bf89a6e8be00000000000000006dc2123f8e710ebf59e498c07326e4bd2a4162bf89a6e8be00000000000000001ea0f43e39aef53e5eb2cfc0918c60bdae8cec3e0c9b62bf0000000000000000a161163f236be3be9a0b97c0918c603dae8cecbe0c9b623f0000000000000000b661ed3e3f36bc3e1d8bd1c0918c60bdae8cec3e0c9b62bf00000000000000006dc2123f8e710ebf59e498c0918c603dae8cecbe0c9b623f00000000000000000a9ec53ee43ff93ef659cfc0db5a043e55e8613f8c93e73e00000000ad2e5a3fca100d3ffe00e2be8ee896c0db5a043e55e8613f8c93e73e0000803fad2e5a3f7a36bd3e7adebf3e8630d1c0db5a04be55e861bf8c93e7be000000000000000002dd083f34b10dbf1ebf98c0db5a04be55e861bf8c93e7be00000000000000000a9ec53ee43ff93ef659cfc01607a9bd71a0ed3e9ec561bf0000000000000000ca100d3ffe00e2be8ee896c01607a93d71a0edbe9ec5613f00000000000000007a36bd3e7adebf3e8630d1c01607a9bd71a0ed3e9ec561bf000000000000000002dd083f34b10dbf1ebf98c01607a93d71a0edbe9ec5613f00000000000000007b37973ef3ecfc3ebae9cec0c951163e418d613ff537e63e00000000e51e5b3f12cf033f8189e0bec6bb96c0c951163e418d613ff537e63e0000803fe51e5b3fe3ab8d3eaca2c33e88bdd0c0c95116be418d61bff537e6be00000000000000008c12fe3ee4e90cbf948f98c0c95116be418d61bff537e6be00000000000000007b37973ef3ecfc3ebae9cec050cde0bd3abbee3ed3b760bf000000000000000012cf033f8189e0bec6bb96c050cde03d3abbeebed3b7603f0000000000000000e3ab8d3eaca2c33e88bdd0c050cde0bd3abbee3ed3b760bf00000000000000008c12fe3ee4e90cbf948f98c050cde03d3abbeebed3b7603f0000000000000000aeee523ea859003f8662cec0e9e6273e6a30613f5498e43e00000000dc0d5c3f4929f53ea403dfbe388596c0e9e6273e6a30613f5498e43e0000803fdc0d5c3fda9b3d3e9e80c73e0733d0c0e9e627be6a3061bf5498e4be0000000000000000df7fea3e2b1b0cbfb95598c0e9e627be6a3061bf5498e4be0000000000000000aeee523ea859003f8662cec0f2b10bbe7adbef3e3b755fbf00000000000000004929f53ea403dfbe388596c0f2b10b3e7adbefbe3b755f3f0000000000000000da9b3d3e9e80c73e0733d0c0f2b10bbe7adbef3e3b755fbf0000000000000000df7fea3e2b1b0cbfb95598c0f2b10b3e7adbefbe3b755f3f0000000000000000c89cf13d6f48023f4ec5cdc0c90a393e51d2603ffdb9e23e00000000d8fb5c3fa0b4e23e556eddbed04496c0c90a393e51d2603ffdb9e23e0000803fd8fb5c3f9a9cc23d1276cb3e0392cfc0c90a39be51d260bffdb9e2be000000000000000094f4d63e90440bbf851198c0c90a39be51d260bffdb9e2be0000000000000000c89cf13d6f48023f4ec5cdc06e4d26be9afff03ef6015ebf0000000000000000a0b4e23e556eddbed04496c06e4d263e9afff0bef6015e3f00000000000000009a9cc23d1276cb3e0392cfc06e4d26be9afff03ef6015ebf000000000000000094f4d63e90440bbf851198c06e4d263e9afff0bef6015e3f0000000000000000d879003dc741043f1113cdc0daaf493e7b73603fdea2e03e000000001de95d3f4b33d03e8dc8dbbe6bfa95c0daaf493e7b73603fdea2e03e0000803f1de95d3fe826d03bd880cf3e87dbcec0daaf49be7b7360bfdea2e0be0000000000000000ac64c33ea1650abfe1c297c0daaf49be7b7360bfdea2e0be0000000000000000d879003dc741043f1113cdc0102440be0d26f23e9b625cbf00000000000000004b33d03e8dc8dbbe6bfa95c01024403e0d26f2be9b625c3f0000000000000000e826d03bd880cf3e87dbcec0102440be0d26f23e9b625cbf0000000000000000ac64c33ea1650abfe1c297c01024403e0d26f2be9b625c3f0000000000000000c04f5cbda144063fdd4cccc083ca593e6614603f4459de3e00000000e9d55e3fe299bd3e3d11dabed7a595c083ca593e6614603f4459de3e0000803fe9d55e3f8679a5bdb39ed33ead10cec083ca59be661460bf4459debe000000000000000079c5af3ee67d09bfa76997c083ca59be661460bf4459debe0000000000000000c04f5cbda144063fdd4cccc0da2359be3f4df33e159c5abf0000000000000000e299bd3e3d11dabed7a595c0da23593e3f4df3be159c5a3f00000000000000008679a5bdb39ed33ead10cec0da2359be3f4df33e159c5abf000000000000000079c5af3ee67d09bfa76997c0da23593e3f4df3be159c5a3f0000000000000000b7be0cbeff4f083fc373cbc03b51693e8db55f3fd1e3db3e0000000077c25f3f9edeaa3e7447d8bee14695c03b51693e8db55f3fd1e3db3e0000803f77c25f3f54602abe86cdd73e9432cdc03b5169be8db55fbfd1e3dbbe0000000000000000d00d9c3ef68c08bfb20597c03b5169be8db55fbfd1e3dbbe0000000000000000b7be0cbeff4f083fc373cbc0f93d71beb973f43e86b358bf00000000000000009edeaa3e7447d8bee14695c0f93d713eb973f4be86b3583f000000000000000054602abe86cdd73e9432cdc0f93d71beb973f43e86b358bf0000000000000000d00d9c3ef68c08bfb20597c0f93d713eb973f4be86b3583f0000000000000000ffda60beed620a3fd988cac0813c783e62575f3f4349d93e00000000fdae603f47f9973e546ad6be4fdd94c0813c783e62575f3f4349d93e0000803ffdae603fd43080be4e0bdc3e6042ccc0813c78be62575fbf4349d9be0000000000000000f235883e709207bfd69696c0813c78be62575fbf4349d9be0000000000000000ffda60beed620a3fd988cac0633384be1798f53e27ae56bf000000000000000047f9973e546ad6be4fdd94c06333843e1798f5be27ae563f0000000000000000d43080be4e0bdc3e6042ccc0633384be1798f53e27ae56bf0000000000000000f235883e709207bfd69696c06333843e1798f5be27ae563f000000000000000079b299be7f7c0c3f338dc9c06343833e51fa5e3f4d90d63e00000000ab9b613f31e3843e1079d4bee76894c06343833e51fa5e3f4d90d63e0000803fab9b613f1a5eaabe1556e03e3141cbc0634383be51fa5ebf4d90d6be00000000000000001f6f683efc8d06bfe51c96c0634383be51fa5ebf4d90d6be000000000000000079b299be7f7c0c3f338dc9c0d54a8fbe07b9f63e329154bf000000000000000031e3843e1079d4bee76894c0d54a8f3e07b9f6be3291543f00000000000000001a5eaabe1556e03e3141cbc0d54a8fbe07b9f63e329154bf00000000000000001f6f683efc8d06bfe51c96c0d54a8f3e07b9f6be3291543f0000000000000000ce2dc2beda9b0e3fe681c8c02e168a3ebd9e5e3f8abfd33e00000000ae88623f372e633eff72d2be76e993c02e168a3ebd9e5e3f8abfd33e0000803fae88623f47b7d3be0eace43e2c30cac02e168abebd9e5ebf8abfd3be0000000000000000451b403e527f05bfbc9795c02e168abebd9e5ebf8abfd3be0000000000000000ce2dc2beda9b0e3fe681c8c075e299be59d5f73ec36152bf0000000000000000372e633eff72d2be76e993c075e2993e59d5f7bec361523f000000000000000047b7d3be0eace43e2c30cac075e299be59d5f73ec36152bf0000000000000000451b403e527f05bfbc9795c075e2993e59d5f7bec361523f000000000000000054e0e9be32c0103ffb67c7c0a395903efd445e3f54ddd03e000000002f76633f38223c3e9457d0bec95e93c0a395903efd445e3f54ddd03e0000803f2f76633f0f3dfcbe8a0be93e6510c9c0a39590befd445ebf54ddd0be0000000000000000c168173e376604bf330795c0a39590befd445ebf54ddd0be000000000000000054e0e9be32c0103ffb67c7c0b8f8a3befcebf83ecb2450bf000000000000000038223c3e9457d0bec95e93c0b8f8a33efcebf8becb24503f00000000000000000f3dfcbe8a0be93e6510c9c0b8f8a3befcebf83ecb2450bf0000000000000000c168173e376604bf330795c0b8f8a33efcebf8becb24503f00000000000000000b6608bfd0e8123f7a40c6c08cc1963e5fed5d3fbfefcd3e000000005064643ff49c143e6b26cebebac892c08cc1963e5fed5d3fbfefcd3e0000803f5064643fbcf811bf0773ed3ef1e2c7c08cc196be5fed5dbfbfefcdbe000000000000000062a4dc3d824203bf316b94c08cc196be5fed5dbfbfefcdbe00000000000000000b6608bfd0e8123f7a40c6c0488dadbe05fcf93efcde4dbf0000000000000000f49c143e6b26cebebac892c0488dad3e05fcf9befcde4d3f0000000000000000bcf811bf0773ed3ef1e2c7c0488dadbe05fcf93efcde4dbf000000000000000062a4dc3d824203bf316b94c0488dad3e05fcf9befcde4d3f0000000000000000107a1bbf0415153f5a0cc5c0559a9c3e24985d3f72fcca3e000000003153653ff035d93d31dfcbbe2a2792c0559a9c3e24985d3f72fcca3e0000803f3153653fcc6b25bf15e1f13ed2a8c6c0559a9cbe24985dbf72fccabe00000000000000000ca8893d121402bfa2c393c0559a9cbe24985dbf72fccabe0000000000000000107a1bbf0415153f5a0cc5c0cea0b6be9c04fb3ebc944bbf0000000000000000f035d93d31dfcbbe2a2792c0cea0b63e9c04fbbebc944b3f0000000000000000cc6b25bf15e1f13ed2a8c6c0cea0b6be9c04fb3ebc944bbf00000000000000000ca8893d121402bfa2c393c0cea0b63e9c04fbbebc944b3f0000000000000000332e2ebf3144173f8eccc3c00221a23e8c455d3fb208c83e00000000ed42663f6035883db881c9be047a91c00221a23e8c455d3fb208c83e0000803fed42663fc47938bf6a54f63e0663c5c00221a2be8c455dbfb208c8be00000000000000005263d73cd8da00bf7c1093c00221a2be8c455dbfb208c8be0000000000000000332e2ebf3144173f8eccc3c0df34bfbe0d05fc3e274a49bf00000000000000006035883db881c9be047a91c0df34bf3e0d05fcbe274a493f0000000000000000c47938bf6a54f63e0663c5c0df34bfbe0d05fc3e274a49bf00000000000000005263d73cd8da00bf7c1093c0df34bf3e0d05fcbe274a493f0000000000000000d08440bfce75193ffe81c2c01557a73ec2f55c3f5619c53e000000009a33673fc0dfd83cf50dc7be40c190c01557a73ec2f55c3f5619c53e0000803f9a33673f18254bbfe8cbfa3e7f12c4c01557a7bec2f55cbf5619c5be0000000000000000885276bca92dffbec15192c01557a7bec2f55cbf5619c5be0000000000000000d08440bfce75193ffe81c2c0ce4bc7bec8fcfc3ef70247bf0000000000000000c0dfd83cf50dc7be40c190c0ce4bc73ec8fcfcbef702473f000000000000000018254bbfe8cbfa3e7f12c4c0ce4bc7bec8fcfc3ef70247bf0000000000000000885276bca92dffbec15192c0ce4bc73ec8fcfcbef702473f00000000000000008a8052bf55a91b3f822dc1c0683eac3eefa85c3fbc32c23e000000004d25683f800266bcee83c4bedefc8fc0683eac3eefa85c3fbc32c23e0000803f4d25683f89705dbf7a46ff3e1eb8c2c0683eacbeefa85cbfbc32c2be00000000000000008b8068bd1e90fcbe7a8791c0683eacbeefa85cbfbc32c2be00000000000000008a8052bf55a91b3f822dc1c080e8cebe4cebfd3e93c244bf0000000000000000800266bcee83c4bedefc8fc080e8ce3e4cebfdbe93c2443f000000000000000089705dbf7a46ff3e1eb8c2c080e8cebe4cebfd3e93c244bf00000000000000008b8068bd1e90fcbe7a8791c080e8ce3e4cebfdbe93c2443f0000000000000000422464bf56de1d3feccfbfc02cd9b03e345f5c3fd258bf3e000000001318693fa05e61bdc7e3c1beea2c8fc02cd9b03e345f5c3fd258bf3e0000803f1318693f1b5f6fbf9be1013fbd54c1c02cd9b0be345f5cbfd258bfbe00000000000000001686cabd3dddf9bebbb190c02cd9b0be345f5cbfd258bfbe0000000000000000422464bf56de1d3feccfbfc0580ed6be39d0fe3e098c42bf0000000000000000a05e61bdc7e3c1beea2c8fc0580ed63e39d0febe098c423f00000000000000001b5f6fbf9be1013fbd54c1c0580ed6be39d0fe3e098c42bf00000000000000001686cabd3dddf9bebbb190c0580ed63e39d0febe098c423f0000000000000000047375bf6914203f016abec0c129b53ea4185c3f1d8fbc3e00000000fa0b6a3fe48fc5bdc02dbfbe7d518ec0c129b53ea4185c3f1d8fbc3e0000803ffa0b6a3f007a80bfa420043f28e9bfc0c129b5bea4185cbf1d8fbcbe0000000000000000e5cb10be4a15f7bea4d08fc0c129b5bea4185cbf1d8fbcbe0000000000000000047375bf6914203f016abec007c1dcbe49abff3e116240bf0000000000000000e48fc5bdc02dbfbe7d518ec007c1dc3e49abffbe1162403f0000000000000000007a80bfa420043f28e9bfc007c1dcbe49abff3e116240bf0000000000000000e5cb10be4a15f7bea4d08fc007c1dc3e49abffbe1162403f0000000000000000fc3783bf2a4b223f7bfcbcc0b132b93e56d55b3fa8d8b93e000000000a016b3fccad0dbe1d62bcbeb76a8dc0b132b93e56d55b3fa8d8b93e0000803f0a016b3f461989bff15f063f1f76bec0b132b9be56d55bbfa8d8b9be00000000000000001db83cbe8e38f4be5be48ec0b132b9be56d55bbfa8d8b9be0000000000000000fc3783bf2a4b223f7bfcbcc08604e3be1c3e003f12473ebf0000000000000000ccad0dbe1d62bcbeb76a8dc08604e33e1c3e00bf12473e3f0000000000000000461989bff15f063f1f76bec08604e3be1c3e003f12473ebf00000000000000001db83cbe8e38f4be5be48ec08604e33e1c3e00bf12473e3f0000000000000000308f8bbf4a82243f0b88bbc0a7f6bc3e56955b3f3538b73e0000000049f76b3fd00539be4981b9bec3788cc0a7f6bc3e56955b3f3538b73e0000803f49f76b3f158f91bf329f083f58fcbcc0a7f6bcbe56955bbf3538b7be0000000000000000fa0469be7947f1be10ed8dc0a7f6bcbe56955bbf3538b7be0000000000000000308f8bbf4a82243f0b88bbc0ecdce8be77a1003f223d3cbf0000000000000000d00539be4981b9bec3788cc0ecdce83e77a100bf223d3c3f0000000000000000158f91bf329f083f58fcbcc0ecdce8be77a1003f223d3cbf0000000000000000fa0469be7947f1be10ed8dc0ecdce83e77a100bf223d3c3f0000000000000000bfc093bf79b9263f5c0dbac05078c03ea8585b3f12b0b43e00000000b8ee6c3f76cc64beb28bb6bed87b8bc05078c03ea8585b3f12b0b43e0000803fb8ee6c3f24dd99bf16de0a3f847cbbc05078c0bea8585bbf12b0b4be0000000000000000d0d78abe7842eebe00eb8cc05078c0bea8585bbf12b0b4be0000000000000000bfc093bf79b9263f5c0dbac0614eeebea9ff003f10463abf000000000000000076cc64beb28bb6bed87b8bc0614eee3ea9ff00bf10463a3f000000000000000024dd99bf16de0a3f847cbbc0614eeebea9ff003f10463abf0000000000000000d0d78abe7842eebe00eb8cc0614eee3ea9ff00bf10463a3f00000000000000004cce9bbf6df0283f0b8db8c04ebac33e521f5b3f4042b23e0000000058e76d3f197f88beca81b3be2f748ac04ebac33e521f5b3f4042b23e0000803f58e76d3f2c05a2bf521c0d3f44f7b9c04ebac3be521f5bbf4042b2be0000000000000000995aa1be002aebbe68de8bc04ebac3be521f5bbf4042b2be00000000000000004cce9bbf6df0283f0b8db8c00b5df3bea958013f6e6338bf0000000000000000197f88beca81b3be2f748ac00b5df33ea95801bf6e63383f00000000000000002c05a2bf521c0d3f44f7b9c00b5df3bea958013f6e6338bf0000000000000000995aa1be002aebbe68de8bc00b5df33ea95801bf6e63383f000000000000000072b9a3bfe6262b3fb207b7c04abfc63e53e95a3f7df0af3e0000000027e16e3fd6cb9ebe2464b0be0e6289c04abfc63e53e95a3f7df0af3e0000803f27e16e3fdd08aabfa6590f3f346db8c04abfc6be53e95abf7df0afbe00000000000000008209b8bea3fee7be90c78ac04abfc6be53e95abf7df0afbe000000000000000072b9a3bfe6262b3fb207b7c0f80cf8be7cac013f909636bf0000000000000000d6cb9ebe2464b0be0e6289c0f80cf83e7cac01bf9096363f0000000000000000dd08aabfa6590f3f346db8c0f80cf8be7cac013f909636bf00000000000000008209b8bea3fee7be90c78ac0f80cf83e7cac01bf9096363f0000000000000000ca83abbfa55c2d3fe27db5c0d189c93ea8b65a3f32bcad3e000000001fdc6f3fd24ab5be4633adbebe4588c0d189c93ea8b65a3f32bcad3e0000803f1fdc6f3fe5e9b1bfd595113feadeb6c0d189c9bea8b65abf32bcadbe00000000000000003ce3cebee6c0e4bec6a689c0d189c9bea8b65abf32bcadbe0000000000000000ca83abbfa55c2d3fe27db5c02a62fcbe24fb013f94e034bf0000000000000000d24ab5be4633adbebe4588c02a62fc3e24fb01bf94e0343f0000000000000000e5e9b1bfd595113feadeb6c02a62fcbe24fb013f94e034bf00000000000000003ce3cebee6c0e4bec6a689c02a62fc3e24fb01bf94e0343f0000000000000000df2eb3bf6c912f3f2af0b3c0551ccc3e4c875a3f95a6ab3e0000000039d8703fa0facbbec3efa9be901f87c0551ccc3e4c875a3f95a6ab3e0000803f39d8703fe2a9b9bfa0d0133ff54cb5c0551cccbe4c875abf95a6abbe0000000000000000ace6e5be5c71e1be5b7c88c0551cccbe4c875abf95a6abbe0000000000000000df2eb3bf6c912f3f2af0b3c0373000bfa744023f6a4233bf0000000000000000a0facbbec3efa9be901f87c03730003fa74402bf6a42333f0000000000000000e2a9b9bfa0d0133ff54cb5c0373000bfa744023f6a4233bf0000000000000000ace6e5be5c71e1be5b7c88c03730003fa74402bf6a42333f000000000000000034bcbabf0ac5313f0f5fb2c03a79ce3e395b5a3fafb0a93e000000006ed5713ffed9e2be449aa6bed9ef85c03a79ce3e395b5a3fafb0a93e0000803f6ed5713f6c4ac1bfd609163fdeb7b3c03a79cebe395b5abfafb0a9be0000000000000000dc12fdbeab10debea84887c03a79cebe395b5abfafb0a9be000000000000000034bcbabf0ac5313f0f5fb2c0b50502bf1689023fdabc31bf0000000000000000fed9e2be449aa6bed9ef85c0b505023f168902bfdabc313f00000000000000006c4ac1bfd609163fdeb7b3c0b50502bf1689023fdabc31bf0000000000000000dc12fdbeab10debea84887c0b505023f168902bfdabc313f0000000000000000402dc2bf42f7333f13cbb0c0aca2d03e65325a3f49dba73e00000000b0d3723fdae7f9be5b33a3bef1b684c0aca2d03e65325a3f49dba73e0000803fb0d3723f0acdc8bf3e41183f2920b2c0aca2d0be65325abf49dba7be000000000000000081330abf639fdabe070c86c0aca2d0be65325abf49dba7be0000000000000000402dc2bf42f7333f13cbb0c049b303bf78c8023f855030bf0000000000000000dae7f9be5b33a3bef1b684c049b3033f78c802bf8550303f00000000000000000acdc8bf3e41183f2920b2c049b303bf78c8023f855030bf000000000000000081330abf639fdabe070c86c049b3033f78c802bf8550303f00000000000000006483c9bfe227363fb734afc0c39ad23ec40c5a3f1227a63e00000000f4d2733fb19108bfacbb9fbe3b7583c0c39ad23ec40c5a3f1227a63e0000803ff4d2733f2f33d0bfa5761a3f5686b0c0c39ad2bec40c5abf1227a6be000000000000000047f115bf261ed7bedac684c0c39ad2bec40c5abf1227a6be00000000000000006483c9bfe227363fb734afc08b3a05bfdc02033ff1fd2ebf0000000000000000b19108bfacbb9fbe3b7583c08b3a053fdc0203bff1fd2e3f00000000000000002f33d0bfa5761a3f5686b0c08b3a05bfdc02033ff1fd2ebf000000000000000047f115bf261ed7bedac684c08b3a053fdc0203bff1fd2e3f0000000000000000fdbfd0bfb456383f759cadc07263d43e56ea593f9294a43e000000002dd3743ff64514bfdc339cbe132b82c07263d43e56ea593f9294a43e0000803f2dd3743f487ed7bfd7a91c3fe2eaaec07263d4be56ea59bf9294a4be00000000000000008cc221bf978dd3be807983c07263d4be56ea59bf9294a4be0000000000000000fdbfd0bfb456383f759cadc0029d06bf5138033f87c52dbf0000000000000000f64514bfdc339cbe132b82c0029d063f513803bf87c52d3f0000000000000000487ed7bfd7a91c3fe2eaaec0029d06bf5138033f87c52dbf00000000000000008cc221bf978dd3be807983c0029d063f513803bf87c52d3f000000000000000054e4d7bf85833a3fc602acc07dfed53e0ccb593f3424a33e0000000049d4753f901020bf8a9c98beded880c07dfed53e0ccb593f3424a33e0000803f49d4753facafdebfa1da1e3f474eadc07dfed5be0ccb59bf3424a3be000000000000000040a72dbf52eecfbe5f2482c07dfed5be0ccb59bf3424a3be000000000000000054e4d7bf85833a3fc602acc00cdc07bfe568033f9ea72cbf0000000000000000901020bf8a9c98beded880c00cdc073fe56803bf9ea72c3f0000000000000000acafdebfa1da1e3f474eadc00cdc07bfe568033f9ea72cbf000000000000000040a72dbf52eecfbe5f2482c00cdc073fe56803bf9ea72c3f0000000000000000a0f1debf1eae3c3f2068aac0836dd73edbae593f4dd6a13e0000000037d6763f6bf12bbf5bf694be08fe7ec0836dd73edbae593f4dd6a13e0000803f37d6763f9fc8e5bfce08213ffab0abc0836dd7bedbae59bf4dd6a1be00000000000000006a9f39bffa40ccbedec780c0836dd7bedbae59bf4dd6a1be0000000000000000a0f1debf1eae3c3f2068aac0ebf808bfa694033f72a42bbf00000000000000006bf12bbf5bf694be08fe7ec0ebf8083fa69403bf72a42b3f00000000000000009fc8e5bfce08213ffab0abc0ebf808bfa694033f72a42bbf00000000000000006a9f39bffa40ccbedec780c0ebf8083fa69403bf72a42b3f00000000000000000fe9e5bf4fd63e3ff9cca8c000b2d83ebd95593f1eaba03e00000000e6d8773f92e837bff24191bed63b7cc000b2d83ebd95593f1eaba03e0000803fe6d8773f5ccaecbf3034233f7313aac000b2d8bebd9559bf1eaba0be00000000000000002bab45bf3086c8becbc87ec000b2d8bebd9559bf1eaba0be00000000000000000fe9e5bf4fd63e3ff9cca8c0c6f409bfa4bb033f3abc2abf000000000000000092e837bff24191bed63b7cc0c6f4093fa4bb03bf3abc2a3f00000000000000005ccaecbf3034233f7313aac0c6f409bfa4bb033f3abc2abf00000000000000002bab45bf3086c8becbc87ec0c6f4093fa4bb03bf3abc2a3f0000000000000000ac9af3bf941e433fef96a5c06ac0da3e996c593f9abd9e3e000000003be0793fa01a50bfe1b089be4a8f76c06ac0da3e996c593f9abd9e3e0000803f3be0793faf8cfabfaf81273f7fd9a6c06ac0dabe996c59bf9abd9ebe0000000000000000a7fe5dbfaceac0be691479c06ac0dabe996c59bf9abd9ebe0000000000000000ac9af3bf941e433fef96a5c05c8d0bbf82fb033f2a3d29bf0000000000000000a01a50bfe1b089be4a8f76c05c8d0b3f82fb03bf2a3d293f0000000000000000af8cfabfaf81273f7fd9a6c05c8d0bbf82fb033f2a3d29bf0000000000000000a7fe5dbfaceac0be691479c05c8d0b3f82fb03bf2a3d293f000000000000000058cd03c07073493f28cda0c09bb2dc3e3245593f07e19c3e00000000ebee7c3f4e1575bfa6f77bbeafb46dc09bb2dc3e3245593f07e19c3e0000803febee7c3f424e07c08cdb2d3fef0ba2c09bb2dcbe324559bf07e19cbe00000000000000007c8c81bf9c2bb5be3d3270c09bb2dcbe324559bf07e19cbe000000000000000058cd03c07073493f28cda0c00a100dbfad38043f44cb27bf00000000000000004e1575bfa6f77bbeafb46dc00a100d3fad3804bf44cb273f0000000000000000424e07c08cdb2d3fef0ba2c00a100dbfad38043f44cb27bf00000000000000007c8c81bf9c2bb5be3d3270c00a100d3fad3804bf44cb273f00000000000000007c840dc07ca64f3f8f169cc09e56dd3e1e38593f0e429c3e000000000000803fa0798dbfd0cd63be2e8564c09e56dd3e1e38593f0e429c3e0000803f0000803f010811c04110343f13549dc09e56ddbe1e3859bf0e429cbe0000000000000000aa8094bf5e13a9be360067c09e56ddbe1e3859bf0e429cbe00000000000000007c840dc07ca64f3f8f169cc0588f0dbff84c043fe04f27bf0000000000000000a0798dbfd0cd63be2e8564c0588f0d3ff84c04bfe04f273f0000000000000000010811c04110343f13549dc0588f0dbff84c043fe04f27bf0000000000000000aa8094bf5e13a9be360067c0588f0d3ff84c04bfe04f273f0000000000000000 + m_CompressedMesh: + m_Vertices: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_UV: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Normals: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Tangents: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_Weights: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_NormalSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_TangentSigns: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_FloatColors: + m_NumItems: 0 + m_Range: 0 + m_Start: 0 + m_Data: + m_BitSize: 0 + m_BoneIndices: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_Triangles: + m_NumItems: 0 + m_Data: + m_BitSize: 0 + m_UVInfo: 0 + m_LocalAABB: + m_Center: {x: 1.6072979, y: 0.6023864, z: -2.1961308} + m_Extent: {x: 3.9050364, y: 1.1758826, z: 4.3664474} + m_MeshUsageFlags: 0 + m_BakedConvexCollisionMesh: + m_BakedTriangleCollisionMesh: + m_MeshOptimized: 0 +--- !u!1 &1073702040 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1073702042} + - component: {fileID: 1073702041} + - component: {fileID: 1073702043} + m_Layer: 0 + m_Name: Road Creator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1073702041 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8e5ac92bc18f545cc84cd886ece82b4d, type: 3} + m_Name: + m_EditorClassIdentifier: + editorData: + _bezierPath: + points: + - {x: -1.9826441, y: 1.37363, z: 0.00679636} + - {x: 0.03144753, y: 0.73501456, z: 0.9092513} + - {x: 2.0455391, y: 0.0963991, z: 1.8117062} + - {x: 3.6016905, y: -0.21516085, z: 0.52946377} + - {x: 4.667126, y: -0.42847365, z: -0.348437} + - {x: 4.716327, y: -0.044535846, z: -2.1495001} + - {x: 4.136929, y: -0.000024080276, z: -3.419808} + - {x: 3.5567427, y: 0.044548243, z: -4.691844} + - {x: 2.1918426, y: -0.08512249, z: -5.565551} + - {x: 0.79646015, y: 0, z: -5.613926} + - {x: -0.1937465, y: 0.060405552, z: -5.6482544} + - {x: -0.92599463, y: 0.1773701, z: -4.936223} + - {x: -1.6582427, y: 0.29433465, z: -4.224191} + isClosed: 0 + localPosition: {x: 0, y: 0, z: 0} + space: 0 + controlMode: 3 + autoControlLength: 0.35 + boundsUpToDate: 1 + pivot: {x: 1.2616136, y: 0.54719853, z: -2.224142} + bounds: + m_Center: {x: 1.2616136, y: 0.54719853, z: -2.224142} + m_Extent: {x: 3.2442577, y: 0.8264315, z: 3.3909774} + rotation: {x: 0, y: 0.0023407342, z: 0, w: 0.9999979} + scale: {x: 1, y: 1, z: 1} + perAnchorNormalsAngle: + - 335.08722 + - 345.02103 + - 327.71606 + - 320.15445 + - 320.15445 + globalNormalsAngle: 0 + flipNormals: 0 + vertexPathUpToDate: 1 + vertexPathMaxAngleError: 0.3 + vertexPathMinVertexSpacing: 0 + pathTransformationEnabled: 0 + showPathBounds: 0 + showPerSegmentBounds: 0 + displayAnchorPoints: 1 + displayControlPoints: 1 + bezierHandleScale: 1 + globalDisplaySettingsFoldout: 1 + keepConstantHandleSize: 1 + vertexHandleSize: 0.2 + showNormalsInVertexMode: 0 + showDisplayOptions: 0 + showPathOptions: 1 + showVertexPathDisplayOptions: 0 + showVertexPathOptions: 1 + showNormals: 1 + showNormalsHelpInfo: 0 + tabIndex: 0 +--- !u!4 &1073702042 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1964068607} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1073702043 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1073702040} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b275c3b78f8717c4cbc60f74a3247f67, type: 3} + m_Name: + m_EditorClassIdentifier: + pathCreator: {fileID: 1073702041} + autoUpdate: 1 + roadWidth: 1 + thickness: 0.127 + flattenSurface: 0 + roadMaterial: {fileID: 2100000, guid: b5df4735192845740bd11076dc9cdc99, type: 2} + undersideMaterial: {fileID: 2100000, guid: c1d8d882244c5344fa86a8fd23246f81, type: 2} + textureTiling: 8 +--- !u!1 &1777349640 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1777349643} + - component: {fileID: 1777349642} + - component: {fileID: 1777349641} + 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 &1777349641 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_Enabled: 1 +--- !u!20 &1777349642 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + 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_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 + m_StereoMirrorMode: 0 +--- !u!4 &1777349643 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1777349640} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 12.48, y: -0.52, z: -31.62} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1964068606 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1964068607} + - component: {fileID: 1964068609} + - component: {fileID: 1964068608} + m_Layer: 0 + m_Name: Mesh Holder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1964068607 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1964068606} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1073702042} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1964068608 +MeshRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1964068606} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_Materials: + - {fileID: 2100000, guid: b5df4735192845740bd11076dc9cdc99, type: 2} + - {fileID: 2100000, guid: c1d8d882244c5344fa86a8fd23246f81, type: 2} + - {fileID: 2100000, guid: c1d8d882244c5344fa86a8fd23246f81, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + 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 +--- !u!33 &1964068609 +MeshFilter: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1964068606} + m_Mesh: {fileID: 732243162} +--- !u!1 &2092520649 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 2092520651} + - component: {fileID: 2092520650} + 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 &2092520650 +Light: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2092520649} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &2092520651 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2092520649} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 12.48, y: 1.48, z: -21.62} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} diff --git a/PathCreator/Examples/Scenes/Road.unity.meta b/PathCreator/Examples/Scenes/Road.unity.meta new file mode 100644 index 0000000..e3e4cc7 --- /dev/null +++ b/PathCreator/Examples/Scenes/Road.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8f020cfa1fb7ded47926b050b82adbe6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts.meta b/PathCreator/Examples/Scripts.meta new file mode 100644 index 0000000..3ad705f --- /dev/null +++ b/PathCreator/Examples/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4708a8b1933bb914e98024a2cd57b3a8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/Editor.meta b/PathCreator/Examples/Scripts/Editor.meta new file mode 100644 index 0000000..c765fef --- /dev/null +++ b/PathCreator/Examples/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 37afd3c65f48fa94bbb5a65473466658 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs b/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs new file mode 100644 index 0000000..4ca318a --- /dev/null +++ b/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs @@ -0,0 +1,101 @@ +using UnityEngine; +using UnityEditor; +using PathCreation; + +namespace PathCreation.Examples +{ + [CustomEditor(typeof(PathSceneTool), true)] + public class PathSceneToolEditor : Editor + { + protected PathSceneTool pathTool; + bool isSubscribed; + + public override void OnInspectorGUI() + { + using (var check = new EditorGUI.ChangeCheckScope()) + { + DrawDefaultInspector(); + + if (check.changed) + { + if (!isSubscribed) + { + TryFindPathCreator(); + Subscribe(); + } + + if (pathTool.autoUpdate) + { + pathTool.CreatePath(); + + } + } + } + + if (GUILayout.Button("Manual Update")) + { + if (TryFindPathCreator()) + { + pathTool.CreatePath(); + SceneView.RepaintAll(); + } + } + + } + + + protected virtual void OnPathModified() + { + if (pathTool.autoUpdate) + { + pathTool.CreatePath(); + } + } + + protected virtual void OnEnable() + { + pathTool = (PathSceneTool)target; + pathTool.onDestroyed += OnToolDestroyed; + + if (TryFindPathCreator()) + { + Subscribe(); + pathTool.CreatePath(); + } + } + + void OnToolDestroyed() { + if (pathTool != null) { + pathTool.pathCreator.pathUpdated -= OnPathModified; + } + } + + + protected virtual void Subscribe() + { + if (pathTool.pathCreator != null) + { + isSubscribed = true; + pathTool.pathCreator.pathUpdated -= OnPathModified; + pathTool.pathCreator.pathUpdated += OnPathModified; + } + } + + bool TryFindPathCreator() + { + // Try find a path creator in the scene, if one is not already assigned + if (pathTool.pathCreator == null) + { + if (pathTool.GetComponent() != null) + { + pathTool.pathCreator = pathTool.GetComponent(); + } + else if (FindObjectOfType()) + { + pathTool.pathCreator = FindObjectOfType(); + } + } + return pathTool.pathCreator != null; + } + } +} \ No newline at end of file diff --git a/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta b/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta new file mode 100644 index 0000000..bf0c49c --- /dev/null +++ b/PathCreator/Examples/Scripts/Editor/PathSceneToolEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: db8aedf6f6cf2734cb24c49646427e96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/PathFollower.cs b/PathCreator/Examples/Scripts/PathFollower.cs new file mode 100644 index 0000000..7bd76ed --- /dev/null +++ b/PathCreator/Examples/Scripts/PathFollower.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace PathCreation.Examples +{ + // Moves along a path at constant speed. + // Depending on the end of path instruction, will either loop, reverse, or stop at the end of the path. + public class PathFollower : MonoBehaviour + { + public PathCreator pathCreator; + public EndOfPathInstruction endOfPathInstruction; + public float speed = 5; + float distanceTravelled; + + void Update() + { + if (pathCreator != null) + { + distanceTravelled += speed * Time.deltaTime; + transform.position = pathCreator.path.GetPointAtDistance(distanceTravelled, endOfPathInstruction); + transform.rotation = pathCreator.path.GetRotationAtDistance(distanceTravelled, endOfPathInstruction); + } + } + } +} \ No newline at end of file diff --git a/PathCreator/Examples/Scripts/PathFollower.cs.meta b/PathCreator/Examples/Scripts/PathFollower.cs.meta new file mode 100644 index 0000000..b353578 --- /dev/null +++ b/PathCreator/Examples/Scripts/PathFollower.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33aabe1a2dec93349b06e6e5dc51bc06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/PathFromObjects.cs b/PathCreator/Examples/Scripts/PathFromObjects.cs new file mode 100644 index 0000000..db81e73 --- /dev/null +++ b/PathCreator/Examples/Scripts/PathFromObjects.cs @@ -0,0 +1,42 @@ +using UnityEngine; + +namespace PathCreation.Examples +{ + // Creates a path from an array of transforms and moves along it + + [RequireComponent(typeof(TrailRenderer))] + public class PathFromObjects : MonoBehaviour + { + public Transform[] waypoints; + public float speed = 8; + + float dstTravelled; + VertexPath path; + + void Start() + { + if (waypoints.Length > 0) + { + // Create a new bezier path from the waypoints. + // The 'true' argument specifies that the path should be a closed loop + BezierPath bezierPath = new BezierPath(waypoints, true, PathSpace.xyz); + // Create a vertex path from the bezier path + path = new VertexPath(bezierPath); + } + else + { + Debug.Log("No waypoints assigned"); + } + } + + void Update() + { + if (path != null) + { + dstTravelled += speed * Time.deltaTime; + transform.position = path.GetPointAtDistance(dstTravelled); + } + } + + } +} \ No newline at end of file diff --git a/PathCreator/Examples/Scripts/PathFromObjects.cs.meta b/PathCreator/Examples/Scripts/PathFromObjects.cs.meta new file mode 100644 index 0000000..ef2955f --- /dev/null +++ b/PathCreator/Examples/Scripts/PathFromObjects.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f417e74782fffcd40b77b0fdbe82af04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/PathSceneTool.cs b/PathCreator/Examples/Scripts/PathSceneTool.cs new file mode 100644 index 0000000..92ef7a1 --- /dev/null +++ b/PathCreator/Examples/Scripts/PathSceneTool.cs @@ -0,0 +1,34 @@ +using UnityEngine; + +namespace PathCreation.Examples +{ + [ExecuteInEditMode] + public abstract class PathSceneTool : MonoBehaviour + { + public event System.Action onDestroyed; + public PathCreator pathCreator; + public bool autoUpdate = true; + + protected VertexPath path { + get { + return pathCreator.path; + } + } + + public void CreatePath() + { + if (pathCreator != null) + { + PathUpdated(); + } + } + + protected virtual void OnDestroy() { + if (onDestroyed != null) { + onDestroyed(); + } + } + + protected abstract void PathUpdated(); + } +} diff --git a/PathCreator/Examples/Scripts/PathSceneTool.cs.meta b/PathCreator/Examples/Scripts/PathSceneTool.cs.meta new file mode 100644 index 0000000..492994c --- /dev/null +++ b/PathCreator/Examples/Scripts/PathSceneTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83aca5399f4e71c44b5d6c1314b5b95f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PathCreator/Examples/Scripts/RoadMeshCreator.cs b/PathCreator/Examples/Scripts/RoadMeshCreator.cs new file mode 100644 index 0000000..e733759 --- /dev/null +++ b/PathCreator/Examples/Scripts/RoadMeshCreator.cs @@ -0,0 +1,170 @@ +using System.Collections.Generic; +using UnityEngine; +using PathCreation.Utility; + +namespace PathCreation.Examples +{ + public class RoadMeshCreator : PathSceneTool + { + [Header("Road settings")] + public float roadWidth = .4f; + [Range(0, .5f)] + public float thickness = .15f; + public bool flattenSurface; + + [Header("Material settings")] + public Material roadMaterial; + public Material undersideMaterial; + public float textureTiling = 1; + + MeshFilter meshFilter; + MeshRenderer meshRenderer; + + protected override void PathUpdated() + { + if (pathCreator != null) + { + AssignMeshComponents(); + AssignMaterials(); + meshFilter.mesh = CreateRoadMesh();; + } + } + + + Mesh CreateRoadMesh() + { + Vector3[] verts = new Vector3[path.NumVertices * 8]; + Vector2[] uvs = new Vector2[verts.Length]; + Vector3[] normals = new Vector3[verts.Length]; + + int numTris = 2 * (path.NumVertices - 1) + ((path.isClosedLoop) ? 2 : 0); + int[] roadTriangles = new int[numTris * 3]; + int[] underRoadTriangles = new int[numTris * 3]; + int[] sideOfRoadTriangles = new int[numTris * 2 * 3]; + + int vertIndex = 0; + int triIndex = 0; + + // Vertices for the top of the road are layed out: + // 0 1 + // 8 9 + // and so on... So the triangle map 0,8,1 for example, defines a triangle from top left to bottom left to bottom right. + int[] triangleMap = { 0, 8, 1, 1, 8, 9 }; + int[] sidesTriangleMap = { 4, 6, 14, 12, 4, 14, 5, 15, 7, 13, 15, 5 }; + + bool usePathNormals = !(path.space == PathSpace.xyz && flattenSurface); + + for (int i = 0; i < path.NumVertices; i++) + { + Vector3 localUp = (usePathNormals) ? Vector3.Cross(path.tangents[i], path.normals[i]) : path.up; + Vector3 localRight = (usePathNormals)?path.normals[i]:Vector3.Cross(localUp,path.tangents[i]); + + // Find position to left and right of current path vertex + Vector3 vertSideA = path.vertices[i] - localRight * Mathf.Abs(roadWidth) -transform.position; + Vector3 vertSideB = path.vertices[i] + localRight * Mathf.Abs(roadWidth) - transform.position; + + // Add top of road vertices + verts[vertIndex + 0] = vertSideA; + verts[vertIndex + 1] = vertSideB; + // Add bottom of road vertices + verts[vertIndex + 2] = vertSideA - localUp * thickness; + verts[vertIndex + 3] = vertSideB - localUp * thickness; + + // Duplicate vertices to get flat shading for sides of road + verts[vertIndex + 4] = verts[vertIndex + 0]; + verts[vertIndex + 5] = verts[vertIndex + 1]; + verts[vertIndex + 6] = verts[vertIndex + 2]; + verts[vertIndex + 7] = verts[vertIndex + 3]; + + // Set uv on y axis to path time (0 at start of path, up to 1 at end of path) + uvs[vertIndex + 0] = new Vector2(0, path.times[i]); + uvs[vertIndex + 1] = new Vector2(1, path.times[i]); + + // Top of road normals + normals[vertIndex + 0] = localUp; + normals[vertIndex + 1] = localUp; + // Bottom of road normals + normals[vertIndex + 2] = -localUp; + normals[vertIndex + 3] = -localUp; + // Sides of road normals + normals[vertIndex + 4] = -localRight; + normals[vertIndex + 5] = localRight; + normals[vertIndex + 6] = -localRight; + normals[vertIndex + 7] = localRight; + + + // Set triangle indices + if (i < path.NumVertices - 1 || path.isClosedLoop) + { + for (int j = 0; j < triangleMap.Length; j++) + { + roadTriangles[triIndex + j] = (vertIndex + triangleMap[j]) % verts.Length; + // reverse triangle map for under road so that triangles wind the other way and are visible from underneath + underRoadTriangles[triIndex + j] = (vertIndex + triangleMap[triangleMap.Length - 1 - j] + 2) % verts.Length; + } + for (int j = 0; j < sidesTriangleMap.Length; j++) + { + sideOfRoadTriangles[triIndex * 2 + j] = (vertIndex + sidesTriangleMap[j]) % verts.Length; + } + + } + + vertIndex += 8; + triIndex += 6; + } + + + Mesh mesh = new Mesh(); + mesh.vertices = verts; + mesh.uv = uvs; + mesh.normals = normals; + mesh.subMeshCount = 3; + mesh.SetTriangles(roadTriangles, 0); + mesh.SetTriangles(underRoadTriangles, 1); + mesh.SetTriangles(sideOfRoadTriangles, 2); + mesh.RecalculateBounds(); + + return mesh; + } + + + // Add MeshRenderer and MeshFilter components to this gameobject if not already attached + void AssignMeshComponents() + { + // Find/creator mesh holder object in children + string meshHolderName = "Mesh Holder"; + Transform meshHolder = transform.Find(meshHolderName); + if (meshHolder == null) { + meshHolder = new GameObject(meshHolderName).transform; + meshHolder.transform.parent = transform; + meshHolder.transform.localPosition = Vector3.zero; + } + + //meshHolder.transform.position = Vector3.zero; + meshHolder.transform.rotation = Quaternion.identity; + + // Ensure mesh renderer and filter components are assigned + if (!meshHolder.gameObject.GetComponent()) + { + meshHolder.gameObject.AddComponent(); + } + if (!meshHolder.GetComponent()) + { + meshHolder.gameObject.AddComponent(); + } + + meshRenderer = meshHolder.GetComponent(); + meshFilter = meshHolder.GetComponent(); + } + + void AssignMaterials() + { + if (roadMaterial != null && undersideMaterial != null) + { + meshRenderer.sharedMaterials = new Material[] { roadMaterial, undersideMaterial, undersideMaterial }; + meshRenderer.sharedMaterials[0].mainTextureScale = new Vector3(1, textureTiling); + } + } + + } +} \ No newline at end of file diff --git a/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta b/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta new file mode 100644 index 0000000..da79b4f --- /dev/null +++ b/PathCreator/Examples/Scripts/RoadMeshCreator.cs.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: b275c3b78f8717c4cbc60f74a3247f67 +timeCreated: 1546381468 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: + - pathCreator: {instanceID: 0} + - roadMaterial: {fileID: 2100000, guid: b5df4735192845740bd11076dc9cdc99, type: 2} + - undersideMaterial: {fileID: 2100000, guid: c1d8d882244c5344fa86a8fd23246f81, type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md new file mode 100644 index 0000000..f293ee8 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Path-Creator