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 public class Avatar implements ViewRenderListener {
18 * Avatar movement speed, relative to the world. When avatar coordinates are
19 * updated within the world, avatar orientation relative to the world is
22 public static final double SPEED_LIMIT = 30;
24 * Just in case we want to adjust global speed for some reason.
26 private static final double SPEED_MULTIPLIER = .02d;
28 * Determines amount of friction user experiences every millisecond while moving around in space.
30 private static final double MILLISECOND_FRICTION = 1.005;
32 * Avatar movement speed, relative to avatar itself. When avatar coordinates
33 * are updated within the world, avatar orientation relative to the world is
36 private final Point3D movementVector = new Point3D();
37 public double avatarAcceleration = 0.1;
39 * Avatar location within the 3D world.
41 private Point3D location = new Point3D();
43 * Avatar orientation on the X-Z plane. It changes when turning left or
46 private double orientationXZ;
48 * Avatar orientation on the Y-Z plane. It changes when looking up or down.
50 private double orientationYZ;
55 public Avatar(final Avatar sourceView) {
56 setLocation(new Point3D(sourceView.location));
57 setAngleXZ(sourceView.getAngleXZ());
58 setAngleYZ(sourceView.getAngleYZ());
61 public Avatar(final Point3D location) {
62 setLocation(location);
65 public Avatar(final Point3D location, final float angleXZ,
66 final float angleYZ) {
67 setLocation(location);
74 public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
76 final Point3D locationBeforeUpdate = new Point3D(location);
77 translateAvatarLocationBasedOnMovementVector(millisecondsSinceLastFrame);
78 applyFrictionToUserMovement(millisecondsSinceLastFrame);
79 return isFrameRepaintNeeded(locationBeforeUpdate);
82 private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
83 final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
84 return distanceMoved > 0.03;
87 public void enforceSpeedLimit() {
88 final double currentSpeed = movementVector.getVectorLength();
90 if (currentSpeed <= SPEED_LIMIT)
93 movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
96 public double getAngleXZ() {
100 public void setAngleXZ(final double angleXZ) {
101 orientationXZ = angleXZ;
104 public double getAngleYZ() {
105 return orientationYZ;
108 public void setAngleYZ(final double angleYZ) {
109 orientationYZ = angleYZ;
112 public Point3D getLocation() {
116 public void setLocation(final Point3D location) {
117 this.location = location;
120 public Point3D getMovementVector() {
121 return movementVector;
124 public double getMovementSpeed() {
125 return movementVector.getVectorLength();
129 * Apply friction to avatar movement vector.
131 * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
132 * Therefore, we take frame rendering time into account when translating
133 * avatar between consecutive frames.
135 private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
136 for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
137 applyMillisecondFrictionToUserMovementVector();
141 * Apply friction to avatar movement vector.
143 private void applyMillisecondFrictionToUserMovementVector() {
144 getMovementVector().x /= MILLISECOND_FRICTION;
145 getMovementVector().y /= MILLISECOND_FRICTION;
146 getMovementVector().z /= MILLISECOND_FRICTION;
150 * Translate coordinates based on avatar movement vector and avatar orientation in the world.
152 * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
153 * Therefore, we take frame rendering time into account when translating
154 * avatar between consecutive frames.
156 private void translateAvatarLocationBasedOnMovementVector(int millisecondsPassedSinceLastFrame) {
157 location.x -= (float) sin(getAngleXZ())
158 * getMovementVector().z * SPEED_MULTIPLIER
159 * millisecondsPassedSinceLastFrame;
160 location.z += (float) cos(getAngleXZ())
161 * getMovementVector().z * SPEED_MULTIPLIER
162 * millisecondsPassedSinceLastFrame;
164 location.x += (float) cos(getAngleXZ())
165 * getMovementVector().x * SPEED_MULTIPLIER
166 * millisecondsPassedSinceLastFrame;
167 location.z += (float) sin(getAngleXZ())
168 * getMovementVector().x * SPEED_MULTIPLIER
169 * millisecondsPassedSinceLastFrame;
171 location.y += getMovementVector().y * SPEED_MULTIPLIER
172 * millisecondsPassedSinceLastFrame;