Updated copyright.
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / gui / Avatar.java
1 /*
2  * Sixth 3D engine. Copyright ©2012-2016, 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 public class Avatar implements ViewUpdateListener {
15
16     public static final double SPEED_LIMIT = 30;
17     /**
18      * Just in case we want to adjust global speed for some reason.
19      */
20     private static final double SPEED_MULTIPLIER = .02d;
21     /**
22      * Avatar movement speed, relative to avatar itself. When avatar coordinates
23      * are updated within the world, avatar orientation relative to the world is
24      * taken into account.
25      */
26     private final Point3D movementDirection = new Point3D();
27     public double avatarAcceleration = 0.1;
28     /**
29      * Avatar location within the 3D world.
30      */
31     private Point3D location = new Point3D();
32
33     /**
34      * Avatar orientation on the X-Z plane. It changes when turning left or
35      * right.
36      */
37     private double orientationXZ;
38
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 beforeViewUpdate(final ViewContext viewContext,
66                                     final int millisecondsSinceLastFrame) {
67
68         final Point3D locationBeforeUpdate = new Point3D(location);
69         updateLocation(millisecondsSinceLastFrame);
70
71         final double distanceMoved = location
72                 .getDistanceTo(locationBeforeUpdate);
73
74         return distanceMoved > 0.03;
75
76     }
77
78     public void enforceSpeedLimit() {
79         final double currentSpeed = movementDirection
80                 .getDistanceTo(Point3D.ZERO);
81
82         if (currentSpeed <= SPEED_LIMIT)
83             return;
84
85         movementDirection.scaleDown(currentSpeed / SPEED_LIMIT);
86     }
87
88     public double getAngleXZ() {
89         return orientationXZ;
90     }
91
92     public void setAngleXZ(final double angleXZ) {
93         orientationXZ = angleXZ;
94     }
95
96     public double getAngleYZ() {
97         return orientationYZ;
98     }
99
100     public void setAngleYZ(final double angleYZ) {
101         orientationYZ = angleYZ;
102     }
103
104     public Point3D getLocation() {
105         return location;
106     }
107
108     public void setLocation(final Point3D location) {
109         this.location = location;
110     }
111
112     public Point3D getMovementDirection() {
113         return movementDirection;
114     }
115
116     public double getMovementSpeed() {
117         return movementDirection.getDistanceTo(Point3D.ZERO);
118     }
119
120     /**
121      * Update camera location based on current speed
122      */
123     public void updateLocation(final int millisecondsPassedSinceLastFrame) {
124
125         // translate user coordinates based on avatar movement speed, and avatar
126         // orientation in the world
127         {
128             location.x -= (float) Math.sin(getAngleXZ())
129                     * getMovementDirection().z * SPEED_MULTIPLIER
130                     * millisecondsPassedSinceLastFrame;
131             location.z += (float) Math.cos(getAngleXZ())
132                     * getMovementDirection().z * SPEED_MULTIPLIER
133                     * millisecondsPassedSinceLastFrame;
134
135             location.x += (float) Math.cos(getAngleXZ())
136                     * getMovementDirection().x * SPEED_MULTIPLIER
137                     * millisecondsPassedSinceLastFrame;
138             location.z += (float) Math.sin(getAngleXZ())
139                     * getMovementDirection().x * SPEED_MULTIPLIER
140                     * millisecondsPassedSinceLastFrame;
141
142             location.y += getMovementDirection().y * SPEED_MULTIPLIER
143                     * millisecondsPassedSinceLastFrame;
144         }
145
146         final double millisecondFriction = 1.005;
147         // apply friction to progressively slow movement
148         for (int i = 0; i < millisecondsPassedSinceLastFrame; i++) {
149             getMovementDirection().x = getMovementDirection().x
150                     / millisecondFriction;
151             getMovementDirection().y = getMovementDirection().y
152                     / millisecondFriction;
153             getMovementDirection().z = getMovementDirection().z
154                     / millisecondFriction;
155         }
156     }
157 }