Fix highlighted block, add crosshairs

This commit is contained in:
Drew DeVault 2015-09-27 17:14:04 -04:00
parent 6066db0bd7
commit 02146108ba
14 changed files with 204 additions and 210 deletions

View File

@ -79,6 +79,14 @@ namespace TrueCraft.API
return new Vector3(Math.Floor(X), Math.Floor(Y), Math.Floor(Z));
}
/// <summary>
/// Rounds the decimal component of each part of this Vector3.
/// </summary>
public Vector3 Round()
{
return new Vector3(Math.Round(X), Math.Round(Y), Math.Round(Z));
}
/// <summary>
/// Clamps the vector to within the specified value.
/// </summary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,38 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace TrueCraft.Client.Modules
{
public class HUDModule : IGraphicalModule
{
private TrueCraftGame Game { get; set; }
private SpriteBatch SpriteBatch { get; set; }
private Texture2D GUI { get; set; }
private Texture2D Icons { get; set; }
public HUDModule(TrueCraftGame game)
{
Game = game;
SpriteBatch = new SpriteBatch(game.GraphicsDevice);
GUI = game.TextureMapper.GetTexture("gui/gui.png");
Icons = game.TextureMapper.GetTexture("gui/icons.png");
}
public void Update(GameTime gameTime)
{
}
static readonly Color CrosshairColor = new Color(255, 255, 255, 70);
public void Draw(GameTime gameTime)
{
SpriteBatch.Begin(samplerState: SamplerState.PointClamp);
SpriteBatch.Draw(Icons, new Vector2(
Game.GraphicsDevice.Viewport.Width / 2 - (8 * Game.ScaleFactor * 2),
Game.GraphicsDevice.Viewport.Height / 2 - (8 * Game.ScaleFactor * 2)),
new Rectangle(0, 0, 16, 16), CrosshairColor,
0, Vector2.Zero, Game.ScaleFactor * 2, SpriteEffects.None, 1);
SpriteBatch.End();
}
}
}

View File

