Improved code readability
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / gui / Avatar.java
1 /*
2  * Sixth 3D engine. Author: Svjatoslav Agejenko.
3  * This project is released under Creative Commons Zero (CC0) license.
4  */
5 package eu.svjatoslav.sixth.e3d.gui;
6
7 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
8
9 import static java.lang.Math.cos;
10 import static java.lang.Math.sin;
11
12 /**
13  * The avatar is a user-controlled object in the 3D world.
14  */
15 public class Avatar implements ViewRenderListener {
16
17     /**
18      * Avatar movement speed, relative to the world. When avatar coordinates are
19      * updated within the world, avatar orientation relative to the world is
20      * taken into account.
21      */
22     public static final double SPEED_LIMIT = 30;
23     /**
24      * Just in case we want to adjust global speed for some reason.
25      */
26     private static final double SPEED_MULTIPLIER = .02d;
27     /**
28      * Determines amount of friction user experiences every millisecond while moving around in space.
29      */
30     private static final double MILLISECOND_FRICTION = 1.005;
31     /**
32      * Avatar movement speed, relative to avatar itself. When avatar coordinates
33      * are updated within the world, avatar orientation relative to the world is
34      * taken into account.
35      */
36     private final Point3D movementVector = new Point3D();
37     public double avatarAcceleration = 0.1;
38     /**
39      * Avatar location within the 3D world.
40      */
41     private Point3D location = new Point3D();
42     /**
43      * Avatar orientation on the X-Z plane. It changes when turning left or
44      * right.
45      */
46     private double orientationXZ;
47     /**
48      * Avatar orientation on the Y-Z plane. It changes when looking up or down.
49      */
50     private double orientationYZ;
51
52     public Avatar() {
53     }
54
55     public Avatar(final Avatar sourceView) {
56         setLocation(new Point3D(sourceView.location));
57         setAngleXZ(sourceView.getAngleXZ());
58         setAngleYZ(sourceView.getAngleYZ());
59     }
60
61     public Avatar(final Point3D location) {
62         setLocation(location);
63     }
64
65     public Avatar(final Point3D location, final float angleXZ,
66                   final float angleYZ) {
67         setLocation(location);
68         setAngleXZ(angleXZ);
69         setAngleYZ(angleYZ);
70     }
71
72
73     @Override
74     public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
75
76         final Point3D locationBeforeUpdate = new Point3D(location);
77         translateAvatarLocationBasedOnMovementVector(millisecondsSinceLastFrame);
78         applyFrictionToUserMovement(millisecondsSinceLastFrame);
79         return isFrameRepaintNeeded(locationBeforeUpdate);
80     }
81
82     private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
83         final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
84         return distanceMoved > 0.03;
85     }
86
87     public void enforceSpeedLimit() {
88         final double currentSpeed = movementVector.getVectorLength();
89
90         if (currentSpeed <= SPEED_LIMIT)
91             return;
92
93         movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
94     }
95
96     public double getAngleXZ() {
97         return orientationXZ;
98     }
99
100     public void setAngleXZ(final double angleXZ) {
101         orientationXZ = angleXZ;
102     }
103
104     public double getAngleYZ() {
105         return orientationYZ;
106     }
107
108     public void setAngleYZ(final double angleYZ) {
109         orientationYZ = angleYZ;
110     }
111
112     public Point3D getLocation() {
113         return location;
114     }
115
116     public void setLocation(final Point3D location) {
117         this.location = location;
118     }
119
120     public Point3D getMovementVector() {
121         return movementVector;
122     }
123
124     public double getMovementSpeed() {
125         return movementVector.getVectorLength();
126     }
127
128     /**
129      * Apply friction to avatar movement vector.
130      *
131      * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
132      *                                         Therefore, we take frame rendering time into account when translating
133      *                                         avatar between consecutive frames.
134      */
135     private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
136         for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
137             applyMillisecondFrictionToUserMovementVector();
138     }
139
140     /**
141      * Apply friction to avatar movement vector.
142      */
143     private void applyMillisecondFrictionToUserMovementVector() {
144         getMovementVector().x /= MILLISECOND_FRICTION;
145         getMovementVector().y /= MILLISECOND_FRICTION;
146         getMovementVector().z /= MILLISECOND_FRICTION;
147     }
148
149     /**
150      * Translate coordinates based on avatar movement vector and avatar orientation in the world.
151      *
152      * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
153      *                                         Therefore, we take frame rendering time into account when translating
154      *                                         avatar between consecutive frames.
155      */
156     private void translateAvatarLocationBasedOnMovementVector(int millisecondsPassedSinceLastFrame) {
157         location.x -= (float) sin(getAngleXZ())
158                 * getMovementVector().z * SPEED_MULTIPLIER
159                 * millisecondsPassedSinceLastFrame;
160         location.z += (float) cos(getAngleXZ())
161                 * getMovementVector().z * SPEED_MULTIPLIER
162                 * millisecondsPassedSinceLastFrame;
163
164         location.x += (float) cos(getAngleXZ())
165                 * getMovementVector().x * SPEED_MULTIPLIER
166                 * millisecondsPassedSinceLastFrame;
167         location.z += (float) sin(getAngleXZ())
168                 * getMovementVector().x * SPEED_MULTIPLIER
169                 * millisecondsPassedSinceLastFrame;
170
171         location.y += getMovementVector().y * SPEED_MULTIPLIER
172                 * millisecondsPassedSinceLastFrame;
173     }
174 }