Add files via upload

main
RubenSchoonbaert 8 months ago committed by GitHub
parent c36bc6e671
commit b363eabfe2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,9 @@
<Application x:Class="Wpf_3D.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Wpf_3D"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

@ -0,0 +1,9 @@
using System.Windows;
namespace Wpf_3D {
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application {
}
}

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

@ -0,0 +1,112 @@
using Global;
using System;
using System.Windows.Media.Media3D;
namespace Wpf_3D {
/// <summary>
/// This class was made with the help of this article
/// https://www.csharphelper.com/howtos/howto_3D_sphere.html
/// Alot of code is reused from this.
/// </summary>
public class Ball3DGenerator {
private MeshGeometry3D mesh;
public Model3D GenerateBall(Ball ball) {
Model3DGroup modelGroup = new Model3DGroup();
DiffuseMaterial ballMaterial = new DiffuseMaterial(ToBrush(ball.color));
mesh = new MeshGeometry3D();
double x = ball.x + 0.1;
double y = ball.y + 0.1;
double z = 0.5;
AddSphere(new Point3D(x + ball.size, y + ball.size, z), ball.size, 32, 32);
GeometryModel3D ballModel = new GeometryModel3D(mesh, ballMaterial);
modelGroup.Children.Add(ballModel);
return modelGroup;
}
/// <summary>
/// This conversion methode is taken from stackoverflow
/// </summary>
/// <href = https://stackoverflow.com/a/33523743></href>
/// <param name="color"></param>
/// <returns></returns>
private System.Windows.Media.Brush ToBrush(System.Drawing.Color color) {
return new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));
}
private void AddTriangle(Point3D p1, Point3D p2, Point3D p3) {
int index1 = mesh.Positions.Count;
int index2 = index1 + 1;
int index3 = index1 + 2;
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.Positions.Add(p3);
mesh.TriangleIndices.Add(index1);
mesh.TriangleIndices.Add(index2);
mesh.TriangleIndices.Add(index3);
}
/// <summary>
/// Taken from https://www.csharphelper.com/howtos/howto_3D_sphere.html
/// </summary>
/// <param name="center"></param>
/// <param name="radius"></param>
/// <param name="num_phi"></param>
/// <param name="num_theta"></param>
private void AddSphere(Point3D center,
double radius, int num_phi, int num_theta) {
double phi0, theta0;
double dphi = Math.PI / num_phi;
double dtheta = 2 * Math.PI / num_theta;
phi0 = 0;
double y0 = radius * Math.Cos(phi0);
double r0 = radius * Math.Sin(phi0);
for (int i = 0; i < num_phi; i++) {
double phi1 = phi0 + dphi;
double y1 = radius * Math.Cos(phi1);
double r1 = radius * Math.Sin(phi1);
// Point ptAB has phi value A and theta value B.
// For example, pt01 has phi = phi0 and theta = theta1.
// Find the points with theta = theta0.
theta0 = 0;
Point3D pt00 = new Point3D(
center.X + r0 * Math.Cos(theta0),
center.Y + y0,
center.Z + r0 * Math.Sin(theta0));
Point3D pt10 = new Point3D(
center.X + r1 * Math.Cos(theta0),
center.Y + y1,
center.Z + r1 * Math.Sin(theta0));
for (int j = 0; j < num_theta; j++) {
// Find the points with theta = theta1.
double theta1 = theta0 + dtheta;
Point3D pt01 = new Point3D(
center.X + r0 * Math.Cos(theta1),
center.Y + y0,
center.Z + r0 * Math.Sin(theta1));
Point3D pt11 = new Point3D(
center.X + r1 * Math.Cos(theta1),
center.Y + y1,
center.Z + r1 * Math.Sin(theta1));
// Create the triangles.
AddTriangle(pt00, pt11, pt10);
AddTriangle(pt00, pt01, pt11);
// Move to the next value of theta.
theta0 = theta1;
pt00 = pt01;
pt10 = pt11;
}
// Move to the next value of phi.
phi0 = phi1;
y0 = y1;
r0 = r1;
}
}
}
}

