234 lines
9.3 KiB
C#
234 lines
9.3 KiB
C#
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<VertexPositionColor>(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<VertexPositionTexture>(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<float, float, float> 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)
|
|
{
|
|
}
|
|
}
|
|
}
|