-
Notifications
You must be signed in to change notification settings - Fork 838
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
Animations #421
Animations #421
Conversation
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the ℹ️ Googlers: Go here for more info. |
454cbbf
to
f5559bf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍 great work!
RobotExpressive by <a href="https://www.patreon.com/quaternius">Tomás Laulhé</a>, | ||
licensed under <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a> | ||
(<a href="https://github.com/mrdoob/three.js/tree/dev/examples/models/gltf/RobotExpressive">source</a>) | ||
|
||
## small_hangar_01 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Horse.glb needs attribution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Attribution added
index.html
Outdated
@@ -149,6 +153,27 @@ <h4>src<a href="#required-for-display">*</a></h4> | |||
<h4>alt</h4> | |||
<p>Configures the model with custom text that will be used to describe the model to viewers who use a screen reader or otherwise depend on additional semantic context to understand what they are viewing.</p> | |||
</li> | |||
<li> | |||
<h4>animated</h4> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think animate
might be a better attribute name than animated
(present vs past tense), although can't think of good prior art (other than "checked", which is past tense, but seems like a different case?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, I forgot: I originally used animate
, but it's a built-in method of HTMLElement
(part of the Web Animations API).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(and yah that's definitely not going to be confusing for folks at all)
😩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been left as is, but if there is another name we like that doesn't overlap with a built-in API, I am open to changing it.
src/features/animation.ts
Outdated
class AnimationModelViewerElement extends ModelViewerElement { | ||
@property({type: Boolean}) animated: boolean = false; | ||
@property({type: Boolean, attribute: 'pause-animation'}) | ||
pauseAnimation: boolean = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pause-animation
seems unused/undocumented (other than in the scenario where it's checked with !animated), is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of pause-animation
, there does seem like there would be a need to reset back to the default/T-Pose, so there is a difference between 'pausing' the animation (freezing in the middle of an animation) as opposed to 'removing' the animation -- of course, I don't think it necessary to solve this in this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is different from stopping the animation. Pause animation is documented, and will be necessary in order to implement @yuinchien 's mocks to spec in #425
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the effect you are describing is achieved when setting animate
to false. Did you have something else in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pause-animation
was not adequately documented, sorry. I added docs for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cdata yes, with pause-animation
and !animate
, both 'freeze' and 'reset' flows are supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After API refactor, to "pause" you must invoke pause()
(there is no corresponding attribute/property). And, to "stop" you subsequently set .currentTime = 0
.
src/features/animation.ts
Outdated
* model. The promise rejects if the currently loaded model is | ||
* changed to something new due to the src attribute changing. | ||
*/ | ||
[$updateModelLoadsPromise]() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Environments has a similar need/way of doing this -- wonder if there's a way to generalize this across all features?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After offline discussion, I'm looking into whether this is necessary or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reduced the complexity of this implementation substantially, and I think it should be fine. Thanks for calling this out!
// NOTE(cdata): If a property changes from values A -> B -> A in the space | ||
// of a microtask, LitElement/UpdatingElement will notify of a change even | ||
// though the value has effectively not changed, so we need to check to make | ||
// sure that the value has actually changed before changing the loaded flag. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it'd be valuable for all properties, not just src
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, although the consequences of src change side-effects occurring unnecessarily seem particularly severe as they could affect all other features.
@@ -53,18 +56,19 @@ export const LoadingMixin = (ModelViewerElement) => { | |||
} | |||
|
|||
get loaded() { | |||
return super.loaded || this[$preloaded]; | |||
return super.loaded || | |||
(this.src && CachingGLTFLoader.hasFinishedLoading(this.src)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method, while probably valuable for tests at least, seems that it'd be more appropriate to live on this.src
, our Model
class (which could just wrap this); CachingGLTFLoader
is an implementation detail that would benefit from continuing being hidden inside of Model, or at least out of features/*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know that I agree. CachingGLTFLoader
was in fact the abstraction that enabled preloading to work in parallel with normal loading via setting src
. Please offer a more concrete proposal for how you would change this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keeping this as-is, but remain open to suggestions. I don't think it's the right answer to conflate preloading and the implementation in the Model
class, because untangling that is what begot CachingGLTFLoader
in the first place.
.catch(() => {}); // Silently ignore exceptions here, they should be | ||
// caught by the invoking function | ||
cache.set(url, loadAttempt); | ||
// window.loadAttempt = loadAttempt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/features/loading.js
Outdated
if (preloaded) { | ||
this.dispatchEvent(new CustomEvent('preload', {detail})); | ||
} else { | ||
loader.preload(this.src) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer await
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll re-evaluate if this depends on asynchronous code flow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I factored this out so that it's wrapped in an async function.
.then(() => { | ||
preloaded.set(url, true); | ||
}) | ||
.catch(() => {}); // Silently ignore exceptions here, they should be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason using .then
instead of await? This will swallow exceptions as indicated, but how would invoking function catch that if they're not thrown?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you invoke then
or catch
in a promise, it does not modify the state of a promise in place. The methods return new promise instances that are resolved differently depending on how you invoked them.
We don't cache the "caught" promise. So, try
/catch
blocks outside of this scope will still work as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yeah, the reason for using then
is that the asynchronous behavior is preferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in #421 (comment) I'll re-evaluate if this is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rewritten with await
.
const scene = await this.loader.load(url); | ||
// If we have pending work due to a previous source change in progress, | ||
// cancel it so that we do not incur a race condition: | ||
if (this[$cancelPendingSourceChange] != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar concept to other features ensuring that the changes are still valid; can that be abstracted so that model can handle it everywhere? No good ideas currently, but this seems like a potential growing source of race conditions throughout features
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree, this problem is fairly unique to the consequences of changing the src
. Let's discuss offline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reviewing, I remain convinced that changing the src
requires special considerations. I'm open to concrete suggestions for refactoring, though.
Tests ended in IE11 timeout, flagging for #433 |
What about |
Although conceptually, we would probably have to introduce an explicit API for pausing + resetting the animation. For example, to "stop" a video.pause();
video.currentTime = 0; WDYT? Should we go for something more like that? I'm also interested to hear what others think of |
I'm good with Chris and I chatted about Overall using |
This all SGTM. I'm going to change the current proposal so that the API tracks closely to
|
PTAL at your leisure! |
// preloaded.set(url, true); | ||
//}) | ||
//.catch(() => {}); // Silently ignore exceptions here, they should be | ||
//// caught by the invoking function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, the API's looking great!
🎉🎉🎉 |
Is there a way to import the array from the gltf file and use it in the component? Because, for me only one of the multiple animations play. rest doesn't! Is this bug already being resolved or yet to ? Also, which is the recommended format for rendering animated 3D models - GLTF or GLB ?? @cdata |
Animations
This change proposes the addition of new API (and related bug fixes) to configure and present animated glTFs with
<model-viewer>
. Morph, skeletal and keyframe should all be supported.New public API introduced by this change:
autoplay
false
animated
changes to false.animation-name
undefined
<model-viewer>
will look for an animation with this name when played. If an animation with that name is found, plays that animation. Otherwise, if an animation with that name is not found, falls back to the first animation (if any).animation-crossfade-duration
300
animation-name
.availableAnimations
[]
paused
true
<model-viewer>
. A<model-viewer>
displaying a model with no animations is always considered paused.currentTime
0
play()
animation-name
, or else falling back to the first animation) if it is available.pause()
pause
<model-viewer>
is paused. The implicit default state of<model-viewer>
is paused, and apause
event will not be dispatched until the<model-viewer>
has been played at least once.play
<model-viewer>
begins playing animations.poster-visibility
event.detail.visible
propertyAdditional highlights
src
attribute quickly<model-viewer>
Examples
autoplay
animation-name="Run"
animationName
on an intervalanimation-name
while pausedFuture work
Fixes #404
Fixes #371