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