RBF Features

Introduction to RBF in py-dem-bones

py-dem-bones integrates SciPy’s Radial Basis Function (RBF) capabilities with Dem-Bones skeletal weight calculations to support advanced animation workflows. This integration enables:

  1. Using Dem-Bones to calculate basic bone weights and transformations

  2. Leveraging SciPy’s RBFInterpolator to create parameter-driven auxiliary joints

  3. Implementing functionality similar to Chad Vernon RBF nodes, but using standard Python scientific computing libraries

Key Advantages

  • Uses production-grade SciPy implementation instead of custom RBF code

  • Supports multiple RBF kernel function options (thin plate spline, multiquadric, gaussian, etc.)

  • Seamlessly integrates with the Python scientific computing ecosystem

  • Maintained and updated by the scientific computing community

RBF Kernel Functions

The following kernel functions are available through SciPy’s RBFInterpolator:

  • thin_plate_spline: φ(r) = r²log(r), suitable for smooth interpolation with minimal curvature

  • multiquadric: φ(r) = sqrt(1 + (εr)²), good for general-purpose interpolation

  • inverse_multiquadric: φ(r) = 1/sqrt(1 + (εr)²), creates smoother interpolations

  • gaussian: φ(r) = exp(-(εr)²), creates very smooth interpolations with local influence

  • linear: φ(r) = r, simple linear interpolation between points

  • cubic: φ(r) = r³, provides smooth interpolation with more local control

  • quintic: φ(r) = r⁵, higher-order polynomial with smoother derivatives

Where r is the distance between points and ε is a shape parameter that controls the influence radius.

Example Usage

Here’s a basic example of using RBF interpolation with py-dem-bones:

