Esempio n. 1
0
        private void DoWork([NotNull] object state)
        {
            try {
                DoWorkInternal(state);

                Log("Done.");
            } catch (Exception ex) {
                var exDesc = ex.ToString();

                Log("Error occurred.");
                Log(exDesc);

                InvokeInForm(() => {
                    MessageBox.Show(exDesc, ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.OK, MessageBoxIcon.Error);
                });
            } finally {
                InvokeInForm(() => _form.EnableMainControls(true));
            }
        }
Esempio n. 2
0
        private void BtnGo_Click(object sender, EventArgs e)
        {
            void Alert(string text)
            {
                MessageBox.Show(text, ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            bool Confirm(string text)
            {
                var r = MessageBox.Show(text, ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

                return(r == DialogResult.Yes);
            }

            bool CheckInputParams()
            {
                bool CheckOutputDir(string filePath)
                {
                    var outputDirExists = !string.IsNullOrEmpty(filePath);

                    if (outputDirExists)
                    {
                        var file = new FileInfo(filePath);
                        var dir  = file.Directory;
                        outputDirExists = dir != null && dir.Exists;
                    }

                    if (!outputDirExists)
                    {
                        Alert($"Output folder of \"{filePath}\" does not exist.");
                    }

                    return(outputDirExists);
                }

                if (!File.Exists(txtInputHead.Text))
                {
                    Alert($"Head model \"{txtInputHead.Text}\" does not exist.");
                    return(false);
                }

                if (!File.Exists(txtInputBody.Text))
                {
                    Alert($"Body model \"{txtInputBody.Text}\" does not exist.");
                    return(false);
                }

                if (chkGenerateModel.Checked)
                {
                    if (!CheckOutputDir(txtOutputModel.Text))
                    {
                        return(false);
                    }
                }

                if (chkOptApplyCharHeight.Checked)
                {
                    if (!float.TryParse(txtOptCharHeight.Text, NumberStyles.Float, GlobalizationHelper.Culture, out var height) || height <= 0.1f)
                    {
                        Alert("Please enter a valid idol height.");
                    }

                    if (height < 1.0f || 2.0f < height)
                    {
                        var heightStr = height.ToString(GlobalizationHelper.Culture);
                        // Don't mind multiple string concatenations. It only executes once. :D
                        var message = "You entered a strange value for idol height." +
                                      " This may be caused by the differences of decimal point among countries." +
                                      " A wrong value may let this program generate wrong mesh and bones for the model." +
                                      Environment.NewLine + Environment.NewLine +
                                      $"The number in the numeric format of your culture ({GlobalizationHelper.Culture.DisplayName}) is: {heightStr}" +
                                      Environment.NewLine + Environment.NewLine +
                                      "Are you sure that you entered a correct value?";

                        if (!Confirm(message))
                        {
                            return(false);
                        }
                    }
                }

                if (chkGenerateCharAnim.Checked)
                {
                    if (!File.Exists(txtInputDance.Text))
                    {
                        Alert($"Dance data \"{txtInputDance.Text}\" does not exist.");
                        return(false);
                    }

                    if (!File.Exists(txtInputFacialExpression.Text))
                    {
                        Alert($"Facial expression data \"{txtInputFacialExpression.Text}\" does not exist.");
                        return(false);
                    }

                    if (!CheckOutputDir(txtOutputCharAnim.Text))
                    {
                        return(false);
                    }

                    if (!File.Exists(txtInputFacialExpression.Text))
                    {
                        Alert($"Facial expression mapping file \"{txtOptFEMappings.Text}\" does not exist.");
                        return(false);
                    }
                }

                if (chkGenerateCameraMotion.Checked)
                {
                    if (!File.Exists(txtInputCamera.Text))
                    {
                        Alert($"Camera data \"{txtInputCamera.Text}\" does not exist.");
                        return(false);
                    }

                    if (!CheckOutputDir(txtOutputCameraMotion.Text))
                    {
                        return(false);
                    }
                }

                if (radOptCamFormatVmd.Checked)
                {
                    // globalization: The type is uint32 so only thousand separator appears. In either way,
                    // it will raise a parse error if the user does not conform the UI locale (e.g. input
                    // "12,345" in French locale, or "12.345" in US locale).
                    if (!uint.TryParse(txtOptFixedFov.Text, NumberStyles.Integer, GlobalizationHelper.Culture, out _))
                    {
                        Alert($"FOV value \"{txtOptFixedFov.Text}\" should be a valid positive integer.");
                        return(false);
                    }
                }

                if (!Regex.IsMatch(txtInputHead.Text, @"ch_[a-z]{2}\d{3}_(?:\d{3}[a-z]{3}|[a-z])\.unity3d$", RegexOptions.CultureInvariant))
                {
                    Alert($"File \"{txtInputHead.Text}\" does not look like a character head file from the game.");
                    return(false);
                }

                if (!Regex.IsMatch(txtInputBody.Text, @"cb_[a-z]{2}\d{3}_(?:\d{3}[a-z]{3}|[a-z])\.unity3d$", RegexOptions.CultureInvariant))
                {
                    Alert($"File \"{txtInputBody.Text}\" does not look like a character body file from the game.");
                    return(false);
                }

                if (chkGenerateCharAnim.Checked)
                {
                    if (!Regex.IsMatch(txtInputDance.Text, @"dan_[a-z0-9]{6}_0[12345]\.imo\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputDance.Text}\" does not look like a dance data file from the game.");
                        return(false);
                    }

                    if (!Regex.IsMatch(txtInputFacialExpression.Text, @"scrobj_[a-z0-9]{6}\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputFacialExpression.Text}\" does not look like a mixed data file from the game containing facial expressions.");
                        return(false);
                    }
                }

                if (chkGenerateCameraMotion.Checked)
                {
                    if (!Regex.IsMatch(txtInputCamera.Text, @"cam_[a-z0-9]{6}\.imo\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputCamera.Text}\" does not look like a camera data file from the game.");
                        return(false);
                    }

                    if (radOptCamFormatVmd.Checked)
                    {
                        if (!txtOutputCameraMotion.Text.EndsWith(".vmd", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!Confirm("The output camera file name does not ends with \".vmd\". Are you sure to continue?"))
                            {
                                return(false);
                            }
                        }
                    }
                    else if (radOptCamFormatMvd.Checked)
                    {
                        if (!txtOutputCameraMotion.Text.EndsWith(".mvd", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!Confirm("The output camera file name does not ends with \".mvd\". Are you sure to continue?"))
                            {
                                return(false);
                            }
                        }
                    }
                }

                return(true);
            }

            InputParams PrepareInputParams()
            {
                var ip = new InputParams();

                ip.GenerateModel           = chkGenerateModel.Checked;
                ip.GenerateCharacterMotion = chkGenerateCharAnim.Checked;
                ip.GenerateCameraMotion    = chkGenerateCameraMotion.Checked;

                ip.InputHead             = txtInputHead.Text;
                ip.InputBody             = txtInputBody.Text;
                ip.InputDance            = txtInputDance.Text;
                ip.InputFacialExpression = txtInputFacialExpression.Text;
                ip.InputCamera           = txtInputCamera.Text;

                ip.OutputModel = txtOutputModel.Text;
                ip.OutputCharacterAnimation = txtOutputCharAnim.Text;
                ip.OutputCamera             = txtOutputCameraMotion.Text;

                ip.MotionSource                   = radOptMotionSourceMltd.Checked ? MotionFormat.Mltd : MotionFormat.Mmd;
                ip.ScalePmx                       = chkOptScalePmx.Checked;
                ip.ConsiderIdolHeight             = chkOptApplyCharHeight.Checked;
                ip.IdolHeight                     = ip.ConsiderIdolHeight ? Convert.ToSingle(txtOptCharHeight.Text) : 0;
                ip.TranslateBoneNames             = chkOptTranslateBoneNames.Checked;
                ip.AppendLegIkBones               = chkOptAppendLegIKBones.Checked;
                ip.FixCenterBones                 = chkOptFixCenterBones.Checked;
                ip.ConvertBindingPose             = chkOptConvertToTdaPose.Checked;
                ip.AppendEyeBones                 = chkOptAppendEyeBones.Checked;
                ip.HideUnityGeneratedBones        = chkOptHideUnityGenBones.Checked;
                ip.TranslateFacialExpressionNames = chkOptTranslateFacialExpressionNames.Checked;
                ip.ImportPhysics                  = chkOptImportPhysics.Checked;

                ip.TransformTo30Fps = radOptAnimFrameRate30.Checked;
                ip.ScaleVmd         = chkOptScaleVmd.Checked;
                ip.UseMvd           = radOptCamFormatMvd.Checked;
                ip.FixedFov         = ip.UseMvd ? Convert.ToUInt32(txtOptFixedFov.Text) : 0;
                ip.SongPosition     = cboOptSongPosition.SelectedIndex + 1;

                ip.FacialExpressionMappingFilePath = txtOptFEMappings.Text;

                return(ip);
            }

            InputParams p;

            try {
                if (!CheckInputParams())
                {
                    return;
                }

                p = PrepareInputParams();
            } catch (Exception ex) {
                Alert(ex.Message);
                return;
            }

            var thread = new Thread(DoWork);

            thread.Name         = "Conversion thread";
            thread.IsBackground = true;

            thread.Start(p);

            EnableMainControls(false);
        }
Esempio n. 3
0
        private void BtnGo_Click(object sender, EventArgs e)
        {
            void Alert(string text)
            {
                MessageBox.Show(text, ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            bool Confirm(string text)
            {
                var r = MessageBox.Show(text, ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

                return(r == DialogResult.Yes);
            }

            bool CheckInputParams()
            {
                bool CheckOutputDir(string filePath)
                {
                    var outputDirExists = !string.IsNullOrEmpty(filePath);

                    if (outputDirExists)
                    {
                        var file = new FileInfo(filePath);
                        var dir  = file.Directory;
                        outputDirExists = dir != null && dir.Exists;
                    }

                    if (!outputDirExists)
                    {
                        Alert($"Output folder of \"{filePath}\" does not exist.");
                    }

                    return(outputDirExists);
                }

                if (chkGenerateModel.Checked || chkGenerateCharAnim.Checked)
                {
                    if (!File.Exists(txtInputHead.Text))
                    {
                        Alert($"Head model \"{txtInputHead.Text}\" does not exist.");
                        return(false);
                    }

                    if (!File.Exists(txtInputBody.Text))
                    {
                        Alert($"Body model \"{txtInputBody.Text}\" does not exist.");
                        return(false);
                    }
                }

                if (chkGenerateModel.Checked)
                {
                    if (!CheckOutputDir(txtOutputModel.Text))
                    {
                        return(false);
                    }
                }

                if (chkOptApplyCharHeight.Checked)
                {
                    if (!uint.TryParse(txtOptCharHeight.Text, out var height) || (height <= 100 || 200 <= height))
                    {
                        Alert("Please enter a valid idol height.");
                    }
                }

                if (chkGenerateCharAnim.Checked)
                {
                    if (!File.Exists(txtInputDance.Text))
                    {
                        Alert($"Dance data \"{txtInputDance.Text}\" does not exist.");
                        return(false);
                    }

                    if (!CheckOutputDir(txtOutputCharAnim.Text))
                    {
                        return(false);
                    }
                }

                if (chkGenerateCharAnim.Checked || chkGenerateLipSync.Checked || chkGenerateFacialExpression.Checked || chkGenerateCameraMotion.Checked)
                {
                    if (!File.Exists(txtInputScenario.Text))
                    {
                        Alert($"Scenario data \"{txtInputScenario.Text}\" does not exist.");
                        return(false);
                    }
                }

                if (chkGenerateLipSync.Checked)
                {
                    if (!CheckOutputDir(txtOutputLipSync.Text))
                    {
                        return(false);
                    }
                }

                if (chkGenerateFacialExpression.Checked)
                {
                    if (!File.Exists(txtOptFEMappings.Text))
                    {
                        Alert($"Facial expression mapping file \"{txtOptFEMappings.Text}\" does not exist.");
                        return(false);
                    }

                    if (!CheckOutputDir(txtOutputFacialExpression.Text))
                    {
                        return(false);
                    }
                }

                if (chkGenerateCameraMotion.Checked)
                {
                    if (!File.Exists(txtInputCamera.Text))
                    {
                        Alert($"Camera data \"{txtInputCamera.Text}\" does not exist.");
                        return(false);
                    }

                    if (!CheckOutputDir(txtOutputCameraMotion.Text))
                    {
                        return(false);
                    }
                }

                if (radOptCamFormatVmd.Checked)
                {
                    if (!uint.TryParse(txtOptFixedFov.Text, out var u) || u == 0)
                    {
                        Alert($"FOV value \"{txtOptFixedFov.Text}\" should be a valid positive integer.");
                        return(false);
                    }
                }

                if (chkGenerateModel.Checked || chkGenerateCharAnim.Checked)
                {
                    if (!Regex.IsMatch(txtInputHead.Text, @"ch_[a-z]{2}\d{3}_(?:\d{3}[a-z]{3}|[a-z])(?:_v2)?\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputHead.Text}\" does not look like a character head file from the game.");
                        return(false);
                    }

                    if (!Regex.IsMatch(txtInputBody.Text, @"cb_[a-z]{2}\d{3}_(?:\d{3}[a-z]{3}|[a-z])\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputBody.Text}\" does not look like a character body file from the game.");
                        return(false);
                    }
                }

                if (chkGenerateCharAnim.Checked)
                {
                    if (!Regex.IsMatch(txtInputDance.Text, @"dan_[a-z0-9]{5}[a-z0-9+]_0[12345](?:\.imo)?\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputDance.Text}\" does not look like a dance data file from the game.");
                        return(false);
                    }
                }

                if (chkGenerateCharAnim.Checked || chkGenerateLipSync.Checked || chkGenerateFacialExpression.Checked || chkGenerateCameraMotion.Checked)
                {
                    if (!Regex.IsMatch(txtInputScenario.Text, @"scrobj_[a-z0-9]{5}[a-z0-9+]\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputScenario.Text}\" does not look like a scenario data file from the game.");
                        return(false);
                    }
                }

                if (chkGenerateCameraMotion.Checked)
                {
                    if (!Regex.IsMatch(txtInputCamera.Text, @"cam_[a-z0-9]{5}[a-z0-9+](?:\.imo)?\.unity3d$", RegexOptions.CultureInvariant))
                    {
                        Alert($"File \"{txtInputCamera.Text}\" does not look like a camera data file from the game.");
                        return(false);
                    }

                    if (radOptCamFormatVmd.Checked)
                    {
                        if (!txtOutputCameraMotion.Text.EndsWith(".vmd", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!Confirm("The output camera file name does not ends with \".vmd\". Are you sure to continue?"))
                            {
                                return(false);
                            }
                        }
                    }
                    else if (radOptCamFormatMvd.Checked)
                    {
                        if (!txtOutputCameraMotion.Text.EndsWith(".mvd", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!Confirm("The output camera file name does not ends with \".mvd\". Are you sure to continue?"))
                            {
                                return(false);
                            }
                        }
                    }
                }

                if (cboOptAppealType.SelectedIndex > 0)
                {
                    if (!string.IsNullOrWhiteSpace(txtOptExternalDanceAppealFile.Text))
                    {
                        if (!Regex.IsMatch(txtOptExternalDanceAppealFile.Text, @"dan_[a-z0-9]{5}[a-z0-9+]_ap\.imo\.unity3d$", RegexOptions.CultureInvariant))
                        {
                            Alert($"File \"{txtOptExternalDanceAppealFile.Text}\" does not look like an external appeal data file from the game.");
                            return(false);
                        }
                    }
                }

                return(true);
            }

            MainWorkerInputParams PrepareInputParams()
            {
                var ip = new MainWorkerInputParams();

                ip.GenerateModel             = chkGenerateModel.Checked;
                ip.GenerateCharacterMotion   = chkGenerateCharAnim.Checked;
                ip.GenerateLipSync           = chkGenerateLipSync.Checked;
                ip.GenerateFacialExpressions = chkGenerateFacialExpression.Checked;
                ip.GenerateCameraMotion      = chkGenerateCameraMotion.Checked;

                ip.InputHead     = txtInputHead.Text;
                ip.InputBody     = txtInputBody.Text;
                ip.InputDance    = txtInputDance.Text;
                ip.InputScenario = txtInputScenario.Text;
                ip.InputCamera   = txtInputCamera.Text;

                ip.OutputModel = txtOutputModel.Text;
                ip.OutputCharacterAnimation = txtOutputCharAnim.Text;
                ip.OutputLipSync            = txtOutputLipSync.Text;
                ip.OutputFacialExpressions  = txtOutputFacialExpression.Text;
                ip.OutputCamera             = txtOutputCameraMotion.Text;

                ip.MotionSource       = radOptMotionSourceMltd.Checked ? MotionFormat.Mltd : MotionFormat.Mmd;
                ip.ScalePmx           = chkOptScalePmx.Checked;
                ip.ConsiderIdolHeight = chkOptApplyCharHeight.Checked;
                var idolHeightInCm = Convert.ToUInt32(txtOptCharHeight.Text) / 100.0f;

                ip.IdolHeight                     = ip.ConsiderIdolHeight ? idolHeightInCm : 0;
                ip.TranslateBoneNames             = chkOptTranslateBoneNames.Checked;
                ip.AppendLegIkBones               = chkOptAppendLegIKBones.Checked;
                ip.FixCenterBones                 = chkOptFixCenterBones.Checked;
                ip.ConvertBindingPose             = chkOptConvertToTdaPose.Checked;
                ip.AppendEyeBones                 = chkOptAppendEyeBones.Checked;
                ip.HideUnityGeneratedBones        = chkOptHideUnityGenBones.Checked;
                ip.TranslateFacialExpressionNames = chkOptTranslateFacialExpressionNames.Checked;
                ip.ImportPhysics                  = chkOptImportPhysics.Checked;
                ip.ApplyGameStyledToon            = chkGameToon.Checked;
                ip.SkinToonNumber                 = cboGameToonSkinNumber.SelectedIndex + 1;
                ip.ClothesToonNumber              = cboGameToonClothesNumber.SelectedIndex + 1;
                ip.AddHairHighlights              = chkOptAddHighlightHair.Checked;
                ip.AddEyesHighlights              = chkOptAddHighlightEyes.Checked;

                ip.TransformTo30Fps        = radOptAnimFrameRate30.Checked;
                ip.ScaleVmd                = chkOptScaleVmd.Checked;
                ip.UseMvdForCamera         = radOptCamFormatMvd.Checked;
                ip.FixedFov                = ip.UseMvdForCamera ? 0 : Convert.ToUInt32(txtOptFixedFov.Text);
                ip.MotionNumber            = cboOptMotionNumber.SelectedIndex + 1;
                ip.FormationNumber         = cboOptFormationNumber.SelectedIndex + 1;
                ip.AppealType              = (AppealType)cboOptAppealType.SelectedIndex;
                ip.ExternalDanceAppealFile = txtOptExternalDanceAppealFile.Text;
                ip.IgnoreSingControl       = chkOptAlwaysSinging.Checked;

                ip.FacialExpressionMappingFilePath = txtOptFEMappings.Text;
                ip.PreferredFacialExpressionSource = radFESourceLandscape.Checked ? MainWorkerInputParams.FallbackFacialExpressionSource.Landscape : MainWorkerInputParams.FallbackFacialExpressionSource.Portrait;

                return(ip);
            }

            txtLog.Clear();

            MainWorkerInputParams p;

            try {
                if (!CheckInputParams())
                {
                    return;
                }

                p = PrepareInputParams();
            } catch (Exception ex) {
                Alert(ex.Message);
                return;
            }

            var worker = new MainWorker(this, p);

            worker.Start();

            EnableMainControls(false);
        }
Esempio n. 4
0
        private void DoWork(object state)
        {
            ConversionConfig PrepareConversionConfig(InputParams ip)
            {
                var cc = new ConversionConfig();

                cc.MotionFormat                        = ip.MotionSource;
                cc.ScaleToPmxSize                      = ip.ScalePmx;
                cc.ApplyPmxCharacterHeight             = ip.ConsiderIdolHeight;
                cc.TranslateBoneNamesToMmd             = ip.TranslateBoneNames;
                cc.AppendIKBones                       = ip.AppendLegIkBones;
                cc.FixMmdCenterBones                   = ip.FixCenterBones;
                cc.FixTdaBindingPose                   = ip.ConvertBindingPose;
                cc.AppendEyeBones                      = ip.AppendEyeBones;
                cc.HideUnityGeneratedBones             = ip.HideUnityGeneratedBones;
                cc.SkeletonFormat                      = ip.MotionSource == MotionFormat.Mltd ? SkeletonFormat.Mltd : SkeletonFormat.Mmd;
                cc.TranslateFacialExpressionNamesToMmd = ip.TranslateFacialExpressionNames;
                cc.ImportPhysics                       = ip.ImportPhysics;

                cc.Transform60FpsTo30Fps = ip.TransformTo30Fps;
                cc.ScaleToVmdSize        = ip.ScaleVmd;

                {
                    var mappingsJson = File.ReadAllText(ip.FacialExpressionMappingFilePath, Encoding.UTF8);
                    var mappingsObj  = JsonConvert.DeserializeObject <FacialConfig>(mappingsJson);

                    var dict = new Dictionary <int, IReadOnlyDictionary <string, float> >();

                    foreach (var expr in mappingsObj.Expressions)
                    {
                        var d2 = new Dictionary <string, float>();

                        foreach (var kv in expr.Data)
                        {
                            d2[kv.Key] = kv.Value;
                        }

                        dict[expr.Key] = d2;
                    }

                    cc.FacialExpressionMappings = dict;
                }

                return(cc);
            }

            Debug.Assert(InvokeRequired, "The worker procedure should be running on the worker thread.");

            Log("Worker started.");

            try {
                var p = (InputParams)state;

                ConversionConfig.Current = PrepareConversionConfig(p);

                if (p.IdolHeight <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(p.IdolHeight), "Invalid idol height.");
                }

                ScalingConfig.CharacterHeight = p.IdolHeight;

                do
                {
                    var bodyAvatar = ResourceLoader.LoadBodyAvatar(p.InputBody);
                    if (bodyAvatar == null)
                    {
                        Log("Failed to load body avatar.");
                        break;
                    }

                    var bodyMesh = ResourceLoader.LoadBodyMesh(p.InputBody);
                    if (bodyMesh == null)
                    {
                        Log("Failed to load body mesh.");
                        break;
                    }

                    var headAvatar = ResourceLoader.LoadHeadAvatar(p.InputHead);
                    if (headAvatar == null)
                    {
                        Log("Failed to load head avatar.");
                        break;
                    }

                    var headMesh = ResourceLoader.LoadHeadMesh(p.InputHead);
                    if (headMesh == null)
                    {
                        Log("Failed to load head mesh.");
                        break;
                    }

                    var(bodySway, headSway) = ResourceLoader.LoadSwayControllers(p.InputBody, p.InputHead);
                    if (bodySway == null || headSway == null)
                    {
                        Log("Failed to load sway controllers.");
                        break;
                    }

                    var combinedAvatar = CompositeAvatar.FromAvatars(bodyAvatar, headAvatar);
                    var combinedMesh   = CompositeMesh.FromMeshes(bodyMesh, headMesh);

                    CharacterImasMotionAsset dance;
                    ScenarioObject           scenario;

                    if (p.GenerateCharacterMotion)
                    {
                        (dance, _, _) = ResourceLoader.LoadDance(p.InputDance, p.SongPosition);
                        if (dance == null)
                        {
                            Log("Failed to load dance data.");
                            break;
                        }

                        scenario = ResourceLoader.LoadScenario(p.InputFacialExpression);
                        if (scenario == null)
                        {
                            Log("Failed to load scenario object.");
                            break;
                        }
                    }
                    else
                    {
                        dance    = null;
                        scenario = null;
                    }

                    CharacterImasMotionAsset camera;

                    if (p.GenerateCameraMotion)
                    {
                        camera = ResourceLoader.LoadCamera(p.InputCamera);
                        if (camera == null)
                        {
                            Log("Failed to load camera data.");
                            break;
                        }
                    }
                    else
                    {
                        camera = null;
                    }

                    do
                    {
                        // Now file names are like "ch_pr001_201xxx.unity3d".
                        var avatarName = (new FileInfo(p.InputHead).Name).Substring(3, 12);

                        // ss001_015siz -> 015ss001
                        // Note: PMD allows max 19 characters in texture file names.
                        // In the format below, textures will be named like:
                        // tex\015ss001_01.png
                        // which is at the limit.
                        var texPrefix = avatarName.Substring(6, 3) + avatarName.Substring(0, 5);
                        texPrefix = @"tex\" + texPrefix + "_";

                        Log("Generating model...");

                        var pmxCreator = new PmxCreator();
                        var pmx        = pmxCreator.CreateFrom(combinedAvatar, combinedMesh, bodyMesh.VertexCount, texPrefix, bodySway, headSway);

                        if (p.GenerateModel)
                        {
                            Log("Saving model...");

                            using (var w = new PmxWriter(File.Open(p.OutputModel, FileMode.Create, FileAccess.Write, FileShare.Write))) {
                                w.Write(pmx);
                            }
                        }

                        if (p.GenerateCharacterMotion)
                        {
                            Log("Generating character motion...");

                            var creator = new VmdCreator {
                                ProcessBoneFrames   = true,
                                ProcessCameraFrames = false,
                                ProcessFacialFrames = true,
                                ProcessLightFrames  = false
                            };

                            var danceVmd = creator.CreateFrom(dance, combinedAvatar, pmx, null, scenario, p.SongPosition);

                            Log("Saving character motion...");

                            using (var w = new VmdWriter(File.Open(p.OutputCharacterAnimation, FileMode.Create, FileAccess.Write, FileShare.Write))) {
                                w.Write(danceVmd);
                            }
                        }

                        if (p.GenerateCameraMotion)
                        {
                            Log("Generating camera motion...");

                            if (p.UseMvd)
                            {
                                var creator = new MvdCreator {
                                    ProcessBoneFrames   = false,
                                    ProcessCameraFrames = true,
                                    ProcessFacialFrames = false,
                                    ProcessLightFrames  = false
                                };

                                var motion = creator.CreateFrom(null, null, null, camera, null, p.SongPosition);

                                Log("Writing camera motion...");

                                using (var w = new MvdWriter(File.Open(p.OutputCamera, FileMode.Create, FileAccess.Write, FileShare.Write))) {
                                    w.Write(motion);
                                }
                            }
                            else
                            {
                                var creator = new VmdCreator {
                                    ProcessBoneFrames   = false,
                                    ProcessCameraFrames = true,
                                    ProcessFacialFrames = false,
                                    ProcessLightFrames  = false
                                };

                                var motion = creator.CreateFrom(null, null, null, camera, null, p.SongPosition);

                                Log("Writing camera motion...");

                                using (var w = new VmdWriter(File.Open(p.OutputCamera, FileMode.Create, FileAccess.Write, FileShare.Write))) {
                                    w.Write(motion);
                                }
                            }
                        }
                    } while (false);
                } while (false);
            } catch (Exception ex) {
                MessageBox.Show(ex.ToString(), ApplicationHelper.GetApplicationTitle(), MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            Log("Done.");

            Invoke(() => EnableMainControls(true));
        }