Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Realistic vehicle wheel speed Sandcastle example #7361

Merged
merged 23 commits into from
Jan 16, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Apps/SampleData/MultipartVehicle_part1.czml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"verticalOrigin":"CENTER"
},
"model":{
"gltf":"models/CesiumMilkTruck/CesiumMilkTruck.glb",
"gltf":"models/GroundVehicle/GroundVehicle.glb",
"minimumPixelSize":100,
"maximumScale":50
},
Expand Down
54 changes: 54 additions & 0 deletions Apps/Sandcastle/gallery/Multi-part CZML.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
var fuelDisplay = document.createElement('div');
var czmlPath = '../../SampleData/';
var vehicleEntity;
var lastVehiclePosition;

// Add a blank CzmlDataSource to hold our multi-part entity/entities.
var dataSource = new Cesium.CzmlDataSource();
Expand Down Expand Up @@ -87,6 +88,8 @@
// Follow the vehicle with the camera.
if (!viewer.trackedEntity) {
viewer.trackedEntity = vehicleEntity = dataSource.entities.getById('Vehicle');
lastVehiclePosition = vehicleEntity.position.getValue(viewer.clock.currentTime);
viewer.clock.multiplier = 0.35;
}
});
}
Expand All @@ -97,8 +100,39 @@
// 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;
var lastTime = new Date();
var pastRotationSpeed = 0;

function updateAnimationSpeed(rotationsPerSecond) {
// Make the animation play at that speed, given that it makes 2 rotations per second by default.
var speedup = (rotationsPerSecond * 1.1) / 2;
speedup = Math.max(Math.abs(speedup), 0.001);
// If it gets too fast it doesn't look great
speedup = Math.min(speedup, 15);
vehicleEntity.model.speedupAnimations = speedup;
}

