17-02-2020, 10:12 AM
(17-02-2020, 07:36 AM)GrimCaplan Wrote: I'm attempting to put together a simple script based off ObiParticlePicker that supports multiple touches. The multiple touches is working fine. The issue I'm having is that when I select a particle index, I do not seem to understand how to modify the particle's position.
I'm attempting to add a value to the y-level of a renderable position in order to increase its height. I know the axis in which I need to affect depends on the rotation and position of the cloth.
I have also done some debugging and confirmed that an index is in-fact being selected.
The reason for this is because I don't really want the effect that the particle dragger produces. When moving the finger around the screen it attempts to hold on to that particle even if I swipe to the other side of the screen whereas my desired functionality is the elevation and/or depression of particles under the finger. I know I could simply adapt the OnParticleDragged and treat all movement as a sequence of OnParticlePicked and then OnParticleReleased, but my goal is not just functioning but actually increasing my understanding of the system. I'll probably be using it a lot in the future. Directly effecting the elevation also seems less 'hackish' than hooking the two events in series.
Also if it would be helpful you can use the multi-touch for the "courtesy" code because that does actually work. I promise I'm not a terrible developer, Obi has just been hard for me to absorb.
Code:using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Obi;
public class SlimeSolverManager : MonoBehaviour
{
[SerializeField]
ObiSolver m_Solver;
[SerializeField]
float m_TouchRadius;
[SerializeField]
float m_TouchDistortionLevel = 1;
private int[] m_PickedParticleIndices;
private float pickedParticleDepth = 0;
private void OnEnable()
{
Debug.Assert(m_Solver != null, "Slime Solver Manager Missing Obi Solver Component Reference!");
}
// Update is called once per frame
void LateUpdate()
{
if (m_Solver)
{
if (Input.touchCount > 0)
m_PickedParticleIndices = new int[Input.touchCount];
foreach (Touch touch in Input.touches)
{
m_PickedParticleIndices[touch.fingerId] = -1;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float closestMu = float.MaxValue;
float closestDistance = float.MaxValue;
// Find the closest particle hit by the ray:
for (int i = 0; i < m_Solver.renderablePositions.count; ++i)
{
float mu;
Vector3 projected = ObiUtils.ProjectPointLine
(m_Solver.renderablePositions[i], ray.origin, ray.origin + ray.direction, out mu, false);
float distanceToRay = Vector3.SqrMagnitude
((Vector3)m_Solver.renderablePositions[i] - projected);
mu = Mathf.Max(0, mu);
float radius = m_Solver.principalRadii[i][0] * m_TouchRadius;
if (distanceToRay <= radius * radius && distanceToRay < closestDistance && mu < closestMu)
{
closestMu = mu;
closestDistance = distanceToRay;
m_PickedParticleIndices[touch.fingerId] = i;
}
}
if (m_PickedParticleIndices[touch.fingerId] >= 0)
{
pickedParticleDepth = Camera.main.transform.InverseTransformVector((Vector3)m_Solver.renderablePositions
[m_PickedParticleIndices[touch.fingerId]] - Camera.main.transform.position).z;
Vector3 worldPosition = Camera.main.ScreenToWorldPoint
(new Vector3(Input.mousePosition.x, Input.mousePosition.y, pickedParticleDepth));
float x = m_Solver.renderablePositions[m_PickedParticleIndices[touch.fingerId]].x;
float y = m_Solver.renderablePositions[m_PickedParticleIndices[touch.fingerId]].y;
float z = m_Solver.renderablePositions[m_PickedParticleIndices[touch.fingerId]].z;
float w = m_Solver.renderablePositions[m_PickedParticleIndices[touch.fingerId]].w;
m_Solver.renderablePositions
[m_PickedParticleIndices[touch.fingerId]] =
new Vector4(x, y + m_TouchDistortionLevel, z, w);
}
}
}
}
}
I know the declaration of four floats is a little lazy but at this point I'm simply trying to get results. I'll opt later.
Renderable positions, as the name implies, are only to be used for rendering. They are generated from scratch at the end of every simulation step, but are not part of the simulation. So modifying them does not have any effect on physics at all, unlike regular positions. From the manual:
Quote:If the solver's interpolation mode is set to interpolate, solver.renderablePositions will contain temporally smoothed-out positions that won´t coincide with the actual particle positions at the end of the simulation step. That's why they are called "renderable": they are only used for smooth rendering, but they aren't needed by the solver and thus not calculated unless requested.
I assume you're going for a "squeeze-release" type of effect: particles should raise under a finger, and slowly fall back to y = 0 after the finger has moved away. Is this correct? if so, easiest way to achieve it would be using external forces, similar to what the default picker does:
- Calculate a spring force that strives to keep each particle at y = 0. This is what will cause particles to fall back to their position when a touch ceases.
- When there's a touch present, calculate a force opposite to the previous one, that strives to keep the particle at y = some positive value.