Skip to content

Commit d372441

Browse files
committed
added very basic integration viewer
1 parent fd4b790 commit d372441

30 files changed

+2499
-1713
lines changed

SharpGLTF.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<File Path="examples/Directory.Build.props" />
1111
<Project Path="examples/Example1/Example1.csproj" />
1212
<Project Path="examples/InfiniteSkinnedTentacle/InfiniteSkinnedTentacle.csproj" />
13+
<Project Path="examples/MonoGameIntegrationDemo/MonoGameIntegrationDemo.csproj" Id="4290940f-087c-434d-b108-0bcd85031cca" />
1314
<Project Path="examples/PointCloudGalaxy/PointCloudGalaxy.csproj" />
1415
<Project Path="examples/SharpGLTF.Plotly/SharpGLTF.Plotly.csproj" />
1516
<Project Path="examples/SharpGLTF.Runtime.MonoGame/SharpGLTF.Runtime.MonoGame.csproj" />
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Application xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="MonoGameIntegrationDemo.App"
4+
xmlns:local="using:MonoGameIntegrationDemo"
5+
RequestedThemeVariant="Default">
6+
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
7+
8+
<Application.DataTemplates>
9+
<local:ViewLocator/>
10+
</Application.DataTemplates>
11+
12+
<Application.Styles>
13+
<FluentTheme />
14+
</Application.Styles>
15+
</Application>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Avalonia;
2+
using Avalonia.Controls.ApplicationLifetimes;
3+
using Avalonia.Data.Core;
4+
using Avalonia.Data.Core.Plugins;
5+
using Avalonia.Markup.Xaml;
6+
7+
using MonoGameIntegrationDemo.ViewModels;
8+
using MonoGameIntegrationDemo.Views;
9+
10+
namespace MonoGameIntegrationDemo
11+
{
12+
public partial class App : Application
13+
{
14+
public override void Initialize()
15+
{
16+
AvaloniaXamlLoader.Load(this);
17+
}
18+
19+
public override void OnFrameworkInitializationCompleted()
20+
{
21+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
22+
{
23+
// Line below is needed to remove Avalonia data validation.
24+
// Without this line you will get duplicate validations from both Avalonia and CT
25+
BindingPlugins.DataValidators.RemoveAt(0);
26+
desktop.MainWindow = new MainWindow
27+
{
28+
DataContext = new MainWindowViewModel(),
29+
};
30+
}
31+
32+
base.OnFrameworkInitializationCompleted();
33+
}
34+
}
35+
}
172 KB
Binary file not shown.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection.Metadata;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
using Microsoft.Xna.Framework;
9+
10+
using Microsoft.Xna.Framework.Graphics;
11+
12+
using SharpGLTF.Runtime;
13+
using SharpGLTF.Runtime.Template;
14+
15+
16+
namespace MonoGameIntegrationDemo.Models
17+
{
18+
public class MonoGameContext : Game
19+
{
20+
#region lifecycle
21+
public MonoGameContext()
22+
{
23+
_graphics = new GraphicsDeviceManager(this);
24+
Content.RootDirectory = "Content";
25+
}
26+
27+
protected override void Dispose(bool disposing)
28+
{
29+
if (disposing)
30+
{
31+
_CurrentModelTemplate?.Dispose();
32+
_CurrentModelTemplate = null;
33+
}
34+
35+
base.Dispose(disposing);
36+
}
37+
38+
#endregion
39+
40+
#region data
41+
42+
private readonly GraphicsDeviceManager _graphics;
43+
44+
private MonoGameDeviceContent<MonoGameModelTemplate>? _CurrentModelTemplate;
45+
46+
private MonoGameModelInstance? _CurrentModelInstance;
47+
48+
#endregion
49+
50+
public MonoGameModelInstance SetModel(SharpGLTF.Schema2.ModelRoot model)
51+
{
52+
_CurrentModelInstance = null;
53+
_CurrentModelTemplate?.Dispose(); // destroy previous model, if any.
54+
55+
_CurrentModelTemplate = MonoGameModelTemplate.CreateDeviceModel(this.GraphicsDevice, model);
56+
_CurrentModelInstance = _CurrentModelTemplate.Instance.CreateInstance();
57+
58+
var bounds = _CurrentModelTemplate.Instance.Bounds;
59+
60+
_CameraPos = bounds.Center.ToNumerics() + new System.Numerics.Vector3(0, 0, bounds.Radius * 4);
61+
62+
return _CurrentModelInstance;
63+
}
64+
65+
protected override void Update(GameTime gameTime)
66+
{
67+
// Your game logic here
68+
base.Update(gameTime);
69+
}
70+
71+
private System.Numerics.Vector3 _CameraPos;
72+
73+
protected override void Draw(GameTime gameTime)
74+
{
75+
GraphicsDevice.Clear(Color.CornflowerBlue);
76+
77+
if (_CurrentModelInstance != null)
78+
{
79+
var bounds = _CurrentModelInstance.Template.Bounds;
80+
81+
var mdlPos = bounds.Center;
82+
83+
var camX = Matrix.CreateWorld(_CameraPos, mdlPos - _CameraPos, Vector3.UnitY);
84+
var mdlX = Matrix.CreateRotationY(0.25f * (float)gameTime.TotalGameTime.TotalSeconds) * Matrix.CreateTranslation(Vector3.Zero);
85+
86+
var dc = new ModelDrawingContext(this.GraphicsDevice);
87+
dc.NearPlane = 0.1f; // for small objects, we need to set the near plane very close to the camera.
88+
dc.SetCamera(camX);
89+
90+
// dc.DrawMesh(_LightsAndFog, _MeshCollection[0], mdlX);
91+
92+
_CurrentModelInstance?.Draw(dc.GetProjectionMatrix(), dc.GetViewMatrix(), mdlX);
93+
}
94+
95+
base.Draw(gameTime);
96+
}
97+
}
98+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>WinExe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
7+
<ApplicationManifest>app.manifest</ApplicationManifest>
8+
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<AvaloniaResource Include="Assets\**" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="Avalonia" />
17+
<PackageReference Include="Avalonia.Desktop" />
18+
<PackageReference Include="Avalonia.Themes.Fluent" />
19+
<PackageReference Include="Avalonia.Fonts.Inter" />
20+
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
21+
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" />
22+
<PackageReference Include="Xaml.Behaviors.Avalonia" />
23+
<PackageReference Include="CommunityToolkit.Mvvm" />
24+
<PackageReference Include="AvaloniaInside.MonoGame" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<ProjectReference Include="..\SharpGLTF.Runtime.MonoGame\SharpGLTF.Runtime.MonoGame.csproj" />
29+
</ItemGroup>
30+
</Project>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
using Avalonia;
4+
5+
namespace MonoGameIntegrationDemo
6+
{
7+
internal sealed class Program
8+
{
9+
// Initialization code. Don't use any Avalonia, third-party APIs or any
10+
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
11+
// yet and stuff might break.
12+
[STAThread]
13+
public static void Main(string[] args) => BuildAvaloniaApp()
14+
.StartWithClassicDesktopLifetime(args);
15+
16+
// Avalonia configuration, don't remove; also used by visual designer.
17+
public static AppBuilder BuildAvaloniaApp()
18+
=> AppBuilder.Configure<App>()
19+
.UsePlatformDetect()
20+
.WithInterFont()
21+
.LogToTrace();
22+
}
23+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
3+
using Avalonia.Controls;
4+
using Avalonia.Controls.Templates;
5+
6+
using MonoGameIntegrationDemo.ViewModels;
7+
8+
namespace MonoGameIntegrationDemo
9+
{
10+
public class ViewLocator : IDataTemplate
11+
{
12+
13+
public Control? Build(object? data)
14+
{
15+
if (data is null)
16+
return null;
17+
18+
var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
19+
var type = Type.GetType(name);
20+
21+
if (type != null)
22+
{
23+
var control = (Control)Activator.CreateInstance(type)!;
24+
control.DataContext = data;
25+
return control;
26+
}
27+
28+
return new TextBlock { Text = "Not Found: " + name };
29+
}
30+
31+
public bool Match(object? data)
32+
{
33+
return data is ViewModelBase;
34+
}
35+
}
36+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
4+
using Avalonia.Platform.Storage;
5+
6+
using CommunityToolkit.Mvvm.ComponentModel;
7+
using CommunityToolkit.Mvvm.Input;
8+
9+
using Microsoft.Xna.Framework;
10+
11+
namespace MonoGameIntegrationDemo.ViewModels
12+
{
13+
public partial class MainWindowViewModel : ViewModelBase
14+
{
15+
private readonly Models.MonoGameContext _CurrentGame = new Models.MonoGameContext();
16+
17+
public Game CurrentGame => _CurrentGame;
18+
19+
[ObservableProperty]
20+
private ModelControlsViewModel? _ModelControls;
21+
22+
23+
[RelayCommand]
24+
public async Task LoadModelAsync(IReadOnlyList<Avalonia.Platform.Storage.IStorageFile> files)
25+
{
26+
var modelPath = files[0].TryGetLocalPath();
27+
28+
var model = await Task.Run(() => SharpGLTF.Schema2.ModelRoot.Load(modelPath));
29+
30+
var inst = _CurrentGame.SetModel(model);
31+
32+
ModelControls = new ModelControlsViewModel(model, inst);
33+
}
34+
}
35+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
using CommunityToolkit.Mvvm.ComponentModel;
8+
9+
using SharpGLTF.Runtime;
10+
using SharpGLTF.Schema2;
11+
12+
namespace MonoGameIntegrationDemo.ViewModels
13+
{
14+
public partial class ModelControlsViewModel : ViewModelBase
15+
{
16+
#region lifecycle
17+
public ModelControlsViewModel(ModelRoot loadModel, MonoGameModelInstance instance)
18+
{
19+
_LoadModel = loadModel;
20+
_Instance = instance;
21+
22+
_SelectedTrack = AnimationTracks.FirstOrDefault();
23+
}
24+
#endregion
25+
26+
#region data
27+
28+
private readonly SharpGLTF.Schema2.ModelRoot _LoadModel;
29+
private readonly MonoGameModelInstance _Instance;
30+
31+
#endregion
32+
33+
#region Properties
34+
35+
public IReadOnlyList<AnimationTrackInfo> AnimationTracks => _Instance.Controller.Armature.AnimationTracks;
36+
37+
[ObservableProperty]
38+
private AnimationTrackInfo? _SelectedTrack;
39+
40+
private float _AnimationTime;
41+
42+
public float AnimationTime
43+
{
44+
get => _AnimationTime;
45+
set
46+
{
47+
_AnimationTime = value;
48+
49+
var trackIdx = _SelectedTrack == null
50+
? -1
51+
: AnimationTracks.ToList().IndexOf(_SelectedTrack);
52+
53+
if (trackIdx < 0) return;
54+
55+
56+
_Instance.Controller.Armature.SetAnimationFrame(trackIdx, _AnimationTime);
57+
}
58+
}
59+
60+
#endregion
61+
}
62+
}

0 commit comments

Comments
 (0)