@pspeed
I’m not really detecting the 0.75, that was an estimate. It looks to me that it gets larger the larger the camera angles gets. When my FOVX was 45…the lost view was about 0.75, when the FOVX was 90…the lost angle was almost 9 degrees.
I can post my code…it just might be too much to go through… but lets give it a try…
This is a multipass camera setup with multiple camera offsets. The multipass works fine…the offsets do not on the other hand.
The cameras all have internal nodes, which are attached to each other in an hierarchy with offsets. Main node is the one that is moved with the main camera, and the other cameras attach their nodes to the main node and then update their position according to the changes in the internal node every frame.
This is the camera controller:
[java]
/**
-
This class controls and syncs multiple cameras together to work in unison.
-
It is time sensitive when this class can be initialized. It has to be done after
-
the application is initialized but before any 3D contend is loaded.
-
-
@author serebrennik
*/
public class SpanCameraSystemState extends StateBase
{
/**This needs to be overridden in the inheriting class */
public static final String STATE_NAME = “CameraSystemState”;
private MavreAppBase mApp = null;
private RenderManager mRenderManager = null;
private AppStateManager mStateManager = null;
private Node mRootNode = null;
private String mLoadedProfile = “1_0_45.asc”;
private int mNumOfCameras = 1;
private float mCameraOffset[] = null;
protected ArrayList<DistanceCameraSystem> mCamArray = new ArrayList();
protected CameraType mCameraType = null;
public SpanCameraSystemState(MavreAppBase app, CameraType cameraType, int numOfCameras)
{
/*Define our member Variables/
mApp = app;
mRenderManager = mApp.getRenderManager();
mStateManager = mApp.getStateManager();
mRootNode = mApp.getRootNode();
mCameraType = cameraType;
mNumOfCameras = numOfCameras;
mCameraOffset = new float[mNumOfCameras];
/**Redefines some systems, cameras, viewports, etc...*/
redefineInitSystems();
}
/**
- Returns the name of this State
-
@return
*/
@Override
public String getName()
{
return STATE_NAME;
}
@Override
protected void initializeState(AppStateManager stateManager, MavreApplication app)
{
}
/**
- Redefines and deletes some systems that were created by
- SimpleApplication and replaces them with our own.
*/
private void redefineInitSystems()
{
reconfigureCameras();
reconfigureDefaultInput();
}
/**
-
Deletes the initial default Camera and View port and
-
implements its own
*/
public void reconfigureCameras()
{
try
{
/**Remove the Main viewport*/
//renderManager.getRenderer().deleteFrameBuffer(renderManager.getMainView("Default").getOutputFrameBuffer());
mRenderManager.getMainView("Default").clearScenes();
mRenderManager.removeMainView(mRenderManager.getMainView("Default"));
mApp.setViewPort(null);
mApp.setCamera(null);
/**Create our cameras*/
createCameras();
}
catch(Exception e)
{
Log.getInstance().severe(Log.getInstance().generateError(e));
}
}
/**
-
Create our cameras, defining the number, layout and field of view
-
@param numOfCameras
*/
private void createCameras()
{
/*Load our camera profile, default values are main screen = 1, getCamSeparationAngle = 0, getFOVX=90 or 45/
CameraProfile cProf = loadCameraProfile(mLoadedProfile);
/*Set our window in the correct monitor/
setWindowPosition(cProf);
/*Create all our cameras/
for(int i = 0; i < mNumOfCameras; i++)
{
mCamArray.add(new DistanceCameraSystem(mApp.getSettings().getWidth()/mNumOfCameras, mApp.getSettings().getHeight(), mCameraType, mRenderManager));
mCamArray.get(i).setFrustumPerspective(cProf.getFOVX()/((float)mCamArray.get(i).getWidth() / (float)mCamArray.get(i).getHeight()),
(float)mCamArray.get(i).getWidth() / (float)mCamArray.get(i).getHeight());
mCamArray.get(i).setViewPort(getCamLB(i+1), getCamRB(i+1), 0f, 1f);
mCamArray.get(i).attachScene(mRootNode);
if(mNumOfCameras == 1 || (i+1) == cProf.getMainScreenNum())
{
mApp.setCamera(mCamArray.get(i));
mApp.setViewPort(mCamArray.get(i).getMainViewPort());
}
}
/*Generate camera offsets/
generateCameraOffsets(cProf);
}
/**
- Calculates and sets the correct Y position for the main window
-
@param cameraProf
/
void setWindowPosition(CameraProfile cameraProf)
{
if(mNumOfCameras == 1)
{
mApp.getDisplayUtil().setWindowPositionOffset(0);
}
else
{
mApp.getDisplayUtil().setWindowPositionOffset((1-cameraProf.getMainScreenNum())(mApp.getSettings().getWidth()/mNumOfCameras));
}
}
/**
-
Generates the camera offsets for all the possible cameras
-
and links the cameras together. Offsets are stored in mCameraOffset
-
@param cameraProf
*/
void generateCameraOffsets(CameraProfile cameraProf)
{
float[] angles = {0f,0f,0f};
for(int i = 0-(cameraProf.getMainScreenNum()-1); i < mNumOfCameras-(cameraProf.getMainScreenNum()-1); i++)
{
if(i<0)
{
/*Generate offsets for cameras left of the main camera/
mCameraOffset[i+(cameraProf.getMainScreenNum()-1)] = (((icameraProf.getFOVX())-cameraProf.getCamSeparationAngle()) * FastMath.DEG_TO_RAD);
}
else if(i>0)
{
/*Generate offsets for cameras right of the main camera/
mCameraOffset[i+(cameraProf.getMainScreenNum()-1)] = (((icameraProf.getFOVX())+cameraProf.getCamSeparationAngle()) * FastMath.DEG_TO_RAD);
}
else
{
/*Generate offset for the main camera/
mCameraOffset[i+(cameraProf.getMainScreenNum()-1)] = 0;
}
if(i != 0)
{
/**Attach slave cameras node to main cameras node*/
((DistanceCameraSystem)mApp.getCamera()).getCameraNode().attachChild(mCamArray.get(i+(cameraProf.getMainScreenNum()-1)).getCameraNode());
/**Create an offset for the slave cameras node*/
angles[1] = -1* mCameraOffset[i+(cameraProf.getMainScreenNum()-1)];
mCamArray.get(i+(cameraProf.getMainScreenNum()-1)).getCameraNode().getLocalRotation().fromAngles(angles);
}
else
{
mApp.getRootNode().attachChild(mCamArray.get(i+(cameraProf.getMainScreenNum()-1)).getCameraNode());
}
}
}
/**
- Returns the cameras left viewport boundary
-
@param camNumber camera number starting from the left 1, 2, …
-
@return
*/
private float getCamLB(int camNumber)
{
return ((float)camNumber-1f);
}
/**
- Returns the cameras right viewport boundary
-
@param camNumber camera number starting from the left 1, 2, …
-
@return
*/
private float getCamRB(int camNumber)
{
return ((float)camNumber);
}
/**
- Loads the passed in CameraProfile
-
@param profile
-
@return
*/
private CameraProfile loadCameraProfile(String profile)
{
ArrayList<String> file = (ArrayList<String>) mApp.getAssetManager().loadAsset(“Config/CameraProfile/” + profile);
String[] camProfile = file.get(1).split(" ");
return new CameraProfile(Integer.parseInt(camProfile[0]), Float.parseFloat(camProfile[1]), Float.parseFloat(camProfile[2]));
}
/**
-
Removed the old FlyCam controls and places new ones on top of the existing camera.
/
private void reconfigureDefaultInput()
{
try
{
/*
* Delete the old com.jme3.app.FlyCamAppState state so its
* not confused with the com.metronaviation.mavre.base.app.input.FlyCamAppState state.
*/
if (mStateManager.getState(com.jme3.app.FlyCamAppState.class) != null)
{
com.jme3.app.FlyCamAppState state = mStateManager.getState(com.jme3.app.FlyCamAppState.class);
mStateManager.detach(state);
}
/**Remove the old flyCam as a listener from inputManager*/
mApp.getFlyByCamera().unregisterInput();
/**Create and setup the new flyCam*/
if (mStateManager.getState(FlyCamAppState.class) != null)
{
mApp.SetFlyByCamera(null);
mApp.SetFlyByCamera(new FlyByCamera(mApp.getCamera()));
mApp.getFlyByCamera().setMoveSpeed(1f);
mApp.getFlyByCamera().setZoomSpeed(0f);
mStateManager.getState(FlyCamAppState.class).setCamera( mApp.getFlyByCamera() );
}
}
catch(Exception e)
{
Log.getInstance().severe(Log.getInstance().generateError(e));
}
}
/**
- Updates the position and rotation of the salve cameras
*/
private void updateCameras()
{
/*Update our camera positions/
for (int i=0; i<mNumOfCameras; i++)
{
/*Make sure we avoid changing the main camera/
if(mCamArray.get(i) != mApp.getCamera())
{
/*Set the cameras location/
mCamArray.get(i).updateFromNode();
}
}
}
@Override
public void update(float tpf)
{
super.update(tpf);
/**Update our camera positions*/
updateCameras();
}
@Override
public void onMessage(MessageBase message)
{
//DO NOTHING
}
}
[/java]
This is the camera itself:
[java]
public class DistanceCameraSystem extends Camera
{
public final String MAIN_CAM_NAME = “MainCam” + UUID.randomUUID().toString();
public final String MAIN_VP_NAME = “MainVP” + UUID.randomUUID().toString();
public final String DIST_CAM_NAME = “DistCam” + UUID.randomUUID().toString();
public final String DIST_VP_NAME = “DistVP” + UUID.randomUUID().toString();
private final float MAX_CLIP_RATIO = 10000f;
private final float CLIP_OVERLAP_PERCENT = 0.1f;
private RenderManager mRenderManager = null;
private ViewPort mMainVP = null;
private ViewPort mDistVP = null;
private Camera mDistCam = null;
private Node mCamNode = new Node();
private CameraType mCamType = CameraType.NORMAL;
private boolean mIsAttached = false;
private float mNearCPlane = 0f;
private float mMidCPlane = 0f;
private float mFarCPlane = 0f;
/**
* ctor
*/
public DistanceCameraSystem(CameraType cameraType, RenderManager rm)
{
this(640, 480, cameraType, rm);
}
public DistanceCameraSystem(int width, int height, CameraType cameraType, RenderManager rm)
{
super(width, height);
mCamType = cameraType;
mRenderManager = rm;
setupCameras();
}
/**
* Creates the Systems Cameras and Viewports
*/
private void setupCameras()
{
mNearCPlane = mCamType.getMinRange();
mMidCPlane = computeMidCPlane();
mFarCPlane = mCamType.getMaxRange();
/**Create our cameras*/
this.setName(MAIN_CAM_NAME);
/**Get our initial default view distances*/
this.setFrustumNear(mNearCPlane);
this.setFrustumFar(mFarCPlane);
if(mCamType == CameraType.MULTIPASS_LONGDIST)
{
mDistCam = new Camera(this.width, this.height);
mDistCam.copyFrom(this);
mDistCam.setName(DIST_CAM_NAME);
}
this.attachToRenderManager();
}
/**
* Removes the used views from the render loops, and frees up the resources.
*/
public void detachFromRenderManager()
{
if(mIsAttached)
{
/**
* This method needs to be tested with just using mMainVP and mDistVP instead of going through
* mRenderManager every time, but in the notes it said that for some reason it won't work that way.
*/
if(mDistVP != null)
{
/**Remove the Distance viewport*/
mRenderManager.getPreView(DIST_VP_NAME).clearScenes();
mRenderManager.removePreView(mRenderManager.getPreView(DIST_VP_NAME));
mDistVP = null;
}
if(mMainVP != null)
{
/**Remove the Main viewport*/
mRenderManager.getMainView(MAIN_VP_NAME).clearScenes();
mRenderManager.removeMainView(mRenderManager.getMainView(MAIN_VP_NAME));
mMainVP = null;
}
mIsAttached = false;
}
}
/**
* Creates internal viewports and attaches the cameras to the RenderManager.
* You dont need to call this when creating the camera system, it is called automaticaly, but you
* do need to call this if you manually detached the system from the manager.
* @param renderManager
*/
public void attachToRenderManager()
{
if(mIsAttached)
{
detachFromRenderManager();
}
/**Create our viewports*/
setupViewPorts();
mIsAttached = true;
}
/**
* Sets up our view ports
*/
private void setupViewPorts()
{
if(mCamType == CameraType.MULTIPASS_LONGDIST)
{
mDistVP = mRenderManager.createPreView(DIST_VP_NAME, mDistCam);
mDistVP.setClearFlags(true, true, true);
mMainVP = mRenderManager.createMainView(MAIN_VP_NAME, this);
mMainVP.setBackgroundColor(new ColorRGBA(0.0f, 0f, 0f, 0f));
mMainVP.setClearFlags(false, true, true);
}
else
{
mMainVP = mRenderManager.createMainView(MAIN_VP_NAME, this);
mMainVP.setBackgroundColor(new ColorRGBA(0.0f, 0f, 0f, 0f));
mMainVP.setClearFlags(true, true, true);
}
}
/**
* Recomputed the Middle Clipping plane from MAX_CLIP_RATIO and the near clipping
* plane of the main Camera
*/
private float computeMidCPlane()
{
return mNearCPlane * MAX_CLIP_RATIO;
}
/**
* Computes Distance Cameras near Clipping Plane accroding to CLIP_OVERLAP_PERCENT and mMidCPlane
* @return
*/
private float computeDistCamNearCP()
{
return mMidCPlane - (mMidCPlane * CLIP_OVERLAP_PERCENT);
}
/**
* Returns the Type of the camera this system is being used as. Single or Multipass.
* @return
*/
public CameraType getCameraType()
{
return mCamType;
}
/**
* Attaches the scene to internal viewports
* @param scene
*/
public void attachScene(Spatial scene)
{
mMainVP.attachScene(scene);
if(mDistVP != null)
{
mDistVP.attachScene(scene);
}
}
/**
* Returns the Main Camera of the system
* @return
*/
public Camera getMainCamera()
{
return this;
}
/**
* Returns the Distance Camera of the system
* @return
*/
public Camera getDistCamera()
{
return mDistCam;
}
/**
* Returns the Main View Port
* @return
*/
public ViewPort getMainViewPort()
{
return mMainVP;
}
/**
* Returns the Distance View Port
* @return
*/
public ViewPort getDistViewPort()
{
return mDistVP;
}
/**
* <code>setFrustumPerspective</code> defines the frustum for the camera. This
* frustum is defined by a viewing angle, aspect ratio, and internal near/far planes
*
* @param fovY Frame of view angle along the Y in degrees.
* @param aspect Width:Height ratio
*/
public void setFrustumPerspective(float fovY, float aspect)
{
if(mCamType == CameraType.MULTIPASS_LONGDIST)
{
super.setFrustumPerspective(fovY, aspect, this.mNearCPlane, computeMidCPlane());
mDistCam.setFrustumPerspective(fovY, aspect, computeDistCamNearCP(), this.mFarCPlane);
}
else
{
super.setFrustumPerspective(fovY, aspect, this.mNearCPlane, this.mFarCPlane);
}
}
////
////
////
////
////
////
//Genera Overriden function*//
//****************************************************************************************************//
/**
* <code>setLocation</code> sets the position of the camera.
*
* @param location the position of the camera.
*/
@Override
public void setLocation(Vector3f location)
{
super.setLocation(location);
mCamNode.setLocalTranslation(location);
if(mDistCam != null)
{
mDistCam.setLocation(location);
}
}
/**
* <code>setRotation</code> sets the orientation of this camera.
* This will be equivelant to setting each of the axes:
* <code><br>
* cam.setLeft(rotation.getRotationColumn(0));<br>
* cam.setUp(rotation.getRotationColumn(1));<br>
* cam.setDirection(rotation.getRotationColumn(2));<br>
* </code>
*
* @param rotation the rotation of this camera
*/
@Override
public void setRotation(Quaternion rotation)
{
super.setRotation(rotation);
mCamNode.setLocalRotation(rotation);
if(mDistCam != null)
{
mDistCam.setRotation(rotation);
}
}
/**
* <code>lookAtDirection</code> sets the direction the camera is facing
* given a direction and an up vector.
*
* @param direction the direction this camera is facing.
*/
@Override
public void lookAtDirection(Vector3f direction, Vector3f up)
{
super.lookAtDirection(direction, up);
mCamNode.setLocalRotation(rotation);
if(mDistCam != null)
{
mDistCam.lookAtDirection(direction, up);
}
}
/**
* <code>setAxes</code> sets the axes (left, up and direction) for this
* camera.
*
* @param left the left axis of the camera.
* @param up the up axis of the camera.
* @param direction the direction the camera is facing.
*
* @see Camera#setAxes(com.jme3.math.Quaternion)
*/
@Override
public void setAxes(Vector3f left, Vector3f up, Vector3f direction)
{
super.setAxes(left, up, direction);
mCamNode.setLocalRotation(super.getRotation());
if(mDistCam != null)
{
mDistCam.setAxes(left, up, direction);
}
}
/**
* <code>setAxes</code> uses a rotational matrix to set the axes of the
* camera.
*
* @param axes the matrix that defines the orientation of the camera.
*/
@Override
public void setAxes(Quaternion axes)
{
super.setAxes(axes);
mCamNode.setLocalRotation(super.getRotation());
if(mDistCam != null)
{
mDistCam.setAxes(axes);
}
}
/**
* normalize normalizes the camera vectors.
*/
@Override
public void normalize()
{
super.normalize();
if(mDistCam != null)
{
mDistCam.normalize();
}
}
/**
* <code>setFrame</code> sets the orientation and location of the camera.
*
* @param location the point position of the camera.
* @param left the left axis of the camera.
* @param up the up axis of the camera.
* @param direction the facing of the camera.
* @see Camera#setFrame(com.jme3.math.Vector3f,
* com.jme3.math.Vector3f, com.jme3.math.Vector3f, com.jme3.math.Vector3f)
*/
@Override
public void setFrame(Vector3f location, Vector3f left, Vector3f up, Vector3f direction)
{
super.setFrame(location, left, up, direction);
if(mDistCam != null)
{
mDistCam.setFrame(location, left, up, direction);
}
}
/**
* <code>lookAt</code> is a convienence method for auto-setting the frame
* based on a world position the user desires the camera to look at. It
* repoints the camera towards the given position using the difference
* between the position and the current camera location as a direction
* vector and the worldUpVector to compute up and left camera vectors.
*
* @param pos where to look at in terms of world coordinates
* @param worldUpVector a normalized vector indicating the up direction of the world.
* (typically {0, 1, 0} in jME.)
*/
@Override
public void lookAt(Vector3f pos, Vector3f worldUpVector)
{
super.lookAt(pos, worldUpVector);
mCamNode.setLocalRotation(super.getRotation());
if(mDistCam != null)
{
mDistCam.lookAt(pos, worldUpVector);
}
}
/**
* <code>setFrame</code> sets the orientation and location of the camera.
*
* @param location
* the point position of the camera.
* @param axes
* the orientation of the camera.
*/
@Override
public void setFrame(Vector3f location, Quaternion axes)
{
super.setFrame(location, axes);
if(mDistCam != null)
{
mDistCam.setFrame(location, axes);
}
}
/**
* <code>update</code> updates the camera parameters by calling
* <code>onFrustumChange</code>,<code>onViewPortChange</code> and
* <code>onFrameChange</code>.
*
* @see Camera#update()
*/
@Override
public void update()
{
super.update();
if(mDistCam != null)
{
mDistCam.update();
}
}
/**
* <code>setViewPortLeft</code> sets the left boundary of the viewport
*
* @param left the left boundary of the viewport
*/
@Override
public void setViewPortLeft(float left)
{
super.setViewPortLeft(left);
if(mDistCam != null)
{
mDistCam.setViewPortLeft(left);
}
}
/**
* <code>setViewPortRight</code> sets the right boundary of the viewport
*
* @param right the right boundary of the viewport
*/
@Override
public void setViewPortRight(float right)
{
super.setViewPortRight(right);
if(mDistCam != null)
{
mDistCam.setViewPortRight(right);
}
}
/**
* <code>setViewPortTop</code> sets the top boundary of the viewport
*
* @param top the top boundary of the viewport
*/
@Override
public void setViewPortTop(float top)
{
super.setViewPortTop(top);
if(mDistCam != null)
{
mDistCam.setViewPortTop(top);
}
}
/**
* <code>setViewPortBottom</code> sets the bottom boundary of the viewport
*
* @param bottom the bottom boundary of the viewport
*/
@Override
public void setViewPortBottom(float bottom)
{
super.setViewPortBottom(bottom);
if(mDistCam != null)
{
mDistCam.setViewPortBottom(bottom);
}
}
/**
* <code>setViewPort</code> sets the boundaries of the viewport
*
* @param left the left boundary of the viewport (default: 0)
* @param right the right boundary of the viewport (default: 1)
* @param bottom the bottom boundary of the viewport (default: 0)
* @param top the top boundary of the viewport (default: 1)
*/
@Override
public void setViewPort(float left, float right, float bottom, float top)
{
super.setViewPort(left, right, bottom, top);
if(mDistCam != null)
{
mDistCam.setViewPort(left, right, bottom, top);
}
}
/**
* Overrides the projection matrix used by the camera. Will
* use the matrix for computing the view projection matrix as well.
* Use null argument to return to normal functionality.
*
* @param projMatrix
*/
@Override
public void setProjectionMatrix(Matrix4f projMatrix)
{
super.setProjectionMatrix(projMatrix);
if(mDistCam != null)
{
mDistCam.setProjectionMatrix(projMatrix);
}
}
/**
* Updates the view projection matrix.
*/
@Override
public void updateViewProjection()
{
super.updateViewProjection();
if(mDistCam != null)
{
mDistCam.updateViewProjection();
}
}
/**
* Clears the viewport changed flag once it has been updated inside
* the renderer.
*/
@Override
public void clearViewportChanged()
{
super.clearViewportChanged();
if(mDistCam != null)
{
mDistCam.clearViewportChanged();
}
}
/**
* Called when the viewport has been changed.
*/
@Override
public void onViewPortChange()
{
super.onViewPortChange();
if(mDistCam != null)
{
mDistCam.onViewPortChange();
}
}
/**
* <code>onFrustumChange</code> updates the frustum to reflect any changes
* made to the planes. The new frustum values are kept in a temporary
* location for use when calculating the new frame. The projection
* matrix is updated to reflect the current values of the frustum.
*/
@Override
public void onFrustumChange()
{
super.onFrustumChange();
if(mDistCam != null)
{
mDistCam.onFrustumChange();
}
}
/**
* <code>onFrameChange</code> updates the view frame of the camera.
*/
@Override
public void onFrameChange()
{
super.onFrameChange();
if(mDistCam != null)
{
mDistCam.onFrameChange();
}
}
/**
* Enable/disable parallel projection.
*
* @param value true to set up this camera for parallel projection is enable, false to enter normal perspective mode
*/
@Override
public void setParallelProjection(final boolean value)
{
super.setParallelProjection(value);
if(mDistCam != null)
{
mDistCam.setParallelProjection(value);
}
}
/**
* Computes the z value in projection space from the z value in view space
* Note that the returned value is going non linearly from 0 to 1.
* for more explanations on non linear z buffer see
* http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html
* @param viewZPos the z value in view space.
* @return the z value in projection space.
*/
@Override
public float getViewToProjectionZ(float viewZPos)
{
float far = getFrustumFar();
float near = getFrustumNear();
if(this.mCamType == CameraType.MULTIPASS_LONGDIST)
{
near = mDistCam.getFrustumFar();
}
float a = far / (far - near);
float b = far * near / (near - far);
return a + b / viewZPos;
}
/**
* Updates the cameras location and rotation from the internal node
*/
public void updateFromNode()
{
super.setLocation(mCamNode.getWorldTranslation());
super.setRotation(mCamNode.getWorldRotation());
if(mDistCam != null)
{
mDistCam.setLocation(mCamNode.getWorldTranslation());
mDistCam.setRotation(mCamNode.getWorldRotation());
}
}
/**
* Returns the internal camera Node
* @return
*/
public Node getCameraNode()
{
return mCamNode;
}
}
[/java]
Any help would be appreciated.