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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -80,7 +80,7 @@ namespace Plugins.JNGame.Sync.Frame
public override Task OnInit()
{
Physics.autoSimulation = false;
Physics.simulationMode = SimulationMode.Script;
Physics.autoSyncTransforms = false;
this.OnReset();
return Task.CompletedTask;
@@ -108,6 +108,8 @@ namespace Plugins.JNGame.Sync.Frame
this._nLocalFrame = 0;
this._nServerFrame = 0;
this._nLocalRunFrame = 0;
this.dtTotal = 0;
this.dtInputTotal = 0;
Physics.SyncTransforms();
EventDispatcher.Event.Dispatch(JNSyncFrameEvent.CREATE);
@@ -174,9 +176,9 @@ namespace Plugins.JNGame.Sync.Frame
}
});
//执行下一帧物理
Physics.Simulate((float)dt / 1000);
Physics.SyncTransforms();
// //执行下一帧物理
// Physics.Simulate((float)dt / 1000);
// Physics.SyncTransforms();
}

View File

@@ -42,7 +42,7 @@ namespace Plugins.JNGame.Util
public async UniTask<T> Post<T,TA>(string url,TA data)
{
var request = UnityWebRequest.Post($"{this._config.BaseURL}/{url}",JsonConvert.SerializeObject(data));
var request = UnityWebRequest.PostWwwForm($"{this._config.BaseURL}/{url}",JsonConvert.SerializeObject(data));
return JsonConvert.DeserializeObject<T>((await request.SendWebRequest()).downloadHandler.text);
}

View File

@@ -11,6 +11,8 @@ GameObject:
- component: {fileID: 664841175096201691}
- component: {fileID: 664841175096201690}
- component: {fileID: 664841175096201689}
- component: {fileID: 574656058432384995}
- component: {fileID: 2113456302054558747}
m_Layer: 0
m_Name: Sphere 1
m_TagString: Untagged
@@ -25,13 +27,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 6.03, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &664841175096201690
MeshFilter:
@@ -83,3 +85,51 @@ MeshRenderer:
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!135 &574656058432384995
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!54 &2113456302054558747
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0

View File

@@ -11,6 +11,8 @@ GameObject:
- component: {fileID: 664841175096201691}
- component: {fileID: 664841175096201690}
- component: {fileID: 664841175096201689}
- component: {fileID: -289448904210163489}
- component: {fileID: -1515477822795860743}
m_Layer: 0
m_Name: Sphere 2
m_TagString: Untagged
@@ -25,13 +27,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 6.03, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &664841175096201690
MeshFilter:
@@ -83,3 +85,51 @@ MeshRenderer:
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!54 &-289448904210163489
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!135 &-1515477822795860743
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}

View File

@@ -11,6 +11,8 @@ GameObject:
- component: {fileID: 664841175096201691}
- component: {fileID: 664841175096201690}
- component: {fileID: 664841175096201689}
- component: {fileID: 5459639529223246069}
- component: {fileID: 6482499274382216103}
m_Layer: 0
m_Name: Sphere
m_TagString: Untagged
@@ -25,13 +27,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 6.03, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &664841175096201690
MeshFilter:
@@ -83,3 +85,51 @@ MeshRenderer:
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!135 &5459639529223246069
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!54 &6482499274382216103
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 664841175096201695}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5f759e05d198dbb4fa441d263f243edb
guid: 636828e4e4b4ac1468433f7433083e68
folderAsset: yes
DefaultImporter:
externalObjects: {}

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:

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