This commit is contained in:
DESKTOP-5RP3AKU\Jisol
2024-01-31 06:12:18 +08:00
parent 9cae51746d
commit 6a66bc165f
232 changed files with 118535 additions and 10519 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 19f77faebdc567b488940d510d374f17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63defbb40447be042a23a204dbabfea1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 05b2d911c4f5b3942a29cd70d1ac526b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 924801193ae5c634d814f3e2efdf5e5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Physics.Custom.EditModeTests")]

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 016d8a3094fcbdf44961b10be9ac5c56
timeCreated: 1680116734

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df54d2bd08525d5449ae01cf06c0117f
timeCreated: 1678291379

View File

@@ -0,0 +1,107 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Unity.Physics.Editor
{
class BeveledBoxBoundsHandle : BoxBoundsHandle
{
public float bevelRadius
{
get => math.min(m_BevelRadius, math.cmin(GetSize()) * 0.5f);
set
{
if (!m_IsDragging)
m_BevelRadius = math.max(0f, value);
}
}
float m_BevelRadius = ConvexHullGenerationParameters.Default.BevelRadius;
bool m_IsDragging = false;
static PhysicsBoundsHandleUtility.Corner[] s_Corners = new PhysicsBoundsHandleUtility.Corner[8];
public new void DrawHandle()
{
int prevHotControl = GUIUtility.hotControl;
if (prevHotControl == 0)
m_IsDragging = false;
base.DrawHandle();
int currHotcontrol = GUIUtility.hotControl;
if (currHotcontrol != prevHotControl)
m_IsDragging = currHotcontrol != 0;
}
protected override void DrawWireframe()
{
if (this.bevelRadius <= 0f)
{
base.DrawWireframe();
return;
}
var cameraPosition = float3.zero;
var cameraForward = new float3 { z = 1f };
if (Camera.current != null)
{
cameraPosition = Camera.current.transform.position;
cameraForward = Camera.current.transform.forward;
}
var bounds = new Bounds(this.center, this.size);
bool isCameraInsideBox = Camera.current != null && bounds.Contains(Handles.inverseMatrix.MultiplyPoint(cameraPosition));
var bevelRadius = this.bevelRadius;
var origin = (float3)this.center;
var size = (float3)this.size;
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), bevelRadius, 0, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(-1f, 1f, 1f), bevelRadius, 0, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), bevelRadius, 1, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, -1f, 1f), bevelRadius, 1, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), bevelRadius, 2, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, -1f), bevelRadius, 2, axes, isCameraInsideBox);
var corner = 0.5f * size - new float3(1f) * bevelRadius;
var axisx = new float3(1f, 0f, 0f);
var axisy = new float3(0f, 1f, 0f);
var axisz = new float3(0f, 0f, 1f);
// Since the geometry is transformed by Handles.matrix during rendering, we transform the camera position
// by the inverse matrix so that the two-shaded wireframe will have the proper orientation.
var invMatrix = Handles.inverseMatrix;
cameraPosition = invMatrix.MultiplyPoint(cameraPosition);
cameraForward = invMatrix.MultiplyVector(cameraForward);
var cameraOrtho = Camera.current == null || Camera.current.orthographic;
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, 1f, -1f), quaternion.LookRotation(-axisz, axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[0]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, 1f, 1f), quaternion.LookRotation(-axisx, axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[1]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, 1f, 1f), quaternion.LookRotation(axisz, axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[2]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, 1f, -1f), quaternion.LookRotation(axisx, axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[3]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, -1f, -1f), quaternion.LookRotation(-axisx, -axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[4]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, -1f, 1f), quaternion.LookRotation(axisz, -axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[5]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, -1f, 1f), quaternion.LookRotation(axisx, -axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[6]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, -1f, -1f), quaternion.LookRotation(-axisz, -axisy), cameraPosition, cameraForward, cameraOrtho, bevelRadius, out s_Corners[7]);
for (int i = 0; i < s_Corners.Length; i++)
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[i], true);
// Draw the horizon edges between the corners
for (int upA = 3, upB = 0; upB < 4; upA = upB, upB++)
{
int dnA = upA + 4;
int dnB = upB + 4;
if (s_Corners[upA].splitAxis[0].z && s_Corners[upB].splitAxis[1].x) Handles.DrawLine(s_Corners[upA].points[0], s_Corners[upB].points[1]);
if (s_Corners[upA].splitAxis[1].z && s_Corners[upB].splitAxis[0].x) Handles.DrawLine(s_Corners[upA].points[1], s_Corners[upB].points[0]);
if (s_Corners[dnA].splitAxis[0].x && s_Corners[dnB].splitAxis[1].z) Handles.DrawLine(s_Corners[dnA].points[0], s_Corners[dnB].points[1]);
if (s_Corners[dnA].splitAxis[1].x && s_Corners[dnB].splitAxis[0].z) Handles.DrawLine(s_Corners[dnA].points[1], s_Corners[dnB].points[0]);
if (s_Corners[dnA].splitAxis[0].y && s_Corners[upA].splitAxis[1].y) Handles.DrawLine(s_Corners[dnA].points[0], s_Corners[upA].points[1]);
if (s_Corners[dnA].splitAxis[1].y && s_Corners[upA].splitAxis[0].y) Handles.DrawLine(s_Corners[dnA].points[1], s_Corners[upA].points[0]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be997ba7f25d7f44b8996dfac81c1a3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,350 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Unity.Physics.Editor
{
class BeveledCylinderBoundsHandle : PrimitiveBoundsHandle
{
public float bevelRadius
{
get => math.min(m_BevelRadius, math.cmin(GetSize()) * 0.5f);
set
{
if (!m_IsDragging)
m_BevelRadius = math.max(0f, value);
}
}
float m_BevelRadius = ConvexHullGenerationParameters.Default.BevelRadius;
bool m_IsDragging = false;
public float height
{
get => GetSize().z;
set
{
var size = GetSize();
size.z = math.max(0f, value);
SetSize(size);
}
}
public float radius
{
get
{
var size = (float3)GetSize();
var diameter = 0f;
// only consider size values on enabled axes
if (IsAxisEnabled(0)) diameter = math.max(diameter, math.abs(size.x));
else if (IsAxisEnabled(1)) diameter = math.max(diameter, math.abs(size.y));
return diameter * 0.5f;
}
set
{
var size = (float3)GetSize();
size.x = size.y = math.max(0f, value * 2.0f);
SetSize(size);
}
}
public int sideCount
{
get => m_SideCount;
set
{
if (value == m_SideCount)
return;
m_SideCount = value;
Array.Resize(ref m_TopPoints, m_SideCount * 2);
Array.Resize(ref m_BottomPoints, m_SideCount * 2);
Array.Resize(ref m_Corners, m_SideCount * 2);
}
}
int m_SideCount;
PhysicsBoundsHandleUtility.Corner[] m_Corners = Array.Empty<PhysicsBoundsHandleUtility.Corner>();
Vector3[] m_TopPoints = Array.Empty<Vector3>();
Vector3[] m_BottomPoints = Array.Empty<Vector3>();
public new void DrawHandle()
{
int prevHotControl = GUIUtility.hotControl;
if (prevHotControl == 0)
m_IsDragging = false;
base.DrawHandle();
int currHotcontrol = GUIUtility.hotControl;
if (currHotcontrol != prevHotControl)
m_IsDragging = currHotcontrol != 0;
}
protected override void DrawWireframe()
{
using (new Handles.DrawingScope(Handles.matrix))
{
var backfacedColor = PhysicsBoundsHandleUtility.GetStateColor(true);
var frontfacedColor = Handles.color;
bool isCameraInsideBox = false;
var radius = this.radius;
var bevelRadius = this.bevelRadius;
var halfHeight = new float3(0f, 0f, height * 0.5f);
var ctr = (float3)center;
var halfAngleStep = math.PI / m_SideCount;
var angleStep = 2f * halfAngleStep;
const float kHalfPI = (math.PI * 0.5f);
var bottom = ctr + halfHeight + new float3 { z = bevelRadius };
var top = ctr - halfHeight - new float3 { z = bevelRadius };
var tangent = new float3(1, 0, 0);
var binormal = new float3(0, 1, 0);
var topBackFaced = PhysicsBoundsHandleUtility.IsBackfaced(top, -tangent, binormal, axes, isCameraInsideBox);
var bottomBackFaced = PhysicsBoundsHandleUtility.IsBackfaced(bottom, tangent, binormal, axes, isCameraInsideBox);
var cameraCenter = float3.zero;
var cameraForward = new float3 { z = 1f };
if (Camera.current != null)
{
cameraCenter = Camera.current.transform.position;
cameraForward = Camera.current.transform.forward;
}
// Since the geometry is transformed by Handles.matrix during rendering, we transform the camera position
// by the inverse matrix so that the two-shaded wireframe will have the proper orientation.
var invMatrix = Handles.inverseMatrix;
cameraCenter = invMatrix.MultiplyPoint(cameraCenter);
cameraForward = invMatrix.MultiplyVector(cameraForward);
var cameraOrtho = Camera.current != null && Camera.current.orthographic;
var noSides = (radius - bevelRadius) < PhysicsBoundsHandleUtility.kDistanceEpsilon;
var up = new float3(0, 0, -1f);
var t = ((m_SideCount - 2) * angleStep);
var xyAngle0 = new float3(math.cos(t), math.sin(t), 0f);
t = (m_SideCount - 1) * angleStep;
var xyAngle1 = new float3(math.cos(t), math.sin(t), 0f);
var sideways1 = new float3(math.cos(t + kHalfPI - halfAngleStep), math.sin(t + kHalfPI - halfAngleStep), 0f);
var direction1 = new float3(math.cos(t + halfAngleStep), math.sin(t + halfAngleStep), 0f);
var bevelGreaterThanZero = bevelRadius > 0f;
var bevelLessThanCylinderRadius = bevelRadius < radius;
for (var i = 0; i < m_SideCount; ++i)
{
t = i * angleStep;
var xyAngle2 = new float3(math.cos(t), math.sin(t), 0f);
var sideways2 = new float3(math.cos(t + kHalfPI - halfAngleStep), math.sin(t + kHalfPI - halfAngleStep), 0f);
var direction2 = new float3(math.cos(t + halfAngleStep), math.sin(t + halfAngleStep), 0f);
var offset0 = xyAngle0 * (radius - bevelRadius);
var offset1 = xyAngle1 * (radius - bevelRadius);
var offset2 = xyAngle2 * (radius - bevelRadius);
var top1 = ctr + offset1 - (halfHeight - new float3 { z = bevelRadius });
var bottom1 = ctr + offset1 + (halfHeight - new float3 { z = bevelRadius });
var top2 = ctr + offset2 - (halfHeight - new float3 { z = bevelRadius });
var bottom2 = ctr + offset2 + (halfHeight - new float3 { z = bevelRadius });
var startOffset = direction1 * bevelRadius;
if (bevelGreaterThanZero)
{
var upOffset = up * bevelRadius;
// top/bottom caps
if (bevelLessThanCylinderRadius)
{
Handles.color = topBackFaced ? backfacedColor : frontfacedColor;
Handles.DrawLine(top1 + upOffset, top2 + upOffset);
Handles.color = bottomBackFaced ? backfacedColor : frontfacedColor;
Handles.DrawLine(bottom1 - upOffset, bottom2 - upOffset);
}
var currSideMidPoint = ctr + ((top1 + bottom1 + top2 + bottom2) * 0.25f) + startOffset;
var currSideBackFaced = PhysicsBoundsHandleUtility.IsBackfaced(currSideMidPoint, up, sideways2, axes, isCameraInsideBox);
Handles.color = currSideBackFaced ? backfacedColor : frontfacedColor;
if (!noSides)
{
// Square side of bevelled cylinder
Handles.DrawLine(top2 + startOffset, bottom2 + startOffset);
Handles.DrawLine(bottom2 + startOffset, bottom1 + startOffset);
Handles.DrawLine(bottom1 + startOffset, top1 + startOffset);
Handles.DrawLine(top1 + startOffset, top2 + startOffset);
}
else
{
// Square side of bevelled cylinder, when squashed to a single line
Handles.DrawLine(top2 + startOffset, bottom2 + startOffset);
}
}
else
{
var top0 = ctr + offset0 - (halfHeight - new float3 { z = bevelRadius });
var bottom0 = ctr + offset0 + (halfHeight - new float3 { z = bevelRadius });
var prevMidPoint = ctr + ((top0 + top1 + bottom0 + bottom1) * 0.25f) + startOffset;
var prevSideBackFaced = PhysicsBoundsHandleUtility.IsBackfaced(prevMidPoint, up, sideways1, axes, isCameraInsideBox);
var currMidPoint = ctr + ((top1 + top2 + bottom1 + bottom2) * 0.25f) + startOffset;
var currSideBackFaced = PhysicsBoundsHandleUtility.IsBackfaced(currMidPoint, up, sideways2, axes, isCameraInsideBox);
// Square side of bevelled cylinder
Handles.color = (currSideBackFaced && prevSideBackFaced) ? backfacedColor : frontfacedColor;
Handles.DrawLine(bottom1 + startOffset, top1 + startOffset);
Handles.color = (currSideBackFaced && topBackFaced) ? backfacedColor : frontfacedColor;
Handles.DrawLine(top1 + startOffset, top2 + startOffset);
Handles.color = (currSideBackFaced && bottomBackFaced) ? backfacedColor : frontfacedColor;
Handles.DrawLine(bottom2 + startOffset, bottom1 + startOffset);
}
if (bevelGreaterThanZero)
{
Handles.color = frontfacedColor;
var cornerIndex0 = i;
var cornerIndex1 = i + m_SideCount;
{
var orientation = quaternion.LookRotation(xyAngle2, up);
var cornerNormal = math.normalize(math.mul(orientation, new float3(0f, 1f, 1f)));
PhysicsBoundsHandleUtility.CalculateCornerHorizon(top2,
new float3x3(direction1, up, direction2),
cornerNormal, cameraCenter, cameraForward, cameraOrtho,
bevelRadius, out m_Corners[cornerIndex0]);
}
{
var orientation = quaternion.LookRotation(xyAngle2, -up);
var cornerNormal = math.normalize(math.mul(orientation, new float3(0f, 1f, 1f)));
PhysicsBoundsHandleUtility.CalculateCornerHorizon(bottom2,
new float3x3(direction2, -up, direction1),
cornerNormal, cameraCenter, cameraForward, cameraOrtho,
bevelRadius, out m_Corners[cornerIndex1]);
}
}
direction1 = direction2;
sideways1 = sideways2;
xyAngle0 = xyAngle1;
xyAngle1 = xyAngle2;
}
if (bevelGreaterThanZero)
{
Handles.color = frontfacedColor;
for (int a = m_SideCount - 1, b = 0; b < m_SideCount; a = b, ++b)
{
var up0 = a;
var dn0 = a + m_SideCount;
var up1 = b;
var dn1 = b + m_SideCount;
// Side horizon on vertical curved edge
if (m_Corners[up1].splitCount > 1 &&
m_Corners[dn1].splitCount > 1)
{
if ((m_Corners[up1].splitAxis[0].y || m_Corners[up1].splitAxis[1].y) &&
(m_Corners[dn1].splitAxis[0].y || m_Corners[dn1].splitAxis[1].y))
{
var point0 = m_Corners[up1].splitAxis[0].y ? m_Corners[up1].points[0] : m_Corners[up1].points[1];
var point1 = m_Corners[dn1].splitAxis[0].y ? m_Corners[dn1].points[0] : m_Corners[dn1].points[1];
Handles.DrawLine(point0, point1);
}
}
// Top horizon on horizontal curved edge
if (m_Corners[up0].splitCount > 1 &&
m_Corners[up1].splitCount > 1)
{
if ((m_Corners[up0].splitAxis[0].x || m_Corners[up0].splitAxis[1].x) &&
(m_Corners[up1].splitAxis[0].z || m_Corners[up1].splitAxis[1].z))
{
var point0 = m_Corners[up0].splitAxis[0].x ? m_Corners[up0].points[0] : m_Corners[up0].points[1];
var point1 = m_Corners[up1].splitAxis[0].z ? m_Corners[up1].points[0] : m_Corners[up1].points[1];
Handles.DrawLine(point0, point1);
}
}
// Bottom horizon on horizontal curved edge
if (m_Corners[dn0].splitCount > 1 &&
m_Corners[dn1].splitCount > 1)
{
if ((m_Corners[dn0].splitAxis[0].z || m_Corners[dn0].splitAxis[1].z) &&
(m_Corners[dn1].splitAxis[0].x || m_Corners[dn1].splitAxis[1].x))
{
var point0 = m_Corners[dn0].splitAxis[0].z ? m_Corners[dn0].points[0] : m_Corners[dn0].points[1];
var point1 = m_Corners[dn1].splitAxis[0].x ? m_Corners[dn1].points[0] : m_Corners[dn1].points[1];
Handles.DrawLine(point0, point1);
}
}
}
for (var i = 0; i < m_Corners.Length; ++i)
PhysicsBoundsHandleUtility.DrawCorner(m_Corners[i], new bool3(true, true, !noSides));
}
}
}
protected override Bounds OnHandleChanged(HandleDirection handle, Bounds boundsOnClick, Bounds newBounds)
{
const int k_DirectionX = 0;
const int k_DirectionY = 1;
const int k_DirectionZ = 2;
var changedAxis = k_DirectionX;
var otherRadiusAxis = k_DirectionY;
switch (handle)
{
case HandleDirection.NegativeY:
case HandleDirection.PositiveY:
changedAxis = k_DirectionY;
otherRadiusAxis = k_DirectionX;
break;
case HandleDirection.NegativeZ:
case HandleDirection.PositiveZ:
changedAxis = k_DirectionZ;
break;
}
var upperBound = newBounds.max;
var lowerBound = newBounds.min;
var convexDiameter = 2f * bevelRadius;
// ensure changed dimension cannot be made less than convex diameter
if (upperBound[changedAxis] - lowerBound[changedAxis] < convexDiameter)
{
switch (handle)
{
case HandleDirection.PositiveX:
case HandleDirection.PositiveY:
case HandleDirection.PositiveZ:
upperBound[changedAxis] = lowerBound[changedAxis] + convexDiameter;
break;
default:
lowerBound[changedAxis] = upperBound[changedAxis] - convexDiameter;
break;
}
}
// ensure radius changes uniformly
if (changedAxis != k_DirectionZ)
{
var rad = 0.5f * (upperBound[changedAxis] - lowerBound[changedAxis]);
lowerBound[otherRadiusAxis] = center[otherRadiusAxis] - rad;
upperBound[otherRadiusAxis] = center[otherRadiusAxis] + rad;
}
return new Bounds((upperBound + lowerBound) * 0.5f, upperBound - lowerBound);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f5e502486013d44c9cc7466f6c3bcd1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,343 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
using static UnityEditor.IMGUI.Controls.PrimitiveBoundsHandle;
namespace Unity.Physics.Editor
{
static class PhysicsBoundsHandleUtility
{
internal const float kBackfaceAlphaMultiplier = 0.2f;
const float kDegreeEpsilon = 0.001f;
public const float kDistanceEpsilon = 0.0001f;
public static bool IsBackfaced(float3 localPos, float3 localTangent, float3 localBinormal, Axes axes, bool isCameraInsideBox)
{
// if inside the box then ignore back facing alpha multiplier (otherwise all handles will look disabled)
if (isCameraInsideBox || axes != Axes.All)
return false;
// use tangent and binormal to calculate normal in case handle matrix is skewed
float3 worldTangent = math.normalize(Handles.matrix.MultiplyVector(localTangent));
float3 worldBinormal = math.normalize(Handles.matrix.MultiplyVector(localBinormal));
float3 worldDir = math.normalize(math.cross(worldTangent, worldBinormal));
// adjust color if handle is back facing
float cosV;
var currentCamera = Camera.current;
if (currentCamera != null && !currentCamera.orthographic)
{
float3 cameraPos = currentCamera.transform.position;
float3 worldPos = Handles.matrix.MultiplyPoint(localPos);
cosV = math.dot(math.normalize(cameraPos - worldPos), worldDir);
}
else
{
float3 cameraForward = currentCamera == null ? Vector3.forward : currentCamera.transform.forward;
cosV = math.dot(-cameraForward, worldDir);
}
return cosV < -0.0001f;
}
public static Color GetStateColor(bool isBackfaced)
{
float alphaMultiplier = isBackfaced ? kBackfaceAlphaMultiplier : 1f;
return Handles.color * new Color(1f, 1f, 1f, alphaMultiplier);
}
static void AdjustMidpointHandleColor(bool isBackfaced)
{
Handles.color = GetStateColor(isBackfaced);
}
static readonly Vector3[] s_FacePoints = new Vector3[8];
static readonly Vector3[] s_LinePoints = new Vector3[2];
static readonly int[] k_NextAxis = { 1, 2, 0 };
public static void DrawFace(float3 center, float3 size, float cornerRadius, int normalAxis, Axes axes, bool isCameraInsideBox)
{
// 0 = 0 1 2
// 1 = 1 2 0
// 2 = 2 0 1
int a = normalAxis;
int b = k_NextAxis[a];
int c = k_NextAxis[b];
cornerRadius = math.abs(cornerRadius);
size *= 0.5f;
var normal = new float3 { [a] = size[a] };
var ctr = center + normal;
size -= new float3(cornerRadius);
// check if our face is a point
if (math.abs(size[c]) < kDistanceEpsilon &&
math.abs(size[b]) < kDistanceEpsilon)
return;
Vector3[] points;
// check if our face is a line or not
if (math.abs(size[c]) >= kDistanceEpsilon &&
math.abs(size[b]) >= kDistanceEpsilon)
{
var i = 0;
s_FacePoints[i++] = ctr + new float3 { [b] = size[b], [c] = size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = -size[b], [c] = size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = -size[b], [c] = size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = -size[b], [c] = -size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = -size[b], [c] = -size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = size[b], [c] = -size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = size[b], [c] = -size[c] };
s_FacePoints[i++] = ctr + new float3 { [b] = size[b], [c] = size[c] };
points = s_FacePoints;
}
else if (math.abs(size[c]) >= kDistanceEpsilon)
{
var i = 0;
s_LinePoints[i++] = ctr + new float3 { [b] = size[b], [c] = size[c] };
s_LinePoints[i++] = ctr + new float3 { [b] = size[b], [c] = -size[c] };
points = s_LinePoints;
}
else
{
var i = 0;
s_LinePoints[i++] = ctr + new float3 { [c] = size[c], [b] = size[b] };
s_LinePoints[i++] = ctr + new float3 { [c] = size[c], [b] = -size[b] };
points = s_LinePoints;
}
float3 tangent, biNormal;
if (size[c] > 0)
{
tangent = math.cross(normal, math.normalizesafe(new float3 { [c] = size[c] }));
biNormal = math.cross(normal, tangent);
}
else
{
tangent = math.cross(normal, math.normalizesafe(new float3 { [b] = size[b] }));
biNormal = math.cross(normal, tangent);
}
using (new Handles.DrawingScope(Handles.color))
{
AdjustMidpointHandleColor(IsBackfaced(ctr, tangent, biNormal, axes, isCameraInsideBox));
Handles.DrawLines(points);
}
}
public struct Corner
{
public float3 angle;
public float3x2 intersections;
public float3x2 points;
public float3x3 axes;
public float3x3 normals;
public bool3x2 splitAxis;
public int splitCount;
public float3 position;
public float radius;
public bool isBackFaced;
public float3 cameraForward;
}
public static void CalculateCornerHorizon(float3 cornerPosition, quaternion orientation, float3 cameraCenter, float3 cameraForward, bool cameraOrtho, float radius, out Corner corner)
{
var axisx = new float3(1f, 0f, 0f);
var axisy = new float3(0f, 1f, 0f);
var axisz = new float3(0f, 0f, 1f);
// a vector pointing away from the center of the corner
var cornerNormal = math.normalize(math.mul(orientation, new float3(1f, 1f, 1f)));
var axes = math.mul(new float3x3(orientation), new float3x3(axisx, axisy, axisz));
CalculateCornerHorizon(cornerPosition,
axes,
cornerNormal,
cameraCenter, cameraForward, cameraOrtho,
radius, out corner);
}
public static void CalculateCornerHorizon(float3 cornerPosition, float3x3 axes, float3 cornerNormal, float3 cameraCenter, float3 cameraForward, bool cameraOrtho, float radius, out Corner corner)
{
var cameraToCenter = cornerPosition - cameraCenter; // vector from camera to center
var sqrRadius = radius * radius;
var sqrDistCameraToCenter = math.lengthsq(cameraToCenter);
var sqrOffset = (sqrRadius * sqrRadius / sqrDistCameraToCenter); // squared distance from actual center to drawn disc center
if (!cameraOrtho)
cameraForward = cameraToCenter;
var normals = new float3x3
{
c0 = math.normalize(math.cross(axes[1], axes[2])),
c1 = math.normalize(math.cross(axes[2], axes[0])),
c2 = math.normalize(math.cross(axes[0], axes[1]))
};
corner = new Corner
{
angle = new float3(
Vector3.Angle(axes[0], axes[1]),
Vector3.Angle(axes[1], axes[2]),
Vector3.Angle(axes[2], axes[0])
),
intersections = default,
points = default,
splitAxis = default,
axes = axes,
normals = normals,
position = cornerPosition,
radius = radius,
cameraForward = cameraForward,
isBackFaced = math.dot(cornerNormal, cameraForward) > 0,
splitCount = 0
};
if (math.abs(sqrDistCameraToCenter) <= sqrRadius)
return;
for (int n = 0, sign = -1; n < 2; n++, sign += 2)
{
for (int i = 0; i < 3; i++)
{
var axis1 = normals[i] * sign;
var axis2 = axes[(i + 1) % 3] * sign;
var axis3 = axes[(i + 2) % 3] * sign;
var Q = Vector3.Angle(cameraForward, axis1);
var f = math.tan(math.radians(90 - math.min(Q, 180 - Q)));
var g = sqrOffset + f * f * sqrOffset;
if (g >= sqrRadius)
continue;
var e = math.degrees(math.asin(math.sqrt(g) / radius));
var vectorToPointOnHorizon = Quaternion.AngleAxis(e, axis1) * math.normalize(math.cross(axis1, cameraForward));
vectorToPointOnHorizon = math.normalize(Vector3.ProjectOnPlane(vectorToPointOnHorizon, axis1));
var intersectionDirection = vectorToPointOnHorizon;
var angle1 = Vector3.SignedAngle(axis2, intersectionDirection, axis1);
var angle2 = Vector3.SignedAngle(axis3, intersectionDirection, axis1);
if (angle1 <= 0 || angle2 >= 0)
continue;
var point = corner.position + (float3)(intersectionDirection * radius);
if (corner.splitCount < 2)
{
corner.splitAxis[corner.splitCount][i] = true;
corner.intersections[corner.splitCount] = intersectionDirection;
corner.points[corner.splitCount] = point;
corner.splitCount++;
}
}
}
if (!math.any(corner.splitAxis[0]) &&
!math.any(corner.splitAxis[1]))
{
corner.splitCount = 0;
corner.splitAxis[0] = false;
corner.splitAxis[1] = false;
}
}
public static void DrawCorner(Corner corner, bool3 showAxis)
{
var color = Handles.color;
var axes = corner.axes;
var intersections = corner.intersections;
var normals = corner.normals;
var origin = corner.position;
var radius = corner.radius;
if (corner.splitCount <= 1)
{
AdjustMidpointHandleColor(corner.isBackFaced);
if (showAxis[0]) Handles.DrawWireArc(origin, normals[0], axes[1], corner.angle[1], radius);
if (showAxis[1]) Handles.DrawWireArc(origin, normals[1], axes[2], corner.angle[2], radius);
if (showAxis[2]) Handles.DrawWireArc(origin, normals[2], axes[0], corner.angle[0], radius);
}
else
{
var angleLength = Vector3.SignedAngle(Vector3.ProjectOnPlane(intersections[0], corner.cameraForward),
Vector3.ProjectOnPlane(intersections[1], corner.cameraForward), corner.cameraForward);
bool reversePolarity = angleLength < 0;
if (reversePolarity)
Handles.DrawWireArc(origin, corner.cameraForward, corner.points[1] - origin, -angleLength, radius);
else
Handles.DrawWireArc(origin, corner.cameraForward, corner.points[0] - origin, angleLength, radius);
var backfacedColor = GetStateColor(true);
var axesBackfaced = new bool3(math.length(intersections[0] - axes[0]) < kDistanceEpsilon || math.length(intersections[1] - axes[0]) < kDistanceEpsilon,
math.length(intersections[0] - axes[1]) < kDistanceEpsilon || math.length(intersections[1] - axes[1]) < kDistanceEpsilon,
math.length(intersections[0] - axes[2]) < kDistanceEpsilon || math.length(intersections[1] - axes[2]) < kDistanceEpsilon);
var color1 = reversePolarity ? color : backfacedColor;
var color2 = reversePolarity ? backfacedColor : color;
for (int A = 1, B = 2, C = 0; C < 3; A = B, B = C, C++)
{
if (corner.splitAxis[0][C] == corner.splitAxis[1][C])
{
if (!axesBackfaced[A]) { angleLength = Vector3.Angle(intersections[0], axes[A]); axesBackfaced[A] = (angleLength<kDegreeEpsilon || angleLength> corner.angle[C] - kDegreeEpsilon); }
if (!axesBackfaced[B]) { angleLength = Vector3.Angle(intersections[1], axes[A]); axesBackfaced[B] = (angleLength<kDegreeEpsilon || angleLength> corner.angle[C] - kDegreeEpsilon); }
}
else if (corner.splitAxis[0][C])
{
if (showAxis[C])
{
angleLength = Vector3.Angle(intersections[0], axes[A]);
Handles.color = color1; Handles.DrawWireArc(origin, normals[C], intersections[0], -angleLength, radius);
Handles.color = color2; Handles.DrawWireArc(origin, normals[C], intersections[0], corner.angle[A] - angleLength, radius);
}
axesBackfaced[A] = true;
}
else
//if (corner.splitAxis[1][C])
{
if (showAxis[C])
{
angleLength = Vector3.Angle(intersections[1], axes[A]);
Handles.color = color2; Handles.DrawWireArc(origin, normals[C], intersections[1], -angleLength, radius);
Handles.color = color1; Handles.DrawWireArc(origin, normals[C], intersections[1], corner.angle[A] - angleLength, radius);
}
axesBackfaced[B] = true;
}
}
// check for singularity
if (math.all(axesBackfaced))
axesBackfaced = corner.isBackFaced;
for (int A = 1, B = 2, C = 0; C < 3; A = B, B = C, C++)
{
if (!showAxis[C])
continue;
if (corner.splitAxis[0][C] == corner.splitAxis[1][C])
{
Handles.color = (axesBackfaced[B] && axesBackfaced[A]) ? color1 : color2;
Handles.DrawWireArc(origin, normals[C], axes[A], corner.angle[A], radius);
}
}
}
Handles.color = color;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3c60d0e0940c3c419ec030b9685c622
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Unity.Physics.Editor
{
class PhysicsCapsuleBoundsHandle : CapsuleBoundsHandle
{
static PhysicsBoundsHandleUtility.Corner[] s_Corners = new PhysicsBoundsHandleUtility.Corner[8];
protected override void DrawWireframe()
{
if (this.radius <= 0f)
{
base.DrawWireframe();
return;
}
var cameraPos = default(float3);
var cameraFwd = new float3 { z = 1f };
var cameraOrtho = true;
if (Camera.current != null)
{
cameraPos = Camera.current.transform.position;
cameraFwd = Camera.current.transform.forward;
cameraOrtho = Camera.current.orthographic;
}
var size = new float3(this.radius * 2f, this.radius * 2f, height);
var radius = this.radius;
var origin = (float3)this.center;
var bounds = new Bounds(this.center, size);
// Since the geometry is transformed by Handles.matrix during rendering, we transform the camera position
// by the inverse matrix so that the two-shaded wireframe will have the proper orientation.
var invMatrix = Handles.inverseMatrix;
var cameraCenter = (float3)invMatrix.MultiplyPoint(cameraPos);
var cameraForward = (float3)invMatrix.MultiplyVector(cameraFwd);
bool isCameraInsideBox = Camera.current != null
&& bounds.Contains(invMatrix.MultiplyPoint(cameraPos));
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), radius, 0, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(-1f, 1f, 1f), radius, 0, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), radius, 1, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, -1f, 1f), radius, 1, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, 1f), radius, 2, axes, isCameraInsideBox);
PhysicsBoundsHandleUtility.DrawFace(origin, size * new float3(1f, 1f, -1f), radius, 2, axes, isCameraInsideBox);
var corner = 0.5f * size - new float3(1f) * radius;
var axisx = new float3(1f, 0f, 0f);
var axisy = new float3(0f, 1f, 0f);
var axisz = new float3(0f, 0f, 1f);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, 1f, -1f), quaternion.LookRotation(-axisz, axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[0]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, 1f, 1f), quaternion.LookRotation(-axisx, axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[1]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, 1f, 1f), quaternion.LookRotation(axisz, axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[2]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, 1f, -1f), quaternion.LookRotation(axisx, axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[3]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, -1f, -1f), quaternion.LookRotation(-axisx, -axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[4]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(-1f, -1f, 1f), quaternion.LookRotation(axisz, -axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[5]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, -1f, 1f), quaternion.LookRotation(axisx, -axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[6]);
PhysicsBoundsHandleUtility.CalculateCornerHorizon(origin + corner * new float3(1f, -1f, -1f), quaternion.LookRotation(-axisz, -axisy), cameraCenter, cameraForward, cameraOrtho, radius, out s_Corners[7]);
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[0], new bool3(false, true, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[3], new bool3(true, false, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[4], new bool3(true, false, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[7], new bool3(false, true, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[1], new bool3(true, false, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[2], new bool3(false, true, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[5], new bool3(false, true, true));
PhysicsBoundsHandleUtility.DrawCorner(s_Corners[6], new bool3(true, false, true));
// Draw the horizon edges between the corners
for (int upA = 3, upB = 0; upB < 4; upA = upB, upB++)
{
int dnA = upA + 4;
int dnB = upB + 4;
if (s_Corners[upA].splitAxis[0].z && s_Corners[upB].splitAxis[1].x) Handles.DrawLine(s_Corners[upA].points[0], s_Corners[upB].points[1]);
if (s_Corners[upA].splitAxis[1].z && s_Corners[upB].splitAxis[0].x) Handles.DrawLine(s_Corners[upA].points[1], s_Corners[upB].points[0]);
if (s_Corners[dnA].splitAxis[0].x && s_Corners[dnB].splitAxis[1].z) Handles.DrawLine(s_Corners[dnA].points[0], s_Corners[dnB].points[1]);
if (s_Corners[dnA].splitAxis[1].x && s_Corners[dnB].splitAxis[0].z) Handles.DrawLine(s_Corners[dnA].points[1], s_Corners[dnB].points[0]);
if (s_Corners[dnA].splitAxis[0].y && s_Corners[upA].splitAxis[1].y) Handles.DrawLine(s_Corners[dnA].points[0], s_Corners[upA].points[1]);
if (s_Corners[dnA].splitAxis[1].y && s_Corners[upA].splitAxis[0].y) Handles.DrawLine(s_Corners[dnA].points[1], s_Corners[upA].points[0]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1af170fae6badc40bb2ed5376031c6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,121 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Unity.Physics.Editor
{
class PhysicsSphereBoundsHandle : SphereBoundsHandle
{
protected override void DrawWireframe()
{
bool x = IsAxisEnabled(Axes.X);
bool y = IsAxisEnabled(Axes.Y);
bool z = IsAxisEnabled(Axes.Z);
if (x && !y && !z)
Handles.DrawLine(Vector3.right * radius, Vector3.left * radius);
if (!x && y && !z)
Handles.DrawLine(Vector3.up * radius, Vector3.down * radius);
if (!x && !y && z)
Handles.DrawLine(Vector3.forward * radius, Vector3.back * radius);
const float kEpsilon = 0.000001F;
if (radius > 0)
{
var frontfacedColor = Handles.color;
var backfacedColor = Handles.color * new Color(1f, 1f, 1f, PhysicsBoundsHandleUtility.kBackfaceAlphaMultiplier);
var discVisible = new bool[]
{
y && z,
x && z,
x && y
};
var discOrientations = new float3[]
{
Vector3.right,
Vector3.up,
Vector3.forward
};
// Since the geometry is transformed by Handles.matrix during rendering, we transform the camera position
// by the inverse matrix so that the two-shaded wireframe will have the proper orientation.
var invMatrix = Handles.inverseMatrix;
var cameraCenter = Camera.current == null ? Vector3.zero : Camera.current.transform.position;
var cameraToCenter = center - invMatrix.MultiplyPoint(cameraCenter); // vector from camera to center
var sqrDistCameraToCenter = cameraToCenter.sqrMagnitude;
var sqrRadius = radius * radius; // squared radius
var isCameraOrthographic = Camera.current == null || Camera.current.orthographic;
var sqrOffset = isCameraOrthographic ? 0 : (sqrRadius * sqrRadius / sqrDistCameraToCenter); // squared distance from actual center to drawn disc center
var insideAmount = sqrOffset / sqrRadius;
if (insideAmount < 1)
{
if (math.abs(sqrDistCameraToCenter) >= kEpsilon)
{
using (new Handles.DrawingScope(frontfacedColor))
{
if (isCameraOrthographic)
{
var horizonRadius = radius;
var horizonCenter = center;
Handles.DrawWireDisc(horizonCenter, cameraToCenter, horizonRadius);
}
else
{
var horizonRadius = math.sqrt(sqrRadius - sqrOffset);
var horizonCenter = center - sqrRadius * cameraToCenter / sqrDistCameraToCenter;
Handles.DrawWireDisc(horizonCenter, cameraToCenter, horizonRadius);
}
}
var planeNormal = cameraToCenter.normalized;
for (int i = 0; i < 3; i++)
{
if (!discVisible[i])
continue;
var discOrientation = discOrientations[i];
var angleBetweenDiscAndNormal = math.acos(math.dot(discOrientation, planeNormal));
angleBetweenDiscAndNormal = (math.PI * 0.5f) - math.min(angleBetweenDiscAndNormal, math.PI - angleBetweenDiscAndNormal);
float f = math.tan(angleBetweenDiscAndNormal);
float g = math.sqrt(sqrOffset + f * f * sqrOffset) / radius;
if (g < 1)
{
var angleToHorizon = math.degrees(math.asin(g));
var discTangent = math.cross(discOrientation, planeNormal);
var vectorToPointOnHorizon = Quaternion.AngleAxis(angleToHorizon, discOrientation) * discTangent;
var horizonArcLength = (90 - angleToHorizon) * 2.0f;
using (new Handles.DrawingScope(frontfacedColor))
Handles.DrawWireArc(center, discOrientation, vectorToPointOnHorizon, horizonArcLength, radius);
using (new Handles.DrawingScope(backfacedColor))
Handles.DrawWireArc(center, discOrientation, vectorToPointOnHorizon, horizonArcLength - 360, radius);
}
else
{
using (new Handles.DrawingScope(backfacedColor))
Handles.DrawWireDisc(center, discOrientation, radius);
}
}
}
}
else
{
using (new Handles.DrawingScope(backfacedColor))
{
for (int i = 0; i < 3; i++)
{
var discOrientation = discOrientations[i];
Handles.DrawWireDisc(center, discOrientation, radius);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4f82f07aa94f9241ad081812f8c3b1a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 30413a61e5b44d54dbd680b077804e45
timeCreated: 1678288991

View File

@@ -0,0 +1,24 @@
#if UNITY_EDITOR
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(BallAndSocketJoint))]
public class BallAndSocketEditor : UnityEditor.Editor
{
protected virtual void OnSceneGUI()
{
BallAndSocketJoint ballAndSocket = (BallAndSocketJoint)target;
EditorGUI.BeginChangeCheck();
EditorUtilities.EditPivot(ballAndSocket.worldFromA, ballAndSocket.worldFromB, ballAndSocket.AutoSetConnected,
ref ballAndSocket.PositionLocal, ref ballAndSocket.PositionInConnectedEntity, ballAndSocket);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bfafdfc59379ec4c86d512554577279
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(CustomPhysicsMaterialTagNames))]
[CanEditMultipleObjects]
class CustomPhysicsMaterialTagNamesEditor : BaseEditor
{
#pragma warning disable 649
[AutoPopulate(ElementFormatString = "Custom Physics Material Tag {0}", Resizable = false, Reorderable = false)]
ReorderableList m_TagNames;
#pragma warning restore 649
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a6f5e4709a915246864e50578af0cf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,248 @@
using UnityEngine;
using Unity.Mathematics;
using static Unity.Physics.Math;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace Unity.Physics.Editor
{
/// <summary>
/// Provides utilities that use Handles to set positions and axes,
/// </summary>
public class EditorUtilities
{
// Editor for a joint pivot or pivot pair
public static void EditPivot(RigidTransform worldFromA, RigidTransform worldFromB, bool lockBtoA,
ref float3 pivotA, ref float3 pivotB, Object target)
{
EditorGUI.BeginChangeCheck();
float3 pivotAinW = Handles.PositionHandle(math.transform(worldFromA, pivotA), quaternion.identity);
float3 pivotBinW;
if (lockBtoA)
{
pivotBinW = pivotAinW;
pivotB = math.transform(math.inverse(worldFromB), pivotBinW);
}
else
{
pivotBinW = Handles.PositionHandle(math.transform(worldFromB, pivotB), quaternion.identity);
}
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Edit joint pivot");
pivotA = math.transform(math.inverse(worldFromA), pivotAinW);
pivotB = math.transform(math.inverse(worldFromB), pivotBinW);
}
Handles.DrawLine(worldFromA.pos, pivotAinW);
Handles.DrawLine(worldFromB.pos, pivotBinW);
}
// Editor for a joint axis or axis pair
public class AxisEditor
{
// Even though we're only editing axes and not rotations, we need to track a full rotation in order to keep the rotation handle stable
private quaternion m_RefA = quaternion.identity;
private quaternion m_RefB = quaternion.identity;
// Detect changes in the object being edited to reset the reference orientations
private Object m_LastTarget;
private static bool NormalizeSafe(ref float3 x)
{
float lengthSq = math.lengthsq(x);
const float epsSq = 1e-8f;
if (math.abs(lengthSq - 1) > epsSq)
{
if (lengthSq > epsSq)
{
x *= math.rsqrt(lengthSq);
}
else
{
x = new float3(1, 0, 0);
}
return true;
}
return false;
}
private static bool NormalizePerpendicular(float3 axis, ref float3 perpendicular)
{
// make sure perpendicular is actually perpendicular to direction
float dot = math.dot(axis, perpendicular);
float absDot = math.abs(dot);
if (absDot > 1.0f - 1e-5f)
{
// parallel, choose an arbitrary perpendicular
float3 dummy;
CalculatePerpendicularNormalized(axis, out perpendicular, out dummy);
return true;
}
if (absDot > 1e-5f)
{
// reject direction
perpendicular -= dot * axis;
NormalizeSafe(ref perpendicular);
return true;
}
return NormalizeSafe(ref perpendicular);
}
public void Update(RigidTransform worldFromA, RigidTransform worldFromB, bool lockBtoA, float3 pivotA, float3 pivotB,
ref float3 directionA, ref float3 directionB, ref float3 perpendicularA, ref float3 perpendicularB, Object target)
{
// Work in world space
float3 directionAinW = math.rotate(worldFromA, directionA);
float3 directionBinW = math.rotate(worldFromB, directionB);
float3 perpendicularAinW = math.rotate(worldFromB, perpendicularA);
float3 perpendicularBinW = math.rotate(worldFromB, perpendicularB);
bool changed = false;
// If the target changed, fix up the inputs and reset the reference orientations to align with the new target's axes
if (target != m_LastTarget)
{
m_LastTarget = target;
// Enforce normalized directions
changed |= NormalizeSafe(ref directionAinW);
changed |= NormalizeSafe(ref directionBinW);
// Enforce normalized perpendiculars, orthogonal to their respective directions
changed |= NormalizePerpendicular(directionAinW, ref perpendicularAinW);
changed |= NormalizePerpendicular(directionBinW, ref perpendicularBinW);
// Calculate the rotation of the joint in A from direction and perpendicular
float3x3 rotationA = new float3x3(directionAinW, perpendicularAinW, math.cross(directionAinW, perpendicularAinW));
m_RefA = new quaternion(rotationA);
if (lockBtoA)
{
m_RefB = m_RefA;
}
else
{
// Calculate the rotation of the joint in B from direction and perpendicular
float3x3 rotationB = new float3x3(directionBinW, perpendicularBinW, math.cross(directionBinW, perpendicularBinW));
m_RefB = new quaternion(rotationB);
}
}
EditorGUI.BeginChangeCheck();
// Make rotators
quaternion oldRefA = m_RefA;
quaternion oldRefB = m_RefB;
float3 pivotAinW = math.transform(worldFromA, pivotA);
m_RefA = Handles.RotationHandle(m_RefA, pivotAinW);
float3 pivotBinW;
if (lockBtoA)
{
directionB = math.rotate(math.inverse(worldFromB), directionAinW);
perpendicularB = math.rotate(math.inverse(worldFromB), perpendicularAinW);
pivotBinW = pivotAinW;
m_RefB = m_RefA;
}
else
{
pivotBinW = math.transform(worldFromB, pivotB);
m_RefB = Handles.RotationHandle(m_RefB, pivotBinW);
}
// Apply changes from the rotators
if (EditorGUI.EndChangeCheck())
{
quaternion dqA = math.mul(m_RefA, math.inverse(oldRefA));
quaternion dqB = math.mul(m_RefB, math.inverse(oldRefB));
directionAinW = math.mul(dqA, directionAinW);
directionBinW = math.mul(dqB, directionBinW);
perpendicularAinW = math.mul(dqB, perpendicularAinW);
perpendicularBinW = math.mul(dqB, perpendicularBinW);
changed = true;
}
// Write back if the axes changed
if (changed)
{
Undo.RecordObject(target, "Edit joint axis");
directionA = math.rotate(math.inverse(worldFromA), directionAinW);
directionB = math.rotate(math.inverse(worldFromB), directionBinW);
perpendicularA = math.rotate(math.inverse(worldFromB), perpendicularAinW);
perpendicularB = math.rotate(math.inverse(worldFromB), perpendicularBinW);
}
// Draw the updated axes
float3 z = new float3(0, 0, 1); // ArrowHandleCap() draws an arrow pointing in (0, 0, 1)
Handles.ArrowHandleCap(0, pivotAinW, Quaternion.FromToRotation(z, directionAinW), HandleUtility.GetHandleSize(pivotAinW) * 0.75f, Event.current.type);
Handles.ArrowHandleCap(0, pivotAinW, Quaternion.FromToRotation(z, perpendicularAinW), HandleUtility.GetHandleSize(pivotAinW) * 0.75f, Event.current.type);
if (!lockBtoA)
{
Handles.ArrowHandleCap(0, pivotBinW, Quaternion.FromToRotation(z, directionBinW), HandleUtility.GetHandleSize(pivotBinW) * 0.75f, Event.current.type);
Handles.ArrowHandleCap(0, pivotBinW, Quaternion.FromToRotation(z, perpendicularBinW), HandleUtility.GetHandleSize(pivotBinW) * 0.75f, Event.current.type);
}
}
}
public static void EditLimits(RigidTransform worldFromA, RigidTransform worldFromB, float3 pivotA, float3 axisA, float3 axisB, float3 perpendicularA, float3 perpendicularB,
ref float minLimit, ref float maxLimit, JointAngularLimitHandle limitHandle, Object target)
{
// Transform to world space
float3 pivotAinW = math.transform(worldFromA, pivotA);
float3 axisAinW = math.rotate(worldFromA, axisA);
float3 perpendicularAinW = math.rotate(worldFromA, perpendicularA);
float3 axisBinW = math.rotate(worldFromA, axisB);
float3 perpendicularBinW = math.rotate(worldFromB, perpendicularB);
// Get rotations from joint space
// JointAngularLimitHandle uses axis = (1, 0, 0) with angle = 0 at (0, 0, 1), so choose the rotations to point those in the directions of our axis and perpendicular
float3x3 worldFromJointA = new float3x3(axisAinW, -math.cross(axisAinW, perpendicularAinW), perpendicularAinW);
float3x3 worldFromJointB = new float3x3(axisBinW, -math.cross(axisBinW, perpendicularBinW), perpendicularBinW);
float3x3 jointBFromA = math.mul(math.transpose(worldFromJointB), worldFromJointA);
// Set orientation for the angular limit control
float angle = CalculateTwistAngle(new quaternion(jointBFromA), 0); // index = 0 because axis is the first column in worldFromJoint
quaternion limitOrientation = math.mul(quaternion.AxisAngle(axisAinW, angle), new quaternion(worldFromJointA));
Matrix4x4 handleMatrix = Matrix4x4.TRS(pivotAinW, limitOrientation, Vector3.one);
float size = HandleUtility.GetHandleSize(pivotAinW) * 0.75f;
limitHandle.xMin = -maxLimit;
limitHandle.xMax = -minLimit;
limitHandle.xMotion = ConfigurableJointMotion.Limited;
limitHandle.yMotion = ConfigurableJointMotion.Locked;
limitHandle.zMotion = ConfigurableJointMotion.Locked;
limitHandle.yHandleColor = new Color(0, 0, 0, 0);
limitHandle.zHandleColor = new Color(0, 0, 0, 0);
limitHandle.radius = size;
using (new Handles.DrawingScope(handleMatrix))
{
// Draw the reference axis
float3 z = new float3(0, 0, 1); // ArrowHandleCap() draws an arrow pointing in (0, 0, 1)
Handles.ArrowHandleCap(0, float3.zero, Quaternion.FromToRotation(z, new float3(1, 0, 0)), size, Event.current.type);
// Draw the limit editor handle
EditorGUI.BeginChangeCheck();
limitHandle.DrawHandle();
if (EditorGUI.EndChangeCheck())
{
// Record the target object before setting new limits so changes can be undone/redone
Undo.RecordObject(target, "Edit joint angular limits");
minLimit = -limitHandle.xMax;
maxLimit = -limitHandle.xMin;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c7e2e7874c307546bb3ab423b21a11e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
#if UNITY_EDITOR
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(LimitedHingeJoint))]
public class LimitedHingeEditor : UnityEditor.Editor
{
private EditorUtilities.AxisEditor m_AxisEditor = new EditorUtilities.AxisEditor();
private JointAngularLimitHandle m_LimitHandle = new JointAngularLimitHandle();
public override void OnInspectorGUI()
{
LimitedHingeJoint limitedHinge = (LimitedHingeJoint)target;
EditorGUI.BeginChangeCheck();
GUILayout.BeginHorizontal();
GUILayout.Label("Editors:");
limitedHinge.EditPivots = GUILayout.Toggle(limitedHinge.EditPivots, new GUIContent("Pivot"), "Button");
limitedHinge.EditAxes = GUILayout.Toggle(limitedHinge.EditAxes, new GUIContent("Axis"), "Button");
limitedHinge.EditLimits = GUILayout.Toggle(limitedHinge.EditLimits, new GUIContent("Limits"), "Button");
GUILayout.EndHorizontal();
DrawDefaultInspector();
if (EditorGUI.EndChangeCheck())
{
SceneView.RepaintAll();
}
}
protected virtual void OnSceneGUI()
{
LimitedHingeJoint limitedHinge = (LimitedHingeJoint)target;
if (limitedHinge.EditPivots)
{
EditorUtilities.EditPivot(limitedHinge.worldFromA, limitedHinge.worldFromB, limitedHinge.AutoSetConnected,
ref limitedHinge.PositionLocal, ref limitedHinge.PositionInConnectedEntity, limitedHinge);
}
if (limitedHinge.EditAxes)
{
m_AxisEditor.Update(limitedHinge.worldFromA, limitedHinge.worldFromB,
limitedHinge.AutoSetConnected,
limitedHinge.PositionLocal, limitedHinge.PositionInConnectedEntity,
ref limitedHinge.HingeAxisLocal, ref limitedHinge.HingeAxisInConnectedEntity,
ref limitedHinge.PerpendicularAxisLocal, ref limitedHinge.PerpendicularAxisInConnectedEntity,
limitedHinge);
}
if (limitedHinge.EditLimits)
{
EditorUtilities.EditLimits(limitedHinge.worldFromA, limitedHinge.worldFromB,
limitedHinge.PositionLocal,
limitedHinge.HingeAxisLocal, limitedHinge.HingeAxisInConnectedEntity,
limitedHinge.PerpendicularAxisLocal, limitedHinge.PerpendicularAxisInConnectedEntity,
ref limitedHinge.MinAngle, ref limitedHinge.MaxAngle, m_LimitHandle, limitedHinge);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f79ce4d7693ece94fb5927586cd79ad9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,156 @@
using System.Collections.Generic;
using Unity.Mathematics;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(PhysicsBodyAuthoring))]
[CanEditMultipleObjects]
class PhysicsBodyAuthoringEditor : BaseEditor
{
static class Content
{
public static readonly GUIContent MassLabel = EditorGUIUtility.TrTextContent("Mass");
public static readonly GUIContent CenterOfMassLabel = EditorGUIUtility.TrTextContent(
"Center of Mass", "Center of mass in the space of this body's transform."
);
public static readonly GUIContent InertiaTensorLabel = EditorGUIUtility.TrTextContent(
"Inertia Tensor", "Resistance to angular motion about each axis of rotation."
);
public static readonly GUIContent OrientationLabel = EditorGUIUtility.TrTextContent(
"Orientation", "Orientation of the body's inertia tensor in the space of its transform."
);
public static readonly GUIContent AdvancedLabel = EditorGUIUtility.TrTextContent(
"Advanced", "Advanced options"
);
}
#pragma warning disable 649
[AutoPopulate] SerializedProperty m_MotionType;
[AutoPopulate] SerializedProperty m_Smoothing;
[AutoPopulate] SerializedProperty m_Mass;
[AutoPopulate] SerializedProperty m_GravityFactor;
[AutoPopulate] SerializedProperty m_LinearDamping;
[AutoPopulate] SerializedProperty m_AngularDamping;
[AutoPopulate] SerializedProperty m_InitialLinearVelocity;
[AutoPopulate] SerializedProperty m_InitialAngularVelocity;
[AutoPopulate] SerializedProperty m_OverrideDefaultMassDistribution;
[AutoPopulate] SerializedProperty m_CenterOfMass;
[AutoPopulate] SerializedProperty m_Orientation;
[AutoPopulate] SerializedProperty m_InertiaTensor;
[AutoPopulate] SerializedProperty m_WorldIndex;
[AutoPopulate] SerializedProperty m_CustomTags;
#pragma warning restore 649
bool showAdvanced;
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_MotionType);
if (m_MotionType.intValue != (int)BodyMotionType.Static)
EditorGUILayout.PropertyField(m_Smoothing);
var dynamic = m_MotionType.intValue == (int)BodyMotionType.Dynamic;
if (dynamic)
EditorGUILayout.PropertyField(m_Mass, Content.MassLabel);
else
{
EditorGUI.BeginDisabledGroup(true);
var position = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
EditorGUI.BeginProperty(position, Content.MassLabel, m_Mass);
EditorGUI.FloatField(position, Content.MassLabel, float.PositiveInfinity);
EditorGUI.EndProperty();
EditorGUI.EndDisabledGroup();
}
if (m_MotionType.intValue == (int)BodyMotionType.Dynamic)
{
EditorGUILayout.PropertyField(m_LinearDamping, true);
EditorGUILayout.PropertyField(m_AngularDamping, true);
}
if (m_MotionType.intValue != (int)BodyMotionType.Static)
{
EditorGUILayout.PropertyField(m_InitialLinearVelocity, true);
EditorGUILayout.PropertyField(m_InitialAngularVelocity, true);
}
if (m_MotionType.intValue == (int)BodyMotionType.Dynamic)
{
EditorGUILayout.PropertyField(m_GravityFactor, true);
}
showAdvanced = EditorGUILayout.Foldout(showAdvanced, Content.AdvancedLabel);
if (showAdvanced)
{
++EditorGUI.indentLevel;
EditorGUILayout.PropertyField(m_WorldIndex);
if (m_MotionType.intValue != (int)BodyMotionType.Static)
{
EditorGUILayout.PropertyField(m_OverrideDefaultMassDistribution);
if (m_OverrideDefaultMassDistribution.boolValue)
{
++EditorGUI.indentLevel;
EditorGUILayout.PropertyField(m_CenterOfMass, Content.CenterOfMassLabel);
EditorGUI.BeginDisabledGroup(!dynamic);
if (dynamic)
{
EditorGUILayout.PropertyField(m_Orientation, Content.OrientationLabel);
EditorGUILayout.PropertyField(m_InertiaTensor, Content.InertiaTensorLabel);
}
else
{
EditorGUI.BeginDisabledGroup(true);
var position =
EditorGUILayout.GetControlRect(true, EditorGUI.GetPropertyHeight(m_InertiaTensor));
EditorGUI.BeginProperty(position, Content.InertiaTensorLabel, m_InertiaTensor);
EditorGUI.Vector3Field(position, Content.InertiaTensorLabel,
Vector3.one * float.PositiveInfinity);
EditorGUI.EndProperty();
EditorGUI.EndDisabledGroup();
}
EditorGUI.EndDisabledGroup();
--EditorGUI.indentLevel;
}
}
EditorGUILayout.PropertyField(m_CustomTags);
--EditorGUI.indentLevel;
}
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
DisplayStatusMessages();
}
MessageType m_Status;
List<string> m_StatusMessages = new List<string>(8);
void DisplayStatusMessages()
{
m_Status = MessageType.None;
m_StatusMessages.Clear();
var hierarchyStatus = StatusMessageUtility.GetHierarchyStatusMessage(targets, out var hierarchyStatusMessage);
if (!string.IsNullOrEmpty(hierarchyStatusMessage))
{
m_StatusMessages.Add(hierarchyStatusMessage);
m_Status = (MessageType)math.max((int)m_Status, (int)hierarchyStatus);
}
if (m_StatusMessages.Count > 0)
EditorGUILayout.HelpBox(string.Join("\n\n", m_StatusMessages), m_Status);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a63bbe7a0e8f0934fb7896991b8408ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(PhysicsCategoryNames))]
[CanEditMultipleObjects]
class PhysicsCategoryNamesEditor : BaseEditor
{
#pragma warning disable 649
[AutoPopulate(ElementFormatString = "Category {0}", Resizable = false, Reorderable = false)]
ReorderableList m_CategoryNames;
#pragma warning restore 649
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fcbb532fa3ab5ca4cb5005410a82cc83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 209b4a4e54f038844baf182ae186f813
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,108 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using Unity.Mathematics;
using Unity.Physics.Authoring;
namespace Unity.Physics.Editor
{
[CustomEditor(typeof(RagdollJoint))]
public class RagdollJointEditor : UnityEditor.Editor
{
private EditorUtilities.AxisEditor m_AxisEditor = new EditorUtilities.AxisEditor();
private JointAngularLimitHandle m_LimitHandle = new JointAngularLimitHandle();
public override void OnInspectorGUI()
{
RagdollJoint ragdoll = (RagdollJoint)target;
EditorGUI.BeginChangeCheck();
GUILayout.BeginVertical();
GUILayout.Space(10.0f);
GUILayout.BeginHorizontal();
GUILayout.Label("Editors:");
ragdoll.EditPivots = GUILayout.Toggle(ragdoll.EditPivots, new GUIContent("Pivot"), "Button");
ragdoll.EditAxes = GUILayout.Toggle(ragdoll.EditAxes, new GUIContent("Axis"), "Button");
ragdoll.EditLimits = GUILayout.Toggle(ragdoll.EditLimits, new GUIContent("Limits"), "Button");
GUILayout.EndHorizontal();
GUILayout.Space(10.0f);
GUILayout.EndVertical();
DrawDefaultInspector();
if (EditorGUI.EndChangeCheck())
{
SceneView.RepaintAll();
}
}
private static void DrawCone(float3 point, float3 axis, float angle, Color color)
{
#if UNITY_EDITOR
Handles.color = color;
float3 dir;
float scale = Math.NormalizeWithLength(axis, out dir);
float3 arm;
{
float3 perp1, perp2;
Math.CalculatePerpendicularNormalized(dir, out perp1, out perp2);
arm = math.mul(quaternion.AxisAngle(perp1, angle), dir) * scale;
}
const int res = 16;
quaternion q = quaternion.AxisAngle(dir, 2.0f * (float)math.PI / res);
for (int i = 0; i < res; i++)
{
float3 nextArm = math.mul(q, arm);
Handles.DrawLine(point, point + arm);
Handles.DrawLine(point + arm, point + nextArm);
arm = nextArm;
}
#endif
}
protected virtual void OnSceneGUI()
{
RagdollJoint ragdoll = (RagdollJoint)target;
bool drawCones = false;
if (ragdoll.EditPivots)
{
EditorUtilities.EditPivot(ragdoll.worldFromA, ragdoll.worldFromB, ragdoll.AutoSetConnected,
ref ragdoll.PositionLocal, ref ragdoll.PositionInConnectedEntity, ragdoll);
}
if (ragdoll.EditAxes)
{
m_AxisEditor.Update(ragdoll.worldFromA, ragdoll.worldFromB, ragdoll.AutoSetConnected,
ragdoll.PositionLocal, ragdoll.PositionInConnectedEntity, ref ragdoll.TwistAxisLocal, ref ragdoll.TwistAxisInConnectedEntity,
ref ragdoll.PerpendicularAxisLocal, ref ragdoll.PerpendicularAxisInConnectedEntity, ragdoll);
drawCones = true;
}
if (ragdoll.EditLimits)
{
EditorUtilities.EditLimits(ragdoll.worldFromA, ragdoll.worldFromB, ragdoll.PositionLocal, ragdoll.TwistAxisLocal, ragdoll.TwistAxisInConnectedEntity,
ragdoll.PerpendicularAxisLocal, ragdoll.PerpendicularAxisInConnectedEntity, ref ragdoll.MinTwistAngle, ref ragdoll.MaxTwistAngle, m_LimitHandle, ragdoll);
}
if (drawCones)
{
float3 pivotB = math.transform(ragdoll.worldFromB, ragdoll.PositionInConnectedEntity);
float3 axisB = math.rotate(ragdoll.worldFromB, ragdoll.TwistAxisInConnectedEntity);
DrawCone(pivotB, axisB, math.radians(ragdoll.MaxConeAngle), Color.yellow);
float3 perpendicularB = math.rotate(ragdoll.worldFromB, ragdoll.PerpendicularAxisInConnectedEntity);
DrawCone(pivotB, perpendicularB, math.radians(ragdoll.MinPerpendicularAngle + 90f), Color.red);
DrawCone(pivotB, perpendicularB, math.radians(ragdoll.MaxPerpendicularAngle + 90f), Color.red);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c97c008c29536647add5a287db56ae2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 23d0032aba561954aaf56c65acf751bd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
abstract class BaseDrawer : PropertyDrawer
{
protected abstract bool IsCompatible(SerializedProperty property);
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return IsCompatible(property)
? EditorGUI.GetPropertyHeight(property)
: EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (IsCompatible(property))
DoGUI(position, property, label);
else
EditorGUIControls.DisplayCompatibilityWarning(position, label, ObjectNames.NicifyVariableName(GetType().Name));
}
protected abstract void DoGUI(Rect position, SerializedProperty property, GUIContent label);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4ce107efdd5d5f439469b8b109f601f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using System;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
class EnumFlagsDrawer : BaseDrawer
{
protected override bool IsCompatible(SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.Enum;
}
protected override void DoGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
var value = property.longValue;
EditorGUI.BeginChangeCheck();
value = Convert.ToInt64(
EditorGUI.EnumFlagsField(position, label, (Enum)Enum.ToObject(fieldInfo.FieldType, value))
);
if (EditorGUI.EndChangeCheck())
property.longValue = value;
EditorGUI.EndProperty();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91d983b20a4beed4794e82ce61acb452
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(EulerAngles))]
class EulerAnglesDrawer : BaseDrawer
{
protected override bool IsCompatible(SerializedProperty property) => true;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var value = property.FindPropertyRelative(nameof(EulerAngles.Value));
return EditorGUI.GetPropertyHeight(value);
}
protected override void DoGUI(Rect position, SerializedProperty property, GUIContent label)
{
var value = property.FindPropertyRelative(nameof(EulerAngles.Value));
EditorGUI.PropertyField(position, value, label, true);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa19ca1ba7846e443a44ba0fff644d17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(ExpandChildrenAttribute))]
class ExpandChildrenDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
property.isExpanded = true;
return EditorGUI.GetPropertyHeight(property)
- EditorGUIUtility.standardVerticalSpacing
- EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var endProperty = property.GetEndProperty();
var childProperty = property.Copy();
childProperty.NextVisible(true);
while (!SerializedProperty.EqualContents(childProperty, endProperty))
{
position.height = EditorGUI.GetPropertyHeight(childProperty);
OnChildPropertyGUI(position, childProperty);
position.y += position.height + EditorGUIUtility.standardVerticalSpacing;
childProperty.NextVisible(false);
}
}
protected virtual void OnChildPropertyGUI(Rect position, SerializedProperty childProperty)
{
EditorGUI.PropertyField(position, childProperty, true);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c02598cdac1be04a9039046cc3b8459
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using System;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(PhysicsMaterialCoefficient))]
class PhysicsMaterialCoefficientDrawer : BaseDrawer
{
static class Styles
{
public const float PopupWidth = 100f;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) =>
EditorGUIUtility.singleLineHeight;
protected override bool IsCompatible(SerializedProperty property) => true;
protected override void DoGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
EditorGUI.PropertyField(
new Rect(position) { xMax = position.xMax - Styles.PopupWidth },
property.FindPropertyRelative("Value"),
label
);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUI.PropertyField(
new Rect(position) { xMin = position.xMax - Styles.PopupWidth + EditorGUIUtility.standardVerticalSpacing },
property.FindPropertyRelative("CombineMode"),
GUIContent.none
);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 637c07be1ce65064687a822ad33de1aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,214 @@
using System.Collections.Generic;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(PhysicsMaterialProperties))]
class PhysicsMaterialPropertiesDrawer : BaseDrawer
{
static class Content
{
public static readonly GUIContent AdvancedGroupFoldout = EditorGUIUtility.TrTextContent("Advanced");
public static readonly GUIContent BelongsToLabel = EditorGUIUtility.TrTextContent(
"Belongs To",
"Specifies the categories to which this object belongs."
);
public static readonly GUIContent CollidesWithLabel = EditorGUIUtility.TrTextContent(
"Collides With",
"Specifies the categories of objects with which this object will collide, " +
"or with which it will raise events if intersecting a trigger."
);
public static readonly GUIContent CollisionFilterGroupFoldout =
EditorGUIUtility.TrTextContent("Collision Filter");
public static readonly GUIContent CustomFlagsLabel =
EditorGUIUtility.TrTextContent("Custom Tags", "Specify custom tags to read at run-time.");
public static readonly GUIContent FrictionLabel = EditorGUIUtility.TrTextContent(
"Friction",
"Specifies how resistant the body is to motion when sliding along other surfaces, " +
"as well as what value should be used when colliding with an object that has a different value."
);
public static readonly GUIContent RestitutionLabel = EditorGUIUtility.TrTextContent(
"Restitution",
"Specifies how bouncy the object will be when colliding with other surfaces, " +
"as well as what value should be used when colliding with an object that has a different value."
);
public static readonly GUIContent CollisionResponseLabel = EditorGUIUtility.TrTextContent(
"Collision Response",
"Specifies whether the shape should collide normally, raise trigger events when intersecting other shapes, " +
"collide normally and raise notifications of collision events with other shapes, " +
"or completely ignore collisions (but still move and intercept queries)."
);
}
const string k_CollisionFilterGroupKey = "m_BelongsToCategories";
const string k_AdvancedGroupKey = "m_CustomMaterialTags";
Dictionary<string, SerializedObject> m_SerializedTemplates = new Dictionary<string, SerializedObject>();
SerializedProperty GetTemplateValueProperty(SerializedProperty property)
{
var key = property.propertyPath;
var template = property.FindPropertyRelative("m_Template").objectReferenceValue;
SerializedObject serializedTemplate;
if (
!m_SerializedTemplates.TryGetValue(key, out serializedTemplate)
|| serializedTemplate?.targetObject != template
)
m_SerializedTemplates[key] = serializedTemplate = template == null ? null : new SerializedObject(template);
serializedTemplate?.Update();
return serializedTemplate?.FindProperty("m_Value");
}
void FindToggleAndValueProperties(
SerializedProperty property, SerializedProperty templateValueProperty, string relativePath,
out SerializedProperty toggle, out SerializedProperty value
)
{
var relative = property.FindPropertyRelative(relativePath);
toggle = relative.FindPropertyRelative("m_Override");
value = toggle.boolValue || templateValueProperty == null
? relative.FindPropertyRelative("m_Value")
: templateValueProperty.FindPropertyRelative(relativePath).FindPropertyRelative("m_Value");
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var templateValueProperty = GetTemplateValueProperty(property);
// m_CollisionResponse, collision filter foldout, advanced foldout
var height = 3f * EditorGUIUtility.singleLineHeight + 2f * EditorGUIUtility.standardVerticalSpacing;
// m_BelongsTo, m_CollidesWith
var group = property.FindPropertyRelative(k_CollisionFilterGroupKey);
if (group.isExpanded)
height += 2f * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
// m_CustomTags
group = property.FindPropertyRelative(k_AdvancedGroupKey);
if (group.isExpanded)
height += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
// m_Template
if (property.FindPropertyRelative("m_SupportsTemplate").boolValue)
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
// m_Friction, m_Restitution
FindToggleAndValueProperties(property, templateValueProperty, "m_CollisionResponse", out _, out var collisionResponse);
// Check if regular collider
CollisionResponsePolicy collisionResponseEnum = (CollisionResponsePolicy)collisionResponse.intValue;
if (collisionResponseEnum == CollisionResponsePolicy.Collide ||
collisionResponseEnum == CollisionResponsePolicy.CollideRaiseCollisionEvents)
height += 2f * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
return height;
}
protected override bool IsCompatible(SerializedProperty property) => true;
static void DisplayOverridableProperty(
Rect position, GUIContent label, SerializedProperty toggle, SerializedProperty value, bool templateAssigned
)
{
if (templateAssigned)
{
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth -= 16f + EditorGUIUtility.standardVerticalSpacing;
var togglePosition = new Rect(position) { width = EditorGUIUtility.labelWidth + 16f + EditorGUIUtility.standardVerticalSpacing };
EditorGUI.PropertyField(togglePosition, toggle, label);
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.BeginDisabledGroup(!toggle.boolValue);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUI.PropertyField(new Rect(position) { xMin = togglePosition.xMax }, value, GUIContent.none, true);
EditorGUI.indentLevel = indent;
EditorGUI.EndDisabledGroup();
}
else
{
EditorGUI.PropertyField(position, value, label, true);
}
}
protected override void DoGUI(Rect position, SerializedProperty property, GUIContent label)
{
var template = property.FindPropertyRelative("m_Template");
var templateAssigned = template.objectReferenceValue != null;
var supportsTemplate = property.FindPropertyRelative("m_SupportsTemplate");
if (supportsTemplate.boolValue)
{
position.height = EditorGUI.GetPropertyHeight(template);
EditorGUI.PropertyField(position, template);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
}
var templateValue = GetTemplateValueProperty(property);
FindToggleAndValueProperties(property, templateValue, "m_CollisionResponse", out var collisionResponseDropDown, out var collisionResponse);
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.CollisionResponseLabel, collisionResponseDropDown, collisionResponse, templateAssigned);
SerializedProperty toggle;
// Check if regular collider
CollisionResponsePolicy collisionResponseEnum = (CollisionResponsePolicy)collisionResponse.intValue;
if (collisionResponseEnum == CollisionResponsePolicy.Collide ||
collisionResponseEnum == CollisionResponsePolicy.CollideRaiseCollisionEvents)
{
FindToggleAndValueProperties(property, templateValue, "m_Friction", out toggle, out var friction);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.FrictionLabel, toggle, friction, templateAssigned);
FindToggleAndValueProperties(property, templateValue, "m_Restitution", out toggle, out var restitution);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.RestitutionLabel, toggle, restitution, templateAssigned);
}
// collision filter group
var collisionFilterGroup = property.FindPropertyRelative(k_CollisionFilterGroupKey);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
collisionFilterGroup.isExpanded =
EditorGUI.Foldout(position, collisionFilterGroup.isExpanded, Content.CollisionFilterGroupFoldout, true);
if (collisionFilterGroup.isExpanded)
{
++EditorGUI.indentLevel;
FindToggleAndValueProperties(property, templateValue, "m_BelongsToCategories", out toggle, out var belongsTo);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.BelongsToLabel, toggle, belongsTo, templateAssigned);
FindToggleAndValueProperties(property, templateValue, "m_CollidesWithCategories", out toggle, out var collidesWith);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.CollidesWithLabel, toggle, collidesWith, templateAssigned);
--EditorGUI.indentLevel;
}
// advanced group
var advancedGroup = property.FindPropertyRelative(k_AdvancedGroupKey);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
advancedGroup.isExpanded =
EditorGUI.Foldout(position, advancedGroup.isExpanded, Content.AdvancedGroupFoldout, true);
if (advancedGroup.isExpanded)
{
++EditorGUI.indentLevel;
FindToggleAndValueProperties(property, templateValue, "m_CustomMaterialTags", out toggle, out var customFlags);
position.y = position.yMax + EditorGUIUtility.standardVerticalSpacing;
position.height = EditorGUIUtility.singleLineHeight;
DisplayOverridableProperty(position, Content.CustomFlagsLabel, toggle, customFlags, templateAssigned);
--EditorGUI.indentLevel;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 72400ec801359f34e9b82e3be224cde5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[CustomPropertyDrawer(typeof(SoftRangeAttribute))]
class SoftRangeDrawer : BaseDrawer
{
protected override bool IsCompatible(SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.Float;
}
protected override void DoGUI(Rect position, SerializedProperty property, GUIContent label)
{
var attr = attribute as SoftRangeAttribute;
EditorGUIControls.SoftSlider(
position, label, property, attr.SliderMin, attr.SliderMax, attr.TextFieldMin, attr.TextFieldMax
);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3973ec625484cb418fb92f5b2bbffdd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,250 @@
using System.Collections.Generic;
using System.Linq;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
abstract class TagsDrawer<T> : PropertyDrawer where T : ScriptableObject, ITagNames
{
static class Styles
{
public static readonly string EverythingName = L10n.Tr("Everything");
public static readonly string MixedName = L10n.Tr("Mixed...");
public static readonly string NothingName = L10n.Tr("Nothing");
public static readonly string MultipleAssetsTooltip =
L10n.Tr("Multiple {0} assets found. UI will display labels defined in {1}.");
public static readonly GUIContent MultipleAssetsWarning =
new GUIContent { image = EditorGUIUtility.Load("console.warnicon") as Texture };
}
protected abstract int MaxNumCategories { get; }
protected abstract string DefaultCategoryName { get; }
internal string FirstChildPropertyPath { get; set; } // TODO: remove when all usages of bool[] are migrated
string DefaultFormatString => L10n.Tr($"(Undefined {DefaultCategoryName})");
string[] DefaultOptions =>
m_DefaultOptions ?? (
m_DefaultOptions =
Enumerable.Range(0, MaxNumCategories)
.Select(i => string.Format(DefaultFormatString, i))
.ToArray()
);
string[] m_DefaultOptions;
string[] GetOptions()
{
if (m_Options != null)
return m_Options;
var guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
m_NamesAssets = guids
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath<T>)
.Where(c => c != null)
.ToArray();
m_Options = m_NamesAssets.FirstOrDefault()?.TagNames.ToArray() ?? DefaultOptions;
for (var i = 0; i < m_Options.Length; ++i)
{
if (string.IsNullOrEmpty(m_Options[i]))
m_Options[i] = DefaultOptions[i];
m_Options[i] = $"{i}: {m_Options[i]}";
}
return m_Options;
}
string[] m_Options;
static string GetButtonLabel(int value, IReadOnlyList<string> optionNames)
{
switch (value)
{
case 0:
return Styles.NothingName;
case ~0:
return Styles.EverythingName;
default:
{
for (var i = 0; i < 32; i++)
{
if (value == 1 << i)
return optionNames[i];
}
break;
}
}
return Styles.MixedName;
}
T[] m_NamesAssets;
// TODO: remove when all usages of bool[] are migrated
SerializedProperty GetFirstChildProperty(SerializedProperty property)
{
if (!string.IsNullOrEmpty(FirstChildPropertyPath))
return property.FindPropertyRelative(FirstChildPropertyPath);
var sp = property.Copy();
sp.NextVisible(true);
return sp;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (m_NamesAssets?.Length > 1)
position.xMax -= EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
EditorGUI.BeginProperty(position, label, property);
var controlPosition = EditorGUI.PrefixLabel(position, label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var showMixed = EditorGUI.showMixedValue;
var value = 0;
var everything = 0;
var sp = GetFirstChildProperty(property);
for (int i = 0, count = MaxNumCategories; i < count; ++i)
{
EditorGUI.showMixedValue |= sp.hasMultipleDifferentValues;
value |= sp.boolValue ? 1 << i : 0;
everything |= 1 << i;
sp.NextVisible(false);
}
// in case size is smaller than 32
if (value == everything)
value = ~0;
var options = GetOptions();
if (
EditorGUI.DropdownButton(
controlPosition,
EditorGUIUtility.TrTempContent(GetButtonLabel(value, options)),
FocusType.Passive,
EditorStyles.popup
)
)
{
var menu = new GenericMenu();
menu.AddItem(
new GUIContent(Styles.NothingName),
value == 0,
() =>
{
sp = GetFirstChildProperty(property);
for (int i = 0, count = MaxNumCategories; i < count; ++i)
{
sp.boolValue = false;
sp.NextVisible(false);
}
sp.serializedObject.ApplyModifiedProperties();
}
);
menu.AddItem(
new GUIContent(Styles.EverythingName),
value == ~0,
() =>
{
sp = GetFirstChildProperty(property);
for (int i = 0, count = MaxNumCategories; i < count; ++i)
{
sp.boolValue = true;
sp.NextVisible(false);
}
sp.serializedObject.ApplyModifiedProperties();
}
);
for (var option = 0; option < options.Length; ++option)
{
var callbackValue = option;
menu.AddItem(
EditorGUIUtility.TrTextContent(options[option]),
((1 << option) & value) != 0,
args =>
{
var changedBitAndValue = (KeyValuePair<int, bool>)args;
sp = GetFirstChildProperty(property);
for (int i = 0, count = changedBitAndValue.Key; i < count; ++i)
sp.NextVisible(false);
sp.boolValue = changedBitAndValue.Value;
sp.serializedObject.ApplyModifiedProperties();
},
new KeyValuePair<int, bool>(callbackValue, ((1 << option) & value) == 0)
);
}
menu.AddSeparator(string.Empty);
menu.AddItem(
EditorGUIUtility.TrTempContent($"Edit {ObjectNames.NicifyVariableName(typeof(T).Name)}"),
false,
() =>
{
if (m_NamesAssets.Length > 0)
Selection.activeObject = m_NamesAssets[0];
else
{
var assetPath = AssetDatabase.GenerateUniqueAssetPath($"Assets/{typeof(T).Name}.asset");
AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<T>(), assetPath);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<T>(assetPath);
m_Options = null;
}
}
);
menu.DropDown(controlPosition);
}
EditorGUI.showMixedValue = showMixed;
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
if (m_NamesAssets?.Length > 1)
{
var id = GUIUtility.GetControlID(FocusType.Passive);
if (Event.current.type == EventType.Repaint)
{
position.width = EditorGUIUtility.singleLineHeight;
position.x = controlPosition.xMax + EditorGUIUtility.standardVerticalSpacing;
Styles.MultipleAssetsWarning.tooltip = string.Format(
Styles.MultipleAssetsTooltip,
ObjectNames.NicifyVariableName(typeof(T).Name),
m_NamesAssets.FirstOrDefault(n => n != null)?.name
);
GUIStyle.none.Draw(position, Styles.MultipleAssetsWarning, id);
}
}
}
}
[CustomPropertyDrawer(typeof(CustomPhysicsBodyTags))]
class CustomBodyTagsDrawer : TagsDrawer<CustomPhysicsBodyTagNames>
{
protected override string DefaultCategoryName => "Custom Physics Body Tag";
protected override int MaxNumCategories => 8;
}
[CustomPropertyDrawer(typeof(CustomPhysicsMaterialTags))]
class CustomMaterialTagsDrawer : TagsDrawer<CustomPhysicsMaterialTagNames>
{
protected override string DefaultCategoryName => "Custom Physics Material Tag";
protected override int MaxNumCategories => 8;
}
[CustomPropertyDrawer(typeof(PhysicsCategoryTags))]
class PhysicsCategoryTagsDrawer : TagsDrawer<PhysicsCategoryNames>
{
protected override string DefaultCategoryName => "Physics Category";
protected override int MaxNumCategories => 32;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bacae2b76edd4224d8ccf21bbeb71833
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
{
"name": "Unity.Physics.Custom.Editor",
"references": [
"Unity.Burst",
"Unity.Collections",
"Unity.Entities",
"Unity.Mathematics",
"Unity.Physics",
"Unity.Physics.Editor",
"Unity.Physics.Hybrid",
"Unity.Physics.Samples",
"Unity.Physics.Custom"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dde4aa253ec0b874ba280d1bde6b5386
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3d7894b5b13854409e8c11b2c933f3e
timeCreated: 1678290090

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity.Mathematics;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
[InitializeOnLoad]
static class EditorGUIControls
{
static EditorGUIControls()
{
if (k_SoftSlider == null)
Debug.LogException(new MissingMemberException("Could not find expected signature of EditorGUI.Slider() for soft slider."));
}
static class Styles
{
public static readonly string CompatibilityWarning = L10n.Tr("Not compatible with {0}.");
}
public static void DisplayCompatibilityWarning(Rect position, GUIContent label, string incompatibleType)
{
EditorGUI.HelpBox(
EditorGUI.PrefixLabel(position, label),
string.Format(Styles.CompatibilityWarning, incompatibleType),
MessageType.Error
);
}
static readonly MethodInfo k_SoftSlider = typeof(EditorGUI).GetMethod(
"Slider",
BindingFlags.Static | BindingFlags.NonPublic,
null,
new[]
{
typeof(Rect), // position
typeof(GUIContent), // label
typeof(float), // value
typeof(float), // sliderMin
typeof(float), // sliderMax
typeof(float), // textFieldMin
typeof(float) // textFieldMax
},
Array.Empty<ParameterModifier>()
);
static readonly object[] k_SoftSliderArgs = new object[7];
public static void SoftSlider(
Rect position, GUIContent label, SerializedProperty property,
float sliderMin, float sliderMax,
float textFieldMin, float textFieldMax
)
{
if (property.propertyType != SerializedPropertyType.Float)
{
DisplayCompatibilityWarning(position, label, property.propertyType.ToString());
}
else if (k_SoftSlider == null)
{
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(position, property, label);
if (EditorGUI.EndChangeCheck())
property.floatValue = math.clamp(property.floatValue, textFieldMin, textFieldMax);
}
else
{
k_SoftSliderArgs[0] = position;
k_SoftSliderArgs[1] = label;
k_SoftSliderArgs[2] = property.floatValue;
k_SoftSliderArgs[3] = sliderMin;
k_SoftSliderArgs[4] = sliderMax;
k_SoftSliderArgs[5] = textFieldMin;
k_SoftSliderArgs[6] = textFieldMax;
EditorGUI.BeginProperty(position, label, property);
EditorGUI.BeginChangeCheck();
var result = k_SoftSlider.Invoke(null, k_SoftSliderArgs);
if (EditorGUI.EndChangeCheck())
property.floatValue = (float)result;
EditorGUI.EndProperty();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 871e6b9236392b347a91f460fd84f976
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System;
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Editor
{
enum MatrixState
{
UniformScale,
NonUniformScale,
ZeroScale,
NotValidTRS
}
static class ManipulatorUtility
{
public static MatrixState GetMatrixState(ref float4x4 localToWorld)
{
if (
localToWorld.c0.w != 0f
|| localToWorld.c1.w != 0f
|| localToWorld.c2.w != 0f
|| localToWorld.c3.w != 1f
)
return MatrixState.NotValidTRS;
var m = new float3x3(localToWorld.c0.xyz, localToWorld.c1.xyz, localToWorld.c2.xyz);
var lossyScale = new float3(math.length(m.c0.xyz), math.length(m.c1.xyz), math.length(m.c2.xyz));
if (math.determinant(m) < 0f)
lossyScale.x *= -1f;
if (math.lengthsq(lossyScale) == 0f)
return MatrixState.ZeroScale;
return math.abs(math.cmax(lossyScale)) - math.abs(math.cmin(lossyScale)) > 0.000001f
? MatrixState.NonUniformScale
: MatrixState.UniformScale;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ed6267b3a6f75e4aa4aca986451797e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,168 @@
using System;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
static class SceneViewUtility
{
static class Styles
{
public static readonly GUIStyle ProgressBarTrack = new GUIStyle
{
fixedHeight = 4f,
normal = new GUIStyleState { background = Texture2D.whiteTexture }
};
public static readonly GUIStyle ProgressBarIndicator = new GUIStyle
{
fixedHeight = 4f,
normal = new GUIStyleState { background = Texture2D.whiteTexture }
};
public static readonly GUIStyle SceneViewStatusMessage = new GUIStyle("NotificationBackground")
{
fontSize = EditorStyles.label.fontSize
};
static Styles() => SceneViewStatusMessage.padding = SceneViewStatusMessage.border;
}
const string k_NotificationsPrefKey = "SceneView/Tools/Notifications";
const bool k_DefaultNotifications = true;
const string k_NotificationSpeedPrefKey = "SceneView/Tools/Notification Speed";
const float k_DefaultNotificationsSpeed = 20f;
const float k_NotificationDuration = 1f;
const float k_NotificationFadeInTime = 0.04f;
const float k_NotificationFadeOutTime = 0.2f;
static readonly AnimationCurve k_NotificationFadeCurve = new AnimationCurve
{
keys = new[]
{
new Keyframe { time = 0f, value = 0f, outTangent = 1f / k_NotificationFadeInTime },
new Keyframe { time = k_NotificationFadeInTime, value = 1f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_NotificationDuration - k_NotificationFadeOutTime, value = 1f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_NotificationDuration, value = 0f, inTangent = -1f / k_NotificationFadeOutTime }
},
postWrapMode = WrapMode.Clamp,
preWrapMode = WrapMode.Clamp
};
const float k_IndeterminateProgressCurveDuration = 2f;
static readonly AnimationCurve k_IndeterminateProgressCurveLeftMargin = new AnimationCurve
{
keys = new[]
{
new Keyframe { time = 0f, value = 0f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_IndeterminateProgressCurveDuration / 2f, value = 0.25f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_IndeterminateProgressCurveDuration, value = 1f, inTangent = 0f, outTangent = 0f }
},
postWrapMode = WrapMode.Loop,
preWrapMode = WrapMode.Loop
};
static readonly AnimationCurve k_IndeterminateProgressCurveRightMargin = new AnimationCurve
{
keys = new[]
{
new Keyframe { time = 0f, value = 1f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_IndeterminateProgressCurveDuration / 2f, value = 0f, inTangent = 0f, outTangent = 0f },
new Keyframe { time = k_IndeterminateProgressCurveDuration, value = 0f, inTangent = 0f, outTangent = 0f }
},
postWrapMode = WrapMode.Loop,
preWrapMode = WrapMode.Loop
};
static string s_StatusMessage;
static DateTime s_StartTime;
static bool s_IsTemporary;
static Func<float> s_GetProgress;
public static void DisplayProgressNotification(string message, Func<float> getProgress) =>
// insert an extra line to make room for progress bar
DisplayNotificationInSceneView(getProgress == null ? message : $"{message}\n", false, getProgress);
public static void DisplayPersistentNotification(string message) =>
DisplayNotificationInSceneView(message, false, null);
public static void DisplayTemporaryNotification(string message) =>
DisplayNotificationInSceneView(message, true, null);
static void DisplayNotificationInSceneView(string message, bool temporary, Func<float> getProgress)
{
s_StatusMessage = message ?? string.Empty;
s_StartTime = DateTime.Now;
s_IsTemporary = temporary;
s_GetProgress = getProgress;
ClearNotificationInSceneView();
SceneView.duringSceneGui += ToolNotificationCallback;
SceneView.RepaintAll();
}
static void ToolNotificationCallback(SceneView obj)
{
if (Camera.current == null)
return;
var duration = math.max(s_StatusMessage.Length, 1)
/ EditorPrefs.GetFloat(k_NotificationSpeedPrefKey, k_DefaultNotificationsSpeed);
var t = (float)(DateTime.Now - s_StartTime).TotalSeconds;
if (
s_IsTemporary
&& (t >= duration || !EditorPrefs.GetBool(k_NotificationsPrefKey, k_DefaultNotifications))
)
{
ClearNotificationInSceneView();
}
else
{
Handles.BeginGUI();
var color = GUI.color;
var progress = s_GetProgress?.Invoke() ?? 0f;
GUI.color *=
new Color(1f, 1f, 1f, math.max(k_NotificationFadeCurve.Evaluate(math.abs(t) / duration), progress));
var rect = new Rect { size = Camera.current.pixelRect.size / EditorGUIUtility.pixelsPerPoint };
using (new GUILayout.AreaScope(rect))
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
using (new GUILayout.VerticalScope())
{
GUILayout.Space(rect.height * 0.75f);
GUILayout.FlexibleSpace();
var maxWidth = rect.width * 0.5f;
GUILayout.Box(s_StatusMessage, Styles.SceneViewStatusMessage, GUILayout.MaxWidth(maxWidth));
if (s_GetProgress != null)
{
rect = GUILayoutUtility.GetLastRect();
rect = Styles.SceneViewStatusMessage.padding.Remove(rect);
rect.y = rect.yMax - Styles.ProgressBarTrack.fixedHeight;
rect.height = Styles.ProgressBarTrack.fixedHeight;
var c = GUI.color;
GUI.color *= Color.black;
GUI.Box(rect, GUIContent.none, Styles.ProgressBarTrack);
GUI.color = c;
if (progress >= 0f && progress <= 1f)
{
rect.width *= progress;
}
else
{
var w = rect.width;
rect.xMin = rect.xMin + w * k_IndeterminateProgressCurveLeftMargin.Evaluate(t);
rect.xMax = rect.xMax - w * k_IndeterminateProgressCurveRightMargin.Evaluate(t);
}
GUI.Box(rect, GUIContent.none, Styles.ProgressBarIndicator);
}
GUILayout.FlexibleSpace();
}
GUILayout.FlexibleSpace();
}
GUI.color = color;
Handles.EndGUI();
}
SceneView.RepaintAll();
}
public static void ClearNotificationInSceneView() => SceneView.duringSceneGui -= ToolNotificationCallback;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7c9bbfa7889d6e4faa42c14d33bebb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Linq;
using Unity.Physics.Authoring;
using UnityEditor;
using UnityEngine;
namespace Unity.Physics.Editor
{
static class StatusMessageUtility
{
public static MessageType GetHierarchyStatusMessage(IReadOnlyList<UnityEngine.Object> targets, out string statusMessage)
{
statusMessage = string.Empty;
if (targets.Count == 0)
return MessageType.None;
var numChildTargets = 0;
foreach (Component c in targets)
{
// hierarchy roots and leaf shapes do not emit a message
if (
c == null
|| c.transform.parent == null
|| PhysicsShapeExtensions.GetPrimaryBody(c.gameObject) != c.gameObject
)
continue;
var targetType = c.GetType();
// only bodies (both explicit and implicit static bodies) will emit a message
if (
targetType == typeof(PhysicsBodyAuthoring)
|| targetType == typeof(Rigidbody)
|| c.GetComponent<PhysicsBodyAuthoring>() == null
&& c.GetComponent<Rigidbody>() == null
)
++numChildTargets;
}
switch (numChildTargets)
{
case 0:
return MessageType.None;
case 1:
statusMessage =
L10n.Tr("Target will be un-parented during the conversion process in order to take part in physics simulation.");
return MessageType.Warning;
default:
statusMessage =
L10n.Tr("One or more targets will be un-parented during the conversion process in order to take part in physics simulation.");
return MessageType.Warning;
}
}
public static MessageType GetMatrixStatusMessage(
IReadOnlyList<MatrixState> matrixStates, out string statusMessage
)
{
statusMessage = string.Empty;
if (matrixStates.Contains(MatrixState.NotValidTRS))
{
statusMessage = L10n.Tr(
matrixStates.Count == 1
? "Target's local-to-world matrix is not a valid transformation."
: "One or more targets' local-to-world matrices are not valid transformations."
);
return MessageType.Error;
}
if (matrixStates.Contains(MatrixState.ZeroScale))
{
statusMessage =
L10n.Tr(matrixStates.Count == 1 ? "Target has zero scale." : "One or more targets has zero scale.");
return MessageType.Warning;
}
if (matrixStates.Contains(MatrixState.NonUniformScale))
{
statusMessage = L10n.Tr(
matrixStates.Count == 1
? "Target has non-uniform scale. Shape data will be transformed during conversion in order to bake scale into the run-time format."
: "One or more targets has non-uniform scale. Shape data will be transformed during conversion in order to bake scale into the run-time format."
);
return MessageType.Warning;
}
return MessageType.None;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73b942099bb387a48abd183643304f85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 447d6d85fbacd7b4ea06884cf28f875e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Physics.Custom.Editor")]
[assembly: InternalsVisibleTo("Unity.Physics.Custom.EditModeTests")]

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 79534194088d466faf51dedb0dbb3012
timeCreated: 1680051514

View File

@@ -0,0 +1,32 @@
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Authoring
{
[RequireComponent(typeof(PhysicsBodyAuthoring))]
public abstract class BaseBodyPairConnector : MonoBehaviour
{
public PhysicsBodyAuthoring LocalBody => GetComponent<PhysicsBodyAuthoring>();
public PhysicsBodyAuthoring ConnectedBody;
public RigidTransform worldFromA => LocalBody == null
? RigidTransform.identity
: Math.DecomposeRigidBodyTransform(LocalBody.transform.localToWorldMatrix);
public RigidTransform worldFromB => ConnectedBody == null
? RigidTransform.identity
: Math.DecomposeRigidBodyTransform(ConnectedBody.transform.localToWorldMatrix);
public Entity EntityA { get; set; }
public Entity EntityB { get; set; }
void OnEnable()
{
// included so tick box appears in Editor
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 239c8ffb2d0d90043a794cea1df207f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67021bc32b354a27adca79ce72cc0632
timeCreated: 1678114562

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: debd1c7da217451682429a0a443b3fb4
timeCreated: 1678225532

View File

@@ -0,0 +1,150 @@
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics.GraphicsIntegration;
using UnityEngine;
namespace Unity.Physics.Authoring
{
[TemporaryBakingType]
public struct PhysicsBodyAuthoringData : IComponentData
{
public bool IsDynamic;
public float Mass;
public bool OverrideDefaultMassDistribution;
public MassDistribution CustomMassDistribution;
}
class PhysicsBodyAuthoringBaker : BasePhysicsBaker<PhysicsBodyAuthoring>
{
internal List<UnityEngine.Collider> colliderComponents = new List<UnityEngine.Collider>();
internal List<PhysicsShapeAuthoring> physicsShapeComponents = new List<PhysicsShapeAuthoring>();
public override void Bake(PhysicsBodyAuthoring authoring)
{
// Priority is to Legacy Components. Ignore if baked by Legacy.
if (GetComponent<Rigidbody>() || GetComponent<UnityEngine.Collider>())
{
return;
}
var entity = GetEntity(TransformUsageFlags.Dynamic);
// To process later in the Baking System
AddComponent(entity, new PhysicsBodyAuthoringData
{
IsDynamic = (authoring.MotionType == BodyMotionType.Dynamic),
Mass = authoring.Mass,
OverrideDefaultMassDistribution = authoring.OverrideDefaultMassDistribution,
CustomMassDistribution = authoring.CustomMassDistribution
});
AddSharedComponent(entity, new PhysicsWorldIndex(authoring.WorldIndex));
var bodyTransform = GetComponent<Transform>();
var motionType = authoring.MotionType;
var hasSmoothing = authoring.Smoothing != BodySmoothing.None;
PostProcessTransform(bodyTransform, motionType);
var customTags = authoring.CustomTags;
if (!customTags.Equals(CustomPhysicsBodyTags.Nothing))
AddComponent(entity, new PhysicsCustomTags { Value = customTags.Value });
// Check that there is at least one collider in the hierarchy to add these three
GetComponentsInChildren(colliderComponents);
GetComponentsInChildren(physicsShapeComponents);
if (colliderComponents.Count > 0 || physicsShapeComponents.Count > 0)
{
AddComponent(entity, new PhysicsCompoundData()
{
AssociateBlobToBody = false,
ConvertedBodyInstanceID = authoring.GetInstanceID(),
Hash = default,
});
AddComponent<PhysicsRootBaked>(entity);
AddComponent<PhysicsCollider>(entity);
}
if (authoring.MotionType == BodyMotionType.Static || IsStatic())
return;
var massProperties = MassProperties.UnitSphere;
AddComponent(entity, authoring.MotionType == BodyMotionType.Dynamic ?
PhysicsMass.CreateDynamic(massProperties, authoring.Mass) :
PhysicsMass.CreateKinematic(massProperties));
var physicsVelocity = new PhysicsVelocity
{
Linear = authoring.InitialLinearVelocity,
Angular = authoring.InitialAngularVelocity
};
AddComponent(entity, physicsVelocity);
if (authoring.MotionType == BodyMotionType.Dynamic)
{
// TODO make these optional in editor?
AddComponent(entity, new PhysicsDamping
{
Linear = authoring.LinearDamping,
Angular = authoring.AngularDamping
});
if (authoring.GravityFactor != 1)
{
AddComponent(entity, new PhysicsGravityFactor
{
Value = authoring.GravityFactor
});
}
}
else if (authoring.MotionType == BodyMotionType.Kinematic)
{
AddComponent(entity, new PhysicsGravityFactor
{
Value = 0
});
}
if (hasSmoothing)
{
AddComponent(entity, new PhysicsGraphicalSmoothing());
if (authoring.Smoothing == BodySmoothing.Interpolation)
{
AddComponent(entity, new PhysicsGraphicalInterpolationBuffer
{
PreviousTransform = Math.DecomposeRigidBodyTransform(bodyTransform.localToWorldMatrix),
PreviousVelocity = physicsVelocity,
});
}
}
}
}
[RequireMatchingQueriesForUpdate]
[UpdateAfter(typeof(EndColliderBakingSystem))]
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
public partial class PhysicsBodyBakingSystem : SystemBase
{
protected override void OnUpdate()
{
// Fill in the MassProperties based on the potential calculated value by BuildCompoundColliderBakingSystem
foreach (var(physicsMass, bodyData, collider) in
SystemAPI.Query<RefRW<PhysicsMass>, RefRO<PhysicsBodyAuthoringData>, RefRO<PhysicsCollider>>().WithOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities))
{
// Build mass component
var massProperties = collider.ValueRO.MassProperties;
if (bodyData.ValueRO.OverrideDefaultMassDistribution)
{
massProperties.MassDistribution = bodyData.ValueRO.CustomMassDistribution;
// Increase the angular expansion factor to account for the shift in center of mass
massProperties.AngularExpansionFactor += math.length(massProperties.MassDistribution.Transform.pos - bodyData.ValueRO.CustomMassDistribution.Transform.pos);
}
physicsMass.ValueRW = bodyData.ValueRO.IsDynamic ?
PhysicsMass.CreateDynamic(massProperties, bodyData.ValueRO.Mass) :
PhysicsMass.CreateKinematic(massProperties);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 922a37a81b1c6cc4da69a1148c8d2988
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,385 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;
namespace Unity.Physics.Authoring
{
class PhysicsShapeBaker : BaseColliderBaker<PhysicsShapeAuthoring>
{
public static List<PhysicsShapeAuthoring> physicsShapeComponents = new List<PhysicsShapeAuthoring>();
public static List<UnityEngine.Collider> colliderComponents = new List<UnityEngine.Collider>();
bool ShouldConvertShape(PhysicsShapeAuthoring authoring)
{
return authoring.enabled;
}
private GameObject GetPrimaryBody(GameObject shape, out bool hasBodyComponent, out bool isStaticBody)
{
var pb = FindFirstEnabledAncestor(shape, PhysicsShapeExtensions_NonBursted.s_PhysicsBodiesBuffer);
var rb = FindFirstEnabledAncestor(shape, PhysicsShapeExtensions_NonBursted.s_RigidbodiesBuffer);
hasBodyComponent = (pb != null || rb != null);
isStaticBody = false;
if (pb != null)
{
return rb == null ? pb.gameObject :
pb.transform.IsChildOf(rb.transform) ? pb.gameObject : rb.gameObject;
}
if (rb != null)
return rb.gameObject;
// for implicit static shape, first see if it is part of static optimized hierarchy
isStaticBody = FindTopmostStaticEnabledAncestor(shape, out var topStatic);
if (topStatic != null)
return topStatic;
// otherwise, find topmost enabled Collider or PhysicsShapeAuthoring
var topCollider = FindTopmostEnabledAncestor(shape, PhysicsShapeExtensions_NonBursted.s_CollidersBuffer);
var topShape = FindTopmostEnabledAncestor(shape, PhysicsShapeExtensions_NonBursted.s_ShapesBuffer);
return topCollider == null
? topShape == null ? shape.gameObject : topShape
: topShape == null
? topCollider
: topShape.transform.IsChildOf(topCollider.transform)
? topCollider
: topShape;
}
ShapeComputationDataBaking GetInputDataFromAuthoringComponent(PhysicsShapeAuthoring shape, Entity colliderEntity)
{
GameObject shapeGameObject = shape.gameObject;
var body = GetPrimaryBody(shapeGameObject, out bool hasBodyComponent, out bool isStaticBody);
var child = shapeGameObject;
var shapeInstanceID = shape.GetInstanceID();
var bodyEntity = GetEntity(body, TransformUsageFlags.Dynamic);
// prepare the static root
if (isStaticBody)
{
var staticRootMarker = CreateAdditionalEntity(TransformUsageFlags.Dynamic, true, "StaticRootBakeMarker");
AddComponent(staticRootMarker, new BakeStaticRoot() { Body = bodyEntity, ConvertedBodyInstanceID = body.transform.GetInstanceID() });
}
// Track dependencies to the transforms
Transform shapeTransform = GetComponent<Transform>(shape);
Transform bodyTransform = GetComponent<Transform>(body);
var instance = new ColliderInstanceBaking
{
AuthoringComponentId = shapeInstanceID,
BodyEntity = bodyEntity,
ShapeEntity = GetEntity(shapeGameObject, TransformUsageFlags.Dynamic),
ChildEntity = GetEntity(child, TransformUsageFlags.Dynamic),
BodyFromShape = ColliderInstanceBaking.GetCompoundFromChild(shapeTransform, bodyTransform),
};
var data = GenerateComputationData(shape, instance, colliderEntity);
data.Instance.ConvertedAuthoringInstanceID = shapeInstanceID;
data.Instance.ConvertedBodyInstanceID = bodyTransform.GetInstanceID();
var rb = FindFirstEnabledAncestor(shapeGameObject, PhysicsShapeExtensions_NonBursted.s_RigidbodiesBuffer);
var pb = FindFirstEnabledAncestor(shapeGameObject, PhysicsShapeExtensions_NonBursted.s_PhysicsBodiesBuffer);
// The Rigidbody cannot know about the Physics Shape Component. We need to take responsibility of baking the collider.
if (rb || (!rb && !pb) && body == shapeGameObject)
{
GetComponents(physicsShapeComponents);
GetComponents(colliderComponents);
// We need to check that there are no other colliders in the same object, if so, only the first one should do this, otherwise there will be 2 bakers adding this to the entity
// This will be needed to trigger BuildCompoundColliderBakingSystem
// If they are legacy Colliders and PhysicsShapeAuthoring in the same object, the PhysicsShapeAuthoring will add this
if (colliderComponents.Count == 0 && physicsShapeComponents.Count > 0 && physicsShapeComponents[0].GetInstanceID() == shapeInstanceID)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
// // Rigid Body bakes always add the PhysicsWorldIndex component and process transform
if (!hasBodyComponent)
{
AddSharedComponent(entity, new PhysicsWorldIndex());
PostProcessTransform(bodyTransform);
}
AddComponent(entity, new PhysicsCompoundData()
{
AssociateBlobToBody = false,
ConvertedBodyInstanceID = shapeInstanceID,
Hash = default,
});
AddComponent<PhysicsRootBaked>(entity);
AddComponent<PhysicsCollider>(entity);
}
}
return data;
}
Material ProduceMaterial(PhysicsShapeAuthoring shape)
{
var materialTemplate = shape.MaterialTemplate;
if (materialTemplate != null)
DependsOn(materialTemplate);
return shape.GetMaterial();
}
CollisionFilter ProduceCollisionFilter(PhysicsShapeAuthoring shape)
{
return shape.GetFilter();
}
UnityEngine.Mesh GetMesh(PhysicsShapeAuthoring shape, out float4x4 childToShape)
{
var mesh = shape.CustomMesh;
childToShape = float4x4.identity;
if (mesh == null)
{
// Try to get a mesh in the children
var filter = GetComponentInChildren<MeshFilter>();
if (filter != null && filter.sharedMesh != null)
{
mesh = filter.sharedMesh;
var childTransform = GetComponent<Transform>(filter);
childToShape = math.mul(shape.transform.worldToLocalMatrix, childTransform.localToWorldMatrix);;
}
}
if (mesh == null)
{
throw new InvalidOperationException(
$"No {nameof(PhysicsShapeAuthoring.CustomMesh)} assigned on {shape.name}."
);
}
DependsOn(mesh);
return mesh;
}
bool GetMeshes(PhysicsShapeAuthoring shape, out List<UnityEngine.Mesh> meshes, out List<float4x4> childrenToShape)
{
meshes = new List<UnityEngine.Mesh>();
childrenToShape = new List<float4x4>();
if (shape.CustomMesh != null)
{
meshes.Add(shape.CustomMesh);
childrenToShape.Add(float4x4.identity);
}
// Try to get all the meshes in the children
var meshFilters = GetComponentsInChildren<MeshFilter>();
foreach (var meshFilter in meshFilters)
{
if (meshFilter != null && meshFilter.sharedMesh != null)
{
var shapeAuthoring = GetComponent<PhysicsShapeAuthoring>(meshFilter);
if (shapeAuthoring != null && shapeAuthoring != shape)
{
// Skip this case, since it will be treated independently
continue;
}
meshes.Add(meshFilter.sharedMesh);
// Don't calculate the children to shape if not needed, to avoid approximation that could prevent collider to be shared
if (shape.transform.localToWorldMatrix.Equals(meshFilter.transform.localToWorldMatrix))
childrenToShape.Add(float4x4.identity);
else
{
var transform = math.mul(shape.transform.worldToLocalMatrix, meshFilter.transform.localToWorldMatrix);
childrenToShape.Add(transform);
}
DependsOn(meshes.Last());
}
}
return meshes.Count > 0;
}
UnityEngine.Mesh CombineMeshes(PhysicsShapeAuthoring shape, List<UnityEngine.Mesh> meshes, List<float4x4> childrenToShape)
{
var instances = new List<CombineInstance>();
var numVertices = 0;
for (var i = 0; i < meshes.Count; ++i)
{
var currentMesh = meshes[i];
var currentChildToShape = childrenToShape[i];
if (!currentMesh.IsValidForConversion(shape.gameObject))
{
throw new InvalidOperationException(
$"Mesh '{currentMesh}' assigned on {shape.name} is not readable. Ensure that you have enabled Read/Write on its import settings."
);
}
// Combine submeshes manually
numVertices += meshes[i].vertexCount;
var combinedSubmeshes = new UnityEngine.Mesh();
combinedSubmeshes.vertices = currentMesh.vertices;
var combinedIndices = new List<int>();
for (int indexSubMesh = 0; indexSubMesh < meshes[i].subMeshCount; ++indexSubMesh)
{
combinedIndices.AddRange(currentMesh.GetIndices(indexSubMesh));
}
combinedSubmeshes.SetIndices(combinedIndices, MeshTopology.Triangles, 0);
combinedSubmeshes.RecalculateNormals();
var instance = new CombineInstance
{
mesh = combinedSubmeshes,
transform = currentChildToShape,
};
instances.Add(instance);
}
var mesh = new UnityEngine.Mesh();
mesh.indexFormat = numVertices > UInt16.MaxValue ? UnityEngine.Rendering.IndexFormat.UInt32 : UnityEngine.Rendering.IndexFormat.UInt16;
mesh.CombineMeshes(instances.ToArray());
mesh.RecalculateBounds();
return mesh;
}
private ShapeComputationDataBaking GenerateComputationData(PhysicsShapeAuthoring shape, ColliderInstanceBaking colliderInstance, Entity colliderEntity)
{
var res = new ShapeComputationDataBaking
{
Instance = colliderInstance,
Material = ProduceMaterial(shape),
CollisionFilter = ProduceCollisionFilter(shape),
ForceUniqueIdentifier = shape.ForceUnique ? (uint)shape.GetInstanceID() : 0u
};
var transform = shape.transform;
var localToWorld = transform.localToWorldMatrix;
var shapeToWorld = shape.GetShapeToWorldMatrix();
EulerAngles orientation;
res.ShapeType = shape.ShapeType;
switch (shape.ShapeType)
{
case ShapeType.Box:
{
res.BoxProperties = shape.GetBoxProperties(out orientation)
.BakeToBodySpace(localToWorld, shapeToWorld, orientation);
break;
}
case ShapeType.Capsule:
{
res.CapsuleProperties = shape.GetCapsuleProperties()
.BakeToBodySpace(localToWorld, shapeToWorld)
.ToRuntime();
break;
}
case ShapeType.Sphere:
{
res.SphereProperties = shape.GetSphereProperties(out orientation)
.BakeToBodySpace(localToWorld, shapeToWorld, ref orientation);
break;
}
case ShapeType.Cylinder:
{
res.CylinderProperties = shape.GetCylinderProperties(out orientation)
.BakeToBodySpace(localToWorld, shapeToWorld, orientation);
break;
}
case ShapeType.Plane:
{
shape.GetPlaneProperties(out var center, out var size, out orientation);
PhysicsShapeExtensions.BakeToBodySpace(
center, size, orientation, localToWorld, shapeToWorld,
out res.PlaneVertices.c0, out res.PlaneVertices.c1, out res.PlaneVertices.c2, out res.PlaneVertices.c3
);
break;
}
case ShapeType.ConvexHull:
{
res.ConvexHullProperties.Filter = res.CollisionFilter;
res.ConvexHullProperties.Material = res.Material;
res.ConvexHullProperties.GenerationParameters = shape.ConvexHullGenerationParameters.ToRunTime();
CreateMeshAuthoringData(shape, colliderEntity);
break;
}
case ShapeType.Mesh:
{
res.MeshProperties.Filter = res.CollisionFilter;
res.MeshProperties.Material = res.Material;
CreateMeshAuthoringData(shape, colliderEntity);
break;
}
}
return res;
}
private void CreateMeshAuthoringData(PhysicsShapeAuthoring shape, Entity colliderEntity)
{
if (GetMeshes(shape, out var meshes, out var childrenToShape))
{
// Combine all detected meshes into a single one
var mesh = CombineMeshes(shape, meshes, childrenToShape);
if (!mesh.IsValidForConversion(shape.gameObject))
{
throw new InvalidOperationException(
$"Mesh '{mesh}' assigned on {shape.name} is not readable. Ensure that you have enabled Read/Write on its import settings."
);
}
var bakeFromShape = shape.GetLocalToShapeMatrix();
var meshBakingData = new PhysicsMeshAuthoringData()
{
Convex = shape.ShapeType == ShapeType.ConvexHull,
Mesh = mesh,
BakeFromShape = bakeFromShape,
MeshBounds = mesh.bounds,
ChildToShape = float4x4.identity
};
AddComponent(colliderEntity, meshBakingData);
}
else
{
throw new InvalidOperationException(
$"No {nameof(PhysicsShapeAuthoring.CustomMesh)} or {nameof(MeshFilter.sharedMesh)} assigned on {shape.name}."
);
}
}
public override void Bake(PhysicsShapeAuthoring authoring)
{
var shapeBakingData = new PhysicsColliderAuthoringData();
// First pass
Profiler.BeginSample("Collect Inputs from Authoring Components");
if (ShouldConvertShape(authoring))
{
// We can have multiple Colliders of the same type on the same game object, so instead of adding the components to the baking entity
// we add the components to an additional entity. These new entities will be processed by the baking system
var colliderEntity = CreateAdditionalEntity(TransformUsageFlags.None, true);
shapeBakingData.ShapeComputationalData = GetInputDataFromAuthoringComponent(authoring, colliderEntity);
AddComponent(colliderEntity, shapeBakingData);
// The data will be filled in by the BaseShapeBakingSystem, but we add it here so it gets reverted from the entity if the collider component is deleted
AddComponent(colliderEntity, new PhysicsColliderBakedData()
{
BodyEntity = shapeBakingData.ShapeComputationalData.Instance.BodyEntity,
BodyFromShape = shapeBakingData.ShapeComputationalData.Instance.BodyFromShape,
ChildEntity = shapeBakingData.ShapeComputationalData.Instance.ChildEntity,
// It is a leaf if the Shape Entity equals Body Entity
IsLeafEntityBody = (shapeBakingData.ShapeComputationalData.Instance.ShapeEntity.Equals(shapeBakingData.ShapeComputationalData.Instance.BodyEntity))
});
}
Profiler.EndSample();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7b2bb3849ee2c24ea6f8416bc3c8ee0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Authoring
{
/// <summary> The physics body authoring. This class cannot be inherited. </summary>
#if UNITY_2021_2_OR_NEWER
[Icon(k_IconPath)]
#endif
[AddComponentMenu("Entities/Physics/Physics Body")]
[DisallowMultipleComponent]
public sealed class PhysicsBodyAuthoring : MonoBehaviour
{
const string k_IconPath = "Packages/com.unity.physics/Unity.Physics.Editor/Editor Default Resources/Icons/d_Rigidbody@64.png";
PhysicsBodyAuthoring() {}
public BodyMotionType MotionType { get => m_MotionType; set => m_MotionType = value; }
[SerializeField]
[Tooltip("Specifies whether the body should be fully physically simulated, moved directly, or fixed in place.")]
BodyMotionType m_MotionType;
public BodySmoothing Smoothing { get => m_Smoothing; set => m_Smoothing = value; }
[SerializeField]
[Tooltip("Specifies how this body's motion in its graphics representation should be smoothed when the rendering framerate is greater than the fixed step rate used by physics.")]
BodySmoothing m_Smoothing = BodySmoothing.None;
const float k_MinimumMass = 0.001f;
public float Mass
{
get => m_MotionType == BodyMotionType.Dynamic ? m_Mass : float.PositiveInfinity;
set => m_Mass = math.max(k_MinimumMass, value);
}
[SerializeField]
float m_Mass = 1.0f;
public float LinearDamping { get => m_LinearDamping; set => m_LinearDamping = math.max(0f, value); }
[SerializeField]
[Tooltip("This is applied to a body's linear velocity reducing it over time.")]
float m_LinearDamping = 0.01f;
public float AngularDamping { get => m_AngularDamping; set => m_AngularDamping = math.max(0f, value); }
[SerializeField]
[Tooltip("This is applied to a body's angular velocity reducing it over time.")]
float m_AngularDamping = 0.05f;
public float3 InitialLinearVelocity { get => m_InitialLinearVelocity; set => m_InitialLinearVelocity = value; }
[SerializeField]
[Tooltip("The initial linear velocity of the body in world space")]
float3 m_InitialLinearVelocity = float3.zero;
public float3 InitialAngularVelocity { get => m_InitialAngularVelocity; set => m_InitialAngularVelocity = value; }
[SerializeField]
[Tooltip("This represents the initial rotation speed around each axis in the local motion space of the body i.e. around the center of mass")]
float3 m_InitialAngularVelocity = float3.zero;
public float GravityFactor
{
get => m_MotionType == BodyMotionType.Dynamic ? m_GravityFactor : 0f;
set => m_GravityFactor = value;
}
[SerializeField]
[Tooltip("Scales the amount of gravity to apply to this body.")]
float m_GravityFactor = 1f;
public bool OverrideDefaultMassDistribution
{
#pragma warning disable 618
get => m_OverrideDefaultMassDistribution;
set => m_OverrideDefaultMassDistribution = value;
#pragma warning restore 618
}
[SerializeField]
[Tooltip("Default mass distribution is based on the shapes associated with this body.")]
bool m_OverrideDefaultMassDistribution;
public MassDistribution CustomMassDistribution
{
get => new MassDistribution
{
Transform = new RigidTransform(m_Orientation, m_CenterOfMass),
InertiaTensor =
m_MotionType == BodyMotionType.Dynamic ? m_InertiaTensor : new float3(float.PositiveInfinity)
};
set
{
m_CenterOfMass = value.Transform.pos;
m_Orientation.SetValue(value.Transform.rot);
m_InertiaTensor = value.InertiaTensor;
#pragma warning disable 618
m_OverrideDefaultMassDistribution = true;
#pragma warning restore 618
}
}
[SerializeField]
float3 m_CenterOfMass;
[SerializeField]
EulerAngles m_Orientation = EulerAngles.Default;
[SerializeField]
// Default value to solid unit sphere : https://en.wikipedia.org/wiki/List_of_moments_of_inertia
float3 m_InertiaTensor = new float3(2f / 5f);
public uint WorldIndex { get => m_WorldIndex; set => m_WorldIndex = value; }
[SerializeField]
[Tooltip("The index of the physics world this body belongs to. Default physics world has index 0.")]
uint m_WorldIndex = 0;
public CustomPhysicsBodyTags CustomTags { get => m_CustomTags; set => m_CustomTags = value; }
[SerializeField]
CustomPhysicsBodyTags m_CustomTags = CustomPhysicsBodyTags.Nothing;
void OnEnable()
{
// included so tick box appears in Editor
}
void OnValidate()
{
m_Mass = math.max(k_MinimumMass, m_Mass);
m_LinearDamping = math.max(m_LinearDamping, 0f);
m_AngularDamping = math.max(m_AngularDamping, 0f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccea9ea98e38942e0b0938c27ed1903e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,929 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;
namespace Unity.Physics.Authoring
{
public sealed class UnimplementedShapeException : NotImplementedException
{
public UnimplementedShapeException(ShapeType shapeType)
: base($"Unknown shape type {shapeType} requires explicit implementation") {}
}
#if UNITY_2021_2_OR_NEWER
[Icon(k_IconPath)]
#endif
[AddComponentMenu("Entities/Physics/Physics Shape")]
public sealed class PhysicsShapeAuthoring : MonoBehaviour, IInheritPhysicsMaterialProperties, ISerializationCallbackReceiver
{
const string k_IconPath = "Packages/com.unity.physics/Unity.Physics.Editor/Editor Default Resources/Icons/d_BoxCollider@64.png";
PhysicsShapeAuthoring() {}
[Serializable]
struct CylindricalProperties
{
public float Height;
public float Radius;
[HideInInspector]
public int Axis;
}
static readonly int[] k_NextAxis = { 1, 2, 0 };
public ShapeType ShapeType => m_ShapeType;
[SerializeField]
ShapeType m_ShapeType = ShapeType.Box;
[SerializeField]
float3 m_PrimitiveCenter;
[SerializeField]
float3 m_PrimitiveSize = new float3(1f, 1f, 1f);
[SerializeField]
EulerAngles m_PrimitiveOrientation = EulerAngles.Default;
[SerializeField]
[ExpandChildren]
CylindricalProperties m_Capsule = new CylindricalProperties { Height = 1f, Radius = 0.5f, Axis = 2 };
[SerializeField]
[ExpandChildren]
CylindricalProperties m_Cylinder = new CylindricalProperties { Height = 1f, Radius = 0.5f, Axis = 2 };
[SerializeField]
[Tooltip("How many sides the convex cylinder shape should have.")]
[Range(CylinderGeometry.MinSideCount, CylinderGeometry.MaxSideCount)]
int m_CylinderSideCount = 20;
[SerializeField]
float m_SphereRadius = 0.5f;
public ConvexHullGenerationParameters ConvexHullGenerationParameters => m_ConvexHullGenerationParameters;
[SerializeField]
[Tooltip(
"Specifies the minimum weight of a skinned vertex assigned to this shape and/or its transform children required for it to be included for automatic detection. " +
"A value of 0 will include all points with any weight assigned to this shape's hierarchy."
)]
[Range(0f, 1f)]
float m_MinimumSkinnedVertexWeight = 0.1f;
[SerializeField]
[ExpandChildren]
ConvexHullGenerationParameters m_ConvexHullGenerationParameters = ConvexHullGenerationParameters.Default.ToAuthoring();
// TODO: remove this accessor in favor of GetRawVertices() when blob data is serializable
internal UnityEngine.Mesh CustomMesh => m_CustomMesh;
[SerializeField]
[Tooltip("If no custom mesh is specified, then one will be generated using this body's rendered meshes.")]
UnityEngine.Mesh m_CustomMesh;
public bool ForceUnique { get => m_ForceUnique; set => m_ForceUnique = value; }
[SerializeField]
bool m_ForceUnique;
public PhysicsMaterialTemplate MaterialTemplate { get => m_Material.Template; set => m_Material.Template = value; }
PhysicsMaterialTemplate IInheritPhysicsMaterialProperties.Template
{
get => m_Material.Template;
set => m_Material.Template = value;
}
public bool OverrideCollisionResponse { get => m_Material.OverrideCollisionResponse; set => m_Material.OverrideCollisionResponse = value; }
public CollisionResponsePolicy CollisionResponse { get => m_Material.CollisionResponse; set => m_Material.CollisionResponse = value; }
public bool OverrideFriction { get => m_Material.OverrideFriction; set => m_Material.OverrideFriction = value; }
public PhysicsMaterialCoefficient Friction { get => m_Material.Friction; set => m_Material.Friction = value; }
public bool OverrideRestitution
{
get => m_Material.OverrideRestitution;
set => m_Material.OverrideRestitution = value;
}
public PhysicsMaterialCoefficient Restitution
{
get => m_Material.Restitution;
set => m_Material.Restitution = value;
}
public bool OverrideBelongsTo
{
get => m_Material.OverrideBelongsTo;
set => m_Material.OverrideBelongsTo = value;
}
public PhysicsCategoryTags BelongsTo
{
get => m_Material.BelongsTo;
set => m_Material.BelongsTo = value;
}
public bool OverrideCollidesWith
{
get => m_Material.OverrideCollidesWith;
set => m_Material.OverrideCollidesWith = value;
}
public PhysicsCategoryTags CollidesWith
{
get => m_Material.CollidesWith;
set => m_Material.CollidesWith = value;
}
public bool OverrideCustomTags
{
get => m_Material.OverrideCustomTags;
set => m_Material.OverrideCustomTags = value;
}
public CustomPhysicsMaterialTags CustomTags { get => m_Material.CustomTags; set => m_Material.CustomTags = value; }
[SerializeField]
PhysicsMaterialProperties m_Material = new PhysicsMaterialProperties(true);
public BoxGeometry GetBoxProperties() => GetBoxProperties(out _);
internal BoxGeometry GetBoxProperties(out EulerAngles orientation)
{
orientation = m_PrimitiveOrientation;
return new BoxGeometry
{
Center = m_PrimitiveCenter,
Size = m_PrimitiveSize,
Orientation = m_PrimitiveOrientation,
BevelRadius = m_ConvexHullGenerationParameters.BevelRadius
};
}
void GetCylindricalProperties(
CylindricalProperties props,
out float3 center, out float height, out float radius, out EulerAngles orientation,
bool rebuildOrientation
)
{
center = m_PrimitiveCenter;
var lookVector = math.mul(m_PrimitiveOrientation, new float3 { [props.Axis] = 1f });
// use previous axis so forward will prefer up
var upVector = math.mul(m_PrimitiveOrientation, new float3 { [k_NextAxis[k_NextAxis[props.Axis]]] = 1f });
orientation = m_PrimitiveOrientation;
if (rebuildOrientation && props.Axis != 2)
orientation.SetValue(quaternion.LookRotation(lookVector, upVector));
radius = props.Radius;
height = props.Height;
}
public CapsuleGeometryAuthoring GetCapsuleProperties()
{
GetCylindricalProperties(
m_Capsule, out var center, out var height, out var radius, out var orientationEuler, m_ShapeType != ShapeType.Capsule
);
return new CapsuleGeometryAuthoring
{
OrientationEuler = orientationEuler,
Center = center,
Height = height,
Radius = radius
};
}
public CylinderGeometry GetCylinderProperties() => GetCylinderProperties(out _);
internal CylinderGeometry GetCylinderProperties(out EulerAngles orientation)
{
GetCylindricalProperties(
m_Cylinder, out var center, out var height, out var radius, out orientation, m_ShapeType != ShapeType.Cylinder
);
return new CylinderGeometry
{
Center = center,
Height = height,
Radius = radius,
Orientation = orientation,
BevelRadius = m_ConvexHullGenerationParameters.BevelRadius,
SideCount = m_CylinderSideCount
};
}
public SphereGeometry GetSphereProperties(out quaternion orientation)
{
var result = GetSphereProperties(out EulerAngles euler);
orientation = euler;
return result;
}
internal SphereGeometry GetSphereProperties(out EulerAngles orientation)
{
orientation = m_PrimitiveOrientation;
return new SphereGeometry
{
Center = m_PrimitiveCenter,
Radius = m_SphereRadius
};
}
public void GetPlaneProperties(out float3 center, out float2 size, out quaternion orientation)
{
GetPlaneProperties(out center, out size, out EulerAngles euler);
orientation = euler;
}
internal void GetPlaneProperties(out float3 center, out float2 size, out EulerAngles orientation)
{
center = m_PrimitiveCenter;
orientation = m_PrimitiveOrientation;
if (m_ShapeType == ShapeType.Plane)
{
size = m_PrimitiveSize.xz;
return;
}
UpdateCapsuleAxis();
var look = m_Capsule.Axis;
var nextAx = k_NextAxis[look];
var prevAx = k_NextAxis[k_NextAxis[look]];
var ax2 = m_PrimitiveSize[nextAx] > m_PrimitiveSize[prevAx] ? nextAx : prevAx;
size = new float2(m_PrimitiveSize[ax2], m_PrimitiveSize[look]);
var up = k_NextAxis[ax2] == look ? k_NextAxis[look] : k_NextAxis[ax2];
var offset = quaternion.LookRotation(new float3 { [look] = 1f }, new float3 { [up] = 1f });
orientation.SetValue(math.mul(m_PrimitiveOrientation, offset));
}
static readonly HashSet<int> s_BoneIDs = new HashSet<int>();
static readonly HashSet<Transform> s_BonesInHierarchy = new HashSet<Transform>();
static readonly List<Vector3> s_Vertices = new List<Vector3>(65535);
static readonly List<int> s_Indices = new List<int>(65535);
static UnityEngine.Mesh ReusableBakeMesh =>
s_ReusableBakeMesh ??
(s_ReusableBakeMesh = new UnityEngine.Mesh { hideFlags = HideFlags.HideAndDontSave });
static UnityEngine.Mesh s_ReusableBakeMesh;
public void GetConvexHullProperties(NativeList<float3> pointCloud) =>
GetConvexHullProperties(pointCloud, true, default, default, default, default);
internal void GetConvexHullProperties(
NativeList<float3> pointCloud, bool validate,
NativeList<HashableShapeInputs> inputs, NativeList<int> allSkinIndices, NativeList<float> allBlendShapeWeights,
HashSet<UnityEngine.Mesh> meshAssets
)
{
if (pointCloud.IsCreated)
pointCloud.Clear();
if (inputs.IsCreated)
inputs.Clear();
if (allSkinIndices.IsCreated)
allSkinIndices.Clear();
if (allBlendShapeWeights.IsCreated)
allBlendShapeWeights.Clear();
meshAssets?.Clear();
if (m_CustomMesh != null)
{
if (validate && !m_CustomMesh.IsValidForConversion(gameObject))
return;
AppendMeshPropertiesToNativeBuffers(
transform.localToWorldMatrix, m_CustomMesh, pointCloud, default, validate, inputs, meshAssets
);
}
else
{
using (var scope = new GetActiveChildrenScope<MeshFilter>(this, transform))
{
foreach (var meshFilter in scope.Buffer)
{
if (scope.IsChildActiveAndBelongsToShape(meshFilter, validate))
{
AppendMeshPropertiesToNativeBuffers(
meshFilter.transform.localToWorldMatrix, meshFilter.sharedMesh, pointCloud, default, validate, inputs, meshAssets
);
}
}
}
using (var skinnedPoints = new NativeList<float3>(8192, Allocator.Temp))
using (var skinnedInputs = new NativeList<HashableShapeInputs>(8, Allocator.Temp))
{
GetAllSkinnedPointsInHierarchyBelongingToShape(
this, skinnedPoints, validate, skinnedInputs, allSkinIndices, allBlendShapeWeights
);
if (pointCloud.IsCreated)
pointCloud.AddRange(skinnedPoints.AsArray());
if (inputs.IsCreated)
inputs.AddRange(skinnedInputs.AsArray());
}
}
}
internal static void GetAllSkinnedPointsInHierarchyBelongingToShape(
PhysicsShapeAuthoring shape, NativeList<float3> pointCloud, bool validate,
NativeList<HashableShapeInputs> inputs, NativeList<int> allIncludedIndices, NativeList<float> allBlendShapeWeights
)
{
if (pointCloud.IsCreated)
pointCloud.Clear();
if (inputs.IsCreated)
inputs.Clear();
// get all the transforms that belong to this shape
s_BonesInHierarchy.Clear();
using (var scope = new GetActiveChildrenScope<Transform>(shape, shape.transform))
{
foreach (var bone in scope.Buffer)
{
if (scope.IsChildActiveAndBelongsToShape(bone))
s_BonesInHierarchy.Add(bone);
}
}
// find all skinned mesh renderers in which this shape's transform might be a bone
using (var scope = new GetActiveChildrenScope<SkinnedMeshRenderer>(shape, shape.transform.root))
{
foreach (var skin in scope.Buffer)
{
var mesh = skin.sharedMesh;
if (
!skin.enabled
|| mesh == null
|| validate && !mesh.IsValidForConversion(shape.gameObject)
|| !scope.IsChildActiveAndBelongsToShape(skin)
)
continue;
// get indices of this shape's transform hierarchy in skinned mesh's bone array
s_BoneIDs.Clear();
var bones = skin.bones;
for (int i = 0, count = bones.Length; i < count; ++i)
{
if (s_BonesInHierarchy.Contains(bones[i]))
s_BoneIDs.Add(i);
}
if (s_BoneIDs.Count == 0)
continue;
// sample the vertices
if (pointCloud.IsCreated)
{
skin.BakeMesh(ReusableBakeMesh);
ReusableBakeMesh.GetVertices(s_Vertices);
}
// add all vertices weighted to at least one bone in this shape's transform hierarchy
var bonesPerVertex = mesh.GetBonesPerVertex(); // Allocator.None
var weights = mesh.GetAllBoneWeights(); // Allocator.None
var vertexIndex = 0;
var weightsOffset = 0;
var shapeFromSkin = math.mul(shape.transform.worldToLocalMatrix, skin.transform.localToWorldMatrix);
var includedIndices = new NativeList<int>(mesh.vertexCount, Allocator.Temp);
foreach (var weightCount in bonesPerVertex)
{
var totalWeight = 0f;
for (var i = 0; i < weightCount; ++i)
{
var weight = weights[weightsOffset + i];
if (s_BoneIDs.Contains(weight.boneIndex))
totalWeight += weight.weight;
}
if (totalWeight > shape.m_MinimumSkinnedVertexWeight)
{
if (pointCloud.IsCreated)
pointCloud.Add(math.mul(shapeFromSkin, new float4(s_Vertices[vertexIndex], 1f)).xyz);
includedIndices.Add(vertexIndex);
}
weightsOffset += weightCount;
++vertexIndex;
}
if (!inputs.IsCreated || !allIncludedIndices.IsCreated || !allBlendShapeWeights.IsCreated)
continue;
var blendShapeWeights = new NativeArray<float>(mesh.blendShapeCount, Allocator.Temp);
for (var i = 0; i < blendShapeWeights.Length; ++i)
blendShapeWeights[i] = skin.GetBlendShapeWeight(i);
var data = HashableShapeInputs.FromSkinnedMesh(
mesh, shapeFromSkin, includedIndices.AsArray(), allIncludedIndices, blendShapeWeights, allBlendShapeWeights
);
inputs.Add(data);
}
}
s_BonesInHierarchy.Clear();
}
public void GetMeshProperties(NativeList<float3> vertices, NativeList<int3> triangles) =>
GetMeshProperties(vertices, triangles, true, default);
internal void GetMeshProperties(
NativeList<float3> vertices, NativeList<int3> triangles, bool validate, NativeList<HashableShapeInputs> inputs, HashSet<UnityEngine.Mesh> meshAssets = null
)
{
if (vertices.IsCreated)
vertices.Clear();
if (triangles.IsCreated)
triangles.Clear();
if (inputs.IsCreated)
inputs.Clear();
meshAssets?.Clear();
if (m_CustomMesh != null)
{
if (validate && !m_CustomMesh.IsValidForConversion(gameObject))
return;
AppendMeshPropertiesToNativeBuffers(
transform.localToWorldMatrix, m_CustomMesh, vertices, triangles, validate, inputs, meshAssets
);
}
else
{
using (var scope = new GetActiveChildrenScope<MeshFilter>(this, transform))
{
foreach (var meshFilter in scope.Buffer)
{
if (scope.IsChildActiveAndBelongsToShape(meshFilter, validate))
{
AppendMeshPropertiesToNativeBuffers(
meshFilter.transform.localToWorldMatrix, meshFilter.sharedMesh, vertices, triangles, validate, inputs, meshAssets
);
}
}
}
}
}
void AppendMeshPropertiesToNativeBuffers(
float4x4 localToWorld, UnityEngine.Mesh mesh, NativeList<float3> vertices, NativeList<int3> triangles, bool validate,
NativeList<HashableShapeInputs> inputs, HashSet<UnityEngine.Mesh> meshAssets
)
{
if (mesh == null || validate && !mesh.IsValidForConversion(gameObject))
return;
var childToShape = math.mul(transform.worldToLocalMatrix, localToWorld);
AppendMeshPropertiesToNativeBuffers(childToShape, mesh, vertices, triangles, inputs, meshAssets);
}
internal static void AppendMeshPropertiesToNativeBuffers(
float4x4 childToShape, UnityEngine.Mesh mesh, NativeList<float3> vertices, NativeList<int3> triangles,
NativeList<HashableShapeInputs> inputs, HashSet<UnityEngine.Mesh> meshAssets
)
{
var offset = 0u;
#if UNITY_EDITOR
// TODO: when min spec is 2020.1, collect all meshes and their data via single Burst job rather than one at a time
using (var meshData = UnityEditor.MeshUtility.AcquireReadOnlyMeshData(mesh))
#else
using (var meshData = UnityEngine.Mesh.AcquireReadOnlyMeshData(mesh))
#endif
{
if (vertices.IsCreated)
{
offset = (uint)vertices.Length;
var tmpVertices = new NativeArray<Vector3>(meshData[0].vertexCount, Allocator.Temp);
meshData[0].GetVertices(tmpVertices);
if (vertices.Capacity < vertices.Length + tmpVertices.Length)
vertices.Capacity = vertices.Length + tmpVertices.Length;
foreach (var v in tmpVertices)
vertices.Add(math.mul(childToShape, new float4(v, 1f)).xyz);
}
if (triangles.IsCreated)
{
switch (meshData[0].indexFormat)
{
case IndexFormat.UInt16:
var indices16 = meshData[0].GetIndexData<ushort>();
var numTriangles = indices16.Length / 3;
if (triangles.Capacity < triangles.Length + numTriangles)
triangles.Capacity = triangles.Length + numTriangles;
for (var sm = 0; sm < meshData[0].subMeshCount; ++sm)
{
var subMesh = meshData[0].GetSubMesh(sm);
for (int i = subMesh.indexStart, count = 0; count < subMesh.indexCount; i += 3, count += 3)
triangles.Add((int3) new uint3(offset + indices16[i], offset + indices16[i + 1], offset + indices16[i + 2]));
}
break;
case IndexFormat.UInt32:
var indices32 = meshData[0].GetIndexData<uint>();
numTriangles = indices32.Length / 3;
if (triangles.Capacity < triangles.Length + numTriangles)
triangles.Capacity = triangles.Length + numTriangles;
for (var sm = 0; sm < meshData[0].subMeshCount; ++sm)
{
var subMesh = meshData[0].GetSubMesh(sm);
for (int i = subMesh.indexStart, count = 0; count < subMesh.indexCount; i += 3, count += 3)
triangles.Add((int3) new uint3(offset + indices32[i], offset + indices32[i + 1], offset + indices32[i + 2]));
}
break;
}
}
}
if (inputs.IsCreated)
inputs.Add(HashableShapeInputs.FromMesh(mesh, childToShape));
meshAssets?.Add(mesh);
}
void UpdateCapsuleAxis()
{
var cmax = math.cmax(m_PrimitiveSize);
var cmin = math.cmin(m_PrimitiveSize);
if (cmin == cmax)
return;
m_Capsule.Axis = m_PrimitiveSize.GetMaxAxis();
}
void UpdateCylinderAxis() => m_Cylinder.Axis = m_PrimitiveSize.GetDeviantAxis();
void Sync(ref CylindricalProperties props)
{
props.Height = m_PrimitiveSize[props.Axis];
props.Radius = 0.5f * math.max(
m_PrimitiveSize[k_NextAxis[props.Axis]],
m_PrimitiveSize[k_NextAxis[k_NextAxis[props.Axis]]]
);
}
void SyncCapsuleProperties()
{
if (m_ShapeType == ShapeType.Box || m_ShapeType == ShapeType.Plane)
UpdateCapsuleAxis();
else
m_Capsule.Axis = 2;
Sync(ref m_Capsule);
}
void SyncCylinderProperties()
{
if (m_ShapeType == ShapeType.Box || m_ShapeType == ShapeType.Plane)
UpdateCylinderAxis();
else
m_Cylinder.Axis = 2;
Sync(ref m_Cylinder);
}
void SyncSphereProperties()
{
m_SphereRadius = 0.5f * math.cmax(m_PrimitiveSize);
}
public void SetBox(BoxGeometry geometry)
{
var euler = m_PrimitiveOrientation;
euler.SetValue(geometry.Orientation);
SetBox(geometry, euler);
}
internal void SetBox(BoxGeometry geometry, EulerAngles orientation)
{
m_ShapeType = ShapeType.Box;
m_PrimitiveCenter = geometry.Center;
m_PrimitiveSize = math.max(geometry.Size, new float3());
m_PrimitiveOrientation = orientation;
m_ConvexHullGenerationParameters.BevelRadius = geometry.BevelRadius;
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
public void SetCapsule(CapsuleGeometryAuthoring geometry)
{
m_ShapeType = ShapeType.Capsule;
m_PrimitiveCenter = geometry.Center;
m_PrimitiveOrientation = geometry.OrientationEuler;
var radius = math.max(0f, geometry.Radius);
var height = math.max(0f, geometry.Height);
m_PrimitiveSize = new float3(radius * 2f, radius * 2f, height);
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
public void SetCylinder(CylinderGeometry geometry)
{
var euler = m_PrimitiveOrientation;
euler.SetValue(geometry.Orientation);
SetCylinder(geometry, euler);
}
internal void SetCylinder(CylinderGeometry geometry, EulerAngles orientation)
{
m_ShapeType = ShapeType.Cylinder;
m_PrimitiveCenter = geometry.Center;
m_PrimitiveOrientation = orientation;
geometry.Radius = math.max(0f, geometry.Radius);
geometry.Height = math.max(0f, geometry.Height);
m_PrimitiveSize = new float3(geometry.Radius * 2f, geometry.Radius * 2f, geometry.Height);
m_ConvexHullGenerationParameters.BevelRadius = geometry.BevelRadius;
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
public void SetSphere(SphereGeometry geometry, quaternion orientation)
{
var euler = m_PrimitiveOrientation;
euler.SetValue(orientation);
SetSphere(geometry, euler);
}
internal void SetSphere(SphereGeometry geometry)
{
SetSphere(geometry, m_PrimitiveOrientation);
}
internal void SetSphere(SphereGeometry geometry, EulerAngles orientation)
{
m_ShapeType = ShapeType.Sphere;
m_PrimitiveCenter = geometry.Center;
var radius = math.max(0f, geometry.Radius);
m_PrimitiveSize = new float3(2f * radius, 2f * radius, 2f * radius);
m_PrimitiveOrientation = orientation;
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
public void SetPlane(float3 center, float2 size, quaternion orientation)
{
var euler = m_PrimitiveOrientation;
euler.SetValue(orientation);
SetPlane(center, size, euler);
}
internal void SetPlane(float3 center, float2 size, EulerAngles orientation)
{
m_ShapeType = ShapeType.Plane;
m_PrimitiveCenter = center;
m_PrimitiveOrientation = orientation;
m_PrimitiveSize = new float3(size.x, 0f, size.y);
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
public void SetConvexHull(
ConvexHullGenerationParameters hullGenerationParameters, float minimumSkinnedVertexWeight
)
{
m_MinimumSkinnedVertexWeight = minimumSkinnedVertexWeight;
SetConvexHull(hullGenerationParameters);
}
public void SetConvexHull(ConvexHullGenerationParameters hullGenerationParameters, UnityEngine.Mesh customMesh = null)
{
m_ShapeType = ShapeType.ConvexHull;
m_CustomMesh = customMesh;
hullGenerationParameters.OnValidate();
m_ConvexHullGenerationParameters = hullGenerationParameters;
}
public void SetMesh(UnityEngine.Mesh mesh = null)
{
m_ShapeType = ShapeType.Mesh;
m_CustomMesh = mesh;
}
void OnEnable()
{
// included so tick box appears in Editor
}
const int k_LatestVersion = 1;
[SerializeField]
int m_SerializedVersion = 0;
void ISerializationCallbackReceiver.OnBeforeSerialize() {}
void ISerializationCallbackReceiver.OnAfterDeserialize() => UpgradeVersionIfNecessary();
#pragma warning disable 618
void UpgradeVersionIfNecessary()
{
if (m_SerializedVersion < k_LatestVersion)
{
// old data from version < 1 have been removed
if (m_SerializedVersion < 1)
m_SerializedVersion = 1;
}
}
#pragma warning restore 618
static void Validate(ref CylindricalProperties props)
{
props.Height = math.max(0f, props.Height);
props.Radius = math.max(0f, props.Radius);
}
void OnValidate()
{
UpgradeVersionIfNecessary();
m_PrimitiveSize = math.max(m_PrimitiveSize, new float3());
Validate(ref m_Capsule);
Validate(ref m_Cylinder);
switch (m_ShapeType)
{
case ShapeType.Box:
SetBox(GetBoxProperties(out var orientation), orientation);
break;
case ShapeType.Capsule:
SetCapsule(GetCapsuleProperties());
break;
case ShapeType.Cylinder:
SetCylinder(GetCylinderProperties(out orientation), orientation);
break;
case ShapeType.Sphere:
SetSphere(GetSphereProperties(out orientation), orientation);
break;
case ShapeType.Plane:
GetPlaneProperties(out var center, out var size2D, out orientation);
SetPlane(center, size2D, orientation);
break;
case ShapeType.ConvexHull:
case ShapeType.Mesh:
break;
default:
throw new UnimplementedShapeException(m_ShapeType);
}
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
m_CylinderSideCount =
math.clamp(m_CylinderSideCount, CylinderGeometry.MinSideCount, CylinderGeometry.MaxSideCount);
m_ConvexHullGenerationParameters.OnValidate();
PhysicsMaterialProperties.OnValidate(ref m_Material, true);
}
// matrix to transform point from shape space into world space
public float4x4 GetShapeToWorldMatrix() =>
new float4x4(Math.DecomposeRigidBodyTransform(transform.localToWorldMatrix));
// matrix to transform point from object's local transform matrix into shape space
internal float4x4 GetLocalToShapeMatrix() =>
math.mul(math.inverse(GetShapeToWorldMatrix()), transform.localToWorldMatrix);
internal unsafe void BakePoints(NativeArray<float3> points)
{
var localToShapeQuantized = GetLocalToShapeMatrix();
using (var aabb = new NativeArray<Aabb>(1, Allocator.TempJob))
{
new PhysicsShapeExtensions.GetAabbJob { Points = points, Aabb = aabb }.Run();
HashableShapeInputs.GetQuantizedTransformations(localToShapeQuantized, aabb[0], out localToShapeQuantized);
}
using (var bakedPoints = new NativeArray<float3>(points.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory))
{
new BakePointsJob
{
Points = points,
LocalToShape = localToShapeQuantized,
Output = bakedPoints
}.Schedule(points.Length, 16).Complete();
UnsafeUtility.MemCpy(points.GetUnsafePtr(), bakedPoints.GetUnsafePtr(), points.Length * UnsafeUtility.SizeOf<float3>());
}
}
[BurstCompile]
struct BakePointsJob : IJobParallelFor
{
[Collections.ReadOnly]
public NativeArray<float3> Points;
public float4x4 LocalToShape;
public NativeArray<float3> Output;
public void Execute(int index) => Output[index] = math.mul(LocalToShape, new float4(Points[index], 1f)).xyz;
}
/// <summary>
/// Fit this shape to render geometry in its GameObject hierarchy. Children in the hierarchy will
/// influence the result if they have enabled MeshRenderer components or have vertices bound to
/// them on a SkinnedMeshRenderer. Children will only count as influences if this shape is the
/// first ancestor shape in their hierarchy. As such, you should add shape components to all
/// GameObjects that should have them before you call this method on any of them.
/// </summary>
///
/// <exception cref="UnimplementedShapeException"> Thrown when an Unimplemented Shape error
/// condition occurs. </exception>
///
/// <param name="minimumSkinnedVertexWeight"> (Optional)
/// The minimum total weight that a vertex in a skinned mesh must have assigned to this object
/// and/or any of its influencing children. </param>
public void FitToEnabledRenderMeshes(float minimumSkinnedVertexWeight = 0f)
{
var shapeType = m_ShapeType;
m_MinimumSkinnedVertexWeight = minimumSkinnedVertexWeight;
using (var points = new NativeList<float3>(65535, Allocator.Persistent))
{
// temporarily un-assign custom mesh and assume this shape is a convex hull
var customMesh = m_CustomMesh;
m_CustomMesh = null;
GetConvexHullProperties(points, Application.isPlaying, default, default, default, default);
m_CustomMesh = customMesh;
if (points.Length == 0)
return;
// TODO: find best rotation, particularly if any points came from skinned mesh
var orientation = quaternion.identity;
var bounds = new Bounds(points[0], float3.zero);
for (int i = 1, count = points.Length; i < count; ++i)
bounds.Encapsulate(points[i]);
SetBox(
new BoxGeometry
{
Center = bounds.center,
Size = bounds.size,
Orientation = orientation,
BevelRadius = m_ConvexHullGenerationParameters.BevelRadius
}
);
}
switch (shapeType)
{
case ShapeType.Capsule:
SetCapsule(GetCapsuleProperties());
break;
case ShapeType.Cylinder:
SetCylinder(GetCylinderProperties(out var orientation), orientation);
break;
case ShapeType.Sphere:
SetSphere(GetSphereProperties(out orientation), orientation);
break;
case ShapeType.Plane:
// force recalculation of plane orientation by making it think shape type is out of date
m_ShapeType = ShapeType.Box;
GetPlaneProperties(out var center, out var size2D, out orientation);
SetPlane(center, size2D, orientation);
break;
case ShapeType.Box:
case ShapeType.ConvexHull:
case ShapeType.Mesh:
m_ShapeType = shapeType;
break;
default:
throw new UnimplementedShapeException(shapeType);
}
SyncCapsuleProperties();
SyncCylinderProperties();
SyncSphereProperties();
}
[Conditional("UNITY_EDITOR")]
void Reset()
{
#if UNITY_EDITOR
InitializeConvexHullGenerationParameters();
FitToEnabledRenderMeshes(m_MinimumSkinnedVertexWeight);
// TODO: also pick best primitive shape
UnityEditor.SceneView.RepaintAll();
#endif
}
public void InitializeConvexHullGenerationParameters()
{
var pointCloud = new NativeList<float3>(65535, Allocator.Temp);
GetConvexHullProperties(pointCloud, false, default, default, default, default);
m_ConvexHullGenerationParameters.InitializeToRecommendedAuthoringValues(pointCloud.AsArray());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b275e5f92732148048d7b77e264ac30e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;
namespace Unity.Physics.Authoring
{
[CreateAssetMenu(menuName = "Unity Physics/Custom Physics Material Tag Names", fileName = "Custom Material Tag Names", order = 506)]
public sealed partial class CustomPhysicsMaterialTagNames : ScriptableObject, ITagNames
{
CustomPhysicsMaterialTagNames() {}
public IReadOnlyList<string> TagNames => m_TagNames;
[SerializeField]
[FormerlySerializedAs("m_FlagNames")]
string[] m_TagNames = Enumerable.Range(0, 8).Select(i => string.Empty).ToArray();
void OnValidate()
{
if (m_TagNames.Length != 8)
Array.Resize(ref m_TagNames, 8);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c8956dd10f66427faa4a4067c5dd97d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7c7a0159cb0d5433b90c970978f6cf5c, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using Unity.Mathematics;
namespace Unity.Physics.Authoring
{
[Serializable]
public struct CustomPhysicsMaterialTags : IEquatable<CustomPhysicsMaterialTags>
{
public static CustomPhysicsMaterialTags Everything => new CustomPhysicsMaterialTags { Value = unchecked((byte)~0) };
public static CustomPhysicsMaterialTags Nothing => new CustomPhysicsMaterialTags { Value = 0 };
public bool Tag00;
public bool Tag01;
public bool Tag02;
public bool Tag03;
public bool Tag04;
public bool Tag05;
public bool Tag06;
public bool Tag07;
internal bool this[int i]
{
get
{
SafetyChecks.CheckInRangeAndThrow(i, new int2(0, 7), nameof(i));
switch (i)
{
case 0: return Tag00;
case 1: return Tag01;
case 2: return Tag02;
case 3: return Tag03;
case 4: return Tag04;
case 5: return Tag05;
case 6: return Tag06;
case 7: return Tag07;
default: return default;
}
}
set
{
SafetyChecks.CheckInRangeAndThrow(i, new int2(0, 7), nameof(i));
switch (i)
{
case 0: Tag00 = value; break;
case 1: Tag01 = value; break;
case 2: Tag02 = value; break;
case 3: Tag03 = value; break;
case 4: Tag04 = value; break;
case 5: Tag05 = value; break;
case 6: Tag06 = value; break;
case 7: Tag07 = value; break;
}
}
}
public byte Value
{
get
{
var result = 0;
result |= (Tag00 ? 1 : 0) << 0;
result |= (Tag01 ? 1 : 0) << 1;
result |= (Tag02 ? 1 : 0) << 2;
result |= (Tag03 ? 1 : 0) << 3;
result |= (Tag04 ? 1 : 0) << 4;
result |= (Tag05 ? 1 : 0) << 5;
result |= (Tag06 ? 1 : 0) << 6;
result |= (Tag07 ? 1 : 0) << 7;
return (byte)result;
}
set
{
Tag00 = (value & (1 << 0)) != 0;
Tag01 = (value & (1 << 1)) != 0;
Tag02 = (value & (1 << 2)) != 0;
Tag03 = (value & (1 << 3)) != 0;
Tag04 = (value & (1 << 4)) != 0;
Tag05 = (value & (1 << 5)) != 0;
Tag06 = (value & (1 << 6)) != 0;
Tag07 = (value & (1 << 7)) != 0;
}
}
public bool Equals(CustomPhysicsMaterialTags other) => Value == other.Value;
public override bool Equals(object obj) => obj is CustomPhysicsMaterialTags other && Equals(other);
public override int GetHashCode() => Value;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05cffa1394b6f481ca7e686dfc6404b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
#if UNITY_EDITOR
using System.Linq;
using UnityEditor;
namespace Unity.Physics.Authoring
{
[InitializeOnLoad]
class EditorInitialization
{
static readonly string k_CustomDefine = "UNITY_PHYSICS_CUSTOM";
static EditorInitialization()
{
var definesStr = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
var defines = definesStr.Split(';').ToList();
var found = defines.Find(define => define.Equals(k_CustomDefine));
if (found == null)
{
defines.Add(k_CustomDefine);
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup,
string.Join(";", defines.ToArray()));
}
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 23f03a5c38aa4a6e81448a178cc0a09f
timeCreated: 1680128866

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f26f023234dd844797f8158565ecb4f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,155 @@
using System;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Authoring
{
[BakingType]
public struct JointEntityBaking : IComponentData
{
public Entity Entity;
}
public class BallAndSocketJoint : BaseJoint
{
// Editor only settings
[HideInInspector]
public bool EditPivots;
[Tooltip("If checked, PositionLocal will snap to match PositionInConnectedEntity")]
public bool AutoSetConnected = true;
public float3 PositionLocal;
public float3 PositionInConnectedEntity;
public virtual void UpdateAuto()
{
if (AutoSetConnected)
{
RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA);
PositionInConnectedEntity = math.transform(bFromA, PositionLocal);
}
}
}
public abstract class JointBaker<T> : Baker<T> where T : BaseJoint
{
protected PhysicsConstrainedBodyPair GetConstrainedBodyPair(BaseJoint authoring)
{
return new PhysicsConstrainedBodyPair(
GetEntity(TransformUsageFlags.Dynamic),
authoring.ConnectedBody == null ? Entity.Null : GetEntity(authoring.ConnectedBody, TransformUsageFlags.Dynamic),
authoring.EnableCollision
);
}
public Entity CreateJointEntity(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair, PhysicsJoint joint)
{
using (var joints = new NativeArray<PhysicsJoint>(1, Allocator.Temp) { [0] = joint })
using (var jointEntities = new NativeList<Entity>(1, Allocator.Temp))
{
CreateJointEntities(worldIndex, constrainedBodyPair, joints, jointEntities);
return jointEntities[0];
}
}
public uint GetWorldIndex(Component c)
{
uint worldIndex = 0;
if (c)
{
var physicsBody = GetComponent<PhysicsBodyAuthoring>(c);
if (physicsBody != null)
{
worldIndex = physicsBody.WorldIndex;
}
}
return worldIndex;
}
public uint GetWorldIndexFromBaseJoint(BaseJoint authoring)
{
var physicsBody = GetComponent<PhysicsBodyAuthoring>(authoring);
uint worldIndex = physicsBody.WorldIndex;
if (authoring.ConnectedBody == null)
{
return worldIndex;
}
var connectedBody = GetComponent<PhysicsBodyAuthoring>(authoring.ConnectedBody);
if (connectedBody != null)
{
Assertions.Assert.AreEqual(worldIndex, connectedBody.WorldIndex);
}
return worldIndex;
}
public void CreateJointEntities(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair, NativeArray<PhysicsJoint> joints, NativeList<Entity> newJointEntities)
{
if (!joints.IsCreated || joints.Length == 0)
return;
if (newJointEntities.IsCreated)
newJointEntities.Clear();
else
newJointEntities = new NativeList<Entity>(joints.Length, Allocator.Temp);
// create all new joints
var multipleJoints = joints.Length > 1;
var entity = GetEntity(TransformUsageFlags.Dynamic);
for (var i = 0; i < joints.Length; ++i)
{
var jointEntity = CreateAdditionalEntity(TransformUsageFlags.Dynamic);
AddSharedComponent(jointEntity, new PhysicsWorldIndex(worldIndex));
AddComponent(jointEntity, constrainedBodyPair);
AddComponent(jointEntity, joints[i]);
newJointEntities.Add(jointEntity);
if (GetComponent<ModifyJointLimitsAuthoring>() != null)
{
AddComponent(jointEntity, new JointEntityBaking()
{
Entity = entity
});
AddSharedComponentManaged(jointEntity, new ModifyJointLimits());
}
}
if (multipleJoints)
{
// set companion buffers for new joints
for (var i = 0; i < joints.Length; ++i)
{
var companions = AddBuffer<PhysicsJointCompanion>(newJointEntities[i]);
for (var j = 0; j < joints.Length; ++j)
{
if (i == j)
continue;
companions.Add(new PhysicsJointCompanion {JointEntity = newJointEntities[j]});
}
}
}
}
}
class BallAndSocketJointBaker : JointBaker<BallAndSocketJoint>
{
public override void Bake(BallAndSocketJoint authoring)
{
authoring.UpdateAuto();
var physicsJoint = PhysicsJoint.CreateBallAndSocket(authoring.PositionLocal, authoring.PositionInConnectedEntity);
physicsJoint.SetImpulseEventThresholdAllConstraints(authoring.MaxImpulse);
var constraintBodyPair = GetConstrainedBodyPair(authoring);
uint worldIndex = GetWorldIndexFromBaseJoint(authoring);
CreateJointEntity(worldIndex, constraintBodyPair, physicsJoint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28a669fa0e32f734c9d34661ccdfd58d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
using Unity.Mathematics;
namespace Unity.Physics.Authoring
{
public abstract class BaseJoint : BaseBodyPairConnector
{
public bool EnableCollision;
public float3 MaxImpulse = float.PositiveInfinity;
void OnEnable()
{
// included so tick box appears in Editor
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02bf6429cb18f3147ae5f7d8e3fd5f07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Authoring
{
public class FreeHingeJoint : BallAndSocketJoint
{
// Editor only settings
[HideInInspector]
public bool EditAxes;
public float3 HingeAxisLocal;
public float3 HingeAxisInConnectedEntity;
public override void UpdateAuto()
{
base.UpdateAuto();
if (AutoSetConnected)
{
RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA);
HingeAxisInConnectedEntity = math.mul(bFromA.rot, HingeAxisLocal);
}
}
}
class FreeHingeJointBaker : JointBaker<FreeHingeJoint>
{
public override void Bake(FreeHingeJoint authoring)
{
authoring.UpdateAuto();
Math.CalculatePerpendicularNormalized(authoring.HingeAxisLocal, out var perpendicularLocal, out _);
Math.CalculatePerpendicularNormalized(authoring.HingeAxisInConnectedEntity, out var perpendicularConnected, out _);
var physicsJoint = PhysicsJoint.CreateHinge(
new BodyFrame {Axis = authoring.HingeAxisLocal, Position = authoring.PositionLocal, PerpendicularAxis = perpendicularLocal},
new BodyFrame {Axis = authoring.HingeAxisInConnectedEntity, Position = authoring.PositionInConnectedEntity, PerpendicularAxis = perpendicularConnected }
);
physicsJoint.SetImpulseEventThresholdAllConstraints(authoring.MaxImpulse);
var constraintBodyPair = GetConstrainedBodyPair(authoring);
uint worldIndex = GetWorldIndexFromBaseJoint(authoring);
CreateJointEntity(worldIndex, constraintBodyPair, physicsJoint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5dc8c079bffa454ca607aac63ea7e7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,145 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace Unity.Physics.Authoring
{
// This Joint allows you to lock one or more of the 6 degrees of freedom of a constrained body.
// This is achieved by combining the appropriate lower level 'constraint atoms' to form the higher level Joint.
// In this case Linear and Angular constraint atoms are combined.
// One use-case for this Joint could be to restrict a 3d simulation to a 2d plane.
public class LimitDOFJoint : BaseJoint
{
public bool3 LockLinearAxes;
public bool3 LockAngularAxes;
public PhysicsJoint CreateLimitDOFJoint(RigidTransform offset)
{
var constraints = new FixedList512Bytes<Constraint>();
if (math.any(LockLinearAxes))
{
constraints.Add(new Constraint
{
ConstrainedAxes = LockLinearAxes,
Type = ConstraintType.Linear,
Min = 0,
Max = 0,
SpringFrequency = Constraint.DefaultSpringFrequency,
SpringDamping = Constraint.DefaultSpringDamping,
MaxImpulse = MaxImpulse,
});
}
if (math.any(LockAngularAxes))
{
constraints.Add(new Constraint
{
ConstrainedAxes = LockAngularAxes,
Type = ConstraintType.Angular,
Min = 0,
Max = 0,
SpringFrequency = Constraint.DefaultSpringFrequency,
SpringDamping = Constraint.DefaultSpringDamping,
MaxImpulse = MaxImpulse,
});
}
var joint = new PhysicsJoint
{
BodyAFromJoint = BodyFrame.Identity,
BodyBFromJoint = offset
};
joint.SetConstraints(constraints);
return joint;
}
}
class LimitDOFJointBaker : Baker<LimitDOFJoint>
{
public Entity CreateJointEntity(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair, PhysicsJoint joint)
{
using (var joints = new NativeArray<PhysicsJoint>(1, Allocator.Temp) { [0] = joint })
using (var jointEntities = new NativeList<Entity>(1, Allocator.Temp))
{
CreateJointEntities(worldIndex, constrainedBodyPair, joints, jointEntities);
return jointEntities[0];
}
}
public void CreateJointEntities(uint worldIndex, PhysicsConstrainedBodyPair constrainedBodyPair, NativeArray<PhysicsJoint> joints, NativeList<Entity> newJointEntities = default)
{
if (!joints.IsCreated || joints.Length == 0)
return;
if (newJointEntities.IsCreated)
newJointEntities.Clear();
else
newJointEntities = new NativeList<Entity>(joints.Length, Allocator.Temp);
// create all new joints
var multipleJoints = joints.Length > 1;
for (var i = 0; i < joints.Length; ++i)
{
var jointEntity = CreateAdditionalEntity(TransformUsageFlags.Dynamic);
AddSharedComponent(jointEntity, new PhysicsWorldIndex(worldIndex));
AddComponent(jointEntity, constrainedBodyPair);
AddComponent(jointEntity, joints[i]);
newJointEntities.Add(jointEntity);
}
if (multipleJoints)
{
// set companion buffers for new joints
for (var i = 0; i < joints.Length; ++i)
{
var companions = AddBuffer<PhysicsJointCompanion>(newJointEntities[i]);
for (var j = 0; j < joints.Length; ++j)
{
if (i == j)
continue;
companions.Add(new PhysicsJointCompanion {JointEntity = newJointEntities[j]});
}
}
}
}
protected PhysicsConstrainedBodyPair GetConstrainedBodyPair(LimitDOFJoint authoring)
{
return new PhysicsConstrainedBodyPair(
GetEntity(TransformUsageFlags.Dynamic),
authoring.ConnectedBody == null ? Entity.Null : GetEntity(authoring.ConnectedBody, TransformUsageFlags.Dynamic),
authoring.EnableCollision
);
}
public uint GetWorldIndex(Component c)
{
uint worldIndex = 0;
var physicsBody = GetComponent<PhysicsBodyAuthoring>(c);
if (physicsBody != null)
{
worldIndex = physicsBody.WorldIndex;
}
return worldIndex;
}
public override void Bake(LimitDOFJoint authoring)
{
if (!math.any(authoring.LockLinearAxes) && !math.any(authoring.LockAngularAxes))
return;
RigidTransform bFromA = math.mul(math.inverse(authoring.worldFromB), authoring.worldFromA);
PhysicsJoint physicsJoint = authoring.CreateLimitDOFJoint(bFromA);
var worldIndex = GetWorldIndex(authoring);
CreateJointEntity(
worldIndex,
GetConstrainedBodyPair(authoring),
physicsJoint
);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9457d04025dadec4b85ae39dad2e1822
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using Unity.Entities;
using static Unity.Physics.Math;
namespace Unity.Physics.Authoring
{
public class LimitedDistanceJoint : BallAndSocketJoint
{
public float MinDistance;
public float MaxDistance;
}
class LimitedDistanceJointBaker : JointBaker<LimitedDistanceJoint>
{
public override void Bake(LimitedDistanceJoint authoring)
{
authoring.UpdateAuto();
var physicsJoint = PhysicsJoint.CreateLimitedDistance(authoring.PositionLocal, authoring.PositionInConnectedEntity, new FloatRange(authoring.MinDistance, authoring.MaxDistance));
physicsJoint.SetImpulseEventThresholdAllConstraints(authoring.MaxImpulse);
var constraintBodyPair = GetConstrainedBodyPair(authoring);
uint worldIndex = GetWorldIndexFromBaseJoint(authoring);
CreateJointEntity(worldIndex, constraintBodyPair, physicsJoint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d79d98418b67aa44db932451943018a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using static Unity.Physics.Math;
namespace Unity.Physics.Authoring
{
public class LimitedHingeJoint : FreeHingeJoint
{
// Editor only settings
[HideInInspector]
public bool EditLimits;
public float3 PerpendicularAxisLocal;
public float3 PerpendicularAxisInConnectedEntity;
public float MinAngle;
public float MaxAngle;
public override void UpdateAuto()
{
base.UpdateAuto();
if (AutoSetConnected)
{
RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA);
HingeAxisInConnectedEntity = math.mul(bFromA.rot, HingeAxisLocal);
PerpendicularAxisInConnectedEntity = math.mul(bFromA.rot, PerpendicularAxisLocal);
}
}
}
class LimitedHingeJointBaker : JointBaker<LimitedHingeJoint>
{
public override void Bake(LimitedHingeJoint authoring)
{
authoring.UpdateAuto();
var physicsJoint = PhysicsJoint.CreateLimitedHinge(
new BodyFrame
{
Axis = math.normalize(authoring.HingeAxisLocal),
PerpendicularAxis = math.normalize(authoring.PerpendicularAxisLocal),
Position = authoring.PositionLocal
},
new BodyFrame
{
Axis = math.normalize(authoring.HingeAxisInConnectedEntity),
PerpendicularAxis = math.normalize(authoring.PerpendicularAxisInConnectedEntity),
Position = authoring.PositionInConnectedEntity
},
math.radians(new FloatRange(authoring.MinAngle, authoring.MaxAngle))
);
physicsJoint.SetImpulseEventThresholdAllConstraints(authoring.MaxImpulse);
var constraintBodyPair = GetConstrainedBodyPair(authoring);
uint worldIndex = GetWorldIndexFromBaseJoint(authoring);
CreateJointEntity(worldIndex, constraintBodyPair, physicsJoint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbaf9813ee7af2f41a5fd776598ab5b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Authoring;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using UnityEngine;
using FloatRange = Unity.Physics.Math.FloatRange;
namespace Unity.Physics.Authoring
{
// stores an initial value and a pair of scalar curves to apply to relevant constraints on the joint
struct ModifyJointLimits : ISharedComponentData, IEquatable<ModifyJointLimits>
{
public PhysicsJoint InitialValue;
public ParticleSystem.MinMaxCurve AngularRangeScalar;
public ParticleSystem.MinMaxCurve LinearRangeScalar;
public bool Equals(ModifyJointLimits other) =>
AngularRangeScalar.Equals(other.AngularRangeScalar) && LinearRangeScalar.Equals(other.LinearRangeScalar);
public override bool Equals(object obj) => obj is ModifyJointLimits other && Equals(other);
public override int GetHashCode() =>
unchecked((AngularRangeScalar.GetHashCode() * 397) ^ LinearRangeScalar.GetHashCode());
}
// an authoring component to add to a GameObject with one or more Joint
public class ModifyJointLimitsAuthoring : MonoBehaviour
{
public ParticleSystem.MinMaxCurve AngularRangeScalar = new ParticleSystem.MinMaxCurve(
1f,
min: new AnimationCurve(
new Keyframe(0f, 0f, 0f, 0f),
new Keyframe(2f, -2f, 0f, 0f),
new Keyframe(4f, 0f, 0f, 0f)
)
{
preWrapMode = WrapMode.Loop,
postWrapMode = WrapMode.Loop
},
max: new AnimationCurve(
new Keyframe(0f, 1f, 0f, 0f),
new Keyframe(2f, -1f, 0f, 0f),
new Keyframe(4f, 1f, 0f, 0f)
)
{
preWrapMode = WrapMode.Loop,
postWrapMode = WrapMode.Loop
}
);
public ParticleSystem.MinMaxCurve LinearRangeScalar = new ParticleSystem.MinMaxCurve(
1f,
min: new AnimationCurve(
new Keyframe(0f, 1f, 0f, 0f),
new Keyframe(2f, 0.5f, 0f, 0f),
new Keyframe(4f, 1f, 0f, 0f)
)
{
preWrapMode = WrapMode.Loop,
postWrapMode = WrapMode.Loop
},
max: new AnimationCurve(
new Keyframe(0f, 0.5f, 0f, 0f),
new Keyframe(2f, 0f, 0f, 0f),
new Keyframe(4f, 0.5f, 0f, 0f)
)
{
preWrapMode = WrapMode.Loop,
postWrapMode = WrapMode.Loop
}
);
}
[BakingType]
public class ModifyJointLimitsBakingData : IComponentData
{
public ParticleSystem.MinMaxCurve AngularRangeScalar;
public ParticleSystem.MinMaxCurve LinearRangeScalar;
}
class ModifyJointLimitsBaker : Baker<ModifyJointLimitsAuthoring>
{
public override void Bake(ModifyJointLimitsAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponentObject(entity, new ModifyJointLimitsBakingData
{
AngularRangeScalar = authoring.AngularRangeScalar,
LinearRangeScalar = authoring.LinearRangeScalar
});
}
}
// after joints have been converted, find the entities they produced and add ModifyJointLimits to them
[UpdateAfter(typeof(EndJointBakingSystem))]
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
partial struct ModifyJointLimitsBakingSystem : ISystem
{
private EntityQuery _ModifyJointLimitsBakingDataQuery;
private EntityQuery _JointEntityBakingQuery;
public void OnCreate(ref SystemState state)
{
_ModifyJointLimitsBakingDataQuery = state.GetEntityQuery(new EntityQueryDesc
{
All = new[] {ComponentType.ReadOnly<ModifyJointLimitsBakingData>()},
Options = EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab
});
_JointEntityBakingQuery = state.GetEntityQuery(new EntityQueryDesc
{
All = new[] {ComponentType.ReadOnly<JointEntityBaking>()}
});
_ModifyJointLimitsBakingDataQuery.AddChangedVersionFilter(typeof(ModifyJointLimitsBakingData));
_JointEntityBakingQuery.AddChangedVersionFilter(typeof(JointEntityBaking));
}
public void OnUpdate(ref SystemState state)
{
if (_ModifyJointLimitsBakingDataQuery.IsEmpty && _JointEntityBakingQuery.IsEmpty)
{
return;
}
// Collect all the joints
NativeParallelMultiHashMap<Entity, (Entity, PhysicsJoint)> jointsLookUp =
new NativeParallelMultiHashMap<Entity, (Entity, PhysicsJoint)>(10, Allocator.TempJob);
foreach (var(jointEntity, physicsJoint, entity) in SystemAPI
.Query<RefRO<JointEntityBaking>, RefRO<PhysicsJoint>>().WithEntityAccess()
.WithOptions(EntityQueryOptions.IncludeDisabledEntities | EntityQueryOptions.IncludePrefab))
{
jointsLookUp.Add(jointEntity.ValueRO.Entity, (entity, physicsJoint.ValueRO));
}
foreach (var(modifyJointLimits, entity) in SystemAPI.Query<ModifyJointLimitsBakingData>()
.WithEntityAccess().WithOptions(EntityQueryOptions.IncludeDisabledEntities |
EntityQueryOptions.IncludePrefab))
{
var angularModification = new ParticleSystem.MinMaxCurve(
multiplier: math.radians(modifyJointLimits.AngularRangeScalar.curveMultiplier),
min: modifyJointLimits.AngularRangeScalar.curveMin,
max: modifyJointLimits.AngularRangeScalar.curveMax
);
foreach (var joint in jointsLookUp.GetValuesForKey(entity))
{
state.EntityManager.SetSharedComponentManaged(joint.Item1, new ModifyJointLimits
{
InitialValue = joint.Item2,
AngularRangeScalar = angularModification,
LinearRangeScalar = modifyJointLimits.LinearRangeScalar
});
}
}
jointsLookUp.Dispose();
}
}
// apply an animated effect to the limits on supported types of joints
[RequireMatchingQueriesForUpdate]
[UpdateInGroup(typeof(PhysicsSystemGroup), OrderLast = true)]
partial struct ModifyJointLimitsSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
var time = (float)SystemAPI.Time.ElapsedTime;
foreach (var(joint, modification) in SystemAPI.Query<RefRW<PhysicsJoint>, ModifyJointLimits>())
{
var animatedAngularScalar = new FloatRange(
modification.AngularRangeScalar.curveMin.Evaluate(time),
modification.AngularRangeScalar.curveMax.Evaluate(time)
);
var animatedLinearScalar = new FloatRange(
modification.LinearRangeScalar.curveMin.Evaluate(time),
modification.LinearRangeScalar.curveMax.Evaluate(time)
);
// in each case, get relevant properties from the initial value based on joint type, and apply scalar
switch (joint.ValueRW.JointType)
{
// Custom type could be anything, so this demo just applies changes to all constraints
case JointType.Custom:
var constraints = modification.InitialValue.GetConstraints();
for (var i = 0; i < constraints.Length; i++)
{
var constraint = constraints[i];
var isAngular = constraint.Type == ConstraintType.Angular;
var scalar = math.select(animatedLinearScalar, animatedAngularScalar, isAngular);
var constraintRange = (FloatRange)(new float2(constraint.Min, constraint.Max) * scalar);
constraint.Min = constraintRange.Min;
constraint.Max = constraintRange.Max;
constraints[i] = constraint;
}
joint.ValueRW.SetConstraints(constraints);
break;
// other types have corresponding getters/setters to retrieve more meaningful data
case JointType.LimitedDistance:
var distanceRange = modification.InitialValue.GetLimitedDistanceRange();
joint.ValueRW.SetLimitedDistanceRange(distanceRange * (float2)animatedLinearScalar);
break;
case JointType.LimitedHinge:
var angularRange = modification.InitialValue.GetLimitedHingeRange();
joint.ValueRW.SetLimitedHingeRange(angularRange * (float2)animatedAngularScalar);
break;
case JointType.Prismatic:
var distanceOnAxis = modification.InitialValue.GetPrismaticRange();
joint.ValueRW.SetPrismaticRange(distanceOnAxis * (float2)animatedLinearScalar);
break;
// ragdoll joints are composed of two separate joints with different meanings
case JointType.RagdollPrimaryCone:
modification.InitialValue.GetRagdollPrimaryConeAndTwistRange(
out var maxConeAngle,
out var angularTwistRange
);
joint.ValueRW.SetRagdollPrimaryConeAndTwistRange(
maxConeAngle * animatedAngularScalar.Max,
angularTwistRange * (float2)animatedAngularScalar
);
break;
case JointType.RagdollPerpendicularCone:
var angularPlaneRange = modification.InitialValue.GetRagdollPerpendicularConeRange();
joint.ValueRW.SetRagdollPerpendicularConeRange(angularPlaneRange *
(float2)animatedAngularScalar);
break;
// remaining types have no limits on their Constraint atoms to meaningfully modify
case JointType.BallAndSocket:
case JointType.Fixed:
case JointType.Hinge:
break;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 09435b2483c7b48409764517bbfff081
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using Unity.Entities;
using Unity.Mathematics;
using static Unity.Physics.Math;
namespace Unity.Physics.Authoring
{
public class PrismaticJoint : BallAndSocketJoint
{
public float3 AxisLocal;
public float3 AxisInConnectedEntity;
public float3 PerpendicularAxisLocal;
public float3 PerpendicularAxisInConnectedEntity;
public float MinDistanceOnAxis;
public float MaxDistanceOnAxis;
public override void UpdateAuto()
{
base.UpdateAuto();
if (AutoSetConnected)
{
RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA);
AxisInConnectedEntity = math.mul(bFromA.rot, AxisLocal);
PerpendicularAxisInConnectedEntity = math.mul(bFromA.rot, PerpendicularAxisLocal);
}
}
}
class PrismaticJointBaker : JointBaker<PrismaticJoint>
{
public override void Bake(PrismaticJoint authoring)
{
authoring.UpdateAuto();
var physicsJoint = PhysicsJoint.CreatePrismatic(
new BodyFrame
{
Axis = authoring.AxisLocal,
PerpendicularAxis = authoring.PerpendicularAxisLocal,
Position = authoring.PositionLocal
},
new BodyFrame
{
Axis = authoring.AxisInConnectedEntity,
PerpendicularAxis = authoring.PerpendicularAxisInConnectedEntity,
Position = authoring.PositionInConnectedEntity
},
new FloatRange(authoring.MinDistanceOnAxis, authoring.MaxDistanceOnAxis)
);
physicsJoint.SetImpulseEventThresholdAllConstraints(authoring.MaxImpulse);
var constraintBodyPair = GetConstrainedBodyPair(authoring);
uint worldIndex = GetWorldIndexFromBaseJoint(authoring);
CreateJointEntity(worldIndex, constraintBodyPair, physicsJoint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b226e7ae02ee34c458747d6c2ee8456c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More