viewer.clock.onTick.addEventListener(function(clock) {
if (vehicleEntity) {
var timeDiff = ((new Date()).getTime() - lastTime.getTime()) / 1000;

var currentPosition = vehicleEntity.position.getValue(clock.currentTime);
var lastCartographic = Cesium.Cartographic.fromCartesian(lastVehiclePosition);
var currentCartographic = Cesium.Cartographic.fromCartesian(currentPosition);

var distance = measure(currentCartographic.latitude, currentCartographic.longitude, lastCartographic.latitude, lastCartographic.longitude);
var velocity = distance / timeDiff;
var wheelRadius = 0.52;
var rotationsPerSecond = rotationSpeedFromVelocity(velocity, wheelRadius);

if (Math.abs(pastRotationSpeed - rotationsPerSecond) > 2.5) {
updateAnimationSpeed(rotationsPerSecond);
}
pastRotationSpeed = rotationsPerSecond;

lastVehiclePosition = currentPosition;
lastTime = new Date();
}
// This example uses time offsets from the start to identify which parts need loading.
var timeOffset = Cesium.JulianDate.secondsDifference(clock.currentTime, clock.startTime);

Expand Down Expand Up @@ -145,6 +179,26 @@
fuelDisplay.style.padding = '5px 10px';
fuelDisplay.style.marginTop = '5px';
document.getElementById('toolbar').appendChild(fuelDisplay);

function rotationSpeedFromVelocity(velocity, wheelRadius) {
// velocity is how fast the vehicle is moving in meters per second.
// wheelRadius in meters.
var circumference = Math.PI * wheelRadius * 2;
return velocity / circumference;
}

// From https://stackoverflow.com/a/11172685/1278023
function measure(lat1, lon1, lat2, lon2){ // generally used geo measurement function
var R = 6378.137; // Radius of earth in KM
var dLat = lat2 - lat1 ;
var dLon = lon2 - lon1 ;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d * 1000; // meters
}
//Sandcastle_End
Sandcastle.finishedLoading();
}
Expand Down
9 changes: 9 additions & 0 deletions Source/DataSources/ModelGraphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ define([
this._uriSubscription = undefined;
this._runAnimations = undefined;
this._clampAnimations = undefined;
this._speedupAnimations = undefined;
this._runAnimationsSubscription = undefined;
this._nodeTransformations = undefined;
this._nodeTransformationsSubscription = undefined;
Expand Down Expand Up @@ -190,6 +191,14 @@ define([
*/
runAnimations : createPropertyDescriptor('runAnimations'),

/**
* Gets or sets the numeric Property specifying the relative speed to play the glTF animations.
* @memberof ModelGraphics.prototype
* @type {Property}
* @default 1.0
*/
speedupAnimations : createPropertyDescriptor('speedupAnimations'),
hpinkos marked this conversation as resolved.
Show resolved Hide resolved

/**
* Gets or sets the boolean Property specifying if glTF animations should hold the last pose for time durations with no keyframes.
* @memberof ModelGraphics.prototype
Expand Down
15 changes: 14 additions & 1 deletion Source/DataSources/ModelVisualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ define([
var defaultColorBlendMode = ColorBlendMode.HIGHLIGHT;
var defaultColorBlendAmount = 0.5;
var defaultImageBasedLightingFactor = new Cartesian2(1.0, 1.0);
var defaultSpeedupAnimations = 1.0;

var modelMatrixScratch = new Matrix4();
var nodeMatrixScratch = new Matrix4();
Expand Down Expand Up @@ -138,7 +139,8 @@ define([
animationsRunning : false,
nodeTransformationsScratch : {},
originalNodeMatrixHash : {},
loadFail : false
loadFail : false,
speedup : defaultSpeedupAnimations
};
modelHash[entity.id] = modelData;

Expand All @@ -165,6 +167,7 @@ define([

if (model.ready) {
var runAnimations = Property.getValueOrDefault(modelGraphics._runAnimations, time, true);
var speedup = Property.getValueOrDefault(modelGraphics._speedupAnimations, time, defaultSpeedupAnimations);
if (modelData.animationsRunning !== runAnimations) {
if (runAnimations) {
model.activeAnimations.addAll({
Expand All @@ -176,6 +179,16 @@ define([
modelData.animationsRunning = runAnimations;
}

// Update the speedup property
if (modelData.animationsRunning && modelData.speedup !== speedup) {
var animationLength = model.activeAnimations.length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly is an activeAnimation can an animation become inactive? And if that happens will it's multiplier setting become out of date?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeAnimations is an instance of ModelAnimationCollection, which keeps track of all currently playing animations. Theoretically yes if you remove an animation, and add it back in its multiplier value will be out of sync. There's no way to do this for individual animations at the Entity level though. So I'd say the solution is, if/when we implement that, to set modelData.multiplier to undefined every time an animation is added/removed, (in the same way that ModelAnimationCollection currently keeps track of when the multiplier changes to recompute duration and other things).

Or we could just loop over all animations and re-set the multiplier every frame the way it sets all other properties on model every frame.

for (var animationIndex = 0; animationIndex < animationLength; ++animationIndex) {
OmarShehata marked this conversation as resolved.
Show resolved Hide resolved
var animation = model.activeAnimations.get(animationIndex);
animation.speedup = speedup;
}
modelData.speedup = speedup;
}

// Apply node transformations
var nodeTransformations = Property.getValueOrUndefined(modelGraphics._nodeTransformations, time, modelData.nodeTransformationsScratch);
if (defined(nodeTransformations)) {
Expand Down
13 changes: 12 additions & 1 deletion Source/Scene/ModelAnimation.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
define([
'../Core/defaultValue',
'../Core/defineProperties',
'../Core/defined',
'../Core/DeveloperError',
'../Core/Event',
'../Core/JulianDate',
'./ModelAnimationLoop',
'./ModelAnimationState'
], function(
defaultValue,
defineProperties,
defined,
DeveloperError,
Event,
JulianDate,
ModelAnimationLoop,
Expand Down Expand Up @@ -109,6 +113,7 @@ define([
// Set during animation update
this._computedStartTime = undefined;
this._duration = undefined;
this._speedupChanged = false;
hpinkos marked this conversation as resolved.
Show resolved Hide resolved

// To avoid allocations in ModelAnimationCollection.update
var that = this;
Expand Down Expand Up @@ -200,13 +205,19 @@ define([
* @memberof ModelAnimation.prototype
*
* @type {Number}
* @readonly
*
* @default 1.0
*/
speedup : {
get : function() {
return this._speedup;
},
set : function(value) {
if (defined(value) && (value <= 0.0)) {
throw new DeveloperError('speedup must be greater than zero.');
OmarShehata marked this conversation as resolved.
Show resolved Hide resolved
}
this._speedup = value;
this._speedupChanged = true;
}
},

Expand Down
3 changes: 2 additions & 1 deletion Source/Scene/ModelAnimationCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,9 @@ define([
scheduledAnimation._computedStartTime = JulianDate.addSeconds(defaultValue(scheduledAnimation.startTime, sceneTime), scheduledAnimation.delay, new JulianDate());
}

if (!defined(scheduledAnimation._duration)) {
if (!defined(scheduledAnimation._duration) || scheduledAnimation._speedupChanged) {
scheduledAnimation._duration = runtimeAnimation.stopTime * (1.0 / scheduledAnimation.speedup);
scheduledAnimation._speedupChanged = false;
}

var startTime = scheduledAnimation._computedStartTime;
Expand Down