[Impeller Scene] Import skinned mesh vertex data (flutter/engine#38554)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<CuboidGeometry> Geometry::MakeCuboid(Vector3 size) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<VertexBufferGeometry> Geometry::MakeVertexBuffer(
|
||||
VertexBuffer vertex_buffer) {
|
||||
auto result = std::make_shared<VertexBufferGeometry>();
|
||||
result->SetVertexBuffer(std::move(vertex_buffer));
|
||||
return result;
|
||||
std::shared_ptr<Geometry> Geometry::MakeVertexBuffer(VertexBuffer vertex_buffer,
|
||||
bool is_skinned) {
|
||||
if (is_skinned) {
|
||||
auto result = std::make_shared<SkinnedVertexBufferGeometry>();
|
||||
result->SetVertexBuffer(std::move(vertex_buffer));
|
||||
return result;
|
||||
} else {
|
||||
auto result = std::make_shared<UnskinnedVertexBufferGeometry>();
|
||||
result->SetVertexBuffer(std::move(vertex_buffer));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<VertexBufferGeometry> Geometry::MakeFromFlatbuffer(
|
||||
std::shared_ptr<Geometry> Geometry::MakeFromFlatbuffer(
|
||||
const fb::MeshPrimitive& mesh,
|
||||
Allocator& allocator) {
|
||||
IndexType index_type;
|
||||
@@ -52,17 +59,34 @@ std::shared_ptr<VertexBufferGeometry> 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<const uint8_t*>(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<const uint8_t*>(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<const uint8_t*>(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<VertexBufferGeometry> Geometry::MakeFromFlatbuffer(
|
||||
auto buffer = allocator.CreateBuffer(buffer_desc);
|
||||
buffer->SetLabel("Mesh vertices+indices");
|
||||
|
||||
const uint8_t* vertices_start =
|
||||
reinterpret_cast<const uint8_t*>(vertices->Get(0));
|
||||
const uint8_t* indices_start =
|
||||
reinterpret_cast<const uint8_t*>(mesh.indices()->data()->Data());
|
||||
|
||||
if (!buffer->CopyHostBuffer(vertices_start, Range(0, vertices_bytes))) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -95,7 +114,7 @@ std::shared_ptr<VertexBufferGeometry> 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
|
||||
|
||||
@@ -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<CuboidGeometry> MakeCuboid(Vector3 size);
|
||||
|
||||
static std::shared_ptr<VertexBufferGeometry> MakeVertexBuffer(
|
||||
VertexBuffer vertex_buffer);
|
||||
static std::shared_ptr<Geometry> MakeVertexBuffer(VertexBuffer vertex_buffer,
|
||||
bool is_skinned);
|
||||
|
||||
static std::shared_ptr<VertexBufferGeometry> MakeFromFlatbuffer(
|
||||
static std::shared_ptr<Geometry> 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
|
||||
|
||||
@@ -27,12 +27,19 @@ static const std::map<std::string, VerticesBuilder::AttributeType> 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<size_t>(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<VerticesBuilder> 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);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> VerticesBuilder::MakeUnskinned() {
|
||||
return std::make_unique<UnskinnedVerticesBuilder>();
|
||||
}
|
||||
|
||||
std::unique_ptr<VerticesBuilder> VerticesBuilder::MakeSkinned() {
|
||||
return std::make_unique<SkinnedVerticesBuilder>();
|
||||
}
|
||||
|
||||
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 <typename SourceType>
|
||||
static Scalar ToNormalizedScalar(const void* source, size_t index) {
|
||||
constexpr SourceType divisor = std::is_integral_v<SourceType>
|
||||
? std::numeric_limits<SourceType>::max()
|
||||
: 1;
|
||||
static Scalar ToScalar(const void* source, size_t index, bool normalized) {
|
||||
const SourceType* s = reinterpret_cast<const SourceType*>(source) + index;
|
||||
return static_cast<Scalar>(*s) / static_cast<Scalar>(divisor);
|
||||
Scalar result = static_cast<Scalar>(*s);
|
||||
if (normalized) {
|
||||
constexpr SourceType divisor = std::is_integral_v<SourceType>
|
||||
? std::numeric_limits<SourceType>::max()
|
||||
: 1;
|
||||
result = static_cast<Scalar>(*s) / static_cast<Scalar>(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::AttributeType, VerticesBuilder::AttributeProperties>
|
||||
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}}};
|
||||
.write_proc = PassthroughAttributeWriter}},
|
||||
{VerticesBuilder::AttributeType::kJoints,
|
||||
{.offset_bytes = offsetof(SkinnedVerticesBuilder::Vertex, joints),
|
||||
.size_bytes = sizeof(SkinnedVerticesBuilder::Vertex::joints),
|
||||
.component_count = 4,
|
||||
.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<VerticesBuilder::ComponentType,
|
||||
VerticesBuilder::ComponentProperties>
|
||||
kComponentTypes = {
|
||||
{VerticesBuilder::ComponentType::kSignedByte,
|
||||
{.size_bytes = sizeof(int8_t),
|
||||
.convert_proc = ToNormalizedScalar<int8_t>}},
|
||||
{.size_bytes = sizeof(int8_t), .convert_proc = ToScalar<int8_t>}},
|
||||
{VerticesBuilder::ComponentType::kUnsignedByte,
|
||||
{.size_bytes = sizeof(int8_t),
|
||||
.convert_proc = ToNormalizedScalar<uint8_t>}},
|
||||
{.size_bytes = sizeof(int8_t), .convert_proc = ToScalar<uint8_t>}},
|
||||
{VerticesBuilder::ComponentType::kSignedShort,
|
||||
{.size_bytes = sizeof(int16_t),
|
||||
.convert_proc = ToNormalizedScalar<int16_t>}},
|
||||
{.size_bytes = sizeof(int16_t), .convert_proc = ToScalar<int16_t>}},
|
||||
{VerticesBuilder::ComponentType::kUnsignedShort,
|
||||
{.size_bytes = sizeof(int16_t),
|
||||
.convert_proc = ToNormalizedScalar<uint16_t>}},
|
||||
{.size_bytes = sizeof(int16_t), .convert_proc = ToScalar<uint16_t>}},
|
||||
{VerticesBuilder::ComponentType::kSignedInt,
|
||||
{.size_bytes = sizeof(int32_t),
|
||||
.convert_proc = ToNormalizedScalar<int32_t>}},
|
||||
{.size_bytes = sizeof(int32_t), .convert_proc = ToScalar<int32_t>}},
|
||||
{VerticesBuilder::ComponentType::kUnsignedInt,
|
||||
{.size_bytes = sizeof(int32_t),
|
||||
.convert_proc = ToNormalizedScalar<uint32_t>}},
|
||||
{.size_bytes = sizeof(int32_t), .convert_proc = ToScalar<uint32_t>}},
|
||||
{VerticesBuilder::ComponentType::kFloat,
|
||||
{.size_bytes = sizeof(float),
|
||||
.convert_proc = ToNormalizedScalar<float>}},
|
||||
{.size_bytes = sizeof(float), .convert_proc = ToScalar<float>}},
|
||||
};
|
||||
|
||||
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<const uint8_t*>(buffer_start) +
|
||||
attribute_stride_bytes * i;
|
||||
uint8_t* destination = reinterpret_cast<uint8_t*>(&vertices_.data()[i]) +
|
||||
attribute_props.offset_bytes;
|
||||
const uint8_t* src =
|
||||
reinterpret_cast<const uint8_t*>(source) + attribute_stride_bytes * i;
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(destination) +
|
||||
i * destination_stride_bytes + attribute_props.offset_bytes;
|
||||
|
||||
attribute_props.write_proc(reinterpret_cast<Scalar*>(destination), source,
|
||||
attribute_props.write_proc(reinterpret_cast<Scalar*>(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
|
||||
|
||||
@@ -15,8 +15,16 @@ namespace impeller {
|
||||
namespace scene {
|
||||
namespace importer {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// VerticesBuilder
|
||||
///
|
||||
|
||||
class VerticesBuilder {
|
||||
public:
|
||||
static std::unique_ptr<VerticesBuilder> MakeUnskinned();
|
||||
|
||||
static std::unique_ptr<VerticesBuilder> MakeSkinned();
|
||||
|
||||
enum class ComponentType {
|
||||
kSignedByte = 5120,
|
||||
kUnsignedByte,
|
||||
@@ -33,10 +41,12 @@ class VerticesBuilder {
|
||||
kTangent,
|
||||
kTextureCoords,
|
||||
kColor,
|
||||
kJoints,
|
||||
kWeights,
|
||||
};
|
||||
|
||||
using ComponentConverter =
|
||||
std::function<Scalar(const void* source, size_t byte_offset)>;
|
||||
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<VerticesBuilder::AttributeType,
|
||||
VerticesBuilder::AttributeProperties>
|
||||
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<Vertex> vertices_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VerticesBuilder);
|
||||
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<Vertex> vertices_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SkinnedVerticesBuilder);
|
||||
};
|
||||
|
||||
} // namespace importer
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace scene {
|
||||
|
||||
enum class GeometryType {
|
||||
kUnskinned = 0,
|
||||
kLastType = kUnskinned,
|
||||
kSkinned = 1,
|
||||
kLastType = kSkinned,
|
||||
};
|
||||
enum class MaterialType {
|
||||
kUnlit = 0,
|
||||
|
||||
@@ -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> context)
|
||||
pipelines_[{PipelineKey{GeometryType::kUnskinned, MaterialType::kUnlit}}] =
|
||||
MakePipelineVariants<UnskinnedVertexShader, UnlitFragmentShader>(
|
||||
*context_);
|
||||
pipelines_[{PipelineKey{GeometryType::kSkinned, MaterialType::kUnlit}}] =
|
||||
MakePipelineVariants<SkinnedVertexShader, UnlitFragmentShader>(*context_);
|
||||
|
||||
{
|
||||
impeller::TextureDescriptor texture_descriptor;
|
||||
|
||||
@@ -8,6 +8,7 @@ impeller_shaders("shaders") {
|
||||
name = "scene"
|
||||
|
||||
shaders = [
|
||||
"skinned.vert",
|
||||
"unskinned.vert",
|
||||
"unlit.frag",
|
||||
]
|
||||
|
||||
40
engine/src/flutter/impeller/scene/shaders/skinned.vert
Normal file
40
engine/src/flutter/impeller/scene/shaders/skinned.vert
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user