This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2016-04-20 20:54:00 -04:00

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)
{
}
}
}