Skip to content

Commit 41ef468

Browse files
Merge pull request #51 from ArtifactForms/working2
Working2
2 parents 710e924 + 07050b6 commit 41ef468

14 files changed

+1969
-1645
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package engine.components;
2+
3+
import engine.input.Input;
4+
import engine.input.Key;
5+
import engine.scene.camera.Camera;
6+
import engine.scene.camera.PerspectiveCamera;
7+
import math.Mathf;
8+
import math.Vector3f;
9+
10+
/**
11+
* The {@code FlyByCameraControl} class provides first-person camera control functionality, allowing
12+
* the user to move the camera in all directions and adjust its orientation using mouse and keyboard
13+
* input. This control simulates a "fly-by" movement, typically used in 3D games or applications
14+
* that require free-form camera navigation.
15+
*
16+
* <p>The movement is controlled by the following keys:
17+
*
18+
* <ul>
19+
* <li>W: Move forward
20+
* <li>S: Move backward
21+
* <li>A: Move left (strafe)
22+
* <li>D: Move right (strafe)
23+
* <li>SPACE: Move up
24+
* <li>SHIFT: Move down
25+
* </ul>
26+
*
27+
* <p>The mouse controls the camera's yaw (left-right) and pitch (up-down) based on the mouse
28+
* movement, with vertical angle limits set for pitch to prevent excessive rotations.
29+
*/
30+
public class FlyByCameraControl extends AbstractComponent {
31+
32+
private static final float DEFAULT_MOUSE_SENSITIVITY = 10f;
33+
34+
private static final float DEFAULT_MOVE_SPEED = 30f;
35+
36+
private static final float MAX_VERTICAL_ANGLE = 80f;
37+
38+
private static final float MIN_VERTICAL_ANGLE = -80f;
39+
40+
private final Vector3f forward = new Vector3f();
41+
42+
private final Vector3f target = new Vector3f();
43+
44+
private float mouseSensitivity = DEFAULT_MOUSE_SENSITIVITY;
45+
46+
private float moveSpeed = DEFAULT_MOVE_SPEED;
47+
48+
private Input input;
49+
50+
private Camera camera;
51+
52+
/**
53+
* Constructs a new {@code FlyByCameraControl} with the specified input and camera.
54+
*
55+
* @param input The {@link Input} instance to capture user input.
56+
* @param camera The {@link PerspectiveCamera} to control.
57+
* @throws IllegalArgumentException if either {@code input} or {@code camera} is {@code null}.
58+
*/
59+
public FlyByCameraControl(Input input, PerspectiveCamera camera) {
60+
if (input == null || camera == null) {
61+
throw new IllegalArgumentException("Input and Camera cannot be null.");
62+
}
63+
this.input = input;
64+
this.camera = camera;
65+
}
66+
67+
/**
68+
* Updates the camera control state, handling mouse input for camera rotation and keyboard input
69+
* for movement.
70+
*
71+
* <p>This method is typically called once per frame.
72+
*
73+
* @param tpf The time per frame (delta time) used for movement scaling.
74+
*/
75+
@Override
76+
public void update(float tpf) {
77+
float mouseX = input.getMouseDeltaX() * mouseSensitivity * tpf;
78+
float mouseY = input.getMouseDeltaY() * mouseSensitivity * tpf;
79+
80+
handleRotation(mouseX, mouseY);
81+
updateTarget();
82+
83+
Vector3f velocity = calculateVelocity();
84+
if (velocity.length() > 0) {
85+
applyMovement(velocity, tpf);
86+
}
87+
88+
input.center();
89+
}
90+
91+
/**
92+
* Handles camera rotation based on mouse input, applying yaw and pitch to the camera's transform.
93+
* The vertical rotation (pitch) is clamped within defined limits to prevent excessive rotation.
94+
*
95+
* @param mouseX The mouse movement on the X-axis (horizontal movement).
96+
* @param mouseY The mouse movement on the Y-axis (vertical movement).
97+
*/
98+
private void handleRotation(float mouseX, float mouseY) {
99+
// Handle yaw (left-right rotation)
100+
float yaw = mouseX;
101+
yaw = Mathf.clamp(yaw, -180f, 180f);
102+
camera.getTransform().rotate(0, Mathf.toRadians(yaw), 0);
103+
104+
// Handle pitch (up-down rotation)
105+
Vector3f rotation = camera.getTransform().getRotation();
106+
float currentPitch = rotation.x;
107+
currentPitch += Mathf.toRadians(mouseY);
108+
currentPitch =
109+
Mathf.clamp(
110+
currentPitch, Mathf.toRadians(MIN_VERTICAL_ANGLE), Mathf.toRadians(MAX_VERTICAL_ANGLE));
111+
112+
rotation.x = currentPitch;
113+
camera.getTransform().setRotation(rotation);
114+
}
115+
116+
/**
117+
* Calculates the movement velocity vector based on the current keyboard input. The velocity is a
118+
* vector that indicates the desired direction of movement.
119+
*
120+
* @return A {@link Vector3f} representing the movement velocity.
121+
*/
122+
private Vector3f calculateVelocity() {
123+
Vector3f velocity = new Vector3f();
124+
Vector3f forward = camera.getTransform().getForward();
125+
Vector3f right = camera.getTransform().getRight();
126+
127+
// Movement controls (WASD, Space, Shift)
128+
if (input.isKeyPressed(Key.W)) velocity.addLocal(forward);
129+
if (input.isKeyPressed(Key.S)) velocity.addLocal(forward.negate());
130+
if (input.isKeyPressed(Key.A)) velocity.addLocal(right.negate());
131+
if (input.isKeyPressed(Key.D)) velocity.addLocal(right);
132+
if (input.isKeyPressed(Key.SPACE)) velocity.addLocal(0, -1, 0);
133+
if (input.isKeyPressed(Key.SHIFT)) velocity.addLocal(0, 1, 0);
134+
135+
return velocity;
136+
}
137+
138+
/**
139+
* Applies the calculated velocity to the camera's position, updating the camera's location based
140+
* on user movement input.
141+
*
142+
* @param velocity The movement velocity to apply.
143+
* @param tpf The time per frame (delta time) to scale the movement.
144+
*/
145+
private void applyMovement(Vector3f velocity, float tpf) {
146+
velocity.normalizeLocal();
147+
Vector3f position = camera.getTransform().getPosition();
148+
position.addLocal(velocity.mult(moveSpeed * tpf));
149+
camera.getTransform().setPosition(position);
150+
updateTarget();
151+
}
152+
153+
/**
154+
* Updates the target position for the camera. The target is a point in the scene that the camera
155+
* looks at, and it is updated based on the camera's current position and rotation.
156+
*/
157+
private void updateTarget() {
158+
Vector3f position = camera.getTransform().getPosition();
159+
Vector3f rotation = camera.getTransform().getRotation();
160+
161+
forward
162+
.set(
163+
Mathf.cos(rotation.y) * Mathf.cos(rotation.x),
164+
Mathf.sin(rotation.x),
165+
Mathf.sin(rotation.y) * Mathf.cos(rotation.x))
166+
.normalizeLocal();
167+
168+
target.set(position).addLocal(forward);
169+
camera.setTarget(target);
170+
}
171+
172+
/** This method is called when the component is attached to an entity. Currently not used. */
173+
@Override
174+
public void onAttach() {
175+
// Not used yet
176+
}
177+
178+
/** This method is called when the component is detached from an entity. Currently not used. */
179+
@Override
180+
public void onDetach() {
181+
// Not used yet
182+
}
183+
}

