/** * Multiplies a specific bone of a different WeightMap onto all boneWeights of this one. * Use this for more exact fading */ public void Multiply(WeightMap map, string bone) { for (int i = 0; i < Width; i++) { for (int j = 0; j < Height; j++) { double fac = 0.0; if (map.Weights[i, j].ContainsKey(bone)) { fac = map.Weights[i, j][bone]; } foreach (Bone b in Bones) { if (Weights[i, j].ContainsKey(b.Name)) { Weights[i, j][b.Name] *= fac; } } } } }
static void Main(string[] args) { /** * General concept: * Unity (well, the version used by VRChat) only supports up to 4 bones per vertex. * Therefore I use this basic algorithm to generate the weights for bat-like wings * by limiting the bones per vertex to 2 from the body side and 2 from the finger side. * The hard edges in the weight map are then smoothed and all weights normalized. * Then the weight maps are exported to PNG images that can be imported to Blender * using some plugins. * * It works for me but I can't promise that it works well for anyone else. Use at your own risk! * * The code below generates the weights for the wing membrane between body and last finger. * It shouldn't be too hard to adapt for the other wing membranes */ // By default the images will be saved in the bin directory string exportPath = Directory.GetCurrentDirectory(); string exportBaseName = Path.Combine(exportPath + "bone"); double edgeSmoothingFac = 10; int exportedImageRes = 100; /* * One bone for the entire body and one for the entire pinky. * This will define the general influence from the body towards the finger and the other way */ HashSet <Bone> bonesBodyFinger = new HashSet <Bone> { /** * Find the bone start and end positions (in pixel) on your UV map, enter them here. * Change 1024 to the pixel size of your UV map. */ new Bone("body", new Vector(470, 1020) / 1024, new Vector(1024, 1020) / 1024), new Bone("finger", new Vector(542, 585) / 1024, new Vector(995, 553) / 1024) }; WeightMap weightMapBodyFinger = new WeightMap(exportedImageRes, exportedImageRes); weightMapBodyFinger.CalculateDistances(bonesBodyFinger); //weightMapBodyFinger.ExportToImage(exportBaseName); // This defines the body bones HashSet <Bone> bonesBodyDetail = new HashSet <Bone> { // I did the weights of the arm by hand (I don't know if this actually works well) //new Bone("upperArm", new Vector(562, 968) / 1024, new Vector(549, 795) / 1024), new Bone("upperleg", new Vector(1024, 1020) / 1024, new Vector(852, 1020) / 1024), new Bone("hips", new Vector(825, 1020) / 1024, new Vector(733, 1020) / 1024), // I got better results by combining spine and chest: 1 bone less //new Bone("spine", new Vector(733, 1020) / 1024, new Vector(665, 1020) / 1024), //new Bone("chest", new Vector(665, 1020) / 1024, new Vector(470, 1020) / 1024) new Bone("spine", new Vector(733, 1020) / 1024, new Vector(470, 1020) / 1024) }; WeightMap weightMapBodyDetail = new WeightMap(exportedImageRes, exportedImageRes); weightMapBodyDetail.CalculateDistances(bonesBodyDetail); weightMapBodyDetail.LimitWeightCount(2); weightMapBodyDetail.SmoothEdge("upperleg", new Bone("", new Vector(.78, 0), new Vector(.78, 1)), edgeSmoothingFac); weightMapBodyDetail.SmoothEdge("spine", new Bone("", new Vector(.78, 0), new Vector(.78, 1)), edgeSmoothingFac); //weightMapBodyDetail.SmoothEdge("hips", new Bone("", new Vector(.69, 0), new Vector(.69, 1)), edgeSmoothingFac); //weightMapBodyDetail.SmoothEdge("chest", new Bone("", new Vector(.69, 0), new Vector(.69, 1)), edgeSmoothingFac); weightMapBodyDetail.Normalize(); weightMapBodyDetail.Multiply(weightMapBodyFinger, "body"); weightMapBodyDetail.ExportToImage(exportBaseName); // This defines the pinky bones HashSet <Bone> bonesBodyFingerDetail = new HashSet <Bone> { // I did the weights of the arm by hand (I don't know if this actually works well) //new Bone("lowerArm", new Vector(549, 795) / 1024, new Vector(538, 610) / 1024), new Bone("pinky1", new Vector(542, 585) / 1024, new Vector(680, 567) / 1024), new Bone("pinky2", new Vector(680, 567) / 1024, new Vector(838, 556) / 1024), new Bone("pinky3", new Vector(838, 556) / 1024, new Vector(995, 553) / 1024) }; WeightMap weightMapFingerDetail = new WeightMap(exportedImageRes, exportedImageRes); weightMapFingerDetail.CalculateDistances(bonesBodyFingerDetail); weightMapFingerDetail.LimitWeightCount(2); weightMapFingerDetail.SmoothEdge("pinky1", new Bone("", new Vector(.71, 0), new Vector(.77, 1)), edgeSmoothingFac); weightMapFingerDetail.SmoothEdge("pinky3", new Bone("", new Vector(.71, 0), new Vector(.77, 1)), edgeSmoothingFac); weightMapFingerDetail.Normalize(); weightMapFingerDetail.Multiply(weightMapBodyFinger, "finger"); weightMapFingerDetail.ExportToImage(exportBaseName); }