Skip to content

Commit fb44666

Browse files
Copilotriccardobl
andcommitted
Refactor collision detection: CameraCollider interface + SceneCameraCollider
Agent-Logs-Url: https://github.com/jMonkeyEngine/jmonkeyengine/sessions/0532228e-79c6-460d-b67d-1b8d6f455024 Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com>
1 parent 45746c9 commit fb44666

3 files changed

Lines changed: 339 additions & 101 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2009-2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.input;
33+
34+
import com.jme3.math.Vector3f;
35+
36+
/**
37+
* Interface for camera collision detection used by {@link ChaseCamera}.
38+
* Implementations determine how the chase camera interacts with the scene
39+
* when there is geometry between the target and the camera.
40+
*
41+
* <p>A {@code CameraCollider} receives the target's world position and the
42+
* desired camera position, and may adjust the camera position in-place to
43+
* prevent it from clipping through geometry.</p>
44+
*
45+
* <p>The built-in implementation is {@link SceneCameraCollider}, which uses
46+
* ray-casting against one or more scene {@link com.jme3.scene.Node Nodes}.
47+
* Custom implementations can integrate with physics engines or any other
48+
* collision system.</p>
49+
*
50+
* @see ChaseCamera#setCameraCollider(CameraCollider)
51+
* @see SceneCameraCollider
52+
*/
53+
public interface CameraCollider {
54+
55+
/**
56+
* Adjusts the camera position to avoid passing through scene geometry.
57+
*
58+
* <p>Implementations should test for obstructions between
59+
* {@code targetPosition} and {@code camPosition} and, if any are found,
60+
* update {@code camPosition} in-place so that the camera stays in front of
61+
* the obstruction.</p>
62+
*
63+
* @param targetPosition the world position of the chase camera's target
64+
* (read-only)
65+
* @param camPosition the desired camera position before collision
66+
* adjustment; updated in-place with the adjusted
67+
* position if a collision is detected
68+
*/
69+
void collide(Vector3f targetPosition, Vector3f camPosition);
70+
}

jme3-core/src/main/java/com/jme3/input/ChaseCamera.java

Lines changed: 22 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,15 @@
3131
*/
3232
package com.jme3.input;
3333

34-
import com.jme3.collision.CollisionResult;
35-
import com.jme3.collision.CollisionResults;
3634
import com.jme3.export.InputCapsule;
3735
import com.jme3.export.JmeExporter;
3836
import com.jme3.export.JmeImporter;
3937
import com.jme3.input.controls.*;
4038
import com.jme3.math.FastMath;
41-
import com.jme3.math.Ray;
4239
import com.jme3.math.Vector3f;
4340
import com.jme3.renderer.Camera;
4441
import com.jme3.renderer.RenderManager;
4542
import com.jme3.renderer.ViewPort;
46-
import com.jme3.scene.Node;
4743
import com.jme3.scene.Spatial;
4844
import com.jme3.scene.control.Control;
4945
import com.jme3.util.clone.Cloner;
@@ -143,11 +139,7 @@ public class ChaseCamera implements ActionListener, AnalogListener, Control, Jme
143139

144140
protected boolean zoomin;
145141
protected boolean hideCursorOnRotate = true;
146-
protected boolean checkCollision = false;
147-
protected Node collisionNode = null;
148-
protected float collisionMinDistance = 0.1f;
149-
private final CollisionResults collisionResults = new CollisionResults();
150-
private final Ray collisionRay = new Ray();
142+
protected CameraCollider cameraCollider = null;
151143