src/main/java/engine/debug/DebugInfoUpdater.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import engine.scene.Scene;
1212
import engine.scene.camera.Camera;
1313
import math.Mathf;
14+
import workspace.GraphicsPImpl;
1415

1516
/**
1617
* The {@code DebugInfoUpdater} class is responsible for updating debug information displayed by a
@@ -101,8 +102,8 @@ private void updateCameraInfo(Camera camera) {
101102
if (camera == null) return;
102103
setInfo(CATEGORY_CAMERA, "Aspect", camera.getAspectRatio());
103104
setInfo(CATEGORY_CAMERA, "FOV", Mathf.toDegrees(camera.getFieldOfView()));
104-
setInfo(CATEGORY_CAMERA, "Near", Mathf.toDegrees(camera.getNearPlane()));
105-
setInfo(CATEGORY_CAMERA, "Far", Mathf.toDegrees(camera.getFarPlane()));
105+
setInfo(CATEGORY_CAMERA, "Near", camera.getNearPlane());
106+
setInfo(CATEGORY_CAMERA, "Far", camera.getFarPlane());
106107
}
107108

108109
private void updateOsMetrics() {
@@ -122,6 +123,8 @@ private void updateSceneMetrics(Scene activeScene) {
122123
setInfo(CATEGORY_SCENE, "Root count", activeScene.getRootCount());
123124
setInfo(CATEGORY_SCENE, "Lights count", activeScene.getLightCount());
124125
setInfo(CATEGORY_SCENE, "Wireframe mode", activeScene.isWireframeMode());
126+
setInfo(CATEGORY_SCENE, "Faces", GraphicsPImpl.faceCount);
127+
setInfo(CATEGORY_SCENE, "Vertices", GraphicsPImpl.vertexCount);
125128
}
126129

127130
private void updateTimeMetrics(Timer timer) {

src/main/java/engine/input/MouseInput.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ public interface MouseInput {
2525
float getMouseDeltaY();
2626

2727
void updateMouseState(); // To track mouse-specific states
28+
29+
void center();
2830
}

src/main/java/engine/processing/ProcessingApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import engine.input.Input;
66
import engine.input.KeyInput;
77
import engine.input.MouseInput;
8+
import engine.resources.ResourceManager;
89
import processing.core.PApplet;
910
import workspace.GraphicsPImpl;
1011
import workspace.ui.Graphics;
@@ -29,6 +30,7 @@ public void settings() {
2930
@Override
3031
public void setup() {
3132
Graphics g = new GraphicsPImpl(this);
33+
ResourceManager.getInstance().setImageLoader(new ProcessingImageLoader(this));
3234
container.setGraphics(g);
3335
getSurface().setTitle(settings.getTitle());
3436
setupInput();

src/main/java/engine/processing/ProcessingInput.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public void updateMouseState() {
9393
mouseInput.updateMouseState();
9494
}
9595

96+
@Override
97+
public void center() {
98+
mouseInput.center();
99+
}
100+
96101
@Override
97102
public void update() {
98103
updateKeyState();

src/main/java/engine/processing/ProcessingMouseInput.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package engine.processing;
22

3+
import java.awt.Robot;
4+
35
import engine.input.MouseInput;
46
import processing.core.PApplet;
57

@@ -9,9 +11,15 @@ public class ProcessingMouseInput implements MouseInput {
911

1012
private float mouseWheelDelta = 0;
1113

14+
private Robot robot;
15+
1216
public ProcessingMouseInput(PApplet applet) {
1317
this.applet = applet;
1418
applet.registerMethod("mouseEvent", this);
19+
try {
20+
robot = new Robot();
21+
} catch (Throwable e) {
22+
}
1523
}
1624

1725
public void mouseEvent(processing.event.MouseEvent event) {
@@ -79,7 +87,14 @@ public float getMouseWheelDelta() {
7987
}
8088

8189
@Override
82-
public void updateMouseState() {
83-
// Handle frame-specific mouse state updates if necessary
90+
public void updateMouseState() {}
91+
92+
@Override
93+
public void center() {
94+
applet.mouseX = applet.width / 2;
95+
applet.mouseY = applet.height / 2;
96+
applet.pmouseX = applet.width / 2;
97+
applet.pmouseY = applet.height / 2;
98+
robot.mouseMove((int) getScreenWidth() / 2, (int) getScreenHeight() / 2);
8499
}
85100
}

0 commit comments

Comments
 (0)