Moving 3D model

Hi,

I wanted to move the model dynamically using keyboard shortcuts. I could not find relevant article on that.

So for now, I’m trying to move the model on click. When click on the model. The model has to move in one direction (increment the value 1 on tick). Find below the sandcastle code for that.

var selectedMesh; var i=0;

var viewer = new Cesium.Viewer(‘cesiumContainer’, {

infoBox: false,

selectionIndicator: false

});

var handle = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

function createModel(url, height) {

viewer.entities.removeAll();

var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);

var heading = Cesium.Math.toRadians(135);

var pitch = 0;

var roll = 0;

var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);

var entity = viewer.entities.add({

    name: url,

    position: position,

    orientation: orientation,

    model: {

        uri: url,

        minimumPixelSize: 128

    }

});

viewer.trackedEntity = entity;

viewer.clock.onTick.addEventListener(function () {

    if (selectedMesh) {

        console.log("Before 0 : " + selectedMesh.primitive.modelMatrix[12]);

        selectedMesh.primitive.modelMatrix[12] = selectedMesh.primitive.modelMatrix[12] + 1;

        console.log("After 0 : " + selectedMesh.primitive.modelMatrix[12]);

    } 

});

}

handle.setInputAction(function (movement) {

console.log("LEFT CLICK");

var pick = viewer.scene.pick(movement.position);

if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {

    if (!selectedMesh) {

        selectedMesh = pick;

    }

}

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

var options = [{

text: 'Aircraft',

onselect: function () {

    createModel('../../SampleData/models/CesiumAir/Cesium_Air.bgltf', 5000.0);

}

}, {

text: 'Ground vehicle',

onselect: function () {

    createModel('../../SampleData/models/CesiumGround/Cesium_Ground.bgltf', 0);

}

}, {

text: 'Milk truck',

onselect: function () {

    createModel('../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck.bgltf', 0);

}

}, {

text: 'Skinned character',

onselect: function () {

    createModel('../../SampleData/models/CesiumMan/Cesium_Man.bgltf', 0);

}

}];

Sandcastle.addToolbarMenu(options);

``

When I click, the model is moving for the first time. After that, It stays on the same place. I’ve printed the value in the console. It seems the value is not changing. I’m not sure about the problem here. or I’m implementing the transformation wrongly.

Any kind of help is appreciated. Thanks in advance.

I highly recommend taking a look at the Monster Milktruck from the Google Earth Developers Samples.

https://github.com/AnalyticalGraphicsInc/cesium-google-earth-examples/blob/master/demos/milktruck/milktruck.js

This JS file is a great example of how to move your models.

Thank you Mike. I manage to do changing position using keys. Now the facing some challenge on retaining the value. I’ve more than one model. While click on the model I’m selecting and performing translation / rotation. Once I click the second model I’m loosing the translation / rotation of the previous model. The modelMatrix is set to default values again.

I’m not sure about the behavior of selected mesh modelMatrix. Find below the code.

var heading = 0.0, pitch = 0.0, roll = 0.0, long = 0.0, lat = 0.0, alt = 0.0;
var selectedMesh;

        /* MOUSE LEFT BUTTON DOWN */

handle.setInputAction(function(movement){

var pick = this.scene.pick(movement.position);

if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {

selectedMesh = pick;

heading = 0.0, pitch = 0.0, roll = 0.0;

} else {

this.selectedMesh = null;

}

        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

     

    viewer.clock.onTick.addEventListener(function() {

       
                   if (!selectedMesh) {

return;

}

/* Rotation */

var headingDegree = degreesToRadians(heading);

var pitchDegree = degreesToRadians(pitch);

var rollDegree = degreesToRadians(roll);

var headingQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -headingDegree);

var pitchQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Y, -pitchDegree);

var rollQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_X, -rollDegree);

var headingPitchQuat = Cesium.Quaternion.multiply(headingQuat, pitchQuat, new Cesium.Quaternion());

var rotationQuaternion = Cesium.Quaternion.multiply(headingPitchQuat, rollQuat, new Cesium.Quaternion());

var rotationMatrix = Cesium.Matrix3.fromQuaternion(rotationQuaternion, new Cesium.Matrix3());

var currentRotation = new Cesium.Cartesian3();

                        Cesium.Matrix4.getRotation(selectedMesh.primitive.modelMatrix, currentRotation);

var newRotation = Cesium.Matrix3.multiply(currentRotation, rotationMatrix, new Cesium.Matrix3());

var currentTranslation = new Cesium.Cartesian3();

Cesium.Matrix4.getTranslation(this.selectedMesh.primitive.modelMatrix, currentTranslation);

currentTranslation.x += long;

currentTranslation.y += lat;

currentTranslation.z += alt;

this.selectedMesh.primitive.modelMatrix = Cesium.Matrix4.fromRotationTranslation(newRotation, currentTranslation, new Cesium.Matrix4());

});

}

