forked from CesiumGS/cesium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for explicit control over model animations
Cesium currently only supports time based animation. This can be inconvenient if the phase of the animation is related to something other than time (eg distance along a path of an object moving at a variable speed). This came up before in CesiumGS#7361, but the author was persuaded that it was better to use nodeTransformations to explicitly control the model. That was (just) doable with that example, because there were just 3 pairs of wheels, all of which needed the exact same, relatively trivial, transformations. The proposed solution was also cumbersome, relying on modifying `multiplier` on the fly, with the downside that modifying multiplier also reset the phase of the animation. My model has a bicycle chain with 90+ links, two wheels, two chain rings (with different rates of rotation, which are both different again from the rate of rotation of the wheels), two drive arms, and a rider. Using nodeTransformations would be extremely complex, and any changes to the model would require updating all the associated javascript to match the model - and on the site where I use the model, I actually use different models for different types of activity - so I want to just be able to drop in a new model, and have it "just work" (to make it work, I have to ensure that the animation's "duration" in seconds is equal to the distance in meters the model would move during one iteration of the animation). This adds an animationTime property to ModelAnimation. If set, it's used by ModelAnimationCollection.update to determine the localAnimationTime, rather than using the current clock time. I also added a manualAnimation property to ModelAnimationCollection so we can still do the short circuit exit from ModelAnimationCollection.update. The new sandcastle example is just a clone of Time Dynamic Wheels, rewritten to use a more complex model, and the new functionality.
- Loading branch information
markw65
committed
Apr 22, 2022
1 parent
d748986
commit bb93f6c
Showing
7 changed files
with
286 additions
and
39 deletions.
There are no files selected for viewing
Binary file not shown.
172 changes: 172 additions & 0 deletions
172
Apps/Sandcastle/gallery/Manually Controlled Animation.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta | ||
name="viewport" | ||
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" | ||
/> | ||
<meta name="description" content="Manually control a model's animations." /> | ||
<meta name="cesium-sandcastle-labels" content="Showcases" /> | ||
<title>Cesium Demo</title> | ||
<script type="text/javascript" src="../Sandcastle-header.js"></script> | ||
<script | ||
type="text/javascript" | ||
src="../../../Build/CesiumUnminified/Cesium.js" | ||
nomodule | ||
></script> | ||
<script type="module" src="../load-cesium-es6.js"></script> | ||
</head> | ||
<body | ||
class="sandcastle-loading" | ||
data-sandcastle-bucket="bucket-requirejs.html" | ||
> | ||
<style> | ||
@import url(../templates/bucket.css); | ||
</style> | ||
<div id="cesiumContainer" class="fullSize"></div> | ||
<div id="loadingOverlay"><h1>Loading...</h1></div> | ||
<div id="toolbar"></div> | ||
<script id="cesium_sandcastle_script"> | ||
function startup(Cesium) { | ||
"use strict"; | ||
//Sandcastle_Begin | ||
const viewer = new Cesium.Viewer("cesiumContainer", { | ||
shouldAnimate: true, | ||
}); | ||
|
||
//Make sure viewer is at the desired time. | ||
const start = Cesium.JulianDate.fromDate(new Date(2018, 11, 12, 15)); | ||
const totalSeconds = 30; | ||
const stop = Cesium.JulianDate.addSeconds( | ||
start, | ||
totalSeconds, | ||
new Cesium.JulianDate() | ||
); | ||
viewer.clock.startTime = start.clone(); | ||
viewer.clock.stopTime = stop.clone(); | ||
viewer.clock.currentTime = start.clone(); | ||
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; | ||
viewer.timeline.zoomTo(start, stop); | ||
|
||
const wheelRadius = 0.52; //in meters. | ||
const circumference = Math.PI * wheelRadius * 2; | ||
// Create a path for our vehicle by lerping between two positions. | ||
const position = new Cesium.SampledPositionProperty(); | ||
const distance = new Cesium.SampledProperty(Number); | ||
const startPosition = new Cesium.Cartesian3( | ||
-2379556.799372864, | ||
-4665528.205030263, | ||
3628013.106599678 | ||
); | ||
const endPosition = new Cesium.Cartesian3( | ||
-2379603.7074103747, | ||
-4665623.48990283, | ||
3627860.82704567 | ||
); | ||
// A velocity vector property will give us the entity's speed and direction at any given time. | ||
const velocityVectorProperty = new Cesium.VelocityVectorProperty( | ||
position, | ||
false | ||
); | ||
const velocityVector = new Cesium.Cartesian3(); | ||
|
||
const numberOfSamples = 100; | ||
let prevLocation = startPosition; | ||
let totalDistance = 0; | ||
for (let i = 0; i <= numberOfSamples; ++i) { | ||
const factor = i / numberOfSamples; | ||
const time = Cesium.JulianDate.addSeconds( | ||
start, | ||
factor * totalSeconds, | ||
new Cesium.JulianDate() | ||
); | ||
|
||
// Lerp using a non-linear factor so that the vehicle accelerates. | ||
const locationFactor = Math.pow(factor, 2); | ||
const location = Cesium.Cartesian3.lerp( | ||
startPosition, | ||
endPosition, | ||
locationFactor, | ||
new Cesium.Cartesian3() | ||
); | ||
position.addSample(time, location); | ||
distance.addSample( | ||
time, | ||
(totalDistance += Cesium.Cartesian3.distance( | ||
location, | ||
prevLocation | ||
)) | ||
); | ||
prevLocation = location; | ||
} | ||
|
||
function updateSpeedLabel(time, result) { | ||
velocityVectorProperty.getValue(time, velocityVector); | ||
const metersPerSecond = Cesium.Cartesian3.magnitude(velocityVector); | ||
const kmPerHour = Math.round(metersPerSecond * 3.6); | ||
|
||
return `${kmPerHour} km/hr`; | ||
} | ||
|
||
// Add our vehicle model. | ||
const vehiclePrimitive = viewer.scene.primitives.add( | ||
Cesium.Model.fromGltf({ | ||
url: "../../SampleData/models/Elliptigo/elliptigo.glb", | ||
cull: false, | ||
}) | ||
); | ||
const vehicleLabel = viewer.entities.add({ | ||
position: position, | ||
orientation: new Cesium.VelocityOrientationProperty(position), // Automatically set the vehicle's orientation to the direction it's facing. | ||
label: { | ||
text: new Cesium.CallbackProperty(updateSpeedLabel, false), | ||
font: "20px sans-serif", | ||
showBackground: true, | ||
distanceDisplayCondition: new Cesium.DistanceDisplayCondition( | ||
0.0, | ||
100.0 | ||
), | ||
eyeOffset: new Cesium.Cartesian3(0, 2.3, 0), | ||
}, | ||
}); | ||
|
||
vehiclePrimitive.readyPromise.then(function (model) { | ||
model.activeAnimations.addAll({ | ||
loop: Cesium.ModelAnimationLoop.REPEAT, | ||
}); | ||
model.activeAnimations.manualAnimation = true; | ||
const rot = new Cesium.Matrix3(); | ||
viewer.scene.preUpdate.addEventListener(function () { | ||
const time = viewer.clock.currentTime; | ||
const pos = position.getValue(time); | ||
const vel = velocityVectorProperty.getValue(time); | ||
Cesium.Cartesian3.normalize(vel, vel); | ||
Cesium.Transforms.rotationMatrixFromPositionVelocity( | ||
pos, | ||
vel, | ||
viewer.scene.globe.ellipsoid, | ||
rot | ||
); | ||
Cesium.Matrix4.fromRotationTranslation(rot, pos, model.modelMatrix); | ||
const dist = distance.getValue(time); | ||
const animations = model.activeAnimations; | ||
const length = animations.length; | ||
for (let i = 0; i < length; ++i) { | ||
animations.get(i).animationTime = dist; | ||
} | ||
}); | ||
}); | ||
viewer.trackedEntity = vehicleLabel; | ||
vehicleLabel.viewFrom = new Cesium.Cartesian3(-10.0, 7.0, 4.0); | ||
//Sandcastle_End | ||
Sandcastle.finishedLoading(); | ||
} | ||
if (typeof Cesium !== "undefined") { | ||
window.startupCalled = true; | ||
startup(Cesium); | ||
} | ||
</script> | ||
</body> | ||
</html> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters