AnimationPlayer* play(Animation* animation, double startTime) { AnimationPlayer* player = timeline->play(animation); player->setStartTime(startTime * 1000); player->update(TimingUpdateOnDemand); return player; }
void AnimationTimeline::serviceAnimations(TimingUpdateReason reason) { TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations"); m_timing->cancelWake(); double timeToNextEffect = std::numeric_limits<double>::infinity(); Vector<RawPtr<AnimationPlayer> > players; players.reserveInitialCapacity(m_playersNeedingUpdate.size()); for (HashSet<RefPtr<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it) players.append(it->get()); std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority); for (size_t i = 0; i < players.size(); ++i) { AnimationPlayer* player = players[i]; if (player->update(reason)) timeToNextEffect = std::min(timeToNextEffect, player->timeToEffectChange()); else m_playersNeedingUpdate.remove(player); } if (timeToNextEffect < s_minimumDelay) m_timing->serviceOnNextFrame(); else if (timeToNextEffect != std::numeric_limits<double>::infinity()) m_timing->wakeAfter(timeToNextEffect - s_minimumDelay); ASSERT(!hasOutdatedAnimationPlayer()); }
void CompositorPendingAnimations::notifyCompositorAnimationStarted(double monotonicAnimationStartTime) { for (size_t i = 0; i < m_waitingForCompositorAnimationStart.size(); ++i) { AnimationPlayer* player = m_waitingForCompositorAnimationStart[i].get(); player->setStartTimeInternal(monotonicAnimationStartTime - player->timeline()->zeroTime(), true); } m_waitingForCompositorAnimationStart.clear(); }
AnimationPlayer* AnimationTimeline::play(AnimationNode* child) { if (!m_document) return 0; AnimationPlayer* player = createAnimationPlayer(child); player->setStartTimeInternal(effectiveTime()); m_document->compositorPendingAnimations().add(player); return player; }
bool CompositorAnimations::startAnimationOnCompositor(const Element& element, int group, double startTime, double timeOffset, const Timing& timing, const AnimationPlayer& player, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate) { ASSERT(startedAnimationIds.isEmpty()); ASSERT(isCandidateForAnimationOnCompositor(timing, element, &player, effect, playerPlaybackRate)); ASSERT(canStartAnimationOnCompositor(element)); const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer(); ASSERT(layer); Vector<OwnPtr<WebCompositorAnimation>> animations; CompositorAnimationsImpl::getAnimationOnCompositor(timing, group, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate); ASSERT(!animations.isEmpty()); for (auto& animation : animations) { int id = animation->id(); if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) { WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); compositorPlayer->addAnimation(animation.leakPtr()); } else if (!layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->addAnimation(animation.release())) { // FIXME: We should know ahead of time whether these animations can be started. for (int startedAnimationId : startedAnimationIds) cancelAnimationOnCompositor(element, player, startedAnimationId); startedAnimationIds.clear(); return false; } startedAnimationIds.append(id); } ASSERT(!startedAnimationIds.isEmpty()); return true; }
const AtomicString CSSAnimations::getAnimationNameForInspector(const AnimationPlayer& player) { for (const auto& it : m_animations) { if (it.value->sequenceNumber() == player.sequenceNumber()) return it.key; } return nullAtom; }
String AnimationTree::get_configuration_warning() const { String warning = Node::get_configuration_warning(); if (!root.is_valid()) { if (warning != String()) { warning += "\n"; } warning += TTR("A root AnimationNode for the graph is not set."); } if (!has_node(animation_player)) { if (warning != String()) { warning += "\n"; } warning += TTR("Path to an AnimationPlayer node containing animations is not set."); return warning; } AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); if (!player) { if (warning != String()) { warning += "\n"; } warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."); return warning; } if (!player->has_node(player->get_root())) { if (warning != String()) { warning += "\n"; } warning += TTR("AnimationPlayer root is not a valid node."); return warning; } return warning; }
// http://w3c.github.io/web-animations/#in-play bool KeyframeEffectReadonly::IsInPlay(const AnimationPlayer& aPlayer) const { if (IsFinishedTransition() || aPlayer.PlayState() == AnimationPlayState::Finished) { return false; } return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase_Active; }
void VisibilityEnabler::_change_node_state(Node *p_node, bool p_enabled) { ERR_FAIL_COND(!nodes.has(p_node)); { RigidBody *rb = Object::cast_to<RigidBody>(p_node); if (rb) rb->set_sleeping(!p_enabled); } { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); if (ap) { ap->set_active(p_enabled); } } }
void VisibilityEnabler2D::_change_node_state(Node *p_node, bool p_enabled) { ERR_FAIL_COND(!nodes.has(p_node)); { RigidBody2D *rb = Object::cast_to<RigidBody2D>(p_node); if (rb) { rb->set_sleeping(!p_enabled); } } { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); if (ap) { ap->set_active(p_enabled); } } { AnimatedSprite *as = Object::cast_to<AnimatedSprite>(p_node); if (as) { if (p_enabled) as->play(); else as->stop(); } } { Particles2D *ps = Object::cast_to<Particles2D>(p_node); if (ps) { ps->set_emitting(p_enabled); } } }
// http://w3c.github.io/web-animations/#current bool KeyframeEffectReadonly::IsCurrent(const AnimationPlayer& aPlayer) const { if (IsFinishedTransition() || aPlayer.PlayState() == AnimationPlayState::Finished) { return false; } ComputedTiming computedTiming = GetComputedTiming(); return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before || computedTiming.mPhase == ComputedTiming::AnimationPhase_Active; }
void CompositorAnimations::attachCompositedLayers(const Element& element, const AnimationPlayer& player) { ASSERT(element.layoutObject()); DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer(); ASSERT(layer); WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); ASSERT(layer->compositedDeprecatedPaintLayerMapping()); compositorPlayer->attachLayer(layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->platformLayer()); }
void CompositorAnimations::cancelIncompatibleAnimationsOnCompositor(const Element& targetElement, const AnimationPlayer& playerToAdd, const AnimationEffect& effectToAdd) { const bool affectsOpacity = effectToAdd.affects(CSSPropertyOpacity); const bool affectsTransform = effectToAdd.affects(CSSPropertyTransform); const bool affectsFilter = effectToAdd.affects(CSSPropertyWebkitFilter); if (!targetElement.hasAnimations()) return; ElementAnimations* elementAnimations = targetElement.elementAnimations(); ASSERT(elementAnimations); for (const auto& entry : elementAnimations->players()) { AnimationPlayer* attachedPlayer = entry.key; if (!considerPlayerAsIncompatible(*attachedPlayer, playerToAdd)) continue; if ((affectsOpacity && attachedPlayer->affects(targetElement, CSSPropertyOpacity)) || (affectsTransform && attachedPlayer->affects(targetElement, CSSPropertyTransform)) || (affectsFilter && attachedPlayer->affects(targetElement, CSSPropertyWebkitFilter))) attachedPlayer->cancelAnimationOnCompositor(); } }
Vector<String> AnimationTreeEditor::get_animation_list() { if (!singleton->is_visible()) { return Vector<String>(); } AnimationTree *tree = singleton->tree; if (!tree || !tree->has_node(tree->get_animation_player())) return Vector<String>(); AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); if (!ap) return Vector<String>(); List<StringName> anims; ap->get_animation_list(&anims); Vector<String> ret; for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { ret.push_back(E->get()); } return ret; }
void AnimationTreePlayer::_update_sources() { if (master==NodePath()) return; if (!is_inside_tree()) return; Node *m = get_node(master); if (!m) { master=NodePath(); ERR_FAIL_COND(!m); } AnimationPlayer *ap = m->cast_to<AnimationPlayer>(); if (!ap) { master=NodePath(); ERR_FAIL_COND(!ap); } for (Map<StringName,NodeBase*>::Element *E=node_map.front();E;E=E->next()) { if (E->get()->type==NODE_ANIMATION) { AnimationNode *an = static_cast<AnimationNode*>(E->get()); if (an->from!="") { an->animation = ap->get_animation(an->from); } } } }
void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, const AnimationPlayer& player, int id) { if (!canStartAnimationOnCompositor(element)) { // When an element is being detached, we cancel any associated // AnimationPlayers for CSS animations. But by the time we get // here the mapping will have been removed. // FIXME: Defer remove/pause operations until after the // compositing update. return; } if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) { WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); compositorPlayer->removeAnimation(id); } else { toLayoutBoxModelObject(element.layoutObject())->layer()->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->removeAnimation(id); } }
void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, const AnimationPlayer& player, int id, double pauseTime) { // FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date. // https://code.google.com/p/chromium/issues/detail?id=339847 DisableCompositingQueryAsserts disabler; if (!canStartAnimationOnCompositor(element)) { ASSERT_NOT_REACHED(); return; } if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) { WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); compositorPlayer->pauseAnimation(id, pauseTime); } else { toLayoutBoxModelObject(element.layoutObject())->layer()->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime); } }
bool CompositorAnimations::canAttachCompositedLayers(const Element& element, const AnimationPlayer& player) { if (!RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) return false; if (!player.compositorPlayer()) return false; if (!element.layoutObject() || !element.layoutObject()->isBoxModelObject()) return false; DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer(); if (!layer || !layer->isAllowedToQueryCompositingState() || !layer->compositedDeprecatedPaintLayerMapping() || !layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()) return false; if (!layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->platformLayer()) return false; return true; }
void AnimController::freeze() { if (!_freezing) { _freezing = true; status_cache.clear(); for (Map<StringName, Node*>::Element *E = anim_nodes.front(); E; E=E->next()) { AnimationPlayer *player = E->get()->cast_to<AnimationPlayer>(); if (player->is_playing()) { AnimationStatus status; status.name = player->get_current_animation(); status.position = player->get_current_animation_pos(); status.player = player; status_cache.push_back(status); player->stop(false); player->set_active(false); } } } }
void AnimationNodeBlendTreeEditor::_update_graph() { if (updating) return; graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE); if (blend_tree->get_parent().is_valid()) { goto_parent->show(); } else { goto_parent->hide(); } graph->clear_connections(); //erase all nodes for (int i = 0; i < graph->get_child_count(); i++) { if (Object::cast_to<GraphNode>(graph->get_child(i))) { memdelete(graph->get_child(i)); i--; } } animations.clear(); List<StringName> nodes; blend_tree->get_node_list(&nodes); for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { GraphNode *node = memnew(GraphNode); graph->add_child(node); Ref<AnimationNode> agnode = blend_tree->get_node(E->get()); if (!agnode->is_connected("changed", this, "_node_changed")) { agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); } node->set_offset(agnode->get_position() * EDSCALE); node->set_title(agnode->get_caption()); node->set_name(E->get()); int base = 0; if (String(E->get()) != "output") { LineEdit *name = memnew(LineEdit); name->set_text(E->get()); name->set_expand_to_text_length(true); node->add_child(name); node->set_slot(0, false, 0, Color(), true, 0, get_color("font_color", "Label")); name->connect("text_entered", this, "_node_renamed", varray(agnode)); name->connect("focus_exited", this, "_node_renamed_focus_out", varray(name, agnode)); base = 1; node->set_show_close_button(true); node->connect("close_request", this, "_delete_request", varray(E->get()), CONNECT_DEFERRED); } for (int i = 0; i < agnode->get_input_count(); i++) { Label *in_name = memnew(Label); node->add_child(in_name); in_name->set_text(agnode->get_input_name(i)); node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color()); } node->connect("dragged", this, "_node_dragged", varray(agnode)); if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); open_in_editor->set_icon(get_icon("Edit", "EditorIcons")); node->add_child(open_in_editor); open_in_editor->connect("pressed", this, "_open_in_editor", varray(E->get()), CONNECT_DEFERRED); open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER); } if (agnode->has_filter()) { node->add_child(memnew(HSeparator)); Button *edit_filters = memnew(Button); edit_filters->set_text(TTR("Edit Filters")); edit_filters->set_icon(get_icon("AnimationFilter", "EditorIcons")); node->add_child(edit_filters); edit_filters->connect("pressed", this, "_edit_filters", varray(E->get()), CONNECT_DEFERRED); edit_filters->set_h_size_flags(SIZE_SHRINK_CENTER); } Ref<AnimationNodeAnimation> anim = agnode; if (anim.is_valid()) { MenuButton *mb = memnew(MenuButton); mb->set_text(anim->get_animation()); mb->set_icon(get_icon("Animation", "EditorIcons")); Array options; node->add_child(memnew(HSeparator)); node->add_child(mb); ProgressBar *pb = memnew(ProgressBar); AnimationTree *player = anim->get_tree(); if (player->has_node(player->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player())); if (ap) { List<StringName> anims; ap->get_animation_list(&anims); for (List<StringName>::Element *F = anims.front(); F; F = F->next()) { mb->get_popup()->add_item(F->get()); options.push_back(F->get()); } if (ap->has_animation(anim->get_animation())) { pb->set_max(ap->get_animation(anim->get_animation())->get_length()); } } } pb->set_percent_visible(false); animations[E->get()] = pb; node->add_child(pb); mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { HBoxContainer *play_stop = memnew(HBoxContainer); play_stop->add_spacer(); Button *play = memnew(Button); play->set_icon(get_icon("Play", "EditorIcons")); play->connect("pressed", this, "_oneshot_start", varray(E->get()), CONNECT_DEFERRED); play_stop->add_child(play); Button *stop = memnew(Button); stop->set_icon(get_icon("Stop", "EditorIcons")); stop->connect("pressed", this, "_oneshot_stop", varray(E->get()), CONNECT_DEFERRED); play_stop->add_child(stop); play_stop->add_spacer(); node->add_child(play_stop); } } List<AnimationNodeBlendTree::NodeConnection> connections; blend_tree->get_node_connections(&connections); for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = connections.front(); E; E = E->next()) { StringName from = E->get().output_node; StringName to = E->get().input_node; int to_idx = E->get().input_index; graph->connect_node(from, 0, to, to_idx); } }
Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Set<Ref<ImageTexture> >& image_map) { // children first.. for(int i=0;i<p_node->get_child_count();i++) { Node *r = _fix_node(p_node->get_child(i),p_root,collision_map,p_flags,image_map); if (!r) { print_line("was erased.."); i--; //was erased } } String name = p_node->get_name(); bool isroot = p_node==p_root; if (!isroot && p_flags&SCENE_FLAG_REMOVE_NOIMP && _teststr(name,"noimp")) { memdelete(p_node); return NULL; } { List<PropertyInfo> pl; p_node->get_property_list(&pl); for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) { _find_resources(p_node->get(E->get().name),image_map); } } } if (p_flags&SCENE_FLAG_CREATE_BILLBOARDS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); bool bb=false; if ((_teststr(name,"bb"))) { bb=true; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) { bb=true; } if (bb) { mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } } } } if (p_flags&SCENE_FLAG_REMOVE_NOIMP && p_node->cast_to<AnimationPlayer>()) { //remove animations referencing non-importable nodes AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>(); List<StringName> anims; ap->get_animation_list(&anims); for(List<StringName>::Element *E=anims.front();E;E=E->next()) { Ref<Animation> anim=ap->get_animation(E->get()); ERR_CONTINUE(anim.is_null()); for(int i=0;i<anim->get_track_count();i++) { NodePath path = anim->track_get_path(i); for(int j=0;j<path.get_name_count();j++) { String node = path.get_name(j); if (_teststr(node,"noimp")) { anim->remove_track(i); i--; break; } } } } } if (p_flags&SCENE_FLAG_CREATE_IMPOSTORS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); String str; if ((_teststr(name,"imp"))) { str=name; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) { str=mi->get_mesh()->get_name(); } if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); String d=str.substr(str.find("imp")+3,str.length()); if (d!="") { if ((d[0]<'0' || d[0]>'9')) d=d.substr(1,d.length()); if (d.length() && d[0]>='0' && d[0]<='9') { float dist = d.to_double(); mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true); mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true); mi->set_draw_range_begin(dist); mi->set_draw_range_end(100000); mip->set_draw_range_begin(0); mip->set_draw_range_end(dist); if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } } } } } } if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); String str; if ((_teststr(name,"lod"))) { str=name; } else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"lod"))) { str=mi->get_mesh()->get_name(); } if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>(); String d=str.substr(str.find("lod")+3,str.length()); if (d!="") { if ((d[0]<'0' || d[0]>'9')) d=d.substr(1,d.length()); if (d.length() && d[0]>='0' && d[0]<='9') { float dist = d.to_double(); mi->set_draw_range_begin(dist); mi->set_draw_range_end(100000); mip->set_draw_range_begin(0); mip->set_draw_range_end(dist); /*if (mi->get_mesh().is_valid()) { Ref<Mesh> m = mi->get_mesh(); for(int i=0;i<m->get_surface_count();i++) { Ref<FixedMaterial> fm = m->surface_get_material(i); if (fm.is_valid()) { fm->set_flag(Material::FLAG_UNSHADED,true); fm->set_flag(Material::FLAG_DOUBLE_SIDED,true); fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true); fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); } } }*/ } } } } if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); Node * col = mi->create_trimesh_collision_node(); ERR_FAIL_COND_V(!col,NULL); col->set_name(_fixstr(name,"colonly")); col->cast_to<Spatial>()->set_transform(mi->get_transform()); p_node->replace_by(col); memdelete(p_node); p_node=col; } else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS &&_teststr(name,"col") && p_node->cast_to<MeshInstance>()) { MeshInstance *mi = p_node->cast_to<MeshInstance>(); mi->set_name(_fixstr(name,"col")); mi->create_trimesh_collision(); } else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); BSP_Tree bsptree(faces); Ref<RoomBounds> area = memnew( RoomBounds ); area->set_bounds(faces); area->set_geometry_hint(faces); Room * room = memnew( Room ); room->set_name(_fixstr(name,"room")); room->set_transform(mi->get_transform()); room->set_room(area); p_node->replace_by(room); memdelete(p_node); p_node=room; } else if (p_flags&SCENE_FLAG_CREATE_ROOMS &&_teststr(name,"room")) { if (isroot) return p_node; Spatial *dummy = p_node->cast_to<Spatial>(); ERR_FAIL_COND_V(!dummy,NULL); Room * room = memnew( Room ); room->set_name(_fixstr(name,"room")); room->set_transform(dummy->get_transform()); p_node->replace_by(room); memdelete(p_node); p_node=room; room->compute_room_from_subtree(); } else if (p_flags&SCENE_FLAG_CREATE_PORTALS &&_teststr(name,"portal") && p_node->cast_to<MeshInstance>()) { if (isroot) return p_node; MeshInstance *mi = p_node->cast_to<MeshInstance>(); DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID); ERR_FAIL_COND_V(faces.size()==0,NULL); //step 1 compute the plane Set<Vector3> points; Plane plane; Vector3 center; for(int i=0;i<faces.size();i++) { Face3 f = faces.get(i); Plane p = f.get_plane(); plane.normal+=p.normal; plane.d+=p.d; for(int i=0;i<3;i++) { Vector3 v = f.vertex[i].snapped(0.01); if (!points.has(v)) { points.insert(v); center+=v; } } } plane.normal.normalize(); plane.d/=faces.size(); center/=points.size(); //step 2, create points Transform t; t.basis.from_z(plane.normal); t.basis.transpose(); t.origin=center; Vector<Point2> portal_points; for(Set<Vector3>::Element *E=points.front();E;E=E->next()) { Vector3 local = t.xform_inv(E->get()); portal_points.push_back(Point2(local.x,local.y)); } // step 3 bubbly sort points int swaps=0; do { swaps=0; for(int i=0;i<portal_points.size()-1;i++) { float a = portal_points[i].atan2(); float b = portal_points[i+1].atan2(); if (a>b) { SWAP( portal_points[i], portal_points[i+1] ); swaps++; } } } while(swaps); Portal *portal = memnew( Portal ); portal->set_shape(portal_points); portal->set_transform( mi->get_transform() * t); p_node->replace_by(portal); memdelete(p_node); p_node=portal; } else if (p_node->cast_to<MeshInstance>()) { //last attempt, maybe collision insde the mesh data MeshInstance *mi = p_node->cast_to<MeshInstance>(); Ref<Mesh> mesh = mi->get_mesh(); if (!mesh.is_null()) { if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(mesh->get_name(),"col")) { mesh->set_name( _fixstr(mesh->get_name(),"col") ); Ref<Shape> shape; if (collision_map.has(mesh)) { shape = collision_map[mesh]; } else { shape = mesh->create_trimesh_shape(); if (!shape.is_null()) collision_map[mesh]=shape; } if (!shape.is_null()) { #if 0 StaticBody* static_body = memnew( StaticBody ); ERR_FAIL_COND_V(!static_body,NULL); static_body->set_name( String(mesh->get_name()) + "_col" ); shape->set_name(static_body->get_name()); static_body->add_shape(shape); mi->add_child(static_body); if (mi->get_owner()) static_body->set_owner( mi->get_owner() ); #endif } } for(int i=0;i<mesh->get_surface_count();i++) { Ref<FixedMaterial> fm = mesh->surface_get_material(i); if (fm.is_valid()) { String name = fm->get_name(); if (_teststr(name,"alpha")) { fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); name=_fixstr(name,"alpha"); } if (_teststr(name,"vcol")) { fm->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); name=_fixstr(name,"vcol"); } fm->set_name(name); } } } } return p_node; }
nsIStyleRule* nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement) { // FIXME (bug 960465): This test should go away. if (!mPresContext->RestyleManager()->IsProcessingAnimationStyleChange()) { if (!mPresContext->IsDynamic()) { // For print or print preview, ignore animations. return nullptr; } // Everything that causes our animation data to change triggers a // style change, which in turn triggers a non-animation restyle. // Likewise, when we initially construct frames, we're not in a // style change, but also not in an animation restyle. const nsStyleDisplay* disp = aStyleContext->StyleDisplay(); AnimationPlayerCollection* collection = GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), false); if (!collection && disp->mAnimationNameCount == 1 && disp->mAnimations[0].GetName().IsEmpty()) { return nullptr; } // build the animations list dom::AnimationTimeline* timeline = aElement->OwnerDoc()->Timeline(); AnimationPlayerPtrArray newPlayers; BuildAnimations(aStyleContext, aElement, timeline, newPlayers); if (newPlayers.IsEmpty()) { if (collection) { collection->Destroy(); } return nullptr; } if (collection) { collection->mStyleRule = nullptr; collection->mStyleRuleRefreshTime = TimeStamp(); collection->UpdateAnimationGeneration(mPresContext); // Copy over the start times and (if still paused) pause starts // for each animation (matching on name only) that was also in the // old list of animations. // This means that we honor dynamic changes, which isn't what the // spec says to do, but WebKit seems to honor at least some of // them. See // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html // In order to honor what the spec said, we'd copy more data over // (or potentially optimize BuildAnimations to avoid rebuilding it // in the first place). if (!collection->mPlayers.IsEmpty()) { for (size_t newIdx = newPlayers.Length(); newIdx-- != 0;) { AnimationPlayer* newPlayer = newPlayers[newIdx]; // Find the matching animation with this name in the old list // of animations. We iterate through both lists in a backwards // direction which means that if there are more animations in // the new list of animations with a given name than in the old // list, it will be the animations towards the of the beginning of // the list that do not match and are treated as new animations. nsRefPtr<CSSAnimationPlayer> oldPlayer; size_t oldIdx = collection->mPlayers.Length(); while (oldIdx-- != 0) { CSSAnimationPlayer* a = collection->mPlayers[oldIdx]->AsCSSAnimationPlayer(); MOZ_ASSERT(a, "All players in the CSS Animation collection should" " be CSSAnimationPlayer objects"); if (a->Name() == newPlayer->Name()) { oldPlayer = a; break; } } if (!oldPlayer) { continue; } // Update the old from the new so we can keep the original object // identity (and any expando properties attached to it). if (oldPlayer->GetSource() && newPlayer->GetSource()) { Animation* oldAnim = oldPlayer->GetSource(); Animation* newAnim = newPlayer->GetSource(); oldAnim->Timing() = newAnim->Timing(); oldAnim->Properties() = newAnim->Properties(); } // Reset compositor state so animation will be re-synchronized. oldPlayer->ClearIsRunningOnCompositor(); // Handle changes in play state. // CSSAnimationPlayer takes care of override behavior so that, // for example, if the author has called pause(), that will // override the animation-play-state. // (We should check newPlayer->IsStylePaused() but that requires // downcasting to CSSAnimationPlayer and we happen to know that // newPlayer will only ever be paused by calling PauseFromStyle // making IsPaused synonymous in this case.) if (!oldPlayer->IsStylePaused() && newPlayer->IsPaused()) { oldPlayer->PauseFromStyle(); } else if (oldPlayer->IsStylePaused() && !newPlayer->IsPaused()) { oldPlayer->PlayFromStyle(); } // Replace new animation with the (updated) old one and remove the // old one from the array so we don't try to match it any more. // // Although we're doing this while iterating this is safe because // we're not changing the length of newPlayers and we've finished // iterating over the list of old iterations. newPlayer = nullptr; newPlayers.ReplaceElementAt(newIdx, oldPlayer); collection->mPlayers.RemoveElementAt(oldIdx); } } } else { collection = GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), true); } collection->mPlayers.SwapElements(newPlayers); collection->mNeedsRefreshes = true; collection->Tick(); TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); UpdateStyleAndEvents(collection, refreshTime, EnsureStyleRule_IsNotThrottled); // We don't actually dispatch the mPendingEvents now. We'll either // dispatch them the next time we get a refresh driver notification // or the next time somebody calls // nsPresShell::FlushPendingNotifications. if (!mPendingEvents.IsEmpty()) { mPresContext->Document()->SetNeedStyleFlush(); } } return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { if (!m_pendingUpdate) { m_previousActiveInterpolationsForAnimations.clear(); return; } OwnPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations()); for (Vector<AtomicString>::const_iterator iter = update->cancelledAnimationNames().begin(); iter != update->cancelledAnimationNames().end(); ++iter) { RefPtr<AnimationPlayer> player = m_animations.take(*iter); player->cancel(); player->update(TimingUpdateOnDemand); } for (Vector<AtomicString>::const_iterator iter = update->animationsWithPauseToggled().begin(); iter != update->animationsWithPauseToggled().end(); ++iter) { AnimationPlayer* player = m_animations.get(*iter); if (player->paused()) player->unpause(); else player->pause(); if (player->outdated()) player->update(TimingUpdateOnDemand); } for (Vector<CSSAnimationUpdate::NewAnimation>::const_iterator iter = update->newAnimations().begin(); iter != update->newAnimations().end(); ++iter) { const InertAnimation* inertAnimation = iter->animation.get(); OwnPtr<AnimationEventDelegate> eventDelegate = adoptPtr(new AnimationEventDelegate(element, iter->name)); RefPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release()); RefPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get()); if (inertAnimation->paused()) player->pause(); player->update(TimingUpdateOnDemand); m_animations.set(iter->name, player.get()); } // Transitions that are run on the compositor only update main-thread state // lazily. However, we need the new state to know what the from state shoud // be when transitions are retargeted. Instead of triggering complete style // recalculation, we find these cases by searching for new transitions that // have matching cancelled animation property IDs on the compositor. HashMap<CSSPropertyID, std::pair<RefPtr<Animation>, double> > retargetedCompositorTransitions; for (HashSet<CSSPropertyID>::iterator iter = update->cancelledTransitions().begin(); iter != update->cancelledTransitions().end(); ++iter) { CSSPropertyID id = *iter; ASSERT(m_transitions.contains(id)); RefPtr<AnimationPlayer> player = m_transitions.take(id).player; player->cancel(); player->update(TimingUpdateOnDemand); } for (CSSAnimationUpdate::NewTransitionMap::const_iterator iter = update->newTransitions().begin(); iter != update->newTransitions().end(); ++iter) { const CSSAnimationUpdate::NewTransition& newTransition = iter->value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertAnimation* inertAnimation = newTransition.animation.get(); OwnPtr<TransitionEventDelegate> eventDelegate = adoptPtr(new TransitionEventDelegate(element, newTransition.eventId)); RefPtr<AnimationEffect> effect = inertAnimation->effect(); RefPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release()); RefPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get()); player->update(TimingUpdateOnDemand); runningTransition.player = player; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); } }
void CSSAnimations::maybeApplyPendingUpdate(Element* element) { if (!m_pendingUpdate) { m_previousActiveInterpolationsForAnimations.clear(); return; } OwnPtrWillBeRawPtr<CSSAnimationUpdate> update = m_pendingUpdate.release(); m_previousActiveInterpolationsForAnimations.swap(update->activeInterpolationsForAnimations()); // FIXME: cancelling, pausing, unpausing animations all query compositingState, which is not necessarily up to date here // since we call this from recalc style. // https://code.google.com/p/chromium/issues/detail?id=339847 DisableCompositingQueryAsserts disabler; for (const AtomicString& animationName : update->cancelledAnimationNames()) { RefPtrWillBeRawPtr<AnimationPlayer> player = m_animations.take(animationName); player->cancel(); player->update(TimingUpdateOnDemand); } for (const AtomicString& animationName : update->animationsWithPauseToggled()) { AnimationPlayer* player = m_animations.get(animationName); if (player->paused()) player->unpause(); else player->pause(); if (player->outdated()) player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newAnimations()) { const InertAnimation* inertAnimation = entry.animation.get(); OwnPtrWillBeRawPtr<AnimationEventDelegate> eventDelegate = adoptPtrWillBeNoop(new AnimationEventDelegate(element, entry.name)); RefPtrWillBeRawPtr<Animation> animation = Animation::create(element, inertAnimation->effect(), inertAnimation->specifiedTiming(), Animation::DefaultPriority, eventDelegate.release()); animation->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(animation.get()); if (inertAnimation->paused()) player->pause(); player->update(TimingUpdateOnDemand); m_animations.set(entry.name, player.get()); } // Transitions that are run on the compositor only update main-thread state // lazily. However, we need the new state to know what the from state shoud // be when transitions are retargeted. Instead of triggering complete style // recalculation, we find these cases by searching for new transitions that // have matching cancelled animation property IDs on the compositor. WillBeHeapHashMap<CSSPropertyID, std::pair<RefPtrWillBeMember<Animation>, double>> retargetedCompositorTransitions; for (CSSPropertyID id : update->cancelledTransitions()) { ASSERT(m_transitions.contains(id)); RefPtrWillBeRawPtr<AnimationPlayer> player = m_transitions.take(id).player; Animation* animation = toAnimation(player->source()); if (animation->hasActiveAnimationsOnCompositor(id) && update->newTransitions().find(id) != update->newTransitions().end()) retargetedCompositorTransitions.add(id, std::pair<RefPtrWillBeMember<Animation>, double>(animation, player->startTimeInternal())); player->cancel(); player->update(TimingUpdateOnDemand); } for (const auto& entry : update->newTransitions()) { const CSSAnimationUpdate::NewTransition& newTransition = entry.value; RunningTransition runningTransition; runningTransition.from = newTransition.from; runningTransition.to = newTransition.to; CSSPropertyID id = newTransition.id; InertAnimation* inertAnimation = newTransition.animation.get(); OwnPtrWillBeRawPtr<TransitionEventDelegate> eventDelegate = adoptPtrWillBeNoop(new TransitionEventDelegate(element, newTransition.eventId)); RefPtrWillBeRawPtr<AnimationEffect> effect = inertAnimation->effect(); if (retargetedCompositorTransitions.contains(id)) { const std::pair<RefPtrWillBeMember<Animation>, double>& oldTransition = retargetedCompositorTransitions.get(id); RefPtrWillBeRawPtr<Animation> oldAnimation = oldTransition.first; double oldStartTime = oldTransition.second; double inheritedTime = isNull(oldStartTime) ? 0 : element->document().timeline().currentTimeInternal() - oldStartTime; AnimatableValueKeyframeEffectModel* oldEffect = toAnimatableValueKeyframeEffectModel(inertAnimation->effect()); const KeyframeVector& frames = oldEffect->getFrames(); AnimatableValueKeyframeVector newFrames; newFrames.append(toAnimatableValueKeyframe(frames[0]->clone().get())); newFrames.append(toAnimatableValueKeyframe(frames[1]->clone().get())); newFrames[0]->clearPropertyValue(id); RefPtrWillBeRawPtr<InertAnimation> inertAnimationForSampling = InertAnimation::create(oldAnimation->effect(), oldAnimation->specifiedTiming(), false); OwnPtrWillBeRawPtr<WillBeHeapVector<RefPtrWillBeMember<Interpolation>>> sample = inertAnimationForSampling->sample(inheritedTime); ASSERT(sample->size() == 1); newFrames[0]->setPropertyValue(id, toLegacyStyleInterpolation(sample->at(0).get())->currentValue()); effect = AnimatableValueKeyframeEffectModel::create(newFrames); } RefPtrWillBeRawPtr<Animation> transition = Animation::create(element, effect, inertAnimation->specifiedTiming(), Animation::TransitionPriority, eventDelegate.release()); transition->setName(inertAnimation->name()); RefPtrWillBeRawPtr<AnimationPlayer> player = element->document().timeline().createAnimationPlayer(transition.get()); player->update(TimingUpdateOnDemand); runningTransition.player = player; m_transitions.set(id, runningTransition); ASSERT(id != CSSPropertyInvalid); blink::Platform::current()->histogramSparse("WebCore.Animation.CSSProperties", UseCounter::mapCSSPropertyIdToCSSSampleIdForHistogram(id)); } }
void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate* update, const Element* animatingElement, Element& element, const RenderStyle& style, RenderStyle* parentStyle, StyleResolver* resolver) { const ActiveAnimations* activeAnimations = animatingElement ? animatingElement->activeAnimations() : nullptr; #if !ENABLE(ASSERT) // If we're in an animation style change, no animations can have started, been cancelled or changed play state. // When ASSERT is enabled, we verify this optimization. if (activeAnimations && activeAnimations->isAnimationStyleChange()) return; #endif const CSSAnimationData* animationData = style.animations(); const CSSAnimations* cssAnimations = activeAnimations ? &activeAnimations->cssAnimations() : nullptr; HashSet<AtomicString> inactive; if (cssAnimations) { for (const auto& entry : cssAnimations->m_animations) inactive.add(entry.key); } if (style.display() != NONE) { for (size_t i = 0; animationData && i < animationData->nameList().size(); ++i) { AtomicString animationName(animationData->nameList()[i]); if (animationName == CSSAnimationData::initialName()) continue; bool isPaused = CSSTimingData::getRepeated(animationData->playStateList(), i) == AnimPlayStatePaused; // Keyframes and animation properties are snapshotted when the // animation starts, so we don't need to track changes to these, // with the exception of play-state. if (cssAnimations) { AnimationMap::const_iterator existing(cssAnimations->m_animations.find(animationName)); if (existing != cssAnimations->m_animations.end()) { inactive.remove(animationName); AnimationPlayer* player = existing->value.get(); if (isPaused != player->paused()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->toggleAnimationPaused(animationName); } continue; } } Timing timing = animationData->convertToTiming(i); RefPtr<TimingFunction> keyframeTimingFunction = timing.timingFunction; timing.timingFunction = Timing::defaults().timingFunction; AnimatableValueKeyframeVector resolvedKeyframes; resolveKeyframes(resolver, animatingElement, element, style, parentStyle, animationName, keyframeTimingFunction.get(), resolvedKeyframes); if (!resolvedKeyframes.isEmpty()) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->startAnimation(animationName, InertAnimation::create(AnimatableValueKeyframeEffectModel::create(resolvedKeyframes), timing, isPaused)); } } } ASSERT(inactive.isEmpty() || cssAnimations); for (const AtomicString& animationName : inactive) { ASSERT(!activeAnimations || !activeAnimations->isAnimationStyleChange()); update->cancelAnimation(animationName, *cssAnimations->m_animations.get(animationName)); } }
void AnimationTree::_process_graph(float p_delta) { _update_properties(); //if properties need updating, update them //check all tracks, see if they need modification root_motion_transform = Transform(); if (!root.is_valid()) { ERR_PRINT("AnimationTree: root AnimationNode is not set, disabling playback."); set_active(false); cache_valid = false; return; } if (!has_node(animation_player)) { ERR_PRINT("AnimationTree: no valid AnimationPlayer path set, disabling playback"); set_active(false); cache_valid = false; return; } AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); ObjectID current_animation_player = 0; if (player) { current_animation_player = player->get_instance_id(); } if (last_animation_player != current_animation_player) { if (last_animation_player) { Object *old_player = ObjectDB::get_instance(last_animation_player); if (old_player) { old_player->disconnect("caches_cleared", this, "_clear_caches"); } } if (player) { player->connect("caches_cleared", this, "_clear_caches"); } last_animation_player = current_animation_player; } if (!player) { ERR_PRINT("AnimationTree: path points to a node not an AnimationPlayer, disabling playback"); set_active(false); cache_valid = false; return; } if (!cache_valid) { if (!_update_caches(player)) { return; } } { //setup process_pass++; state.valid = true; state.invalid_reasons = ""; state.animation_states.clear(); //will need to be re-created state.valid = true; state.player = player; state.last_pass = process_pass; state.tree = this; // root source blends root->blends.resize(state.track_count); float *src_blendsw = root->blends.ptrw(); for (int i = 0; i < state.track_count; i++) { src_blendsw[i] = 1.0; //by default all go to 1 for the root input } } //process { if (started) { //if started, seek root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, NULL, &state, 0, true, Vector<StringName>()); started = false; } root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, NULL, &state, p_delta, false, Vector<StringName>()); } if (!state.valid) { return; //state is not valid. do nothing. } //apply value/transform/bezier blends to track caches and execute method/audio/animation tracks { bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); for (List<AnimationNode::AnimationState>::Element *E = state.animation_states.front(); E; E = E->next()) { const AnimationNode::AnimationState &as = E->get(); Ref<Animation> a = as.animation; float time = as.time; float delta = as.delta; bool seeked = as.seeked; for (int i = 0; i < a->get_track_count(); i++) { NodePath path = a->track_get_path(i); ERR_CONTINUE(!track_cache.has(path)); TrackCache *track = track_cache[path]; if (track->type != a->track_get_type(i)) { continue; //may happen should not } track->root_motion = root_motion_track == path; ERR_CONTINUE(!state.track_map.has(path)); int blend_idx = state.track_map[path]; ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); float blend = (*as.track_blends)[blend_idx]; if (blend < CMP_EPSILON) continue; //nothing to blend switch (track->type) { case Animation::TYPE_TRANSFORM: { TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (track->root_motion) { if (t->process_pass != process_pass) { t->process_pass = process_pass; t->loc = Vector3(); t->rot = Quat(); t->rot_blend_accum = 0; t->scale = Vector3(1, 1, 1); } float prev_time = time - delta; if (prev_time < 0) { if (!a->has_loop()) { prev_time = 0; } else { prev_time = a->get_length() + prev_time; } } Vector3 loc[2]; Quat rot[2]; Vector3 scale[2]; if (prev_time > time) { Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); if (err != OK) { continue; } a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]); t->loc += (loc[1] - loc[0]) * blend; t->scale += (scale[1] - scale[0]) * blend; Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); if (err != OK) { continue; } a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]); t->loc += (loc[1] - loc[0]) * blend; t->scale += (scale[1] - scale[0]) * blend; Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } else { Vector3 loc; Quat rot; Vector3 scale; Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale); //ERR_CONTINUE(err!=OK); //used for testing, should be removed if (t->process_pass != process_pass) { t->process_pass = process_pass; t->loc = loc; t->rot = rot; t->rot_blend_accum = 0; t->scale = scale; } if (err != OK) continue; t->loc = t->loc.linear_interpolate(loc, blend); if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; } else { float rot_total = t->rot_blend_accum + blend; t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized(); t->rot_blend_accum = rot_total; } t->scale = t->scale.linear_interpolate(scale, blend); } } break; case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek Variant value = a->value_track_interpolate(i, time); if (value == Variant()) continue; if (t->process_pass != process_pass) { t->value = value; t->process_pass = process_pass; } Variant::interpolate(t->value, value, blend, t->value); } else if (delta != 0) { List<int> indices; a->value_track_get_key_indices(i, time, delta, &indices); for (List<int>::Element *F = indices.front(); F; F = F->next()) { Variant value = a->track_get_key_value(i, F->get()); t->object->set_indexed(t->subpath, value); } } } break; case Animation::TYPE_METHOD: { if (delta == 0) { continue; } TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); List<int> indices; a->method_track_get_key_indices(i, time, delta, &indices); for (List<int>::Element *F = indices.front(); F; F = F->next()) { StringName method = a->method_track_get_name(i, F->get()); Vector<Variant> params = a->method_track_get_params(i, F->get()); int s = params.size(); ERR_CONTINUE(s > VARIANT_ARG_MAX); if (can_call) { t->object->call_deferred( method, s >= 1 ? params[0] : Variant(), s >= 2 ? params[1] : Variant(), s >= 3 ? params[2] : Variant(), s >= 4 ? params[3] : Variant(), s >= 5 ? params[4] : Variant()); } } } break; case Animation::TYPE_BEZIER: { TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); float bezier = a->bezier_track_interpolate(i, time); if (t->process_pass != process_pass) { t->value = bezier; t->process_pass = process_pass; } t->value = Math::lerp(t->value, bezier, blend); } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); if (seeked) { //find whathever should be playing int idx = a->track_find_key(i, time); if (idx < 0) continue; Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); if (!stream.is_valid()) { t->object->call("stop"); t->playing = false; playing_caches.erase(t); } else { float start_ofs = a->audio_track_get_key_start_offset(i, idx); start_ofs += time - a->track_get_key_time(i, idx); float end_ofs = a->audio_track_get_key_end_offset(i, idx); float len = stream->get_length(); if (start_ofs > len - end_ofs) { t->object->call("stop"); t->playing = false; playing_caches.erase(t); continue; } t->object->call("set_stream", stream); t->object->call("play", start_ofs); t->playing = true; playing_caches.insert(t); if (len && end_ofs > 0) { //force a end at a time t->len = len - start_ofs - end_ofs; } else { t->len = 0; } t->start = time; } } else { //find stuff to play List<int> to_play; a->track_get_key_indices_in_range(i, time, delta, &to_play); if (to_play.size()) { int idx = to_play.back()->get(); Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx); if (!stream.is_valid()) { t->object->call("stop"); t->playing = false; playing_caches.erase(t); } else { float start_ofs = a->audio_track_get_key_start_offset(i, idx); float end_ofs = a->audio_track_get_key_end_offset(i, idx); float len = stream->get_length(); t->object->call("set_stream", stream); t->object->call("play", start_ofs); t->playing = true; playing_caches.insert(t); if (len && end_ofs > 0) { //force a end at a time t->len = len - start_ofs - end_ofs; } else { t->len = 0; } t->start = time; } } else if (t->playing) { bool loop = a->has_loop(); bool stop = false; if (!loop && time < t->start) { stop = true; } else if (t->len > 0) { float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; if (len > t->len) { stop = true; } } if (stop) { //time to stop t->object->call("stop"); t->playing = false; playing_caches.erase(t); } } } float db = Math::linear2db(MAX(blend, 0.00001)); if (t->object->has_method("set_unit_db")) { t->object->call("set_unit_db", db); } else { t->object->call("set_volume_db", db); } } break; case Animation::TYPE_ANIMATION: { TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object); if (!player2) continue; if (delta == 0 || seeked) { //seek int idx = a->track_find_key(i, time); if (idx < 0) continue; float pos = a->track_get_key_time(i, idx); StringName anim_name = a->animation_track_get_key_animation(i, idx); if (String(anim_name) == "[stop]" || !player2->has_animation(anim_name)) continue; Ref<Animation> anim = player2->get_animation(anim_name); float at_anim_pos; if (anim->has_loop()) { at_anim_pos = Math::fposmod(time - pos, anim->get_length()); //seek to loop } else { at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end } if (player2->is_playing() || seeked) { player2->play(anim_name); player2->seek(at_anim_pos); t->playing = true; playing_caches.insert(t); } else { player2->set_assigned_animation(anim_name); player2->seek(at_anim_pos, true); } } else { //find stuff to play List<int> to_play; a->track_get_key_indices_in_range(i, time, delta, &to_play); if (to_play.size()) { int idx = to_play.back()->get(); StringName anim_name = a->animation_track_get_key_animation(i, idx); if (String(anim_name) == "[stop]" || !player2->has_animation(anim_name)) { if (playing_caches.has(t)) { playing_caches.erase(t); player2->stop(); t->playing = false; } } else { player2->play(anim_name); t->playing = true; playing_caches.insert(t); } } } } break; } } } } { // finally, set the tracks const NodePath *K = NULL; while ((K = track_cache.next(K))) { TrackCache *track = track_cache[*K]; if (track->process_pass != process_pass) continue; //not processed, ignore switch (track->type) { case Animation::TYPE_TRANSFORM: { TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); Transform xform; xform.origin = t->loc; xform.basis.set_quat_scale(t->rot, t->scale); if (t->root_motion) { root_motion_transform = xform; if (t->skeleton && t->bone_idx >= 0) { root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse(); } } else if (t->skeleton && t->bone_idx >= 0) { t->skeleton->set_bone_pose(t->bone_idx, xform); } else { t->spatial->set_transform(xform); } } break; case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); t->object->set_indexed(t->subpath, t->value); } break; case Animation::TYPE_BEZIER: { TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); t->object->set_indexed(t->subpath, t->value); } break; default: { } //the rest don't matter } } } }
void AnimationNodeBlendTreeEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); } if (p_what == NOTIFICATION_PROCESS) { String error; if (!blend_tree->get_tree()) { error = TTR("BlendTree does not belong to an AnimationTree node."); } else if (!blend_tree->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); } else if (blend_tree->get_tree()->is_state_invalid()) { error = blend_tree->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { error_label->set_text(error); if (error != String()) { error_panel->show(); } else { error_panel->hide(); } } List<AnimationNodeBlendTree::NodeConnection> conns; blend_tree->get_node_connections(&conns); for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) { float activity = 0; if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) { activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index); } graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity); } AnimationTree *graph_player = blend_tree->get_tree(); AnimationPlayer *player = NULL; if (graph_player->has_node(graph_player->get_animation_player())) { player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player())); } if (player) { for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) { Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key()); if (an.is_valid()) { if (player->has_animation(an->get_animation())) { Ref<Animation> anim = player->get_animation(an->get_animation()); if (anim.is_valid()) { E->get()->set_max(anim->get_length()); E->get()->set_value(an->get_playback_time()); } } } } } } }
void AnimationTreeEditor::_popup_edit_dialog() { updating_edit=true; for(int i=0;i<2;i++) edit_scroll[i]->hide(); for(int i=0;i<4;i++) { edit_line[i]->hide(); edit_label[i]->hide(); } edit_option->hide(); edit_button->hide();; filter_button->hide(); edit_check->hide();; Point2 pos = anim_tree->node_get_pos(edited_node)-Point2(h_scroll->get_val(),v_scroll->get_val()); Ref<StyleBox> style = get_stylebox("panel","PopupMenu"); Size2 size = get_node_size(edited_node); Point2 popup_pos( pos.x+style->get_margin(MARGIN_LEFT), pos.y+size.y-style->get_margin(MARGIN_BOTTOM)); popup_pos+=get_global_pos(); if (renaming_edit) { edit_label[0]->set_text("New name:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_line[0]->set_begin(Point2(15,25)); edit_line[0]->set_text(edited_node); edit_line[0]->show(); edit_dialog->set_size(Size2(150,50)); } else { AnimationTreePlayer::NodeType type=anim_tree->node_get_type(edited_node); switch(type) { case AnimationTreePlayer::NODE_ANIMATION: if (anim_tree->get_master_player()!=NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && anim_tree->get_node(anim_tree->get_master_player())->cast_to<AnimationPlayer>()) { AnimationPlayer *ap = anim_tree->get_node(anim_tree->get_master_player())->cast_to<AnimationPlayer>(); master_anim_popup->clear(); List<StringName> sn; ap->get_animation_list(&sn); sn.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E=sn.front();E;E=E->next()) { master_anim_popup->add_item(E->get()); } master_anim_popup->set_pos(popup_pos); master_anim_popup->popup(); } else { property_editor->edit(this,"",Variant::OBJECT,anim_tree->animation_node_get_animation(edited_node),PROPERTY_HINT_RESOURCE_TYPE,"Animation"); property_editor->set_pos(popup_pos); property_editor->popup(); updating_edit=false; } return; case AnimationTreePlayer::NODE_TIMESCALE: edit_label[0]->set_text("Scale:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_line[0]->set_begin(Point2(15,25)); edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); edit_line[0]->show(); edit_dialog->set_size(Size2(150,50)); break; case AnimationTreePlayer::NODE_ONESHOT: edit_label[0]->set_text("Fade In (s):"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_line[0]->set_begin(Point2(15,25)); edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); edit_line[0]->show(); edit_label[1]->set_text("Fade Out (s):"); edit_label[1]->set_pos(Point2(5,55)); edit_label[1]->show(); edit_line[1]->set_begin(Point2(15,75)); edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); edit_line[1]->show(); edit_option->clear(); edit_option->add_item("Blend",0); edit_option->add_item("Mix",1); edit_option->set_begin(Point2(15,105)); edit_option->select( anim_tree->oneshot_node_get_mix_mode(edited_node)); edit_option->show(); edit_check->set_text("Auto Restart:"); edit_check->set_begin(Point2(15,125)); edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); edit_check->show(); edit_label[2]->set_text("Restart (s):"); edit_label[2]->set_pos(Point2(5,145)); edit_label[2]->show(); edit_line[2]->set_begin(Point2(15,165)); edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); edit_line[2]->show(); edit_label[3]->set_text("Random Restart (s):"); edit_label[3]->set_pos(Point2(5,195)); edit_label[3]->show(); edit_line[3]->set_begin(Point2(15,215)); edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); edit_line[3]->show(); filter_button->set_begin(Point2(10,245)); filter_button->show(); edit_button->set_begin(Point2(10,268)); edit_button->set_text("Start!"); edit_button->show(); edit_dialog->set_size(Size2(180,293)); break; case AnimationTreePlayer::NODE_MIX: edit_label[0]->set_text("Amount:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_scroll[0]->set_min(0); edit_scroll[0]->set_max(1); edit_scroll[0]->set_val(anim_tree->mix_node_get_amount(edited_node)); edit_scroll[0]->set_begin(Point2(15,25)); edit_scroll[0]->show(); edit_dialog->set_size(Size2(150,50)); break; case AnimationTreePlayer::NODE_BLEND2: edit_label[0]->set_text("Blend:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_scroll[0]->set_min(0); edit_scroll[0]->set_max(1); edit_scroll[0]->set_val(anim_tree->blend2_node_get_amount(edited_node)); edit_scroll[0]->set_begin(Point2(15,25)); edit_scroll[0]->show(); filter_button->set_begin(Point2(10,47)); filter_button->show(); edit_dialog->set_size(Size2(150,74)); break; case AnimationTreePlayer::NODE_BLEND3: edit_label[0]->set_text("Blend:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_scroll[0]->set_min(-1); edit_scroll[0]->set_max(1); edit_scroll[0]->set_val(anim_tree->blend3_node_get_amount(edited_node)); edit_scroll[0]->set_begin(Point2(15,25)); edit_scroll[0]->show(); edit_dialog->set_size(Size2(150,50)); break; case AnimationTreePlayer::NODE_BLEND4: edit_label[0]->set_text("Blend 0:"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_scroll[0]->set_min(0); edit_scroll[0]->set_max(1); edit_scroll[0]->set_val(anim_tree->blend4_node_get_amount(edited_node).x); edit_scroll[0]->set_begin(Point2(15,25)); edit_scroll[0]->show(); edit_label[1]->set_text("Blend 1:"); edit_label[1]->set_pos(Point2(5,55)); edit_label[1]->show(); edit_scroll[1]->set_min(0); edit_scroll[1]->set_max(1); edit_scroll[1]->set_val(anim_tree->blend4_node_get_amount(edited_node).y); edit_scroll[1]->set_begin(Point2(15,75)); edit_scroll[1]->show(); edit_dialog->set_size(Size2(150,100)); break; case AnimationTreePlayer::NODE_TRANSITION: { edit_label[0]->set_text("X-Fade Time (s):"); edit_label[0]->set_pos(Point2(5,5)); edit_label[0]->show(); edit_line[0]->set_begin(Point2(15,25)); edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); edit_line[0]->show(); edit_label[1]->set_text("Current:"); edit_label[1]->set_pos(Point2(5,55)); edit_label[1]->show(); edit_option->set_begin(Point2(15,75)); edit_option->clear();; for(int i=0;i<anim_tree->transition_node_get_input_count(edited_node);i++) { edit_option->add_item(itos(i),i); } edit_option->select(anim_tree->transition_node_get_current(edited_node)); edit_option->show(); edit_dialog->set_size(Size2(150,100)); } break; default: {} } } edit_dialog->set_pos(popup_pos); edit_dialog->popup(); updating_edit=false; }
AEResult GameLightsUpdate::ShadowLightRenderGameObject(GameObject* gameObject, const glm::mat4& lightView, const glm::mat4& lightProj) { AEAssert(gameObject != nullptr); if (gameObject == nullptr) { return AEResult::NullParameter; } if (gameObject->HasMeshGOC() && gameObject->HasMaterialGOCs()) { Mesh* mesh = gameObject->GetMeshGOC()->GetMeshResource(); if (mesh != nullptr) { ConstantBuffer* cb = nullptr; Material* mat = nullptr; bool hasAnimation = gameObject->HasMeshAnimationGOC(); if (hasAnimation) { mat = m_VarianceSkinningShadowMaterial; ShaderProperties* vsProps = m_VarianceSkinningShadowMaterial->GetVSProps(); cb = vsProps->GetConstantBuffer(AE_CB_WORLD_VIEW_PROJ_NAME); ConstantBuffer* cbBones = vsProps->GetConstantBuffer(AE_CB_BONES_NAME); if (cbBones != nullptr) { AnimationPlayer* player = gameObject->GetMeshAnimationGOC()->GetAnimationPlayer(); cbBones->SetValue(AE_CB_BONE_TRANSFORMS_VAR_NAME, player->GetBoneTransforms(), sizeof(glm::mat4) * AE_MAX_BONES); } } else { mat = m_VarianceShadowMaterial; ShaderProperties* vsProps = m_VarianceShadowMaterial->GetVSProps(); cb = vsProps->GetConstantBuffer(AE_CB_WORLD_VIEW_PROJ_NAME); } cb->SetValueT<glm::mat4>(AE_CB_WORLD_VAR_NAME, gameObject->GetWorldTransform()); cb->SetValueT<glm::mat4>(AE_CB_VIEW_VAR_NAME, lightView); cb->SetValueT<glm::mat4>(AE_CB_PROJECTION_VAR_NAME, lightProj); AETODO("check return"); mat->Apply(); for (uint32_t i = 0; i < mesh->GetNumberMeshParts(); i++) { MeshPart* meshPart = mesh->GetMeshPart(i); m_GraphicDevice->SetVertexBuffer(meshPart->GetVertexBuffer()); m_GraphicDevice->SetIndexBuffer(meshPart->GetIndexBuffer()); m_GraphicDevice->SetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_GraphicDevice->DrawIndexed(0, 0, meshPart->GetIndexBuffer()->GetSize()); } AETODO("check return"); mat->UnApply(); } } for (auto goIt : *gameObject) { ShadowLightRenderGameObject(goIt.second, lightView, lightProj); } return AEResult::Ok; }
bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &anode) { if (updating || _filter_edit != anode) return false; NodePath player_path = anode->get_tree()->get_animation_player(); if (!anode->get_tree()->has_node(player_path)) { EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names.")); return false; } AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path)); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names.")); return false; } Node *base = player->get_node(player->get_root()); if (!base) { EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names.")); return false; } updating = true; Set<String> paths; { List<StringName> animations; player->get_animation_list(&animations); for (List<StringName>::Element *E = animations.front(); E; E = E->next()) { Ref<Animation> anim = player->get_animation(E->get()); for (int i = 0; i < anim->get_track_count(); i++) { paths.insert(anim->track_get_path(i)); } } } filter_enabled->set_pressed(anode->is_filter_enabled()); filters->clear(); TreeItem *root = filters->create_item(); Map<String, TreeItem *> parenthood; for (Set<String>::Element *E = paths.front(); E; E = E->next()) { NodePath path = E->get(); TreeItem *ti = NULL; String accum; for (int i = 0; i < path.get_name_count(); i++) { String name = path.get_name(i); if (accum != String()) { accum += "/"; } accum += name; if (!parenthood.has(accum)) { if (ti) { ti = filters->create_item(ti); } else { ti = filters->create_item(root); } parenthood[accum] = ti; ti->set_text(0, name); ti->set_selectable(0, false); ti->set_editable(0, false); if (base->has_node(accum)) { Node *node = base->get_node(accum); if (has_icon(node->get_class(), "EditorIcons")) { ti->set_icon(0, get_icon(node->get_class(), "EditorIcons")); } else { ti->set_icon(0, get_icon("Node", "EditorIcons")); } } } else { ti = parenthood[accum]; } } Node *node = NULL; if (base->has_node(accum)) { node = base->get_node(accum); } if (!node) continue; //no node, cant edit if (path.get_subname_count()) { String concat = path.get_concatenated_subnames(); Skeleton *skeleton = Object::cast_to<Skeleton>(node); if (skeleton && skeleton->find_bone(concat) != -1) { //path in skeleton String bone = concat; int idx = skeleton->find_bone(bone); List<String> bone_path; while (idx != -1) { bone_path.push_front(skeleton->get_bone_name(idx)); idx = skeleton->get_bone_parent(idx); } accum += ":"; for (List<String>::Element *F = bone_path.front(); F; F = F->next()) { if (F != bone_path.front()) { accum += "/"; } accum += F->get(); if (!parenthood.has(accum)) { ti = filters->create_item(ti); parenthood[accum] = ti; ti->set_text(0, F->get()); ti->set_selectable(0, false); ti->set_editable(0, false); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); } else { ti = parenthood[accum]; } } ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); ti->set_metadata(0, path); } else { //just a property ti = filters->create_item(ti); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); } } else { if (ti) { //just a node, likely call or animation track ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); } } } updating = false; return true; }