-
Notifications
You must be signed in to change notification settings - Fork 1
/
Engine.cs
453 lines (368 loc) · 18 KB
/
Engine.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
//-----------------------------------------------
// Synapse Gaming - Instancing Example
// Copyright © Synapse Gaming 2009
//-----------------------------------------------
//
// This example shows how to perform object instancing using
// SunBurn's built-in effects and skinning support.
//
// The concept behind this type of instancing is: multiple
// meshes packed into a single vertex and index buffer, can be
// rendered in a single draw call, and still be supplied their own
// unique transforms using SunBurn's bone transforms and the
// BlendIndices vertex channel (both normally used for skinning).
//
// During rendering the mesh instance's blend/bone index is used as
// an instance id and index into the provided bone transform
// array. This effectively becomes:
//
// Matrix instancetransform = bonetransforms[instance.BoneIndex0];
//
// Because each mesh uses its own transform it can be manipulated
// separately from all other meshes (as though it's a separate object).
//
// While this example uses procedural boxes and XNA Models, any single meshes
// will work well - even meshes that differ from each other like trees.
//
// Note: multiple part meshes either need to be provided several
// transforms (one per-part) or baked down to a single mesh.
//
// Note: all instances packed into the same container object share
// the same effect / material.
//
//-----------------------------------------------------------------------------
#region Using Statements
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
// Include the necessary SunBurn namespaces.
using SynapseGaming.LightingSystem.Core;
using SynapseGaming.LightingSystem.Effects;
using SynapseGaming.LightingSystem.Effects.Forward;
using SynapseGaming.LightingSystem.Lights;
using SynapseGaming.LightingSystem.Rendering;
using SynapseGaming.LightingSystem.Rendering.Forward;
using SynapseGaming.LightingSystem.Shadows;
#if !SUNBURN_FRAMEWORK
using SynapseGaming.LightingSystem.Editor;
#endif
#endregion
namespace VoxelEngine
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Engine : Microsoft.Xna.Framework.Game
{
public static bool DEBUG = false;
VoxelManager voxelManager = new VoxelManager();
// The SunBurn lighting system.
LightingSystemManager lightingSystemManager;
SceneInterface sceneInterface;
SceneState sceneState;
SceneEnvironment environment;
LightingSystemPreferences preferences;
SplashScreenGameComponent splashScreenGameComponent;
LightingSystemStatistic totalObjectCountStat;
// Scene/camera supporting members.
bool firstMouseSample = true;
Vector3 viewPosition = new Vector3(86.5f, 11.2f, 57.0f);
Vector3 viewRotation = new Vector3(-2.2f, 0.16f, 0.0f);
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
// Default XNA members.
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
const float moveScale = 20.0f;
const string userPreferencesFile = "UserPreferences.xml";
public Engine()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content-" + LightingSystemManager.Edition;
// Minimum requirement.
graphics.MinimumPixelShaderProfile = ShaderProfile.PS_3_0;
graphics.MinimumVertexShaderProfile = ShaderProfile.VS_3_0;
graphics.PreferredBackBufferWidth = 1920;
graphics.PreferredBackBufferHeight = 1080;
graphics.IsFullScreen = true;
graphics.SynchronizeWithVerticalRetrace = true;
graphics.PreparingDeviceSettings += PrepareDeviceSettings;
// Used for advanced edge cleanup.
graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;
// Required for lighting system.
splashScreenGameComponent = new SplashScreenGameComponent(this, graphics);
Components.Add(splashScreenGameComponent);
// Create the lighting system.
lightingSystemManager = new LightingSystemManager(Services);
sceneState = new SceneState();
// Create the scene interface. Acts as a service provider containing all scene managers
// and returning them by type (including custom managers). Also acts as a component
// container where calls to manager methods on the SceneInterface (such as BeginFrameRendering,
// Unload, ...) are automatically called on all contained managers.
//
// This design allows managers to be plugged-in like modular components and for managers
// to easily be added, removed, or replaced with custom implementations.
//
sceneInterface = new SceneInterface(graphics);
sceneInterface.CreateDefaultManagers(false, false, false);
// The skybox handles the back buffer clear.
if (sceneInterface.RenderManager is BaseRenderManager)
(sceneInterface.RenderManager as BaseRenderManager).ClearBackBufferEnabled = true;
// Create a custom statistic, which is rendered to the screen with SunBurn's statistics.
totalObjectCountStat = LightingSystemStatistics.GetStatistic("Instancing_TotalObjectCount", LightingSystemStatisticCategory.Rendering);
// Load the user preferences (example - not required).
preferences = new LightingSystemPreferences();
if (File.Exists(userPreferencesFile))
preferences.LoadFromFile(userPreferencesFile);
else
{
preferences.EffectDetail = DetailPreference.High;
preferences.MaxAnisotropy = 4;
preferences.PostProcessingDetail = DetailPreference.High;
preferences.ShadowDetail = DetailPreference.High;
preferences.ShadowQuality = 1.0f;
preferences.TextureQuality = DetailPreference.High;
preferences.TextureSampling = SamplingPreference.Anisotropic;
}
view = GetViewMatrix();
}
/// <summary>
/// Improves overall performance with dynamic shadows.
/// </summary>
private void PrepareDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PlatformContents;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
// Add objects and lights to the ObjectManager and LightManager
// respectively. The ObjectManager accepts objects in several forms:
//
// -As SceneObjects, which can be dynamic (movable) or static and are
// created from XNA Models or custom vertex / index buffer.
//
// -As XNA Models, which can only be static.
//
voxelManager.AddVoxelType("Models/cobblestone", 10000);
voxelManager.FinishInit(Content, GraphicsDevice, sceneInterface);
//Window.Title = Window.Title + " - Instanced Object Count: " + (instancesPerContainerObject * containerObjects.Length);
// LightRigs contain many lights and light groups.
LightRig rig = new LightRig();
// Ambient lights uniformly illuminate the scene.
AmbientLight ambientlight = new AmbientLight();
ambientlight.Enabled = true;
ambientlight.DiffuseColor = new Vector3(0.8f, 0.98f, 0.99f);
ambientlight.Intensity = 0.5f;
// Directional lights illuminate the scene from a specific direction, similar to sunlight.
DirectionalLight sunlight = new DirectionalLight();
sunlight.Enabled = true;
sunlight.DiffuseColor = new Vector3(1.0f, 0.97f, 0.77f);
sunlight.Intensity = 2.6f;
sunlight.Direction = new Vector3(-0.60f, -0.73f, -0.32f);
sunlight.ShadowType = ShadowType.AllObjects;
sunlight.ShadowQuality = 1.0f;
sunlight.ShadowPrimaryBias = 1.0f;
sunlight.ShadowSecondaryBias = 0.04f;
DirectionalLight sunlightB = new DirectionalLight();
sunlightB.Enabled = true;
sunlightB.DiffuseColor = new Vector3(0.0f, 0.97f, 0.77f);
sunlightB.Intensity = 2.6f;
sunlightB.Direction = new Vector3(0.60f, -0.73f, -0.32f);
sunlightB.ShadowType = ShadowType.AllObjects;
sunlightB.ShadowQuality = 1.0f;
sunlightB.ShadowPrimaryBias = 1.0f;
sunlightB.ShadowSecondaryBias = 0.04f;
// Add the lights to a group.
LightGroup group = new LightGroup();
group.Add(ambientlight);
group.Add(sunlight);
group.Add(sunlightB);
// Add the group to the light rig and commit the changes.
rig.LightGroups.Add(group);
rig.CommitChanges();
// Submit the light rig to the light manager.
sceneInterface.LightManager.Submit(rig);
// Setup the scene settings.
environment = new SceneEnvironment();
environment.VisibleDistance = 250;
environment.FogEnabled = true;
environment.FogColor = new Vector3(0.5f, 0.5f, 0.5f);
environment.FogStartDistance = 200;
environment.FogEndDistance = 250;
environment.ShadowFadeStartDistance = 200;
environment.ShadowFadeEndDistance = 250;
environment.ShadowCasterDistance = 250;
// Apply the user preferences (example - not required).
sceneInterface.ApplyPreferences(preferences);
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
sceneInterface.Unload();
lightingSystemManager.Unload();
environment = null;
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
KeyboardState prev_ks = new KeyboardState();
protected override void Update(GameTime gameTime)
{
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.Escape))
this.Exit();
view = ProcessCameraInput(gameTime);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(70.0f),
graphics.GraphicsDevice.Viewport.AspectRatio, 0.1f, environment.VisibleDistance);
sceneInterface.Update(gameTime);
if (ks.IsKeyDown(Keys.OemTilde) && !prev_ks.IsKeyDown(Keys.OemTilde))
DEBUG = !DEBUG;
voxelManager.Update();
prev_ks = ks;
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
// Check to see if the splash screen is finished.
if (!SplashScreenGameComponent.DisplayComplete)
{
base.Draw(gameTime);
return;
}
// Render the scene.
sceneState.BeginFrameRendering(view, projection, gameTime, environment, true);
sceneInterface.BeginFrameRendering(sceneState);
// Add custom rendering that should occur before the scene is rendered.
sceneInterface.RenderManager.Render();
// Add custom rendering that should occur after the scene is rendered.
sceneInterface.EndFrameRendering();
sceneState.EndFrameRendering();
// Get SunBurn's stats for the frame.
//totalObjectCountStat.AccumulationValue = instancesPerContainerObject * containerObjectCount;
if (DEBUG)
LightingSystemStatistics.Render(GraphicsDevice, LightingSystemStatisticCategory.Rendering, new Vector2(20.0f), Vector2.One, Color.White, gameTime);
base.Draw(gameTime);
}
/// <summary>
/// Handles controller input.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
public Matrix ProcessCameraInput(GameTime gameTime)
{
if (IsActive)
{
GamePadState gamepad = GamePad.GetState(PlayerIndex.One);
KeyboardState keyboard = Keyboard.GetState();
MouseState mouse = Mouse.GetState();
float timescale = (float)gameTime.ElapsedGameTime.TotalSeconds;
float rotatescale = 3.0f * timescale;
float movescale = timescale * moveScale;
// Get the right trigger, which affects speed.
if (gamepad.IsConnected)
{
rotatescale *= (1.0f - gamepad.Triggers.Right * 0.5f);
movescale *= (1.0f - gamepad.Triggers.Right * 0.5f);
}
else if (mouse.RightButton == ButtonState.Pressed)
movescale *= 0.25f;
// If the gamepad is connected use its input instead of the mouse and keyboard.
if (gamepad.IsConnected)
viewRotation -= new Vector3(gamepad.ThumbSticks.Right.X * rotatescale, gamepad.ThumbSticks.Right.Y * rotatescale, 0.0f);
else
{
GraphicsDevice device = graphics.GraphicsDevice;
int halfx = device.Viewport.Width / 2;
int halfy = device.Viewport.Height / 2;
if (!firstMouseSample)
{
// Convert the amount the mouse was moved into camera rotation.
viewRotation.X += MathHelper.ToRadians((float)(halfx - mouse.X) * rotatescale * 1.5f);
viewRotation.Y -= MathHelper.ToRadians((float)(halfy - mouse.Y) * rotatescale * 1.5f);
}
else
firstMouseSample = false;
Mouse.SetPosition(halfx, halfy);
}
if (viewRotation.Y > MathHelper.PiOver2 - 0.01f)
viewRotation.Y = MathHelper.PiOver2 - 0.01f;
else if (viewRotation.Y < -MathHelper.PiOver2 + 0.01f)
viewRotation.Y = -MathHelper.PiOver2 + 0.01f;
Quaternion rot = Quaternion.CreateFromYawPitchRoll(viewRotation.X, viewRotation.Y, viewRotation.Z);
// Now apply the camera movement based on either the gamepad or keyboard input.
if (gamepad.IsConnected)
{
viewPosition += Vector3.Transform(new Vector3(movescale, 0, movescale) * new Vector3(
-gamepad.ThumbSticks.Left.X, 0,
gamepad.ThumbSticks.Left.Y), rot);
}
else
{
Vector3 move = new Vector3();
if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up))
move.Z += 1.0f;
if (keyboard.IsKeyDown(Keys.S) || keyboard.IsKeyDown(Keys.Down))
move.Z -= 1.0f;
if (keyboard.IsKeyDown(Keys.A) || keyboard.IsKeyDown(Keys.Left))
move.X += 1.0f;
if (keyboard.IsKeyDown(Keys.D) || keyboard.IsKeyDown(Keys.Right))
move.X -= 1.0f;
viewPosition += Vector3.Transform(new Vector3(movescale, 0, movescale) * move, rot);
}
}
// mouse visibility...
if (!IsActive || GamePad.GetState(PlayerIndex.One).IsConnected)
IsMouseVisible = true;
else
IsMouseVisible = false;
// Convert the camera rotation and movement into a view transform.
return GetViewMatrix();
}
/// <summary>
/// Convert the camera rotation and movement into a view transform.
/// </summary>
/// <returns></returns>
private Matrix GetViewMatrix()
{
Matrix rotation = Matrix.CreateFromYawPitchRoll(viewRotation.X, viewRotation.Y, viewRotation.Z);
Vector3 target = viewPosition + Vector3.Transform(Vector3.Backward, rotation);
return Matrix.CreateLookAt(viewPosition, target, Vector3.Up);
}
}
}