1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "buildblendtreesjob_p.h"
5#include <Qt3DAnimation/private/handler_p.h>
6#include <Qt3DAnimation/private/managers_p.h>
7#include <Qt3DAnimation/private/clipblendnodevisitor_p.h>
8#include <Qt3DAnimation/private/clipblendnode_p.h>
9#include <Qt3DAnimation/private/clipblendvalue_p.h>
10#include <Qt3DAnimation/private/lerpclipblend_p.h>
11#include <Qt3DAnimation/private/job_common_p.h>
12
13QT_BEGIN_NAMESPACE
14
15namespace Qt3DAnimation {
16namespace Animation {
17
18BuildBlendTreesJob::BuildBlendTreesJob()
19 : Qt3DCore::QAspectJob()
20 , m_handler(nullptr)
21{
22 SET_JOB_RUN_STAT_TYPE(this, JobTypes::BuildBlendTree, 0)
23}
24
25void BuildBlendTreesJob::setBlendedClipAnimators(const QVector<HBlendedClipAnimator> &blendedClipAnimatorHandles)
26{
27 m_blendedClipAnimatorHandles = blendedClipAnimatorHandles;
28 BlendedClipAnimatorManager *blendedClipAnimatorManager = m_handler->blendedClipAnimatorManager();
29 BlendedClipAnimator *blendedClipAnimator = nullptr;
30 for (const auto &blendedClipAnimatorHandle : std::as_const(t&: m_blendedClipAnimatorHandles)) {
31 blendedClipAnimator = blendedClipAnimatorManager->data(handle: blendedClipAnimatorHandle);
32 Q_ASSERT(blendedClipAnimator);
33 }
34}
35
36// Note this job is run once for all stopped blended animators that have been marked dirty
37// We assume that the structure of blend node tree does not change once a BlendClipAnimator has been set to running
38void BuildBlendTreesJob::run()
39{
40 for (const HBlendedClipAnimator &blendedClipAnimatorHandle : std::as_const(t&: m_blendedClipAnimatorHandles)) {
41 // Retrieve BlendTree node
42 BlendedClipAnimator *blendClipAnimator = m_handler->blendedClipAnimatorManager()->data(handle: blendedClipAnimatorHandle);
43 Q_ASSERT(blendClipAnimator);
44
45
46 const bool canRun = blendClipAnimator->canRun();
47 const bool running = blendClipAnimator->isRunning();
48 const bool seeking = blendClipAnimator->isSeeking();
49 m_handler->setBlendedClipAnimatorRunning(handle: blendedClipAnimatorHandle, running: canRun && (seeking || running));
50
51 if (!canRun && !(seeking || running))
52 continue;
53
54 // Build the format for clip results that should be used by nodes in the blend
55 // tree when used with this animator
56 const ChannelMapper *mapper = m_handler->channelMapperManager()->lookupResource(id: blendClipAnimator->mapperId());
57 if (!mapper)
58 continue;
59 const QVector<ChannelNameAndType> channelNamesAndTypes
60 = buildRequiredChannelsAndTypes(handler: m_handler, mapper);
61 const QVector<ComponentIndices> channelComponentIndices
62 = assignChannelComponentIndices(namesAndTypes: channelNamesAndTypes);
63
64 // Find the leaf value nodes of the blend tree and for each of them
65 // create a set of format indices that can later be used to map the
66 // raw ClipResults resulting from evaluating an animation clip to the
67 // layout used by the blend tree for this animator
68 QVector<QBitArray> blendTreeChannelMask;
69 const QVector<Qt3DCore::QNodeId> valueNodeIds
70 = gatherValueNodesToEvaluate(handler: m_handler, blendTreeRootId: blendClipAnimator->blendTreeRootId());
71
72 // Store the clip value nodes to avoid further lookups below.
73 // TODO: Refactor this next block into a function in animationutils.cpp that takes
74 // a QList<QClipBlendValue*> as input.
75 QList<ClipBlendValue *> valueNodes;
76 valueNodes.reserve(asize: valueNodeIds.size());
77 for (const auto &valueNodeId : valueNodeIds) {
78 ClipBlendValue *valueNode
79 = static_cast<ClipBlendValue *>(m_handler->clipBlendNodeManager()->lookupNode(id: valueNodeId));
80 Q_ASSERT(valueNode);
81 valueNodes.push_back(t: valueNode);
82
83 const Qt3DCore::QNodeId clipId = valueNode->clipId();
84 AnimationClip *clip = m_handler->animationClipLoaderManager()->lookupResource(id: clipId);
85 Q_ASSERT(clip);
86
87 const ClipFormat format = generateClipFormatIndices(targetChannels: channelNamesAndTypes,
88 targetIndices: channelComponentIndices,
89 clip);
90 valueNode->setClipFormat(animatorId: blendClipAnimator->peerId(), formatIndices: format);
91
92 // this BlendClipAnimator needs to be notified when the clip has been loaded
93 clip->addDependingBlendedClipAnimator(id: blendClipAnimator->peerId());
94
95 // Combine the masks from each source clip to see which channels should be
96 // evaluated and result in a mappingData being created. If any contributing clip
97 // supports a channel, that will produce a channel mapping. Clips with those channels
98 // missing will use default values when blending:
99 //
100 // Default scale = (1, 1, 1)
101 // Default rotation = (1, 0, 0, 0)
102 // Default translation = (0, 0, 0)
103 // Default joint transforms should be taken from the skeleton initial pose
104 //
105 // Everything else has all components set to 0. If user wants something else, they
106 // should provide a clip with a single keyframe at the desired default value.
107 if (blendTreeChannelMask.isEmpty()) {
108 // Initialize the blend tree mask from the mask of the first clip
109 blendTreeChannelMask = format.sourceClipMask;
110 } else {
111 // We allow through a channel if any source clip in the tree has
112 // data for that channel. Clips without data for a channel will
113 // have default values substituted when evaluating the blend tree.
114 int channelIndex = 0;
115 for (const auto &channelMask : std::as_const(t: format.sourceClipMask))
116 blendTreeChannelMask[channelIndex++] |= channelMask;
117 }
118 }
119
120 // Now that we know the overall blend tree mask, go back and compare this to
121 // the masks from each of the value nodes. If the overall mask requires a
122 // channel but the value node does not provide it, we need to store default
123 // values to use for that channel so that the blending evaluation works as
124 // expected.
125 for (const auto valueNode : valueNodes) {
126 ClipFormat &f = valueNode->clipFormat(animatorId: blendClipAnimator->peerId());
127
128 const qsizetype channelCount = blendTreeChannelMask.size();
129 for (qsizetype i = 0; i < channelCount; ++i) {
130 if (blendTreeChannelMask[i] == f.sourceClipMask[i])
131 continue; // Masks match, nothing to do
132
133 // If we get to here then we need to obtain a default value
134 // for this channel and store it for later application to any
135 // missing components of this channel.
136 const QVector<float> defaultValue = defaultValueForChannel(handler: m_handler,
137 channelDescription: f.namesAndTypes[i]);
138
139 // Find the indices where we later need to inject these default
140 // values and store them in the format.
141 const ComponentIndices &componentIndices = f.formattedComponentIndices[i];
142 Q_ASSERT(componentIndices.size() == defaultValue.size());
143 for (qsizetype j = 0; j < defaultValue.size(); ++j)
144 f.defaultComponentValues.push_back(t: {.componentIndex: componentIndices[j], .value: defaultValue[j]});
145 }
146 }
147
148 // Finally, build the mapping data vector for this blended clip animator. This
149 // gets used during the final stage of evaluation when sending the property changes
150 // out to the targets of the animation. We do the costly work once up front.
151 const Qt3DCore::QNodeIdVector channelMappingIds = mapper->mappingIds();
152 QVector<ChannelMapping *> channelMappings;
153 channelMappings.reserve(asize: channelMappingIds.size());
154 for (const auto &mappingId : channelMappingIds) {
155 ChannelMapping *mapping = m_handler->channelMappingManager()->lookupResource(id: mappingId);
156 Q_ASSERT(mapping);
157 channelMappings.push_back(t: mapping);
158 }
159
160 const QVector<MappingData> mappingDataVec = buildPropertyMappings(channelMappings,
161 channelNamesAndTypes,
162 channelComponentIndices,
163 sourceClipMask: blendTreeChannelMask);
164 blendClipAnimator->setMappingData(mappingDataVec);
165 }
166}
167
168} // Animation
169} // Qt3DAnimation
170
171QT_END_NAMESPACE
172

source code of qt3d/src/animation/backend/buildblendtreesjob.cpp