// 球体テクスチャ
using System;
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;
Texture2D texture;
// 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");
texture = Content.Load<Texture2D>("earthmap1k");
sprite = new SpriteBatch(GraphicsDevice);
effect = new BasicEffect(GraphicsDevice);
//effect.VertexColorEnabled = true;
//effect.EnableDefaultLighting();
effect.TextureEnabled = true;
effect.Texture = texture;
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 1, 100);
// 頂点バッファ
int stackNum = 16; // 輪切りの数
VertexPositionNormalTexture[] vertices =
new VertexPositionNormalTexture[(stackNum + 1) * stackNum + 2];
int i = 0;
vertices[i++] = new VertexPositionNormalTexture(
new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector2(0.5f, 0));
for (int stack = 1; stack < stackNum; stack++)
{
float v = stack / (float)stackNum;
float rad = ((stackNum - 2 * stack) / (float)stackNum) * MathHelper.PiOver2;
float y = (float)Math.Sin(rad);
float r = (float)Math.Cos(rad);
int sliceNum = 4 * Math.Min(stack, stackNum - stack); // 放射頂点の数
for (int slice = 0; slice < sliceNum; slice++)
{
rad = (slice / (float)sliceNum) * MathHelper.TwoPi;
float x = (float)Math.Cos(rad) * r;
float z = (float)Math.Sin(rad) * r;
Vector3 normal = Vector3.Normalize(new Vector3(x, y, z));
vertices[i++] = new VertexPositionNormalTexture(normal, normal,
new Vector2(1 - slice / (float)sliceNum, v));
}
vertices[i] = vertices[i - sliceNum];
vertices[i].TextureCoordinate.X = 0;
i++;
}
vertices[i++] = new VertexPositionNormalTexture(
new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector2(0.5f, 1));
vertexBuffer = new VertexBuffer(GraphicsDevice,
typeof(VertexPositionNormalTexture), vertices.Length, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices);
// 索引バッファ
short[] indices = new short[stackNum * stackNum * 6];
i = 0;
int prevHead = 0; // 前の先頭頂点番号
int prevVtx = 1; // 前の頂点数
for (int stack = 0; stack < stackNum / 2; stack++)
{
int currHead = prevHead + prevVtx; // 現在の先頭頂点番号
int currVtx = 4 * (stack + 1) + 1; // 現在の頂点数
for (int quad = 0; quad < 4; quad++) // 4象限
{
int prevQuad = quad * stack; // 前の象限オフセット
int currQuad = quad * (stack + 1); // 現在の象限オフセット
for (int n = 0; ; n++)
{
indices[i++] = (short)(prevHead + prevQuad + n);
indices[i++] = (short)(currHead + currQuad + n);
indices[i++] = (short)(currHead + currQuad + n + 1);
if (stack <= n) break;
indices[i++] = (short)(prevHead + prevQuad + n);
indices[i++] = (short)(currHead + currQuad + n + 1);
indices[i++] = (short)(prevHead + prevQuad + n + 1);
}
}
prevHead = currHead;
prevVtx = currVtx;
}
for (int stack = stackNum / 2 - 1; 0 <= stack; stack--)
{
int currHead = prevHead + prevVtx;
int currVtx = 4 * stack + 1;
for (int quad = 0; quad < 4; quad++) // 4象限
{
int prevQuad = quad * (stack + 1);
int currQuad = quad * stack;
for (int n = 0; ; n++)
{
indices[i++] = (short)(currHead + currQuad + n);
indices[i++] = (short)(prevHead + prevQuad + n + 1);
indices[i++] = (short)(prevHead + prevQuad + n);
if (stack <= n) break;
indices[i++] = (short)(currHead + currQuad + n);
indices[i++] = (short)(currHead + currQuad + n + 1);
indices[i++] = (short)(prevHead + prevQuad + n + 1);
}
}
prevHead = currHead;
prevVtx = currVtx;
}
indexBuffer = new IndexBuffer(GraphicsDevice,
typeof(short), indices.Length, BufferUsage.WriteOnly);
indexBuffer.SetData(indices);
base.LoadContent();
}
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.RasterizerState =
// new RasterizerState { FillMode = FillMode.WireFrame };
GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
// カメラ位置
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)
{
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, 180 - camLong, camDist);
sprite.DrawString(font, text, new Vector2(0, 0), Color.White);
sprite.End();
base.Draw(gameTime);
}
}
}