document.addEventListener(‘keyup’, (e) => {

if (e.keyCode === ‘A’.charCodeAt(0)) {

heading -= 1;

} else if (e.keyCode === ‘S’.charCodeAt(0)) {

heading += 1;

} else if (e.keyCode === ‘Z’.charCodeAt(0)) {

pitch -= 2;

} else if (e.keyCode === ‘X’.charCodeAt(0)) {

pitch += 2;

} else if (e.keyCode === ‘Q’.charCodeAt(0)) {

roll -= 2;

} else if (e.keyCode === ‘W’.charCodeAt(0)) {

roll += 2;

} else if (e.keyCode === ‘O’.charCodeAt(0)) {

lat -= 1;

} else if (e.keyCode === ‘P’.charCodeAt(0)) {

lat += 1;

} else if (e.keyCode === ‘K’.charCodeAt(0)) {

long -= 1;

} else if (e.keyCode === ‘L’.charCodeAt(0)) {

long += 1;

}else if (e.keyCode === ‘N’.charCodeAt(0)) {

alt -= 1;

} else if (e.keyCode === ‘M’.charCodeAt(0)) {

alt += 1;

}

}, false);

``

I’m expecting that, the changes I’m doing with the selectedMesh should reflect in the actual mesh. So when I select the same model again. It should have the configured configuration.

Any clue on this issue?

The only thing that is popping out at me is that you’re mixing “this.selectedMesh” and “selectedMesh”. I recommend debugging your application using console.log messages and breakpoints to figure out what is happening to the states of your models.

Here is a complete code of the issue. Just paste the code in sandcastle and you could replicate the below scenario.

Scenario:

  1. Switch to full screen mode.

  2. Select the model and use keys (Q,W,A,S,Z,X,O,P,K,L,N,M) to rotate and translation.

  3. select the another model.

Issue: The first model transformation will be reset. I’m updating the “modelMatrix”. of the selected mesh.

var selectedMesh;

var i = 0;

var heading = 0.0,

pitch = 0.0,

roll = 0.0,

long = 0.0,

lat = 0.0,

alt = 0.0;

var viewer = new Cesium.Viewer(‘cesiumContainer’, {

infoBox: false,

selectionIndicator: false

});

var handle = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

function createModel(url, height) {

var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);

var heading = Cesium.Math.toRadians(135);

var pitch = 0;

var roll = 0;

var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);

var entity = viewer.entities.add({

name: url,

position: position,

orientation: orientation,

model: {

uri: url,

minimumPixelSize: 128

}

});

viewer.trackedEntity = entity;

}

function degreesToRadians (val) {

return val * Math.PI / 180;

}

viewer.clock.onTick.addEventListener(function() {

if (!selectedMesh) {

return;

}

console.log(selectedMesh);

/* Rotation */

var headingDegree = degreesToRadians(heading);

var pitchDegree = degreesToRadians(pitch);

var rollDegree = degreesToRadians(roll);

var headingQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -headingDegree);

var pitchQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Y, -pitchDegree);

var rollQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_X, -rollDegree);

