Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
6
7#include <inttypes.h>
8#include <atomic>
9#include <memory>
10#include <vector>
11
12#include "flutter/fml/logging.h"
19#include "impeller/scene/importer/scene_flatbuffers.h"
20#include "impeller/scene/mesh.h"
21#include "impeller/scene/node.h"
23
24namespace impeller {
25namespace scene {
26
27static std::atomic_uint64_t kNextNodeID = 0;
28
29void Node::MutationLog::Append(const Entry& entry) {
30 WriterLock lock(write_mutex_);
31 dirty_ = true;
32 entries_.push_back(entry);
33}
34
35std::optional<std::vector<Node::MutationLog::Entry>>
36Node::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
47std::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
61static 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
126std::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
181void 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
224Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){};
225
226Node::~Node() = default;
227
228Mesh::Mesh(Mesh&& mesh) = default;
229
230Mesh& Mesh::operator=(Mesh&& mesh) = default;
231
232const std::string& Node::GetName() const {
233 return name_;
234}
235
236void Node::SetName(const std::string& new_name) {
237 name_ = new_name;
238}
239
241 return parent_;
242}
243
244std::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
261std::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
271AnimationClip* 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
279 local_transform_ = transform;
280}
281
283 return local_transform_;
284}
285
287 Matrix inverse_global_transform =
288 parent_ ? parent_->GetGlobalTransform().Invert() : Matrix();
289
290 local_transform_ = inverse_global_transform * transform;
291}
292
294 if (parent_) {
295 return parent_->GetGlobalTransform() * local_transform_;
296 }
297 return local_transform_;
298}
299
300bool 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
325std::vector<std::shared_ptr<Node>>& Node::GetChildren() {
326 return children_;
327}
328
329void Node::SetMesh(Mesh mesh) {
330 mesh_ = std::move(mesh);
331}
332
334 return mesh_;
335}
336
337void Node::SetIsJoint(bool is_joint) {
338 is_joint_ = is_joint;
339}
340
341bool Node::IsJoint() const {
342 return is_joint_;
343}
344
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)) {
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)) {
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
413 mutation_log_.Append(entry);
414}
415
416} // namespace scene
417} // namespace impeller
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
virtual const uint8_t * GetMapping() const =0
virtual size_t GetSize() const =0
An object that allocates device memory.
Definition allocator.h:22
std::shared_ptr< Texture > CreateTexture(const TextureDescriptor &desc)
Definition allocator.cc:49
static std::shared_ptr< Animation > MakeFromFlatbuffer(const fb::Animation &animation, const std::vector< std::shared_ptr< Node > > &scene_nodes)
Definition animation.cc:19
static std::shared_ptr< Geometry > MakeFromFlatbuffer(const fb::MeshPrimitive &mesh, Allocator &allocator)
Definition geometry.cc:50
static std::unique_ptr< Material > MakeFromFlatbuffer(const fb::Material &material, const std::vector< std::shared_ptr< Texture > > &textures)
Definition material.cc:27
bool Render(SceneEncoder &encoder, const Matrix &transform, const std::shared_ptr< Texture > &joints) const
Definition mesh.cc:36
Mesh & operator=(Mesh &&mesh)
std::variant< SetTransformEntry, SetAnimationStateEntry, SeekAnimationEntry > Entry
Definition node.h:52
void Append(const Entry &entry)
Definition node.cc:29
void SetIsJoint(bool is_joint)
Definition node.cc:337
bool AddChild(std::shared_ptr< Node > child)
Definition node.cc:300
void SetLocalTransform(Matrix transform)
Definition node.cc:278
void SetGlobalTransform(Matrix transform)
Definition node.cc:286
Node * GetParent() const
Definition node.cc:240
std::shared_ptr< Animation > FindAnimationByName(const std::string &name) const
Definition node.cc:261
Matrix GetGlobalTransform() const
Definition node.cc:293
AnimationClip * AddAnimation(const std::shared_ptr< Animation > &animation)
Definition node.cc:271
void AddMutation(const MutationLog::Entry &entry)
Definition node.cc:412
bool Render(SceneEncoder &encoder, Allocator &allocator, const Matrix &parent_transform)
Definition node.cc:345
const std::string & GetName() const
Definition node.cc:232
void SetName(const std::string &new_name)
Definition node.cc:236
std::shared_ptr< Node > FindChildByName(const std::string &name, bool exclude_animation_players=false) const
Definition node.cc:244
std::vector< std::shared_ptr< Node > > & GetChildren()
Definition node.cc:325
void SetMesh(Mesh mesh)
Definition node.cc:329
bool IsJoint() const
Definition node.cc:341
Matrix GetLocalTransform() const
Definition node.cc:282
static std::shared_ptr< Node > MakeFromFlatbuffer(const fml::Mapping &ipscene_mapping, Allocator &allocator)
Definition node.cc:47
static std::unique_ptr< Skin > MakeFromFlatbuffer(const fb::Skin &skin, const std::vector< std::shared_ptr< Node > > &scene_nodes)
Definition skin.cc:19
std::vector< std::shared_ptr< FakeTexture > > textures
GAsyncResult * result
#define FML_LOG(severity)
Definition logging.h:82
const char * name
Definition fuchsia.cc:50
FlTexture * texture
Matrix ToMatrix(const std::vector< double > &m)
static std::shared_ptr< Texture > UnpackTextureFromFlatbuffer(const fb::Texture *iptexture, Allocator &allocator)
Definition node.cc:61
static std::atomic_uint64_t kNextNodeID
Definition node.cc:27
std::string SPrintF(const char *format,...)
Definition strings.cc:12
TSize< int64_t > ISize
Definition size.h:138
std::chrono::duration< float > SecondsF
Definition timing.h:13
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
A 4x4 matrix using column-major storage.
Definition matrix.h:37
Matrix Invert() const
Definition matrix.cc:97
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define ERROR(message)
#define VALIDATION_LOG
Definition validation.h:73