152144
/**
153145
* Constructs the chase camera
@@ -532,8 +524,8 @@ protected void updateCamera(float tpf) {
532524
//computing the position
533525
computePosition();
534526
//checking for collision with the environment
535-
if (checkCollision && collisionNode != null) {
536-
checkCameraCollision();
527+
if (cameraCollider != null) {
528+
cameraCollider.collide(target.getWorldTranslation(), pos);
537529
}
538530
//setting the position at last
539531
cam.setLocation(pos.addLocal(lookAtOffset));
@@ -544,8 +536,8 @@ protected void updateCamera(float tpf) {
544536
distance = targetDistance;
545537
computePosition();
546538
//checking for collision with the environment
547-
if (checkCollision && collisionNode != null) {
548-
checkCameraCollision();
539+
if (cameraCollider != null) {
540+
cameraCollider.collide(target.getWorldTranslation(), pos);
549541
}
550542
cam.setLocation(pos.addLocal(lookAtOffset));
551543
}
@@ -1024,102 +1016,31 @@ public Vector3f getUpVector() {
10241016
}
10251017

10261018
/**
1027-
* Checks for collisions between the camera and the collision node.
1028-
* If a collision is detected along the ray from the target to the
1029-
* computed camera position, the camera is moved to
1030-
* {@code collisionMinDistance} units in front of the collision point
1031-
* to prevent clipping through geometry.
1032-
*/
1033-
protected void checkCameraCollision() {
1034-
Vector3f targetPos = target.getWorldTranslation();
1035-
// Direction from target to computed camera position
1036-
Vector3f camDir = pos.subtract(targetPos);
1037-
float maxDist = camDir.length();
1038-
if (maxDist < FastMath.ZERO_TOLERANCE) {
1039-
return;
1040-
}
1041-
camDir.normalizeLocal();
1042-
collisionRay.setOrigin(targetPos);
1043-
collisionRay.setDirection(camDir);
1044-
collisionRay.setLimit(maxDist);
1045-
1046-
collisionResults.clear();
1047-
collisionNode.collideWith(collisionRay, collisionResults);
1048-
1049-
if (collisionResults.size() > 0) {
1050-
CollisionResult closest = collisionResults.getClosestCollision();
1051-
float collisionDist = closest.getDistance();
1052-
if (collisionDist < maxDist) {
1053-
// Place camera just in front of the collision point
1054-
float adjustedDist = Math.max(collisionDist - collisionMinDistance, 0);
1055-
pos.set(targetPos).addLocal(camDir.mult(adjustedDist));
1056-
}
1057-
}
1058-
}
1059-
1060-
/**
1061-
* Returns whether camera collision detection with the environment is enabled.
1019+
* Returns the {@link CameraCollider} used to prevent the camera from
1020+
* passing through scene geometry, or {@code null} if collision detection
1021+
* is disabled (the default).
10621022
*
1063-
* @return true if collision detection is enabled, false otherwise (default=false)
1023+
* @return the current camera collider, or {@code null}
10641024
*/
1065-
public boolean isCheckCollision() {
1066-
return checkCollision;
1025+
public CameraCollider getCameraCollider() {
1026+
return cameraCollider;
10671027
}
10681028

10691029
/**
1070-
* Enables or disables camera collision detection with the environment.
1071-
* When enabled, the camera will not pass through geometry in the
1072-
* collision node. This feature is disabled by default.
1030+
* Sets the {@link CameraCollider} that will be used to prevent the camera
1031+
* from passing through scene geometry. Set to {@code null} (the default)
1032+
* to disable collision detection.
10731033
*
1074-
* @param checkCollision true to enable collision detection, false to disable
1075-
* @see #setCollisionNode(Node)
1076-
*/
1077-
public void setCheckCollision(boolean checkCollision) {
1078-
this.checkCollision = checkCollision;
1079-
}
1080-
1081-
/**
1082-
* Returns the node used for camera collision detection.
1083-
*
1084-
* @return the collision node, or null if none is set
1085-
*/
1086-
public Node getCollisionNode() {
1087-
return collisionNode;
1088-
}
1089-
1090-
/**
1091-
* Sets the node to use for camera collision detection.
1092-
* When the chase camera has collision detection enabled, it will cast
1093-
* a ray from the target to the camera position and stop the camera
1094-
* before any geometry in this node.
1095-
*
1096-
* @param collisionNode the node to collide with (alias created), or null to disable
1097-
* @see #setCheckCollision(boolean)
1098-
*/
1099-
public void setCollisionNode(Node collisionNode) {
1100-
this.collisionNode = collisionNode;
1101-
}
1102-
1103-
/**
1104-
* Returns the minimum distance to maintain between the camera and a
1105-
* collision point when collision detection is enabled.
1106-
*
1107-
* @return the minimum distance (in world units, default=0.1)
1108-
*/
1109-
public float getCollisionMinDistance() {
1110-
return collisionMinDistance;
1111-
}
1112-
1113-
/**
1114-
* Sets the minimum distance to maintain between the camera and a
1115-
* collision point when collision detection is enabled. The camera will
1116-
* be placed this far in front of any detected collision.
1034+
* <p>The built-in {@link SceneCameraCollider} supports ray-casting against
1035+
* one or more scene nodes with optional per-geometry exclusion via
1036+
* userData. Custom implementations can integrate with physics engines or
1037+
* any other collision system.</p>
11171038
*
1118-
* @param collisionMinDistance the desired minimum distance (in world units,
1119-
* default=0.1)
1039+
* @param cameraCollider the collider to use, or {@code null} to disable
1040+
* @see SceneCameraCollider
11201041
*/
1121-
public void setCollisionMinDistance(float collisionMinDistance) {
1122-
this.collisionMinDistance = collisionMinDistance;
1042+
public void setCameraCollider(CameraCollider cameraCollider) {
1043+
this.cameraCollider = cameraCollider;
11231044
}
11241045

11251046
public boolean isHideCursorOnRotate() {

0 commit comments

Comments
 (0)