I mentioned before that I have been slowly working on an extension to MotionBuilder’s python classes that make things easier and add more functionality. I been playing with different ways to implement these features and I think I found the best way that makes it very easy to attach without reinventing the whole pyfbsdk module. Thanks to a python guru at work that gave me the idea, I can now attach my own classes to MotionBuilder’s native ones so your code does not have to change, but can immediately begin to take advantage of the new functionality.

Below is a sample of the direction I am taking. I am still putting together all the pieces and finishing up a few other areas before I release my prototype code. I am dubbing the name PyMobu as the project name. So that will be the name until I can think of something better.

One of my bigger complaints in MotionBuilder python is the complete lack of vector and matrix mathematics that are essential in 3D programming. So my sample is to enhance the FBVector3d class to add in all useful operations with 3 dimensional vectors:

(note: I omitted some code to keep it short and not take up the whole page, so some functions are missing)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
import math import operator from pyfbsdk import * class PMVector3d(object): '''Base class for FBVector3d. Using euclid functions''' def __copy__(self): return self.__class__(self.x, self.y, self.z) copy = __copy__ def __eq__(self, other): if isinstance(other, FBVector3d): return self.x == other.x and self.y == other.y and self.z == other.z elif hasattr(other, '__len__') and len(other) == 3: return self.x == other[0] and self.y == other[1] and self.z == other[2] else: raise Exception("Comparison value must have at least 3 items") def __neq__(self, other): return not self.__eq__(other) def __nonzero__(self): return self.x != 0 or self.y != 0 or self.z != 0 def __mul__(self, other): if isinstance(other, FBVector3d): return self.__class__(self.x * other.x, self.y * other.y, self.z * other.z) elif type(other) in (int, long, float): return self.__class__(self.x * other, self.y * other, self.z * other) else: raise TypeError("Invalid type given") def __neg__(self): return self.__class__(-self.x, -self.y, -self.z) magnitude = __abs__ def magnitude_squared(self): return self.x ** 2 + self.y ** 2 + self.z ** 2 def normalize(self): d = self.magnitude() if d: self.x /= d self.y /= d self.z /= d return self def normalized(self): d = self.magnitude() if d: return self.__class__(self.x / d, self.y / d, self.z / d) return self.copy() def dot(self, other): if isinstance(other, FBVector3d): return self.x * other.x + self.y * other.y + self.z * other.z else: raise TypeError("Invalid type given") def cross(self, other): if isinstance(other, FBVector3d): return self.__class__(self.y * other.z - self.z * other.y, -self.x * other.z + self.z * other.x, self.x * other.y - self.y * other.x) else: raise TypeError("Invalid type given") def reflect(self, normal): # assume normal is normalized if isinstance(normal, FBVector3d): d = 2 * (self.x * normal.x + self.y * normal.y + self.z * normal.z) return self.__class__(self.x - d * normal.x, self.y - d * normal.y, self.z - d * normal.z) else: raise TypeError("Invalid type given") @property def x(self): return self[0] @ property def y(self): return self[1] @property def z(self): return self[2] _baseClasses = list(FBVector3d.__bases__) _baseClasses.insert(0, PMVector3d) FBVector3d.__bases__ = tuple(_baseClasses) |

The last 3 lines above is what actually adds my custom class as a base class to the FBVector3d class so you still just work with FBVector3d objects but now have the benefit of additional methods. The code was taken and adjusted from the python euclid module.

This is the idea around most of my code. This makes it a lot easier to work with inside MotionBuilder without actually having to create new instances of my classes.

More updates and news on the way. Feel free to contact me with questions or comments on the direction I am taking this.