public static bool Intersect(CubeXCollider src, SphereXCollider dst, out XContact?contact) { // 反向旋转sphere的位置,使得可以在cube的坐标系下进行碰撞判断 var extents = src.Size * 0.5f; var invQ = Quaternion.Inverse(src.Quaternion); var invP = invQ * (dst.Position - src.Position); // 以下所有操作都是在cube的坐标系下,随后的实际方向需要进行坐标系转换 var n = invP; var closest = n; closest.x = Mathf.Clamp(closest.x, -extents.x, extents.x); closest.y = Mathf.Clamp(closest.y, -extents.y, extents.y); closest.z = Mathf.Clamp(closest.z, -extents.z, extents.z); var inside = false; if (n == closest) { inside = true; var disX = extents.x - Mathf.Abs(n.x); var disY = extents.y - Mathf.Abs(n.y); var disZ = extents.z - Mathf.Abs(n.z); //找到最近的一个面 if (disX < disY && disX < disZ) { // 沿X轴 if (n.x > 0) { closest.x = extents.x; } else { closest.x = -extents.x; } } else if (disY < disX && disY < disZ) { // 沿Y轴 if (n.y > 0) { closest.y = extents.y; } else { closest.y = -extents.y; } } else { // 沿Z轴 if (n.z > 0) { closest.z = extents.z; } else { closest.z = -extents.z; } } } var dir = n - closest; var sqrDist = dir.sqrMagnitude; var space = dst.Radius; var sqrSpace = space * space; if (sqrDist < sqrSpace || inside) { var dist = Mathf.Sqrt(sqrDist); var normal = (src.Quaternion * dir).normalized; var penetration = space - dist; if (inside) { normal = -normal; penetration = space + dist; } if (normal == Vector3.zero) { normal = Vector3.up; } contact = new XContact(src, dst, normal, penetration); return(true); } contact = null; return(false); }
public static bool Intersect(CubeXCollider src, CylinderXCollider dst, out XContact?contact) { var invP = Quaternion.Inverse(src.Quaternion) * (dst.Position - src.Position); Vector3 n = Vector3.zero; n.x = invP.x; n.z = invP.z; var extents = src.Size * 0.5f; var halfHa = extents.y; var topA = halfHa; var bottomA = -halfHa; var halfHb = dst.Height * 0.5f; var topB = invP.y + halfHb; var bottomB = invP.y - halfHb; var space = dst.Radius; var sqrSpace = space * space; // 相撞时,俯视图下的圆和矩形必然相交 var closest = n; closest.x = Mathf.Clamp(closest.x, -extents.x, extents.x); closest.z = Mathf.Clamp(closest.z, -extents.z, extents.z); if ((n - closest).sqrMagnitude > sqrSpace) { contact = null; return(false); } // 处理相交的情况 Vector3 normal; float penetration; float verticalP = float.PositiveInfinity; float horizontalP; var inside = false; if (n == closest) { inside = true; var disX = extents.x - Mathf.Abs(n.x); var disZ = extents.z - Mathf.Abs(n.z); //找到最近的一个面 if (disX < disZ) { // 沿X轴 if (n.x > 0) { closest.x = extents.x; } else { closest.x = -extents.x; } } else { // 沿Z轴 if (n.z > 0) { closest.z = extents.z; } else { closest.z = -extents.z; } } horizontalP = space + (n - closest).magnitude; } else { horizontalP = space - (n - closest).magnitude; } if (Mathf.Sign(topA - topB) != Mathf.Sign(bottomB - bottomA)) { // 斜向相撞 if (topB > topA) { verticalP = topA - bottomB; } else { verticalP = topB - bottomA; } } if (horizontalP < verticalP) { normal = (src.Quaternion * (n - closest)).normalized; if (inside) { normal = -normal; } penetration = horizontalP; } else { normal = topB > topA ? Vector3.up : Vector3.down; penetration = verticalP; } if (normal == Vector3.zero) { normal = Vector3.up; } contact = new XContact(src, dst, normal, penetration); return(true); }
private void RenderUpcomingEvents() { string vessel_guid = vessel_.id.ToString(); double current_time = plugin_.CurrentTime(); bool should_clear_guidance = true; for (int i = 0; i < burn_editors_.Count; ++i) { NavigationManoeuvre manoeuvre = plugin_.FlightPlanGetManoeuvre(vessel_guid, i); if (manoeuvre.final_time > current_time) { if (manoeuvre.burn.initial_time > current_time) { UnityEngine.GUILayout.TextArea("Upcoming manœuvre: #" + (i + 1)); UnityEngine.GUILayout.Label( "Ignition " + FormatTimeSpan(TimeSpan.FromSeconds( current_time - manoeuvre.burn.initial_time))); } else { UnityEngine.GUILayout.TextArea("Ongoing manœuvre: #" + (i + 1)); UnityEngine.GUILayout.Label( "Cutoff " + FormatTimeSpan(TimeSpan.FromSeconds( current_time - manoeuvre.final_time))); } show_guidance_ = UnityEngine.GUILayout.Toggle(show_guidance_, "Show on navball"); if (show_guidance_ && !double.IsNaN(manoeuvre.inertial_direction.x + manoeuvre.inertial_direction.y + manoeuvre.inertial_direction.z)) { if (guidance_node_ == null) { guidance_node_ = vessel_.patchedConicSolver.AddManeuverNode( manoeuvre.burn.initial_time); } Vector3d stock_velocity_at_node_time = vessel_.orbit.getOrbitalVelocityAtUT( manoeuvre.burn.initial_time).xzy; Vector3d stock_displacement_from_parent_at_node_time = vessel_.orbit.getRelativePositionAtUT( manoeuvre.burn.initial_time).xzy; UnityEngine.Quaternion stock_frenet_frame_to_world = UnityEngine.Quaternion.LookRotation( stock_velocity_at_node_time, Vector3d.Cross(stock_velocity_at_node_time, stock_displacement_from_parent_at_node_time)); guidance_node_.OnGizmoUpdated( ((Vector3d)manoeuvre.burn.delta_v).magnitude * (Vector3d)(stock_frenet_frame_to_world.Inverse() * (Vector3d)manoeuvre.inertial_direction), manoeuvre.burn.initial_time); should_clear_guidance = false; } break; } } if (should_clear_guidance && guidance_node_ != null) { vessel_.patchedConicSolver.RemoveManeuverNode(guidance_node_); guidance_node_ = null; } }
static Matrix4x4 ToMatrix(Vector3 position, Quaternion rotation, Vector2 scale) { return(Matrix4x4.TRS(position, Quaternion.Inverse(rotation), new Vector3(scale.x, scale.y, -1))); }
public static bool GenerateExtrudedShapeAsset(CSGBrushMeshAsset brushMeshAsset, Curve2D shape, Path path, int curveSegments, CSGSurfaceAsset[] surfaceAssets, ref SurfaceDescription[] surfaceDescriptions) { var shapeVertices = new List <Vector2>(); var shapeSegmentIndices = new List <int>(); GetPathVertices(shape, curveSegments, shapeVertices, shapeSegmentIndices); Vector2[][] polygonVerticesArray; int[][] polygonIndicesArray; if (!Decomposition.ConvexPartition(shapeVertices, shapeSegmentIndices, out polygonVerticesArray, out polygonIndicesArray)) { return(false); } // TODO: make each extruded quad split into two triangles when it's not a perfect plane, // split it to make sure it's convex // TODO: make it possible to smooth (parts) of the shape // TODO: make materials work well // TODO: make it possible to 'draw' shapes on any surface // TODO: make path work as a spline, with subdivisions // TODO: make this work well with twisted rotations // TODO: make shape/path subdivisions be configurable / automatic var subMeshes = new List <CSGBrushSubMesh>(); for (int p = 0; p < polygonVerticesArray.Length; p++) { var polygonVertices = polygonVerticesArray[p]; var segmentIndices = polygonIndicesArray[p]; var shapeSegments = polygonVertices.Length; for (int s = 0; s < path.segments.Length - 1; s++) { var pathPointA = path.segments[s]; var pathPointB = path.segments[s + 1]; int subSegments = 1; var offsetQuaternion = pathPointB.rotation * Quaternion.Inverse(pathPointA.rotation); var offsetEuler = offsetQuaternion.eulerAngles; if (offsetEuler.x > 180) { offsetEuler.x = 360 - offsetEuler.x; } if (offsetEuler.y > 180) { offsetEuler.y = 360 - offsetEuler.y; } if (offsetEuler.z > 180) { offsetEuler.z = 360 - offsetEuler.z; } var maxAngle = Mathf.Max(offsetEuler.x, offsetEuler.y, offsetEuler.z); if (maxAngle != 0) { subSegments = Mathf.Max(1, (int)Mathf.Ceil(maxAngle / 5)); } if ((pathPointA.scale.x / pathPointA.scale.y) != (pathPointB.scale.x / pathPointB.scale.y) && (subSegments & 1) == 1) { subSegments += 1; } for (int n = 0; n < subSegments; n++) { var matrix0 = PathPoint.Lerp(ref path.segments[s], ref path.segments[s + 1], n / (float)subSegments); var matrix1 = PathPoint.Lerp(ref path.segments[s], ref path.segments[s + 1], (n + 1) / (float)subSegments); // TODO: this doesn't work if top and bottom polygons intersect // => need to split into two brushes then, invert one of the two brushes var invertDot = Vector3.Dot(matrix0.MultiplyVector(Vector3.forward).normalized, (matrix1.MultiplyPoint(shapeVertices[0]) - matrix0.MultiplyPoint(shapeVertices[0])).normalized); if (invertDot == 0.0f) { continue; } Vector3[] vertices; if (invertDot < 0) { var m = matrix0; matrix0 = matrix1; matrix1 = m; } if (!GetExtrudedVertices(polygonVertices, matrix0, matrix1, out vertices)) { continue; } var subMesh = new CSGBrushSubMesh(); CreateExtrudedSubMesh(subMesh, shapeSegments, segmentIndices, 0, 1, vertices, surfaceAssets, surfaceDescriptions); subMeshes.Add(subMesh); } } } brushMeshAsset.SubMeshes = subMeshes.ToArray(); brushMeshAsset.CalculatePlanes(); brushMeshAsset.OnValidate(); brushMeshAsset.SetDirty(); return(true); }