RBF Interpolation Example
  1#!/usr/bin/env python
  2# -*- coding: utf-8 -*-
  3"""
  4RBF Integration with DemBones Demo
  5
  6This example demonstrates how to combine DemBones with SciPy's RBF functionality, similar to Chad Vernon's implementation.
  7We will use DemBones to calculate bone weights and transformations, then use RBF interpolators to drive auxiliary joints.
  8
  9To run this example, you need to install the following dependencies:
 10    pip install py-dem-bones numpy matplotlib scipy
 11"""
 12
 13import numpy as np
 14import matplotlib.pyplot as plt
 15from mpl_toolkits.mplot3d import Axes3D
 16from scipy.interpolate import RBFInterpolator
 17import py_dem_bones as pdb
 18
 19
 20def create_simple_mesh():
 21    """
 22    Create a simple test mesh (cube)
 23    """
 24    # Create cube vertices
 25    vertices = np.array([
 26        [0, 0, 0],  # 0
 27        [1, 0, 0],  # 1
 28        [1, 1, 0],  # 2
 29        [0, 1, 0],  # 3
 30        [0, 0, 1],  # 4
 31        [1, 0, 1],  # 5
 32        [1, 1, 1],  # 6
 33        [0, 1, 1],  # 7
 34    ], dtype=np.float64)
 35    
 36    return vertices
 37
 38
 39def create_deformed_mesh(vertices, deformation_amount=0.3):
 40    """
 41    Create a deformed mesh
 42    """
 43    # Deform the upper part of the cube
 44    deformed = vertices.copy()
 45    # Deform the upper part (vertices 4-7)
 46    deformed[4:, 0] += deformation_amount  # Offset along X axis
 47    deformed[4:, 2] += deformation_amount  # Offset along Z axis
 48    
 49    return deformed
 50
 51
 52def compute_dem_bones(rest_pose, deformed_pose, num_bones=2):
 53    """
 54    Use DemBones to calculate skinning weights and bone transformations
 55    """
 56    # Create DemBones instance
 57    dem_bones = pdb.DemBones()
 58    
 59    # Set parameters
 60    dem_bones.nIters = 30
 61    dem_bones.nInitIters = 10
 62    dem_bones.nTransIters = 5
 63    dem_bones.nWeightsIters = 3
 64    dem_bones.nnz = 4  # Number of non-zero weights per vertex
 65    dem_bones.weightsSmooth = 1e-4
 66    
 67    # Set data
 68    dem_bones.nV = len(rest_pose)  # Number of vertices
 69    dem_bones.nB = num_bones  # Number of bones
 70    dem_bones.nF = 1  # Number of frames
 71    dem_bones.nS = 1  # Number of subjects
 72    dem_bones.fStart = np.array([0], dtype=np.int32)  # Starting frame index for each subject
 73    dem_bones.subjectID = np.zeros(1, dtype=np.int32)  # Subject ID for each frame
 74    dem_bones.u = rest_pose  # Rest pose
 75    dem_bones.v = deformed_pose  # Deformed pose
 76    
 77    # Compute skinning decomposition
 78    dem_bones.compute()
 79    
 80    # Get results
 81    weights = dem_bones.get_weights()
 82    transformations = dem_bones.get_transformations()
 83    
 84    return weights, transformations
 85
 86
 87def create_rbf_interpolator(key_poses, key_values, rbf_function='thin_plate_spline'):
 88    """
 89    Create RBF interpolator, similar to Chad Vernon's RBF nodes
 90    
 91    Parameters:
 92        key_poses: Input values for key poses (n_samples, n_features)
 93        key_values: Output values for each key pose (n_samples, m)
 94        rbf_function: RBF function type, options include:
 95            - 'thin_plate_spline': Thin plate spline (default)
 96            - 'multiquadric': Multiquadric
 97            - 'inverse_multiquadric': Inverse multiquadric
 98            - 'gaussian': Gaussian function
 99            - 'linear': Linear function
100            - 'cubic': Cubic function
101            - 'quintic': Quintic function
102    
103    Returns:
104        RBF interpolator
105    """
106    # Use SciPy's RBFInterpolator, which is a more modern alternative to the Rbf class
107    return RBFInterpolator(
108        key_poses, 
109        key_values,
110        kernel=rbf_function,
111        smoothing=0.0  # No smoothing, exact interpolation
112    )
113
114
115def visualize_results(rest_pose, deformed_pose, dem_bones_weights, helper_joint_positions):
116    """
117    Visualize results
118    
119    Parameters:
120        rest_pose: Vertex positions in rest pose
121        deformed_pose: Vertex positions in deformed pose
122        dem_bones_weights: Bone weights calculated by DemBones
123        helper_joint_positions: Auxiliary joint positions calculated by RBF interpolator
124    """
125    fig = plt.figure(figsize=(18, 6))
126    
127    # First subplot: Original mesh
128    ax1 = fig.add_subplot(131, projection='3d')
129    ax1.scatter(rest_pose[:, 0], rest_pose[:, 1], rest_pose[:, 2], c='blue', s=100)
130    ax1.set_title('Original Mesh')
131    ax1.set_xlabel('X')
132    ax1.set_ylabel('Y')
133    ax1.set_zlabel('Z')
134    
135    # Second subplot: Deformed mesh
136    ax2 = fig.add_subplot(132, projection='3d')
137    ax2.scatter(deformed_pose[:, 0], deformed_pose[:, 1], deformed_pose[:, 2], c='red', s=100)
138    ax2.set_title('Deformed Mesh')
139    ax2.set_xlabel('X')
140    ax2.set_ylabel('Y')
141    ax2.set_zlabel('Z')
142    
143    # Third subplot: Weights and auxiliary joints
144    ax3 = fig.add_subplot(133, projection='3d')
145    # Use weight values as colors
146    colors = dem_bones_weights[:, 0]  # Use weights of the first bone as colors
147    scatter = ax3.scatter(
148        deformed_pose[:, 0], 
149        deformed_pose[:, 1], 
150        deformed_pose[:, 2], 
151        c=colors, 
152        cmap='viridis', 
153        s=100
154    )
155    # Add auxiliary joint positions
156    ax3.scatter(
157        helper_joint_positions[:, 0],
158        helper_joint_positions[:, 1],
159        helper_joint_positions[:, 2],
160        c='red',
161        marker='x',
162        s=200
163    )
164    ax3.set_title('Bone Weights and Auxiliary Joints')
165    ax3.set_xlabel('X')
166    ax3.set_ylabel('Y')
167    ax3.set_zlabel('Z')
168    plt.colorbar(scatter, ax=ax3, label='Bone Weights')
169    
170    plt.tight_layout()
171    plt.show()
172
173
174def main():
175    # Create test data
176    rest_pose = create_simple_mesh()
177    deformed_pose = create_deformed_mesh(rest_pose)
178    
179    print("1. Computing DemBones weights and transformations...")
180    weights, transformations = compute_dem_bones(rest_pose, deformed_pose)
181    
182    print("Bone weights:")
183    print(weights)
184    print("\nBone transformations:")
185    print(transformations)
186    
187    # Create RBF interpolation data
188    print("\n2. Creating RBF interpolator...")
189    
190    # Define input key poses
191    # In a real scenario, these might be controller positions or other control values
192    key_poses = np.array([
193        [0.0, 0.0],  # Default pose
194        [1.0, 0.0],  # X-direction extreme
195        [0.0, 1.0],  # Y-direction extreme
196    ])
197    
198    # Define output values - auxiliary joint positions
199    # These are the positions of auxiliary joints corresponding to each key pose
200    key_values = np.array([
201        # Auxiliary joint positions for default pose
202        [[0.5, 0.5, 0.0], [0.5, 0.5, 1.0]],
203        # Auxiliary joint positions for X-direction extreme
204        [[0.7, 0.5, 0.0], [0.7, 0.5, 1.2]],
205        # Auxiliary joint positions for Y-direction extreme
206        [[0.5, 0.7, 0.0], [0.5, 0.7, 1.2]],
207    ])
208    
209    # Create RBF interpolator
210    rbf = create_rbf_interpolator(key_poses, key_values.reshape(3, -1), rbf_function='thin_plate_spline')
211    
212    # Test RBF interpolation
213    test_pose = np.array([[0.5, 0.5]])  # Test pose
214    interpolated = rbf(test_pose).reshape(-1, 3)  # Get interpolation result
215    
216    print("Input test pose:", test_pose)
217    print("Interpolated auxiliary joint positions:")
218    print(interpolated)
219    
220    # Visualize results
221    visualize_results(rest_pose, deformed_pose, weights, interpolated)
222    
223    print("\n3. Testing RBF interpolation for different poses:")
224    # Test RBF interpolation for different poses
225    test_poses = [
226        [0.0, 0.0],  # Default pose
227        [1.0, 0.0],  # X-direction extreme
228        [0.0, 1.0],  # Y-direction extreme
229        [0.5, 0.5],  # Middle pose
230        [0.25, 0.75],  # Other pose
231    ]
232    
233    for i, pose in enumerate(test_poses):
234        test_pose = np.array([pose])
235        result = rbf(test_pose).reshape(-1, 3)
236        print(f"\nTest pose {i+1}: {pose}")
237        print(f"Interpolated auxiliary joint positions:")
238        print(result)
239
240
241if __name__ == "__main__":
242    main()