@ -3,6 +3,9 @@ using Microsoft.Xna.Framework.Graphics;
using TrueCraft.API;
using TrueCraft.Client.Rendering;
using Microsoft.Xna.Framework;
using XVector3 = Microsoft.Xna.Framework.Vector3;
using TVector3 = TrueCraft.API.Vector3;
using TRay = TrueCraft.API.Ray;
namespace TrueCraft.Client.Modules
{
@ -10,51 +13,37 @@ namespace TrueCraft.Client.Modules
{
public TrueCraftGame Game { get; set; }
private Texture2D HighlightTexture { get; set; }
private Coordinates3D HighlightedBlock { get; set; }
private Mesh HighlightMesh { get; set; }
private BasicEffect HighlightEffect { get; set; }
private static readonly VertexPositionColor[] CubeVerticies;
private static readonly short[] CubeIndicies;
static HighlightModule()
{
var color = Color.Black;
CubeVerticies = new[]
{
new VertexPositionColor(new XVector3(0, 0, 1), color),
new VertexPositionColor(new XVector3(1, 0, 1), color),
new VertexPositionColor(new XVector3(1, 1, 1), color),
new VertexPositionColor(new XVector3(0, 1, 1), color),
new VertexPositionColor(new XVector3(0, 0, 0), color),
new VertexPositionColor(new XVector3(1, 0, 0), color),
new VertexPositionColor(new XVector3(1, 1, 0), color),
new VertexPositionColor(new XVector3(0, 1, 0), color)
};
CubeIndicies = new short[]
{
0, 1, 1, 2, 2, 3, 3, 0,
0, 4, 4, 7, 7, 6, 6, 2,
1, 5, 5, 4, 3, 7, 6, 5
};
}
public HighlightModule(TrueCraftGame game)
{
Game = game;
const int size = 64;
HighlightedBlock = -Coordinates3D.One;
HighlightTexture = new Texture2D(Game.GraphicsDevice, size, size);
var colors = new Color[size * size];
for (int i = 0; i < colors.Length; i++)
colors[i] = Color.Transparent;
for (int x = 0; x < size; x++)
colors[x] = Color.Black; // Top
for (int x = 0; x < size; x++)
colors[x + (size - 1) * size] = Color.Black; // Bottom
for (int y = 0; y < size; y++)
colors[y * size] = Color.Black; // Left
for (int y = 0; y < size; y++)
colors[y * size + (size - 1)] = Color.Black; // Right
HighlightTexture.SetData<Color>(colors);
var texcoords = new[]
{
Vector2.UnitX + Vector2.UnitY,
Vector2.UnitY,
Vector2.Zero,
Vector2.UnitX
};
int[] indicies;
var verticies = BlockRenderer.CreateUniformCube(Microsoft.Xna.Framework.Vector3.Zero,
texcoords, VisibleFaces.All, 0, out indicies, Color.White);
HighlightMesh = new Mesh(Game, verticies, indicies);
HighlightEffect = new BasicEffect(Game.GraphicsDevice);
HighlightEffect.EnableDefaultLighting();
HighlightEffect.DirectionalLight0.SpecularColor = Color.Black.ToVector3();
HighlightEffect.DirectionalLight1.SpecularColor = Color.Black.ToVector3();
HighlightEffect.DirectionalLight2.SpecularColor = Color.Black.ToVector3();
HighlightEffect.TextureEnabled = true;
HighlightEffect.Texture = HighlightTexture;
HighlightEffect.VertexColorEnabled = true;
}
@ -66,18 +55,19 @@ namespace TrueCraft.Client.Modules
Matrix.CreateRotationY(MathHelper.ToRadians(Game.Client.Yaw)));
var cast = VoxelCast.Cast(Game.Client.World,
new TrueCraft.API.Ray(Game.Camera.Position,
new TrueCraft.API.Vector3(direction.X, direction.Y, direction.Z)),
Game.BlockRepository, TrueCraftGame.Reach);
new TRay(Game.Camera.Position, new TVector3(direction.X, direction.Y, direction.Z)),
Game.BlockRepository, (int)TrueCraftGame.Reach);
if (cast == null)
HighlightedBlock = -Coordinates3D.One;
else
{
HighlightedBlock = cast.Item1;
HighlightEffect.World = Matrix.CreateScale(1.02f) *
Matrix.CreateTranslation(new Microsoft.Xna.Framework.Vector3(
cast.Item1.X, cast.Item1.Y, cast.Item1.Z));
HighlightEffect.World =
Matrix.CreateTranslation(new XVector3(-0.5f)) *
Matrix.CreateScale(1.01f) *
Matrix.CreateTranslation(new XVector3(0.5f)) *
Matrix.CreateTranslation(new XVector3(cast.Item1.X, cast.Item1.Y, cast.Item1.Z));
}
}
@ -87,8 +77,13 @@ namespace TrueCraft.Client.Modules
if (HighlightedBlock != -Coordinates3D.One)
{
Game.GraphicsDevice.DepthStencilState = DepthStencilState.None;
HighlightMesh.Draw(HighlightEffect);
foreach (var pass in HighlightEffect.CurrentTechnique.Passes)
{
pass.Apply();
HighlightEffect.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
PrimitiveType.LineList, CubeVerticies, 0,
CubeVerticies.Length, CubeIndicies, 0, CubeIndicies.Length / 2);
}
}
}
}

View File

