Sunday, 31 May 2015

Editing BVH files

Motion capture is becoming quite accessible to the indie game developer, thanks to the Microsoft Kinect device. Although it didn't really take off as a dedicated game controller, in the same way that the Nintendo Wii of a few years back, it has proved very popular as a PC connected USB-device for all kinds of 3D applications.


There are two flavours of Xbox Kinect - the v1 (top) is the original device, originally released for the Xbox 360. The improved v2 version (bottom) was released for both the Xbox One and for PC as a development kit from Microsoft. Over time, Microsoft stopped producing the PC version and instead created an adapter to make the Xbox version compatible with any PC supporting a USB3 port.

Using either of these sensors, it's pretty easy to do motion capture with a half-decent PC and any one of a number of mo-cap applications. You can even do mo-cap directly in Unity with something like Cinema Mo Cap, which supports both types of Kinect device.


Although motion capture with a Kinect is (relatively) cheap - about £200 for the hardware and software, compared with £15,000 for a profession set-up - some of the captured motions leave a little bit to be desired, and almost always require some degree of "cleaning up" to remove rogue movements and the odd limb-twitch.

And this is where the cheap vs expensive approach really shows. Raw motion capture data is great for capturing key poses, but the problem with a lot of cheap mo-cap solutions is that they don't provide any editing tools - they simply expect you to use the motion capture data as provided.

This has two main drawbacks:

The first is that every now and again the mo-cap software mis-interprets the position of a limb or extremity (usually a foot or a finger) which creates strange glitches in the animation when played back.

The second is that a massive amount of extra data is captured that isn't really necessary. Most 3D packages provide "tweening" for animating a 3d character between two poses. BVH mo-cap files include all the inbetween movements as the subject moves from one pose to another.

If we had a way of capturing just the keyframes with the "important" poses in them (just before/after a major movement) we'd be able to eliminate both of these problems in one - we'd only need to keep the actual poses that add to our character movement, and in doing so, we could easily exclude any poses that contained glitches.

The problem with this approach is cost.
Mo-cap editing software is expensive.
And we're pretty cheap.
So we set about creating a simple mo-cap editor.

Now recreating a 3d animation from mo-cap data would be cool, but there's loads of this software around that does a far better job than we could do in a weekend. So we're not going to do that. We're going to use the fact the the BVH (mocap) data file format is pretty basic, to allow us to parse an animation file and extract only the data we're interested in.

For example, a BVH file consists of  a simple skeleton description, followed by a number of rotations for each "bone" or joint in the skeleton.

As we're looking at the Brekel motion capture software, we had a look at the sample data they provide from their Microsoft Kinect capture solution. The header of their BVH file looks something like this:

