Skip to content
24 changes: 3 additions & 21 deletions jme3-core/src/main/java/com/jme3/input/FlyByCamera.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,28 +382,10 @@ protected void rotateCamera(float value, Vector3f axis){
* @param value zoom amount
*/
protected void zoomCamera(float value){
// derive fovY value
float h = cam.getFrustumTop();
float w = cam.getFrustumRight();
float aspect = w / h;

float near = cam.getFrustumNear();

float fovY = FastMath.atan(h / near)
/ (FastMath.DEG_TO_RAD * .5f);
float newFovY = fovY + value * 0.1f * zoomSpeed;
if (newFovY > 0f) {
// Don't let the FOV go zero or negative.
fovY = newFovY;
float newFov = cam.getFov() + value * 0.1F * zoomSpeed;
if (newFov > 0) {
cam.setFov(newFov);
}

h = FastMath.tan( fovY * FastMath.DEG_TO_RAD * .5f) * near;
w = h * aspect;

cam.setFrustumTop(h);
cam.setFrustumBottom(-h);
cam.setFrustumLeft(-w);
cam.setFrustumRight(w);
}

/**
Expand Down
48 changes: 48 additions & 0 deletions jme3-core/src/main/java/com/jme3/renderer/Camera.java
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,54 @@ public void setFrustumTop(float frustumTop) {
onFrustumChange();
}

/**
* Obtains field of view when the camera is in perspective mode.
*
* @return Frame of view angle along the Y in degrees, or 0 if the camera is in orthogonal mode.
*/
public float getFov() {
if (!this.parallelProjection) {
float fovY = frustumTop / frustumNear;
fovY = FastMath.atan(fovY);
fovY /= 0.5F * FastMath.DEG_TO_RAD;
return fovY;
}
return 0;
}

/**
* Sets the field of view when the camera is in perspective mode. Note that this method has no
* effect when the camera is in orthogonal mode.
*
* @param fovY Frame of view angle along the Y in degrees. This must be greater than 0.
*/
public void setFov(float fovY) {
if (fovY <= 0) {
throw new IllegalArgumentException("Field of view must be greater than 0");
}
if (this.parallelProjection) {
throw new IllegalArgumentException("Cannot set field of view on orthogonal camera");
}
float h = FastMath.tan(fovY * FastMath.DEG_TO_RAD * .5f) * frustumNear;
float w = h * getAspect();
frustumLeft = -w;
frustumRight = w;
frustumBottom = -h;
frustumTop = h;
onFrustumChange();
}

/**
* Obtains the aspect ratio.
*
* @return Width:Height ratio.
*/
public float getAspect() {
float h = height * (viewPortTop - viewPortBottom);
float w = width * (viewPortRight - viewPortLeft);
return w / h;
}

/**
* <code>getLocation</code> retrieves the location vector of the camera.
*
Expand Down
126 changes: 126 additions & 0 deletions jme3-examples/src/main/java/jme3test/renderer/TestAspectFov.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.renderer;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;

/**
* Tests the setting of the FOV and aspect ratios.
*
* @author Markil 3
*/
public class TestAspectFov extends SimpleApplication implements AnalogListener {
private static final String FOV_IN = "fovIn";
private static final String FOV_OUT = "fovOut";
private BitmapText header, fov;

public static void main(String[] args) {
new TestAspectFov().start();
}

@Override
public void simpleInitApp() {
header = new BitmapText(this.guiFont);
header.setText("Adjust FOV with R/F or with mouse scroll");
guiNode.attachChild(header);
fov = new BitmapText(this.guiFont);
guiNode.attachChild(fov);

viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
flyCam.setMoveSpeed(100);

// We add light so we see the scene
AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(1.3f));
rootNode.addLight(al);

DirectionalLight dl = new DirectionalLight();
dl.setColor(ColorRGBA.White);
dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
rootNode.addLight(dl);

assetManager.registerLocator("https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/jmonkeyengine/town.zip",
HttpZipLocator.class);
Spatial sceneModel = assetManager.loadModel("main.scene");
sceneModel.setLocalScale(2f);

rootNode.attachChild(sceneModel);

inputManager.addMapping(FOV_IN, new KeyTrigger(KeyInput.KEY_R));
inputManager.addMapping(FOV_OUT, new KeyTrigger(KeyInput.KEY_F));
inputManager.addListener(this, FOV_IN, FOV_OUT);
}

@Override
public void update() {
/*
* Updates the labels
*/
TempVars vars = TempVars.get();
super.update();
header.setLocalTranslation(0, cam.getHeight(), 0);
vars.vect1.set(header.getLocalTranslation());
vars.vect1.subtractLocal(0, header.getLineHeight(), 0);
fov.setLocalTranslation(vars.vect1);
fov.setText("FOV: " + cam.getFov());
vars.vect1.subtractLocal(0, fov.getLineHeight(), 0);
vars.release();
}

@Override
public void onAnalog(String name, float value, float tpf) {
final float CHANGE_VALUE = tpf * 10;
float newFov = cam.getFov();
switch (name) {
case FOV_IN:
newFov -= CHANGE_VALUE;
break;
case FOV_OUT:
newFov += CHANGE_VALUE;
break;
}
if (newFov > 0 && newFov != cam.getFov()) {
cam.setFov(newFov);
}
}
}