| Flutter Impeller |

node.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/scene/node.h"
6
7 #include <inttypes.h>
8 #include <atomic>
9 #include <memory>
10 #include <vector>
11
12 #include "flutter/fml/logging.h"
13 #include "impeller/base/strings.h"
14 #include "impeller/base/thread.h"
15 #include "impeller/base/validation.h"
16 #include "impeller/geometry/matrix.h"
17 #include "impeller/scene/animation/animation_player.h"
18 #include "impeller/scene/importer/conversions.h"
19 #include "impeller/scene/importer/scene_flatbuffers.h"
20 #include "impeller/scene/mesh.h"
21 #include "impeller/scene/node.h"
22 #include "impeller/scene/scene_encoder.h"
23
24 namespace impeller {
25 namespace scene {
26
27 static std::atomic_uint64_t kNextNodeID = 0;
28
29 void Node::MutationLog::Append(const Entry& entry) {
30 WriterLock lock(write_mutex_);
31 dirty_ = true;
32 entries_.push_back(entry);
33 }
34
35 std::optional<std::vector<Node::MutationLog::Entry>>
36 Node::MutationLog::Flush() {
37 WriterLock lock(write_mutex_);
38 if (!dirty_) {
39 return std::nullopt;
40 }
41 dirty_ = false;
42 auto result = entries_;
43 entries_ = {};
44 return result;
45 }
46
47 std::shared_ptr<Node> Node::MakeFromFlatbuffer(
48 const fml::Mapping& ipscene_mapping,
49 Allocator& allocator) {
50 flatbuffers::Verifier verifier(ipscene_mapping.GetMapping(),
51 ipscene_mapping.GetSize());
52 if (!fb::VerifySceneBuffer(verifier)) {
53 VALIDATION_LOG << "Failed to unpack scene: Scene flatbuffer is invalid.";
54 return nullptr;
55 }
56
57 return Node::MakeFromFlatbuffer(*fb::GetScene(ipscene_mapping.GetMapping()),
58 allocator);
59 }
60
61 static std::shared_ptr<Texture> UnpackTextureFromFlatbuffer(
62 const fb::Texture* iptexture,
63 Allocator& allocator) {
64 if (iptexture == nullptr || iptexture->embedded_image() == nullptr ||
65 iptexture->embedded_image()->bytes() == nullptr) {
66 return nullptr;
67 }
68
69 auto embedded = iptexture->embedded_image();
70
71 uint8_t bytes_per_component = 0;
72 switch (embedded->component_type()) {
73 case fb::ComponentType::k8Bit:
74 bytes_per_component = 1;
75 break;
76 case fb::ComponentType::k16Bit:
77 // bytes_per_component = 2;
78 FML_LOG(WARNING) << "16 bit textures not yet supported.";
79 return nullptr;
80 }
81
82 switch (embedded->component_count()) {
83 case 4:
84 // RGBA.
85 break;
86 case 1:
87 case 3:
88 default:
89 FML_LOG(WARNING) << "Textures with " << embedded->component_count()
90 << " components are not supported." << std::endl;
91 return nullptr;
92 }
93 if (embedded->bytes()->size() != bytes_per_component *
94 embedded->component_count() *
95 embedded->width() * embedded->height()) {
96 FML_LOG(WARNING) << "Embedded texture has an unexpected size. Skipping."
97 << std::endl;
98 return nullptr;
99 }
100
101 auto image_mapping = std::make_shared<fml::NonOwnedMapping>(
102 embedded->bytes()->Data(), embedded->bytes()->size());
103
104 auto texture_descriptor = TextureDescriptor{};
105 texture_descriptor.storage_mode = StorageMode::kHostVisible;
106 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
107 texture_descriptor.size = ISize(embedded->width(), embedded->height());
108 // TODO(bdero): Generate mipmaps for embedded textures.
109 texture_descriptor.mip_count = 1u;
110
111 auto texture = allocator.CreateTexture(texture_descriptor);
112 if (!texture) {
113 FML_LOG(ERROR) << "Could not allocate texture.";
114 return nullptr;
115 }
116
117 auto uploaded = texture->SetContents(image_mapping);
118 if (!uploaded) {
119 FML_LOG(ERROR) << "Could not upload texture to device memory.";
120 return nullptr;
121 }
122
123 return texture;
124 }
125
126 std::shared_ptr<Node> Node::MakeFromFlatbuffer(const fb::Scene& scene,
127 Allocator& allocator) {
128 // Unpack textures.
129 std::vector<std::shared_ptr<Texture>> textures;
130 if (scene.textures()) {
131 for (const auto iptexture : *scene.textures()) {
132 // The elements of the unpacked texture array must correspond exactly with
133 // the ipscene texture array. So if a texture is empty or invalid, a
134 // nullptr is inserted as a placeholder.
135 textures.push_back(UnpackTextureFromFlatbuffer(iptexture, allocator));
136 }
137 }
138
139 auto result = std::make_shared<Node>();
140 result->SetLocalTransform(importer::ToMatrix(*scene.transform()));
141
142 if (!scene.nodes() || !scene.children()) {
143 return result; // The scene is empty.
144 }
145
146 // Initialize nodes for unpacking the entire scene.
147 std::vector<std::shared_ptr<Node>> scene_nodes;
148 scene_nodes.reserve(scene.nodes()->size());
149 for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) {
150 scene_nodes.push_back(std::make_shared<Node>());
151 }
152
153 // Connect children to the root node.
154 for (int child : *scene.children()) {
155 if (child < 0 || static_cast<size_t>(child) >= scene_nodes.size()) {
156 VALIDATION_LOG << "Scene child index out of range.";
157 continue;
158 }
159 result->AddChild(scene_nodes[child]);
160 }
161
162 // Unpack each node.
163 for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) {
164 scene_nodes[node_i]->UnpackFromFlatbuffer(*scene.nodes()->Get(node_i),
165 scene_nodes, textures, allocator);
166 }
167
168 // Unpack animations.
169 if (scene.animations()) {
170 for (const auto animation : *scene.animations()) {
171 if (auto out_animation =
172 Animation::MakeFromFlatbuffer(*animation, scene_nodes)) {
173 result->animations_.push_back(out_animation);
174 }
175 }
176 }
177
178 return result;
179 }
180
181 void Node::UnpackFromFlatbuffer(
182 const fb::Node& source_node,
183 const std::vector<std::shared_ptr<Node>>& scene_nodes,
184 const std::vector<std::shared_ptr<Texture>>& textures,
185 Allocator& allocator) {
186 name_ = source_node.name()->str();
187 SetLocalTransform(importer::ToMatrix(*source_node.transform()));
188
189 /// Meshes.
190
191 if (source_node.mesh_primitives()) {
192 Mesh mesh;
193 for (const auto* primitives : *source_node.mesh_primitives()) {
194 auto geometry = Geometry::MakeFromFlatbuffer(*primitives, allocator);
195 auto material =
196 primitives->material()
197 ? Material::MakeFromFlatbuffer(*primitives->material(), textures)
198 : Material::MakeUnlit();
199 mesh.AddPrimitive({std::move(geometry), std::move(material)});
200 }
201 SetMesh(std::move(mesh));
202 }
203
204 /// Child nodes.
205
206 if (source_node.children()) {
207 // Wire up graph connections.
208 for (int child : *source_node.children()) {
209 if (child < 0 || static_cast<size_t>(child) >= scene_nodes.size()) {
210 VALIDATION_LOG << "Node child index out of range.";
211 continue;
212 }
213 AddChild(scene_nodes[child]);
214 }
215 }
216
217 /// Skin.
218
219 if (source_node.skin()) {
220 skin_ = Skin::MakeFromFlatbuffer(*source_node.skin(), scene_nodes);
221 }
222 }
223
224 Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){};
225
226 Node::~Node() = default;
227
228 Mesh::Mesh(Mesh&& mesh) = default;
229
230 Mesh& Mesh::operator=(Mesh&& mesh) = default;
231
232 const std::string& Node::GetName() const {
233 return name_;
234 }
235
236 void Node::SetName(const std::string& new_name) {
237 name_ = new_name;
238 }
239
240 Node* Node::GetParent() const {
241 return parent_;
242 }
243
244 std::shared_ptr<Node> Node::FindChildByName(
245 const std::string& name,
246 bool exclude_animation_players) const {
247 for (auto& child : children_) {
248 if (exclude_animation_players && child->animation_player_.has_value()) {
249 continue;
250 }
251 if (child->GetName() == name) {
252 return child;
253 }
254 if (auto found = child->FindChildByName(name)) {
255 return found;
256 }
257 }
258 return nullptr;
259 }
260
261 std::shared_ptr<Animation> Node::FindAnimationByName(
262 const std::string& name) const {
263 for (const auto& animation : animations_) {
264 if (animation->GetName() == name) {
265 return animation;
266 }
267 }
268 return nullptr;
269 }
270
271 AnimationClip* Node::AddAnimation(const std::shared_ptr<Animation>& animation) {
272 if (!animation_player_.has_value()) {
273 animation_player_ = AnimationPlayer();
274 }
275 return animation_player_->AddAnimation(animation, this);
276 }
277
278 void Node::SetLocalTransform(Matrix transform) {
279 local_transform_ = transform;
280 }
281
282 Matrix Node::GetLocalTransform() const {
283 return local_transform_;
284 }
285
286 void Node::SetGlobalTransform(Matrix transform) {
287 Matrix inverse_global_transform =
288 parent_ ? parent_->GetGlobalTransform().Invert() : Matrix();
289
290 local_transform_ = inverse_global_transform * transform;
291 }
292
293 Matrix Node::GetGlobalTransform() const {
294 if (parent_) {
295 return parent_->GetGlobalTransform() * local_transform_;
296 }
297 return local_transform_;
298 }
299
300 bool Node::AddChild(std::shared_ptr<Node> node) {
301 if (!node) {
302 VALIDATION_LOG << "Cannot add null child to node.";
303 return false;
304 }
305
306 // TODO(bdero): Figure out a better paradigm/rules for nodes with multiple
307 // parents. We should probably disallow this, make deep
308 // copying of nodes cheap and easy, add mesh instancing, etc.
309 // Today, the parent link is only used for skin posing, and so
310 // it's reasonable to not have a check and allow multi-parenting.
311 // Even still, there should still be some kind of cycle
312 // prevention/detection, ideally at the protocol level.
313 //
314 // if (node->parent_ != nullptr) {
315 // VALIDATION_LOG
316 // << "Cannot add a node as a child which already has a parent.";
317 // return false;
318 // }
319 node->parent_ = this;
320 children_.push_back(std::move(node));
321
322 return true;
323 }
324
325 std::vector<std::shared_ptr<Node>>& Node::GetChildren() {
326 return children_;
327 }
328
329 void Node::SetMesh(Mesh mesh) {
330 mesh_ = std::move(mesh);
331 }
332
333 Mesh& Node::GetMesh() {
334 return mesh_;
335 }
336
337 void Node::SetIsJoint(bool is_joint) {
338 is_joint_ = is_joint;
339 }
340
341 bool Node::IsJoint() const {
342 return is_joint_;
343 }
344
345 bool Node::Render(SceneEncoder& encoder,
346 Allocator& allocator,
347 const Matrix& parent_transform) {
348 std::optional<std::vector<MutationLog::Entry>> log = mutation_log_.Flush();
349 if (log.has_value()) {
350 for (const auto& entry : log.value()) {
351 if (auto e = std::get_if<MutationLog::SetTransformEntry>(&entry)) {
352 local_transform_ = e->transform;
353 } else if (auto e =
354 std::get_if<MutationLog::SetAnimationStateEntry>(&entry)) {
355 AnimationClip* clip =
356 animation_player_.has_value()
357 ? animation_player_->GetClip(e->animation_name)
358 : nullptr;
359 if (!clip) {
360 auto animation = FindAnimationByName(e->animation_name);
361 if (!animation) {
362 continue;
363 }
364 clip = AddAnimation(animation);
365 if (!clip) {
366 continue;
367 }
368 }
369
370 clip->SetPlaying(e->playing);
371 clip->SetLoop(e->loop);
372 clip->SetWeight(e->weight);
373 clip->SetPlaybackTimeScale(e->time_scale);
374 } else if (auto e =
375 std::get_if<MutationLog::SeekAnimationEntry>(&entry)) {
376 AnimationClip* clip =
377 animation_player_.has_value()
378 ? animation_player_->GetClip(e->animation_name)
379 : nullptr;
380 if (!clip) {
381 auto animation = FindAnimationByName(e->animation_name);
382 if (!animation) {
383 continue;
384 }
385 clip = AddAnimation(animation);
386 if (!clip) {
387 continue;
388 }
389 }
390
391 clip->Seek(SecondsF(e->time));
392 }
393 }
394 }
395
396 if (animation_player_.has_value()) {
397 animation_player_->Update();
398 }
399
400 Matrix transform = parent_transform * local_transform_;
401 mesh_.Render(encoder, transform,
402 skin_ ? skin_->GetJointsTexture(allocator) : nullptr);
403
404 for (auto& child : children_) {
405 if (!child->Render(encoder, allocator, transform)) {
406 return false;
407 }
408 }
409 return true;
410 }
411
412 void Node::AddMutation(const MutationLog::Entry& entry) {
413 mutation_log_.Append(entry);
414 }
415
416 } // namespace scene
417 } // namespace impeller
impeller::scene::Node::Node
Node()
Definition: node.cc:224
impeller::scene::Node::GetLocalTransform
Matrix GetLocalTransform() const
Definition: node.cc:282
impeller::scene::Node::GetParent
Node * GetParent() const
Definition: node.cc:240
impeller::scene::Mesh
Definition: mesh.h:21
impeller::scene::Node::FindChildByName
std::shared_ptr< Node > FindChildByName(const std::string &name, bool exclude_animation_players=false) const
Definition: node.cc:244
impeller::scene::Node::~Node
~Node()
impeller::scene::AnimationClip::SetPlaybackTimeScale
void SetPlaybackTimeScale(Scalar playback_speed)
Sets the animation playback speed. Negative values make the clip play in reverse.
Definition: animation_clip.cc:61
impeller::scene::Mesh::Mesh
Mesh()
impeller::scene::Node::SetIsJoint
void SetIsJoint(bool is_joint)
Definition: node.cc:337
impeller::WriterLock
Definition: thread.h:116
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
animation_player.h
impeller::scene::AnimationClip::SetLoop
void SetLoop(bool looping)
Definition: animation_clip.cc:53
impeller::scene::Skin::MakeFromFlatbuffer
static std::unique_ptr< Skin > MakeFromFlatbuffer(const fb::Skin &skin, const std::vector< std::shared_ptr< Node >> &scene_nodes)
Definition: skin.cc:19
impeller::scene::Node::SetMesh
void SetMesh(Mesh mesh)
Definition: node.cc:329
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::Allocator::CreateTexture
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition: allocator.cc:49
validation.h
impeller::scene::AnimationClip
Definition: animation_clip.h:22
conversions.h
impeller::scene::Animation::MakeFromFlatbuffer
static std::shared_ptr< Animation > MakeFromFlatbuffer(const fb::Animation &animation, const std::vector< std::shared_ptr< Node >> &scene_nodes)
Definition: animation.cc:19
impeller::scene::Node::GetName
const std::string & GetName() const
Definition: node.cc:232
scene_encoder.h
impeller::scene::Node::GetChildren
std::vector< std::shared_ptr< Node > > & GetChildren()
Definition: node.cc:325
impeller::scene::Node::SetName
void SetName(const std::string &new_name)
Definition: node.cc:236
impeller::scene::Mesh::Render
bool Render(SceneEncoder &encoder, const Matrix &transform, const std::shared_ptr< Texture > &joints) const
Definition: mesh.cc:36
matrix.h
impeller::SecondsF
std::chrono::duration< float > SecondsF
Definition: timing.h:13
impeller::scene::Node::FindAnimationByName
std::shared_ptr< Animation > FindAnimationByName(const std::string &name) const
Definition: node.cc:261
node.h
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::scene::UnpackTextureFromFlatbuffer
static std::shared_ptr< Texture > UnpackTextureFromFlatbuffer(const fb::Texture *iptexture, Allocator &allocator)
Definition: node.cc:61
impeller::scene::Mesh::AddPrimitive
void AddPrimitive(Primitive mesh_)
Definition: mesh.cc:21
impeller::scene::kNextNodeID
static std::atomic_uint64_t kNextNodeID
Definition: node.cc:27
impeller::Allocator
An object that allocates device memory.
Definition: allocator.h:22
impeller::scene::Node::GetMesh
Mesh & GetMesh()
Definition: node.cc:333
impeller::scene::AnimationClip::SetWeight
void SetWeight(Scalar weight)
Definition: animation_clip.cc:69
impeller::scene::Geometry::MakeFromFlatbuffer
static std::shared_ptr< Geometry > MakeFromFlatbuffer(const fb::MeshPrimitive &mesh, Allocator &allocator)
Definition: geometry.cc:50
mesh.h
impeller::scene::AnimationPlayer
Definition: animation_player.h:26
strings.h
impeller::scene::Material::MakeFromFlatbuffer
static std::unique_ptr< Material > MakeFromFlatbuffer(const fb::Material &material, const std::vector< std::shared_ptr< Texture >> &textures)
Definition: material.cc:27
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::scene::Node::SetGlobalTransform
void SetGlobalTransform(Matrix transform)
Definition: node.cc:286
impeller::scene::Node::AddChild
bool AddChild(std::shared_ptr< Node > child)
Definition: node.cc:300
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
impeller::scene::Node::Render
bool Render(SceneEncoder &encoder, Allocator &allocator, const Matrix &parent_transform)
Definition: node.cc:345
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::scene::Node::MutationLog::Entry
std::variant< SetTransformEntry, SetAnimationStateEntry, SeekAnimationEntry > Entry
Definition: node.h:52
textures
std::vector< std::shared_ptr< FakeTexture > > textures
Definition: content_context_unittests.cc:92
impeller::scene::Node::IsJoint
bool IsJoint() const
Definition: node.cc:341
impeller::scene::Node::AddAnimation
AnimationClip * AddAnimation(const std::shared_ptr< Animation > &animation)
Definition: node.cc:271
impeller::scene::Node
Definition: node.h:30
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:38
impeller::scene::Node::AddMutation
void AddMutation(const MutationLog::Entry &entry)
Definition: node.cc:412
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:37
impeller::scene::SceneEncoder
Definition: scene_encoder.h:30
impeller::scene::Node::SetLocalTransform
void SetLocalTransform(Matrix transform)
Definition: node.cc:278
impeller::scene::AnimationClip::Seek
void Seek(SecondsF time)
Move the animation to the specified time. The given time is clamped to the animation's playback range...
Definition: animation_clip.cc:77
impeller::scene::Node::MakeFromFlatbuffer
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition: node.cc:47
impeller::scene::AnimationClip::SetPlaying
void SetPlaying(bool playing)
Definition: animation_clip.cc:32
thread.h
impeller
Definition: aiks_blur_unittests.cc:20
impeller::scene::Node::MutationLog::Append
void Append(const Entry &entry)
Definition: node.cc:29
impeller::scene::Mesh::operator=
Mesh & operator=(Mesh &&mesh)
impeller::scene::Material
Definition: material.h:27
impeller::scene::Node::GetGlobalTransform
Matrix GetGlobalTransform() const
Definition: node.cc:293
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::scene::importer::ToMatrix
Matrix ToMatrix(const std::vector< double > &m)
Definition: conversions.cc:15