diff --git a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java index afdeb2c086..542a179097 100644 --- a/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java +++ b/jme3-core/src/main/java/com/jme3/input/FlyByCamera.java @@ -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); } /** diff --git a/jme3-core/src/main/java/com/jme3/renderer/Camera.java b/jme3-core/src/main/java/com/jme3/renderer/Camera.java index 6201a0d1f8..80ee35d5c4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Camera.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Camera.java @@ -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; + } + /** * getLocation retrieves the location vector of the camera. * diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestAspectFov.java b/jme3-examples/src/main/java/jme3test/renderer/TestAspectFov.java new file mode 100644 index 0000000000..50a2377bef --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/renderer/TestAspectFov.java @@ -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); + } + } +}