Skip to content

Commit

Permalink
Skinning now supports non-rigid transforms
Browse files Browse the repository at this point in the history
A full 3x3 + translate matrix is supported.
  • Loading branch information
pixelflinger committed Oct 10, 2018
1 parent 95a2686 commit 7485cc5
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 57 deletions.
2 changes: 1 addition & 1 deletion filament/include/filament/RenderableManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI {
Builder& castShadows(bool enable) noexcept; // false by default
Builder& receiveShadows(bool enable) noexcept; // true by default
Builder& skinning(size_t boneCount) noexcept; // 0 by default, 255 max
Builder& skinning(size_t boneCount, Bone const* transforms) noexcept;
Builder& skinning(size_t boneCount, Bone const* bones) noexcept;
Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept;

// Sets an ordering index for blended primitives that all live at the same Z value.
Expand Down
65 changes: 41 additions & 24 deletions filament/src/components/RenderableManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ namespace filament {

using namespace details;

struct InternalBone {
// Bones are stored as row-major
math::float4 rows[3] = {
{ 1.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f, 0.0f }
};
};

struct RenderableManager::BuilderDetails {
using Entry = RenderableManager::Builder::Entry;
Entry* mEntries = nullptr;
Expand All @@ -45,8 +54,8 @@ struct RenderableManager::BuilderDetails {
bool mCastShadows : 1;
bool mReceiveShadows : 1;
uint8_t mSkinningBoneCount = 0;
Bone const* mBones = nullptr;
math::mat4f const* mBoneMatrices = nullptr;
Bone const* mUserBones = nullptr;
math::mat4f const* mUserBoneMatrices = nullptr;

explicit BuilderDetails(size_t count)
: mEntriesCount(count), mCulling(true), mCastShadows(false), mReceiveShadows(true) {
Expand Down Expand Up @@ -139,16 +148,16 @@ RenderableManager::Builder& RenderableManager::Builder::skinning(size_t boneCoun
}

RenderableManager::Builder& RenderableManager::Builder::skinning(
size_t boneCount, Bone const* transforms) noexcept {
size_t boneCount, Bone const* bones) noexcept {
mImpl->mSkinningBoneCount = (uint8_t)std::min(size_t(255), boneCount);
mImpl->mBones = transforms;
mImpl->mUserBones = bones;
return *this;
}

RenderableManager::Builder& RenderableManager::Builder::skinning(
size_t boneCount, math::mat4f const* transforms) noexcept {
mImpl->mSkinningBoneCount = (uint8_t)std::min(size_t(255), boneCount);
mImpl->mBoneMatrices = transforms;
mImpl->mUserBoneMatrices = transforms;
return *this;
}

Expand Down Expand Up @@ -287,23 +296,23 @@ void FRenderableManager::create(
std::unique_ptr<Bones>& bones = manager[ci].bones;

bones.reset(new Bones); // FIXME: maybe use a pool allocator
bones->bones = UniformBuffer(CONFIG_MAX_BONE_COUNT * sizeof(Bone));
bones->handle = driver.createUniformBuffer(CONFIG_MAX_BONE_COUNT * sizeof(Bone));
bones->bones = UniformBuffer(CONFIG_MAX_BONE_COUNT * sizeof(InternalBone));
bones->handle = driver.createUniformBuffer(CONFIG_MAX_BONE_COUNT * sizeof(InternalBone));
}
}
if (builder->mSkinningBoneCount) {
std::unique_ptr<Bones> const& bones = manager[ci].bones;
assert(bones);
bones->count = builder->mSkinningBoneCount;
if (builder->mBones) {
setBones(ci, builder->mBones, builder->mSkinningBoneCount);
} else if (builder->mBoneMatrices) {
setBones(ci, builder->mBoneMatrices, builder->mSkinningBoneCount);
if (builder->mUserBones) {
setBones(ci, builder->mUserBones, builder->mSkinningBoneCount);
} else if (builder->mUserBoneMatrices) {
setBones(ci, builder->mUserBoneMatrices, builder->mSkinningBoneCount);
} else {
// initialize the bones to identity
Bone* UTILS_RESTRICT out =
(Bone*)bones->bones.invalidateUniforms(0, bones->count * sizeof(Bone));
std::fill_n(out, bones->count, Bone{});
InternalBone* UTILS_RESTRICT out =
(InternalBone*)bones->bones.invalidateUniforms(0, bones->count * sizeof(InternalBone));
std::uninitialized_fill_n(out, bones->count, InternalBone{});
}
}
}
Expand Down Expand Up @@ -461,10 +470,17 @@ void FRenderableManager::setBones(Instance ci,
assert(bones && offset + boneCount <= bones->count);
if (bones) {
boneCount = std::min(boneCount, bones->count - offset);
Bone* UTILS_RESTRICT out = (Bone*)bones->bones.invalidateUniforms(
offset * sizeof(Bone),
boneCount * sizeof(Bone));
std::copy_n(transforms, boneCount, out);
InternalBone* UTILS_RESTRICT out = (InternalBone*)bones->bones.invalidateUniforms(
offset * sizeof(InternalBone),
boneCount * sizeof(InternalBone));
for (size_t i = 0, c = bones->count; i < c; ++i) {
mat4f m(transforms[i].unitQuaternion);
m[3].xyz = transforms[i].translation;
m = transpose(m); // row-major
out[i].rows[0] = m[0];
out[i].rows[1] = m[1];
out[i].rows[2] = m[2];
}
}
}
}
Expand All @@ -476,13 +492,14 @@ void FRenderableManager::setBones(Instance ci,
assert(bones && offset + boneCount <= bones->count);
if (bones) {
boneCount = std::min(boneCount, bones->count - offset);
Bone* UTILS_RESTRICT out = (Bone*)bones->bones.invalidateUniforms(
offset * sizeof(Bone),
boneCount * sizeof(Bone));
InternalBone* UTILS_RESTRICT out = (InternalBone*)bones->bones.invalidateUniforms(
offset * sizeof(InternalBone),
boneCount * sizeof(InternalBone));
for (size_t i = 0, c = bones->count; i < c; ++i) {
mat4f const& m = transforms[i];
out[i].unitQuaternion = m.toQuaternion();
out[i].translation = m[3].xyz;
mat4f m = transpose(transforms[i]); // row-major
out[i].rows[0] = m[0];
out[i].rows[1] = m[1];
out[i].rows[2] = m[2];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion libs/filabridge/src/UibGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ UniformInterfaceBlock& UibGenerator::getPostProcessingUib() noexcept {
UniformInterfaceBlock& UibGenerator::getPerRenderableBonesUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
.name("BonesUniforms")
.add("bones", CONFIG_MAX_BONE_COUNT * 2, UniformInterfaceBlock::Type::FLOAT4, Precision::MEDIUM)
.add("bones", CONFIG_MAX_BONE_COUNT * 3, UniformInterfaceBlock::Type::FLOAT4, Precision::MEDIUM)
.build();
return uib;
}
Expand Down
21 changes: 0 additions & 21 deletions shaders/src/common_math.fs
Original file line number Diff line number Diff line change
Expand Up @@ -95,24 +95,3 @@ void toTangentFrame(const HIGHP vec4 q, out HIGHP vec3 n, out HIGHP vec3 t) {
vec3(-2.0, 2.0, -2.0) * q.y * q.yxw +
vec3(-2.0, 2.0, 2.0) * q.z * q.zwx;
}

vec3 halfPartialTransformVertexUnitQ(const vec3 v, const vec4 q) {
// this work only for unit-quaternions
return cross(q.xyz, cross(q.xyz, v) + q.w * v);
}

vec3 transformVertexUnitQ(const vec3 v, const vec4 q) {
// this work only for unit-quaternions
return v + 2.0 * halfPartialTransformVertexUnitQ(v, q);
}

vec3 transformVertexUnitQT(const vec3 v, const vec4 q, const vec3 t) {
// this work only for unit-quaternions
return transformVertexUnitQ(v, q) + t;
}

vec3 partialTransformVertexUnitQT(const vec3 v, const vec4 q, const vec3 t) {
// this work only for unit-quaternions
return 2.0f * halfPartialTransformVertexUnitQ(v, q) + t;
}

32 changes: 22 additions & 10 deletions shaders/src/getters.vs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,32 @@ mat3 getWorldFromModelNormalMatrix() {
//------------------------------------------------------------------------------

#if defined(HAS_SKINNING)
vec3 mulBoneNormal(const vec3 n, uint i) {
return vec3(
dot(n, bonesUniforms.bones[i + 0u].xyz),
dot(n, bonesUniforms.bones[i + 1u].xyz),
dot(n, bonesUniforms.bones[i + 2u].xyz));
}

vec3 mulBoneVertice(const vec3 v, uint i) {
return vec3(
dot(v, bonesUniforms.bones[i + 0u].xyz) + bonesUniforms.bones[i + 0u].w,
dot(v, bonesUniforms.bones[i + 1u].xyz) + bonesUniforms.bones[i + 1u].w,
dot(v, bonesUniforms.bones[i + 2u].xyz) + bonesUniforms.bones[i + 2u].w);
}

void skinNormal(inout vec3 n, const uvec4 ids, const vec4 weights) {
// this assumes that the sum of the weight is 1.0
n += (halfPartialTransformVertexUnitQ(n, bonesUniforms.bones[ids.x * 2u]) * weights.x
+ halfPartialTransformVertexUnitQ(n, bonesUniforms.bones[ids.y * 2u]) * weights.y
+ halfPartialTransformVertexUnitQ(n, bonesUniforms.bones[ids.z * 2u]) * weights.z
+ halfPartialTransformVertexUnitQ(n, bonesUniforms.bones[ids.w * 2u]) * weights.w) * 2.0f;
n += (mulBoneNormal(n, ids.x * 3u) * weights.x
+ mulBoneNormal(n, ids.y * 3u) * weights.y
+ mulBoneNormal(n, ids.z * 3u) * weights.z
+ mulBoneNormal(n, ids.w * 3u) * weights.w);
}

void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) {
// this assumes that the sum of the weight is 1.0
p += partialTransformVertexUnitQT(p, bonesUniforms.bones[ids.x * 2u], bonesUniforms.bones[ids.x * 2u + 1u].xyz) * weights.x
+ partialTransformVertexUnitQT(p, bonesUniforms.bones[ids.y * 2u], bonesUniforms.bones[ids.y * 2u + 1u].xyz) * weights.y
+ partialTransformVertexUnitQT(p, bonesUniforms.bones[ids.z * 2u], bonesUniforms.bones[ids.z * 2u + 1u].xyz) * weights.z
+ partialTransformVertexUnitQT(p, bonesUniforms.bones[ids.w * 2u], bonesUniforms.bones[ids.w * 2u + 1u].xyz) * weights.w;
p += mulBoneVertice(p, ids.x * 3u) * weights.x
+ mulBoneVertice(p, ids.y * 3u) * weights.y
+ mulBoneVertice(p, ids.z * 3u) * weights.z
+ mulBoneVertice(p, ids.w * 3u) * weights.w;
}
#endif

Expand Down

0 comments on commit 7485cc5

Please sign in to comment.