What should I do if the camera is locked when using lookAtTransform?


As shown in the figure, I am using lookAtTransform to modify the camera position to achieve a first-person view, but this way the camera is locked and cannot perform any mouse operations, such as clicking, rotating, zooming, etc.

This is my code

viewer.clock.onTick.addEventListener(function(clock) {
			let center = entity.position.getValue(clock.currentTime);
			let orientation = entity.orientation.getValue(clock.currentTime);
			let transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromQuaternion(orientation), center);
			viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-80, 0, 7000))
		});

What should I do, is there any good solution? Thanks!

@Baker

Welcome to the community! :fireworks:

It looks like your application is based on the Multi-part CZML sandcastle demo. Is that correct? If so, could you please send me a sandcastle demo of what you have so far? Seeing your code would help me understand where you added

viewer.clock.onTick.addEventListener(function(clock) {
			let center = entity.position.getValue(clock.currentTime);
			let orientation = entity.orientation.getValue(clock.currentTime);
			let transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromQuaternion(orientation), center);
			viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-80, 0, 7000))
});

You could update the position and rotation of the Camera based on the CZML path. You could also get the Cesium Truck’s rotational quaternion and use this to update the Camera position and rotation. Here is a thread that discusses the second approach

I slightly prefer the second method, however, I would love to hear your thoughts. Regardless, the following sandcastle demo showcases how the Camera can be updated in the javascript code.

This will clearly be important in any implementation. Let me know if you have any other questions or concerns!

-Sam

1 Like

Hi Sam,
Thank you for your reply. Yes, I changed it using this demoMulti-part CZML. I just want to change the perspective while the object is following.

The complete code after modification is as follows

var viewer = new Cesium.Viewer("cesiumContainer", {
  shouldAnimate: true,
});

var statusDisplay = document.createElement("div");
var fuelDisplay = document.createElement("div");
var czmlPath = "../SampleData/";
var vehicleEntity;
var startToCorrect=false;

// Add a blank CzmlDataSource to hold our multi-part entity/entities.
var dataSource = new Cesium.CzmlDataSource();
viewer.dataSources.add(dataSource);

// This demo shows how a single path can be broken up into several CZML streams.
var partsToLoad = [
  {
    url: "MultipartVehicle_part1.czml",
    range: [0, 1500],
    requested: false,
    loaded: false,
  },
  {
    url: "MultipartVehicle_part2.czml",
    range: [1500, 3000],
    requested: false,
    loaded: false,
  },
  {
    url: "MultipartVehicle_part3.czml",
    range: [3000, 4500],
    requested: false,
    loaded: false,
  },
];

function updateStatusDisplay() {
  var msg = "";
  partsToLoad.forEach(function (part) {
    msg += part.url + " - ";
    if (part.loaded) {
      msg += "Loaded.<br/>";
    } else if (part.requested) {
      msg += "Loading now...<br/>";
    } else {
      msg += "Not needed yet.<br/>";
    }
  });
  statusDisplay.innerHTML = msg;
}

// Helper function to mark a part as requested, and process it into the dataSource.
function processPart(part) {
  part.requested = true;
  updateStatusDisplay();
  dataSource.process(czmlPath + part.url).then(function () {
    part.loaded = true;
    updateStatusDisplay();

    // Follow the vehicle with the camera.
    if (!viewer.trackedEntity) {
      viewer.trackedEntity = vehicleEntity = dataSource.entities.getById(
        "Vehicle"
      );
    
      startToCorrect = true;
    }
  });
}

// Load the first part up front.
processPart(partsToLoad[0]);

// Load a new section before the clock naturally gets there.
// Note this can't predict when a user may fast-forward to it.
var preloadTimeInSeconds = 100;

viewer.clock.onTick.addEventListener(function (clock) {
  // This example uses time offsets from the start to identify which parts need loading.
  var timeOffset = Cesium.JulianDate.secondsDifference(
    clock.currentTime,
    clock.startTime
  );

  // Filter the list of parts to just the ones that need loading right now.
  // Then, process each part that needs loading.
  partsToLoad
    .filter(function (part) {
      return (
        !part.requested &&
        timeOffset >= part.range[0] - preloadTimeInSeconds &&
        timeOffset <= part.range[1]
      );
    })
    .forEach(function (part) {
      processPart(part);
    });

  if (vehicleEntity) {
    var fuel = vehicleEntity.properties.fuel_remaining.getValue(
      clock.currentTime
    );
    if (Cesium.defined(fuel)) {
      fuelDisplay.textContent = "Fuel: " + fuel.toFixed(2) + " gal";
    }
    
  }
  if(startToCorrect){
    	let center = vehicleEntity.position.getValue(clock.currentTime);
		let orientation = vehicleEntity.orientation.getValue(clock.currentTime)
		let transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromQuaternion(orientation), center);
		viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-80, 0, 7000))
   }
});


// Add a reset button, for convenience.
Sandcastle.addToolbarButton("Reset demo", function () {
  // Put things back to the starting position.
  viewer.clock.currentTime = viewer.clock.startTime;
  viewer.clock.shouldAnimate = true;

  partsToLoad.forEach(function (part) {
    part.requested = false;
    part.loaded = false;
  });

  dataSource.entities.removeAll();
  processPart(partsToLoad[0]);
});

// Show the status display below the reset button.
statusDisplay.style.background = "rgba(42, 42, 42, 0.7)";
statusDisplay.style.padding = "5px 10px";
document.getElementById("toolbar").appendChild(statusDisplay);

// Show a multi-part custom property being read from CZML.
fuelDisplay.style.background = "rgba(42, 42, 42, 0.7)";
fuelDisplay.style.padding = "5px 10px";
fuelDisplay.style.marginTop = "5px";
document.getElementById("toolbar").appendChild(fuelDisplay);

I have seen this caseCamera Tutorial, which is to control mouse events, but the result I want is to modify the perspective while performing normal event interactions.
But in this demoMulti-part CZML, when I modify the code, the mouse events can no longer be called. I don’t know if my idea is wrong, and I shouldn’t modify it in this way.
These days I have been looking for another way to achieve first-person perspective, but unfortunately I have not found another way.

@Baker

Thank you for sending over your demo! It looks great so far but clearly, the camera is not quite in the correct position. I put together two demos for you that should address this problem. You can use them as a guide to help you with your final implementation.

In this sandcastle demo, we are following the car from behind. It is a close 3rd person perspective that almost appears to be 1st person.

This next demo showcases a truly 1st person perspective by removing the car asset.

If you wish to tweak the perspective in either of these demos, the simplest way to do this would be to modify the line

viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-80, 0, 6));

I am curious to hear your thoughts! Let me know if you have any other questions or concerns :grinning:

-Sam

@sam.rothstein Hi Sam, thank you very much for your help. My headache now is how to enable mouse events in the above scene? For example, when the mouse is pressed, the model can be dragged, and the mouse wheel can be scrolled to change the map level.

@Baker

I am so glad that the demos helped out! Adding mouse events would add a whole new dimension to your project. What specific events are you looking to add? Admittedly, I do not have a significant amount of experience working with mouse events in CesiumJS. Any suggestions from the rest of the community?

-Sam

Alas, any friend has experience in dealing with similar problems? Please help me.

1 Like