Maya Integration

To use the RBF functionality in Maya:

  1. Implement the MayaDCCInterface to handle Maya-specific data structures

  2. Replace matplotlib visualizations with Maya nodes/views

  3. Consider Maya’s Python environment compatibility

Maya RBF Integration Example (Simplified)
import maya.cmds as cmds
from py_dem_bones.interfaces.dcc import DCCInterface
from scipy.interpolate import RBFInterpolator
import numpy as np

class MayaDCCInterface(DCCInterface):
    def from_dcc_data(self, **kwargs):
        # Implementation for Maya data extraction
        pass

    def to_dcc_data(self, **kwargs):
        # Implementation for Maya data application
        pass

# Create RBF setup between two poses
def create_rbf_setup(source_joint, target_joint, poses):
    # Extract pose data
    source_poses = []
    target_poses = []

    for pose in poses:
        # Set the pose
        cmds.setAttr(f"{pose['control']}.{pose['attribute']}", pose['value'])

        # Get joint positions
        source_pos = cmds.xform(source_joint, q=True, ws=True, t=True)
        target_pos = cmds.xform(target_joint, q=True, ws=True, t=True)

        source_poses.append([pose['value']])
        target_poses.append(target_pos)

    # Create RBF interpolator
    rbf = RBFInterpolator(
        np.array(source_poses),
        np.array(target_poses),
        kernel='thin_plate_spline'
    )

    return rbf

Jupyter Notebook Integration

A Jupyter Notebook version of the RBF demo is available for interactive exploration. The notebook includes:

  • Interactive visualizations

  • Step-by-step explanations with markdown

  • Compatibility with Google Colab

  • Installation instructions for dependencies

Compatibility

The RBF functionality requires:

  • Python 3.8+

  • SciPy 1.7.0+

  • NumPy 1.20.0+

For Maya integration, compatibility has been tested with:

  • Maya 2020+

  • Python 3.8+ (as provided by Maya)

API Reference

For detailed API documentation, see the Python API section.