141 lines
5.0 KiB
C#
141 lines
5.0 KiB
C#
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.positions.count; ++i)
|
|
{
|
|
|
|
Vector3 worldPos = solver2World.MultiplyPoint3x4(solver.positions[i]);
|
|
|
|
float mu;
|
|
Vector3 projected = ObiUtils.ProjectPointLine(ray.origin, ray.origin + ray.direction, worldPos, 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.positions[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;
|
|
}
|
|
}
|
|
}
|