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