2 * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 3 of the GNU Lesser General Public License
6 * or later as published by the Free Software Foundation.
10 package eu.svjatoslav.sixth.e3d.gui;
12 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
14 import static java.lang.Math.cos;
15 import static java.lang.Math.sin;
17 public class Avatar implements ViewUpdateListener {
19 public static final double SPEED_LIMIT = 30;
21 * Just in case we want to adjust global speed for some reason.
23 private static final double SPEED_MULTIPLIER = .02d;
25 * Avatar movement speed, relative to avatar itself. When avatar coordinates
26 * are updated within the world, avatar orientation relative to the world is
29 private final Point3D movementVector = new Point3D();
30 public double avatarAcceleration = 0.1;
32 * Avatar location within the 3D world.
34 private Point3D location = new Point3D();
37 * Avatar orientation on the X-Z plane. It changes when turning left or
40 private double orientationXZ;
43 * Avatar orientation on the Y-Z plane. It changes when looking up or down.
45 private double orientationYZ;
48 * Determines amount of friction user experiences every millisecond while moving around in space.
50 private static final double MILLISECOND_FRICTION = 1.005;
55 public Avatar(final Avatar sourceView) {
56 setLocation(new Point3D(sourceView.location));
57 setAngleXZ(sourceView.getAngleXZ());
58 setAngleYZ(sourceView.getAngleYZ());
61 public Avatar(final Point3D location) {
62 setLocation(location);
65 public Avatar(final Point3D location, final float angleXZ,
66 final float angleYZ) {
67 setLocation(location);
73 public boolean beforeViewUpdate(final ViewContext viewContext, final int millisecondsSinceLastFrame) {
75 final Point3D locationBeforeUpdate = new Point3D(location);
76 translateAvatarLocation(millisecondsSinceLastFrame);
77 applyFrictionToUserMovement(millisecondsSinceLastFrame);
78 return isFrameRepaintNeeded(locationBeforeUpdate);
81 private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
82 final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
83 return distanceMoved > 0.03;
86 public void enforceSpeedLimit() {
87 final double currentSpeed = movementVector
88 .getDistanceTo(Point3D.ZERO);
90 if (currentSpeed <= SPEED_LIMIT)
93 movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
96 public double getAngleXZ() {
100 public void setAngleXZ(final double angleXZ) {
101 orientationXZ = angleXZ;
104 public double getAngleYZ() {
105 return orientationYZ;
108 public void setAngleYZ(final double angleYZ) {
109 orientationYZ = angleYZ;
112 public Point3D getLocation() {
116 public void setLocation(final Point3D location) {
117 this.location = location;
120 public Point3D getMovementVector() {
121 return movementVector;
124 public double getMovementSpeed() {
125 return movementVector.getDistanceTo(Point3D.ZERO);
128 private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
129 for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
130 applyMillisecondFrictionToUserMovementVector();
133 private void applyMillisecondFrictionToUserMovementVector() {
134 getMovementVector().x /= MILLISECOND_FRICTION;
135 getMovementVector().y /= MILLISECOND_FRICTION;
136 getMovementVector().z /= MILLISECOND_FRICTION;
140 * Translate coordinates based on avatar movement vector and avatar orientation in the world.
142 * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
143 * Therefore we take frame rendering time into account when translating
144 * avatar between consecutive frames.
146 private void translateAvatarLocation(int millisecondsPassedSinceLastFrame) {
147 location.x -= (float) sin(getAngleXZ())
148 * getMovementVector().z * SPEED_MULTIPLIER
149 * millisecondsPassedSinceLastFrame;
150 location.z += (float) cos(getAngleXZ())
151 * getMovementVector().z * SPEED_MULTIPLIER
152 * millisecondsPassedSinceLastFrame;
154 location.x += (float) cos(getAngleXZ())
155 * getMovementVector().x * SPEED_MULTIPLIER
156 * millisecondsPassedSinceLastFrame;
157 location.z += (float) sin(getAngleXZ())
158 * getMovementVector().x * SPEED_MULTIPLIER
159 * millisecondsPassedSinceLastFrame;
161 location.y += getMovementVector().y * SPEED_MULTIPLIER
162 * millisecondsPassedSinceLastFrame;