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

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