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