var headingPitchQuat = Cesium.Quaternion.multiply(headingQuat, pitchQuat, new Cesium.Quaternion());

var rotationQuaternion = Cesium.Quaternion.multiply(headingPitchQuat, rollQuat, new Cesium.Quaternion());

var rotationMatrix = Cesium.Matrix3.fromQuaternion(rotationQuaternion, new Cesium.Matrix3());

var currentRotation = new Cesium.Cartesian3();

Cesium.Matrix4.getRotation(selectedMesh.primitive.modelMatrix, currentRotation);

var newRotation = Cesium.Matrix3.multiply(currentRotation, rotationMatrix, new Cesium.Matrix3());

var currentTranslation = new Cesium.Cartesian3();

Cesium.Matrix4.getTranslation(selectedMesh.primitive.modelMatrix, currentTranslation);

currentTranslation.x += long;

currentTranslation.y += lat;

currentTranslation.z += alt;

selectedMesh.primitive.modelMatrix = Cesium.Matrix4.fromRotationTranslation(newRotation, currentTranslation, new Cesium.Matrix4());

});

document.addEventListener(‘keyup’, function(e) {

if (e.keyCode === ‘A’.charCodeAt(0)) {

heading -= 1;

} else if (e.keyCode === ‘S’.charCodeAt(0)) {

heading += 1;

} else if (e.keyCode === ‘Z’.charCodeAt(0)) {

pitch -= 2;

} else if (e.keyCode === ‘X’.charCodeAt(0)) {

pitch += 2;

} else if (e.keyCode === ‘Q’.charCodeAt(0)) {

roll -= 2;

} else if (e.keyCode === ‘W’.charCodeAt(0)) {

roll += 2;

} else if (e.keyCode === ‘O’.charCodeAt(0)) {

lat -= 1;

} else if (e.keyCode === ‘P’.charCodeAt(0)) {

lat += 1;

} else if (e.keyCode === ‘K’.charCodeAt(0)) {

long -= 1;

} else if (e.keyCode === ‘L’.charCodeAt(0)) {

long += 1;

} else if (e.keyCode === ‘N’.charCodeAt(0)) {

alt -= 1;

} else if (e.keyCode === ‘M’.charCodeAt(0)) {

alt += 1;

}

}, false);