@ -0,0 +1,37 @@
<Window x:Class="Wpf_3D.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Wpf_3D"
Title="Window1" Height="398" Width="608" KeyDown="Window_KeyDown">
<Window.Resources>
<local:Maze3DGenerator x:Key="maze3DGenerator" />
<Model3DGroup x:Key="Model3D">
<AmbientLight Color="Gray" />
<DirectionalLight Color="Gray" Direction="1,-2,-3" />
</Model3DGroup>
</Window.Resources>
<Grid>
<Viewport3D Margin="4,4,4,0" Name="viewport3D1" Focusable="True">
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<StaticResource ResourceKey="Model3D" />
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<DirectionalLight x:Name="dirLightMain" Direction="-1,0,2" Color="Gray"></DirectionalLight>
</ModelVisual3D.Content>
</ModelVisual3D>
<Viewport3D.Camera>
<PerspectiveCamera
Position="0,0,15"
LookDirection="0,0,-1"
UpDirection="0,1,0"
FieldOfView="90">
</PerspectiveCamera>
</Viewport3D.Camera>
</Viewport3D>
</Grid>
</Window>

@ -0,0 +1,256 @@
using Global;
using Logic;
using Logica;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Media3D;
namespace Wpf_3D {
public partial class MainWindow : Window {
private int vertR = 0; //vertical angle
private int horzR = 0; //horizontal angle
private ModelVisual3D visual3D;
private Maze filled_maze;
private Ball3DGenerator ball3DGenerator = new Ball3DGenerator();
private Model3DGroup modelGroup = new Model3DGroup();
Maze3DGenerator mazeGen = new Maze3DGenerator();
private bool isGameOver = false;
private bool popupBeingShown = false;
private CancellationTokenSource gameLoopCancellationTokenSource;
private PhysicsEngine physicsEngine;
public MainWindow() {
InitializeComponent();
StartGamePopup();
}
/// <summary>
/// initialise the maze generation & 3D models
/// </summary>
private void InitializeMaze() {
Maze maze = new Maze(15, 15); //Change these parameters to change the size of the maze.
maze.GenerateGrid(System.Drawing.Color.Red);
DepthFirstAlgoritm algo = new DepthFirstAlgoritm(maze);
algo.Generate();
filled_maze = algo.maze;
mazeGen = new Maze3DGenerator();
AddGuiElements(mazeGen.GenerateMaze(algo.maze), ball3DGenerator.GenerateBall(maze.ball));
physicsEngine = new PhysicsEngine(maze.ball, maze);
isGameOver = false;
Task.Run(async () => await GameLoopAsync());
}
/// <summary>
/// calculate physics on the ball & redraw it every 50ms
/// </summary>
/// <returns></returns>
private async Task GameLoopAsync() {
gameLoopCancellationTokenSource = new CancellationTokenSource();
while (!isGameOver) {
await Task.Delay(50);
Dispatcher.Invoke(() => {
CalculateBallMovement();
});
}
}
/// <summary>
/// Popup for the start of the game
/// Starts the game;
/// </summary>
private void StartGamePopup() {
MessageBoxResult result = MessageBox.Show("Druk op OK om het spel te starten, \r\nKantel het bord met de pijltjes (↑ ↓ → ←)\r\nEn zoom de camera in & uit met de 'I' en 'O' toetsen.\r\nDuw op 'R' om het doolhof te resetten", "welkom", MessageBoxButton.OK);
if (result == MessageBoxResult.OK) {
InitializeMaze();
}
else {
Application.Current.Shutdown();
}
}
/// <summary>
/// Popup for the end of the game.
/// Can restart or end the game
/// </summary>
private async void ShowEndPopup() {
if (physicsEngine.isFinished == true && !popupBeingShown) {
popupBeingShown = true;
MessageBoxResult result = MessageBox.Show("Proficiat!\r\nU hebt het doolhof opgelost. \r\nDuw op OK om opnieuw te beginnen en duw op cancel om te stoppen", "Proficiat!", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK) {
await Task.Delay(100);
InitializeMaze();
}
else if (result == MessageBoxResult.Cancel) {
gameLoopCancellationTokenSource.Cancel();
Application.Current.Shutdown();
}
popupBeingShown = false;
}
}
/// <summary>
/// Add all GUI elements to the modelgroup
/// </summary>
/// <param name="maze"></param>
/// <param name="ball"></param>
/// <returns></returns>
private Task AddGuiElements(Model3D maze, Model3D ball) {
modelGroup.Children.Clear();
modelGroup.Children.Add(maze);
modelGroup.Children.Add(ball);
visual3D = new ModelVisual3D {
Content = modelGroup
};
viewport3D1.Children[1] = visual3D;
CentreScreen();
return null;
}
/// <summary>
/// Call the PhysicsEngine and move the ball.
/// Check if the ball is in the end cell
/// </summary>
private void CalculateBallMovement() {
if (!isGameOver) {
Ball calcBall = physicsEngine.CalculateAngleMovement(vertR, horzR);
Dispatcher.Invoke(() => {
modelGroup.Children[1] = ball3DGenerator.GenerateBall(calcBall);
});
if (physicsEngine.isFinished) {
ShowEndPopup();
isGameOver = true;
}
}
}
/// <summary>
/// Center the screen
/// </summary>
private void CentreScreen() {
vertR = 0;
horzR = 0;
Transform3DGroup rotationGroup = new Transform3DGroup();
double centerX = filled_maze.length / 2;
double centerY = filled_maze.height / 2;
TranslateTransform3D centerTranslation = new TranslateTransform3D(-centerX, -centerY, 0);
rotationGroup.Children.Add(centerTranslation);
visual3D.Transform = rotationGroup;
}
/// <summary>
/// Handle the GUI controls
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_KeyDown(object sender, KeyEventArgs e) {
switch (e.Key) {
case Key.Left:
horzR--;
HorizontalMovement();
break;
case Key.Right:
horzR++;
HorizontalMovement();
break;
case Key.Up:
vertR--;
VerticalMovement();
break;
case Key.Down:
vertR++;
VerticalMovement();
break;
case Key.I:
ZoomCamera(-5);
break;
case Key.O:
ZoomCamera(5);
break;
case Key.R:
InitializeMaze();
break;
}
}
/// <summary>
/// Angle the board on the horizontal axis
/// </summary>
private void HorizontalMovement() {
if (horzR > 30) {
horzR = 30;
}
if (horzR < -30) {
horzR = -30;
}
double centerX = filled_maze.length / 2;
double centerY = filled_maze.height / 2;
TranslateTransform3D centerTranslation = new TranslateTransform3D(-centerX, -centerY, 0);
RotateTransform3D horizontalRotation = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), horzR));
RotateTransform3D verticalRotation = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), vertR));
Transform3DGroup rotationGroup = new Transform3DGroup();
rotationGroup.Children.Add(centerTranslation);
rotationGroup.Children.Add(horizontalRotation);
rotationGroup.Children.Add(verticalRotation);
visual3D.Transform = rotationGroup;
}
/// <summary>
/// Angle the board on the vertical axis
/// </summary>
private void VerticalMovement() {
if (vertR > 30) {
vertR = 30;
}
if (vertR < -30) {
vertR = -30;
}
double centerX = filled_maze.length / 2;
double centerY = filled_maze.height / 2;
TranslateTransform3D centerTranslation = new TranslateTransform3D(-centerX, -centerY, 0);
RotateTransform3D horizontalRotation = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), horzR));
RotateTransform3D verticalRotation = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), vertR));
Transform3DGroup rotationGroup = new Transform3DGroup();
rotationGroup.Children.Add(centerTranslation);
rotationGroup.Children.Add(horizontalRotation);
rotationGroup.Children.Add(verticalRotation);
visual3D.Transform = rotationGroup;
}
/// <summary>
/// Change the perspective of the camera to zoom in & out
/// </summary>
/// <param name="zoomFactor"></param>
private void ZoomCamera(double zoomFactor) {
PerspectiveCamera camera = (PerspectiveCamera)viewport3D1.Camera;
camera.FieldOfView += zoomFactor;
if (camera.FieldOfView < 1.0) {
camera.FieldOfView = 1.0;
}
if (camera.FieldOfView > 120.0) {
camera.FieldOfView = 120.0;
}
}
}
}

