blob: 2ebcaf1f7f1180a4faa40b8803bf6d442a94a45f [file]
#ifndef _RIVE_STATE_TRANSITION_HPP_
#define _RIVE_STATE_TRANSITION_HPP_
#include "rive/animation/keyframe_interpolator.hpp"
#include "rive/animation/state_transition_flags.hpp"
#include "rive/generated/animation/state_transition_base.hpp"
#ifdef WITH_RIVE_EDITOR
#include "rive/editor/object_arena.hpp"
#endif
#include <stdio.h>
#include <vector>
namespace rive
{
class LayerState;
class StateMachineLayerImporter;
class StateTransitionImporter;
class TransitionCondition;
class StateInstance;
class StateMachineInstance;
class StateMachineLayerInstance;
class LinearAnimation;
class LinearAnimationInstance;
enum class AllowTransition : unsigned char
{
no,
waitingForExit,
yes
};
class StateTransition : public StateTransitionBase
{
friend class StateMachineLayerImporter;
friend class StateTransitionImporter;
private:
StateTransitionFlags transitionFlags() const
{
return static_cast<StateTransitionFlags>(flags());
}
LayerState* m_StateTo = nullptr;
uint32_t m_EvaluatedRandomWeight = 1;
KeyFrameInterpolator* m_Interpolator = nullptr;
#ifdef WITH_RIVE_EDITOR
// Slice 6 Phase E dual-storage. See targeted_constraint.hpp.
CoreHandle m_StateToHandle;
CoreHandle m_InterpolatorHandle;
#endif
std::vector<TransitionCondition*> m_Conditions;
void addCondition(TransitionCondition* condition);
public:
~StateTransition() override;
#ifdef WITH_RIVE_EDITOR
// Bodies in editor_native/native/src/editor/animation/sm/
// state_transition_editor.cpp.
const LayerState* stateTo() const;
KeyFrameInterpolator* interpolator() const;
#else
inline const LayerState* stateTo() const { return m_StateTo; }
inline KeyFrameInterpolator* interpolator() const { return m_Interpolator; }
#endif
inline uint32_t evaluatedRandomWeight() const
{
return m_EvaluatedRandomWeight;
}
void evaluatedRandomWeight(uint32_t value)
{
m_EvaluatedRandomWeight = value;
}
StatusCode onAddedDirty(CoreContext* context) override;
StatusCode onAddedClean(CoreContext* context) override;
/// Whether the transition is marked disabled (usually done in the
/// editor).
bool isDisabled() const
{
return (transitionFlags() & StateTransitionFlags::Disabled) ==
StateTransitionFlags::Disabled;
}
/// Returns AllowTransition::yes when this transition can be taken from
/// stateFrom with the given inputs.
AllowTransition allowed(StateInstance* stateFrom,
StateMachineInstance* stateMachineInstance,
StateMachineLayerInstance* layerInstance) const;
/// Whether the animation is held at exit or if it keeps advancing
/// during mixing.
bool pauseOnExit() const
{
return (transitionFlags() & StateTransitionFlags::PauseOnExit) ==
StateTransitionFlags::PauseOnExit;
}
/// Whether exit time is enabled. All other conditions still apply, the
/// exit time is effectively an AND with the rest of the conditions.
bool enableExitTime() const
{
return (transitionFlags() & StateTransitionFlags::EnableExitTime) ==
StateTransitionFlags::EnableExitTime;
}
/// Whether the transition can be interrupted.
bool enableEarlyExit() const
{
return (transitionFlags() & StateTransitionFlags::EnableEarlyExit) ==
StateTransitionFlags::EnableEarlyExit;
}
bool durationIsPercentage() const
{
return (transitionFlags() &
StateTransitionFlags::DurationIsPercentage) ==
StateTransitionFlags::DurationIsPercentage;
}
StatusCode import(ImportStack& importStack) override;
size_t conditionCount() const
{
#ifdef WITH_RIVE_EDITOR
if (m_Conditions.empty())
{
return m_editorConditions.size();
}
#endif
return m_Conditions.size();
}
TransitionCondition* condition(size_t index) const
{
if (index < m_Conditions.size())
{
return m_Conditions[index];
}
#ifdef WITH_RIVE_EDITOR
if (m_Conditions.empty() && index < m_editorConditions.size())
{
return m_editorConditions[index];
}
#endif
return nullptr;
}
#ifdef WITH_RIVE_EDITOR
// Editor-only parallel non-owning condition list (see
// `StateMachineLayer::m_editorStates` for pattern rationale).
// The runtime importer's resolve step (`StateMachineLayerImporter::
// resolve`) sets `m_StateTo` from the layer-relative `stateToId`
// index and `m_Interpolator` from `interpolatorId`. Coop bypasses
// that path; finalizeBatch resolves both via `EditorFile::resolve`
// (CoopId map) and pokes the values in directly.
void addConditionForEditor(TransitionCondition* condition);
void clearEditorConditions();
size_t editorConditionCount() const { return m_editorConditions.size(); }
// Bodies in state_transition_editor.cpp — dispatch through
// editorArena() so editor-flow Cores get the handle path while
// the runtime importer (which still assigns m_StateTo directly
// via friend class access) keeps the raw fallback.
void setStateToForEditor(LayerState* state);
void setInterpolatorForEditor(KeyFrameInterpolator* interp);
#endif
private:
#ifdef WITH_RIVE_EDITOR
std::vector<TransitionCondition*> m_editorConditions;
#endif
public:
/// The amount of time to mix the outgoing animation onto the incoming
/// one when changing state. Only applies when going out from an
/// AnimationState.
float mixTime(const LayerState* stateFrom) const;
/// Computes the exit time in seconds of the stateFrom. Set absolute to
/// true if you want the returned time to be relative to the entire
/// animation. Set absolute to false if you want it relative to the work
/// area.
float exitTimeSeconds(const LayerState* stateFrom,
bool absolute = false) const;
/// Provide the animation instance to use for computing percentage
/// durations for exit time.
virtual const LinearAnimationInstance* exitTimeAnimationInstance(
const StateInstance* from) const;
/// Provide the animation to use for computing percentage durations for
/// exit time.
virtual const LinearAnimation* exitTimeAnimation(
const LayerState* from) const;
/// Retruns true when we need to hold the exit time, also applies the
/// correct time to the animation instance in the stateFrom, when
/// applicable (when it's an AnimationState).
bool applyExitCondition(StateInstance* stateFrom) const;
/// Marks any trigger based condition as used for this layer
void useLayerInConditions(StateMachineInstance* stateMachineInstance,
StateMachineLayerInstance* layerInstance) const;
};
} // namespace rive
#endif