@ -11,10 +11,12 @@ namespace TrueCraft.Client.Modules
{
private TrueCraftGame Game { get; set; }
private Vector3 Delta { get; set; }
private bool Capture { get; set; }
public PlayerControlModule(TrueCraftGame game)
{
Game = game;
Capture = true;
}
public bool KeyDown(GameTime gameTime, KeyboardKeyEventArgs e)
@ -55,6 +57,14 @@ namespace TrueCraft.Client.Modules
Delta += Vector3.Backward;
return true;
case Keys.I:
Game.Client.Position = Game.Client.Position.Floor();
return true;
case Keys.Tab:
Capture = !Capture;
return true;
case Keys.Space:
if (Math.Floor(Game.Client.Position.Y) == Game.Client.Position.Y)
Game.Client.Velocity += TrueCraft.API.Vector3.Up * 0.3;
@ -96,6 +106,8 @@ namespace TrueCraft.Client.Modules
public void MouseMove(GameTime gameTime, MouseMoveEventArgs e)
{
if (!Capture)
return;
var centerX = Game.GraphicsDevice.Viewport.Width / 2;
var centerY = Game.GraphicsDevice.Viewport.Height / 2;
Mouse.SetPosition(centerX, centerY);

View File

@ -256,7 +256,8 @@ namespace TrueCraft.Client
{
get
{
return new BoundingBox(Position, Position + Size);
var pos = Position - new Vector3(Width / 2, 0, Depth / 2);
return new BoundingBox(pos, pos + Size);
}
}

View File

@ -147,50 +147,50 @@ namespace TrueCraft.Client.Rendering
CubeMesh[0] = new[] // Positive Z face
{
new Vector3(0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3(0.5f, 0.5f, 0.5f)
new Vector3(1, 0, 1),
new Vector3(0, 0, 1),
new Vector3(0, 1, 1),
new Vector3(1, 1, 1)
};
CubeMesh[1] = new[] // Negative Z face
{
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(0.5f, 0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f)
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(1, 1, 0),
new Vector3(0, 1, 0)
};
CubeMesh[2] = new[] // Positive X face
{
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(0.5f, -0.5f, 0.5f),
new Vector3(0.5f, 0.5f, 0.5f),
new Vector3(0.5f, 0.5f, -0.5f)
new Vector3(1, 0, 0),
new Vector3(1, 0, 1),
new Vector3(1, 1, 1),
new Vector3(1, 1, 0)
};
CubeMesh[3] = new[] // Negative X face
{
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, 0.5f)
new Vector3(0, 0, 1),
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(0, 1, 1)
};
CubeMesh[4] = new[] // Positive Y face
{
new Vector3(0.5f, 0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3(0.5f, 0.5f, -0.5f)
new Vector3(1, 1, 1),
new Vector3(0, 1, 1),
new Vector3(0, 1, 0),
new Vector3(1, 1, 0)
};
CubeMesh[5] = new[] // Negative Y face
{
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(0.5f, -0.5f, 0.5f)
new Vector3(1, 0, 0),
new Vector3(0, 0, 0),
new Vector3(0, 0, 1),
new Vector3(1, 0, 1)
};
}
}

View File

@ -27,17 +27,13 @@ namespace TrueCraft.Client.Rendering.Blocks
public override VertexPositionNormalColorTexture[] Render(BlockDescriptor descriptor, Vector3 offset,
VisibleFaces faces, Tuple<int, int> textureMap, int indiciesOffset, out int[] indicies)
{
var overhead = new Vector3(0.5f, 0.5f, 0.5f);
var cube = CreateUniformCube(overhead, Texture, faces, indiciesOffset, out indicies, Color.White);
var cube = CreateUniformCube(Vector3.Zero, Texture, faces, indiciesOffset, out indicies, Color.White);
var heightMultiplier = new Vector3(1, ((descriptor.Metadata + 1) / 16f), 1);
for (int i = 0; i < cube.Length; i++)
{
if (cube[i].Position.Y > 0)
{
cube[i].Position *= heightMultiplier;
}
cube[i].Position += offset;
cube[i].Position -= overhead;
}
return cube;
}

View File

@ -46,10 +46,11 @@ namespace TrueCraft.Client.Rendering
indicies = new int[4 * 2 * 6];
var verticies = new VertexPositionNormalColorTexture[4 * 2 * 6];
int[] _indicies;
var center = new Vector3(-0.5f, -0.5f, -0.5f);
for (int _side = 0; _side < 4; _side++) // Y faces are the last two in the CubeFace enum, so we can just iterate to 4
{
var side = (CubeFace)_side;
var quad = CreateQuad(side, Vector3.Zero, texture, 0, indiciesOffset, out _indicies, Color.White);
var quad = CreateQuad(side, center, texture, 0, indiciesOffset, out _indicies, Color.White);
if (side == CubeFace.NegativeX || side == CubeFace.PositiveX)
{
for (int i = 0; i < quad.Length; i++)
@ -73,7 +74,7 @@ namespace TrueCraft.Client.Rendering
for (int _side = 0; _side < 4; _side++)
{
var side = (CubeFace)_side;
var quad = CreateQuad(side, Vector3.Zero, texture, 0, indiciesOffset, out _indicies, Color.White);
var quad = CreateQuad(side, center, texture, 0, indiciesOffset, out _indicies, Color.White);
if (side == CubeFace.NegativeX || side == CubeFace.PositiveX)
{
for (int i = 0; i < quad.Length; i++)
@ -98,6 +99,7 @@ namespace TrueCraft.Client.Rendering
for (int i = 0; i < verticies.Length; i++)
{
verticies[i].Position.Y -= 1 / 16f;
verticies[i].Position -= center;
}
return verticies;
}

View File

@ -80,34 +80,34 @@ namespace TrueCraft.Client.Rendering
QuadMesh[0] = new[]
{
new Vector3(0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3(0.5f, 0.5f, 0.5f)
new Vector3(1, 0, 1),
new Vector3(0, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 1, 1)
};
QuadMesh[1] = new[]
{
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(0.5f, -0.5f, 0.5f),
new Vector3(0.5f, 0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, -0.5f)
new Vector3(0, 0, 0),
new Vector3(1, 0, 1),
new Vector3(1, 1, 1),
new Vector3(0, 1, 0)
};
QuadMesh[2] = new[]
{
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(0.5f, 0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, 0.5f)
new Vector3(0, 0, 1),
new Vector3(1, 0, 0),
new Vector3(1, 1, 0),
new Vector3(0, 1, 1)
};
QuadMesh[3] = new[]
{
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3(0.5f, 0.5f, -0.5f)
new Vector3(1, 0, 0),
new Vector3(0, 0, 1),
new Vector3(0, 1, 1),
new Vector3(1, 1, 0)
};
}
}

View File

@ -31,6 +31,8 @@ namespace TrueCraft.Client.Rendering
Defaults.Add("items.png", new PngReader().Read(File.OpenRead("Content/items.png"), graphicsDevice));
Defaults.Add("terrain.png", new PngReader().Read(File.OpenRead("Content/terrain.png"), graphicsDevice));
Defaults.Add("gui/gui.png", new PngReader().Read(File.OpenRead("Content/gui.png"), graphicsDevice));
Defaults.Add("gui/icons.png", new PngReader().Read(File.OpenRead("Content/icons.png"), graphicsDevice));
}
/// <summary>

View File

@ -132,6 +132,7 @@
<Compile Include="Modules\HighlightModule.cs" />
<Compile Include="Modules\PlayerControlModule.cs" />
<Compile Include="Modules\DebugInfoModule.cs" />
<Compile Include="Modules\HUDModule.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -230,8 +231,11 @@
<Content Include="Content\gui.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\icons.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Modules\" />
</ItemGroup>
</Project>
</Project>

View File

@ -28,6 +28,7 @@ namespace TrueCraft.Client
public ConcurrentBag<Action> PendingMainThreadActions { get; set; }
public double Bobbing { get; set; }
public ChunkModule ChunkModule { get; set; }
public float ScaleFactor { get; set; }
private List<IGameplayModule> Modules { get; set; }
private SpriteBatch SpriteBatch { get; set; }
@ -43,7 +44,7 @@ namespace TrueCraft.Client
private GameTime GameTime { get; set; }
private DebugInfoModule DebugInfoModule { get; set; }
public static readonly int Reach = 5;
public static readonly double Reach = 3;
public IBlockRepository BlockRepository
{
@ -63,6 +64,7 @@ namespace TrueCraft.Client
Graphics.PreferredBackBufferWidth = UserSettings.Local.WindowResolution.Width;
Graphics.PreferredBackBufferHeight = UserSettings.Local.WindowResolution.Height;
Graphics.ApplyChanges();
Window.ClientSizeChanged += Window_ClientSizeChanged;
Client = client;
EndPoint = endPoint;
LastPhysicsUpdate = DateTime.MinValue;
@ -80,6 +82,16 @@ namespace TrueCraft.Client
Components.Add(mouseComponent);
}
void Window_ClientSizeChanged(object sender, EventArgs e)
{
if (GraphicsDevice.Viewport.Width < 640 || GraphicsDevice.Viewport.Height < 480)
ScaleFactor = 0.5f;
else if (GraphicsDevice.Viewport.Width < 978 || GraphicsDevice.Viewport.Height < 720)
ScaleFactor = 1.0f;
else
ScaleFactor = 1.5f;
}
protected override void Initialize()
{
Modules = new List<IGameplayModule>();
@ -92,6 +104,7 @@ namespace TrueCraft.Client
Modules.Add(ChunkModule);
Modules.Add(new HighlightModule(this));
Modules.Add(new PlayerControlModule(this));
Modules.Add(new HUDModule(this));
Modules.Add(DebugInfoModule);
Client.PropertyChanged += HandleClientPropertyChanged;
@ -111,6 +124,8 @@ namespace TrueCraft.Client
Window.ClientSizeChanged += (sender, e) => CreateRenderTarget();
CreateRenderTarget();
SpriteBatch = new SpriteBatch(GraphicsDevice);
Window_ClientSizeChanged(null, null);
}
private void CreateRenderTarget()
@ -235,9 +250,10 @@ namespace TrueCraft.Client
var bobbing = Bobbing * 1.5;
var xbob = Math.Cos(bobbing + Math.PI / 2) * bobbingMultiplier;
var ybob = Math.Sin(Math.PI / 2 - (2 * bobbing)) * bobbingMultiplier;
Camera.Position = new TrueCraft.API.Vector3(
Client.Position.X + xbob - (Client.Size.Width / 2),
Client.Position.Y + (Client.Size.Height - 0.5) + ybob,
Client.Position.Y + Client.Size.Height + ybob,
Client.Position.Z - (Client.Size.Depth / 2));
Camera.Pitch = Client.Pitch;

View File

@ -15,122 +15,42 @@ namespace TrueCraft.Client
// Thanks to http://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game
public static Tuple<Coordinates3D, BlockFace> Cast(ReadOnlyWorld world,
Ray ray, IBlockRepository repository, double max)
Ray ray, IBlockRepository repository, int max)
{
var origin = ray.Position.Floor();
var direction = ray.Direction;
var step = new Vector3(SigNum(ray.Direction.X), SigNum(ray.Direction.Y), SigNum(ray.Direction.Z));
var tMax = new Vector3(
IntBound(origin.X, direction.X),
IntBound(origin.Y, direction.Y),
IntBound(origin.Z, direction.Z));
var tDelta = new Vector3(
step.X / direction.X,
step.Y / direction.Y,
step.Z / direction.Z);
BlockFace face = BlockFace.PositiveY;
// TODO: There are more efficient ways of doing this, fwiw
if (ray.Direction == Vector3.Zero)
double min = max * 2;
var pick = -Coordinates3D.One;
for (int x = -max; x <= max; x++)
{
for (int y = -max; y <= max; y++)
{
for (int z = -max; z <= max; z++)
{
var coords = (Coordinates3D)(new Vector3(x, y, z) + ray.Position).Round();
if (!world.IsValidPosition(coords))
continue;
var id = world.GetBlockID(coords);
if (id != 0)
{
var provider = repository.GetBlockProvider(id);
var box = provider.BoundingBox;
if (box != null)
{
var distance = ray.Intersects(box.Value.OffsetBy(coords));
if (distance != null && distance.Value < min)
{
min = distance.Value;
pick = coords;
}
}
}
}
}
}
if (pick == -Coordinates3D.One)
return null;
max /= Math.Sqrt(ray.Direction.X * ray.Direction.X
+ ray.Direction.Y * ray.Direction.Y
+ ray.Direction.Z * ray.Direction.Z);
while (world.IsValidPosition((Coordinates3D)origin))
{
var provider = repository.GetBlockProvider(world.GetBlockID((Coordinates3D)origin));
var _box = provider.BoundingBox;
if (_box != null)
{
var box = _box.Value.OffsetBy((Coordinates3D)origin);
if (ray.Intersects(box) != null)
return new Tuple<Coordinates3D, BlockFace>((Coordinates3D)origin, face);
}
if (tMax.X < tMax.Y)
{
if (tMax.X < tMax.Z)
{
if (tMax.X > max)
return null;
// Update which cube we are now in.
origin.X += step.X;
// Adjust tMaxX to the next X-oriented boundary crossing.
tMax.X += tDelta.X;
// Record the normal vector of the cube face we entered.
if (step.X < 0)
face = BlockFace.PositiveX;
else
face = BlockFace.NegativeX;
}
else
{
if (tMax.Z > max)
return null;
origin.Z += step.Z;
tMax.Z += tDelta.Z;
if (step.Z < 0)
face = BlockFace.PositiveZ;
else
face = BlockFace.NegativeZ;
}
}
else
{
if (tMax.Y < tMax.Z)
{
if (tMax.Y > max)
return null;
origin.Y += step.Y;
tMax.Y += tDelta.Y;
if (step.Y < 0)
face = BlockFace.PositiveY;
else
face = BlockFace.NegativeY;
}
else
{
// Identical to the second case, repeated for simplicity in
// the conditionals.
if (tMax.Z > max)
break;
origin.Z += step.Z;
tMax.Z += tDelta.Z;
if (step.Z < 0)
face = BlockFace.PositiveZ;
else
face = BlockFace.NegativeZ;
}
}
}
return null;
}
private static double IntBound(double s, double ds)
{
// Find the smallest positive t such that s+t*ds is an integer.
if (ds < 0)
{
return IntBound(-s, -ds);
}
else
{
s = Mod(s, 1);
// problem is now s+t*ds = 1
return (1 - s) / ds;
}
}
private static int SigNum(double x)
{
return x > 0 ? 1 : x < 0 ? -1 : 0;
}
private static double Mod(double value, double modulus)
{
return (value % modulus + modulus) % modulus;
return new Tuple<Coordinates3D, BlockFace>(pick, BlockFace.PositiveY);
}
}
}