11/*
2- * Copyright (c) 2009-2021 jMonkeyEngine
2+ * Copyright (c) 2009-2025 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
4444import java .io .IOException ;
4545
4646/**
47- * This Control maintains a reference to a Camera,
48- * which will be synched with the position (worldTranslation)
49- * of the current spatial.
47+ * `CameraControl` synchronizes the world transformation (position and rotation)
48+ * of a `Camera` with its attached `Spatial`.
49+ * This control allows a camera to follow a spatial, or a spatial to follow a camera,
50+ * depending on the chosen {@link ControlDirection}.
51+ * <p>
52+ * This is particularly useful for attaching cameras to player characters,
53+ * vehicles, or dynamically controlled objects, ensuring the camera's view
54+ * or the spatial's position/orientation remains synchronized.
55+ * </p>
56+ *
5057 * @author tim
5158 */
5259public class CameraControl extends AbstractControl {
5360
54- public static enum ControlDirection {
55-
61+ public enum ControlDirection {
5662 /**
5763 * Means, that the Camera's transform is "copied"
5864 * to the Transform of the Spatial.
@@ -64,25 +70,31 @@ public static enum ControlDirection {
6470 */
6571 SpatialToCamera ;
6672 }
73+
6774 private Camera camera ;
6875 private ControlDirection controlDir = ControlDirection .SpatialToCamera ;
6976
7077 /**
71- * Constructor used for Serialization .
78+ * For serialization only. Do not use .
7279 */
7380 public CameraControl () {
7481 }
7582
7683 /**
77- * @param camera The Camera to be synced.
84+ * Creates a new `CameraControl` that synchronizes the spatial's transform to the camera.
85+ * The camera will follow the spatial.
86+ *
87+ * @param camera The Camera to be synced. Cannot be null.
7888 */
7989 public CameraControl (Camera camera ) {
8090 this .camera = camera ;
8191 }
8292
8393 /**
84- * @param camera The Camera to be synced.
85- * @param controlDir SpatialToCamera or CameraToSpatial
94+ * Creates a new `CameraControl` with a specified synchronization direction.
95+ *
96+ * @param camera The Camera to be synced. Cannot be null.
97+ * @param controlDir The direction of synchronization (SpatialToCamera or CameraToSpatial).
8698 */
8799 public CameraControl (Camera camera , ControlDirection controlDir ) {
88100 this .camera = camera ;
@@ -105,61 +117,62 @@ public void setControlDir(ControlDirection controlDir) {
105117 this .controlDir = controlDir ;
106118 }
107119
108- // fields used, when inverting ControlDirection:
109120 @ Override
110121 protected void controlUpdate (float tpf ) {
111- if (spatial != null && camera != null ) {
112- switch (controlDir ) {
113- case SpatialToCamera :
114- camera .setLocation (spatial .getWorldTranslation ());
115- camera .setRotation (spatial .getWorldRotation ());
116- break ;
117- case CameraToSpatial :
118- // Set the local transform so that the world transform would be equal to the camera's transform.
119- // Location:
120- TempVars vars = TempVars .get ();
121-
122- Vector3f vecDiff = vars .vect1 .set (camera .getLocation ()).subtractLocal (spatial .getWorldTranslation ());
123- spatial .setLocalTranslation (vecDiff .addLocal (spatial .getLocalTranslation ()));
124-
125- // Rotation:
126- Quaternion worldDiff = vars .quat1 .set (camera .getRotation ()).subtractLocal (spatial .getWorldRotation ());
127- spatial .setLocalRotation (worldDiff .addLocal (spatial .getLocalRotation ()));
128- vars .release ();
129- break ;
130- }
122+ switch (controlDir ) {
123+ case SpatialToCamera :
124+ spatialToCamera ();
125+ break ;
126+ case CameraToSpatial :
127+ cameraToSpatial ();
128+ break ;
131129 }
132130 }
133131
132+ /**
133+ * Updates the camera's position and rotation to match the spatial's
134+ * world transformation. The camera will follow the spatial.
135+ */
136+ private void spatialToCamera () {
137+ camera .setLocation (spatial .getWorldTranslation ());
138+ camera .setRotation (spatial .getWorldRotation ());
139+ }
140+
141+ /**
142+ * Updates the spatial's local transformation (position and rotation)
143+ * such that its world transformation matches the camera's world transformation.
144+ * The spatial will follow the camera.
145+ */
146+ private void cameraToSpatial () {
147+ TempVars vars = TempVars .get ();
148+
149+ Vector3f position = vars .vect1 .set (camera .getLocation ()).subtractLocal (spatial .getWorldTranslation ());
150+ spatial .setLocalTranslation (position .addLocal (spatial .getLocalTranslation ()));
151+
152+ Quaternion rotation = vars .quat1 .set (camera .getRotation ()).subtractLocal (spatial .getWorldRotation ());
153+ spatial .setLocalRotation (rotation .addLocal (spatial .getLocalRotation ()));
154+
155+ vars .release ();
156+ }
157+
134158 @ Override
135159 protected void controlRender (RenderManager rm , ViewPort vp ) {
136160 // nothing to do
137161 }
138162
139- // default implementation from AbstractControl is equivalent
140- //@Override
141- //public Control cloneForSpatial(Spatial newSpatial) {
142- // CameraControl control = new CameraControl(camera, controlDir);
143- // control.setSpatial(newSpatial);
144- // control.setEnabled(isEnabled());
145- // return control;
146- //}
147- private static final String CONTROL_DIR_NAME = "controlDir" ;
148- private static final String CAMERA_NAME = "camera" ;
149-
150163 @ Override
151164 public void read (JmeImporter im ) throws IOException {
152165 super .read (im );
153166 InputCapsule ic = im .getCapsule (this );
154- controlDir = ic .readEnum (CONTROL_DIR_NAME , ControlDirection .class , ControlDirection .SpatialToCamera );
155- camera = (Camera )ic .readSavable (CAMERA_NAME , null );
167+ controlDir = ic .readEnum ("controlDir" , ControlDirection .class , ControlDirection .SpatialToCamera );
168+ camera = (Camera ) ic .readSavable ("camera" , null );
156169 }
157170
158171 @ Override
159172 public void write (JmeExporter ex ) throws IOException {
160173 super .write (ex );
161174 OutputCapsule oc = ex .getCapsule (this );
162- oc .write (controlDir , CONTROL_DIR_NAME , ControlDirection .SpatialToCamera );
163- oc .write (camera , CAMERA_NAME , null );
175+ oc .write (controlDir , "controlDir" , ControlDirection .SpatialToCamera );
176+ oc .write (camera , "camera" , null );
164177 }
165- }
178+ }
0 commit comments