handle.setInputAction(function(movement) {

console.log(“LEFT CLICK”);

var pick = viewer.scene.pick(movement.position);

if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {

heading = 0.0, pitch = 0.0, roll = 0.0, long = 0.0, lat = 0.0, alt = 0.0;

selectedMesh = pick;

}

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

createModel(’…/…/SampleData/models/CesiumAir/Cesium_Air.bgltf’, 10);

createModel(’…/…/SampleData/models/CesiumGround/Cesium_Ground.bgltf’, 0);

``

Appreciate your support.

The problem is that you are modifying the primitive directly and not the Entity associated with the primitive. The Entity system is overwriting your changes to make sure the primitive matches the Entity. The easiest way to fix this is to modify the entity.position and entity.orientation properties instead of the modelMatrix.

Thanks @Matthew.

If I’ve a single model, then I shall go and set value to the entity.position or entity.orientation property. Similar to this example (http://mramato.github.io/Demos/FOSS4GNA2015/interpolation.html).Here I’m picking the models dynamically and I’m not getting the position and orientation of the pick object.

Can you please guide me, how I can update the position and orientation on the selected model dynamic (selectedMesh)?

Any suggestion for this post guys!

If you wrap your models in an entity

var position = Cesium.Cartesian3.fromDegrees(

plane.position.lon,

plane.position.lat,

plane.position.alt

);

viewer.entities.add({
id: plane.id,
model: {
uri: plane.modelUrl
},
position : position,
orientation: Cesium.Transforms.headingPitchRollQuaternion(
position,
plane.orientation.heading,
plane.orientation.pitch,
plane.orientation.roll
)
});

``

You can than modify the position and orientation at the entity level that the scene.pick returned. Your model will stay pristine.

At this point if you want to specifically modify the model’s mesh you can drill down into the model on the entity and manipulate away.

Hi Mike,

Thank you for taking time to respond my post.

I’m having the same code as you suggested. Please have a look on the below code (complete sandcastle code is listed in the previous reply). Can you make the changes in the sandcastle code and send me the updated code please.

function createModel(url, height) {

var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);

var heading = Cesium.Math.toRadians(135);

var pitch = 0;

var roll = 0;

var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);

var entity = viewer.entities.add({

name: url,

position: position,

orientation: orientation,

model: {

uri: url,

minimumPixelSize: 128

}

});

viewer.trackedEntity = entity;

}

createModel(’…/…/SampleData/models/CesiumAir/Cesium_Air.bgltf’, 10);

createModel(’…/…/SampleData/models/CesiumGround/Cesium_Ground.bgltf’, 0);

``

Thank you!

Have you checked out some of the older posts in the forum about moving models programmatically?

You might find this one helpful: https://groups.google.com/forum/#!searchin/cesium-dev/move$20model/cesium-dev/hq0dnHLoYH4/e5ldMKtQY1oJ

Thanks Mike,

I’ve already tried that approach. It is also the same as your suggestion. It works well for the single model.

The below code is from the old post. You could see the model transformation and orientation changes inside onTick event.The positionModel takes model as input. In my case, I will pick the model dynamically and passing it to the positionModel. When I use pick, I’m have access to model.primitive.modelMatrix instead of model.modelMatrix.

var createModel = function(lon, lat, alt, heading, pitch, roll) {
var model = scene.primitives.add(Cesium.Model.fromGltf({
url : ‘…/…/SampleData/models/CesiumAir/Cesium_Air.gltf’
}));

model.readyToRender.addEventListener(function(model) {
// Play and loop all animations at half-spead
model.activeAnimations.addAll({
speedup : 0.5,
loop : Cesium.ModelAnimationLoop.REPEAT
});

   viewer.clock.onTick.addEventListener(function() {
       positionModel(model, lon, lat, alt, heading, pitch, roll);
       positionCamera(model);
       
       //lon = lon+0.00001;
       //lat = lat+0.000005;
       //alt = alt+0.01;
       heading += 0.01;
       pitch += 0.05;
       roll += 0.1;
   });

});
};

``

Is there is a way to change the model transformation and orientation dynamically?

Have you tried using Matt’s suggestion for using an Entity instead of a Model? You can still attach the model to the entity and it should simplify the movements and orientations.

Thank you for taking time to reply Mike.

I believe Matt was suggested the below logic. If I make the entity as a global variable, then I’m having access to entity.position and entity.orientation. else not. Since, I’m loading many model and selecting the models dynamically and want to perform movement and orientation.

function createModel(url, height) {

viewer.entities.removeAll();

var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);

var heading = Cesium.Math.toRadians(135);

var pitch = 0;

var roll = 0;

var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);

var entity = viewer.entities.add({
   name: url,
   position: position,
   orientation: orientation,
   model: {
       uri: url,
       minimumPixelSize: 128
   }

});

viewer.trackedEntity = entity;

}

``

To achieve that I’ve used the below pick function. But I’m not having the property called position and orientation.

handle.setInputAction(function(movement) {

console.log(“LEFT CLICK”);

var pick = viewer.scene.pick(movement.position);

if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {

       selectedMesh = pick;

}

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

``

Can you please confirm, whether I’ve followed Matt’s suggestion or I understand it wrongly… If so, please guide me to give some proper solution for this.You can find the complete code here.

Appreciate your support.

Haven’t forgotten you, I’m catching up on the forums from my vacation so I’ll have something for you either later today or over the weekend.

Thank you so much Mike :slight_smile:

I’m waiting for you!

Hello Mike,

I’ve fixed the issue. By changing the position and orientation from the below code. I could retain the value of the selected model dynamically. I’m not sure, whether this is the right way to fix the problem. Please advice if there is a better solution than this.

selectedMesh.id.position._value = currentTranslation;

selectedMesh.id.orientation._value = newRotation;

``

Thank you!