public static FP ToFPRotation2D(this Quaternion r) { #if QUANTUM_XY return(-FP.FromFloat_UNSAFE(r.eulerAngles.z * Mathf.Deg2Rad)); #else return(-FP.FromFloat_UNSAFE(r.eulerAngles.y * Mathf.Deg2Rad)); #endif }
public override Tuple <Input, DeterministicInputFlags> PollInput(int player) { Input i = new Input(); //Need to query unity to get controls FP x = FP.FromFloat_UNSAFE(UnityEngine.Input.GetAxis("Horizontal")); FP y = FP.FromFloat_UNSAFE(UnityEngine.Input.GetAxis("Vertical")); if (player == 1) { y = -y; } i.Movement = new FPVector2(x, y); i.Fire = UnityEngine.Input.GetButton("Fire1"); return(Tuple.Create(i, DeterministicInputFlags.Repeatable)); }
static public void DrawGizmoGrid(Vector3 bottomLeft, Int32 width, Int32 height, Int32 nodeSize, Color color) { Gizmos.color = color; for (Int32 z = 0; z < height; ++z) { for (Int32 x = 0; x < width; ++x) { var zn = FP.FromFloat_UNSAFE(z * nodeSize + (nodeSize / 2f)); var xn = FP.FromFloat_UNSAFE(x * nodeSize + (nodeSize / 2f)); Gizmos.DrawWireCube(bottomLeft + new FPVector2(zn, xn).ToUnityVector3(), new FPVector2(nodeSize, nodeSize).ToUnityVector3()); } } Gizmos.color = Color.white; }
public static void Draw(Rect p, SerializedProperty prop, GUIContent label) { // grab value var f = FP.FromRaw(prop.longValue); var v = (Single)Math.Round(f.AsFloat, 5); // edit value try { var n = label == null?EditorGUI.FloatField(p, v) : EditorGUI.FloatField(p, label, v); if (n != v) { prop.longValue = FP.FromFloat_UNSAFE(n).RawValue; } GUI.Label(p, "(Fixed Point)", OverlayStyle); } catch (FormatException exn) { if (exn.Message != ".") { Debug.LogException(exn); } } }
/* * [MenuItem("Assets/Export Mecanim Animation Controller", false, 301)] * private static void CreateAnimatorFile() { * var animatorController = Selection.activeObject as AnimatorController; * if (animatorController == null) { * return; * } * * CreateAsset(animatorController); * } * * [MenuItem("Assets/Export Mecanim Animation Controller", true)] * private static bool CreateAnimatorController() { * return Selection.activeObject is AnimatorController; * } */ // public static AnimatorGraphAsset Fetch(string name) { // AnimatorGraphAsset output = null; //#if UNITY_EDITOR // string pathToAnimationResource = "Assets/Resources/DB/Animator/"; // string animationFilePath = pathToAnimationResource + name + ".asset"; // output = UnityEditor.AssetDatabase.LoadAssetAtPath(animationFilePath, typeof(AnimatorGraphAsset)) as AnimatorGraphAsset; // if (output == null) { // output = CreateInstance<AnimatorGraphAsset>(); // animationFilePath = UnityEditor.AssetDatabase.GenerateUniqueAssetPath(animationFilePath); // UnityEditor.AssetDatabase.CreateAsset(output, animationFilePath); // UnityEditor.AssetDatabase.SaveAssets(); // UnityEditor.AssetDatabase.Refresh(); // } //#endif // if (output != null) { // if (output.Settings == null) // output.Settings = new AnimatorGraph(); // } // return output; // } public static void CreateAsset(AnimatorGraphAsset dataAsset, AnimatorController controller) { if (!controller) { return; } if (!dataAsset) { return; } QuantumRunner.Init();//make sure we can get debug calls from Quantum dataAsset.controller = controller; int weightTableResolution = (int)dataAsset.weight_table_resolution; int variableCount = controller.parameters.Length; dataAsset.Settings.variables = new AnimatorVariable[variableCount]; //Mecanim Parameters/Variables //make a dictionary of paramets by name for use when extracting conditions for transitions Dictionary <string, AnimatorControllerParameter> parameterDic = new Dictionary <string, AnimatorControllerParameter>(); for (int v = 0; v < variableCount; v++) { AnimatorControllerParameter parameter = controller.parameters[v]; parameterDic.Add(parameter.name, parameter); AnimatorVariable newVariable = new AnimatorVariable(); newVariable.name = parameter.name; newVariable.index = v; switch (parameter.type) { case AnimatorControllerParameterType.Bool: newVariable.type = AnimatorVariable.VariableType.Bool; newVariable.defaultBool = parameter.defaultBool; break; case AnimatorControllerParameterType.Float: newVariable.type = AnimatorVariable.VariableType.FP; newVariable.defaultFp = FP.FromFloat_UNSAFE(parameter.defaultFloat); break; case AnimatorControllerParameterType.Int: newVariable.type = AnimatorVariable.VariableType.Int; newVariable.defaultInt = parameter.defaultInt; break; case AnimatorControllerParameterType.Trigger: newVariable.type = AnimatorVariable.VariableType.Trigger; break; } dataAsset.Settings.variables[v] = newVariable; } //Mecanim State Graph int layerCount = controller.layers.Length; dataAsset.clips.Clear(); dataAsset.Settings.layers = new AnimatorLayer[layerCount]; for (int l = 0; l < layerCount; l++) { AnimatorLayer newLayer = new AnimatorLayer(); newLayer.name = controller.layers[l].name; newLayer.id = l; int stateCount = controller.layers[l].stateMachine.states.Length; newLayer.states = new Quantum.AnimatorState[stateCount + 1];//additional element for the any state Dictionary <UnityEditor.Animations.AnimatorState, Quantum.AnimatorState> stateDictionary = new Dictionary <AnimatorState, Quantum.AnimatorState>(); for (int s = 0; s < stateCount; s++) { UnityEditor.Animations.AnimatorState state = controller.layers[l].stateMachine.states[s].state; Quantum.AnimatorState newState = new Quantum.AnimatorState(); newState.name = state.name; newState.id = state.nameHash; newState.isDefault = controller.layers[l].stateMachine.defaultState == state; newState.speed = FP.FromFloat_UNSAFE(state.speed); newState.cycleOffset = FP.FromFloat_UNSAFE(state.cycleOffset); if (state.motion != null) { AnimationClip clip = state.motion as AnimationClip; if (clip != null) { dataAsset.clips.Add(clip); AnimatorClip newClip = new AnimatorClip(); newClip.name = state.motion.name; newClip.data = Extract(clip); newState.motion = newClip; } else { BlendTree tree = state.motion as BlendTree; if (tree != null) { int childCount = tree.children.Length; AnimatorBlendTree newBlendTree = new AnimatorBlendTree(); newBlendTree.name = state.motion.name; newBlendTree.motionCount = childCount; newBlendTree.motions = new AnimatorMotion[childCount]; newBlendTree.positions = new FPVector2[childCount]; newBlendTree.weights = new FP[childCount]; string parameterXname = tree.blendParameter; string parameterYname = tree.blendParameterY; for (int v = 0; v < variableCount; v++) { if (controller.parameters[v].name == parameterXname) { newBlendTree.blendParameterIndex = v; } if (controller.parameters[v].name == parameterYname) { newBlendTree.blendParameterIndexY = v; } } for (int c = 0; c < childCount; c++) { ChildMotion cMotion = tree.children[c]; AnimationClip cClip = cMotion.motion as AnimationClip; newBlendTree.positions[c] = new FPVector2(FP.FromFloat_UNSAFE(cMotion.position.x), FP.FromFloat_UNSAFE(cMotion.position.y)); if (cClip != null) { dataAsset.clips.Add(cClip); AnimatorClip newClip = new AnimatorClip(); newClip.data = Extract(cClip); newClip.name = newClip.clipName; newBlendTree.motions[c] = newClip; } } FP val = FP._0 / 21; newBlendTree.CalculateWeightTable(weightTableResolution); newState.motion = newBlendTree; } } } newLayer.states[s] = newState; stateDictionary.Add(state, newState); } //State Transistions //once the states have all been created //we'll hook up the transitions for (int s = 0; s < stateCount; s++) { UnityEditor.Animations.AnimatorState state = controller.layers[l].stateMachine.states[s].state; Quantum.AnimatorState newState = newLayer.states[s]; int transitionCount = state.transitions.Length; newState.transitions = new Quantum.AnimatorTransition[transitionCount]; for (int t = 0; t < transitionCount; t++) { AnimatorStateTransition transition = state.transitions[t]; if (!stateDictionary.ContainsKey(transition.destinationState)) { continue; } Quantum.AnimatorTransition newTransition = new Quantum.AnimatorTransition(); newTransition.index = t; newTransition.name = string.Format("{0} to {1}", state.name, transition.destinationState.name); newTransition.duration = FP.FromFloat_UNSAFE(transition.duration * state.motion.averageDuration); newTransition.hasExitTime = transition.hasExitTime; newTransition.exitTime = FP.FromFloat_UNSAFE(transition.exitTime * state.motion.averageDuration); newTransition.offset = FP.FromFloat_UNSAFE(transition.offset * transition.destinationState.motion.averageDuration); newTransition.destinationStateId = stateDictionary[transition.destinationState].id; newTransition.destinationStateName = stateDictionary[transition.destinationState].name; newTransition.canTransitionToSelf = transition.canTransitionToSelf; int conditionCount = transition.conditions.Length; newTransition.conditions = new Quantum.AnimatorCondition[conditionCount]; for (int c = 0; c < conditionCount; c++) { UnityEditor.Animations.AnimatorCondition condition = state.transitions[t].conditions[c]; if (!parameterDic.ContainsKey(condition.parameter)) { continue; } AnimatorControllerParameter parameter = parameterDic[condition.parameter]; Quantum.AnimatorCondition newCondition = new Quantum.AnimatorCondition(); newCondition.variableName = condition.parameter; newCondition.mode = (Quantum.AnimatorCondition.Modes)condition.mode; switch (parameter.type) { case AnimatorControllerParameterType.Float: newCondition.thresholdFp = FP.FromFloat_UNSAFE(condition.threshold); break; case AnimatorControllerParameterType.Int: newCondition.thresholdInt = Mathf.RoundToInt(condition.threshold); break; } newTransition.conditions[c] = newCondition; } newState.transitions[t] = newTransition; } } //Create Any State Quantum.AnimatorState anyState = new Quantum.AnimatorState(); anyState.name = "Any State"; anyState.id = anyState.name.GetHashCode(); anyState.isAny = true;//important for this one AnimatorStateTransition[] anyStateTransitions = controller.layers[l].stateMachine.anyStateTransitions; int anyStateTransitionCount = anyStateTransitions.Length; anyState.transitions = new Quantum.AnimatorTransition[anyStateTransitionCount]; for (int t = 0; t < anyStateTransitionCount; t++) { AnimatorStateTransition transition = anyStateTransitions[t]; if (!stateDictionary.ContainsKey(transition.destinationState)) { continue; } Quantum.AnimatorTransition newTransition = new Quantum.AnimatorTransition(); newTransition.index = t; newTransition.name = string.Format("Any State to {0}", transition.destinationState.name); newTransition.duration = FP.FromFloat_UNSAFE(transition.duration); newTransition.hasExitTime = transition.hasExitTime; newTransition.exitTime = FP._1; newTransition.offset = FP.FromFloat_UNSAFE(transition.offset * transition.destinationState.motion.averageDuration); newTransition.destinationStateId = stateDictionary[transition.destinationState].id; newTransition.destinationStateName = stateDictionary[transition.destinationState].name; newTransition.canTransitionToSelf = transition.canTransitionToSelf; int conditionCount = transition.conditions.Length; newTransition.conditions = new Quantum.AnimatorCondition[conditionCount]; for (int c = 0; c < conditionCount; c++) { UnityEditor.Animations.AnimatorCondition condition = anyStateTransitions[t].conditions[c]; if (!parameterDic.ContainsKey(condition.parameter)) { continue; } AnimatorControllerParameter parameter = parameterDic[condition.parameter]; Quantum.AnimatorCondition newCondition = new Quantum.AnimatorCondition(); newCondition.variableName = condition.parameter; newCondition.mode = (Quantum.AnimatorCondition.Modes)condition.mode; switch (parameter.type) { case AnimatorControllerParameterType.Float: newCondition.thresholdFp = FP.FromFloat_UNSAFE(condition.threshold); break; case AnimatorControllerParameterType.Int: newCondition.thresholdInt = Mathf.RoundToInt(condition.threshold); break; } newTransition.conditions[c] = newCondition; } anyState.transitions[t] = newTransition; } newLayer.states[stateCount] = anyState; dataAsset.Settings.layers[l] = newLayer; } EditorUtility.SetDirty(dataAsset); }
public static AnimatorData Extract(AnimationClip clip) { AnimatorData animationData = new AnimatorData(); animationData.clipName = clip.name; EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip); AnimationClipSettings settings = AnimationUtility.GetAnimationClipSettings(clip); float usedTime = settings.stopTime - settings.startTime; animationData.frameRate = Mathf.RoundToInt(clip.frameRate); animationData.length = FP.FromFloat_UNSAFE(usedTime); animationData.frameCount = Mathf.RoundToInt(clip.frameRate * usedTime); animationData.frames = new AnimatorFrame[animationData.frameCount]; animationData.looped = clip.isLooping && settings.loopTime; animationData.mirror = settings.mirror; //Read the curves of animation int frameCount = animationData.frameCount; int curveBindingsLength = curveBindings.Length; if (curveBindingsLength == 0) { return(animationData); } AnimationCurve curveTx = null, curveTy = null, curveTz = null, curveRx = null, curveRy = null, curveRz = null, curveRw = null; for (int c = 0; c < curveBindingsLength; c++) { string propertyName = curveBindings[c].propertyName; if (propertyName == "m_LocalPosition.x" || propertyName == "RootT.x") { curveTx = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalPosition.y" || propertyName == "RootT.y") { curveTy = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalPosition.z" || propertyName == "RootT.z") { curveTz = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalRotation.x" || propertyName == "RootQ.x") { curveRx = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalRotation.y" || propertyName == "RootQ.y") { curveRy = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalRotation.z" || propertyName == "RootQ.z") { curveRz = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } if (propertyName == "m_LocalRotation.w" || propertyName == "RootQ.w") { curveRw = AnimationUtility.GetEditorCurve(clip, curveBindings[c]); } } // if (curveBindingsLength >= 7) // { // //Position Curves // curveTx = AnimationUtility.GetEditorCurve(clip, curveBindings[0]); // curveTy = AnimationUtility.GetEditorCurve(clip, curveBindings[1]); // curveTz = AnimationUtility.GetEditorCurve(clip, curveBindings[2]); // // //Rotation Curves // curveRx = AnimationUtility.GetEditorCurve(clip, curveBindings[3]); // curveRy = AnimationUtility.GetEditorCurve(clip, curveBindings[4]); // curveRz = AnimationUtility.GetEditorCurve(clip, curveBindings[5]); // curveRw = AnimationUtility.GetEditorCurve(clip, curveBindings[6]); // } bool hasPosition = curveTx != null && curveTy != null && curveTz != null; bool hasRotation = curveRx != null && curveRy != null && curveRz != null && curveRw != null; if (!hasPosition) { Debug.LogWarning("No movement data was found in the animation: " + clip.name); } if (!hasRotation) { Debug.LogWarning("No rotation data was found in the animation: " + clip.name); } //The initial pose might not be the first frame and might not face foward //calculate the initial direction and create an offset Quaternion to apply to transforms; Quaternion startRotUq = Quaternion.identity; FPQuaternion startRot = FPQuaternion.Identity; if (hasRotation) { float srotxu = curveRx.Evaluate(settings.startTime); float srotyu = curveRy.Evaluate(settings.startTime); float srotzu = curveRz.Evaluate(settings.startTime); float srotwu = curveRw.Evaluate(settings.startTime); FP srotx = FP.FromFloat_UNSAFE(srotxu); FP sroty = FP.FromFloat_UNSAFE(srotyu); FP srotz = FP.FromFloat_UNSAFE(srotzu); FP srotw = FP.FromFloat_UNSAFE(srotwu); startRotUq = new Quaternion(srotxu, srotyu, srotzu, srotwu); startRot = new FPQuaternion(srotx, sroty, srotz, srotw); } Quaternion offsetRotUq = Quaternion.Inverse(startRotUq); FPQuaternion offsetRot = FPQuaternion.Inverse(startRot); for (int i = 0; i < frameCount; i++) { var frameData = new AnimatorFrame(); frameData.id = i; float percent = i / (frameCount - 1f); float frameTime = usedTime * percent; frameData.time = FP.FromFloat_UNSAFE(frameTime); float clipTIme = settings.startTime + percent * (settings.stopTime - settings.startTime); if (hasPosition) { FP posx = FP.FromFloat_UNSAFE(i > 0 ? curveTx.Evaluate(clipTIme) - curveTx.Evaluate(settings.startTime) : 0); FP posy = FP.FromFloat_UNSAFE(i > 0 ? curveTy.Evaluate(clipTIme) - curveTy.Evaluate(settings.startTime) : 0); FP posz = FP.FromFloat_UNSAFE(i > 0 ? curveTz.Evaluate(clipTIme) - curveTz.Evaluate(settings.startTime) : 0); FPVector3 newPosition = offsetRot * new FPVector3(posx, posy, posz); if (settings.mirror) { newPosition.X = -newPosition.X; } frameData.position = newPosition; } if (hasRotation) { float curveRxEval = curveRx.Evaluate(clipTIme); float curveRyEval = curveRy.Evaluate(clipTIme); float curveRzEval = curveRz.Evaluate(clipTIme); float curveRwEval = curveRw.Evaluate(clipTIme); Quaternion curveRotation = offsetRotUq * new Quaternion(curveRxEval, curveRyEval, curveRzEval, curveRwEval); if (settings.mirror)//mirror the Y axis rotation { Quaternion mirrorRotation = new Quaternion(curveRotation.x, -curveRotation.y, -curveRotation.z, curveRotation.w); if (Quaternion.Dot(curveRotation, mirrorRotation) < 0) { mirrorRotation = new Quaternion(-mirrorRotation.x, -mirrorRotation.y, -mirrorRotation.z, -mirrorRotation.w); } curveRotation = mirrorRotation; } FP rotx = FP.FromFloat_UNSAFE(curveRotation.x); FP roty = FP.FromFloat_UNSAFE(curveRotation.y); FP rotz = FP.FromFloat_UNSAFE(curveRotation.z); FP rotw = FP.FromFloat_UNSAFE(curveRotation.w); FPQuaternion newRotation = new FPQuaternion(rotx, roty, rotz, rotw); frameData.rotation = newRotation * offsetRot; float rotY = curveRotation.eulerAngles.y * Mathf.Deg2Rad; while (rotY < -Mathf.PI) { rotY += Mathf.PI * 2; } while (rotY > Mathf.PI) { rotY += -Mathf.PI * 2; } frameData.rotationY = FP.FromFloat_UNSAFE(rotY); } animationData.frames[i] = frameData; } return(animationData); }
public static FP ToFP(this Single v) { return(FP.FromFloat_UNSAFE(v)); }
static void BakeData(MapData data, Boolean inEditor) { #if UNITY_EDITOR if (inEditor) { if (EditorSceneManager.loadedSceneCount != 1) { Debug.LogErrorFormat("Can't bake map data when more than one scene is open."); return; } // set scene name data.Asset.Settings.Scene = EditorSceneManager.GetActiveScene().name; } #endif // clear existing colliders data.Asset.Settings.StaticColliders = new MapStaticCollider[0]; // circle colliders foreach (var collider in UnityEngine.Object.FindObjectsOfType <QuantumStaticCircleCollider2D>()) { ArrayUtils.Add(ref data.Asset.Settings.StaticColliders, new MapStaticCollider { Position = collider.transform.position.ToFPVector2(), Rotation = collider.transform.rotation.ToFPRotation2D(), PhysicsMaterial = collider.Settings.PhysicsMaterial, Trigger = collider.Settings.Trigger, StaticData = GetStaticData(collider.gameObject, collider.Settings), Layer = collider.gameObject.layer, // circle ShapeType = Quantum.Core.DynamicShapeType.Circle, CircleRadius = FP.FromFloat_UNSAFE(collider.Radius.AsFloat * collider.transform.localScale.x) }); } // polygon colliders foreach (var collider in UnityEngine.Object.FindObjectsOfType <QuantumStaticPolygonCollider2D>()) { var s = collider.transform.localScale; var vertices = collider.Vertices.Select(x => { var v = x.ToUnityVector3(); return(new Vector3(v.x * s.x, v.y * s.y, v.z * s.z)); }).Select(x => x.ToFPVector2()).ToArray(); if (FPVector2.IsClockWise(vertices)) { FPVector2.MakeCounterClockWise(vertices); } var normals = FPVector2.CalculatePolygonNormals(vertices); ArrayUtils.Add(ref data.Asset.Settings.StaticColliders, new MapStaticCollider { Position = collider.transform.position.ToFPVector2(), Rotation = collider.transform.rotation.ToFPRotation2D(), PhysicsMaterial = collider.Settings.PhysicsMaterial, Trigger = collider.Settings.Trigger, StaticData = GetStaticData(collider.gameObject, collider.Settings), Layer = collider.gameObject.layer, // polygon ShapeType = Quantum.Core.DynamicShapeType.Polygon, PolygonCollider = new PolygonCollider { Vertices = vertices, Normals = normals } }); } // polygon colliders foreach (var collider in UnityEngine.Object.FindObjectsOfType <QuantumStaticBoxCollider2D>()) { var e = collider.Size.ToUnityVector3(); var s = collider.transform.localScale; e.x *= s.x; e.y *= s.y; e.z *= s.z; ArrayUtils.Add(ref data.Asset.Settings.StaticColliders, new MapStaticCollider { Position = collider.transform.position.ToFPVector2(), Rotation = collider.transform.rotation.ToFPRotation2D(), PhysicsMaterial = collider.Settings.PhysicsMaterial, Trigger = collider.Settings.Trigger, StaticData = GetStaticData(collider.gameObject, collider.Settings), Layer = collider.gameObject.layer, // polygon ShapeType = Quantum.Core.DynamicShapeType.Box, BoxExtents = e.ToFPVector2() * FP._0_50 }); } // invoke callbacks foreach (var callback in Quantum.TypeUtils.GetSubClasses(typeof(MapDataBakerCallback), "Assembly-CSharp", "Assembly-CSharp-firstpass", "Assembly-CSharp-Editor", "Assembly-CSharp-Editor-firstpass")) { if (callback.IsAbstract == false) { try { (Activator.CreateInstance(callback) as MapDataBakerCallback).OnBake(data); } catch (Exception exn) { Debug.LogException(exn); } } } if (inEditor) { Debug.LogFormat("Baked {0} static colliders", data.Asset.Settings.StaticColliders.Length); } }
public static NavMesh BakeNavMesh(MapData data, MapNavMeshDefinition navmeshDefinition) { try { FPMathUtils.LoadLookupTables(); var vs_array = navmeshDefinition.Vertices.ToArray(); var nav_vertices = vs_array.Map(x => new NavMeshVertex { Point = x.Position.ToFPVector2(), Neighbors = new Int32[0], Triangles = new Int32[0], Borders = new Int32[0] }); var nav_triangles = new NavMeshTriangle[0]; // TRIANGLES for (Int32 i = 0; i < navmeshDefinition.Triangles.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Triangles", i, navmeshDefinition.Triangles.Length); var t = navmeshDefinition.Triangles[i]; var v0 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[0]); var v1 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[1]); var v2 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[2]); ArrayUtils.Add(ref nav_triangles, new NavMeshTriangle { Vertex0 = v0, Vertex1 = v1, Vertex2 = v2, Center = (nav_vertices[v0].Point + nav_vertices[v1].Point + nav_vertices[v2].Point) / FP._3 }); } // TRIANGLE GRID var nav_triangles_grid = new NavMeshTriangleNode[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; //for (Int32 i = 0; i < nav_triangles_grid.Length; ++i) { // nav_triangles_grid[i] = i + 1; //} for (Int32 i = 0; i < nav_triangles.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Triangle Grid", i, nav_triangles.Length); var v0 = nav_vertices[nav_triangles[i].Vertex0].Point; var v1 = nav_vertices[nav_triangles[i].Vertex1].Point; var v2 = nav_vertices[nav_triangles[i].Vertex2].Point; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var bl = data.Asset.Settings.WorldOffset + new FPVector2(x * data.Asset.Settings.GridNodeSize, z * data.Asset.Settings.GridNodeSize); var br = bl + new FPVector2(data.Asset.Settings.GridNodeSize, 0); var ur = bl + new FPVector2(data.Asset.Settings.GridNodeSize, data.Asset.Settings.GridNodeSize); var ul = bl + new FPVector2(0, data.Asset.Settings.GridNodeSize); if ( // if any of the corners are inside the triangle FPCollision.TriangleContainsPoint(bl, v0, v1, v2) || FPCollision.TriangleContainsPoint(br, v0, v1, v2) || FPCollision.TriangleContainsPoint(ur, v0, v1, v2) || FPCollision.TriangleContainsPoint(ul, v0, v1, v2) || // BL => BR FPCollision.TriangleContainsPoint(v0, v1, bl, br) || FPCollision.TriangleContainsPoint(v1, v2, bl, br) || FPCollision.TriangleContainsPoint(v2, v0, bl, br) || // BR => UR FPCollision.TriangleContainsPoint(v0, v1, br, ur) || FPCollision.TriangleContainsPoint(v1, v2, br, ur) || FPCollision.TriangleContainsPoint(v2, v0, br, ur) || // UR => UL FPCollision.TriangleContainsPoint(v0, v1, ur, ul) || FPCollision.TriangleContainsPoint(v1, v2, ur, ul) || FPCollision.TriangleContainsPoint(v2, v0, ur, ul) || // UL => BL FPCollision.TriangleContainsPoint(v0, v1, ul, bl) || FPCollision.TriangleContainsPoint(v1, v2, ul, bl) || FPCollision.TriangleContainsPoint(v2, v0, ul, bl) ) { var idx = (z * data.Asset.Settings.GridSize) + x; if (nav_triangles_grid[idx].Triangles == null) { nav_triangles_grid[idx].Triangles = new Int32[0]; } // add triangle to this grid node ArrayUtils.Add(ref nav_triangles_grid[idx].Triangles, i); } } } } // VERTEX NEIGHBORS for (Int32 v = 0; v < nav_vertices.Length; ++v) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Vertex Neighbors", v, nav_vertices.Length); var triangles = new HashSet <Int32>(); var neighbors = new HashSet <Int32>(); for (Int32 t = 0; t < nav_triangles.Length; ++t) { var tr = nav_triangles[t]; if (tr.Vertex0 == v || tr.Vertex1 == v || tr.Vertex2 == v) { triangles.Add(t); neighbors.Add(tr.Vertex0); neighbors.Add(tr.Vertex1); neighbors.Add(tr.Vertex2); } } // remove itself from neighbors set neighbors.Remove(v); // nav_vertices[v].Triangles = triangles.OrderBy(x => x).ToArray(); nav_vertices[v].Neighbors = neighbors.ToArray(); } // BORDER EDGES for (Int32 t = 0; t < nav_triangles.Length; ++t) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Border Edges", t, nav_triangles.Length); var tr = nav_triangles[t]; if (IsBorderEdge(nav_triangles, t, tr.Vertex0, tr.Vertex1)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex0].Borders, tr.Vertex1); ArrayUtils.Add(ref nav_vertices[tr.Vertex1].Borders, tr.Vertex0); } if (IsBorderEdge(nav_triangles, t, tr.Vertex1, tr.Vertex2)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex1].Borders, tr.Vertex2); ArrayUtils.Add(ref nav_vertices[tr.Vertex2].Borders, tr.Vertex1); } if (IsBorderEdge(nav_triangles, t, tr.Vertex2, tr.Vertex0)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex2].Borders, tr.Vertex0); ArrayUtils.Add(ref nav_vertices[tr.Vertex0].Borders, tr.Vertex2); } } // NORMALS var pt2 = FP._0_10 * FP._2; var pt3 = FP._0_10 * FP._3; for (Int32 i = 0; i < nav_vertices.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Normals", i, nav_vertices.Length); var v = nav_vertices[i]; var tn = new FPVector2[3]; if (v.Borders != null) { // 0. preferred middle of borders var borders = v.Borders.Map(x => FPVector2.Normalize(nav_vertices[x].Point - v.Point)); if (borders.Length == 2) { tn[0] = FPVector2.Normalize(FPVector2.Lerp(borders[0], borders[1], FP._0_50)); } // 1. second preferred neighbor edge that is furthest away from borders if (v.Neighbors != null) { var neighbors = v.Neighbors.Where(x => !v.Borders.Contains(x)).Select(x => new Neighbor { Direction = FPVector2.Normalize(nav_vertices[x].Point - v.Point), Vertex = x } ).ToArray(); var max_dot = FP.MinValue; var max_neighbor = default(Neighbor); max_neighbor.Vertex = -1; for (Int32 n = 0; n < neighbors.Length; ++n) { var dot = FP._0; for (Int32 b = 0; b < borders.Length; ++b) { dot += FPVector2.Dot(borders[b], neighbors[n].Direction); } dot = FPMath.Abs(dot); if (dot > max_dot) { max_dot = dot; max_neighbor = neighbors[n]; } } if (max_neighbor.Vertex >= 0) { tn[1] = max_neighbor.Direction * pt2; } } } // 2. least preferred, avarage of triangle normals foreach (var tc in v.Triangles.Select(x => FPVector2.Normalize(TriangleCenter(nav_triangles[x], nav_vertices) - v.Point))) { tn[2] += tc; } tn[2] = FPVector2.Normalize(tn[2]); // find normal var failed = true; for (Int32 k = 0; failed && k < tn.Length; ++k) { if (tn[k] != FPVector2.Zero) { if (failed && TriangleContains(nav_triangles, nav_vertices, (v.Point + (tn[k] * pt3)))) { nav_vertices[i].Normal = FPVector2.Normalize(tn[k] * pt2); // we're done failed = false; } if (failed && TriangleContains(nav_triangles, nav_vertices, (v.Point + (-tn[k] * pt3)))) { nav_vertices[i].Normal = FPVector2.Normalize(-tn[k] * pt2); // we're done failed = false; } } } } // BORDER SET HashSet <Border> border_set = new HashSet <Border>(); for (Int32 v = 0; v < nav_vertices.Length; ++v) { Progress(navmeshDefinition.name + " Baking: Border Set", v, nav_vertices.Length); if (nav_vertices[v].Borders != null) { for (Int32 n = 0; n < nav_vertices[v].Borders.Length; ++n) { border_set.Add(new Border(v, nav_vertices[v].Borders[n], border_set.Count + 1)); } } } // BORDER GRID var nav_border_grid = new NavMeshBorderNode[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var idx = (z * data.Asset.Settings.GridSize) + x; Progress("Baking NavMesh '" + navmeshDefinition.name + "': Border Grid", idx, data.Asset.Settings.GridSize * data.Asset.Settings.GridSize); // set index key // nav_border_grid[idx].key = idx + 1; // var zn = (FP)z * data.Asset.Settings.GridNodeSize; var xn = (FP)x * data.Asset.Settings.GridNodeSize; FPVector2 bl = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn); FPVector2 br = data.Asset.Settings.WorldOffset + new FPVector2(xn + data.Asset.Settings.GridNodeSize, zn); FPVector2 ur = data.Asset.Settings.WorldOffset + new FPVector2(xn + data.Asset.Settings.GridNodeSize, zn + data.Asset.Settings.GridNodeSize); FPVector2 ul = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn + data.Asset.Settings.GridNodeSize); foreach (var b in border_set) { var p0 = nav_vertices[b.V0].Point; var p1 = nav_vertices[b.V1].Point; if ( FPCollision.LineIntersectsLine(p0, p1, bl, br) || FPCollision.LineIntersectsLine(p0, p1, br, ur) || FPCollision.LineIntersectsLine(p0, p1, ur, ul) || FPCollision.LineIntersectsLine(p0, p1, ul, bl) ) { if (nav_border_grid[idx].Borders == null) { nav_border_grid[idx].Borders = new NavMeshBorder[0]; } ArrayUtils.Add(ref nav_border_grid[idx].Borders, new NavMeshBorder { Key = b.Key, V0 = p0, V1 = p1, }); } } } } // TRIANGLE CENTER GRID var nav_triangles_center_grid = new Int32[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var idx = (z * data.Asset.Settings.GridSize) + x; Progress("Baking NavMesh '" + navmeshDefinition.name + "': Triangle Center Grid", idx, data.Asset.Settings.GridSize * data.Asset.Settings.GridSize); var zn = (FP)(z * data.Asset.Settings.GridNodeSize); var xn = (FP)(x * data.Asset.Settings.GridNodeSize); var g = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn) + new FPVector2(FP.FromFloat_UNSAFE(data.Asset.Settings.GridNodeSize * 0.5f), FP.FromFloat_UNSAFE(data.Asset.Settings.GridNodeSize * 0.5f)); var d = FP.MaxValue; var t = -1; for (Int32 i = 0; i < nav_triangles.Length; ++i) { var c = nav_triangles[i].Center; if (FPVector2.DistanceSquared(g, c) < d) { d = FPVector2.DistanceSquared(g, c); t = i; } } Assert.Check(t >= 0); nav_triangles_center_grid[idx] = t; } } NavMesh navmesh; navmesh = new NavMesh(); navmesh.GridSize = data.Asset.Settings.GridSize; navmesh.GridNodeSize = data.Asset.Settings.GridNodeSize; navmesh.WorldOffset = data.Asset.Settings.WorldOffset; navmesh.Name = navmeshDefinition.name; navmesh.Vertices = nav_vertices; navmesh.BorderGrid = nav_border_grid; navmesh.Triangles = nav_triangles; navmesh.TrianglesGrid = nav_triangles_grid; navmesh.TrianglesCenterGrid = nav_triangles_center_grid; return(navmesh); } finally { #if UNITY_EDITOR EditorUtility.ClearProgressBar(); #endif } }