HIERARCHY
ROOT Hips
{
   OFFSET 0.000 77.138 0.000
   CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
   JOINT LeftHip
   {
      OFFSET 8.415 0.567 3.660
      CHANNELS 3 Zrotation Xrotation Yrotation
      JOINT LeftKnee
      {
         OFFSET 0.000 -39.733 0.000
         CHANNELS 3 Zrotation Xrotation Yrotation
         JOINT LeftAnkle
         {
            OFFSET 0.000 -37.972 0.000
            CHANNELS 3 Zrotation Xrotation Yrotation
            End Site
            {
               OFFSET 0.000 0.000 11.130
            }
         }
      }
   }
   JOINT RightHip
   {
      OFFSET -8.415 0.567 3.660
      CHANNELS 3 Zrotation Xrotation Yrotation
      JOINT RightKnee
      {
         OFFSET 0.000 -39.733 0.000
         CHANNELS 3 Zrotation Xrotation Yrotation
         JOINT RightAnkle
         {
            OFFSET 0.000 -37.972 0.000
            CHANNELS 3 Zrotation Xrotation Yrotation
            End Site
            {
               OFFSET 0.000 0.000 11.130
            }
         }
      }
   }
   JOINT Chest
   {
      OFFSET 0.000 32.175 0.000
      CHANNELS 3 Zrotation Xrotation Yrotation
      JOINT Chest2
      {
         OFFSET 0.000 23.529 0.000
         CHANNELS 3 Zrotation Xrotation Yrotation
         JOINT LeftCollar
         {
            OFFSET 1.862 -0.567 0.239
            CHANNELS 3 Zrotation Xrotation Yrotation
            JOINT LeftShoulder
            {
               OFFSET 17.792 0.000 0.000
               CHANNELS 3 Zrotation Xrotation Yrotation
               JOINT LeftElbow
               {
                  OFFSET 26.436 0.000 0.000
                  CHANNELS 3 Zrotation Xrotation Yrotation
                  JOINT LeftWrist
                  {
                     OFFSET 24.686 0.000 0.000
                     CHANNELS 3 Zrotation Xrotation Yrotation
                     JOINT LeftFinger0
                     {
                        OFFSET 4.877 -1.044 3.608
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT LeftFinger01
                        {
                           OFFSET 2.665 -0.000 -0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT LeftFinger02
                           {
                              OFFSET 2.543 -0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET 2.667 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT LeftFinger1
                     {
                        OFFSET 9.232 -0.301 2.144
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT LeftFinger11
                        {
                           OFFSET 4.225 0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT LeftFinger12
                           {
                              OFFSET 2.654 -0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET 1.958 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT LeftFinger2
                     {
                        OFFSET 8.920 0.000 -0.000
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT LeftFinger21
                        {
                           OFFSET 4.863 0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT LeftFinger22
                           {
                              OFFSET 2.765 0.000 -0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET 2.006 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT LeftFinger3
                     {
                        OFFSET 8.689 -0.126 -2.087
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT LeftFinger31
                        {
                           OFFSET 4.538 0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT LeftFinger32
                           {
                              OFFSET 2.305 0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET 1.923 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT LeftFinger4
                     {
                        OFFSET 8.391 -0.817 -3.758
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT LeftFinger41
                        {
                           OFFSET 3.044 -0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT LeftFinger42
                           {
                              OFFSET 1.975 0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET 1.667 0.000 0.000
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
         JOINT RightCollar
         {
            OFFSET -1.862 -0.567 0.239
            CHANNELS 3 Zrotation Xrotation Yrotation
            JOINT RightShoulder
            {
               OFFSET -17.792 0.000 0.000
               CHANNELS 3 Zrotation Xrotation Yrotation
               JOINT RightElbow
               {
                  OFFSET -26.436 0.000 0.000
                  CHANNELS 3 Zrotation Xrotation Yrotation
                  JOINT RightWrist
                  {
                     OFFSET -24.686 0.000 0.000
                     CHANNELS 3 Zrotation Xrotation Yrotation
                     JOINT RightFinger0
                     {
                        OFFSET -4.877 -1.044 3.608
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT RightFinger01
                        {
                           OFFSET -2.665 0.000 -0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT RightFinger02
                           {
                              OFFSET -2.543 0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET -2.667 -0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT RightFinger1
                     {
                        OFFSET -9.232 -0.301 2.144
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT RightFinger11
                        {
                           OFFSET -4.225 -0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT RightFinger12
                           {
                              OFFSET -2.654 -0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET -1.958 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT RightFinger2
                     {
                        OFFSET -8.920 -0.000 0.000
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT RightFinger21
                        {
                           OFFSET -4.863 0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT RightFinger22
                           {
                              OFFSET -2.765 -0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET -2.006 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT RightFinger3
                     {
                        OFFSET -8.689 -0.126 -2.087
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT RightFinger31
                        {
                           OFFSET -4.538 -0.000 0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT RightFinger32
                           {
                              OFFSET -2.305 -0.000 -0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET -1.923 0.000 0.000
                              }
                           }
                        }
                     }
                     JOINT RightFinger4
                     {
                        OFFSET -8.391 -0.817 -3.758
                        CHANNELS 3 Zrotation Xrotation Yrotation
                        JOINT RightFinger41
                        {
                           OFFSET -3.044 0.000 -0.000
                           CHANNELS 3 Zrotation Xrotation Yrotation
                           JOINT RightFinger42
                           {
                              OFFSET -1.975 0.000 0.000
                              CHANNELS 3 Zrotation Xrotation Yrotation
                              End Site
                              {
                                 OFFSET -1.667 0.000 0.000
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
         JOINT Neck
         {
            OFFSET 0.000 0.767 0.000
            CHANNELS 3 Zrotation Xrotation Yrotation
            JOINT Head
            {
               OFFSET 0.000 6.907 0.000
               CHANNELS 3 Zrotation Xrotation Yrotation
               End Site
               {
                  OFFSET 0.000 14.799 0.000
               }
            }
         }
      }
   }
}

And after all this junk, we start to see the actual animation data:

MOTION
Frames: 1222
Frame Time: 0.033333

-19.09096 86.72628 -193.07651 -0.17976 -3.92397 0.68769 11.14140 7.20556 -0.92078 -2.29540 5.11692 1.91521 -4.62996 20.50473 12.81783 -1.86685 12.98398 -2.19955 4.44833 -6.98297 -8.14759 1.86476 28.76261 -3.89672 -0.15640 2.07988 0.12122 -0.19043 2.47325 0.22838 -13.72436 1.22983 -4.97758 -56.87571 -0.90905 2.53451 4.08357 4.61863 -27.81969 90.82480 13.02917 -11.51783 -0.00002 -0.00001 0.00000 0.00000 -0.00000 0.00000 0.00000 0.00000 0.00003 -0.00000 0.00000 -0.00000 -0.00000 0.00000 0.00000 -0.00000 0.00001 -0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 20.66945 1.98194 5.24341 45.82977 -3.28564 -12.81110 -4.51496 4.06287 32.23806 -67.48892 16.51301 3.78246 40.90540 11.75460 -3.91033 0.00000 0.00000 -5.00000 0.00000 -0.00000 -60.00000 -2.00000 0.00000 0.00000 -2.00000 -0.00000 -0.00000 -2.00000 -0.00000 -0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 0.00007 -0.00009 0.00017 0.36586 5.18664 -1.03638

-18.93125 86.57366 -193.57823 -0.16391 -3.94172 0.83452 10.91408 7.45215 -0.60187 -1.98701 4.62638 2.93570 -4.33215 20.72713 11.94433 -2.08694 12.07617 -2.03624 4.64667 -5.82190 -8.21822 2.01918 28.58089 -4.24085 -0.15541 2.07988 0.18790 -0.18295 2.47323 0.31173 -13.54430 1.22964 -5.07436 -56.47004 -0.41186 2.11939 4.26038 4.77343 -29.40027 91.83288 13.19723 -10.56911 -0.00002 -0.00001 0.00000 0.00000 -0.00000 0.00000 0.00000 0.00000 0.00003 -0.00000 0.00000 -0.00000 -0.00000 0.00000 0.00000 -0.00000 0.00001 -0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 20.79433 2.03776 5.34991 45.90844 -3.74690 -12.70695 -4.24716 3.86554 32.13912 -66.30006 16.40318 4.01541 40.90540 11.75460 -3.91033 0.00000 0.00000 -5.00000 0.00000 -0.00000 -60.00000 -2.00000 0.00000 0.00000 -2.00000 -0.00000 -0.00000 -2.00000 -0.00000 -0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 0.00007 -0.00009 0.00017 0.38941 5.29672 -1.33426

-18.69029 86.44820 -194.06238 -0.13211 -3.93218 0.98746 10.64748 7.47083 -0.35351 -1.75983 4.40165 3.84590 -4.08096 20.97137 11.17286 -2.40106 11.17657 -1.92644 4.91138 -4.70405 -8.28357 2.14832 28.34713 -4.54350 -0.15157 2.07987 0.23891 -0.17385 2.47322 0.37546 -13.39292 1.21721 -5.08208 -56.20360 0.13947 1.70207 4.39204 4.79714 -30.64237 91.87553 13.25845 -8.73148 -0.00002 -0.00001 0.00000 0.00000 -0.00000 0.00000 0.00000 0.00000 0.00003 -0.00000 0.00000 -0.00000 -0.00000 0.00000 0.00000 -0.00000 0.00001 -0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 -0.00004 0.00000 0.00000 -0.00005 -0.00000 -0.00000 -0.00003 -0.00001 0.00000 20.85100 2.04922 5.36268 46.08860 -4.05784 -12.48223 -3.93563 3.70835 31.93921 -65.79578 16.03147 4.05839 40.90540 11.75460 -3.91033 0.00000 0.00000 -5.00000 0.00000 -0.00000 -60.00000 -2.00000 0.00000 0.00000 -2.00000 -0.00000 -0.00000 -2.00000 -0.00000 -0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 80.00001 -0.00001 0.00001 90.00000 -0.00000 0.00001 70.00000 0.00000 0.00000 0.00007 -0.00009 0.00017 0.28722 5.38471 -1.59374

The motion data follows the structure of the defined skeleton exactly. So in our example, the first root of the tree is the hips bone/joint. This is defined as having 6 "channels"- the X,Y,Z positions of the hips in real space, and the Z,X,Y rotation applied to the hips. The position of every bone in the skeleton after this is in relation to the "root" bone.

This means that the first six values on each line of the motion data define the position and rotation of the hips joint. The next entry in the skeleton is the left-hip joint. This is defined as having three rotational values. So the next three values in each line of the motion data (the sixth, seventh and eighth values) define the rotation of the left-hip joint. The next joint in the skeleton is the left-knee, defined with three channels, so the ninth, tenth and eleventh values in the motion data are the Z,X,Y rotation values for the left-knee joint.

And so the motion data continues, each set of three values relating to the next bone in the skeleton. To confirm that this is true, we can take any line of motion data and apply it the skeleton description in the BVH header file. Here is the first line of motion data, applied to the skeleton:

MOTION
Frames: 1222
Frame Time: 0.033333

ROOT Hips -19.09096 86.72628 -193.07651 -0.17976 -3.92397 0.68769
JOINT LeftHip 11.14140 7.20556 -0.92078
JOINT LeftKnee -2.29540 5.11692 1.91521
JOINT LeftAnkle -4.62996 20.50473 12.81783
JOINT RightHip -1.86685 12.98398 -2.19955
JOINT RightKnee 4.44833 -6.98297 -8.14759
JOINT RightAnkle 1.86476 28.76261 -3.89672
JOINT Chest -0.15640 2.07988 0.12122
JOINT Chest2 -0.19043 2.47325 0.22838
JOINT LeftCollar -13.72436 1.22983 -4.97758
JOINT LeftShoulder -56.87571 -0.90905 2.53451
JOINT LeftElbow 4.08357 4.61863 -27.81969
JOINT LeftWrist 90.82480 13.02917 -11.51783
JOINT LeftFinger0 -0.00002 -0.00001 0.00000
JOINT LeftFinger01 0.00000 -0.00000 0.00000
JOINT LeftFinger02 0.00000 0.00000 0.00003
JOINT LeftFinger1 -0.00000 0.00000 -0.00000
JOINT LeftFinger11 -0.00000 0.00000 0.00000
JOINT LeftFinger12 -0.00000 0.00001 -0.00000
JOINT LeftFinger2 -0.00004 0.00000 0.00000
JOINT LeftFinger21 -0.00005 -0.00000 -0.00000
JOINT LeftFinger22 -0.00003 -0.00001 0.00000
JOINT LeftFinger3 -0.00004 0.00000 0.00000
JOINT LeftFinger31 -0.00005 -0.00000 -0.00000
JOINT LeftFinger32 -0.00003 -0.00001 0.00000
JOINT LeftFinger4 -0.00004 0.00000 0.00000
JOINT LeftFinger41 -0.00005 -0.00000 -0.00000
JOINT LeftFinger42 -0.00003 -0.00001 0.00000
JOINT RightCollar 20.66945 1.98194 5.24341
JOINT RightShoulder 45.82977 -3.28564 -12.81110
JOINT RightElbow -4.51496 4.06287 32.23806
JOINT RightWrist -67.48892 16.51301 3.78246
JOINT RightFinger0 40.90540 11.75460 -3.91033
JOINT RightFinger01 0.00000 0.00000 -5.00000
JOINT RightFinger02 0.00000 -0.00000 -60.00000
JOINT RightFinger1 -2.00000 0.00000 0.00000
JOINT RightFinger11 -2.00000 -0.00000 -0.00000
JOINT RightFinger12 -2.00000 -0.00000 -0.00000
JOINT RightFinger2 80.00001 -0.00001 0.00001
JOINT RightFinger21 90.00000 -0.00000 0.00001
JOINT RightFinger22 70.00000 0.00000 0.00000
JOINT RightFinger3 80.00001 -0.00001 0.00001
JOINT RightFinger31 90.00000 -0.00000 0.00001
JOINT RightFinger32 70.00000 0.00000 0.00000
JOINT RightFinger4 80.00001 -0.00001 0.00001
JOINT RightFinger41 90.00000 -0.00000 0.00001
JOINT RightFinger42 70.00000 0.00000 0.00000
JOINT Neck 0.00007 -0.00009 0.00017
JOINT Head 0.36586 5.18664 -1.03638

With this information, it should be pretty trivial to create an interface (in VB6 of course!) that can read and re-create the BVH skeleton tree visually:


We've added in some tick boxes in the tree structure, to allow us to select which bones/joints we want to include (or remove, if necessary). By walking through the tree structure, we're able to re-create the BVH structure, amended with bones removed if required.


By ticking and unticking boxes in our editor, we're able to remove entire limbs and tree branches from the skeleton. In this example, we've removed the left forearm, hand and fingers, as well as the right ankle and foot.


Of course removing a bone (or bones) from the skeleton means we also need to split each line of the motion data, and re-create it to apply only to the bones selected in the interface. This is also a relatively trival exercise, but one that needs careful attention, since removing the wrong set of three values can vastly effect the rest of the animation.

Lastly, we added in a list of "keyframes" we want to capture into our editor. So instead of recreating the entire bvh file as captured, we can take only the frames we're interested in from the BVH data. This is done by simply reading through the original BVH motion data and if the line number (plus the offset from where the motion data starts) matches any one of the keyframes required, the motion data is parsed and re-applied to our modified skeleton, before being written to a second BVH file.

By using this approach, we've been able to create motion capture files with only the bones/joints we're interested in (for example, removing the lower limbs from an animation from which only the upper torso is only required) as well as only the key poses from the animation we need to recreate the animation in our Unity game. This results in much smaller files, with only a tiny amount of data in them (compared to the original animation files).

Of course a BVH file containing only the key poses we're interested in won't play back at the correct speed (since we've dropped about 98% of the captured data). But by capturing only the key poses, after importing into Unity, we can stretch the imported animation out, using the original animation to work out how many frames to leave between each key pose. And we still need to load the original data into some kind of BVH playback application (such as BVHViewer) in order to see the animation being played out on a 3d character. But using a bvh-viewing app allows us to select a frame with a pose we want to keep and then simply enter this frame number into our VB editor app - it's a bit clunky, but it works. And, as much as anything, doesn't cost a penny!


Messing about with Unity 5 and BVH motion capture

Unity is a great platform for putting games together. But it can also be really, really frustrating. A mix of visual and code-based development reminds me very much of Flash, circa version 5 (or MX). Building stuff is fun and easy - but when you come back to a project after a little while, trying to find which object contains which script can be a real pain.

Similarly, the visual-based editor can lead to really annoying lost hours, as you trawl through numerous online examples and forums, trying to understand why everyone else can get a figure to animate, while yours remains resolutely inert on the screen!

But it's worth persevering with; and once you've worked out it's quirks and remembered which tick boxes to click on (and, more importantly, in which order), you can often be rewarded with some what I tend to think of as "scriptable animations". Sure, Unity has a full development system on the backend but, until you go way below the surface, it's not much more than Actionscript 2 gave us Flash developers in the late 90s.

In particular, getting 3d model rigs and skeletons to line up between 3ds Max and Unity can be a bit of a trial (especially if the model is bought from the Unity Asset store and has a Unity/mecanim-friendly rig, but isn't particularly easy to use in 3ds Max). This makes using motion capture (BVH) files a bit of a trial too.

But after some fiddling about, and messing about with both Unity and 3ds Max (it was very hit-and-miss so a write-up may have to wait until we can work out exactly what we did and how to repeat it!) we managed to get the example Brekel motion capture file (from a Microsoft Kinect v2 system) played out by one of our characters:


While the first two orcs remain under the control of the scripts they were imported with, the third little character is completely under the influence of our BVH file (which in turn has been converted into a mechanim-friendly .anim file, and is being driven by the mecanim/animator controller).

The characters are also wearing modified armour - 3d models that we took into 3ds Max and modified a bit, to make them more customisable (is that even a word?).

Each helmet basically has all possible options enabled, and in script we loop through each character on the stage and delete the parts of their armour that we no longer require.


The character in the middle is wearing a modified helmet with no script applied - all the different combinations are visible. The orc on the left has a "tribal-style" helmet, complete with horns and a skull and a full face guard. The orc on the right is more lightly armoured, and so has a lighter helmet, consisting of a few spikes on the top and a single face protector bar.


By creating different helmet types in the 3ds Max, then merging in components from the previous helmet style, we're able to create a range of armour for our orcs. In the image above, we've an (as yet untextured) German-style helmet, complete with face guard, ear-flaps, skull and a spike on top. Any or all of these component parts can be modified using script, so one model can be configured to give a number of different helmet types.

By mixing and matching just a couple of different props, we'll be able to create a horde of orcs, each slightly different from the next and each armoured differently, while maintaining the feel of the armour being made from whatever spoils they may have found lying around - either at the end of an earlier game of Blood Bowl, or on a battlefield (hence the World War reference in some of the armour).

Thursday, 28 May 2015

Modelling in 3DS Max - getting started

After commissioning some disappointing custom 3D models, we figured it was time to knuckle down and give this 3DS Max thing a go. Our electronic board game is still on a back burner, but smoldering along - this time we're looking at creating some fantasy football teams (and considering making some custom animations using a Kinect for mo-cap but more on that in the future!)

Our original commission was for a couple of human footballers and a couple of orcs.
The initial photos came back looking like one character had been created, then simply re-shaped and re-skinned to create three other derivatives. Not quite what we were looking for!

Luckily the Unity Asset store had just what we needed - some nice, slightly cartoon-y looking orc characters, ready-rigged, with a few sample animations included.


These orcs not only keep their humanoid shape, but also manage to capture the ork-iness of an orc: the long, muscular arms and slightly hunched stance tell you that these are definitely not just human characters that have been coloured green.

In fact, we're not quite sure how to go about "re-skinning" these fellas, but it can be done. For now we're more interested in getting a usable character or two. These characters also come with a couple of bits of armour, which can be attached to empty game objects, placed on the elbows, shoulders and knee joints.


What we needed was a bit of 3DS Max magic, to create some custom armour that we can then simply hook onto these connection points on the model. Here's a run down of how we created our first ever 3DS Max model, from scratch.

Firstly, we're creating a "blood-bowl-like" shoulder pad, complete with raised edges and lots of spikes. This is like a squashed-up half-orange shape, with a bit of modification. This is how we did it:

The first thing was to create a sphere, and rotate it so that the "segments" travelled around the object, from top to bottom. Then we reduced the number of segments as low as we could go without it looking too angular.


We only actually want half a ball, so we changed the "hemisphere" option to 0.5.
Setting "slice" to 180 meant that as well as only drawing half the ball vertically, this half-ball shape was cut horizontally, so only 180 (of the total 360) degrees of the shape were drawn


We then made two copies of this quarter-ball shape, and placed one each to the left and to the right of the original. We made one slightly larger than the original and placed it - slightly higher and slightly to the right - of the original.


With the first shape selected, we created a new compound shape object and selected "pro-boolean". This allows shapes to be added together, subtracted from each other and combinations of these functions. Ensuring our first shape was selected first, we went for subtraction and started picking.


When we selected the (larger) shape that was overlapping the original, it immediately took a big slice out of the original shape.


We then took the other shape (previously deposited to the left of the original) and scaled it down slightly, before positioning it in the "empty space" just created. Then created a clone of this (smaller) shape and put it to one side.


Already it's starting to look a bit like a shoulder pad from a Games Workshop character (although at this stage it's more sci-fi Space Marine than fantasy Blood Bowl player) To make 3DS Max treat this as one continuous shape, we once again created a compound shape - only this time selecting unison as the pro-boolean modifier, and selected both pieces.


We're starting to get there - only we've a big solid lump of a shape, rather than a nice, hollow shoulder pad. So after shrinking the new quarter-ball shape (that we set aside earlier) just a smidgen, we placed it slightly offset from the shoulder pad shape:


Another compound shape/pro-boolean substraction, and our shoulder pad is really starting to look like a "proper" 3d model.


All that remained now was to scale the shell to make it slightly less wide (while retaining the height) using the scale cursor selector from the top toolbar....


... and add some cones-for-spikes:


A few extra shapes were thrown down (cylinder slices under the spikes, additional compound shapes on the corners of the shoulder pads) and we're pretty much done!


Skinning - or UV-mapping - 3d models is an art within itself. We managed to create a half-decent layout, ready to create a bitmap to apply to our model. But this is going to need some serious planning to get a half decent result. But so far, we're pretty pleased with making a 3d shape that not only looks ok, but exports as FBX and imports perfectly into the Unity game engine.

Maybe it won't be too long 'til we have a team full of Orcs ready to play Digital Blood Bowl after all.....

Tuesday, 26 May 2015

Karaoke + Jamming = Jamaoke

The other night we hosted another of our blues jam nights, this time at the Neptune Inn in Hove.
The Neptune has a reputation locally for being something of a music bar (it's not really big enough to call a pub, but is a great little venue - and gets quite packed in there when a good band are playing!). So we really wanted to make a half-decent impression with our first (hopefully of many) jam night there.

Now jam nights can go one of two ways.
The first is a house band play some songs, and people are invited to get up on stage and join in. These can be great nights out, but do tend to become a little bit "clique-y" after time. The same faces turn up and play the same songs - or the band have preferred players who they try to encourage to stay on stage.

The other kind of jam night is a bit more "messy" and more of a "jam" than a "play-what-you've-been-practicing-all-week" kind of session. Whoever turns up plays along with whoever else happens to be there. Normally this means people stick to "simpler" tunes, or at least common 12-bar patterns, or chord structures that are quite predictable, so everyone can easily play along.

At our jam night we wanted to avoid the clique-y-ness of a "structured" jam night. Plus, we didn't really have a house band - we had no idea how many people, let alone who, would turn up!

But we didn't want our jam night to be too messy either, nor did we want to be confined to playing three-chord, 12-bar blues all night. What we needed was something like a teleprompter, much like you see at karaoke nights.

So anyone with a passing familiarity with a song can join in - as both the words and the music are displayed on a large screen. And that's exactly what we did.




A few nights before the jam night, people intending on playing were asked to fill in a website, with the chord structure (and lyrics) to songs they wanted to play. We also added in some songs, so that we had some "filler" numbers for on the night.

Using nothing more than a bit of simple javascript, we built an on-screen highlighter, which could be moved along using the arrow keys on the keyboard. Left and right moved the highlight one place to the left or right, and up and down moved to the start of the next/previous section (verse or chorus, however the song had been entered onto the site).


It worked surprisingly well.
In such a tiny place as the Neptune, the massive screen did look a little imposing. But the basic idea worked quite nicely. We even managed a half-decent version of Peter Green's "Need your love so bad" played by a hastily-assembled band - many of whom had never even met before that night - playing a song that many had never played before - simply following along to the chords as they flashed up on the screen.

The actual jamaoke system basically consists of a TV with HDMI input and a Raspberry Pi containing a clone of the website used to upload the songs (the Pi is running MySQL and an Apache web server to host PHP web pages).

In the fullness of time we'd like to make the system operable by the band on stage - perhaps using foot pedals or similar to move the prompt along. Or maybe even hook it up to a MIDI-based "clicker track" so that the highlighter moves automatically over time. Such an approach might need a slightly better band on stage though, as it could prove quite tricky keeping the (live) music in time to an automated system.

For now, we're happy to just jam along and control the jamaoke system manually. Sure, it needs an extra person - for now - to keep everything in sync. Maybe in years to come our jamaoke operator will be known as our "fifth Beatle"....

Friday, 22 May 2015

Press-n-peel toner transfer - best printers

We've known, almost from the day we got it, that the Brother laser desktop printer isn't as good as the big massive Xerox we used to have, for press-n-peel toner transfer.

When printing onto the blue paper, the images appeared "scaly" and after transferring, large planes appeared pitted. This was made even worse when we tried our cheap chinese press-n-peel alternative - it was practically unusable (our first pcbs did actually etch into working boards, but it would only be a matter of time before we started getting impossible-to-debug hairline cracks in traces).

However, we weren't quite ready to give up on our chinese press-n-peel alternative just yet.
The blue original paper is about £21 for just five sheets. After postage costs, that's nearly a fiver a sheet!

The cheap chinese stuff we got cost us ten pounds for a hundred sheets, delivered to the door. That's 10p a sheet. A hundred sheets of blue press-n-peel would be in the region of £450!

After doing a bit of digging around online, we found a few forums in which readers said that they too had problems with press-n-peel and the Brother desktop laser printers. It's all to do with the propriety toner they say.

Now we already know that Xerox toner works really well. It's something to do with the high plastic content in their toner, apparently. But which other printers are recommended for toner transfer etching?

  • Xerox Phaser printers give great results
  • Oki-based printers are the same as Xerox (one is just a re-badged version of the other!)
  • From experience, we know that HP Laserjets give a decent result
  • A lot of people have had success with Dell lasers.


As it happens, a seller local to Nerd Towers was selling a Dell laser printer (an unwanted office raffle prize) for £20. For less than the cost of replenishing our blue press-n-peel, we could get hold of a different laser printer to try with our chinese clone paper.

Even if the chinese paper still turns out to be no good, we're not really very happy with the Brother desktop laser printer, so it seemed like a twenty-quid punt; even if we had to stick to blue paper in the future, the Dell surely had to give better results than the nasty Brother printer - didn't it?

A few hours later, we had our Brother printer on Freecycle, and a new Dell printer installed.


Already the black print on the cheap chinese paper looked better than the results we were getting with the Brother printer. Things were looking promising....


On the press-n-peel blue, however, the black toner still appeared quite "scaly", just like the Brother. Perhaps not so promising after all!

The yellow image transferred onto the copper pretty well.


It's not 100% perfect, and there are still a few little areas where you can see the image is slightly broken - around large planes - but all the tracks and pads transferred as solid shapes and look good. This transfer looks better than the images we were getting with genuine press-n-peel blue and our Brother printer.

The press-n-peel blue paper gave even better results


Nice solid lines, good definition on the pads, and large planes completely covered in toner. The best transfer we've had since the days of the Xerox printer!

And for the final test - etching.


The cheap chinese paper actually performed pretty well. There's still a bit of pitting around the large planes, but the traces and pads all came our really nicely. There's probably no problem with colouring over the large planar areas with a sharpie pen before etching on the next board.

In comparison terms, we felt that this was actually a better etched result than we got with our Brother printer and the genuine press-n-peel blue paper.

And to complete the test, here's a board etched using press-n-peel blue and the new Dell printer.


An almost perfect etch. Pads and traces and sharply defined, and large planes are complete and full, with very little sign of pitting. There's no doubt that press-n-peel blue gives a superior result. But our cheap chinese alternative paper is actually pretty usable too, when used with the right printer!

So there we have it.
Toner transfer is highly dependent on the type of toner in your printer (and by extension, since most people stick to the toner provided by their printer manufacturer, the make of your printer).

We've never had success with using magazine paper or cheap photo paper, but others swear by it, because it's a much cheaper alternative to press-n-peel blue. Maybe they're just using laser printers that happen to work well with the type of paper that they're using. Because we've just found that not only does the paper make a difference to the transfer, but the type of toner being used has a massive impact on it too.

So it's quite possible that you can use cheaper alternatives to press-n-peel blue toner transfer paper; you just need to make sure you use a suitable printer with it too.

In our experience (from the limited number of different printers we've actually tried) we rate laser printers in the following order, for toner transfer:

  • Xerox/Oki - the best results by a long shot
  • HP Laserjet - very good results requiring little or no touching up before etching
  • Dell - very good results, a little touching up required on large areas
  • Brother - don't even bother; useable only with expensive blue paper and lots of touching up still required before etching

For now, we're pretty happy with our Dell laser printer.
And with a stack of 99 sheets of cheap chinese press-n-peel alternative, it'll be a long time before we hope to repeat this kind of experiment - there's enough paper there to keep us going for a long time!



Tuesday, 12 May 2015

G1Xon multi-effects pedal

Having made a few different guitar pedals in recent weeks, it's been great fun just playing with shaping the sounds that come out of the guitar and into the amp. Playing loud is always great - far better than practicing with headphones or - even less fun if your neighbours object to too much noise - without an amp at all.

The few different effects created recently all sound great in isolation. Hit the Fuzz Face pedal, and a nice, warm fuzzy sound comes out of the amp. So the pedal is doing it's job.

What we haven't yet tried is actually comparing the sound from our home-made pedal to the sound of a genuine Fuzz Face (or Fuzz Factory, for the more complex, germanium-based effect). And there's only really one way to do that, and it involves buying a genuine Fuzz Face/Factory pedal.



But what exactly is "genuine"?
There are a few different Fuzz Face pedals available on the market - some retaining the original "face" design, some looking more like the "boxy" pedal we made ourselves. The biggest problem with a "genuine" Dunlop Fuzz Face is... the price. Nearly a hundred pounds for something which - we know from making one - has a few quid's worth of components inside it, a fancy pedal shape, and a brand name. That's a bit too much, just to cure a passing curiosity!

While in Brighton town centre (ok, pedants, city centre) I wandered into GAK (Guitar, Amps and Keyboards). I thought I might pop in and try out a genuine fuzz face and - while not exactly a side-by-side comparison - I'd at least be able to tell if it sounded vastly different from the one we built ourselves.



One of the really nice things about trying kit out at GAK (other than, of course, a massive shop jammed full of a huge range of guitars) is they ask what guitar you use, which amp, and try to set you up with the same kit. They didn't have my twin Laney Hardcore amp, but I was more than happy to try out one of their Orange amps. But they did let me use a brand new Yamaha Pacifica guitar (my current favourite) to try out different pedals with.

It's the little touches of great customer service like this which reminds us why we don't always get the best deal by "buying blind" online.

As it turns out, our fuzz face sounds fuzzy and buzzy and an original Fuzz Face sounds fuzzy and buzzy too. I can't really say whether or not the two are comparable. Because, while at GAK, I forgot all about Fuzz Face effects, once I came across a really "good value" multi-effects pedal: the G1Xon from Zoom.

This bad boy was on sale at £59 - over 120 different effects, a drum machine, expression pedal and 30-second loop recorder, for less than the price of many of their "boutique" effects pedals. It just seemed rude not to try it out.

Now I'm no great purist for "guitar tone". I play everything at eleven and it's either loud and clean, or crunchy and dirty. There's no mid-ground when I play. But being able to easily flip between a massive range of different effects suddenly had me wanting to play riffs from songs I'd long forgotten about over twenty years ago, and pay a bit more attention to how they actually sounded.

The ability to play a backing rhythm and record (and playback) multiple layers with the 30-second looper clinched the deal for me. With a headphone output, this little unit is not just a bazillion effects in one, it's also a great way to practice leads and solos to your own backing tracks, without having to faff about with amps and recorders (nor even setting up my previously favoured rig of a guitar into Native's Guitar Rig software-based amp/effect simulator on the PC).

If I tried to build even two or three of the simulated effects in this unit, I'd be over sixty quid in no time, after electronics, jack plugs, footswitches, enclosures and so on were all taken into account.

After less than 10 minutes of playing around in the shop, I'd splashed the cash and was on my way back home with the new effects unit all boxed up, ready to try out. To date, I've only used it for a total of about an hour. But what a great hour it's been - mixing effects, playing with compression and overdrive, getting the hang of a wah pedal again, recording and playing along to all kinds of different backing tracks. It's been great fun!

It also transpires (after much online searching for the same model) that GAK are selling this pedal at below internet prices. So not only do you get great customer service and an opportunity to try stuff out before buying, but it turns out they're also cheaper than buying online.



After this weekend, I might just be visiting GAK a little more frequently!

Inductorless wah pedal using TL072 op amp

Having made a few guitar effects pedals lately (a fuzz face, tuner pedal, fuzz factory and so on) and spurred on by the relative success we've had to date, the next obvious project seemed to be a wah pedal.

A wah pedal is a great effect for a guitar. It has an instantly recognisable sound - and can make even the simplest of riffs sound really cool and "bluesy" - perfect for a live jam night, for example.

There are quite a few different designs for wah pedals, but usually break down into two distinct types - inductor based (like the classic Dunlop CryBaby) and non-inductor (or inductorless) varieties, like the ColorSound Wah. We even made a simple wah, a few years back.

While it worked, the wah effect wasn't very pronounced. Not really understanding the concepts behind analogue/audio electronics, it just meant this was something we learned to live with - i.e. we'd made a pretty poor wah pedal. But it was still cool - because it was made, and not bought (though in truth, our genuine, second-hand, Dunlop CryBaby pedal is the one that gets used if we want some of that funky 70s chukka-chukka sound, and the home-made pedal hasn't really been used much after being tested out just to see if it works).

Having recently discovered a load of power amp and op amp IC chips in a stash of stuff long forgotten in one of our many moves, the idea of making an inductorless wah seemed strangely alluring. After all, what better way to use our newly created pedals, than to daisy-chain them and make some really crazy sounds at the next jam night?

Most inductorless designs use the "classic T" layout.
In fact, this is the core schematic for the £159-a-time ColorSound wah pedals.




A while back we had a go at making one of these, but the effect was more of a poor tone controller than a "proper" wah sound. Instead of reducing the frequency "envelope" of the sound, it simply cut either the high-end (treble) or low-end (bass) from the guitar sound.

But a quick google search for "inductorless wah op amp" turned up a few interesting circuits.


.
This design uses a single TL072 dual-channel op amp.
If truth be told, we don't really understand how it works (yes, there are explanations on line about what each part of the circuit is doing, and the idea of creating an envelope and phase-shifting to create a peak of sound in a limited frequency range does make sense - just not in the way that explains why certain components are used, and why specific values are chosen).

But that's not going to stop us having a go at making one anyway!
After all, if it doesn't work, the sum cost of the components is probably little more than  a quid or so (if it doesn't work, we can always reuse the more expensive parts, the like jack sockets and footswitch in another project in the future!)

The nice thing about this design is that the wah control is a single 3-pin potentiometer. And from where it's placed in the circuit, it looks like it should be easy enough to isolate from the rest of the circuit. The reason?

Well, it might be cool to have an onboard, hand-operated, maybe capacitive sensing wah controller, instead of a footswitch. Or perhaps we could create a sequencer to vary the resistance between R2 and R3, to control the wah sound. Or use an ultrasonic sensor to vary a resistive load between the two resistors. Or an LDR... or any number of other ways of creating a variable resistance.

In short, being able to isolate the bit that controls the wah sound means we can use this same design for a number of different types of wah effect, rather than being stuck with the old-fashioned foot rocker switch (though in all probability, that's the most likely design we'll end up using).



(press-n-peel ready layout - the 9-pin footswitch is not necessary - it was just used during the design of the PCB to ensure we got all the connections on the board!)

This PCB layout is surprisingly small, just 30mm x 45mm, so it should fit in even the smallest of enclosures and still leave plenty room for the battery.