重新导入obi

This commit is contained in:
2026-04-06 11:35:18 +08:00
parent 05fa2d6e5e
commit ae3002a0e2
1643 changed files with 232496 additions and 13 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b8154151b8aa747e3870dae448f70d4b
folderAsset: yes
timeCreated: 1511453393
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ChildrenOnly : MultiPropertyAttribute
{
#if UNITY_EDITOR
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
float height = 0;
SerializedProperty it = property;
int depth = it.depth;
it.NextVisible(true);
do
{
EditorGUI.PropertyField(new Rect(position.x,position.y+height,position.width,EditorGUIUtility.singleLineHeight),it,true);
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}while (it.NextVisible(false) && it.depth != depth);
}
internal override float? GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float height = -EditorGUIUtility.standardVerticalSpacing;
SerializedProperty it = property;
int depth = it.depth;
it.NextVisible(true);
do
{
height += EditorGUI.GetPropertyHeight(it, label) + EditorGUIUtility.standardVerticalSpacing;
}while (it.NextVisible(false) && it.depth != depth);
return height;
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 37995b0258e3041208a4b13bd6feb31c
timeCreated: 1511965883
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
#if UNITY_EDITOR
[System.AttributeUsage(System.AttributeTargets.Field)]
public class DisplayAs : MultiPropertyAttribute
{
string name;
public DisplayAs(string name)
{
this.name = name;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.PropertyField(position,property,new GUIContent(name),true);
}
}
#endif
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: dd2e6690a4e514f0b93596b416ea6e1a
timeCreated: 1515069828
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class Indent : MultiPropertyAttribute
{
#if UNITY_EDITOR
internal override void OnPreGUI(Rect position, SerializedProperty property)
{
EditorGUI.indentLevel++;
}
internal override void OnPostGUI(Rect position, SerializedProperty property)
{
EditorGUI.indentLevel--;
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a02d36b0684b3461fad08bed56e9798c
timeCreated: 1511529530
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Reflection;
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class InspectorButtonAttribute : PropertyAttribute
{
public static float kDefaultButtonWidth = 80;
public readonly string MethodName;
private float _buttonWidth = kDefaultButtonWidth;
public float ButtonWidth
{
get { return _buttonWidth; }
set { _buttonWidth = value; }
}
public InspectorButtonAttribute(string MethodName)
{
this.MethodName = MethodName;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(InspectorButtonAttribute))]
public class InspectorButtonPropertyDrawer : PropertyDrawer
{
private MethodInfo _eventMethodInfo = null;
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
{
InspectorButtonAttribute inspectorButtonAttribute = (InspectorButtonAttribute)attribute;
Rect buttonRect = new Rect(position.x + (position.width - inspectorButtonAttribute.ButtonWidth) * 0.5f, position.y, inspectorButtonAttribute.ButtonWidth, position.height);
if (GUI.Button(buttonRect, label.text))
{
System.Type eventOwnerType = prop.serializedObject.targetObject.GetType();
string eventName = inspectorButtonAttribute.MethodName;
if (_eventMethodInfo == null)
_eventMethodInfo = eventOwnerType.GetMethod(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (_eventMethodInfo != null)
_eventMethodInfo.Invoke(prop.serializedObject.targetObject, null);
else
Debug.LogWarning(string.Format("InspectorButton: Unable to find method {0} in {1}", eventName, eventOwnerType));
}
}
}
#endif
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bada34fec41c44b4b90af1fe27ca7a95
timeCreated: 1440029519
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class MinMaxAttribute : MultiPropertyAttribute
{
float min;
float max;
public MinMaxAttribute(float min, float max)
{
this.min = min;
this.max = max;
}
#if UNITY_EDITOR
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType == SerializedPropertyType.Vector2){
float minValue = property.vector2Value.x;
float maxValue = property.vector2Value.y;
EditorGUI.MinMaxSlider(position, label, ref minValue, ref maxValue, min, max);
var vec = Vector2.zero;
vec.x = minValue;
vec.y = maxValue;
property.vector2Value = vec;
}else{
EditorGUI.LabelField(position, label.text, "Use MinMaxAttribute with Vector2.");
}
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,29 @@
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class MultiDelayed : MultiPropertyAttribute
{
#if UNITY_EDITOR
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
if (property.propertyType == SerializedPropertyType.Float)
EditorGUI.DelayedFloatField(position, property, label);
else if (property.propertyType == SerializedPropertyType.Integer)
EditorGUI.DelayedIntField(position, property, label);
else if (property.propertyType == SerializedPropertyType.String)
EditorGUI.DelayedTextField(position, property, label);
else
EditorGUI.LabelField(position, label.text, "Use MultiRange with float or int.");
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 17d0e7e43f7f844aba84497d6385c920
timeCreated: 1511969575
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public abstract class MultiPropertyAttribute : PropertyAttribute
{
#if UNITY_EDITOR
public IOrderedEnumerable<object> stored = null;
public virtual void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.PropertyField(position,property,label);
}
internal virtual void OnPreGUI(Rect position, SerializedProperty property){}
internal virtual void OnPostGUI(Rect position, SerializedProperty property){}
internal virtual bool IsVisible(SerializedProperty property){return true;}
internal virtual float? GetPropertyHeight( SerializedProperty property, GUIContent label){return null;}
#endif
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(MultiPropertyAttribute),true)]
public class MultiPropertyDrawer : PropertyDrawer
{
private MultiPropertyAttribute RetrieveAttributes()
{
MultiPropertyAttribute mAttribute = attribute as MultiPropertyAttribute;
// Get the attribute list, sorted by "order".
if (mAttribute.stored == null)
{
mAttribute.stored = fieldInfo.GetCustomAttributes(typeof(MultiPropertyAttribute), false).OrderBy(s => ((PropertyAttribute)s).order);
}
return mAttribute;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
MultiPropertyAttribute mAttribute = RetrieveAttributes();
// If the attribute is invisible, regain the standard vertical spacing.
foreach (MultiPropertyAttribute attr in mAttribute.stored)
if (!attr.IsVisible(property))
return -EditorGUIUtility.standardVerticalSpacing;
// In case no attribute returns a modified height, return the property's default one:
float height = base.GetPropertyHeight(property, label);
// Check if any of the attributes wants to modify height:
foreach (object atr in mAttribute.stored)
{
if (atr as MultiPropertyAttribute != null)
{
var tempheight = ((MultiPropertyAttribute)atr).GetPropertyHeight(property, label);
if (tempheight.HasValue)
{
height = tempheight.Value;
break;
}
}
}
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
MultiPropertyAttribute mAttribute = RetrieveAttributes();
// Calls to IsVisible. If it returns false for any attribute, the property will not be rendered.
foreach (MultiPropertyAttribute attr in mAttribute.stored)
if (!attr.IsVisible(property)) return;
// Calls to OnPreRender before the last attribute draws the UI.
foreach (MultiPropertyAttribute attr in mAttribute.stored)
attr.OnPreGUI(position,property);
// The last attribute is in charge of actually drawing something:
((MultiPropertyAttribute)mAttribute.stored.Last()).OnGUI(position,property,label);
// Calls to OnPostRender after the last attribute draws the UI. These are called in inverse order.
foreach (MultiPropertyAttribute attr in mAttribute.stored.Reverse())
attr.OnPostGUI(position,property);
}
}
#endif
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 71f4de68ec5de40edbf3e4f24c49ba18
timeCreated: 1511952779
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class MultiRange : MultiPropertyAttribute
{
float min;
float max;
public MultiRange(float min, float max)
{
this.min = min;
this.max = max;
}
#if UNITY_EDITOR
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
if (property.propertyType == SerializedPropertyType.Float)
EditorGUI.Slider(position, property, min, max, label);
else if (property.propertyType == SerializedPropertyType.Integer)
EditorGUI.IntSlider(position, property, (int)min, (int)max, label);
else
EditorGUI.LabelField(position, label.text, "Use MultiRange with float or int.");
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ab2178abf0a6343e19742febdd9784e3
timeCreated: 1511954360
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,126 @@
using System;
using System.Reflection;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class SerializeProperty : PropertyAttribute
{
public string PropertyName { get; private set; }
public SerializeProperty(string propertyName)
{
PropertyName = propertyName;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SerializeProperty))]
public class SerializePropertyAttributeDrawer : PropertyDrawer
{
private PropertyInfo propertyFieldInfo = null;
private object target = null;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (target == null)
target = GetSource(property);
// Find the property field using reflection, in order to get access to its getter/setter.
if (propertyFieldInfo == null)
propertyFieldInfo = target.GetType().GetProperty(((SerializeProperty)attribute).PropertyName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (propertyFieldInfo != null)
{
// Retrieve the value using the property getter:
object value = propertyFieldInfo.GetValue(target,null);
// Draw the property:
EditorGUI.BeginProperty(position,label,property);
EditorGUI.BeginChangeCheck();
value = DrawProperty(position,property.propertyType,propertyFieldInfo.PropertyType,value,label);
// If any changes were detected, call the property setter:
if (EditorGUI.EndChangeCheck() && propertyFieldInfo != null)
{
// Record object state for undo:
Undo.RecordObject(property.serializedObject.targetObject, "Inspector");
// Call property setter:
propertyFieldInfo.SetValue(target,value,null);
// Record prefab modification:
PrefabUtility.RecordPrefabInstancePropertyModifications(property.serializedObject.targetObject);
}
EditorGUI.EndProperty();
}else
{
EditorGUI.LabelField(position,"Error: could not retrieve property.");
}
}
private object GetSource(SerializedProperty property)
{
object target = property.serializedObject.targetObject;
string[] data = property.propertyPath.Split('.');
if (data.Length == 1)
return target;
else{
for (int i = 0; i < data.Length-1;++i){
target = target.GetType().GetField(data[i]).GetValue(target);
}
}
return target;
}
private object DrawProperty(Rect position, SerializedPropertyType propertyType, Type type, object value, GUIContent label)
{
switch (propertyType)
{
case SerializedPropertyType.Integer:
return EditorGUI.IntField(position,label,(int)value);
case SerializedPropertyType.Boolean:
return EditorGUI.Toggle(position,label,(bool)value);
case SerializedPropertyType.Float:
return EditorGUI.FloatField(position,label,(float)value);
case SerializedPropertyType.String:
return EditorGUI.TextField(position,label,(string)value);
case SerializedPropertyType.Color:
return EditorGUI.ColorField(position,label,(Color)value);
case SerializedPropertyType.ObjectReference:
return EditorGUI.ObjectField(position,label,(UnityEngine.Object)value,type,true);
case SerializedPropertyType.ExposedReference:
return EditorGUI.ObjectField(position,label,(UnityEngine.Object)value,type,true);
case SerializedPropertyType.LayerMask:
return EditorGUI.LayerField(position,label,(int)value);
case SerializedPropertyType.Enum:
return EditorGUI.EnumPopup(position,label,(Enum)value);
case SerializedPropertyType.Vector2:
return EditorGUI.Vector2Field(position,label,(Vector2)value);
case SerializedPropertyType.Vector3:
return EditorGUI.Vector3Field(position,label,(Vector3)value);
case SerializedPropertyType.Vector4:
return EditorGUI.Vector4Field(position,label,(Vector4)value);
case SerializedPropertyType.Rect:
return EditorGUI.RectField(position,label,(Rect)value);
case SerializedPropertyType.AnimationCurve:
return EditorGUI.CurveField(position,label,(AnimationCurve)value);
case SerializedPropertyType.Bounds:
return EditorGUI.BoundsField(position,label,(Bounds)value);
default:
throw new NotImplementedException("Unimplemented propertyType "+propertyType+".");
}
}
}
#endif
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9b22b3eff389444b1b4ab3bc2d6402b6
timeCreated: 1511456560
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Reflection;
namespace Obi{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class VisibleIf : MultiPropertyAttribute
{
public string MethodName { get; private set; }
public bool Negate {get; private set;}
private MethodInfo eventMethodInfo = null;
private FieldInfo fieldInfo = null;
public VisibleIf(string methodName, bool negate = false)
{
this.MethodName = methodName;
this.Negate = negate;
}
#if UNITY_EDITOR
internal override bool IsVisible(SerializedProperty property)
{
return Visibility(property) == !Negate;
}
private bool Visibility(SerializedProperty property)
{
System.Type eventOwnerType = property.serializedObject.targetObject.GetType();
string eventName = MethodName;
// Try finding a method with the name provided:
if (eventMethodInfo == null)
eventMethodInfo = eventOwnerType.GetMethod(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
// If we could not find a method with that name, look for a field:
if (eventMethodInfo == null && fieldInfo == null)
fieldInfo = eventOwnerType.GetField(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (eventMethodInfo != null)
return (bool)eventMethodInfo.Invoke(property.serializedObject.targetObject, null);
else if (fieldInfo != null)
return (bool)fieldInfo.GetValue(property.serializedObject.targetObject);
else
Debug.LogWarning(string.Format("VisibleIf: Unable to find method or field {0} in {1}", eventName, eventOwnerType));
return true;
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e52da649989a94da8b1a5d1e1fe5ff8a
timeCreated: 1511456085
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 232dbc8dfe1624cc3ac35594884d0edd
folderAsset: yes
timeCreated: 1438097540
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
using UnityEngine;
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
namespace Obi{
/**
* Implementation of asynchronous jobs that can return data, throw exceptions, and have a duration threshold
* below which they are run synchronously.
*/
public class CoroutineJob{
public class ProgressInfo{
public string userReadableInfo;
public float progress;
public ProgressInfo(string userReadableInfo,float progress){
this.userReadableInfo = userReadableInfo;
this.progress = progress;
}
}
public object Result {
get{
if(e != null){
throw e;
}
return result;
}
}
public bool IsDone{
get{
return isDone;
}
}
public bool RaisedException{
get{
return raisedException;
}
}
private object result;
private bool isDone;
private bool raisedException;
private bool stop;
private Exception e;
public int asyncThreshold = 0; //Time in milliseconds that must pass before job switches to async mode. By default, the job is asynchronous from the start.
private void Init(){
isDone = false;
raisedException = false;
stop = false;
result = null;
}
/**
* Runs the provided coroutine in a completely syncrhonous way, just like it would if it wasn't a coroutine, and
* returns a list of all coroutine results, in the order they were yielded. Will immediately rethrow any exceptions thrown by the coroutine.
*/
public static object RunSynchronously(IEnumerator coroutine){
List<object> results = new List<object>();
if (coroutine == null){
return results;
}
try{
while(coroutine.MoveNext()){
results.Add(coroutine.Current);
}
}catch(Exception e){
throw e;
}
return results;
}
public IEnumerator Start(IEnumerator coroutine){
Init();
if (coroutine == null){
isDone = true;
yield break;
}
Stopwatch sw = new Stopwatch();
sw.Start();
while(!stop){
try{
if(!coroutine.MoveNext()){
isDone = true;
sw.Stop();
yield break;
}
}
catch(Exception e){
this.e = e;
raisedException = true;
UnityEngine.Debug.LogException(e);
isDone = true;
sw.Stop();
yield break;
}
result = coroutine.Current;
//If too much time has passed sine job start, switch to async mode:
if (sw.ElapsedMilliseconds > asyncThreshold){
yield return result;
}
}
}
public void Stop(){
stop = true;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b34adb10b4f264b629f464bfc872d515
timeCreated: 1438097545
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using UnityEngine;
#if (UNITY_EDITOR)
using UnityEditor;
#endif
using System.Collections;
namespace Obi
{
public class EditorCoroutine
{
public static void ShowCoroutineProgressBar(string title, ref IEnumerator coroutine){
#if (UNITY_EDITOR)
if (coroutine != null){
CoroutineJob.ProgressInfo progressInfo;
do{
if (!coroutine.MoveNext())
progressInfo = null;
else
progressInfo = coroutine.Current as CoroutineJob.ProgressInfo;
if (progressInfo != null && EditorUtility.DisplayCancelableProgressBar(title, progressInfo.userReadableInfo, progressInfo.progress)){
progressInfo = null;
}
}while (progressInfo != null);
// once finished, clear progress bar and set coroutine to null.
coroutine = null;
// Unity bug here: https://issuetracker.unity3d.com/issues/unity-throws-nullreferenceexception-or-endlayoutgroup-errors-when-editorutility-dot-clearprogressbar-is-called
EditorUtility.ClearProgressBar();
}
#endif
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 65c4fa737e0dc4ac8acb314d96b75115
timeCreated: 1440488659
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 57e51b62adcc84dc19a301be9ebf0e51
folderAsset: yes
timeCreated: 1480348631
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
using UnityEngine;
using System;
namespace Obi
{
public class ObiAmbientForceZone : ObiExternalForce
{
public override void ApplyForcesToActor(ObiActor actor)
{
Matrix4x4 l2sTransform = actor.solver.transform.worldToLocalMatrix * transform.localToWorldMatrix;
Vector4 force = l2sTransform.MultiplyVector(Vector3.forward * (intensity + GetTurbulence(turbulence)));
if (actor.usesCustomExternalForces)
{
for (int i = 0; i < actor.activeParticleCount; ++i)
actor.solver.wind[actor.solverIndices[i]] += force;
}
else
{
for (int i = 0; i < actor.activeParticleCount; ++i)
actor.solver.externalForces[actor.solverIndices[i]] += force;
}
}
public void OnDrawGizmosSelected()
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = new Color(0,0.7f,1,1);
// arrow body:
ObiUtils.DrawArrowGizmo(0.5f + GetTurbulence(1),0.2f,0.3f,0.2f);
}
}
}

View File

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

View File

@@ -0,0 +1,50 @@
using UnityEngine;
using System;
namespace Obi
{
public abstract class ObiExternalForce : MonoBehaviour
{
public float intensity = 0;
public float turbulence = 0;
public float turbulenceFrequency = 1;
public float turbulenceSeed = 0;
public ObiSolver[] affectedSolvers;
public void OnEnable()
{
foreach (ObiSolver solver in affectedSolvers)
{
if (solver != null)
solver.OnBeginStep += Solver_OnStepBegin;
}
}
public void OnDisable()
{
foreach (ObiSolver solver in affectedSolvers)
{
if (solver != null)
solver.OnBeginStep -= Solver_OnStepBegin;
}
}
void Solver_OnStepBegin(ObiSolver solver, float stepTime)
{
foreach (ObiActor actor in solver.actors)
{
if (actor != null)
ApplyForcesToActor(actor);
}
}
protected float GetTurbulence(float turbulenceIntensity){
return Mathf.PerlinNoise(Time.fixedTime * turbulenceFrequency,turbulenceSeed) * turbulenceIntensity;
}
public abstract void ApplyForcesToActor(ObiActor actor);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4978a525b6164476d96f5d28d8b309f8
timeCreated: 1480349422
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using UnityEngine;
using System;
namespace Obi
{
public class ObiSphericalForceZone : ObiExternalForce
{
public float radius = 5;
public bool radial = true;
public override void ApplyForcesToActor(ObiActor actor)
{
float sqrRadius = radius * radius;
float finalIntensity = intensity + GetTurbulence(turbulence);
Matrix4x4 l2sTransform = actor.solver.transform.worldToLocalMatrix * transform.localToWorldMatrix;
Vector4 center = l2sTransform.MultiplyPoint3x4(Vector4.zero);
Vector4 forward = l2sTransform.MultiplyVector(Vector3.forward);
// Calculate force intensity for each actor particle:
for (int i = 0; i < actor.activeParticleCount; ++i){
Vector4 distanceVector = actor.solver.positions[actor.solverIndices[i]] - center;
float sqrMag = distanceVector.sqrMagnitude;
float falloff = Mathf.Clamp01((sqrRadius - sqrMag) / sqrRadius);
Vector4 force;
if (radial)
force = distanceVector/(Mathf.Sqrt(sqrMag) + float.Epsilon) * falloff * finalIntensity;
else
force = forward * falloff * finalIntensity;
if (actor.usesCustomExternalForces)
actor.solver.wind[actor.solverIndices[i]] += force;
else
actor.solver.externalForces[actor.solverIndices[i]] += force;
}
}
public void OnDrawGizmosSelected()
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = new Color(0,0.7f,1,1);
Gizmos.DrawWireSphere(Vector3.zero,radius);
float turb = GetTurbulence(1);
if (!radial){
ObiUtils.DrawArrowGizmo(radius + turb,radius*0.2f,radius*0.3f,radius*0.2f);
}else{
Gizmos.DrawLine(new Vector3(0,0,-radius*0.5f)*turb,new Vector3(0,0,radius*0.5f)*turb);
Gizmos.DrawLine(new Vector3(0,-radius*0.5f,0)*turb,new Vector3(0,radius*0.5f,0)*turb);
Gizmos.DrawLine(new Vector3(-radius*0.5f,0,0)*turb,new Vector3(radius*0.5f,0,0)*turb);
}
}
}
}

View File

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

View File

@@ -0,0 +1,154 @@
using UnityEngine;
using UnityEngine.Events;
using System;
using System.Collections.Generic;
namespace Obi
{
[RequireComponent(typeof(ObiSolver))]
public class ObiContactEventDispatcher : MonoBehaviour
{
private ObiSolver solver;
private Oni.Contact[] prevData;
private int prevCount;
private ContactComparer comparer;
private class ContactComparer : IComparer<Oni.Contact>
{
ObiSolver solver;
public ContactComparer(ObiSolver solver)
{
this.solver = solver;
}
public int Compare(Oni.Contact x, Oni.Contact y)
{
return CompareByRef(ref x, ref y, solver);
}
}
private static int CompareByRef(ref Oni.Contact a, ref Oni.Contact b, ObiSolver solver)
{
if (a.bodyB == b.bodyB)
{
int hashA = solver.particleToActor[a.bodyA].actor.GetInstanceID();
int hashB = solver.particleToActor[b.bodyA].actor.GetInstanceID();
return hashA.CompareTo(hashB);
}
return a.bodyB.CompareTo(b.bodyB);
}
[System.Serializable]
public class ContactCallback : UnityEvent<ObiSolver, Oni.Contact> { }
public float distanceThreshold = 0.01f;
public ContactCallback onContactEnter = new ContactCallback();
public ContactCallback onContactStay = new ContactCallback();
public ContactCallback onContactExit = new ContactCallback();
void Awake()
{
solver = GetComponent<ObiSolver>();
comparer = new ContactComparer(solver);
prevData = new Oni.Contact[0];
}
void OnEnable()
{
solver.OnCollision += Solver_OnCollision;
}
void OnDisable()
{
solver.OnCollision -= Solver_OnCollision;
}
private int FilterOutDistantContacts(Oni.Contact[] data, int count)
{
int filteredCount = count;
// simply iterate trough all contacts,
// moving the ones above the threshold to the end of the array:
for (int i = count - 1; i >= 0; --i)
if (data[i].distance > distanceThreshold)
ObiUtils.Swap(ref data[i], ref data[--filteredCount]);
return filteredCount;
}
private int RemoveDuplicates(Oni.Contact[] data, int count)
{
if (count == 0)
return 0;
// assuming the array is sorted, iterate trough the array
// replacing duplicates by the first contact that's different:
int i = 0, r = 0;
while (++i != count)
if (CompareByRef(ref data[i], ref data[r], solver) != 0 && ++r != i)
data[r] = data[i];
return ++r;
}
private void InvokeCallbacks(Oni.Contact[] data, int count)
{
int a = 0, b = 0;
int lengthA = count, lengthB = prevCount;
// while we haven't reached the end of either array:
while (a < lengthA && b < lengthB)
{
// compare both contacts:
int compare = CompareByRef(ref data[a], ref prevData[b], solver);
// call the appropiate event depending on the comparison result:
if (compare < 0)
onContactEnter.Invoke(solver, data[a++]);
else if (compare > 0)
onContactExit.Invoke(solver, prevData[b++]);
else
{
onContactStay.Invoke(solver, data[a++]); b++;
}
}
// finish iterating trough both lists:
while (a < lengthA)
onContactEnter.Invoke(solver, data[a++]);
while (b < lengthB)
onContactExit.Invoke(solver, prevData[b++]);
}
void Solver_OnCollision(object sender, ObiSolver.ObiCollisionEventArgs args)
{
// here we access the internal backing array (Data) directly,
// instead of using the accessor property. This slightly improves performance.
// note: the backing array length is the lists' capacity, so we
// need to use args.contacts.Count to get the actual number of contacts.
// skip all contacts above the distance threshold by moving them to the end of the array:
int filteredCount = FilterOutDistantContacts(args.contacts.Data, args.contacts.Count);
// sort the remaining contacts by collider, then by actor:
Array.Sort(args.contacts.Data, 0, filteredCount, comparer);
// remove duplicates:
filteredCount = RemoveDuplicates(args.contacts.Data, filteredCount);
// zip trough the current and previous contact lists once, invoking events when appropiate.
InvokeCallbacks(args.contacts.Data, filteredCount);
// store current contact list/count for next frame.
// could get better performance by double buffering instead of copying:
if (filteredCount > prevData.Length)
Array.Resize(ref prevData, filteredCount);
Array.Copy(args.contacts.Data, prevData, filteredCount);
prevCount = filteredCount;
}
}
}

View File

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

View File

@@ -0,0 +1,178 @@
using UnityEngine;
using System.Collections.Generic;
using Obi;
/**
* Sample component that makes a collider "grab" any particle it touches (regardless of which Actor it belongs to).
*/
[RequireComponent(typeof(ObiCollider))]
public class ObiContactGrabber : MonoBehaviour
{
public ObiSolver[] solvers = { };
public bool grabbed
{
get { return grabbedActors.Count > 0; }
}
/**
* Helper class that stores the index of a particle in the solver, its position in the grabber's local space, and its inverse mass previous to being grabbed.
* This makes it easy to tell if a particle has been grabbed, update its position while grabbing, and restore its mass after being released.
*/
private class GrabbedParticle : IEqualityComparer<GrabbedParticle>
{
public int index;
public float invMass;
public Vector3 localPosition;
public ObiSolver solver;
public GrabbedParticle(ObiSolver solver, int index, float invMass)
{
this.solver = solver;
this.index = index;
this.invMass = invMass;
}
public bool Equals(GrabbedParticle x, GrabbedParticle y)
{
return x.index == y.index;
}
public int GetHashCode(GrabbedParticle obj)
{
return index;
}
}
private Dictionary<ObiSolver, ObiSolver.ObiCollisionEventArgs> collisionEvents = new Dictionary<ObiSolver, ObiSolver.ObiCollisionEventArgs>(); /**< store the current collision event*/
private ObiCollider localCollider; /**< the collider on this gameObject.*/
private HashSet<GrabbedParticle> grabbedParticles = new HashSet<GrabbedParticle>(); /**< set to store all currently grabbed particles.*/
private HashSet<ObiActor> grabbedActors = new HashSet<ObiActor>(); /**< set of softbodies grabbed during this step.*/
private void Awake()
{
localCollider = GetComponent<ObiCollider>();
}
private void OnEnable()
{
if (solvers != null)
foreach (ObiSolver solver in solvers)
solver.OnCollision += Solver_OnCollision;
}
private void OnDisable()
{
if (solvers != null)
foreach (ObiSolver solver in solvers)
solver.OnCollision -= Solver_OnCollision;
}
private void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
{
collisionEvents[(ObiSolver)sender] = e;
}
private void UpdateParticleProperties()
{
// Update rest shape matching of all grabbed softbodies:
foreach (ObiActor actor in grabbedActors)
{
actor.UpdateParticleProperties();
}
}
/**
* Creates and stores a GrabbedParticle from the particle at the given index.
* Returns true if we sucessfully grabbed a particle, false if the particle was already grabbed.
*/
private bool GrabParticle(ObiSolver solver, int index)
{
GrabbedParticle p = new GrabbedParticle(solver, index, solver.invMasses[index]);
// in case this particle has not been grabbed yet:
if (!grabbedParticles.Contains(p))
{
Matrix4x4 solver2Grabber = transform.worldToLocalMatrix * solver.transform.localToWorldMatrix;
// record the particle's position relative to the grabber, and store it.
p.localPosition = solver2Grabber.MultiplyPoint3x4(solver.positions[index]);
grabbedParticles.Add(p);
// Set inv mass and velocity to zero:
solver.invMasses[index] = 0;
solver.velocities[index] = Vector4.zero;
return true;
}
return false;
}
/**
* Grabs all particles currently touching the grabber.
*/
public void Grab()
{
Release();
var world = ObiColliderWorld.GetInstance();
if (solvers != null && collisionEvents != null)
{
foreach (ObiSolver solver in solvers)
{
ObiSolver.ObiCollisionEventArgs collisionEvent;
if (collisionEvents.TryGetValue(solver, out collisionEvent))
{
foreach (Oni.Contact contact in collisionEvent.contacts)
{
// this one is an actual collision:
if (contact.distance < 0.01f)
{
var contactCollider = world.colliderHandles[contact.bodyB].owner;
int particleIndex = solver.simplices[contact.bodyA];
// if the current contact references our collider, proceed to grab the particle.
if (contactCollider == localCollider)
{
// try to grab the particle, if not already grabbed.
if (GrabParticle(solver, particleIndex))
grabbedActors.Add(solver.particleToActor[particleIndex].actor);
}
}
}
}
}
}
UpdateParticleProperties();
}
/**
* Releases all currently grabbed particles. This boils down to simply resetting their invMass.
*/
public void Release()
{
// Restore the inverse mass of all grabbed particles, so dynamics affect them.
foreach (GrabbedParticle p in grabbedParticles)
p.solver.invMasses[p.index] = p.invMass;
UpdateParticleProperties();
grabbedActors.Clear();
grabbedParticles.Clear();
}
/**
* Updates the position of the grabbed particles.
*/
private void FixedUpdate()
{
foreach (GrabbedParticle p in grabbedParticles)
{
Matrix4x4 grabber2Solver = p.solver.transform.worldToLocalMatrix * transform.localToWorldMatrix;
p.solver.positions[p.index] = grabber2Solver.MultiplyPoint3x4(p.localPosition);
}
}
}

View File

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

View File

@@ -0,0 +1,508 @@
using System;
using UnityEngine;
namespace Obi
{
[AddComponentMenu("Physics/Obi/Obi Particle Attachment", 820)]
[RequireComponent(typeof(ObiActor))]
[ExecuteInEditMode]
public class ObiParticleAttachment : MonoBehaviour
{
public enum AttachmentType
{
Static,
Dynamic
}
[SerializeField] [HideInInspector] private ObiActor m_Actor;
[SerializeField] [HideInInspector] private Transform m_Target;
[SerializeField] [HideInInspector] private ObiParticleGroup m_ParticleGroup;
[SerializeField] [HideInInspector] private AttachmentType m_AttachmentType = AttachmentType.Static;
[SerializeField] [HideInInspector] private bool m_ConstrainOrientation = false;
[SerializeField] [HideInInspector] private float m_Compliance = 0;
[SerializeField] [HideInInspector] [Delayed] private float m_BreakThreshold = float.PositiveInfinity;
// private variables are serialized during script reloading, to keep their value. Must mark them explicitly as non-serialized.
[NonSerialized] private ObiPinConstraintsBatch pinBatch;
[NonSerialized] private ObiColliderBase attachedCollider;
[NonSerialized] private int attachedColliderHandleIndex;
[NonSerialized] private int[] m_SolverIndices;
[NonSerialized] private Vector3[] m_PositionOffsets = null;
[NonSerialized] private Quaternion[] m_OrientationOffsets = null;
/// <summary>
/// The actor this attachment is added to.
/// </summary>
public ObiActor actor
{
get { return m_Actor; }
}
/// <summary>
/// The target transform that the <see cref="particleGroup"/> should be attached to.
/// </summary>
public Transform target
{
get { return m_Target; }
set
{
if (value != m_Target)
{
m_Target = value;
Bind();
}
}
}
/// <summary>
/// The particle group that should be attached to the <see cref="target"/>.
/// </summary>
public ObiParticleGroup particleGroup
{
get
{
return m_ParticleGroup;
}
set
{
if (value != m_ParticleGroup)
{
m_ParticleGroup = value;
Bind();
}
}
}
/// <summary>
/// Whether this attachment is currently bound or not.
/// </summary>
public bool isBound
{
get { return m_Target != null && m_SolverIndices != null && m_PositionOffsets != null; }
}
/// <summary>
/// Type of attachment, can be either static or dynamic.
/// </summary>
public AttachmentType attachmentType
{
get { return m_AttachmentType; }
set
{
if (value != m_AttachmentType)
{
DisableAttachment(m_AttachmentType);
m_AttachmentType = value;
EnableAttachment(m_AttachmentType);
}
}
}
/// <summary>
/// Should this attachment constraint particle orientations too?
/// </summary>
public bool constrainOrientation
{
get { return m_ConstrainOrientation; }
set
{
if (value != m_ConstrainOrientation)
{
DisableAttachment(m_AttachmentType);
m_ConstrainOrientation = value;
EnableAttachment(m_AttachmentType);
}
}
}
/// <summary>
/// Constraint compliance, in case this attachment is dynamic.
/// </summary>
/// High compliance values will increase the attachment's elasticity.
public float compliance
{
get { return m_Compliance; }
set
{
if (!Mathf.Approximately(value, m_Compliance))
{
m_Compliance = value;
if (m_AttachmentType == AttachmentType.Dynamic && pinBatch != null)
{
for (int i = 0; i < m_SolverIndices.Length; ++i)
pinBatch.stiffnesses[i * 2] = m_Compliance;
}
}
}
}
/// <summary>
/// Force thershold above which the attachment should break.
/// </summary>
/// Only affects dynamic attachments, as static attachments do not work with forces.
public float breakThreshold
{
get { return m_BreakThreshold; }
set
{
if (!Mathf.Approximately(value, m_BreakThreshold))
{
m_BreakThreshold = value;
if (m_AttachmentType == AttachmentType.Dynamic && pinBatch != null)
{
for (int i = 0; i < m_SolverIndices.Length; ++i)
pinBatch.breakThresholds[i] = m_BreakThreshold;
}
}
}
}
private void OnEnable()
{
m_Actor = GetComponent<ObiActor>();
m_Actor.OnBlueprintLoaded += Actor_OnBlueprintLoaded;
m_Actor.OnPrepareStep += Actor_OnPrepareStep;
m_Actor.OnEndStep += Actor_OnEndStep;
if (m_Actor.solver != null)
Actor_OnBlueprintLoaded(m_Actor, m_Actor.sourceBlueprint);
EnableAttachment(m_AttachmentType);
}
private void OnDisable()
{
DisableAttachment(m_AttachmentType);
m_Actor.OnBlueprintLoaded -= Actor_OnBlueprintLoaded;
m_Actor.OnPrepareStep -= Actor_OnPrepareStep;
m_Actor.OnEndStep -= Actor_OnEndStep;
}
private void OnValidate()
{
m_Actor = GetComponent<ObiActor>();
// do not re-bind: simply disable and re-enable the attachment.
DisableAttachment(AttachmentType.Static);
DisableAttachment(AttachmentType.Dynamic);
EnableAttachment(m_AttachmentType);
}
void Actor_OnBlueprintLoaded(ObiActor act, ObiActorBlueprint blueprint)
{
Bind();
}
void Actor_OnPrepareStep(ObiActor act, float stepTime)
{
// Attachments must be updated at the start of the step, before performing any simulation.
UpdateAttachment();
}
private void Actor_OnEndStep(ObiActor act, float stepTime)
{
// dynamic attachments must be tested at the end of the step, once constraint forces have been calculated.
// if there's any broken constraint, flag pin constraints as dirty for remerging at the start of the next step.
BreakDynamicAttachment(stepTime);
}
private void Bind()
{
// Disable attachment.
DisableAttachment(m_AttachmentType);
if (m_Target != null && m_ParticleGroup != null && m_Actor.isLoaded)
{
Matrix4x4 bindMatrix = m_Target.worldToLocalMatrix * m_Actor.solver.transform.localToWorldMatrix;
m_SolverIndices = new int[m_ParticleGroup.Count];
m_PositionOffsets = new Vector3[m_ParticleGroup.Count];
m_OrientationOffsets = new Quaternion[m_ParticleGroup.Count];
for (int i = 0; i < m_ParticleGroup.Count; ++i)
{
int particleIndex = m_ParticleGroup.particleIndices[i];
if (particleIndex >= 0 && particleIndex < m_Actor.solverIndices.Length)
{
m_SolverIndices[i] = m_Actor.solverIndices[particleIndex];
m_PositionOffsets[i] = bindMatrix.MultiplyPoint3x4(m_Actor.solver.positions[m_SolverIndices[i]]);
}
else
{
Debug.LogError("The particle group \'" + m_ParticleGroup.name + "\' references a particle that does not exist in the actor \'" + m_Actor.name + "\'.");
m_SolverIndices = null;
m_PositionOffsets = null;
m_OrientationOffsets = null;
return;
}
}
if (m_Actor.usesOrientedParticles)
{
Quaternion bindOrientation = bindMatrix.rotation;
for (int i = 0; i < m_ParticleGroup.Count; ++i)
{
int particleIndex = m_ParticleGroup.particleIndices[i];
if (particleIndex >= 0 && particleIndex < m_Actor.solverIndices.Length)
m_OrientationOffsets[i] = bindOrientation * m_Actor.solver.orientations[m_SolverIndices[i]];
}
}
}
else
{
m_PositionOffsets = null;
m_OrientationOffsets = null;
}
EnableAttachment(m_AttachmentType);
}
private void EnableAttachment(AttachmentType type)
{
if (enabled && m_Actor.isLoaded && isBound)
{
var solver = m_Actor.solver;
switch (type)
{
case AttachmentType.Dynamic:
var pins = m_Actor.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiPinConstraintsData;
attachedCollider = m_Target.GetComponent<ObiColliderBase>();
if (pins != null && attachedCollider != null && pinBatch == null)
{
// create a new data batch with all our pin constraints:
pinBatch = new ObiPinConstraintsBatch(pins);
for (int i = 0; i < m_SolverIndices.Length; ++i)
{
pinBatch.AddConstraint(m_SolverIndices[i],
attachedCollider,
m_PositionOffsets[i],
m_OrientationOffsets[i],
m_Compliance,
constrainOrientation ? 0 : 10000,
m_BreakThreshold);
pinBatch.activeConstraintCount++;
}
// add the batch to the actor:
pins.AddBatch(pinBatch);
// store the attached collider's handle:
attachedColliderHandleIndex = -1;
if (attachedCollider.Handle != null)
attachedColliderHandleIndex = attachedCollider.Handle.index;
m_Actor.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
break;
case AttachmentType.Static:
for (int i = 0; i < m_SolverIndices.Length; ++i)
if (m_SolverIndices[i] >= 0 && m_SolverIndices[i] < solver.invMasses.count)
solver.invMasses[m_SolverIndices[i]] = 0;
if (m_Actor.usesOrientedParticles && m_ConstrainOrientation)
{
for (int i = 0; i < m_SolverIndices.Length; ++i)
if (m_SolverIndices[i] >= 0 && m_SolverIndices[i] < solver.invRotationalMasses.count)
solver.invRotationalMasses[m_SolverIndices[i]] = 0;
}
m_Actor.UpdateParticleProperties();
break;
}
}
}
private void DisableAttachment(AttachmentType type)
{
if (isBound)
{
switch (type)
{
case AttachmentType.Dynamic:
if (pinBatch != null)
{
var pins = m_Actor.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
if (pins != null)
{
pins.RemoveBatch(pinBatch);
if (actor.isLoaded)
m_Actor.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
attachedCollider = null;
pinBatch = null;
attachedColliderHandleIndex = -1;
}
break;
case AttachmentType.Static:
var solver = m_Actor.solver;
var blueprint = m_Actor.sourceBlueprint;
for (int i = 0; i < m_SolverIndices.Length; ++i)
{
int solverIndex = m_SolverIndices[i];
if (solverIndex >= 0 && solverIndex < solver.invMasses.count)
solver.invMasses[solverIndex] = blueprint.invMasses[i];
}
if (m_Actor.usesOrientedParticles)
{
for (int i = 0; i < m_SolverIndices.Length; ++i)
{
int solverIndex = m_SolverIndices[i];
if (solverIndex >= 0 && solverIndex < solver.invRotationalMasses.count)
solver.invRotationalMasses[solverIndex] = blueprint.invRotationalMasses[i];
}
}
m_Actor.UpdateParticleProperties();
break;
}
}
}
private void UpdateAttachment()
{
if (enabled && m_Actor.isLoaded && isBound)
{
var solver = m_Actor.solver;
switch (m_AttachmentType)
{
case AttachmentType.Dynamic:
// in case the handle has been updated/invalidated (for instance, when disabling the target) rebuild constraints:
if (attachedCollider != null &&
attachedCollider.Handle != null &&
attachedCollider.Handle.index != attachedColliderHandleIndex)
{
attachedColliderHandleIndex = attachedCollider.Handle.index;
m_Actor.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
break;
case AttachmentType.Static:
var blueprint = m_Actor.sourceBlueprint;
bool targetActive = m_Target.gameObject.activeInHierarchy;
// Build the attachment matrix:
Matrix4x4 attachmentMatrix = solver.transform.worldToLocalMatrix * m_Target.localToWorldMatrix;
// Fix all particles in the group and update their position
// Note: skip assignment to startPositions if you want attached particles to be interpolated too.
for (int i = 0; i < m_SolverIndices.Length; ++i)
{
int solverIndex = m_SolverIndices[i];
if (solverIndex >= 0 && solverIndex < solver.invMasses.count)
{
if (targetActive)
{
solver.invMasses[solverIndex] = 0;
solver.velocities[solverIndex] = Vector3.zero;
solver.startPositions[solverIndex] = solver.positions[solverIndex] = attachmentMatrix.MultiplyPoint3x4(m_PositionOffsets[i]);
}else
solver.invMasses[solverIndex] = blueprint.invMasses[i];
}
}
if (m_Actor.usesOrientedParticles && m_ConstrainOrientation)
{
Quaternion attachmentRotation = attachmentMatrix.rotation;
for (int i = 0; i < m_SolverIndices.Length; ++i)
{
int solverIndex = m_SolverIndices[i];
if (solverIndex >= 0 && solverIndex < solver.invRotationalMasses.count)
{
if (targetActive)
{
solver.invRotationalMasses[solverIndex] = 0;
solver.angularVelocities[solverIndex] = Vector3.zero;
solver.startOrientations[solverIndex] = solver.orientations[solverIndex] = attachmentRotation * m_OrientationOffsets[i];
}
else
solver.invRotationalMasses[solverIndex] = blueprint.invRotationalMasses[i];
}
}
}
break;
}
}
}
private void BreakDynamicAttachment(float stepTime)
{
if (enabled && m_AttachmentType == AttachmentType.Dynamic && m_Actor.isLoaded && isBound)
{
var solver = m_Actor.solver;
var actorConstraints = m_Actor.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
var solverConstraints = solver.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
bool dirty = false;
if (actorConstraints != null && pinBatch != null)
{
int pinBatchIndex = actorConstraints.batches.IndexOf(pinBatch);
if (pinBatchIndex >= 0 && pinBatchIndex < actor.solverBatchOffsets[(int)Oni.ConstraintType.Pin].Count)
{
int offset = actor.solverBatchOffsets[(int)Oni.ConstraintType.Pin][pinBatchIndex];
var solverBatch = solverConstraints.batches[pinBatchIndex];
float sqrTime = stepTime * stepTime;
for (int i = 0; i < pinBatch.activeConstraintCount; i++)
{
// In case the handle has been created/destroyed.
if (pinBatch.pinBodies[i] != attachedCollider.Handle)
{
pinBatch.pinBodies[i] = attachedCollider.Handle;
dirty = true;
}
// in case the constraint has been broken:
if (-solverBatch.lambdas[(offset + i) * 4 + 3] / sqrTime > pinBatch.breakThresholds[i])
{
pinBatch.DeactivateConstraint(i);
dirty = true;
}
}
}
}
// constraints are recreated at the start of a step.
if (dirty)
m_Actor.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
}
}
}

View File

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

View File

@@ -0,0 +1,84 @@
using System;
using UnityEngine;
namespace Obi
{
[RequireComponent(typeof(LineRenderer))]
[RequireComponent(typeof(ObiParticlePicker))]
public class ObiParticleDragger : MonoBehaviour
{
public float springStiffness = 500;
public float springDamping = 50;
public bool drawSpring = true;
private LineRenderer lineRenderer;
private ObiParticlePicker picker;
private ObiParticlePicker.ParticlePickEventArgs pickArgs;
void OnEnable()
{
lineRenderer = GetComponent<LineRenderer>();
picker = GetComponent<ObiParticlePicker>();
picker.OnParticlePicked.AddListener(Picker_OnParticleDragged);
picker.OnParticleDragged.AddListener(Picker_OnParticleDragged);
picker.OnParticleReleased.AddListener(Picker_OnParticleReleased);
}
void OnDisable()
{
picker.OnParticlePicked.RemoveListener(Picker_OnParticleDragged);
picker.OnParticleDragged.RemoveListener(Picker_OnParticleDragged);
picker.OnParticleReleased.RemoveListener(Picker_OnParticleReleased);
lineRenderer.positionCount = 0;
}
void FixedUpdate()
{
ObiSolver solver = picker.solver;
if (solver != null && pickArgs != null)
{
// Calculate picking position in solver space:
Vector4 targetPosition = solver.transform.InverseTransformPoint(pickArgs.worldPosition);
// Calculate effective inverse mass:
float invMass = solver.invMasses[pickArgs.particleIndex];
if (invMass > 0)
{
// Calculate and apply spring force:
Vector4 position = solver.positions[pickArgs.particleIndex];
Vector4 velocity = solver.velocities[pickArgs.particleIndex];
solver.externalForces[pickArgs.particleIndex] = ((targetPosition - position) * springStiffness - velocity * springDamping) / invMass;
if (drawSpring)
{
lineRenderer.positionCount = 2;
lineRenderer.SetPosition(0, pickArgs.worldPosition);
lineRenderer.SetPosition(1, solver.transform.TransformPoint(position));
}
else
{
lineRenderer.positionCount = 0;
}
}
}
}
void Picker_OnParticleDragged(ObiParticlePicker.ParticlePickEventArgs e)
{
pickArgs = e;
}
void Picker_OnParticleReleased(ObiParticlePicker.ParticlePickEventArgs e)
{
pickArgs = null;
lineRenderer.positionCount = 0;
}
}
}

View File

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

View File

@@ -0,0 +1,45 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Obi
{
[RequireComponent(typeof(ObiSolver))]
public class ObiParticleGridDebugger : MonoBehaviour
{
ObiSolver solver;
ObiNativeAabbList cells;
void OnEnable()
{
solver = GetComponent<ObiSolver>();
cells = new ObiNativeAabbList();
}
private void OnDisable()
{
cells.Dispose();
}
void LateUpdate ()
{
cells.count = solver.implementation.GetParticleGridSize();
solver.implementation.GetParticleGrid(cells);
}
void OnDrawGizmos()
{
if (cells != null)
{
Gizmos.color = Color.yellow;
for(int i = 0; i < cells.count; ++i)
Gizmos.DrawWireCube(cells[i].center, cells[i].size);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 12c0ac28542b14de6a887a6a9067d86d
timeCreated: 1487351379
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Obi
{
public class ObiParticlePicker : MonoBehaviour
{
public class ParticlePickEventArgs : EventArgs
{
public int particleIndex;
public Vector3 worldPosition;
public ParticlePickEventArgs(int particleIndex, Vector3 worldPosition)
{
this.particleIndex = particleIndex;
this.worldPosition = worldPosition;
}
}
[Serializable]
public class ParticlePickUnityEvent : UnityEvent<ParticlePickEventArgs> { }
public ObiSolver solver;
public float radiusScale = 1;
public ParticlePickUnityEvent OnParticlePicked;
public ParticlePickUnityEvent OnParticleHeld;
public ParticlePickUnityEvent OnParticleDragged;
public ParticlePickUnityEvent OnParticleReleased;
private Vector3 lastMousePos = Vector3.zero;
private int pickedParticleIndex = -1;
private float pickedParticleDepth = 0;
void Awake()
{
lastMousePos = Input.mousePosition;
}
void LateUpdate()
{
if (solver != null)
{
// Click:
if (Input.GetMouseButtonDown(0))
{
pickedParticleIndex = -1;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float closestMu = float.MaxValue;
float closestDistance = float.MaxValue;
Matrix4x4 solver2World = solver.transform.localToWorldMatrix;
// Find the closest particle hit by the ray:
for (int i = 0; i < solver.renderablePositions.count; ++i)
{
Vector3 worldPos = solver2World.MultiplyPoint3x4(solver.renderablePositions[i]);
float mu;
Vector3 projected = ObiUtils.ProjectPointLine(worldPos, ray.origin, ray.origin + ray.direction, out mu, false);
float distanceToRay = Vector3.SqrMagnitude(worldPos - projected);
// Disregard particles behind the camera:
mu = Mathf.Max(0, mu);
float radius = solver.principalRadii[i][0] * radiusScale;
if (distanceToRay <= radius * radius && distanceToRay < closestDistance && mu < closestMu)
{
closestMu = mu;
closestDistance = distanceToRay;
pickedParticleIndex = i;
}
}
if (pickedParticleIndex >= 0)
{
pickedParticleDepth = Camera.main.transform.InverseTransformVector(solver2World.MultiplyPoint3x4(solver.renderablePositions[pickedParticleIndex]) - Camera.main.transform.position).z;
if (OnParticlePicked != null)
{
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pickedParticleDepth));
OnParticlePicked.Invoke(new ParticlePickEventArgs(pickedParticleIndex, worldPosition));
}
}
}
else if (pickedParticleIndex >= 0)
{
// Drag:
Vector3 mouseDelta = Input.mousePosition - lastMousePos;
if (mouseDelta.magnitude > 0.01f && OnParticleDragged != null)
{
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pickedParticleDepth));
OnParticleDragged.Invoke(new ParticlePickEventArgs(pickedParticleIndex, worldPosition));
}
else if (OnParticleHeld != null)
{
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pickedParticleDepth));
OnParticleHeld.Invoke(new ParticlePickEventArgs(pickedParticleIndex, worldPosition));
}
// Release:
if (Input.GetMouseButtonUp(0))
{
if (OnParticleReleased != null)
{
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pickedParticleDepth));
OnParticleReleased.Invoke(new ParticlePickEventArgs(pickedParticleIndex, worldPosition));
}
pickedParticleIndex = -1;
}
}
}
lastMousePos = Input.mousePosition;
}
}
}

View File

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

View File

@@ -0,0 +1,215 @@
using System;
using UnityEngine;
namespace Obi
{
[DisallowMultipleComponent]
public class ObiProfiler : MonoBehaviour
{
[Header("Appearance")]
public GUISkin skin;
public Color threadColor = Color.white;
public Color taskColor = new Color(0.1f, 1, 0.2f);
public Color parallelTaskColor = new Color(1,0.8f,0.2f);
//public Color idleColor = new Color(0.7f,0.7f,0.7f);
public Color renderTaskColor = new Color(0.2f, 0.7f, 1.0f);
public Color defaultTaskColor = new Color(1, 0.5f, 0.2f);
[Header("Visualization")]
public bool showPercentages = false;
public int profileThrottle = 30;
private Oni.ProfileInfo[] info;
private double frameStart;
private double frameEnd;
private int frameCounter = 0;
private int yPos = 25;
private bool profiling = false;
private float zoom = 1;
private Vector2 scrollPosition = Vector2.zero;
private static ObiProfiler _instance;
private void Awake()
{
if (_instance != null && _instance != this)
DestroyImmediate(this);
else{
_instance = this;
}
}
public void OnDestroy(){
#if (OBI_ONI_SUPPORTED)
_instance = null;
Oni.EnableProfiler(false);
#endif
}
private void OnEnable()
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null && _instance.profiling)
Oni.EnableProfiler(true);
#endif
}
private void OnDisable()
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null)
Oni.EnableProfiler(false);
#endif
}
public static void EnableProfiler()
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null)
{
_instance.profiling = true;
if (_instance.isActiveAndEnabled)
Oni.EnableProfiler(true);
}
#endif
}
public static void DisableProfiler()
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null)
{
_instance.profiling = false;
Oni.EnableProfiler(false);
}
#endif
}
public static void BeginSample(string name, byte type)
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null)
Oni.BeginSample(name, type);
#endif
}
public static void EndSample()
{
#if (OBI_ONI_SUPPORTED)
if (_instance != null)
Oni.EndSample();
#endif
}
private void UpdateProfilerInfo(){
#if (OBI_ONI_SUPPORTED)
frameCounter--;
if (frameCounter <= 0)
{
int count = Oni.GetProfilingInfoCount();
info = new Oni.ProfileInfo[count];
Oni.GetProfilingInfo(info,count);
frameCounter = profileThrottle;
// Calculate frame duration:
frameStart = double.MaxValue;
frameEnd = double.MinValue;
foreach (Oni.ProfileInfo i in info){
frameStart = Math.Min(frameStart,i.start);
frameEnd = Math.Max(frameEnd,i.end);
}
}
Oni.ClearProfiler();
#endif
}
public void OnGUI()
{
#if (OBI_ONI_SUPPORTED)
if (Event.current.type == EventType.Layout)
UpdateProfilerInfo();
if (info == null)
return;
GUI.skin = skin;
int toolbarHeight = 20;
int threadHeight = 20;
int scrollViewWidth = (int)(Screen.width / zoom);
double frameDuration = frameEnd - frameStart;
// Toolbar:
GUI.BeginGroup(new Rect(0,0,Screen.width,toolbarHeight),"","Box");
GUI.Label(new Rect(5,0,50,toolbarHeight),"Zoom:");
zoom = GUI.HorizontalSlider(new Rect(50,5,100,toolbarHeight),zoom,0.005f,1);
GUI.Label(new Rect(Screen.width - 100,0,100,toolbarHeight),(frameDuration/1000.0f).ToString("0.###") + " ms/frame");
GUI.EndGroup();
// Timeline view:
scrollPosition = GUI.BeginScrollView(new Rect(0, toolbarHeight, Screen.width, Screen.height-20), scrollPosition,
new Rect(0, 0, scrollViewWidth, yPos+30)); // height depends on amount of threads.
GUI.color = threadColor;
GUI.Label(new Rect(5,0,200,20),"Thread 1");
GUI.Box(new Rect(0, 0, scrollViewWidth, 40),"","Thread");
yPos = 25;
uint currentThreadId = 0;
uint currentLevel = 0;
foreach (Oni.ProfileInfo i in info)
{
uint threadId = (i.info & (uint)Oni.ProfileMask.ThreadIdMask) >> 16;
uint level = (i.info & (uint)Oni.ProfileMask.StackLevelMask) >> 8;
uint type = i.info & (uint)Oni.ProfileMask.TypeMask;
if (currentThreadId != threadId){
yPos += threadHeight+1;
GUI.color = threadColor;
GUI.Label(new Rect(5,yPos+5,200,20),"Thread "+(threadId+1));
GUI.Box(new Rect(0, yPos+5, scrollViewWidth, 40),"","Thread");
yPos += 30;
}else if (currentLevel != level){
yPos += threadHeight+1;
}
currentLevel = level;
currentThreadId = threadId;
switch(type){
case 0: GUI.color = taskColor; break;
//case 1: GUI.color = idleColor; break;
case 2: GUI.color = parallelTaskColor; break;
case 3: GUI.color = renderTaskColor; break;
default: GUI.color = defaultTaskColor; break;
}
// task duration:
int taskStart = (int) ((i.start - frameStart) / frameDuration * (Screen.width-10) / zoom);
int taskEnd = (int) ((i.end - frameStart) / frameDuration * (Screen.width-10) / zoom);
int taskDuration = taskEnd-taskStart;
string name;
if (showPercentages)
{
double pctg = (i.end-i.start)/frameDuration*100;
name = i.name + " ("+pctg.ToString("0.#")+"%)";
}
else{
double ms = (i.end-i.start)/1000.0f;
name = i.name + " ("+ms.ToString("0.###")+"ms)";
}
GUI.Box(new Rect(taskStart, yPos, taskDuration-1, threadHeight),name,"Task");
}
GUI.EndScrollView();
#endif
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 62e83a599724f45a4aa337d74764e572
timeCreated: 1482252100
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences:
- skin: {fileID: 11400000, guid: b90d3c214c99743b0865fc0e0d1f1a15, type: 2}
executionOrder: 0
icon: {fileID: 2800000, guid: 1c322d61eeb3640408ee1119729d5c21, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,233 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Obi
{
/**
* ObiStitcher will create stitch constraints between 2 actors. All actors must be assigned to the same solver.
* - Add the constraint batch to the solver once all actors have been added.
* - If any of the actors is removed from the solver, remove the stitcher too.
* - Stitch constraints can keep n particles together, respecting their masses.
* - In edit mode, select the actors to be stitched and then select groups of particles and click "stitch". Or, create stitches by closeness.
*/
[ExecuteInEditMode]
public class ObiStitcher : MonoBehaviour
{
[Serializable]
public class Stitch
{
public int particleIndex1;
public int particleIndex2;
public Stitch(int particleIndex1, int particleIndex2)
{
this.particleIndex1 = particleIndex1;
this.particleIndex2 = particleIndex2;
}
}
[SerializeField] [HideInInspector] private List<Stitch> stitches = new List<Stitch>();
[SerializeField] [HideInInspector] private ObiActor actor1 = null; /**< one of the actors used by the stitcher.*/
[SerializeField] [HideInInspector] private ObiActor actor2 = null; /**< the second actor used by the stitcher.*/
[HideInInspector] public ObiNativeIntList particleIndices = new ObiNativeIntList();
[HideInInspector] public ObiNativeFloatList stiffnesses = new ObiNativeFloatList();
[HideInInspector] public ObiNativeFloatList lambdas = new ObiNativeFloatList();
//private IntPtr batch;
private IStitchConstraintsBatchImpl m_BatchImpl;
private bool inSolver = false;
public ObiActor Actor1
{
set
{
if (actor1 != value)
{
UnregisterActor(actor1);
actor1 = value;
RegisterActor(actor1);
}
}
get { return actor1; }
}
public ObiActor Actor2
{
set
{
if (actor2 != value)
{
UnregisterActor(actor2);
actor2 = value;
RegisterActor(actor2);
}
}
get { return actor2; }
}
public int StitchCount
{
get { return stitches.Count; }
}
public IEnumerable<Stitch> Stitches
{
get { return stitches.AsReadOnly(); }
}
private void RegisterActor(ObiActor actor)
{
if (actor != null)
{
actor.OnBlueprintLoaded += Actor_OnBlueprintLoaded;
actor.OnBlueprintUnloaded += Actor_OnBlueprintUnloaded;
if (actor.solver != null)
Actor_OnBlueprintLoaded(actor, actor.sourceBlueprint);
}
}
private void UnregisterActor(ObiActor actor)
{
if (actor != null)
{
actor.OnBlueprintLoaded -= Actor_OnBlueprintLoaded;
actor.OnBlueprintUnloaded -= Actor_OnBlueprintUnloaded;
if (actor.solver != null)
Actor_OnBlueprintUnloaded(actor, actor.sourceBlueprint);
}
}
public void OnEnable()
{
RegisterActor(actor1);
RegisterActor(actor2);
}
public void OnDisable()
{
UnregisterActor(actor1);
UnregisterActor(actor2);
}
/**
* Adds a new stitch to the stitcher. Note that unlike calling Clear(), AddStitch does not automatically perform a
* PushDataToSolver(). You should manually call it once you're done adding multiple stitches.
* @param index of a particle in the first actor.
* @param index of a particle in the second actor.
* @return constrant index, that can be used to remove it with a call to RemoveStitch.
*/
public int AddStitch(int particle1, int particle2)
{
stitches.Add(new Stitch(particle1, particle2));
return stitches.Count - 1;
}
/**
* Removes. Note that unlike calling Clear(), AddStitch does not automatically perform a
* PushDataToSolver(). You should manually call it once you're done adding multiple stitches.
* @param constraint index.
*/
public void RemoveStitch(int index)
{
if (index >= 0 && index < stitches.Count)
stitches.RemoveAt(index);
}
public void Clear()
{
stitches.Clear();
PushDataToSolver();
}
void Actor_OnBlueprintUnloaded(ObiActor actor, ObiActorBlueprint blueprint)
{
// when any actor is removed from solver, remove stitches.
this.RemoveFromSolver(actor.solver);
}
void Actor_OnBlueprintLoaded(ObiActor actor, ObiActorBlueprint blueprint)
{
// when both actors are in the same solver, add stitches.
if (actor1 != null && actor2 != null && actor1.isLoaded && actor2.isLoaded)
{
if (actor1.solver != actor2.solver)
{
Debug.LogError("ObiStitcher cannot handle actors in different solvers.");
return;
}
AddToSolver(actor1.solver);
}
}
private void AddToSolver(ObiSolver solver)
{
if (!inSolver)
{
inSolver = true;
// create a constraint batch (CreateStitchConstraints() in burst returns a singleton):
m_BatchImpl = solver.implementation.CreateConstraintsBatch(Oni.ConstraintType.Stitch) as IStitchConstraintsBatchImpl;
// push current data to solver:
PushDataToSolver();
// enable/disable the batch:
m_BatchImpl.enabled = isActiveAndEnabled;
}
}
private void RemoveFromSolver(ObiSolver solver)
{
// remove the constraint batch from the solver
// (no need to destroy it as its destruction is managed by the solver)
// Oni.RemoveBatch(actor1.solver.OniSolver, batch);
if (inSolver && m_BatchImpl != null)
{
solver.implementation.DestroyConstraintsBatch(m_BatchImpl as IStitchConstraintsBatchImpl);
m_BatchImpl.Destroy();
m_BatchImpl = null;
inSolver = false;
}
}
public void PushDataToSolver()
{
if (!inSolver)
return;
// set solver constraint data:
lambdas.Clear();
particleIndices.ResizeUninitialized(stitches.Count * 2);
stiffnesses.ResizeUninitialized(stitches.Count);
lambdas.ResizeUninitialized(stitches.Count);
for (int i = 0; i < stitches.Count; i++)
{
particleIndices[i * 2] = actor1.solverIndices[stitches[i].particleIndex1];
particleIndices[i * 2 + 1] = actor2.solverIndices[stitches[i].particleIndex2];
stiffnesses[i] = 0;
lambdas[i] = 0;
}
m_BatchImpl.SetStitchConstraints(particleIndices, stiffnesses, lambdas, stitches.Count);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cdace116829344344988718be4fb70ab
timeCreated: 1489589304
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ec05a2c75bb4a4ce5a3a1baa76b4c8d5, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,888 @@
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Obi
{
public static class Constants
{
public const int maxVertsPerMesh = 65000;
public const int maxInstancesPerBatch = 1023;
}
public static class ObiUtils
{
public const float epsilon = 0.0000001f;
public const float sqrt3 = 1.73205080f;
public const float sqrt2 = 1.41421356f;
public const int FilterMaskBitmask = unchecked((int)0xffff0000);
public const int FilterCategoryBitmask = 0x0000ffff;
public const int ParticleGroupBitmask = 0x00ffffff;
public const int CollideWithEverything = 0x0000ffff;
public const int CollideWithNothing= 0x0;
public const int MaxCategory = 15;
public const int MinCategory = 0;
[Flags]
public enum ParticleFlags
{
SelfCollide = 1 << 24,
Fluid = 1 << 25,
OneSided = 1 << 26
}
// Colour alphabet from https://www.aic-color.org/resources/Documents/jaic_v5_06.pdf
public static readonly Color32[] colorAlphabet =
{
new Color32(240,163,255,255),
new Color32(0,117,220,255),
new Color32(153,63,0,255),
new Color32(76,0,92,255),
new Color32(25,25,25,255),
new Color32(0,92,49,255),
new Color32(43,206,72,255),
new Color32(255,204,153,255),
new Color32(128,128,128,255),
new Color32(148,255,181,255),
new Color32(143,124,0,255),
new Color32(157,204,0,255),
new Color32(194,0,136,255),
new Color32(0,51,128,255),
new Color32(255,164,5,255),
new Color32(255,168,187,255),
new Color32(66,102,0,255),
new Color32(255,0,16,255),
new Color32(94,241,242,255),
new Color32(0,153,143,255),
new Color32(224,255,102,255),
new Color32(116,10,255,255),
new Color32(153,0,0,255),
new Color32(255,255,128,255),
new Color32(255,255,0,255),
new Color32(255,80,5,255)
};
public static readonly string[] categoryNames =
{
"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"
};
public static void DrawArrowGizmo(float bodyLenght, float bodyWidth, float headLenght, float headWidth)
{
float halfBodyLenght = bodyLenght * 0.5f;
float halfBodyWidth = bodyWidth * 0.5f;
// arrow body:
Gizmos.DrawLine(new Vector3(halfBodyWidth, 0, -halfBodyLenght), new Vector3(halfBodyWidth, 0, halfBodyLenght));
Gizmos.DrawLine(new Vector3(-halfBodyWidth, 0, -halfBodyLenght), new Vector3(-halfBodyWidth, 0, halfBodyLenght));
Gizmos.DrawLine(new Vector3(-halfBodyWidth, 0, -halfBodyLenght), new Vector3(halfBodyWidth, 0, -halfBodyLenght));
// arrow head:
Gizmos.DrawLine(new Vector3(halfBodyWidth, 0, halfBodyLenght), new Vector3(headWidth, 0, halfBodyLenght));
Gizmos.DrawLine(new Vector3(-halfBodyWidth, 0, halfBodyLenght), new Vector3(-headWidth, 0, halfBodyLenght));
Gizmos.DrawLine(new Vector3(0, 0, halfBodyLenght + headLenght), new Vector3(headWidth, 0, halfBodyLenght));
Gizmos.DrawLine(new Vector3(0, 0, halfBodyLenght + headLenght), new Vector3(-headWidth, 0, halfBodyLenght));
}
public static void DebugDrawCross(Vector3 pos, float size, Color color)
{
Debug.DrawLine(pos - Vector3.right * size, pos + Vector3.right * size, color);
Debug.DrawLine(pos - Vector3.up * size, pos + Vector3.up * size, color);
Debug.DrawLine(pos - Vector3.forward * size, pos + Vector3.forward * size, color);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Swap<T>(ref T lhs, ref T rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Swap<T>(this T[] source, int index1, int index2)
{
if (source != null && index1 >= 0 && index2 != 0 && index1 < source.Length && index2 < source.Length)
{
T temp = source[index1];
source[index1] = source[index2];
source[index2] = temp;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Swap<T>(this IList<T> list, int index1, int index2)
{
if (list != null && index1 >= 0 && index2 != 0 && index1 < list.Count && index2 < list.Count)
{
T temp = list[index1];
list[index1] = list[index2];
list[index2] = temp;
}
}
public static void ShiftLeft<T>(this T[] source, int index, int count, int positions)
{
for (int j = 0; j < positions; ++j)
{
for (int i = index; i < index + count; ++i)
source.Swap(i, i - 1);
index--;
}
}
public static void ShiftRight<T>(this T[] source, int index, int count, int positions)
{
for (int j = 0; j < positions; ++j)
{
for (int i = index + count - 1; i >= index; --i)
source.Swap(i, i + 1);
index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool AreValid(this Bounds bounds)
{
return !(float.IsNaN(bounds.center.x) || float.IsInfinity(bounds.center.x) ||
float.IsNaN(bounds.center.y) || float.IsInfinity(bounds.center.y) ||
float.IsNaN(bounds.center.z) || float.IsInfinity(bounds.center.z));
}
public static Bounds Transform(this Bounds b, Matrix4x4 m)
{
var xa = m.GetColumn(0) * b.min.x;
var xb = m.GetColumn(0) * b.max.x;
var ya = m.GetColumn(1) * b.min.y;
var yb = m.GetColumn(1) * b.max.y;
var za = m.GetColumn(2) * b.min.z;
var zb = m.GetColumn(2) * b.max.z;
Bounds result = new Bounds();
Vector3 pos = m.GetColumn(3);
result.SetMinMax(Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + pos,
Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + pos);
return result;
}
public static int CountTrailingZeroes(int x)
{
int mask = 1;
for (int i = 0; i < 32; i++, mask <<= 1)
if ((x & mask) != 0)
return i;
return 32;
}
public static void Add(Vector3 a, Vector3 b, ref Vector3 result)
{
result.x = a.x + b.x;
result.y = a.y + b.y;
result.z = a.z + b.z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Remap(this float value, float from1, float to1, float from2, float to2)
{
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}
/**
* Modulo operator that also follows intuition for negative arguments. That is , -1 mod 3 = 2, not -1.
*/
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Mod(float a, float b)
{
return a - b * Mathf.Floor(a / b);
}
public static Matrix4x4 Add(this Matrix4x4 a, Matrix4x4 other)
{
for (int i = 0; i < 16; ++i)
a[i] += other[i];
return a;
}
public static float FrobeniusNorm(this Matrix4x4 a)
{
float norm = 0;
for (int i = 0; i < 16; ++i)
norm += a[i] * a[i];
return Mathf.Sqrt(norm);
}
public static Matrix4x4 ScalarMultiply(this Matrix4x4 a, float s)
{
for (int i = 0; i < 16; ++i)
a[i] *= s;
return a;
}
public static Vector3 ProjectPointLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd, out float mu, bool clampToSegment = true)
{
Vector3 ap = point - lineStart;
Vector3 ab = lineEnd - lineStart;
mu = Vector3.Dot(ap, ab) / Vector3.Dot(ab, ab);
if (clampToSegment)
mu = Mathf.Clamp01(mu);
return lineStart + ab * mu;
}
public static bool LinePlaneIntersection(Vector3 planePoint, Vector3 planeNormal, Vector3 linePoint, Vector3 lineDirection, out Vector3 point)
{
point = linePoint;
Vector3 lineNormal = lineDirection.normalized;
float denom = Vector3.Dot(planeNormal, lineNormal);
if (Mathf.Approximately(denom, 0))
return false;
float t = (Vector3.Dot(planeNormal,planePoint) - Vector3.Dot(planeNormal,linePoint)) / denom;
point = linePoint + lineNormal * t;
return true;
}
public static float RaySphereIntersection(Vector3 rayOrigin, Vector3 rayDirection, Vector3 center, float radius)
{
Vector3 oc = rayOrigin - center;
float a = Vector3.Dot(rayDirection, rayDirection);
float b = 2.0f * Vector3.Dot(oc, rayDirection);
float c = Vector3.Dot(oc, oc) - radius * radius;
float discriminant = b * b - 4 * a * c;
if(discriminant < 0){
return -1.0f;
}
else{
return (-b - Mathf.Sqrt(discriminant)) / (2.0f * a);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float InvMassToMass(float invMass)
{
return 1.0f / invMass;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MassToInvMass(float mass)
{
return 1.0f / Mathf.Max(mass, 0.00001f);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int PureSign(this float val)
{
return ((0 <= val)?1:0) - ((val < 0)?1:0);
}
public static void NearestPointOnTri(in Vector3 p1,
in Vector3 p2,
in Vector3 p3,
in Vector3 p,
out Vector3 result)
{
float e0x = p2.x - p1.x;
float e0y = p2.y - p1.y;
float e0z = p2.z - p1.z;
float e1x = p3.x - p1.x;
float e1y = p3.y - p1.y;
float e1z = p3.z - p1.z;
float v0x = p1.x - p.x;
float v0y = p1.y - p.y;
float v0z = p1.z - p.z;
float a00 = e0x * e0x + e0y * e0y + e0z * e0z;
float a01 = e0x * e1x + e0y * e1y + e0z * e1z;
float a11 = e1x * e1x + e1y * e1y + e1z * e1z;
float b0 = e0x * v0x + e0y * v0y + e0z * v0z;
float b1 = e1x * v0x + e1y * v0y + e1z * v0z;
const float zero = 0;
const float one = 1;
float det = a00 * a11 - a01 * a01;
float t0 = a01 * b1 - a11 * b0;
float t1 = a01 * b0 - a00 * b1;
if (t0 + t1 <= det)
{
if (t0 < zero)
{
if (t1 < zero) // region 4
{
if (b0 < zero)
{
t1 = zero;
if (-b0 >= a00) // V0
{
t0 = one;
}
else // E01
{
t0 = -b0 / a00;
}
}
else
{
t0 = zero;
if (b1 >= zero) // V0
{
t1 = zero;
}
else if (-b1 >= a11) // V2
{
t1 = one;
}
else // E20
{
t1 = -b1 / a11;
}
}
}
else // region 3
{
t0 = zero;
if (b1 >= zero) // V0
{
t1 = zero;
}
else if (-b1 >= a11) // V2
{
t1 = one;
}
else // E20
{
t1 = -b1 / a11;
}
}
}
else if (t1 < zero) // region 5
{
t1 = zero;
if (b0 >= zero) // V0
{
t0 = zero;
}
else if (-b0 >= a00) // V1
{
t0 = one;
}
else // E01
{
t0 = -b0 / a00;
}
}
else // region 0, interior
{
float invDet = one / det;
t0 *= invDet;
t1 *= invDet;
}
}
else
{
float tmp0, tmp1, numer, denom;
if (t0 < zero) // region 2
{
tmp0 = a01 + b0;
tmp1 = a11 + b1;
if (tmp1 > tmp0)
{
numer = tmp1 - tmp0;
denom = a00 - 2 * a01 + a11;
if (numer >= denom) // V1
{
t0 = one;
t1 = zero;
}
else // E12
{
t0 = numer / denom;
t1 = one - t0;
}
}
else
{
t0 = zero;
if (tmp1 <= zero) // V2
{
t1 = one;
}
else if (b1 >= zero) // V0
{
t1 = zero;
}
else // E20
{
t1 = -b1 / a11;
}
}
}
else if (t1 < zero) // region 6
{
tmp0 = a01 + b1;
tmp1 = a00 + b0;
if (tmp1 > tmp0)
{
numer = tmp1 - tmp0;
denom = a00 - 2 * a01 + a11;
if (numer >= denom) // V2
{
t1 = one;
t0 = zero;
}
else // E12
{
t1 = numer / denom;
t0 = one - t1;
}
}
else
{
t1 = zero;
if (tmp1 <= zero) // V1
{
t0 = one;
}
else if (b0 >= zero) // V0
{
t0 = zero;
}
else // E01
{
t0 = -b0 / a00;
}
}
}
else // region 1
{
numer = a11 + b1 - a01 - b0;
if (numer <= zero) // V2
{
t0 = zero;
t1 = one;
}
else
{
denom = a00 - 2 * a01 + a11;
if (numer >= denom) // V1
{
t0 = one;
t1 = zero;
}
else // 12
{
t0 = numer / denom;
t1 = one - t0;
}
}
}
}
result.x = p1.x + t0 * e0x + t1 * e1x;
result.y = p1.y + t0 * e0y + t1 * e1y;
result.z = p1.z + t0 * e0z + t1 * e1z;
}
/**
* Calculates the area of a triangle.
*/
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float TriangleArea(Vector3 p1, Vector3 p2, Vector3 p3)
{
return Mathf.Sqrt(Vector3.Cross(p2 - p1, p3 - p1).sqrMagnitude) / 2f;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float EllipsoidVolume(Vector3 principalRadii)
{
return 4.0f / 3.0f * Mathf.PI * principalRadii.x * principalRadii.y * principalRadii.z;
}
public static Quaternion RestDarboux(Quaternion q1, Quaternion q2)
{
Quaternion darboux = Quaternion.Inverse(q1) * q2;
Vector4 omega_plus, omega_minus;
omega_plus = new Vector4(darboux.x, darboux.y, darboux.z, darboux.w + 1);
omega_minus = new Vector4(darboux.x, darboux.y, darboux.z, darboux.w - 1);
if (omega_minus.sqrMagnitude > omega_plus.sqrMagnitude)
darboux = new Quaternion(darboux.x * -1, darboux.y * -1, darboux.z * -1, darboux.w * -1);
return darboux;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float RestBendingConstraint(Vector3 positionA, Vector3 positionB, Vector3 positionC)
{
Vector3 center = (positionA + positionB + positionC) / 3;
return (positionC - center).magnitude;
}
public static System.Collections.IEnumerable BilateralInterleaved(int count)
{
for (int i = 0; i < count; ++i)
{
if (i % 2 != 0)
yield return count - (count % 2) - i;
else yield return i;
}
}
public static void BarycentricCoordinates(in Vector3 A,
in Vector3 B,
in Vector3 C,
in Vector3 P,
ref Vector3 bary)
{
// Compute vectors
Vector3 v0 = C - A;
Vector3 v1 = B - A;
Vector3 v2 = P - A;
// Compute dot products
float dot00 = Vector3.Dot(v0,v0);
float dot01 = Vector3.Dot(v0,v1);
float dot02 = Vector3.Dot(v0,v2);
float dot11 = Vector3.Dot(v1,v1);
float dot12 = Vector3.Dot(v1,v2);
// Compute barycentric coordinates
float det = dot00 * dot11 - dot01 * dot01;
if (Math.Abs(det) > 1E-38)
{
float u = (dot11 * dot02 - dot01 * dot12) / det;
float v = (dot00 * dot12 - dot01 * dot02) / det;
bary = new Vector3(1-u-v,v,u);
}
}
public static void BarycentricInterpolation(in Vector3 p1, in Vector3 p2, in Vector3 p3, in Vector3 coords, out Vector3 result)
{
result.x = coords.x * p1.x + coords.y * p2.x + coords.z * p3.x;
result.y = coords.x * p1.y + coords.y * p2.y + coords.z * p3.y;
result.z = coords.x * p1.z + coords.y * p2.z + coords.z * p3.z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float BarycentricInterpolation(float p1, float p2, float p3, Vector3 coords)
{
return coords[0] * p1 + coords[1] * p2 + coords[2] * p3;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float BarycentricExtrapolationScale(Vector3 coords)
{
return 1.0f / (coords[0] * coords[0] +
coords[1] * coords[1] +
coords[2] * coords[2]);
}
public static Vector3[] CalculateAngleWeightedNormals(Vector3[] vertices, int[] triangles)
{
Vector3[] normals = new Vector3[vertices.Length];
var normalBuffer = new Dictionary<Vector3, Vector3>();
Vector3 v1, v2, v3, e1, e2;
for (int i = 0; i < triangles.Length; i += 3)
{
v1 = vertices[triangles[i]];
v2 = vertices[triangles[i + 1]];
v3 = vertices[triangles[i + 2]];
if (!normalBuffer.ContainsKey(v1))
normalBuffer[v1] = Vector3.zero;
if (!normalBuffer.ContainsKey(v2))
normalBuffer[v2] = Vector3.zero;
if (!normalBuffer.ContainsKey(v3))
normalBuffer[v3] = Vector3.zero;
e1 = v2 - v1;
e2 = v3 - v1;
normalBuffer[v1] += Vector3.Cross(e1,e2).normalized * Mathf.Acos(Vector3.Dot(e1.normalized, e2.normalized));
e1 = v3 - v2;
e2 = v1 - v2;
normalBuffer[v2] += Vector3.Cross(e1, e2).normalized * Mathf.Acos(Vector3.Dot(e1.normalized, e2.normalized));
e1 = v1 - v3;
e2 = v2 - v3;
normalBuffer[v3] += Vector3.Cross(e1, e2).normalized * Mathf.Acos(Vector3.Dot(e1.normalized, e2.normalized));
}
for (int i = 0; i < vertices.Length; ++i)
normals[i] = normalBuffer[vertices[i]].normalized;
return normals;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int MakePhase(int group, ParticleFlags flags)
{
return (group & ParticleGroupBitmask) | (int)flags;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetGroupFromPhase(int phase)
{
return phase & ParticleGroupBitmask;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ParticleFlags GetFlagsFromPhase(int phase)
{
return (ParticleFlags)(phase & ~ParticleGroupBitmask);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int MakeFilter(int mask, int category)
{
return (mask << 16) | (1 << category);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetCategoryFromFilter(int filter)
{
return CountTrailingZeroes(filter & FilterCategoryBitmask);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetMaskFromFilter(int filter)
{
return (filter & FilterMaskBitmask) >> 16;
}
public static void EigenSolve(Matrix4x4 D, out Vector3 S, out Matrix4x4 V)
{
// D is symmetric
// S is a vector whose elements are eigenvalues
// V is a matrix whose columns are eigenvectors
S = EigenValues(D);
Vector3 V0, V1, V2;
if (S[0] - S[1] > S[1] - S[2])
{
V0 = EigenVector(D, S[0]);
if (S[1] - S[2] < epsilon)
{
V2 = V0.unitOrthogonal();
}
else
{
V2 = EigenVector(D, S[2]); V2 -= V0 * Vector3.Dot(V0, V2); V2 = Vector3.Normalize(V2);
}
V1 = Vector3.Cross(V2, V0);
}
else
{
V2 = EigenVector(D, S[2]);
if (S[0] - S[1] < epsilon)
{
V1 = V2.unitOrthogonal();
}
else
{
V1 = EigenVector(D, S[1]); V1 -= V2 * Vector3.Dot(V2, V1); V1 = Vector3.Normalize(V1);
}
V0 = Vector3.Cross(V1, V2);
}
V = Matrix4x4.identity;
V.SetColumn(0,V0);
V.SetColumn(1,V1);
V.SetColumn(2,V2);
}
static Vector3 unitOrthogonal(this Vector3 input)
{
// Find a vector to cross() the input with.
if (!(input.x < input.z * epsilon)
|| !(input.y < input.z * epsilon))
{
float invnm = 1 / Vector3.Magnitude(new Vector2(input.x,input.y));
return new Vector3(-input.y * invnm, input.x * invnm, 0);
}
else
{
float invnm = 1 / Vector3.Magnitude(new Vector2(input.y,input.z));
return new Vector3(0, -input.z * invnm, input.y * invnm);
}
}
// D is symmetric, S is an eigen value
static Vector3 EigenVector(Matrix4x4 D, float S)
{
// Compute a cofactor matrix of D - sI.
Vector4 c0 = D.GetColumn(0); c0[0] -= S;
Vector4 c1 = D.GetColumn(1); c1[1] -= S;
Vector4 c2 = D.GetColumn(2); c2[2] -= S;
// Use an upper triangle
Vector3 c0p = new Vector3(c1[1] * c2[2] - c2[1] * c2[1], 0, 0);
Vector3 c1p = new Vector3(c2[1] * c2[0] - c1[0] * c2[2], c0[0] * c2[2] - c2[0] * c2[0], 0);
Vector3 c2p = new Vector3(c1[0] * c2[1] - c1[1] * c2[0], c1[0] * c2[0] - c0[0] * c2[1], c0[0] * c1[1] - c1[0] * c1[0]);
// Get a column vector with a largest norm (non-zero).
float C01s = c1p[0] * c1p[0];
float C02s = c2p[0] * c2p[0];
float C12s = c2p[1] * c2p[1];
Vector3 norm = new Vector3(c0p[0] * c0p[0] + C01s + C02s,
C01s + c1p[1] * c1p[1] + C12s,
C02s + C12s + c2p[2] * c2p[2]);
// index of largest:
int index = 0;
if (norm[0] > norm[1] && norm[0] > norm[2])
index = 0;
else if (norm[1] > norm[0] && norm[1] > norm[2])
index = 1;
else
index = 2;
Vector3 V = Vector3.zero;
// special case
if (norm[index] < epsilon)
{
V[0] = 1; return V;
}
else if (index == 0)
{
V[0] = c0p[0]; V[1] = c1p[0]; V[2] = c2p[0];
}
else if (index == 1)
{
V[0] = c1p[0]; V[1] = c1p[1]; V[2] = c2p[1];
}
else
{
V = c2p;
}
return Vector3.Normalize(V);
}
static Vector3 EigenValues(Matrix4x4 D)
{
float one_third = 1 / 3.0f;
float one_sixth = 1 / 6.0f;
float three_sqrt = Mathf.Sqrt(3.0f);
Vector3 c0 = D.GetColumn(0);
Vector3 c1 = D.GetColumn(1);
Vector3 c2 = D.GetColumn(2);
float m = one_third * (c0[0] + c1[1] + c2[2]);
// K is D - I*diag(S)
float K00 = c0[0] - m;
float K11 = c1[1] - m;
float K22 = c2[2] - m;
float K01s = c1[0] * c1[0];
float K02s = c2[0] * c2[0];
float K12s = c2[1] * c2[1];
float q = 0.5f * (K00 * (K11 * K22 - K12s) - K22 * K01s - K11 * K02s) + c1[0] * c2[1] * c0[2];
float p = one_sixth * (K00 * K00 + K11 * K11 + K22 * K22 + 2 * (K01s + K02s + K12s));
float p_sqrt = Mathf.Sqrt(p);
float tmp = p * p * p - q * q;
float phi = one_third * Mathf.Atan2(Mathf.Sqrt(Mathf.Max(0, tmp)), q);
float phi_c = Mathf.Cos(phi);
float phi_s = Mathf.Sin(phi);
float sqrt_p_c_phi = p_sqrt * phi_c;
float sqrt_p_3_s_phi = p_sqrt * three_sqrt * phi_s;
float e0 = m + 2 * sqrt_p_c_phi;
float e1 = m - sqrt_p_c_phi - sqrt_p_3_s_phi;
float e2 = m - sqrt_p_c_phi + sqrt_p_3_s_phi;
float aux;
if (e0 > e1)
{
aux = e0;
e0 = e1;
e1 = aux;
}
if (e0 > e2)
{
aux = e0;
e0 = e2;
e2 = aux;
}
if (e1 > e2)
{
aux = e1;
e1 = e2;
e2 = aux;
}
return new Vector3(e2, e1, e0);
}
public static Vector3 GetPointCloudCentroid(List<Vector3> points)
{
Vector3 centroid = Vector3.zero;
for (int i = 0; i < points.Count; ++i)
centroid += points[i];
return centroid / points.Count;
}
public static void GetPointCloudAnisotropy(List<Vector3> points, float max_anisotropy, float radius, in Vector3 hint_normal, ref Vector3 centroid, ref Quaternion orientation, ref Vector3 principal_radii)
{
int count = points.Count;
if (count < 2 || radius <= 0 || max_anisotropy <= 0)
{
principal_radii = Vector3.one * radius;
orientation = Quaternion.identity;
return;
}
centroid = GetPointCloudCentroid(points);
// three columns of a 3x3 anisotropy matrix:
Vector4 c0 = Vector4.zero,
c1 = Vector4.zero,
c2 = Vector4.zero;
Matrix4x4 anisotropy = Matrix4x4.zero;
// multiply offset by offset transposed, and add to matrix:
for (int i = 0; i < count; i++)
{
Vector4 offset = points[i] - centroid;
c0 += offset * offset[0];
c1 += offset * offset[1];
c2 += offset * offset[2];
}
// calculate maximum absolute value:
float max0 = Mathf.Max(Mathf.Max(Mathf.Abs(c0.x), Mathf.Abs(c0.y)), Mathf.Abs(c0.z));
float max1 = Mathf.Max(Mathf.Max(Mathf.Abs(c1.x), Mathf.Abs(c1.y)), Mathf.Abs(c1.z));
float max2 = Mathf.Max(Mathf.Max(Mathf.Abs(c2.x), Mathf.Abs(c2.y)), Mathf.Abs(c2.z));
float max = Mathf.Max(Mathf.Max(max0, max1), max2);

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8ac31071459b040f39f1d86300525ed4
timeCreated: 1435569421
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
using UnityEngine;
using System.Collections;
namespace Obi
{
public static class ObiVectorMath
{
public static void Cross(Vector3 a, Vector3 b, ref float x, ref float y, ref float z)
{
x = a.y * b.z - a.z * b.y;
y = a.z * b.x - a.x * b.z;
z = a.x * b.y - a.y * b.x;
}
public static void Cross(Vector3 a, Vector3 b, ref Vector3 res)
{
res.x = a.y * b.z - a.z * b.y;
res.y = a.z * b.x - a.x * b.z;
res.z = a.x * b.y - a.y * b.x;
}
public static void Cross(float ax, float ay, float az, float bx, float by, float bz, ref float x, ref float y, ref float z)
{
x = ay * bz - az * by;
y = az * bx - ax * bz;
z = ax * by - ay * bx;
}
/**
* res = b - a
*/
public static void Subtract(Vector3 a, Vector3 b, ref Vector3 res)
{
res.x = b.x - a.x;
res.y = b.y - a.y;
res.z = b.z - a.z;
}
public static void BarycentricInterpolation(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 n1, Vector3 n2, Vector3 n3, Vector3 coords, float height, ref Vector3 res)
{
res.x = coords.x * p1.x + coords.y * p2.x + coords.z * p3.x + (coords.x * n1.x + coords.y * n2.x + coords.z * n3.x) * height;
res.y = coords.x * p1.y + coords.y * p2.y + coords.z * p3.y + (coords.x * n1.y + coords.y * n2.y + coords.z * n3.y) * height;
res.z = coords.x * p1.z + coords.y * p2.z + coords.z * p3.z + (coords.x * n1.z + coords.y * n2.z + coords.z * n3.z) * height;
}
}
}

View File

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

View File

@@ -0,0 +1,38 @@
using UnityEngine;
namespace Obi
{
[RequireComponent(typeof(ObiActor))]
public class SetCategory : MonoBehaviour
{
public int category;
private ObiActor act;
private void Awake()
{
act = GetComponent<ObiActor>();
act.OnBlueprintLoaded += OnLoad;
if (act.isLoaded)
act.SetFilterCategory(category);
}
private void OnDestroy()
{
act.OnBlueprintLoaded -= OnLoad;
}
private void OnValidate()
{
category = Mathf.Clamp(category, ObiUtils.MinCategory, ObiUtils.MaxCategory);
if (act != null && act.isLoaded)
act.SetFilterCategory(category);
}
private void OnLoad(ObiActor actor, ObiActorBlueprint blueprint)
{
actor.SetFilterCategory(category);
}
}
}

View File

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