2 * Sixth 3D engine. Author: Svjatoslav Agejenko.
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.sixth.e3d.gui;
7 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
9 import static java.lang.Math.cos;
10 import static java.lang.Math.sin;
13 * The avatar is a user-controlled object in the 3D world.
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.
18 public class Avatar implements ViewRenderListener {
21 * Avatar movement speed, relative to the world. When avatar coordinates are
22 * updated within the world, avatar orientation relative to the world is
25 public static final double SPEED_LIMIT = 30;
27 * Just in case we want to adjust global speed for some reason.
29 private static final double SPEED_MULTIPLIER = .02d;
31 * Determines amount of friction user experiences every millisecond while moving around in space.
33 private static final double MILLISECOND_FRICTION = 1.005;
35 * Avatar movement speed, relative to avatar itself. When avatar coordinates
36 * are updated within the world, avatar orientation relative to the world is
39 private final Point3D movementVector = new Point3D();
40 public double avatarAcceleration = 0.1;
42 * Avatar location within the 3D world.
44 private Point3D location = new Point3D();
46 * Avatar orientation on the X-Z plane. It changes when turning left or
49 private double orientationXZ;
51 * Avatar orientation on the Y-Z plane. It changes when looking up or down.
53 private double orientationYZ;
58 public Avatar(final Avatar sourceView) {
59 setLocation(new Point3D(sourceView.location));
60 setAngleXZ(sourceView.getAngleXZ());
61 setAngleYZ(sourceView.getAngleYZ());
64 public Avatar(final Point3D location) {
65 setLocation(location);
68 public Avatar(final Point3D location, final float angleXZ,
69 final float angleYZ) {
70 setLocation(location);
77 public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
79 final Point3D locationBeforeUpdate = new Point3D(location);
80 translateAvatarLocationBasedOnMovementVector(millisecondsSinceLastFrame);
81 applyFrictionToUserMovement(millisecondsSinceLastFrame);
82 return isFrameRepaintNeeded(locationBeforeUpdate);
85 private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
86 final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
87 return distanceMoved > 0.03;
90 public void enforceSpeedLimit() {
91 final double currentSpeed = movementVector.getVectorLength();
93 if (currentSpeed <= SPEED_LIMIT)
96 movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
99 public double getAngleXZ() {
100 return orientationXZ;
103 public void setAngleXZ(final double angleXZ) {
104 orientationXZ = angleXZ;
107 public double getAngleYZ() {
108 return orientationYZ;
111 public void setAngleYZ(final double angleYZ) {
112 orientationYZ = angleYZ;
115 public Point3D getLocation() {
119 public void setLocation(final Point3D location) {
120 this.location = location;
123 public Point3D getMovementVector() {
124 return movementVector;
127 public double getMovementSpeed() {
128 return movementVector.getVectorLength();
132 * Apply friction to avatar movement vector.
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.
138 private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
139 for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
140 applyMillisecondFrictionToUserMovementVector();
144 * Apply friction to avatar movement vector.
146 private void applyMillisecondFrictionToUserMovementVector() {
147 getMovementVector().x /= MILLISECOND_FRICTION;
148 getMovementVector().y /= MILLISECOND_FRICTION;
149 getMovementVector().z /= MILLISECOND_FRICTION;
153 * Translate coordinates based on avatar movement vector and avatar orientation in the world.
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.
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;
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;
174 location.y += getMovementVector().y * SPEED_MULTIPLIER
175 * millisecondsPassedSinceLastFrame;