There's gonna be some problems getting the camera to look down from the top

This video shows the camera looking from right to left.
Everything’s fine. There’s no problem.

This video shows the camera looking down from above.
Here’s the problem, I simply changed cam.lookAtDirection() and didn’t set anything else, but the scene disappears before it appears out of the camera!

You can observe the rectangles in the upper left corner of the jme window.

The camera’s debug cone is also a little bit out of sync with the camera Angle, but looks fine from another window.

I hope someone can give me some tips and help, if you need me to provide more information, please let me know

We can’t see this code.

It’s hard to debug code that we cannot see.

lookAtDirection() requires an up vector. If the up vector is the same as the look direction (or exactly opposite the look direction) then you are going to have problems.

We can’t see the code so that’s my guess. Your up vector is not orthogonal to your look vector.

2 Likes

Thank you for your reply.
This is the part of the code that controls the camera and the mouse.
I set up the camera like this.

        cam.lookAtDirection(new Vector3f(0,-1,0),new Vector3f(0,1,0));
        cam.setLocation(new Vector3f(0, 400, 0));
/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package RTSCameraAndMouse;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetManager;
import com.jme3.asset.TextureKey;
import com.jme3.cursors.plugins.CursorLoader;
import com.jme3.cursors.plugins.JmeCursor;
import com.jme3.input.CameraInput;
import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.scene.debug.WireFrustum;
import com.jme3.scene.shape.Box;
import com.jme3.shadow.ShadowUtil;
import com.jme3.system.AppSettings;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ImageRaster;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author icyboxs
 */
public class CamAndMouseCur extends BaseAppState implements ActionListener, AnalogListener{
    
    private static Logger log =Logger.getLogger(CamAndMouseCur.class.toString());
    private InputManager inputManager;
    private AssetManager assetManager;
    private SimpleApplication simpleApp;
    private Camera cam;
    private Vector2f Resolution = new Vector2f();
//相机移动
    private String Left= "Left";//左
    private String Right= "Right";//右
    private String Up= "Up";//前
    private String Backward= "Backward";//后
    private String UpperLeft= "UpperLeft";//左上
    private String UpperRight= "UpperRight";//右上
    private String LowerLeft= "LowerLeft";//左下
    private String LowerRight= "LowerRight";//右下
    
    @Override
    protected void initialize(Application aplctn) {
       simpleApp = (SimpleApplication) aplctn;
        assetManager = aplctn.getAssetManager();
        inputManager = aplctn.getInputManager();
        cam=aplctn.getCamera();
                // 初始化摄像机,从Z轴正上方往下看。
       // simpleApp.getCamera().lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        cam.lookAtDirection(new Vector3f(0,-1,0),new Vector3f(0,1,0));
        cam.setLocation(new Vector3f(0, 400, 0));
        
        
    JmeCursor x = (JmeCursor) assetManager.loadAsset("Textures/ColdCyan/aim.ani");
    x.setxHotSpot(16);
    x.setyHotSpot(16);
JmeCursor c = (JmeCursor) assetManager.loadAsset("Textures/ColdCyan/cursor.cur");
    c.setxHotSpot(0);
    c.setyHotSpot(32);
inputManager.setMouseCursor(c);

   // Texture customCursorTexture = assetManager.loadTexture("Textures/ColdCyan/normal.png");
    
//    if(customCursorTexture!=null){
//       Image image = customCursorTexture.getImage();
//       ByteBuffer imgByteBuff = image.getData(0);
//    imgByteBuff.rewind();
//  IntBuffer imgIntBuff = imgByteBuff.asIntBuffer();
//    JmeCursor c = new JmeCursor();
//    c.setHeight(image.getHeight());
//    c.setWidth(image.getWidth());
//    c.setNumImages(1);
//    c.setyHotSpot(image.getHeight()-3);
//    c.setxHotSpot(3);
//    c.setImagesData(imgIntBuff);
//    
//    inputManager.setMouseCursor(c);
//
//    }
    
    
//    // 从 Texture 中获取 Image,并提取像素数据
//    Image image = customCursorTexture.getImage();
//    ByteBuffer = image.getData(32);
//    // 将像素数据转换为 IntBuffer
//    IntBuffer imageData = pixelData.asIntBuffer();
//    // 设置光标的图像数据
//    jmeCursor.setImagesData(imageData);
//
//    // 设置自定义光标
//    inputManager.setMouseCursor(jmeCursor);
BOX();
}

