private void Reset() { StopAnimation(); _monoGameContent?.Dispose(); _monoGameContent = null; Debug.Assert(ModelNode == null || ModelNode.IsDisposed, "ModelNode should be disposed together with the ContentManager."); ModelNode = null; Model = null; _tempDirectoryHelper?.Dispose(); _tempDirectoryHelper = null; _assimpScene = null; if (_outlineService != null && _outlineService.Outline == Outline) { _outlineService.Outline = null; } if (_propertiesService != null && _propertiesService.PropertySource == _currentPropertySource) { _propertiesService.PropertySource = null; } CleanErrors(); }
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)); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- 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); }
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; } }
/// <summary> /// Creates a new content builder. /// </summary> /// <param name="editor">The editor.</param> public XnaContentBuilder(IEditorService editor) { if (editor == null) { throw new ArgumentNullException(nameof(editor)); } _outputService = editor.Services.GetInstance <IOutputService>().ThrowIfMissing(); //_buildLogger = new BuildLogger(outputService); _tempDirectoryHelper = new TempDirectoryHelper(editor.ApplicationName, "XnaContentBuilder"); CreateBuildProject(); }
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; } }
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(); }
private void Reset() { StopAnimation(); _monoGameContent?.Dispose(); _monoGameContent = null; Debug.Assert(ModelNode == null || ModelNode.IsDisposed, "ModelNode should be disposed together with the ContentManager."); ModelNode = null; Model = null; _tempDirectoryHelper?.Dispose(); _tempDirectoryHelper = null; _assimpScene = null; if (_outlineService != null && _outlineService.Outline == Outline) _outlineService.Outline = null; if (_propertiesService != null && _propertiesService.PropertySource == _currentPropertySource) _propertiesService.PropertySource = null; CleanErrors(); }
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(); }
//-------------------------------------------------------------- /// <summary> /// Creates a new content builder. /// </summary> /// <param name="editor">The editor.</param> public XnaContentBuilder(IEditorService editor) { if (editor == null) throw new ArgumentNullException(nameof(editor)); _outputService = editor.Services.GetInstance<IOutputService>().ThrowIfMissing(); //_buildLogger = new BuildLogger(outputService); _tempDirectoryHelper = new TempDirectoryHelper(editor.ApplicationName, "XnaContentBuilder"); CreateBuildProject(); }