#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace Nuclex.Graphics.Debugging {
/// Generates vertices for a solid (filled) arrow
internal static class SolidArrowVertexGenerator {
/// Number of vertices this generator produces
internal const int VertexCount = 144;
/// Outputs the vertices for a solid arrow into the specified array
/// Array to write the arrow vertices into
/// Index in the array to begin writing at
///
/// Location at which to draw the arrow (this will form the exact center of
/// the drawn arrow's base)
///
///
/// Direction the arrow is pointing into. The arrow's size is relative to
/// the length of this vector.
///
/// Color for the arrow
internal static void Generate(
VertexPositionColor[] vertices, int startIndex,
Vector3 origin, Vector3 direction, Color color
) {
// Build a vector pointing in an arbitrary direction that is perpendicular to
// the direction the arrow is pointing at. This can be done by simply juggling
// the vector elements around by one place.
Vector3 normalizedDirection = Vector3.Normalize(direction);
Vector3 up = VectorHelper.GetPerpendicularVector(normalizedDirection);
Vector3 right = Vector3.Cross(normalizedDirection, up);
float length = direction.Length();
up *= length;
right *= length;
// Generate supporting points for the cylinder shape of the arrow's base
Vector3[/*8*/] ring = new Vector3[8];
for(int segment = 0; segment < 8; ++segment) {
double angle = (double)segment / 4.0 * Math.PI;
ring[segment] = rotateAroundAxis(up * 0.1f, normalizedDirection, angle);
}
int index = startIndex;
Vector3 twoThird = origin + (direction * 0.667f);
Vector3 end = origin + direction;
for(int segment = 0; segment < 8; ++segment) {
int nextSegment = (segment + 1) % 8;
// Triangle on the cap of cylinder at the arrow's base
vertices[index].Position = origin;
vertices[index].Color = color;
vertices[index + 1].Position = origin + ring[nextSegment];
vertices[index + 1].Color = color;
vertices[index + 2].Position = origin + ring[segment];
vertices[index + 2].Color = color;
// Side wall segment of the cylinder at the arrow's base
vertices[index + 3].Position = twoThird + ring[segment];
vertices[index + 3].Color = color;
vertices[index + 4].Position = origin + ring[segment];
vertices[index + 4].Color = color;
vertices[index + 5].Position = origin + ring[nextSegment];
vertices[index + 5].Color = color;
vertices[index + 6].Position = twoThird + ring[segment];
vertices[index + 6].Color = color;
vertices[index + 7].Position = origin + ring[nextSegment];
vertices[index + 7].Color = color;
vertices[index + 8].Position = twoThird + ring[nextSegment];
vertices[index + 8].Color = color;
// On the arrowhead, alternate between close and far vertices to
// create four 'blades' making it easier to spot the arrow's direction than
// with a completely rounded cap.
bool isEvenSegment = (segment & 1) == 0;
float scale = isEvenSegment ? 3.0f : 1.0f;
float nextScale = isEvenSegment ? 1.0f : 3.0f;
// Segment on underside of arrowhead
vertices[index + 9].Position = twoThird + ring[segment] * scale;
vertices[index + 9].Color = color;
vertices[index + 10].Position = twoThird + ring[segment];
vertices[index + 10].Color = color;
vertices[index + 11].Position = twoThird + ring[nextSegment];
vertices[index + 11].Color = color;
vertices[index + 12].Position = twoThird + ring[segment] * scale;
vertices[index + 12].Color = color;
vertices[index + 13].Position = twoThird + ring[nextSegment];
vertices[index + 13].Color = color;
vertices[index + 14].Position = twoThird + ring[nextSegment] * nextScale;
vertices[index + 14].Color = color;
// Segment on the blade of the arrowhead
vertices[index + 15].Position = end;
vertices[index + 15].Color = color;
vertices[index + 16].Position = twoThird + ring[segment] * scale;
vertices[index + 16].Color = color;
vertices[index + 17].Position = twoThird + ring[nextSegment] * nextScale;
vertices[index + 17].Color = color;
index += 18;
} // for segment 1 .. 8
}
/// Rotates a point around an arbitrary axis defined by a vector
/// Point to be rotated
/// Axis to rotate the point around
/// Angle, in radians, to rotate the point by
/// The point rotated by the specified amount around the give axis
private static Vector3 rotateAroundAxis(
Vector3 pointToRotate, Vector3 axis, double angle
) {
// Precalculate some values to make the actual math more readable
float sinAngle = (float)Math.Sin(angle);
float cosAngle = (float)Math.Cos(angle);
float xx = axis.X * pointToRotate.X;
float xy = axis.X * pointToRotate.Y;
float xz = axis.X * pointToRotate.Z;
float yx = axis.Y * pointToRotate.X;
float yy = axis.Y * pointToRotate.Y;
float yz = axis.Y * pointToRotate.Z;
float zx = axis.Z * pointToRotate.X;
float zy = axis.Z * pointToRotate.Y;
float zz = axis.Z * pointToRotate.Z;
// Apply the rotation and return the resulting coordinates as a vector
return new Vector3(
axis.X * (xx + yy + zz) +
(pointToRotate.X * (axis.Y * axis.Y + axis.Z * axis.Z) -
axis.X * (yy + zz)) * cosAngle +
(-zy + yz) * sinAngle,
axis.Y * (xx + yy + zz) +
(pointToRotate.Y * (axis.X * axis.X + axis.Z * axis.Z) -
axis.Y * (xx + zz)) * cosAngle +
(zx - xz) * sinAngle,
axis.Z * (xx + yy + zz) +
(pointToRotate.Z * (axis.X * axis.X + axis.Y * axis.Y) -
axis.Z * (xx + yy)) * cosAngle +
(-yx + xy) * sinAngle
); // new Vector3
}
}
} // namespace Nuclex.Graphics.Debugging