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