diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 18284ad8e26..ca13a31a481 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -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. diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index ca454fac72c..aaa267671a9 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -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; @@ -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) { @@ -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; } @@ -287,23 +296,23 @@ void FRenderableManager::create( std::unique_ptr& 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 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{}); } } } @@ -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]; + } } } } @@ -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]; } } } diff --git a/libs/filabridge/src/UibGenerator.cpp b/libs/filabridge/src/UibGenerator.cpp index 12e5930c1a9..0f64ee940c8 100644 --- a/libs/filabridge/src/UibGenerator.cpp +++ b/libs/filabridge/src/UibGenerator.cpp @@ -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; } diff --git a/shaders/src/common_math.fs b/shaders/src/common_math.fs index 8f366eb647d..2ceb4ddf8e8 100644 --- a/shaders/src/common_math.fs +++ b/shaders/src/common_math.fs @@ -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; -} - diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index 4521e096e83..06f4b8dca2a 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -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