public void GenerateFromCatsButtonClickHandler() { if (_openedFile == null) { return; } UpdateChanges(); var res = new Dictionary <string, Dictionary <string, BoneData> >(); foreach (var catsVisemeMixerDataElement in _catsVisemeMixerData) { var blendshapeBoneChangesWithIntensity = catsVisemeMixerDataElement.Value .Select(x => new BlendShapeUtility.BlendshapeBoneChangesWithIntensity() { BlendShapeBoneChanges = ConvertBlendshapeBonesToFile(_blendshapeData[x.Key].Bones), Intensity = x.Value }) .ToArray(); res.Add(catsVisemeMixerDataElement.Key, ConvertBlendshapeBonesFromFile(BlendShapeUtility.MixBlendshapeBoneChanges(blendshapeBoneChangesWithIntensity))); } foreach (var resItem in res) { _blendshapeData[resItem.Key].Bones = resItem.Value; } // Если меняем на этот же блендшейп, то обновлять ненужно, т.к. смысл этой операции наоборот из измененного состояния обновить отображение (иначе получится затирание данных) SelectBlendshape(_selectedblendShapeName, false); }
private void UpdateChanges() { UpdateBonesToSelectedBlendshape(); // Если сейчас выбран basic блендшейп - запекаем в _basicVertexPositions измененные вершины на основании него if (_selectedblendShapeName == WowVrcFileData.BlendshapeData.basicBlendshapeName) { var basicVertexChanges = BlendShapeUtility.BakeBlendShape( _character.WowObject.GlobalPosition, _character.WowObject.MainMesh.Vertices, _character.WowObject.Bones, ConvertBlendshapeBonesToFile(_blendshapeData[WowVrcFileData.BlendshapeData.basicBlendshapeName].Bones), 1f); _defaultVertexPositions.CopyTo(_basicVertexPositions, 0); _defaultVertexNormals.CopyTo(_basicVertexNormals, 0); foreach (var basicVertexChange in basicVertexChanges) { _basicVertexPositions[basicVertexChange.Key] = new Vector3(basicVertexChange.Value.Position.X, basicVertexChange.Value.Position.Y, basicVertexChange.Value.Position.Z); _basicVertexNormals[basicVertexChange.Key] = new Vector3(basicVertexChange.Value.Normal.X, basicVertexChange.Value.Normal.Y, basicVertexChange.Value.Normal.Z); } } }
public static bool ExportToFbx(this WowVrcFile file, string exportDirectory, bool prepareForVRChat, float scale, out string warnings) { var exporter = new WowModelExporter(); WowObject characterWowObject; var opts = file.GetOpts(); if (opts != null) { characterWowObject = exporter.LoadCharacter(WhViewerOptions.FromJson(opts), scale); } else { var manualHeader = file.GetManualHeader(); if (manualHeader == null) { throw new System.InvalidOperationException(); } characterWowObject = exporter.LoadCharacter(manualHeader.Race, manualHeader.Gender, manualHeader.ItemIds, scale); } // ToDo: после запекания идет привязка на текущие индексы вершин. Если вершины будут перестроены, надо тут тоже обновить индексы var bakedBlendshapes = new List <BlendShapeUtility.BakedBlendshape>(); if (file.Blendshapes != null) { foreach (var blendshape in file.Blendshapes) { if (blendshape.Bones.Length > 0) { if (blendshape.Name == WowVrcFileData.BlendshapeData.basicBlendshapeName) { var basicBakedBlendshape = BlendShapeUtility.BakeBlendShape(characterWowObject.GlobalPosition, characterWowObject.MainMesh.Vertices, characterWowObject.Bones, blendshape.Bones, scale); foreach (var basicBakedBlendshapeElement in basicBakedBlendshape) { characterWowObject.MainMesh.Vertices[basicBakedBlendshapeElement.Key].Position = new Vec3(basicBakedBlendshapeElement.Value.Position.X, basicBakedBlendshapeElement.Value.Position.Y, basicBakedBlendshapeElement.Value.Position.Z); characterWowObject.MainMesh.Vertices[basicBakedBlendshapeElement.Key].Normal = new Vec3(basicBakedBlendshapeElement.Value.Normal.X, basicBakedBlendshapeElement.Value.Normal.Y, basicBakedBlendshapeElement.Value.Normal.Z); } } else { bakedBlendshapes.Add(new BlendShapeUtility.BakedBlendshape { BlendshapeName = blendshape.Name, Changes = BlendShapeUtility.BakeBlendShape(characterWowObject.GlobalPosition, characterWowObject.MainMesh.Vertices, characterWowObject.Bones, blendshape.Bones, scale) }); } } } } if (prepareForVRChat) { warnings = PrepareForVRChatUtility.PrepareObject(characterWowObject, bakedBlendshapes, scale, true, true, true, true, true, true); } else { warnings = null; } var fbxExporter = new Exporter(); return(fbxExporter.ExportWowObject(characterWowObject, bakedBlendshapes, exportDirectory, false)); }