diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5a7f8c8018777..32fb3fed2ef1f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1635,6 +1635,7 @@ ORIGIN: ../../../flutter/impeller/scene/scene_context.cc + ../../../flutter/LICE ORIGIN: ../../../flutter/impeller/scene/scene_context.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/scene_encoder.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/scene_encoder.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/scene/shaders/skinned.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/shaders/unlit.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/shaders/unskinned.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.cc + ../../../flutter/LICENSE @@ -4094,6 +4095,7 @@ FILE: ../../../flutter/impeller/scene/scene_context.cc FILE: ../../../flutter/impeller/scene/scene_context.h FILE: ../../../flutter/impeller/scene/scene_encoder.cc FILE: ../../../flutter/impeller/scene/scene_encoder.h +FILE: ../../../flutter/impeller/scene/shaders/skinned.vert FILE: ../../../flutter/impeller/scene/shaders/unlit.frag FILE: ../../../flutter/impeller/scene/shaders/unskinned.vert FILE: ../../../flutter/impeller/tessellator/c/tessellator.cc diff --git a/impeller/scene/geometry.cc b/impeller/scene/geometry.cc index 7ce1f2eb5fc8d..f39bd7b3b6355 100644 --- a/impeller/scene/geometry.cc +++ b/impeller/scene/geometry.cc @@ -15,6 +15,7 @@ #include "impeller/renderer/vertex_buffer.h" #include "impeller/renderer/vertex_buffer_builder.h" #include "impeller/scene/importer/scene_flatbuffers.h" +#include "impeller/scene/shaders/skinned.vert.h" #include "impeller/scene/shaders/unskinned.vert.h" namespace impeller { @@ -32,14 +33,20 @@ std::shared_ptr Geometry::MakeCuboid(Vector3 size) { return result; } -std::shared_ptr Geometry::MakeVertexBuffer( - VertexBuffer vertex_buffer) { - auto result = std::make_shared(); - result->SetVertexBuffer(std::move(vertex_buffer)); - return result; +std::shared_ptr Geometry::MakeVertexBuffer(VertexBuffer vertex_buffer, + bool is_skinned) { + if (is_skinned) { + auto result = std::make_shared(); + result->SetVertexBuffer(std::move(vertex_buffer)); + return result; + } else { + auto result = std::make_shared(); + result->SetVertexBuffer(std::move(vertex_buffer)); + return result; + } } -std::shared_ptr Geometry::MakeFromFlatbuffer( +std::shared_ptr Geometry::MakeFromFlatbuffer( const fb::MeshPrimitive& mesh, Allocator& allocator) { IndexType index_type; @@ -52,17 +59,34 @@ std::shared_ptr Geometry::MakeFromFlatbuffer( break; } - if (mesh.vertices_type() == fb::VertexBuffer::SkinnedVertexBuffer) { - VALIDATION_LOG << "Skinned meshes not yet supported."; - return nullptr; - } - if (mesh.vertices_type() != fb::VertexBuffer::UnskinnedVertexBuffer) { - VALIDATION_LOG << "Invalid vertex buffer type."; - return nullptr; + const uint8_t* vertices_start; + size_t vertices_bytes; + bool is_skinned; + + switch (mesh.vertices_type()) { + case fb::VertexBuffer::UnskinnedVertexBuffer: { + const auto* vertices = + mesh.vertices_as_UnskinnedVertexBuffer()->vertices(); + vertices_start = reinterpret_cast(vertices->Get(0)); + vertices_bytes = vertices->size() * sizeof(fb::Vertex); + is_skinned = false; + break; + } + case fb::VertexBuffer::SkinnedVertexBuffer: { + const auto* vertices = mesh.vertices_as_SkinnedVertexBuffer()->vertices(); + vertices_start = reinterpret_cast(vertices->Get(0)); + vertices_bytes = vertices->size() * sizeof(fb::SkinnedVertex); + is_skinned = true; + break; + } + case fb::VertexBuffer::NONE: + VALIDATION_LOG << "Invalid vertex buffer type."; + return nullptr; } - const auto* vertices = mesh.vertices_as_UnskinnedVertexBuffer()->vertices(); - const size_t vertices_bytes = vertices->size() * sizeof(fb::Vertex); + const uint8_t* indices_start = + reinterpret_cast(mesh.indices()->data()->Data()); + const size_t indices_bytes = mesh.indices()->data()->size(); if (vertices_bytes == 0 || indices_bytes == 0) { return nullptr; @@ -75,11 +99,6 @@ std::shared_ptr Geometry::MakeFromFlatbuffer( auto buffer = allocator.CreateBuffer(buffer_desc); buffer->SetLabel("Mesh vertices+indices"); - const uint8_t* vertices_start = - reinterpret_cast(vertices->Get(0)); - const uint8_t* indices_start = - reinterpret_cast(mesh.indices()->data()->Data()); - if (!buffer->CopyHostBuffer(vertices_start, Range(0, vertices_bytes))) { return nullptr; } @@ -95,7 +114,7 @@ std::shared_ptr Geometry::MakeFromFlatbuffer( .index_count = mesh.indices()->count(), .index_type = index_type, }; - return MakeVertexBuffer(std::move(vertex_buffer)); + return MakeVertexBuffer(std::move(vertex_buffer), is_skinned); } //------------------------------------------------------------------------------ @@ -151,32 +170,35 @@ void CuboidGeometry::BindToCommand(const SceneContext& scene_context, } //------------------------------------------------------------------------------ -/// VertexBufferGeometry +/// UnskinnedVertexBufferGeometry /// -VertexBufferGeometry::VertexBufferGeometry() = default; +UnskinnedVertexBufferGeometry::UnskinnedVertexBufferGeometry() = default; -VertexBufferGeometry::~VertexBufferGeometry() = default; +UnskinnedVertexBufferGeometry::~UnskinnedVertexBufferGeometry() = default; -void VertexBufferGeometry::SetVertexBuffer(VertexBuffer vertex_buffer) { +void UnskinnedVertexBufferGeometry::SetVertexBuffer( + VertexBuffer vertex_buffer) { vertex_buffer_ = std::move(vertex_buffer); } // |Geometry| -GeometryType VertexBufferGeometry::GetGeometryType() const { +GeometryType UnskinnedVertexBufferGeometry::GetGeometryType() const { return GeometryType::kUnskinned; } // |Geometry| -VertexBuffer VertexBufferGeometry::GetVertexBuffer(Allocator& allocator) const { +VertexBuffer UnskinnedVertexBufferGeometry::GetVertexBuffer( + Allocator& allocator) const { return vertex_buffer_; } // |Geometry| -void VertexBufferGeometry::BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - Command& command) const { +void UnskinnedVertexBufferGeometry::BindToCommand( + const SceneContext& scene_context, + HostBuffer& buffer, + const Matrix& transform, + Command& command) const { command.BindVertices( GetVertexBuffer(*scene_context.GetContext()->GetResourceAllocator())); @@ -185,5 +207,42 @@ void VertexBufferGeometry::BindToCommand(const SceneContext& scene_context, UnskinnedVertexShader::BindVertInfo(command, buffer.EmplaceUniform(info)); } +//------------------------------------------------------------------------------ +/// SkinnedVertexBufferGeometry +/// + +SkinnedVertexBufferGeometry::SkinnedVertexBufferGeometry() = default; + +SkinnedVertexBufferGeometry::~SkinnedVertexBufferGeometry() = default; + +void SkinnedVertexBufferGeometry::SetVertexBuffer(VertexBuffer vertex_buffer) { + vertex_buffer_ = std::move(vertex_buffer); +} + +// |Geometry| +GeometryType SkinnedVertexBufferGeometry::GetGeometryType() const { + return GeometryType::kSkinned; +} + +// |Geometry| +VertexBuffer SkinnedVertexBufferGeometry::GetVertexBuffer( + Allocator& allocator) const { + return vertex_buffer_; +} + +// |Geometry| +void SkinnedVertexBufferGeometry::BindToCommand( + const SceneContext& scene_context, + HostBuffer& buffer, + const Matrix& transform, + Command& command) const { + command.BindVertices( + GetVertexBuffer(*scene_context.GetContext()->GetResourceAllocator())); + + SkinnedVertexShader::VertInfo info; + info.mvp = transform; + SkinnedVertexShader::BindVertInfo(command, buffer.EmplaceUniform(info)); +} + } // namespace scene } // namespace impeller diff --git a/impeller/scene/geometry.h b/impeller/scene/geometry.h index f056fe11eb8a5..b38850080b6e6 100644 --- a/impeller/scene/geometry.h +++ b/impeller/scene/geometry.h @@ -22,7 +22,7 @@ namespace impeller { namespace scene { class CuboidGeometry; -class VertexBufferGeometry; +class UnskinnedVertexBufferGeometry; class Geometry { public: @@ -30,10 +30,10 @@ class Geometry { static std::shared_ptr MakeCuboid(Vector3 size); - static std::shared_ptr MakeVertexBuffer( - VertexBuffer vertex_buffer); + static std::shared_ptr MakeVertexBuffer(VertexBuffer vertex_buffer, + bool is_skinned); - static std::shared_ptr MakeFromFlatbuffer( + static std::shared_ptr MakeFromFlatbuffer( const fb::MeshPrimitive& mesh, Allocator& allocator); @@ -73,11 +73,11 @@ class CuboidGeometry final : public Geometry { FML_DISALLOW_COPY_AND_ASSIGN(CuboidGeometry); }; -class VertexBufferGeometry final : public Geometry { +class UnskinnedVertexBufferGeometry final : public Geometry { public: - VertexBufferGeometry(); + UnskinnedVertexBufferGeometry(); - ~VertexBufferGeometry() override; + ~UnskinnedVertexBufferGeometry() override; void SetVertexBuffer(VertexBuffer vertex_buffer); @@ -96,7 +96,33 @@ class VertexBufferGeometry final : public Geometry { private: VertexBuffer vertex_buffer_; - FML_DISALLOW_COPY_AND_ASSIGN(VertexBufferGeometry); + FML_DISALLOW_COPY_AND_ASSIGN(UnskinnedVertexBufferGeometry); +}; + +class SkinnedVertexBufferGeometry final : public Geometry { + public: + SkinnedVertexBufferGeometry(); + + ~SkinnedVertexBufferGeometry() override; + + void SetVertexBuffer(VertexBuffer vertex_buffer); + + // |Geometry| + GeometryType GetGeometryType() const override; + + // |Geometry| + VertexBuffer GetVertexBuffer(Allocator& allocator) const override; + + // |Geometry| + void BindToCommand(const SceneContext& scene_context, + HostBuffer& buffer, + const Matrix& transform, + Command& command) const override; + + private: + VertexBuffer vertex_buffer_; + + FML_DISALLOW_COPY_AND_ASSIGN(SkinnedVertexBufferGeometry); }; } // namespace scene diff --git a/impeller/scene/importer/importer_gltf.cc b/impeller/scene/importer/importer_gltf.cc index c68e3d03384eb..87fe6b73767e8 100644 --- a/impeller/scene/importer/importer_gltf.cc +++ b/impeller/scene/importer/importer_gltf.cc @@ -27,12 +27,19 @@ static const std::map kAttributes = {"NORMAL", VerticesBuilder::AttributeType::kNormal}, {"TANGENT", VerticesBuilder::AttributeType::kTangent}, {"TEXCOORD_0", VerticesBuilder::AttributeType::kTextureCoords}, - {"COLOR_0", VerticesBuilder::AttributeType::kColor}}; + {"COLOR_0", VerticesBuilder::AttributeType::kColor}, + {"JOINTS_0", VerticesBuilder::AttributeType::kJoints}, + {"WEIGHTS_0", VerticesBuilder::AttributeType::kWeights}}; static bool WithinRange(int index, size_t size) { return index >= 0 && static_cast(index) < size; } +static bool MeshPrimitiveIsSkinned(const tinygltf::Primitive& primitive) { + return primitive.attributes.find("JOINTS_0") != primitive.attributes.end() && + primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end(); +} + static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, const tinygltf::Primitive& primitive, fb::MeshPrimitiveT& mesh_primitive) { @@ -41,7 +48,10 @@ static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, /// { - VerticesBuilder builder; + bool is_skinned = MeshPrimitiveIsSkinned(primitive); + std::unique_ptr builder = + is_skinned ? VerticesBuilder::MakeSkinned() + : VerticesBuilder::MakeUnskinned(); for (const auto& attribute : primitive.attributes) { auto attribute_type = kAttributes.find(attribute.first); @@ -50,6 +60,14 @@ static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, << "\" not supported." << std::endl; continue; } + if (!is_skinned && + (attribute_type->second == VerticesBuilder::AttributeType::kJoints || + attribute_type->second == + VerticesBuilder::AttributeType::kWeights)) { + // If the primitive doesn't have enough information to be skinned, skip + // skinning-related attributes. + continue; + } const auto accessor = gltf.accessors[attribute.second]; const auto view = gltf.bufferViews[accessor.bufferView]; @@ -86,14 +104,15 @@ static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, continue; } - builder.SetAttributeFromBuffer(attribute_type->second, // attribute - type, // component_type - source_start, // buffer_start - accessor.ByteStride(view), // stride_bytes - accessor.count); // count + builder->SetAttributeFromBuffer( + attribute_type->second, // attribute + type, // component_type + source_start, // buffer_start + accessor.ByteStride(view), // stride_bytes + accessor.count); // count } - builder.WriteFBVertices(mesh_primitive); + builder->WriteFBVertices(mesh_primitive); } //--------------------------------------------------------------------------- diff --git a/impeller/scene/importer/importer_unittests.cc b/impeller/scene/importer/importer_unittests.cc index 8a4fd7eab1212..1dd20b56169cc 100644 --- a/impeller/scene/importer/importer_unittests.cc +++ b/impeller/scene/importer/importer_unittests.cc @@ -14,7 +14,7 @@ namespace scene { namespace importer { namespace testing { -TEST(ImporterTest, CanParseGLTF) { +TEST(ImporterTest, CanParseUnskinnedGLTF) { auto mapping = flutter::testing::OpenFixtureAsMapping("flutter_logo.glb"); fb::SceneT scene; @@ -55,6 +55,56 @@ TEST(ImporterTest, CanParseGLTF) { ASSERT_COLOR_NEAR(color, Color(0.0221714, 0.467781, 0.921584, 1)); } +TEST(ImporterTest, CanParseSkinnedGLTF) { + auto mapping = flutter::testing::OpenFixtureAsMapping("two_triangles.glb"); + + fb::SceneT scene; + ASSERT_TRUE(ParseGLTF(*mapping, scene)); + + ASSERT_EQ(scene.children.size(), 1u); + auto& node = scene.nodes[scene.children[0]]; + + Matrix node_transform = ToMatrix(*node->transform); + ASSERT_MATRIX_NEAR(node_transform, Matrix()); + + ASSERT_EQ(node->mesh_primitives.size(), 0u); + ASSERT_EQ(node->children.size(), 2u); + + // The skinned node contains both a skeleton and skinned mesh primitives that + // reference bones in the skeleton. + auto& skinned_node = scene.nodes[node->children[0]]; + ASSERT_EQ(skinned_node->mesh_primitives.size(), 2u); + auto& bottom_triangle = *skinned_node->mesh_primitives[0]; + ASSERT_EQ(bottom_triangle.indices->count, 3u); + + ASSERT_EQ(bottom_triangle.vertices.type, + fb::VertexBuffer::SkinnedVertexBuffer); + auto& vertices = bottom_triangle.vertices.AsSkinnedVertexBuffer()->vertices; + ASSERT_EQ(vertices.size(), 3u); + auto& vertex = vertices[0]; + + Vector3 position = ToVector3(vertex.vertex().position()); + ASSERT_VECTOR3_NEAR(position, Vector3(1, 1, 0)); + + Vector3 normal = ToVector3(vertex.vertex().normal()); + ASSERT_VECTOR3_NEAR(normal, Vector3(0, 0, 1)); + + Vector4 tangent = ToVector4(vertex.vertex().tangent()); + ASSERT_VECTOR4_NEAR(tangent, Vector4(0, 0, 0, 1)); + + Vector2 texture_coords = ToVector2(vertex.vertex().texture_coords()); + ASSERT_POINT_NEAR(texture_coords, Vector2(0, 1)); + + Color color = ToColor(vertex.vertex().color()); + ASSERT_COLOR_NEAR(color, Color(1, 1, 1, 1)); + + Vector4 joints = ToVector4(vertex.joints()); + ASSERT_COLOR_NEAR(joints, Vector4(0, 0, 0, 0)); + + Vector4 weights = ToVector4(vertex.weights()); + ASSERT_COLOR_NEAR(weights, Vector4(1, 0, 0, 0)); +} + } // namespace testing } // namespace importer } // namespace scene diff --git a/impeller/scene/importer/vertices_builder.cc b/impeller/scene/importer/vertices_builder.cc index 3ddc6d45230b2..14f56dc214d54 100644 --- a/impeller/scene/importer/vertices_builder.cc +++ b/impeller/scene/importer/vertices_builder.cc @@ -17,29 +17,36 @@ namespace impeller { namespace scene { namespace importer { -VerticesBuilder::VerticesBuilder() = default; +//------------------------------------------------------------------------------ +/// VerticesBuilder +/// -void VerticesBuilder::WriteFBVertices(fb::MeshPrimitiveT& primitive) const { - auto vertex_buffer = fb::UnskinnedVertexBufferT(); - vertex_buffer.vertices.resize(0); - for (auto& v : vertices_) { - vertex_buffer.vertices.push_back(fb::Vertex( - ToFBVec3(v.position), ToFBVec3(v.normal), ToFBVec4(v.tangent), - ToFBVec2(v.texture_coords), ToFBColor(v.color))); - } - primitive.vertices.Set(std::move(vertex_buffer)); +std::unique_ptr VerticesBuilder::MakeUnskinned() { + return std::make_unique(); +} + +std::unique_ptr VerticesBuilder::MakeSkinned() { + return std::make_unique(); } +VerticesBuilder::VerticesBuilder() = default; + +VerticesBuilder::~VerticesBuilder() = default; + /// @brief Reads a numeric component from `source` and returns a 32bit float. -/// Signed SourceTypes convert to a range of -1 to 1, and unsigned -/// SourceTypes convert to a range of 0 to 1. +/// If `normalized` is `true`, signed SourceTypes convert to a range of +/// -1 to 1, and unsigned SourceTypes convert to a range of 0 to 1. template -static Scalar ToNormalizedScalar(const void* source, size_t index) { - constexpr SourceType divisor = std::is_integral_v - ? std::numeric_limits::max() - : 1; +static Scalar ToScalar(const void* source, size_t index, bool normalized) { const SourceType* s = reinterpret_cast(source) + index; - return static_cast(*s) / static_cast(divisor); + Scalar result = static_cast(*s); + if (normalized) { + constexpr SourceType divisor = std::is_integral_v + ? std::numeric_limits::max() + : 1; + result = static_cast(*s) / static_cast(divisor); + } + return result; } /// @brief A ComponentWriter which simply converts all of an attribute's @@ -53,7 +60,8 @@ static void PassthroughAttributeWriter( attribute.component_count * sizeof(Scalar)); for (size_t component_i = 0; component_i < attribute.component_count; component_i++) { - *(destination + component_i) = component.convert_proc(source, component_i); + *(destination + component_i) = + component.convert_proc(source, component_i, true); } } @@ -65,87 +73,181 @@ static void PositionAttributeWriter( const VerticesBuilder::ComponentProperties& component, const VerticesBuilder::AttributeProperties& attribute) { FML_DCHECK(attribute.component_count == 3); - *(destination + 0) = component.convert_proc(source, 0); - *(destination + 1) = component.convert_proc(source, 1); - *(destination + 2) = -component.convert_proc(source, 2); + *(destination + 0) = component.convert_proc(source, 0, true); + *(destination + 1) = component.convert_proc(source, 1, true); + *(destination + 2) = -component.convert_proc(source, 2, true); +} + +/// @brief A ComponentWriter which converts four vertex indices to scalars. +static void JointsAttributeWriter( + Scalar* destination, + const void* source, + const VerticesBuilder::ComponentProperties& component, + const VerticesBuilder::AttributeProperties& attribute) { + FML_DCHECK(attribute.component_count == 4); + for (int i = 0; i < 4; i++) { + *(destination + i) = component.convert_proc(source, i, false); + } } std::map VerticesBuilder::kAttributeTypes = { {VerticesBuilder::AttributeType::kPosition, - {.offset_bytes = offsetof(Vertex, position), - .size_bytes = sizeof(Vertex::position), + {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, position), + .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::position), .component_count = 3, .write_proc = PositionAttributeWriter}}, {VerticesBuilder::AttributeType::kNormal, - {.offset_bytes = offsetof(Vertex, normal), - .size_bytes = sizeof(Vertex::normal), + {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, normal), + .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::normal), .component_count = 3, .write_proc = PassthroughAttributeWriter}}, {VerticesBuilder::AttributeType::kTangent, - {.offset_bytes = offsetof(Vertex, tangent), - .size_bytes = sizeof(Vertex::tangent), + {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, tangent), + .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::tangent), .component_count = 4, .write_proc = PassthroughAttributeWriter}}, {VerticesBuilder::AttributeType::kTextureCoords, - {.offset_bytes = offsetof(Vertex, texture_coords), - .size_bytes = sizeof(Vertex::texture_coords), + {.offset_bytes = + offsetof(UnskinnedVerticesBuilder::Vertex, texture_coords), + .size_bytes = + sizeof(UnskinnedVerticesBuilder::Vertex::texture_coords), .component_count = 2, .write_proc = PassthroughAttributeWriter}}, {VerticesBuilder::AttributeType::kColor, - {.offset_bytes = offsetof(Vertex, color), - .size_bytes = sizeof(Vertex::color), + {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, color), + .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::color), + .component_count = 4, + .write_proc = PassthroughAttributeWriter}}, + {VerticesBuilder::AttributeType::kJoints, + {.offset_bytes = offsetof(SkinnedVerticesBuilder::Vertex, joints), + .size_bytes = sizeof(SkinnedVerticesBuilder::Vertex::joints), .component_count = 4, - .write_proc = PassthroughAttributeWriter}}}; + .write_proc = JointsAttributeWriter}}, + {VerticesBuilder::AttributeType::kWeights, + {.offset_bytes = offsetof(SkinnedVerticesBuilder::Vertex, weights), + .size_bytes = sizeof(SkinnedVerticesBuilder::Vertex::weights), + .component_count = 4, + .write_proc = JointsAttributeWriter}}}; static std::map kComponentTypes = { {VerticesBuilder::ComponentType::kSignedByte, - {.size_bytes = sizeof(int8_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int8_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kUnsignedByte, - {.size_bytes = sizeof(int8_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int8_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kSignedShort, - {.size_bytes = sizeof(int16_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int16_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kUnsignedShort, - {.size_bytes = sizeof(int16_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int16_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kSignedInt, - {.size_bytes = sizeof(int32_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int32_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kUnsignedInt, - {.size_bytes = sizeof(int32_t), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(int32_t), .convert_proc = ToScalar}}, {VerticesBuilder::ComponentType::kFloat, - {.size_bytes = sizeof(float), - .convert_proc = ToNormalizedScalar}}, + {.size_bytes = sizeof(float), .convert_proc = ToScalar}}, }; -void VerticesBuilder::SetAttributeFromBuffer(AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) { - if (attribute_count > vertices_.size()) { - vertices_.resize(attribute_count, Vertex()); - } - +void VerticesBuilder::WriteAttribute(void* destination, + size_t destination_stride_bytes, + AttributeType attribute, + ComponentType component_type, + const void* source, + size_t attribute_stride_bytes, + size_t attribute_count) { const ComponentProperties& component_props = kComponentTypes[component_type]; const AttributeProperties& attribute_props = kAttributeTypes[attribute]; for (size_t i = 0; i < attribute_count; i++) { - const uint8_t* source = reinterpret_cast(buffer_start) + - attribute_stride_bytes * i; - uint8_t* destination = reinterpret_cast(&vertices_.data()[i]) + - attribute_props.offset_bytes; + const uint8_t* src = + reinterpret_cast(source) + attribute_stride_bytes * i; + uint8_t* dst = reinterpret_cast(destination) + + i * destination_stride_bytes + attribute_props.offset_bytes; - attribute_props.write_proc(reinterpret_cast(destination), source, + attribute_props.write_proc(reinterpret_cast(dst), src, component_props, attribute_props); } } +//------------------------------------------------------------------------------ +/// UnskinnedVerticesBuilder +/// + +UnskinnedVerticesBuilder::UnskinnedVerticesBuilder() = default; + +UnskinnedVerticesBuilder::~UnskinnedVerticesBuilder() = default; + +void UnskinnedVerticesBuilder::WriteFBVertices( + fb::MeshPrimitiveT& primitive) const { + auto vertex_buffer = fb::UnskinnedVertexBufferT(); + vertex_buffer.vertices.resize(0); + for (auto& v : vertices_) { + vertex_buffer.vertices.push_back(fb::Vertex( + ToFBVec3(v.position), ToFBVec3(v.normal), ToFBVec4(v.tangent), + ToFBVec2(v.texture_coords), ToFBColor(v.color))); + } + primitive.vertices.Set(std::move(vertex_buffer)); +} + +void UnskinnedVerticesBuilder::SetAttributeFromBuffer( + AttributeType attribute, + ComponentType component_type, + const void* buffer_start, + size_t attribute_stride_bytes, + size_t attribute_count) { + if (attribute_count > vertices_.size()) { + vertices_.resize(attribute_count, Vertex()); + } + WriteAttribute(vertices_.data(), // destination + sizeof(Vertex), // destination_stride_bytes + attribute, // attribute + component_type, // component_type + buffer_start, // source + attribute_stride_bytes, // attribute_stride_bytes + attribute_count); // attribute_count +} + +//------------------------------------------------------------------------------ +/// SkinnedVerticesBuilder +/// + +SkinnedVerticesBuilder::SkinnedVerticesBuilder() = default; + +SkinnedVerticesBuilder::~SkinnedVerticesBuilder() = default; + +void SkinnedVerticesBuilder::WriteFBVertices( + fb::MeshPrimitiveT& primitive) const { + auto vertex_buffer = fb::SkinnedVertexBufferT(); + vertex_buffer.vertices.resize(0); + for (auto& v : vertices_) { + auto unskinned_attributes = fb::Vertex( + ToFBVec3(v.vertex.position), ToFBVec3(v.vertex.normal), + ToFBVec4(v.vertex.tangent), ToFBVec2(v.vertex.texture_coords), + ToFBColor(v.vertex.color)); + vertex_buffer.vertices.push_back(fb::SkinnedVertex( + unskinned_attributes, ToFBVec4(v.joints), ToFBVec4(v.weights))); + } + primitive.vertices.Set(std::move(vertex_buffer)); +} + +void SkinnedVerticesBuilder::SetAttributeFromBuffer( + AttributeType attribute, + ComponentType component_type, + const void* buffer_start, + size_t attribute_stride_bytes, + size_t attribute_count) { + if (attribute_count > vertices_.size()) { + vertices_.resize(attribute_count, Vertex()); + } + WriteAttribute(vertices_.data(), // destination + sizeof(Vertex), // destination_stride_bytes + attribute, // attribute + component_type, // component_type + buffer_start, // source + attribute_stride_bytes, // attribute_stride_bytes + attribute_count); // attribute_count +} + } // namespace importer } // namespace scene } // namespace impeller diff --git a/impeller/scene/importer/vertices_builder.h b/impeller/scene/importer/vertices_builder.h index 174d0fe5c4e7a..2ebc343aa672e 100644 --- a/impeller/scene/importer/vertices_builder.h +++ b/impeller/scene/importer/vertices_builder.h @@ -15,8 +15,16 @@ namespace impeller { namespace scene { namespace importer { +//------------------------------------------------------------------------------ +/// VerticesBuilder +/// + class VerticesBuilder { public: + static std::unique_ptr MakeUnskinned(); + + static std::unique_ptr MakeSkinned(); + enum class ComponentType { kSignedByte = 5120, kUnsignedByte, @@ -33,10 +41,12 @@ class VerticesBuilder { kTangent, kTextureCoords, kColor, + kJoints, + kWeights, }; - using ComponentConverter = - std::function; + using ComponentConverter = std::function< + Scalar(const void* source, size_t byte_offset, bool normalized)>; struct ComponentProperties { size_t size_bytes = 0; ComponentConverter convert_proc; @@ -57,19 +67,39 @@ class VerticesBuilder { VerticesBuilder(); - void WriteFBVertices(fb::MeshPrimitiveT& primitive) const; + virtual ~VerticesBuilder(); - void SetAttributeFromBuffer(AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count); + virtual void WriteFBVertices(fb::MeshPrimitiveT& primitive) const = 0; + + virtual void SetAttributeFromBuffer(AttributeType attribute, + ComponentType component_type, + const void* buffer_start, + size_t attribute_stride_bytes, + size_t attribute_count) = 0; + + protected: + static void WriteAttribute(void* destination, + size_t destination_stride_bytes, + AttributeType attribute, + ComponentType component_type, + const void* source, + size_t attribute_stride_bytes, + size_t attribute_count); private: static std::map kAttributeTypes; + FML_DISALLOW_COPY_AND_ASSIGN(VerticesBuilder); +}; + +//------------------------------------------------------------------------------ +/// UnskinnedVerticesBuilder +/// + +class UnskinnedVerticesBuilder final : public VerticesBuilder { + public: struct Vertex { Vector3 position; Vector3 normal; @@ -78,9 +108,56 @@ class VerticesBuilder { Color color = Color::White(); }; + UnskinnedVerticesBuilder(); + + virtual ~UnskinnedVerticesBuilder() override; + + // |VerticesBuilder| + void WriteFBVertices(fb::MeshPrimitiveT& primitive) const override; + + // |VerticesBuilder| + void SetAttributeFromBuffer(AttributeType attribute, + ComponentType component_type, + const void* buffer_start, + size_t attribute_stride_bytes, + size_t attribute_count) override; + + private: + std::vector vertices_; + + FML_DISALLOW_COPY_AND_ASSIGN(UnskinnedVerticesBuilder); +}; + +//------------------------------------------------------------------------------ +/// SkinnedVerticesBuilder +/// + +class SkinnedVerticesBuilder final : public VerticesBuilder { + public: + struct Vertex { + UnskinnedVerticesBuilder::Vertex vertex; + Vector4 joints; + Vector4 weights; + }; + + SkinnedVerticesBuilder(); + + virtual ~SkinnedVerticesBuilder() override; + + // |VerticesBuilder| + void WriteFBVertices(fb::MeshPrimitiveT& primitive) const override; + + // |VerticesBuilder| + void SetAttributeFromBuffer(AttributeType attribute, + ComponentType component_type, + const void* buffer_start, + size_t attribute_stride_bytes, + size_t attribute_count) override; + + private: std::vector vertices_; - FML_DISALLOW_COPY_AND_ASSIGN(VerticesBuilder); + FML_DISALLOW_COPY_AND_ASSIGN(SkinnedVerticesBuilder); }; } // namespace importer diff --git a/impeller/scene/pipeline_key.h b/impeller/scene/pipeline_key.h index c4cd3e07c6421..d059a48e47ae0 100644 --- a/impeller/scene/pipeline_key.h +++ b/impeller/scene/pipeline_key.h @@ -11,7 +11,8 @@ namespace scene { enum class GeometryType { kUnskinned = 0, - kLastType = kUnskinned, + kSkinned = 1, + kLastType = kSkinned, }; enum class MaterialType { kUnlit = 0, diff --git a/impeller/scene/scene_context.cc b/impeller/scene/scene_context.cc index 359f18b24582c..3f1ab5ba6cd85 100644 --- a/impeller/scene/scene_context.cc +++ b/impeller/scene/scene_context.cc @@ -5,6 +5,7 @@ #include "impeller/scene/scene_context.h" #include "impeller/renderer/formats.h" #include "impeller/scene/material.h" +#include "impeller/scene/shaders/skinned.vert.h" #include "impeller/scene/shaders/unlit.frag.h" #include "impeller/scene/shaders/unskinned.vert.h" @@ -38,6 +39,8 @@ SceneContext::SceneContext(std::shared_ptr context) pipelines_[{PipelineKey{GeometryType::kUnskinned, MaterialType::kUnlit}}] = MakePipelineVariants( *context_); + pipelines_[{PipelineKey{GeometryType::kSkinned, MaterialType::kUnlit}}] = + MakePipelineVariants(*context_); { impeller::TextureDescriptor texture_descriptor; diff --git a/impeller/scene/shaders/BUILD.gn b/impeller/scene/shaders/BUILD.gn index f17b6719a7b58..c4622c74b9ffb 100644 --- a/impeller/scene/shaders/BUILD.gn +++ b/impeller/scene/shaders/BUILD.gn @@ -8,6 +8,7 @@ impeller_shaders("shaders") { name = "scene" shaders = [ + "skinned.vert", "unskinned.vert", "unlit.frag", ] diff --git a/impeller/scene/shaders/skinned.vert b/impeller/scene/shaders/skinned.vert new file mode 100644 index 0000000000000..887ce8f1f3778 --- /dev/null +++ b/impeller/scene/shaders/skinned.vert @@ -0,0 +1,40 @@ +// 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. + +uniform VertInfo { + mat4 mvp; +} +vert_info; + +// This attribute layout is expected to be identical to `SkinnedVertex` within +// `impeller/scene/importer/scene.fbs`. +in vec3 position; +in vec3 normal; +in vec4 tangent; +in vec2 texture_coords; +in vec4 color; +// TODO(bdero): Use the joint indices to sample bone matrices from a texture. +in vec4 joints; +in vec4 weights; + +out vec3 v_position; +out mat3 v_tangent_space; +out vec2 v_texture_coords; +out vec4 v_color; + +void main() { + // The following two lines are temporary placeholders to prevent the vertex + // attributes from being removed from the shader. + v_color = joints; + v_color = weights; + + gl_Position = vert_info.mvp * vec4(position, 1.0); + v_position = gl_Position.xyz; + + vec3 lh_tangent = tangent.xyz * tangent.w; + v_tangent_space = + mat3(vert_info.mvp) * mat3(lh_tangent, cross(normal, lh_tangent), normal); + v_texture_coords = texture_coords; + v_color = color; +}