I'm using the line renderer to create a "drawing" app, and now I'm trying to enable pen pressure with the width curve on the LineRenderer. The problem is that the "time" values (horizontal axis) of the AnimationCurve are normalized from 0 to 1, so I can't just add a value to the end of it every time a position is added. Unless there's a function I'm unaware of, the only solution I can think of is finding a way to shift all hundreds of previous values by an exact percentage as I'm drawing the line, and do this as positions are being added. This seems excessive.
I'm at a loss on what to do here.
This is the basic line that adds the point every frame as I draw with the pen.
curve.AddKey(1.0f, penPressureValue);
The "1.0f" is the position on the curve (1 being the last), so this just adds a point at the end every frame, changing the entire line's width as I draw.
CodePudding user response:
Unfortunately I don't think there is a way of achieveing this that is not in some way uncanny and performance intense.
Of course you can calculate it (assuming the LineRenderer
starts with position count = 0) like e.g.
public LineRenderer line;
private int positionCount;
private float totalLengthOld;
private void AddPoint(Vector3 position, float width)
{
// increase the position count by one
positionCount ;
// set the count back to the line
line.positionCount = positionCount;
// add our new point
line.SetPosition(positionCount - 1, position);
// now get the current width curve
var curve = line.widthCurve;
// Is this the beginning of the line?
if (positionCount == 1)
{
// First point => simply set the first keyframe
curve.MoveKey(0, new Keyframe(0f, width));
}
else
{
// otherwise get all positions
var positions = new Vector3[positionCount];
line.GetPositions(positions);
// sum up the distances between positions to obtain the length of the line
var totalLengthNew = 0f;
for (var i = 1; i < positionCount; i )
{
totalLengthNew = Vector3.Distance(positions[i - 1], positions[i]);
}
// calculate the time factor we have to apply to all already existing keyframes
var factor = totalLengthOld / totalLengthNew;
// then store for the next added point
totalLengthOld = totalLengthNew;
// now move all existing keys which are currently based on the totalLengthOld to according positions based on the totalLengthNew
// we can skip the first one as it will stay at 0 always
var keys = curve.keys;
for (var i = 1; i < keys.Length; i )
{
var key = keys[i];
key.time *= factor;
curve.MoveKey(i, key);
}
// add the new last keyframe
curve.AddKey(1f, width);
}
// finally write the curve back to the line
line.widthCurve = curve;
}
Just as a little demo
public class Example : MonoBehaviour
{
public LineRenderer line;
public Transform pen;
[Range(0.01f, 0.5f)] public float width;
public float drawThreshold = 0.1f;
private int positionCount;
private float totalLengthOld;
private Vector3 lastPenPosition;
private void Awake()
{
line = GetComponent<LineRenderer>();
line.useWorldSpace = true;
line.positionCount = 0;
lastPenPosition = pen.position;
}
private void Update()
{
// just for the demo simply ping-pong the width over time
width = Mathf.Lerp(0.01f, 0.8f, Mathf.PingPong(Time.time, 1f));
var currentPenPosition = pen.position;
if (Vector3.Distance(lastPenPosition, currentPenPosition) >= drawThreshold)
{
lastPenPosition = currentPenPosition;
AddPoint(currentPenPosition, width);
}
}
private void AddPoint(Vector3 position, float width)
{
positionCount ;
line.positionCount = positionCount;
line.SetPosition(positionCount - 1, position);
var curve = line.widthCurve;
if (positionCount == 1)
{
curve.MoveKey(0, new Keyframe(0f, width));
}
else
{
var positions = new Vector3[positionCount];
line.GetPositions(positions);
var totalLengthNew = 0f;
for (var i = 1; i < positionCount; i )
{
totalLengthNew = Vector3.Distance(positions[i - 1], positions[i]);
}
var factor = totalLengthOld / totalLengthNew;
totalLengthOld = totalLengthNew;
var keys = curve.keys;
for (var i = 1; i < keys.Length; i )
{
var key = keys[i];
key.time *= factor;
curve.MoveKey(i, key);
}
curve.AddKey(1f, width);
}
line.widthCurve = curve;
}
}
Of course this will hit performance limits after a certain amount of points. But I think this is as far as you can go for now using the LineRenderer
. Otherwise the LineRenderer
is maybe just not the correct tool for drawing.
You could of course go tricky and after certain amount of points bake the existing line into a separate fixed mesh using
You'll have to play around a bit with the threshold, 50
is probably a bit low, just used it for the demo. You want to find a balance between performance cost of iterating all the keyframes and baking meshes ;)