@ -0,0 +1,138 @@
using Logica;
using System.Collections.Generic;
namespace Wpf_3D {
using System.Windows.Media;
using System.Windows.Media.Media3D;
public class Maze3DGenerator {
List<GeometryModel3D> geom = new List<GeometryModel3D>();
private Maze maze;
public Model3D GenerateMaze(Maze maze) {
this.maze = maze;
geom.Clear();
GenCubes();
Model3D mazeModel = ConvertToSingleModel(geom);
return mazeModel;
}
private void GenCubes() {
for (int x = 0; x < maze.cels.GetLength(0); x++) {
for (int y = 0; y < maze.cels.GetLength(1); y++) {
if (maze.cels[x, y].isWall) {
GenCube(x, y);
}
else {
GenFloor(x, y);
}
}
}
}
private void GenCube(int x, int y) {
int z = 0;
var material = new MaterialGroup();
material.Children.Add(new DiffuseMaterial(Brushes.Red));
material.Children.Add(new SpecularMaterial(Brushes.White, 30));
var geometry = new MeshGeometry3D {
Positions = new Point3DCollection
{
new Point3D(x, y, 0 + z),
new Point3D(x + 1, y, 0 + z),
new Point3D(x, y + 1, 0 + z),
new Point3D(x + 1, y + 1, 0 + z),
new Point3D(x, y, 1 + z),
new Point3D(x + 1, y, 1 + z),
new Point3D(x, y + 1, 1 + z),
new Point3D(x + 1, y + 1, 1 + z)
},
TriangleIndices = new Int32Collection
{
// Bottom face
0, 1, 2, 1, 3, 2,
// Top face
4, 6, 5, 5, 6, 7,
// Side faces
0, 4, 1, 1, 4, 5,
2, 3, 6, 3, 7, 6,
0, 2, 4, 2, 6, 4,
1, 5, 3, 3, 5, 7
},
Normals = new Vector3DCollection
{
new Vector3D(0, 0, -1), // Bottom face
new Vector3D(0, 0, 1), // Top face
new Vector3D(-1, 0, 0), // Left face
new Vector3D(1, 0, 0), // Right face
new Vector3D(0, -1, 0), // Front face
new Vector3D(0, 1, 0) // Back face
}
};
var model = new GeometryModel3D {
Geometry = geometry,
Material = material
};
geometry.Freeze();
geom.Add(model);
}
/// <summary>
/// Generate a floor tile and add it to the model
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
private void GenFloor(int x, int y) {
int z = 0;
var floorMaterial = new DiffuseMaterial();
if (maze.cels[x, y].color == System.Drawing.Color.Blue) {
floorMaterial = new DiffuseMaterial(Brushes.Blue);
}
else {
floorMaterial = new DiffuseMaterial(Brushes.Green);
}
var floorGeometry = new MeshGeometry3D {
Positions = new Point3DCollection
{
new Point3D(x, y, 0 + z),
new Point3D(x + 1, y, 0 + z),
new Point3D(x, y + 1, 0 + z),
new Point3D(x + 1, y + 1, 0 + z),
},
TriangleIndices = new Int32Collection {
0, 2, 1,
1, 2, 3,
1, 3, 2,
2, 0, 1
}
};
var floorModel = new GeometryModel3D {
Geometry = floorGeometry,
Material = floorMaterial
};
geom.Add(floorModel);
}
/// <summary>
/// Convert the List of Geometry 3D models to one single modelgroup
/// </summary>
/// <param name="geom"></param>
/// <returns></returns>
private Model3D ConvertToSingleModel(List<GeometryModel3D> geom) {
Model3DGroup modelGroup = new Model3DGroup();
foreach (var geometryModel in geom) {
modelGroup.Children.Add(geometryModel);
}
return modelGroup;
}
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Logica\Global.csproj" />
<ProjectReference Include="..\Logic\Logic.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<ApplicationDefinition Update="App.xaml">
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<Page Update="MainWindow.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>

@ -0,0 +1,50 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v7.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v7.0": {
"Wpf_3D/1.0.0": {
"dependencies": {
"Global": "1.0.0",
"Logic": "1.0.0"
},
"runtime": {
"Wpf_3D.dll": {}
}
},
"Global/1.0.0": {
"runtime": {
"Global.dll": {}
}
},
"Logic/1.0.0": {
"dependencies": {
"Global": "1.0.0"
},
"runtime": {
"Logic.dll": {}
}
}
}
},
"libraries": {
"Wpf_3D/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Global/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Logic/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

@ -0,0 +1,15 @@
{
"runtimeOptions": {
"tfm": "net7.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "7.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "7.0.0"
}
]
}
}
Loading…
Cancel
Save