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;
12 public class Avatar implements ViewRenderListener {
14 public static final double SPEED_LIMIT = 30;
16 * Just in case we want to adjust global speed for some reason.
18 private static final double SPEED_MULTIPLIER = .02d;
20 * Determines amount of friction user experiences every millisecond while moving around in space.
22 private static final double MILLISECOND_FRICTION = 1.005;
24 * Avatar movement speed, relative to avatar itself. When avatar coordinates
25 * are updated within the world, avatar orientation relative to the world is
28 private final Point3D movementVector = new Point3D();
29 public double avatarAcceleration = 0.1;
31 * Avatar location within the 3D world.
33 private Point3D location = new Point3D();
35 * Avatar orientation on the X-Z plane. It changes when turning left or
38 private double orientationXZ;
40 * Avatar orientation on the Y-Z plane. It changes when looking up or down.
42 private double orientationYZ;
47 public Avatar(final Avatar sourceView) {
48 setLocation(new Point3D(sourceView.location));
49 setAngleXZ(sourceView.getAngleXZ());
50 setAngleYZ(sourceView.getAngleYZ());
53 public Avatar(final Point3D location) {
54 setLocation(location);
57 public Avatar(final Point3D location, final float angleXZ,
58 final float angleYZ) {
59 setLocation(location);
65 public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
67 final Point3D locationBeforeUpdate = new Point3D(location);
68 translateAvatarLocationBasedOnMovementVector(millisecondsSinceLastFrame);
69 applyFrictionToUserMovement(millisecondsSinceLastFrame);
70 return isFrameRepaintNeeded(locationBeforeUpdate);
73 private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
74 final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
75 return distanceMoved > 0.03;
78 public void enforceSpeedLimit() {
79 final double currentSpeed = movementVector.getVectorLength();
81 if (currentSpeed <= SPEED_LIMIT)
84 movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
87 public double getAngleXZ() {
91 public void setAngleXZ(final double angleXZ) {
92 orientationXZ = angleXZ;
95 public double getAngleYZ() {
99 public void setAngleYZ(final double angleYZ) {
100 orientationYZ = angleYZ;
103 public Point3D getLocation() {
107 public void setLocation(final Point3D location) {
108 this.location = location;
111 public Point3D getMovementVector() {
112 return movementVector;
115 public double getMovementSpeed() {
116 return movementVector.getVectorLength();
119 private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
120 for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
121 applyMillisecondFrictionToUserMovementVector();
124 private void applyMillisecondFrictionToUserMovementVector() {
125 getMovementVector().x /= MILLISECOND_FRICTION;
126 getMovementVector().y /= MILLISECOND_FRICTION;
127 getMovementVector().z /= MILLISECOND_FRICTION;
131 * Translate coordinates based on avatar movement vector and avatar orientation in the world.
133 * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
134 * Therefore we take frame rendering time into account when translating
135 * avatar between consecutive frames.
137 private void translateAvatarLocationBasedOnMovementVector(int millisecondsPassedSinceLastFrame) {
138 location.x -= (float) sin(getAngleXZ())
139 * getMovementVector().z * SPEED_MULTIPLIER
140 * millisecondsPassedSinceLastFrame;
141 location.z += (float) cos(getAngleXZ())
142 * getMovementVector().z * SPEED_MULTIPLIER
143 * millisecondsPassedSinceLastFrame;
145 location.x += (float) cos(getAngleXZ())
146 * getMovementVector().x * SPEED_MULTIPLIER
147 * millisecondsPassedSinceLastFrame;
148 location.z += (float) sin(getAngleXZ())
149 * getMovementVector().x * SPEED_MULTIPLIER
150 * millisecondsPassedSinceLastFrame;
152 location.y += getMovementVector().y * SPEED_MULTIPLIER
153 * millisecondsPassedSinceLastFrame;