private static async Task <Tuple <bool, List <Error> > > BuildMonoGameAsync(
            ServiceContainer services, string applicationName, TextDocument document, DelegateCommand <Error> goToLocationCommand)
        {
            string errorMessage = await Task.Run(() =>
            {
                using (var tempDirectoryHelper = new TempDirectoryHelper(applicationName, "ShaderDocument"))
                {
                    var contentBuilder = new GameContentBuilder(services)
                    {
                        IntermediateFolder = tempDirectoryHelper.TempDirectoryName + "\\obj",
                        OutputFolder       = tempDirectoryHelper.TempDirectoryName + "\\bin",
                    };

                    string message;
                    contentBuilder.Build(document.Uri.LocalPath, null, "EffectProcessor", null, out message);
                    return(message);
                }
            });

            List <Error> errors = null;

            if (!string.IsNullOrEmpty(errorMessage))
            {
                errorMessage = errorMessage.TrimEnd(' ', '\r', '\n');
                errors       = new List <Error>();

                // Use regular expression to parse the MSBuild output lines.
                // Example: "X:\DigitalRune\Samples\DigitalRune.Graphics.Content\DigitalRune\Billboard.fx(22,3) : Unexpected token 'x' found. Expected CloseBracket"
                var regex = new Regex(@"(?<file>[^\\/]*)\((?<line>\d+),(?<column>\d+)\) : (?<message>.*)");

                var match = regex.Match(errorMessage);
                if (match.Success)
                {
                    string lineText   = match.Groups["line"].Value;
                    string columnText = match.Groups["column"].Value;
                    string message    = match.Groups["message"].Value;
                    bool   isWarning  = match.Groups["warning"].Success;
                    var    error      = new Error(
                        isWarning ? ErrorType.Warning : ErrorType.Error,
                        $"[MonoGame] {message}",
                        match.Groups["file"].Value,
                        int.Parse(lineText),
                        int.Parse(columnText));

                    error.UserData            = document;
                    error.GoToLocationCommand = goToLocationCommand;
                    errors.Add(error);
                }
                else
                {
                    errors.Add(new Error(ErrorType.Error, errorMessage));
                }
            }

            return(Tuple.Create(string.IsNullOrEmpty(errorMessage), errors));
        }
예제 #2
0
        private static Tuple <string, TempDirectoryHelper> BuildXnb(IServiceLocator services, string applicationName, string fileName, bool createModelNode, bool recreateModelAndMaterialFiles)
        {
            Debug.Assert(services != null);
            Debug.Assert(applicationName != null);
            Debug.Assert(applicationName.Length > 0);
            Debug.Assert(fileName != null);
            Debug.Assert(fileName.Length > 0);

            var tempDirectoryHelper = new TempDirectoryHelper(applicationName, "ModelDocument");

            try
            {
                var contentBuilder = new GameContentBuilder(services)
                {
                    IntermediateFolder = tempDirectoryHelper.TempDirectoryName + "\\obj",
                    OutputFolder       = tempDirectoryHelper.TempDirectoryName + "\\bin",
                };

                string processorName;
                var    processorParams = new OpaqueDataDictionary();
                if (createModelNode)
                {
                    processorName = "GameModelProcessor";
                    processorParams.Add("RecreateModelDescriptionFile", recreateModelAndMaterialFiles);
                    processorParams.Add("RecreateMaterialDefinitionFiles", recreateModelAndMaterialFiles);
                }
                else
                {
                    processorName = "ModelProcessor";
                }

                string errorMessage;
                bool   success = contentBuilder.Build(Path.GetFullPath(fileName), null, processorName, processorParams, out errorMessage);
                if (!success)
                {
                    throw new EditorException(Invariant($"Could not process 3d model: {fileName}.\n See output window for details."));
                }

                // Return output folder into which we built the XNB.
                var outputFolder = contentBuilder.OutputFolder;
                if (!Path.IsPathRooted(outputFolder))
                {
                    outputFolder = Path.GetFullPath(Path.Combine(contentBuilder.ExecutableFolder, contentBuilder.OutputFolder));
                }

                return(Tuple.Create(outputFolder, tempDirectoryHelper));
            }
            catch
            {
                tempDirectoryHelper.Dispose();
                throw;
            }
        }