    @Override
    public void update(float tpf) {
    
    if((cam.getWidth()*0.05)>inputManager.getCursorPosition().getX()&&(cam.getHeight()*0.05)>inputManager.getCursorPosition().getY()){
        MoveCam(LowerLeft);//左下
    }else if((cam.getWidth()-(cam.getWidth()*0.05))<inputManager.getCursorPosition().getX()&&(cam.getHeight()*0.05)>inputManager.getCursorPosition().getY()){
        MoveCam(LowerRight);//右下
    }else if((cam.getWidth()*0.05)>inputManager.getCursorPosition().getX()&&(cam.getHeight()-(cam.getHeight()*0.05))<inputManager.getCursorPosition().getY()){
        MoveCam(UpperLeft);//左上
    }else if((cam.getWidth()-(cam.getWidth()*0.05))<inputManager.getCursorPosition().getX()&&(cam.getHeight()-(cam.getHeight()*0.05))<inputManager.getCursorPosition().getY()){
        MoveCam(UpperRight);//右上
    }else if((cam.getWidth()*0.05)>inputManager.getCursorPosition().getX()){
        MoveCam(Left);//左
    }else if((cam.getWidth()-(cam.getWidth()*0.05))<inputManager.getCursorPosition().getX()){
        MoveCam(Right);//右
    }else if((cam.getHeight()*0.05)>inputManager.getCursorPosition().getY()){
        MoveCam(Backward);//后
    }else if((cam.getHeight()-(cam.getHeight()*0.05))<inputManager.getCursorPosition().getY()){
        MoveCam(Up);//前
    }
    
   
   // System.err.println(inputManager.getCursorPosition());
     
    cam.setLocation(cam.getLocation());
    System.err.println(cam.getLocation());
       simpleApp.getRootNode().getChild("Viewing.Frustum").setLocalTranslation(cam.getLocation());
       simpleApp.getRootNode().getChild("Viewing.Frustum").setLocalRotation(cam.getRotation());
       simpleApp.getRootNode().getChild("Box").setLocalTranslation(cam.getLocation());
       simpleApp.getRootNode().getChild("Box").setLocalRotation(cam.getRotation());
    //logger.info(inputManager.getCursorPosition().toString());
    //System.out.println(Arrays.toString(points));

    }
    
    @Override
    protected void cleanup(Application app) {
    }

    @Override
    protected void onEnable() {
createCameraFrustum();
   }

