/// <summary> /// Смешивает изменения блендшейпов между собой с разной интенсивностью, получая новый блендшейп /// Используется для генерирования визем для vrchat на основании нескольких ключевых визем (аналогично cats плагину для блендера) /// </summary> public static WowVrcFileData.BlendshapeData.BoneData[] MixBlendshapeBoneChanges(BlendshapeBoneChangesWithIntensity[] blendshapeBoneChangesWithIntensity) { // <имя_кости, list<tuple<локальное изменение трансформа кости, интенсивность изменения>>> var changes = new Dictionary <string, List <Tuple <WowTransform, float> > >(); foreach (var blendshapeBoneChangesWithIntensityItem in blendshapeBoneChangesWithIntensity) { foreach (var boneChange in blendshapeBoneChangesWithIntensityItem.BlendShapeBoneChanges) { List <Tuple <WowTransform, float> > transforms; if (!changes.ContainsKey(boneChange.Name)) { transforms = new List <Tuple <WowTransform, float> >(); changes[boneChange.Name] = transforms; } else { transforms = changes[boneChange.Name]; } transforms.Add(new Tuple <WowTransform, float>(boneChange.LocalTransform, blendshapeBoneChangesWithIntensityItem.Intensity)); } } return(changes .Select(change => { var transform = new WowTransform { position = new Vec3(0, 0, 0), rotation = Quat.Create(), scale = new Vec3(1, 1, 1) }; foreach (var transformWithIntensity in change.Value) { var position = Vec3.Lerp(new Vec3(0, 0, 0), transformWithIntensity.Item1.position, transformWithIntensity.Item2); transform.position.X += position.X; transform.position.Y += position.Y; transform.position.Z += position.Z; var rotation = Quat.Slerp(Quat.Create(), transformWithIntensity.Item1.rotation, transformWithIntensity.Item2); transform.rotation = Quat.Multiply(transform.rotation, rotation); var scale = Vec3.Lerp(new Vec3(1, 1, 1), transformWithIntensity.Item1.scale, transformWithIntensity.Item2); transform.scale.X *= scale.X; transform.scale.Y *= scale.Y; transform.scale.Z *= scale.Z; } return new WowVrcFileData.BlendshapeData.BoneData { Name = change.Key, LocalTransform = transform }; }) .ToArray()); }
private static void RotateShoulderAttachments(WowObject characterWowObject, string attachmentBoneName, string upperArmBoneName, float rotationAngle) { var shoulderAttachment = characterWowObject.FindBoneByName(attachmentBoneName); var upperArm = characterWowObject.FindBoneByName(upperArmBoneName); if (shoulderAttachment != null && upperArm != null) { var change = new WowVrcFileData.BlendshapeData.BoneData[] { new WowVrcFileData.BlendshapeData.BoneData() { LocalTransform = new WowTransform() { position = new Vec3(), rotation = Quat.RotateY(Quat.Create(), rotationAngle), scale = new Vec3(1f, 1f, 1f) }, Name = upperArmBoneName } }; foreach (var obj in shoulderAttachment.AttachedWowObjects) { foreach (var mesh in obj.Meshes) { var basicBakedBlendshape = BlendShapeUtility.BakeBlendShape(obj.GlobalPosition, mesh.Vertices, characterWowObject.Bones, change, 1f); foreach (var basicBakedBlendshapeElement in basicBakedBlendshape) { mesh.Vertices[basicBakedBlendshapeElement.Key].Position = new Vec3(basicBakedBlendshapeElement.Value.Position.X, basicBakedBlendshapeElement.Value.Position.Y, basicBakedBlendshapeElement.Value.Position.Z); mesh.Vertices[basicBakedBlendshapeElement.Key].Normal = new Vec3(basicBakedBlendshapeElement.Value.Normal.X, basicBakedBlendshapeElement.Value.Normal.Y, basicBakedBlendshapeElement.Value.Normal.Z); } } } } }