Eye movement

Eye contact is an important factor to make characters feel alive and aware of yours and other things' presence. In this chapter, we'll make a control that will follow a spatial with its eyes, as shown in the following screenshot:

Eye movement

How to do it...

Eye tracking can be implemented in a single control using the following steps:

  1. We begin by creating a new class called EyeTrackingControl that extends AbstractControl.
  2. It needs two Bone fields: one called leftEye and another called rightEye. Furthermore, we should add a spatial called lookAtObject and a related Vector3f called focusPoint.
  3. In the setSpatial method, we find and store the bones for leftEye and rightEye.
  4. We also need a method to set lookAtObject.
  5. With this done, we add most of the other functionalities to the controlUpdate method. First of all, we need to take control of the bones or we won't be able to modify their rotation, as shown in the following code:
    if(enabled && lookAtObject != null){
      leftEye.setUserControl(true);
      rightEye.setUserControl(true);
  6. Next, we need to establish the lookAtObject position that is relative to the eyes. We do this by converting the position to model space and storing it in focusPoint, as shown in the following code:
    focusPoint.set(lookAtObject.getWorldTranslation().subtract(getSpatial().getWorldTranslation()));
  7. Subtracting the eye position from Vector3f gives us the relative direction:
    Vector3f eyePos = leftEye.getModelSpacePosition();
    Vector3f direction = eyePos.subtract(focusPoint).negateLocal();
  8. We create a new Quaternion and have it look in the direction of the direction vector. We can apply this on our eyes after modifying it a bit as its 0-rotation is up:
    Quaternion q = new Quaternion();
    q.lookAt(direction, Vector3f.UNIT_Y);
    q.addLocal(offsetQuat);
    q.normalizeLocal();
  9. Then, we apply it by using setUserTransformsWorld. Finally, we give the control of the bones back to the system using the following code:
    leftEye.setUserTransformsWorld(leftEye.getModelSpacePosition(), q);
    rightEye.setUserTransformsWorld(rightEye.getModelSpacePosition(), q);
    leftEye.setUserControl(false);
    rightEye.setUserControl(false);

How it works...

The actual code is a fairly straightforward trigonometry, but knowing what values to use and the flow of doing it can be tricky.

Once the class receives an object to look at, it subtracts the model's worldTranslation from lookAtObjects so they end up in a coordinate system that is relative to the model's origo point also called modelspace.

Using setUserTransformsWorld also sets the position, but since we supply its current modelSpacePosition, no change will be applied.

Actually, the direction of each eye should be calculated separately for the result to be entirely correct.

There's more...

By now, the character has a very intent stare at the camera. This is an improvement, but it can be made more lifelike. Something that may not be so obvious is that we rarely look at the same point all the time even if we look at the same object. We can emulate this behavior by adding a random bit of flickering to the control:

private float flickerTime = 0f;
private float flickerAmount = 0.2f;
private Vector3f flickerDirection = new Vector3f();

By introducing these three fields, we have a base for what we want to do:

flickerTime += tpf * FastMath.nextRandomFloat();
if(flickerTime > 0.5f){
  flickerTime = 0;
  flickerDirection.set(FastMath.nextRandomFloat() * flickerAmount, FastMath.nextRandomFloat() * flickerAmount, 0);
}
direction.addLocal(flickerDirection);

This piece of code goes in the middle of the controlUpdate method, right after calculating the direction. What we do is we increase flickerTime until it reaches 0.5f (note that this is not in seconds since we apply a random number). Once this happens, we randomize flickerDirection based on flickerAmount and reset flickerTime.

With each consecutive update, we will apply this to the calculated direction and slightly offset the focus point.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.254.61