Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 27960a7

Browse files
authored
[Impeller Scene] Import animation data (#38583)
* [Impeller Scene] Import animation data * Cast * Use correct length modifier * Fix translation import * Use ++ instead of fetch_add
1 parent 8b17efe commit 27960a7

File tree

8 files changed

+226
-14
lines changed

8 files changed

+226
-14
lines changed

impeller/fixtures/two_triangles.glb

6.64 KB
Binary file not shown.

impeller/scene/importer/conversions.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ Color ToColor(const fb::Color& c) {
5151
/// Impeller -> Flatbuffers
5252
///
5353

54-
std::unique_ptr<fb::Matrix> ToFBMatrix(const Matrix& m) {
54+
fb::Matrix ToFBMatrix(const Matrix& m) {
55+
auto array = std::array<Scalar, 16>{m.m[0], m.m[1], m.m[2], m.m[3], //
56+
m.m[4], m.m[5], m.m[6], m.m[7], //
57+
m.m[8], m.m[9], m.m[10], m.m[11], //
58+
m.m[12], m.m[13], m.m[14], m.m[15]};
59+
return fb::Matrix(array);
60+
}
61+
62+
std::unique_ptr<fb::Matrix> ToFBMatrixUniquePtr(const Matrix& m) {
5563
auto array = std::array<Scalar, 16>{m.m[0], m.m[1], m.m[2], m.m[3], //
5664
m.m[4], m.m[5], m.m[6], m.m[7], //
5765
m.m[8], m.m[9], m.m[10], m.m[11], //

impeller/scene/importer/conversions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ Color ToColor(const fb::Color& c);
3535
/// Impeller -> Flatbuffers
3636
///
3737

38-
std::unique_ptr<fb::Matrix> ToFBMatrix(const Matrix& m);
38+
fb::Matrix ToFBMatrix(const Matrix& m);
39+
40+
std::unique_ptr<fb::Matrix> ToFBMatrixUniquePtr(const Matrix& m);
3941

4042
fb::Vec2 ToFBVec2(const Vector2 v);
4143

impeller/scene/importer/importer_gltf.cc

Lines changed: 163 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ static void ProcessNode(const tinygltf::Model& gltf,
202202
if (in_node.translation.size() == 3) {
203203
transform = transform * Matrix::MakeTranslation(
204204
{static_cast<Scalar>(in_node.translation[0]),
205-
static_cast<Scalar>(in_node.translation[0]),
206-
static_cast<Scalar>(in_node.translation[0])});
205+
static_cast<Scalar>(in_node.translation[1]),
206+
static_cast<Scalar>(in_node.translation[2])});
207207
}
208208
if (in_node.rotation.size() == 4) {
209209
transform = transform * Matrix::MakeRotation(Quaternion(
@@ -226,7 +226,7 @@ static void ProcessNode(const tinygltf::Model& gltf,
226226
}
227227
transform = ToMatrix(in_node.matrix);
228228
}
229-
out_node.transform = ToFBMatrix(transform);
229+
out_node.transform = ToFBMatrixUniquePtr(transform);
230230

231231
//---------------------------------------------------------------------------
232232
/// Static meshes.
@@ -242,13 +242,42 @@ static void ProcessNode(const tinygltf::Model& gltf,
242242
out_node.mesh_primitives.push_back(std::move(mesh_primitive));
243243
}
244244
}
245+
246+
//---------------------------------------------------------------------------
247+
/// Skin.
248+
///
249+
250+
if (WithinRange(in_node.skin, gltf.skins.size())) {
251+
auto& skin = gltf.skins[in_node.skin];
252+
253+
auto ipskin = std::make_unique<fb::SkinT>();
254+
ipskin->joints = skin.joints;
255+
{
256+
std::vector<fb::Matrix> matrices;
257+
auto& matrix_accessor = gltf.accessors[skin.inverseBindMatrices];
258+
auto& matrix_view = gltf.bufferViews[matrix_accessor.bufferView];
259+
auto& matrix_buffer = gltf.buffers[matrix_view.buffer];
260+
for (size_t matrix_i = 0; matrix_i < matrix_accessor.count; matrix_i++) {
261+
auto* s = reinterpret_cast<const float*>(
262+
matrix_buffer.data.data() + matrix_view.byteOffset +
263+
matrix_accessor.ByteStride(matrix_view) * matrix_i);
264+
Matrix m(s[0], s[1], s[2], s[3], //
265+
s[4], s[5], s[6], s[7], //
266+
s[8], s[9], s[10], s[11], //
267+
s[12], s[13], s[14], s[15]);
268+
matrices.push_back(ToFBMatrix(m));
269+
}
270+
ipskin->inverse_bind_matrices = std::move(matrices);
271+
}
272+
ipskin->skeleton = skin.skeleton;
273+
out_node.skin = std::move(ipskin);
274+
}
245275
}
246276

247277
static void ProcessTexture(const tinygltf::Model& gltf,
248278
const tinygltf::Texture& in_texture,
249279
fb::TextureT& out_texture) {
250-
if (in_texture.source < 0 ||
251-
in_texture.source >= static_cast<int>(gltf.images.size())) {
280+
if (!WithinRange(in_texture.source, gltf.images.size())) {
252281
return;
253282
}
254283
auto& image = gltf.images[in_texture.source];
@@ -283,6 +312,128 @@ static void ProcessTexture(const tinygltf::Model& gltf,
283312
out_texture.uri = image.uri;
284313
}
285314

315+
static void ProcessAnimation(const tinygltf::Model& gltf,
316+
const tinygltf::Animation& in_animation,
317+
fb::AnimationT& out_animation) {
318+
out_animation.name = in_animation.name;
319+
320+
std::vector<std::unique_ptr<impeller::fb::ChannelT>> channels;
321+
for (auto& in_channel : in_animation.channels) {
322+
auto out_channel = std::make_unique<fb::ChannelT>();
323+
324+
out_channel->node = in_channel.target_node;
325+
auto& sampler = in_animation.samplers[in_channel.sampler];
326+
327+
/// Keyframe times.
328+
auto& times_accessor = gltf.accessors[sampler.input];
329+
if (times_accessor.count <= 0) {
330+
continue; // Nothing to record.
331+
}
332+
{
333+
auto& times_bufferview = gltf.bufferViews[times_accessor.bufferView];
334+
auto& times_buffer = gltf.buffers[times_bufferview.buffer];
335+
if (times_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
336+
std::cerr << "Unexpected component type \""
337+
<< times_accessor.componentType
338+
<< "\" for animation channel times accessor. Skipping."
339+
<< std::endl;
340+
continue;
341+
}
342+
if (times_accessor.type != TINYGLTF_TYPE_SCALAR) {
343+
std::cerr << "Unexpected type \"" << times_accessor.type
344+
<< "\" for animation channel times accessor. Skipping."
345+
<< std::endl;
346+
continue;
347+
}
348+
for (size_t time_i = 0; time_i < times_accessor.count; time_i++) {
349+
const float* time_p = reinterpret_cast<const float*>(
350+
times_buffer.data.data() + times_bufferview.byteOffset +
351+
times_accessor.ByteStride(times_bufferview) * time_i);
352+
out_channel->timeline.push_back(*time_p);
353+
}
354+
}
355+
356+
/// Keyframe values.
357+
auto& values_accessor = gltf.accessors[sampler.output];
358+
if (values_accessor.count != times_accessor.count) {
359+
std::cerr << "Mismatch between time and value accessors for animation "
360+
"channel. Skipping."
361+
<< std::endl;
362+
continue;
363+
}
364+
{
365+
auto& values_bufferview = gltf.bufferViews[values_accessor.bufferView];
366+
auto& values_buffer = gltf.buffers[values_bufferview.buffer];
367+
if (values_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
368+
std::cerr << "Unexpected component type \""
369+
<< values_accessor.componentType
370+
<< "\" for animation channel values accessor. Skipping."
371+
<< std::endl;
372+
continue;
373+
}
374+
if (in_channel.target_path == "translation") {
375+
if (values_accessor.type != TINYGLTF_TYPE_VEC3) {
376+
std::cerr << "Unexpected type \"" << values_accessor.type
377+
<< "\" for animation channel \"translation\" accessor. "
378+
"Skipping."
379+
<< std::endl;
380+
continue;
381+
}
382+
fb::TranslationKeyframesT keyframes;
383+
for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
384+
const float* value_p = reinterpret_cast<const float*>(
385+
values_buffer.data.data() + values_bufferview.byteOffset +
386+
values_accessor.ByteStride(values_bufferview) * value_i);
387+
keyframes.values.push_back(
388+
fb::Vec3(value_p[0], value_p[1], value_p[2]));
389+
}
390+
out_channel->keyframes.Set(std::move(keyframes));
391+
} else if (in_channel.target_path == "rotation") {
392+
if (values_accessor.type != TINYGLTF_TYPE_VEC4) {
393+
std::cerr << "Unexpected type \"" << values_accessor.type
394+
<< "\" for animation channel \"rotation\" accessor. "
395+
"Skipping."
396+
<< std::endl;
397+
continue;
398+
}
399+
fb::RotationKeyframesT keyframes;
400+
for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
401+
const float* value_p = reinterpret_cast<const float*>(
402+
values_buffer.data.data() + values_bufferview.byteOffset +
403+
values_accessor.ByteStride(values_bufferview) * value_i);
404+
keyframes.values.push_back(
405+
fb::Vec4(value_p[0], value_p[1], value_p[2], value_p[3]));
406+
}
407+
out_channel->keyframes.Set(std::move(keyframes));
408+
} else if (in_channel.target_path == "scale") {
409+
if (values_accessor.type != TINYGLTF_TYPE_VEC3) {
410+
std::cerr << "Unexpected type \"" << values_accessor.type
411+
<< "\" for animation channel \"scale\" accessor. "
412+
"Skipping."
413+
<< std::endl;
414+
continue;
415+
}
416+
fb::ScaleKeyframesT keyframes;
417+
for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
418+
const float* value_p = reinterpret_cast<const float*>(
419+
values_buffer.data.data() + values_bufferview.byteOffset +
420+
values_accessor.ByteStride(values_bufferview) * value_i);
421+
keyframes.values.push_back(
422+
fb::Vec3(value_p[0], value_p[1], value_p[2]));
423+
}
424+
out_channel->keyframes.Set(std::move(keyframes));
425+
} else {
426+
std::cerr << "Unsupported animation channel target path \""
427+
<< in_channel.target_path << "\". Skipping." << std::endl;
428+
continue;
429+
}
430+
}
431+
432+
channels.push_back(std::move(out_channel));
433+
}
434+
out_animation.channels = std::move(channels);
435+
}
436+
286437
bool ParseGLTF(const fml::Mapping& source_mapping, fb::SceneT& out_scene) {
287438
tinygltf::Model gltf;
288439

@@ -319,6 +470,13 @@ bool ParseGLTF(const fml::Mapping& source_mapping, fb::SceneT& out_scene) {
319470
out_scene.nodes.push_back(std::move(node));
320471
}
321472

473+
for (size_t animation_i = 0; animation_i < gltf.animations.size();
474+
animation_i++) {
475+
auto animation = std::make_unique<fb::AnimationT>();
476+
ProcessAnimation(gltf, gltf.animations[animation_i], *animation);
477+
out_scene.animations.push_back(std::move(animation));
478+
}
479+
322480
return true;
323481
}
324482

impeller/scene/importer/importer_unittests.cc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ TEST(ImporterTest, CanParseSkinnedGLTF) {
9191
ASSERT_VECTOR3_NEAR(normal, Vector3(0, 0, 1));
9292

9393
Vector4 tangent = ToVector4(vertex.vertex().tangent());
94-
ASSERT_VECTOR4_NEAR(tangent, Vector4(0, 0, 0, 1));
94+
ASSERT_VECTOR4_NEAR(tangent, Vector4(1, 0, 0, -1));
9595

9696
Vector2 texture_coords = ToVector2(vertex.vertex().texture_coords());
9797
ASSERT_POINT_NEAR(texture_coords, Vector2(0, 1));
@@ -104,6 +104,19 @@ TEST(ImporterTest, CanParseSkinnedGLTF) {
104104

105105
Vector4 weights = ToVector4(vertex.weights());
106106
ASSERT_COLOR_NEAR(weights, Vector4(1, 0, 0, 0));
107+
108+
ASSERT_EQ(scene.animations.size(), 2u);
109+
ASSERT_EQ(scene.animations[0]->name, "Idle");
110+
ASSERT_EQ(scene.animations[1]->name, "Metronome");
111+
ASSERT_EQ(scene.animations[1]->channels.size(), 6u);
112+
auto& channel = scene.animations[1]->channels[4];
113+
ASSERT_EQ(channel->keyframes.type, fb::Keyframes::RotationKeyframes);
114+
auto* keyframes = channel->keyframes.AsRotationKeyframes();
115+
ASSERT_EQ(keyframes->values.size(), 40u);
116+
ASSERT_VECTOR4_NEAR(ToVector4(keyframes->values[0]),
117+
Vector4(0.653281, 0.270598, -0.270598, 0.653281));
118+
ASSERT_VECTOR4_NEAR(ToVector4(keyframes->values[10]),
119+
Vector4(0.425122, 0.565041, -0.565041, 0.425122));
107120
}
108121

109122
} // namespace testing

impeller/scene/importer/scene.fbs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ table Animation {
164164
}
165165

166166
table Skin {
167-
joints: [int]; // Index into `Scene`->`nodes`.
167+
joints: [int]; // Indices into `Scene`->`nodes`.
168168
inverse_bind_matrices: [Matrix];
169169
/// The root joint of the skeleton.
170170
skeleton: int; // Index into `Scene`->`nodes`.
@@ -180,14 +180,14 @@ struct Matrix {
180180

181181
table Node {
182182
name: string;
183-
children: [int]; // Index into `Scene`->`nodes`.
183+
children: [int]; // Indices into `Scene`->`nodes`.
184184
transform: Matrix;
185185
mesh_primitives: [MeshPrimitive];
186186
skin: Skin;
187187
}
188188

189189
table Scene {
190-
children: [int]; // Index into `Scene`->`nodes`.
190+
children: [int]; // Indices into `Scene`->`nodes`.
191191
nodes: [Node];
192192
textures: [Texture]; // Textures may be reused across different materials.
193193
animations: [Animation];

impeller/scene/node.cc

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
#include "impeller/scene/node.h"
66

7+
#include <inttypes.h>
8+
#include <atomic>
79
#include <memory>
810

911
#include "flutter/fml/logging.h"
12+
#include "impeller/base/strings.h"
1013
#include "impeller/base/validation.h"
1114
#include "impeller/geometry/matrix.h"
1215
#include "impeller/scene/importer/conversions.h"
@@ -18,6 +21,8 @@
1821
namespace impeller {
1922
namespace scene {
2023

24+
static std::atomic_uint64_t kNextNodeID = 0;
25+
2126
std::shared_ptr<Node> Node::MakeFromFlatbuffer(
2227
const fml::Mapping& ipscene_mapping,
2328
Allocator& allocator) {
@@ -155,8 +160,7 @@ void Node::UnpackFromFlatbuffer(
155160
const std::vector<std::shared_ptr<Node>>& scene_nodes,
156161
const std::vector<std::shared_ptr<Texture>>& textures,
157162
Allocator& allocator) {
158-
/// Transform.
159-
163+
name_ = source_node.name()->str();
160164
SetLocalTransform(importer::ToMatrix(*source_node.transform()));
161165

162166
/// Meshes.
@@ -190,7 +194,7 @@ void Node::UnpackFromFlatbuffer(
190194
}
191195
}
192196

193-
Node::Node() = default;
197+
Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){};
194198

195199
Node::~Node() = default;
196200

@@ -202,6 +206,26 @@ Node::Node(Node&& node) = default;
202206

203207
Node& Node::operator=(Node&& node) = default;
204208

209+
const std::string& Node::GetName() const {
210+
return name_;
211+
}
212+
213+
void Node::SetName(const std::string& new_name) {
214+
name_ = new_name;
215+
}
216+
217+
std::shared_ptr<Node> Node::FindNodeByName(const std::string& name) const {
218+
for (auto& child : children_) {
219+
if (child->GetName() == name) {
220+
return child;
221+
}
222+
if (auto found = child->FindNodeByName(name)) {
223+
return found;
224+
}
225+
}
226+
return nullptr;
227+
}
228+
205229
void Node::SetLocalTransform(Matrix transform) {
206230
local_transform_ = transform;
207231
}

impeller/scene/node.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include <memory>
8+
#include <optional>
89
#include <vector>
910

1011
#include "flutter/fml/macros.h"
@@ -33,6 +34,11 @@ class Node final {
3334
Node(Node&& node);
3435
Node& operator=(Node&& node);
3536

37+
const std::string& GetName() const;
38+
void SetName(const std::string& new_name);
39+
40+
std::shared_ptr<Node> FindNodeByName(const std::string& name) const;
41+
3642
void SetLocalTransform(Matrix transform);
3743
Matrix GetLocalTransform() const;
3844

@@ -57,6 +63,7 @@ class Node final {
5763
const std::vector<std::shared_ptr<Texture>>& textures,
5864
Allocator& allocator);
5965

66+
std::string name_;
6067
bool is_root_ = false;
6168
Node* parent_ = nullptr;
6269
std::vector<std::shared_ptr<Node>> children_;

0 commit comments

Comments
 (0)