    @Override
    protected void onDisable() {
    }
   private void MoveCam(String direction){
   switch(direction){

   case("Left"):
              cam.setLocation(cam.getLocation().add(new Vector3f(1,0,0)));
    break;
   case("Right"):
       cam.setLocation(cam.getLocation().add(new Vector3f(-1,0,0)));
    break;
   case("Up"):
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,1)));
    break;
   case("Backward"):
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,-1)));
    break;
   case("UpperLeft"):
        cam.setLocation(cam.getLocation().add(new Vector3f(1,0,0)));
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,1)));
    break;
   case("UpperRight"):
        cam.setLocation(cam.getLocation().add(new Vector3f(-1,0,0)));
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,1)));
    break;
   case("LowerLeft"):
        cam.setLocation(cam.getLocation().add(new Vector3f(1,0,0)));
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,-1)));
    break;
   case("LowerRight"):
        cam.setLocation(cam.getLocation().add(new Vector3f(-1,0,0)));
        cam.setLocation(cam.getLocation().add(new Vector3f(0,0,-1)));
    break;
   }
   
   }

    public void cleanupWithInput(InputManager mgr) {
        mgr.deleteMapping(CameraInput.CHASECAM_TOGGLEROTATE);
        mgr.deleteMapping(CameraInput.CHASECAM_DOWN);
        mgr.deleteMapping(CameraInput.CHASECAM_UP);
        mgr.deleteMapping(CameraInput.CHASECAM_MOVELEFT);
        mgr.deleteMapping(CameraInput.CHASECAM_MOVERIGHT);
        mgr.deleteMapping(CameraInput.CHASECAM_ZOOMIN);
        mgr.deleteMapping(CameraInput.CHASECAM_ZOOMOUT);
        mgr.removeListener(this);
    }
   
    public  void createCameraFrustum() {
       Vector3f[]  points = new Vector3f[8];
	for (int i = 0; i < 8; i++) {
		points[i] = new Vector3f();
	}
	
	Camera frustumCam = cam.clone();
        frustumCam.setLocation(new Vector3f(0, 0, 0));
        frustumCam.lookAt(Vector3f.UNIT_Z, Vector3f.ZERO);
	ShadowUtil.updateFrustumPoints2(frustumCam, points);
	Mesh mesh = WireFrustum.makeFrustum(points);
	
	Geometry frustumGeo = new Geometry("Viewing.Frustum", mesh);
	Material mat = new Material( simpleApp.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
	mat.setColor("Color", ColorRGBA.Blue);
	frustumGeo.setMaterial(mat);
	frustumGeo.setCullHint(Spatial.CullHint.Never);
	frustumGeo.setShadowMode(RenderQueue.ShadowMode.Off);
      
        simpleApp.getRootNode().attachChild(frustumGeo);
}
    
  public void BOX() {
        // 创建一个蓝色盒子
        Box box = new Box(1, 1, 1);
        Geometry boxGeometry = new Geometry("Box", box);

        // 创建一个蓝色材质
        Material boxMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        boxMaterial.setColor("Color", ColorRGBA.Blue);
        boxGeometry.setMaterial(boxMaterial);

        // 将盒子添加到场景中
        simpleApp.getRootNode().attachChild(boxGeometry);
    }

   
    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
    }

    @Override
    public void onAnalog(String name, float value, float tpf) {
        throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
    }
    
    
}

Can you give some tips and advice on how I should modify it?

Looks like @pspeed was correct. Here you’re saying that you want to look downwards and you want at the top of the users screen to be upwards. That is physically impossible, because upwards is behind them.

You as the designer need to decide what you want the the user to see. They are looking downwards. Which direction do you want the top of the screen to be.

(To kind of explain what this decision is; look down at the floor now. At the top of your vision is something, it might be your front door, or back door. You can change that by rotating while keeping looking downwards. That is the decision you are making. But no matter how you rotate you can’t make your ceiling the top of your vision while looking downwards; because your ceiling is “behind you” ).

It may not matter that much, if so just choose something, but choose something that’s physically possible

1 Like

Maybe I can do it with setRotation.

Anyway thanks a lot for your reply and tips

You can do it the way you are doing it. You just need to set the up vector to something that makes sense.

Put another way:
If text is written on the ground where is the top of the text?

x axis? z axis? -x axis? -z axis? Something else?

…that is your up vector.

Normally “look”, “up”, and “left” vectors must be orthogonal (90 degrees) from each other.

JME tries to be smart by calculating the “left vector” from “look” and “up” then calculating the real “up” vector from “look” and “left”. (Cross products in all cases.)

…but when “look” and “up” are the same line it is impossible to calculate a “left vector” because there are an infinite number of left vectors in that case.

Edit: put yet another way:
Change:

cam.lookAtDirection(new Vector3f(0,-1,0),new Vector3f(0,1,0));

To:

cam.lookAtDirection(new Vector3f(0,-1,0),new Vector3f(0,0,-1));

If that doesn’t look right then see above or post a picture of what you have and what you want.

2 Likes

Thanks for the tips and guidance everything seems to be working fine so far

1 Like