diff --git a/TrueCraft.Client/Content/moon.png b/TrueCraft.Client/Content/moon.png new file mode 100644 index 0000000..eb14853 Binary files /dev/null and b/TrueCraft.Client/Content/moon.png differ diff --git a/TrueCraft.Client/Content/sun.png b/TrueCraft.Client/Content/sun.png new file mode 100644 index 0000000..00d5931 Binary files /dev/null and b/TrueCraft.Client/Content/sun.png differ diff --git a/TrueCraft.Client/Modules/ChunkModule.cs b/TrueCraft.Client/Modules/ChunkModule.cs index 1107105..1aea85d 100644 --- a/TrueCraft.Client/Modules/ChunkModule.cs +++ b/TrueCraft.Client/Modules/ChunkModule.cs @@ -46,16 +46,17 @@ namespace TrueCraft.Client.Modules OpaqueEffect.TextureEnabled = true; OpaqueEffect.Texture = Game.TextureMapper.GetTexture("terrain.png"); OpaqueEffect.FogEnabled = true; - OpaqueEffect.FogStart = 512f; - OpaqueEffect.FogEnd = 1000f; - OpaqueEffect.FogColor = Color.CornflowerBlue.ToVector3(); + OpaqueEffect.FogStart = 0; + OpaqueEffect.FogEnd = Game.Camera.Frustum.Far.D * 0.8f; OpaqueEffect.VertexColorEnabled = true; + OpaqueEffect.LightingEnabled = true; TransparentEffect = new AlphaTestEffect(Game.GraphicsDevice); TransparentEffect.AlphaFunction = CompareFunction.Greater; TransparentEffect.ReferenceAlpha = 127; TransparentEffect.Texture = Game.TextureMapper.GetTexture("terrain.png"); TransparentEffect.VertexColorEnabled = true; + OpaqueEffect.LightingEnabled = true; ChunkMeshes = new List(); IncomingChunks = new ConcurrentBag(); @@ -164,8 +165,11 @@ namespace TrueCraft.Client.Modules public void Draw(GameTime gameTime) { + OpaqueEffect.FogColor = Game.SkyModule.WorldFogColor.ToVector3(); Game.Camera.ApplyTo(OpaqueEffect); Game.Camera.ApplyTo(TransparentEffect); + OpaqueEffect.AmbientLightColor = TransparentEffect.DiffuseColor = Color.White.ToVector3() + * new Microsoft.Xna.Framework.Vector3(0.25f + Game.SkyModule.BrightnessModifier); int chunks = 0; Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default; diff --git a/TrueCraft.Client/Modules/SkyModule.cs b/TrueCraft.Client/Modules/SkyModule.cs new file mode 100644 index 0000000..6571f48 --- /dev/null +++ b/TrueCraft.Client/Modules/SkyModule.cs @@ -0,0 +1,233 @@ +using Microsoft.Xna.Framework; +using System; +using Microsoft.Xna.Framework.Graphics; + +namespace TrueCraft.Client.Modules +{ + public class SkyModule : IGraphicalModule + { + // https://github.com/SirCmpwn/TrueCraft/wiki/Sky + + private TrueCraftGame Game { get; set; } + private BasicEffect SkyPlaneEffect { get; set; } + private BasicEffect CelestialPlaneEffect { get; set; } + private VertexBuffer SkyPlane { get; set; } + private VertexBuffer CelestialPlane { get; set; } + + public SkyModule(TrueCraftGame game) + { + Game = game; + CelestialPlaneEffect = new BasicEffect(Game.GraphicsDevice); + CelestialPlaneEffect.TextureEnabled = true; + + SkyPlaneEffect = new BasicEffect(Game.GraphicsDevice); + SkyPlaneEffect.VertexColorEnabled = false; + SkyPlaneEffect.FogEnabled = true; + SkyPlaneEffect.FogStart = 0; + SkyPlaneEffect.FogEnd = 64 * 0.8f; + SkyPlaneEffect.LightingEnabled = true; + var plane = new[] + { + new VertexPositionColor(new Vector3(-64, 0, -64), Color.White), + new VertexPositionColor(new Vector3(64, 0, -64), Color.White), + new VertexPositionColor(new Vector3(-64, 0, 64), Color.White), + + new VertexPositionColor(new Vector3(64, 0, -64), Color.White), + new VertexPositionColor(new Vector3(64, 0, 64), Color.White), + new VertexPositionColor(new Vector3(-64, 0, 64), Color.White) + }; + SkyPlane = new VertexBuffer(Game.GraphicsDevice, VertexPositionColor.VertexDeclaration, + plane.Length, BufferUsage.WriteOnly); + SkyPlane.SetData(plane); + var celestialPlane = new[] + { + new VertexPositionTexture(new Vector3(-60, 0, -60), new Vector2(0, 0)), + new VertexPositionTexture(new Vector3(60, 0, -60), new Vector2(1, 0)), + new VertexPositionTexture(new Vector3(-60, 0, 60), new Vector2(0, 1)), + + new VertexPositionTexture(new Vector3(60, 0, -60), new Vector2(1, 0)), + new VertexPositionTexture(new Vector3(60, 0, 60), new Vector2(1, 1)), + new VertexPositionTexture(new Vector3(-60, 0, 60), new Vector2(0, 1)) + }; + CelestialPlane = new VertexBuffer(Game.GraphicsDevice, VertexPositionTexture.VertexDeclaration, + celestialPlane.Length, BufferUsage.WriteOnly); + CelestialPlane.SetData(celestialPlane); + } + + private float CelestialAngle + { + get + { + float x = (Game.Client.World.Time % 24000f) / 24000f - 0.25f; + if (x < 0) x = 0; + if (x > 1) x = 1; + return x + ((1 - ((float)Math.Cos(x * MathHelper.Pi) + 1) / 2) - x) / 3; + } + } + + public static Color HSL2RGB(float h, float sl, float l) + { + // Thanks http://www.java2s.com/Code/CSharp/2D-Graphics/HSLtoRGBconversion.htm + float v, r, g, b; + r = g = b = l; // default to gray + v = (l <= 0.5f) ? (l * (1.0f + sl)) : (l + sl - l * sl); + if (v > 0) + { + int sextant; + float m, sv, fract, vsf, mid1, mid2; + m = l + l - v; + sv = (v - m) / v; + h *= 6.0f; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) + { + case 0: + r = v; g = mid1; b = m; + break; + case 1: + r = mid2; g = v; b = m; + break; + case 2: + r = m; g = v; b = mid1; + break; + case 3: + r = m; g = mid2; b = v; + break; + case 4: + r = mid1; g = m; b = v; + break; + case 5: + r = v; g = m; b = mid2; + break; + } + } + return new Color(r, g, b); + } + + private Color BaseColor + { + get + { + // Note: temperature comes from the current biome, but we have to + // do biomes differently than Minecraft so we'll un-hardcode this later. + const float temp = 0.8f / 3; + return HSL2RGB(0.6222222f - temp * 0.05f, 0.5f + temp * 0.1f, BrightnessModifier); + } + } + + public float BrightnessModifier + { + get + { + var mod = (float)Math.Cos(CelestialAngle * MathHelper.TwoPi) * 2 + 0.5f; + if (mod < 0) mod = 0; + if (mod > 1) mod = 1; + return mod; + } + } + + public Color WorldSkyColor + { + get + { + return BaseColor; + } + } + + public Color WorldFogColor + { + get + { + float y = (float)Math.Cos(CelestialAngle * MathHelper.TwoPi) * 2 + 0.5f; + return new Color(0.7529412f * y * 0.94f + 0.06f, + 0.8470588f * y * 0.94f + 0.06f, 1.0f * y * 0.91f + 0.09f); + } + } + + public Color AtmosphereColor + { + get + { + const float blendFactor = 0.29f; // TODO: Compute based on view distance + Func blend = (float source, float destination) => + destination + (source - destination) * blendFactor; + var fog = WorldFogColor.ToVector3(); + var sky = WorldSkyColor.ToVector3(); + var color = new Vector3(blend(sky.X, fog.X), blend(sky.Y, fog.Y), blend(sky.Z, fog.Z)); + // TODO: more stuff + return new Color(color); + } + } + + public void Draw(GameTime gameTime) + { + Game.GraphicsDevice.Clear(AtmosphereColor); + Game.GraphicsDevice.SetVertexBuffer(SkyPlane); + + var position = Game.Camera.Position; + var yaw = Game.Camera.Yaw; + Game.Camera.Position = TrueCraft.API.Vector3.Zero; + Game.Camera.Yaw = 0; + Game.Camera.ApplyTo(SkyPlaneEffect); + Game.Camera.Yaw = yaw; + Game.Camera.ApplyTo(CelestialPlaneEffect); + Game.Camera.Position = position; + // Sky + SkyPlaneEffect.FogColor = AtmosphereColor.ToVector3(); + SkyPlaneEffect.World = Matrix.CreateRotationX(MathHelper.Pi) + * Matrix.CreateTranslation(0, 100, 0) + * Matrix.CreateRotationX(MathHelper.TwoPi * CelestialAngle); + SkyPlaneEffect.AmbientLightColor = WorldSkyColor.ToVector3(); foreach (var pass in SkyPlaneEffect.CurrentTechnique.Passes) + { + pass.Apply(); + SkyPlaneEffect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); + } + + // Sun + Game.GraphicsDevice.SetVertexBuffer(CelestialPlane); + var backup = Game.GraphicsDevice.BlendState; + Game.GraphicsDevice.BlendState = BlendState.Additive; + Game.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + CelestialPlaneEffect.Texture = Game.TextureMapper.GetTexture("terrain/sun.png"); + CelestialPlaneEffect.World = Matrix.CreateRotationX(MathHelper.Pi) + * Matrix.CreateTranslation(0, 100, 0) + * Matrix.CreateRotationX(MathHelper.TwoPi * CelestialAngle); + foreach (var pass in CelestialPlaneEffect.CurrentTechnique.Passes) + { + pass.Apply(); + CelestialPlaneEffect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); + } + // Moon + CelestialPlaneEffect.Texture = Game.TextureMapper.GetTexture("terrain/moon.png"); + CelestialPlaneEffect.World = Matrix.CreateTranslation(0, -100, 0) + * Matrix.CreateRotationX(MathHelper.TwoPi * CelestialAngle); + foreach (var pass in CelestialPlaneEffect.CurrentTechnique.Passes) + { + pass.Apply(); + CelestialPlaneEffect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); + } + Game.GraphicsDevice.BlendState = backup; + Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default; + + // Void + Game.GraphicsDevice.SetVertexBuffer(SkyPlane); + SkyPlaneEffect.World = Matrix.CreateTranslation(0, -16, 0); + SkyPlaneEffect.AmbientLightColor = WorldSkyColor.ToVector3() + * new Vector3(0.2f, 0.2f, 0.6f) + + new Vector3(0.04f, 0.04f, 0.1f); + foreach (var pass in SkyPlaneEffect.CurrentTechnique.Passes) + { + pass.Apply(); + SkyPlaneEffect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); + } + } + + public void Update(GameTime gameTime) + { + } + } +} diff --git a/TrueCraft.Client/ReadOnlyWorld.cs b/TrueCraft.Client/ReadOnlyWorld.cs index bd33d2e..a618b2f 100644 --- a/TrueCraft.Client/ReadOnlyWorld.cs +++ b/TrueCraft.Client/ReadOnlyWorld.cs @@ -15,6 +15,8 @@ namespace TrueCraft.Client internal World World { get; set; } + public long Time { get { return World.Time; } } + internal ReadOnlyWorld() { World = new World("default"); diff --git a/TrueCraft.Client/Rendering/TextureMapper.cs b/TrueCraft.Client/Rendering/TextureMapper.cs index 0d2020a..f6bf708 100644 --- a/TrueCraft.Client/Rendering/TextureMapper.cs +++ b/TrueCraft.Client/Rendering/TextureMapper.cs @@ -36,6 +36,8 @@ namespace TrueCraft.Client.Rendering Defaults.Add("gui/crafting.png", new PngReader().Read(File.OpenRead("Content/crafting.png"), graphicsDevice)); Defaults.Add("gui/furnace.png", new PngReader().Read(File.OpenRead("Content/furnace.png"), graphicsDevice)); Defaults.Add("gui/inventory.png", new PngReader().Read(File.OpenRead("Content/inventory.png"), graphicsDevice)); + Defaults.Add("terrain/moon.png", new PngReader().Read(File.OpenRead("Content/moon.png"), graphicsDevice)); + Defaults.Add("terrain/sun.png", new PngReader().Read(File.OpenRead("Content/sun.png"), graphicsDevice)); } /// diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index c7f3a67..6510758 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -151,6 +151,7 @@ + @@ -379,6 +380,12 @@ OpenTK.dll.config PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index d1259de..3445638 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -42,6 +42,7 @@ namespace TrueCraft.Client public AudioManager Audio { get; set; } public Texture2D White1x1 { get; set; } public PlayerControlModule ControlModule { get; set; } + public SkyModule SkyModule { get; set; } private List InputModules { get; set; } private List GraphicalModules { get; set; } @@ -121,18 +122,23 @@ namespace TrueCraft.Client base.Initialize(); // (calls LoadContent) + Camera = new Camera(GraphicsDevice.Viewport.AspectRatio, 70.0f, 0.1f, 1000.0f); + UpdateCamera(); + White1x1 = new Texture2D(GraphicsDevice, 1, 1); White1x1.SetData(new[] { Color.White }); Audio = new AudioManager(); Audio.LoadDefaultPacks(Content); + SkyModule = new SkyModule(this); ChunkModule = new ChunkModule(this); DebugInfoModule = new DebugInfoModule(this, Pixel); ChatModule = new ChatModule(this, Pixel); var hud = new HUDModule(this, Pixel); var windowModule = new WindowModule(this, Pixel); + GraphicalModules.Add(SkyModule); GraphicalModules.Add(ChunkModule); GraphicalModules.Add(new HighlightModule(this)); GraphicalModules.Add(hud); @@ -161,9 +167,6 @@ namespace TrueCraft.Client var centerY = GraphicsDevice.Viewport.Height / 2; Mouse.SetPosition(centerX, centerY); - Camera = new Camera(GraphicsDevice.Viewport.AspectRatio, 70.0f, 0.1f, 1000.0f); - UpdateCamera(); - MouseComponent.Scroll += OnMouseComponentScroll; MouseComponent.Move += OnMouseComponentMove; MouseComponent.ButtonDown += OnMouseComponentButtonDown; @@ -397,7 +400,6 @@ namespace TrueCraft.Client GraphicsDevice.SetRenderTarget(RenderTarget); GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; - Graphics.GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp; GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp;