Skip to content

Commit

Permalink
Extend skinning for >4 bones per vertex (#6772)
Browse files Browse the repository at this point in the history
* Add skinning and morphing samples to check functionality
* Implement skinning for more than four bones pair vertex

The API allows defining an unlimited number of bone indices and weights of primitives. Data is defined in building process of the renderable manager. Backward compatibility with the original solution.
Skinning of vertices is calculated on GPU, data is transferred to the vertex shader in the texture.
  • Loading branch information
fvbj authored Sep 21, 2023
1 parent d02231d commit 5d30435
Show file tree
Hide file tree
Showing 25 changed files with 2,062 additions and 37 deletions.
1 change: 1 addition & 0 deletions NEW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).

## Release notes for next branch cut

- engine: add support for skinning with more than four bones per vertex.
- engine: remove `BloomOptions::anamorphism` which wasn't working well in most cases [**API CHANGE**]
- engine: new API to return a Material's supported variants, C++ only (b/297456590)
- build: fix emscripten-1.3.46 build
53 changes: 53 additions & 0 deletions filament/include/filament/RenderableManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <utils/compiler.h>
#include <utils/EntityInstance.h>
#include <utils/FixedCapacityVector.h>

#include <math/mathfwd.h>

Expand Down Expand Up @@ -349,6 +350,58 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI {
Builder& skinning(size_t boneCount, Bone const* bones) noexcept; //!< \overload
Builder& skinning(size_t boneCount) noexcept; //!< \overload

/**
* Define bone indices and weights "pairs" for vertex skinning as a float2.
* The unsigned int(pair.x) defines index of the bone and pair.y is the bone weight.
* The pairs substitute \c BONE_INDICES and the \c BONE_WEIGHTS defined in the VertexBuffer.
* Both ways of indices and weights definition must not be combined in one primitive.
* Number of pairs per vertex bonesPerVertex is not limited to 4 bones.
* Vertex buffer used for \c primitiveIndex must be set for advance skinning.
* All bone weights of one vertex should sum to one. Otherwise they will be normalized.
* Data must be rectangular and number of bone pairs must be same for all vertices of this
* primitive.
* The data is arranged sequentially, all bone pairs for the first vertex, then for the
* second vertex, and so on.
*
* @param primitiveIndex zero-based index of the primitive, must be less than the primitive
* count passed to Builder constructor
* @param indicesAndWeights pairs of bone index and bone weight for all vertices
* sequentially
* @param count number of all pairs, must be a multiple of vertexCount of the primitive
* count = vertexCount * bonesPerVertex
* @param bonesPerVertex number of bone pairs, same for all vertices of the primitive
*
* @return Builder reference for chaining calls.
*
* @see VertexBuffer:Builder:advancedSkinning
*/
Builder& boneIndicesAndWeights(size_t primitiveIndex,
math::float2 const* indicesAndWeights, size_t count, size_t bonesPerVertex) noexcept;

/**
* Define bone indices and weights "pairs" for vertex skinning as a float2.
* The unsigned int(pair.x) defines index of the bone and pair.y is the bone weight.
* The pairs substitute \c BONE_INDICES and the \c BONE_WEIGHTS defined in the VertexBuffer.
* Both ways of indices and weights definition must not be combined in one primitive.
* Number of pairs is not limited to 4 bones per vertex.
* Vertex buffer used for \c primitiveIndex must be set for advance skinning.
* All bone weights of one vertex should sum to one. Otherwise they will be normalized.
* Data doesn't have to be rectangular and number of pairs per vertices of primitive can be
* variable.
* The vector of the vertices contains the vectors of the pairs
*
* @param primitiveIndex zero-based index of the primitive, must be less than the primitive
* count passed to Builder constructor
* @param indicesAndWeightsVectors pairs of bone index and bone weight for all vertices of
* the primitive sequentially
*
* @return Builder reference for chaining calls.
*
* @see VertexBuffer:Builder:advancedSkinning
*/
Builder& boneIndicesAndWeights(size_t primitiveIndex,
utils::FixedCapacityVector<
utils::FixedCapacityVector<math::float2>> indicesAndWeightsVector) noexcept;
/**
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
Expand Down
13 changes: 13 additions & 0 deletions filament/include/filament/VertexBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI {
*/
Builder& normalized(VertexAttribute attribute, bool normalize = true) noexcept;

/**
* Sets advanced skinning mode. Bone data, indices and weights will be
* set in RenderableManager:Builder:boneIndicesAndWeights methods.
* Works with or without buffer objects.
*
* @param enabled If true, enables advanced skinning mode. False by default.
*
* @return A reference to this Builder for chaining calls.
*
* @see RenderableManager:Builder:boneIndicesAndWeights
*/
Builder& advancedSkinning(bool enabled) noexcept;

/**
* Creates the VertexBuffer object and returns a pointer to it.
*
Expand Down
14 changes: 12 additions & 2 deletions filament/src/RenderPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ void RenderPass::instanceify(FEngine& engine) noexcept {
lhs.primitive.skinningHandle == rhs.primitive.skinningHandle &&
lhs.primitive.skinningOffset == rhs.primitive.skinningOffset &&
lhs.primitive.morphWeightBuffer == rhs.primitive.morphWeightBuffer &&
lhs.primitive.morphTargetBuffer == rhs.primitive.morphTargetBuffer;
lhs.primitive.morphTargetBuffer == rhs.primitive.morphTargetBuffer &&
lhs.primitive.skinningTexture == rhs.primitive.skinningTexture ;
});

uint32_t const instanceCount = e - curr;
Expand Down Expand Up @@ -585,6 +586,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,

cmdColor.primitive.skinningHandle = skinning.handle;
cmdColor.primitive.skinningOffset = skinning.offset;
cmdColor.primitive.skinningTexture = skinning.handleSampler;

cmdColor.primitive.morphWeightBuffer = morphing.handle;
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();

Expand Down Expand Up @@ -689,6 +692,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,

cmdDepth.primitive.skinningHandle = skinning.handle;
cmdDepth.primitive.skinningOffset = skinning.offset;
cmdDepth.primitive.skinningTexture = skinning.handleSampler;

cmdDepth.primitive.morphWeightBuffer = morphing.handle;
cmdDepth.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();

Expand Down Expand Up @@ -873,7 +878,12 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
}

if (UTILS_UNLIKELY(info.skinningTexture)) {
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
}
}

if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
// Instead of using a UBO per primitive, we could also have a single UBO for all
Expand Down
3 changes: 2 additions & 1 deletion filament/src/RenderPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,15 @@ class RenderPass {
backend::RasterState rasterState; // 4 bytes
backend::Handle<backend::HwRenderPrimitive> primitiveHandle; // 4 bytes
backend::Handle<backend::HwBufferObject> skinningHandle; // 4 bytes
backend::Handle<backend::HwSamplerGroup> skinningTexture; // 4 bytes
backend::Handle<backend::HwBufferObject> morphWeightBuffer; // 4 bytes
backend::Handle<backend::HwSamplerGroup> morphTargetBuffer; // 4 bytes
backend::Handle<backend::HwBufferObject> instanceBufferHandle; // 4 bytes
uint32_t index = 0; // 4 bytes
uint32_t skinningOffset = 0; // 4 bytes
uint16_t instanceCount; // 2 bytes [MSb: user]
Variant materialVariant; // 1 byte
uint8_t reserved[4] = {}; // 4 bytes
// uint8_t reserved[0] = {}; // 0 bytes

static const uint16_t USER_INSTANCE_MASK = 0x8000u;
static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu;
Expand Down
Loading

0 comments on commit 5d30435

Please sign in to comment.