[Impeller Scene] Import skinned mesh vertex data (flutter/engine#38554)

This commit is contained in:
Brandon DeRosier
2022-12-29 14:37:28 -08:00
committed by GitHub
parent d412d64cef
commit dabbd16be5
11 changed files with 498 additions and 118 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
//---------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -11,7 +11,8 @@ namespace scene {
enum class GeometryType {
kUnskinned = 0,
kLastType = kUnskinned,
kSkinned = 1,
kLastType = kSkinned,
};
enum class MaterialType {
kUnlit = 0,

View File

@@ -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;

View File

@@ -8,6 +8,7 @@ impeller_shaders("shaders") {
name = "scene"
shaders = [
"skinned.vert",
"unskinned.vert",
"unlit.frag",
]

View 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;
}