예제 #3
0
        private async Task LoadAsync(string fileName, bool recreateModelAndMaterialFiles)
        {
            Debug.Assert(_tempDirectoryHelper == null);
            Debug.Assert(_monoGameContent == null);
            Debug.Assert(ModelNode == null);
            Debug.Assert(Model == null);
            Debug.Assert(_assimpScene == null);
            Debug.Assert(State == ModelDocumentState.Loading);

            string extension = Path.GetExtension(fileName);

            IsXnb = string.Compare(extension, ".XNB", StringComparison.OrdinalIgnoreCase) == 0;

            // ----- Build XNB
            string directoryName;

            try
            {
                if (IsXnb)
                {
                    // Get the folder that contains the XNB.
                    directoryName = Path.GetDirectoryName(fileName);
                }
                else if (GameContentBuilder.IsSupportedModelFileExtension(extension))
                {
                    // Build the XNB and get the output folder.
                    var buildResult = await Task.Run(() => BuildXnb(Editor.Services, Editor.ApplicationName, fileName, UseDigitalRuneGraphics, recreateModelAndMaterialFiles));

                    directoryName        = buildResult.Item1;
                    _tempDirectoryHelper = buildResult.Item2;
                }
                else
                {
                    throw new EditorException(Invariant($"Unsupported 3D model file format (file extension \"{extension}\")."));
                }
            }
            catch (Exception)
            {
                if (IsDisposed)
                {
                    // Document was closed during loading.
                    Reset();
                    return;
                }

                State = ModelDocumentState.Error;
                UpdateProperties();
                UpdateOutline();

                // The GameContentBuilder logs to the output service.
                _outputService.Show();

                throw;
            }

            if (IsDisposed)
            {
                // Document was closed during loading.
                Reset();
                return;
            }

            Debug.Assert(directoryName != null);

            // ----- Load XNB
            try
            {
                // Get asset name for use with ContentManager. XNBs and unprocessed models
                // use different folder hierarchies.
                string assetFileName;
                if (IsXnb)
                {
                    // Use absolute path.
                    assetFileName = fileName;
                }
                else
                {
                    // The asset is built relative to the root folder (e.g. "C:\"). The folder
                    // hierarchy (from root to asset) is rebuilt in the temporary output folder.

                    // Make file name relative to root.
                    assetFileName = DRPath.GetRelativePath(Path.GetPathRoot(fileName), fileName);

                    // Get absolute file name relative to temporary output folder.
                    assetFileName = Path.Combine(directoryName, assetFileName);

                    // Change extension. .fbx --> .xnb
                    assetFileName = Path.ChangeExtension(assetFileName, "xnb");
                }

                _monoGameContent = await Task.Run(() => _monoGameService.LoadXnb(directoryName, assetFileName, cacheResult: false));

                if (_monoGameContent.Asset is ModelNode)
                {
                    ModelNode = (ModelNode)_monoGameContent.Asset;
                    UseDigitalRuneGraphics = true;
                    HasAnimations          = ModelNode.GetDescendants()
                                             .OfType <MeshNode>()
                                             .FirstOrDefault()?
                                             .Mesh?
                                             .Animations?
                                             .Count > 0;
                }
                else if (_monoGameContent.Asset is Model)
                {
                    Model = (Model)_monoGameContent.Asset;
                    UseDigitalRuneGraphics = false;
                    HasAnimations          = false;

                    // Enable default lighting.
                    var effects = Model.Meshes
                                  .SelectMany(m => m.Effects)
                                  .OfType <IEffectLights>();
                    foreach (var effect in effects)
                    {
                        effect.EnableDefaultLighting();
                    }
                }
                else
                {
                    throw new EditorException("XNB does not contain ModelNode or Model.");
                }
            }
            catch (Exception exception)
            {
                Reset();

                if (IsDisposed)
                {
                    return;
                }

                State = ModelDocumentState.Error;
                Logger.Error(exception, "XNB could not be loaded.");

                // Let LoadAsync return and fail, then show message box.
                WindowsHelper.BeginInvokeOnUI(() =>
                {
                    var message = Invariant($"XNB could not be loaded:\n\n\"{exception.Message}\"");
                    MessageBox.Show(message, Editor.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Error);
                });

                throw;
            }

            if (IsDisposed)
            {
                // Document was closed during loading.
                Reset();
                return;
            }

            // ----- Load Assimp scene
            try
            {
                if (!IsXnb)
                {
                    _assimpScene = await Task.Run(() => LoadAssimp(fileName));
                }
            }
            catch (Exception exception)
            {
                Logger.Warn(exception, "Assimp could not read model file.");
            }

            if (IsDisposed)
            {
                // Document was closed during loading.
                Reset();
                return;
            }

            State = ModelDocumentState.Loaded;

            // ----- Validate model
            ValidateModelNode();
            // TODO: Validate MonoGame Model.

            // If there are errors or warnings, show Output and Errors window.
            // (Drawback: This steals focus from the document.)
            //if (_errors.Count > 0)
            //{
            //    _outputService?.Show();
            //    _errorService?.Show();
            //}

            // ----- Update outline and properties
            UpdateOutline();
            UpdateProperties();
        }