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 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | namespace Qt3DAnimation { |
49 | namespace Animation { |
50 | |
51 | BuildBlendTreesJob::BuildBlendTreesJob() |
52 | : Qt3DCore::QAspectJob() |
53 | , m_handler(nullptr) |
54 | { |
55 | SET_JOB_RUN_STAT_TYPE(this, JobTypes::BuildBlendTree, 0) |
56 | } |
57 | |
58 | void 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 |
71 | void 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 | |
205 | QT_END_NAMESPACE |
206 | |