// XnaSphere5 - 再帰分割slerp+index
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace XnaSphere
{
class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch sprite;
SpriteFont font;
BasicEffect effect;
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
List<VertexPositionColor> vtxList = new List<VertexPositionColor>();
List<short> idxList = new List<short>();
const int level = 4;
// fps
int sec;
int draw = 0;
int fps = 0;
// カメラ
int camLat = 0;
int camLong = 90;
float camDist = 5;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void LoadContent()
{
font = Content.Load<SpriteFont>("SpriteFont1");
sprite = new SpriteBatch(GraphicsDevice);
effect = new BasicEffect(GraphicsDevice);
//effect.EnableDefaultLighting();
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 1, 100);
// 頂点バッファ
Vector3[] pos =
{ Vector3.Up, Vector3.Right, Vector3.Backward, Vector3.Left, Vector3.Forward, Vector3.Down };
int[] idx =
{ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 5, 2, 1, 5, 3, 2, 5, 4, 3, 5, 1, 4 };
for (int n = 0; n < idx.Length; n += 3)
{
GenerateTriangle(level, pos[idx[n]], pos[idx[n + 1]], pos[idx[n + 2]]);
}
vertexBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionColor), vtxList.Count, BufferUsage.WriteOnly);
vertexBuffer.SetData(vtxList.ToArray());
indexBuffer = new IndexBuffer(GraphicsDevice,
typeof(short), idxList.Count, BufferUsage.WriteOnly);
indexBuffer.SetData(idxList.ToArray());
base.LoadContent();
}
void GenerateTriangle(int lv, Vector3 p1, Vector3 p2, Vector3 p3)
{
if (lv-- <= 0)
{
AddVertex(p1);
AddVertex(p2);
AddVertex(p3);
return;
}
Vector3 p4 = QV(Quaternion.Slerp(new Quaternion(p1, 0), new Quaternion(p2, 0), 0.5f));
Vector3 p5 = QV(Quaternion.Slerp(new Quaternion(p2, 0), new Quaternion(p3, 0), 0.5f));
Vector3 p6 = QV(Quaternion.Slerp(new Quaternion(p3, 0), new Quaternion(p1, 0), 0.5f));
GenerateTriangle(lv, p1, p4, p6);
GenerateTriangle(lv, p2, p5, p4);
GenerateTriangle(lv, p3, p6, p5);
GenerateTriangle(lv, p4, p5, p6);
}
void AddVertex(Vector3 pos)
{
for (int n = 0; n < vtxList.Count; n++)
{
if ((pos - vtxList[n].Position).Length() < 0.00001f)
{
idxList.Add((short)n);
return;
}
}
vtxList.Add(new VertexPositionColor(pos, (vtxList.Count & 1) == 0 ? Color.Red : Color.Yellow));
idxList.Add((short)(vtxList.Count - 1));
}
Vector3 QV(Quaternion q)
{
return new Vector3(q.X, q.Y, q.Z);
}
protected override void Update(GameTime gameTime)
{
KeyboardState kState = Keyboard.GetState();
if (kState.IsKeyDown(Keys.Escape)) Exit();
if (kState.IsKeyDown(Keys.Up)) camLat = Math.Min(camLat + 1, 89);
if (kState.IsKeyDown(Keys.Down)) camLat = Math.Max(camLat - 1, -89);
if (kState.IsKeyDown(Keys.Left)) camLong = (camLong + 1) % 360;
if (kState.IsKeyDown(Keys.Right)) camLong = (camLong + 359) % 360;
if (kState.IsKeyDown(Keys.PageUp)) camDist -= 0.1f;
if (kState.IsKeyDown(Keys.PageDown)) camDist += 0.1f;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
// カメラ位置
float rad = MathHelper.ToRadians(camLat);
float y = (float)Math.Sin(rad) * camDist;
float r = (float)Math.Cos(rad) * camDist;
rad = MathHelper.ToRadians(camLong);
float x = (float)Math.Cos(rad) * r;
float z = (float)Math.Sin(rad) * r;
effect.View = Matrix.CreateLookAt(new Vector3(x, y, z), Vector3.Zero, Vector3.Up);
GraphicsDevice.SetVertexBuffer(vertexBuffer);
GraphicsDevice.Indices = indexBuffer;
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
GraphicsDevice.RasterizerState =
new RasterizerState { FillMode = FillMode.WireFrame };
effect.World = Matrix.CreateTranslation(-1, 0, 0);
effect.VertexColorEnabled = false;
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
0, 0, vertexBuffer.VertexCount, 0, indexBuffer.IndexCount / 3);
GraphicsDevice.RasterizerState =
new RasterizerState { FillMode = FillMode.Solid };
effect.World = Matrix.CreateTranslation(1, 0, 0);
effect.VertexColorEnabled = true;
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
0, 0, vertexBuffer.VertexCount, 0, indexBuffer.IndexCount / 3);
}
// fps
draw++;
if (gameTime.TotalGameTime.Seconds != sec)
{
fps = draw;
draw = 0;
sec = gameTime.TotalGameTime.Seconds;
}
sprite.Begin();
string text = String.Format(
"fps={0} lat={1} long={2} dist={3:f1}", fps, camLat, camLong, camDist);
sprite.DrawString(font, text, new Vector2(0, 0), Color.White);
text = String.Format("lv={0} vtx={1} idx={2}",
level, vertexBuffer.VertexCount, indexBuffer.IndexCount);
sprite.DrawString(font, text, new Vector2(0, 20), Color.White);
sprite.End();
base.Draw(gameTime);
}
}
}