| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "impeller/scene/skin.h" | |
| #include <cmath> | |
| #include <memory> | |
| #include <optional> | |
| #include <vector> | |
| #include "flutter/fml/logging.h" | |
| #include "impeller/renderer/allocator.h" | |
| #include "impeller/scene/importer/conversions.h" | |
| namespace impeller { | |
| namespace scene { | |
| std::unique_ptr<Skin> Skin::MakeFromFlatbuffer( | |
| const fb::Skin& skin, | |
| const std::vector<std::shared_ptr<Node>>& scene_nodes) { | |
| if (!skin.joints() || !skin.inverse_bind_matrices() || | |
| skin.joints()->size() != skin.inverse_bind_matrices()->size()) { | |
| VALIDATION_LOG << "Skin data is missing joints or bind matrices."; | |
| return nullptr; | |
| } | |
| Skin result; | |
| result.joints_.reserve(skin.joints()->size()); | |
| for (auto joint : *skin.joints()) { | |
| if (joint < 0 || static_cast<size_t>(joint) > scene_nodes.size()) { | |
| VALIDATION_LOG << "Skin joint index out of range."; | |
| result.joints_.push_back(nullptr); | |
| continue; | |
| } | |
| if (scene_nodes[joint]) { | |
| scene_nodes[joint]->SetIsJoint(true); | |
| } | |
| result.joints_.push_back(scene_nodes[joint]); | |
| } | |
| result.inverse_bind_matrices_.reserve(skin.inverse_bind_matrices()->size()); | |
| for (size_t matrix_i = 0; matrix_i < skin.inverse_bind_matrices()->size(); | |
| matrix_i++) { | |
| const auto* ip_matrix = skin.inverse_bind_matrices()->Get(matrix_i); | |
| Matrix matrix = ip_matrix ? importer::ToMatrix(*ip_matrix) : Matrix(); | |
| result.inverse_bind_matrices_.push_back(matrix); | |
| // Overwrite the joint transforms with the inverse bind pose. | |
| result.joints_[matrix_i]->SetGlobalTransform(matrix.Invert()); | |
| } | |
| return std::make_unique<Skin>(std::move(result)); | |
| } | |
| Skin::Skin() = default; | |
| Skin::~Skin() = default; | |
| Skin::Skin(Skin&&) = default; | |
| Skin& Skin::operator=(Skin&&) = default; | |
| std::shared_ptr<Texture> Skin::GetJointsTexture(Allocator& allocator) { | |
| // Each joint has a matrix. 1 matrix = 16 floats. 1 pixel = 4 floats. | |
| // Therefore, each joint needs 4 pixels. | |
| auto required_pixels = joints_.size() * 4; | |
| auto dimension_size = std::max( | |
| 2u, | |
| Allocation::NextPowerOfTwoSize(std::ceil(std::sqrt(required_pixels)))); | |
| impeller::TextureDescriptor texture_descriptor; | |
| texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; | |
| texture_descriptor.format = PixelFormat::kR32G32B32A32Float; | |
| texture_descriptor.size = {dimension_size, dimension_size}; | |
| texture_descriptor.mip_count = 1u; | |
| auto result = allocator.CreateTexture(texture_descriptor); | |
| result->SetLabel("Joints Texture"); | |
| if (!result) { | |
| FML_LOG(ERROR) << "Could not create joint texture."; | |
| return nullptr; | |
| } | |
| std::vector<Matrix> joints; | |
| joints.resize(result->GetSize().Area() / 4, Matrix()); | |
| FML_DCHECK(joints.size() >= joints_.size()); | |
| for (size_t joint_i = 0; joint_i < joints_.size(); joint_i++) { | |
| const Node* joint = joints_[joint_i].get(); | |
| if (!joint) { | |
| // When a joint is missing, just let it remain as an identity matrix. | |
| continue; | |
| } | |
| // Compute a model space matrix for the joint by walking up the bones to the | |
| // skeleton root. | |
| while (joint && joint->IsJoint()) { | |
| joints[joint_i] = joint->GetLocalTransform() * joints[joint_i]; | |
| joint = joint->GetParent(); | |
| } | |
| // Get the joint transform relative to the default pose of the bone by | |
| // incorporating the joint's inverse bind matrix. The inverse bind matrix | |
| // transforms from model space to the default pose space of the joint. The | |
| // result is a model space matrix that only captures the difference between | |
| // the joint's default pose and the joint's current pose in the scene. This | |
| // is necessary because the skinned model's vertex positions (which _define_ | |
| // the default pose) are all in model space. | |
| joints[joint_i] = joints[joint_i] * inverse_bind_matrices_[joint_i]; | |
| } | |
| if (!result->SetContents(reinterpret_cast<uint8_t*>(joints.data()), | |
| joints.size() * sizeof(Matrix))) { | |
| FML_LOG(ERROR) << "Could not set contents of joint texture."; | |
| return nullptr; | |
| } | |
| return result; | |
| } | |
| } // namespace scene | |
| } // namespace impeller |