Related
I am using SharpGL.
I am trying to draw three overlapped spheres with three different colors like the following -
However, I am getting output like the following -
In other words, the gl.Color() is having no effect on the drawn object.
How can I fix this?
Source Code:
public static class OpenGLhelper
{
public static void Init(OpenGL gl)
{
float[] mat_specular = { 1.0f, 1.0f, 1.0f, 1.0f };
float[] mat_shininess = { 50.0f };
float[] light_position = { 0.5f, 0.5f, 0.750f, 0.0f };
gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.ShadeModel(OpenGL.GL_SMOOTH);
gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, mat_specular);
gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, mat_shininess);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, light_position);
gl.Enable(OpenGL.GL_LIGHTING);
gl.Enable(OpenGL.GL_LIGHT0);
gl.Enable(OpenGL.GL_DEPTH_TEST);
//// Cull back faces.
gl.Enable(OpenGL.GL_CULL_FACE);
gl.CullFace(OpenGL.GL_BACK);
gl.FrontFace(OpenGL.GL_CW);
}
private static void drawSphere(OpenGL gl, Point3d c, Rgb color, double r, int n)
{
int i, j;
double theta1, theta2, theta3;
Point3d e = new Point3d();
Point3d p = new Point3d();
if (c == null)
{
c = new Point3d(0, 0, 0);
}
double twoPi = Math.PI * 2;
double piD2 = Math.PI / 2;
if (r < 0)
r = -r;
if (n < 0)
n = -n;
if (n < 4 || r <= 0)
{
gl.Begin(OpenGL.GL_POINTS);
gl.Color(color.Red, color.Green, color.Blue);
gl.Vertex(c.X, c.Y, c.Z);
gl.End();
return;
}
for (j = 0; j < n / 2; j++)
{
theta1 = j * twoPi / n - piD2;
theta2 = (j + 1) * twoPi / n - piD2;
gl.Begin(OpenGL.GL_QUAD_STRIP);
for (i = 0; i <= n; i++)
{
theta3 = i * twoPi / n;
e.X = Math.Cos(theta2) * Math.Cos(theta3);
e.Y = Math.Sin(theta2);
e.Z = Math.Cos(theta2) * Math.Sin(theta3);
p.X = c.X + r * e.X;
p.Y = c.Y + r * e.Y;
p.Z = c.Z + r * e.Z;
gl.Normal(e.X, e.Y, e.Z);
gl.TexCoord(i / (double)n, 2 * (j + 1) / (double)n);
gl.Color(color.Red, color.Green, color.Blue);
gl.Vertex(p.X, p.Y, p.Z);
e.X = Math.Cos(theta1) * Math.Cos(theta3);
e.Y = Math.Sin(theta1);
e.Z = Math.Cos(theta1) * Math.Sin(theta3);
p.X = c.X + r * e.X;
p.Y = c.Y + r * e.Y;
p.Z = c.Z + r * e.Z;
gl.Normal(e.X, e.Y, e.Z);
gl.TexCoord(i / (double)n, 2 * j / (double)n);
gl.Color(color.Red, color.Green, color.Blue);
gl.Vertex(p.X, p.Y, p.Z);
}
gl.End();
}
}
public static void Display(OpenGL gl)
{
gl.Clear(OpenGL . GL_COLOR_BUFFER_BIT | OpenGL . GL_DEPTH_BUFFER_BIT);
drawSphere(gl, new Point3d(0, 0, 0), new Rgb(1, 0, 0), 0.5, 20);
drawSphere(gl, new Point3d(0, 0.5, 0), new Rgb(0, 1, 0), 0.5, 20);
drawSphere(gl, new Point3d(0, -0.5, 0), new Rgb(0, 0, 1), 0.5, 20);
gl.Flush();
}
public static void Reshape(OpenGL gl, int width, int height)
{
}
}
public partial class SharpGLForm : Form
{
private float rotation = 0.0f;
public SharpGLForm()
{
InitializeComponent();
OpenGL gl = openGLControl1.OpenGL;
OpenGLhelper.Init(gl);
}
private void openGLControl_OpenGLDraw(object sender, RenderEventArgs e)
{
OpenGL gl = openGLControl1.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity();
gl.Translate(0.0f, 0.0f, -4.0f); // Move into the screen
gl.Rotate(rotation, 0.0f, 1.0f, 0.0f);
OpenGLhelper.Display(gl);
rotation += 3.0f;
}
}
When lighting (GL_LIGHTING) is enabled, the render color is defined by the material parameters (glMaterial). If you want to define the color with glColor, you must enable GL_COLOR_MATERIAL
and to set the color material parameters (glColorMaterial):
gl.Enable(OpenGL.GL_LIGHTING);
gl.Enable(OpenGL.GL_COLOR_MATERIAL);
gl.ColorMaterial(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT_AND_DIFFUSE);
Alternatively set the material color with GL.Material. e.g.:
float[] ambient_diffuse_color = { 1.0f, 0.0f, 0.0f, 1.0f }; // RED
GL.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT_AND_DIFFUSE,
ambient_diffuse_color);
I am using SharpGL library.
The following source code draws several hundred solid spheres at various locations -
// Initialization routine.
void setup()
{
gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.Enable(OpenGL.GL_DEPTH_TEST); // Enable depth testing.
// Turn on OpenGL lighting.
gl.Enable(OpenGL.GL_LIGHTING);
// Light property vectors.
float []lightAmb = { 0.0f, 0.0f, 0.0f, 1.0f };
float[]lightDifAndSpec0 = { 1.0f, 1.0f, 1.0f, 1.0f };
float[]lightDifAndSpec1 = { 0.0f, 1.0f, 0.0f, 1.0f };
float[]globAmb = { 0.2f, 0.2f, 0.2f, 1.0f };
// Light0 properties.
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightAmb);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightDifAndSpec0);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightDifAndSpec0);
// Light1 properties.
gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_AMBIENT, lightAmb);
gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, lightDifAndSpec1);
gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_SPECULAR, lightDifAndSpec1);
gl.Enable(OpenGL.GL_LIGHT0); // Enable particular light source.
gl.Enable(OpenGL.GL_LIGHT1); // Enable particular light source.
gl.LightModel(OpenGL.GL_LIGHT_MODEL_AMBIENT, globAmb); // Global ambient light.
gl.LightModel(OpenGL.GL_LIGHT_MODEL_LOCAL_VIEWER, OpenGL.GL_TRUE); // Enable local viewpoint
// Cull back faces.
gl.Enable(OpenGL.GL_CULL_FACE);
gl.CullFace(OpenGL.GL_BACK);
}
//http://www.java2s.com/example/java/javax.media.opengl/opengl-method-to-draw-a-sphere-in-opengl.html
void drawSphere(Point3d c, double r, int n)
{
int i, j;
double theta1, theta2, theta3;
Point3d e = new Point3d();
Point3d p = new Point3d();
if (c == null)
{
c = new Point3d(0, 0, 0);
}//from w ww .j ava2 s . c o m
double twoPi = Math.PI * 2;
double piD2 = Math.PI / 2;
if (r < 0)
r = -r;
if (n < 0)
n = -n;
if (n < 4 || r <= 0)
{
gl.Begin(OpenGL.GL_POINTS);
gl.Vertex(c.X, c.Y, c.Z);
gl.End();
return;
}
for (j = 0; j < n / 2; j++)
{
theta1 = j * twoPi / n - piD2;
theta2 = (j + 1) * twoPi / n - piD2;
gl.Begin(OpenGL.GL_QUAD_STRIP);
// gl.glBegin(GL.GL_TRIANGLE_STRIP);
for (i = 0; i <= n; i++)
{
theta3 = i * twoPi / n;
e.X = Math.Cos(theta2) * Math.Cos(theta3);
e.Y = Math.Sin(theta2);
e.Z = Math.Cos(theta2) * Math.Sin(theta3);
p.X = c.X + r * e.X;
p.Y = c.Y + r * e.Y;
p.Z = c.Z + r * e.Z;
gl.Normal(e.X, e.Y, e.Z);
gl.TexCoord(i / (double)n, 2 * (j + 1) / (double)n);
gl.Vertex(p.X, p.Y, p.Z);
e.X = Math.Cos(theta1) * Math.Cos(theta3);
e.Y = Math.Sin(theta1);
e.Z = Math.Cos(theta1) * Math.Sin(theta3);
p.X = c.X + r * e.X;
p.Y = c.Y + r * e.Y;
p.Z = c.Z + r * e.Z;
gl.Normal(e.X, e.Y, e.Z);
gl.TexCoord(i / (double)n, 2 * j / (double)n);
gl.Vertex(p.X, p.Y, p.Z);
}
gl.End();
}
}
/// <summary>
/// Handles the OpenGLDraw event of the openGLControl control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RenderEventArgs"/> instance containing the event data.</param>
private void openGLControl_OpenGLDraw(object sender, RenderEventArgs e)
{
// Get the OpenGL object.
gl = openGLControl.OpenGL;
// Clear the color and depth buffer.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
// Load the identity matrix.
gl.LoadIdentity();
// Rotate around the Y axis.
////gl.Rotate(rotation, 0.0f, 1.0f, 0.0f);
foreach (var item in atomsList)
{
// Point3d coord = item.Coordinate;
drawSphere(item.Coordinate, 0.75f, 20);
}
// Nudge the rotation.
rotation += 30.0f;
}
My aim is to draw something like this --
However, my output is something like the following --
Probably, the hidden surface removal is not working as expected.
How can I obtain my expected output?
The winding order of your primitives appears to be clockwise. The default winding order is counterclockwise (see Face Culling). Change the winding order that defines front-facing polygons:
gl.Enable(OpenGL.GL_CULL_FACE);
gl.CullFace(OpenGL.GL_BACK);
gl.FrontFace(OpenGL.GL_CW);
So my code I am using is below (C# in Unity)
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class PolygonGenerator : MonoBehaviour
{
public List<Vector3> newVertices = new List<Vector3>();
public List<int> newTriangles = new List<int>();
public List<Vector2> newUV = new List<Vector2>();
private Mesh mesh;
private float tUnitY = 0.33333f; //These are just objects I've given IDs for textures
private float tUnitX = 0.166667f;
private Vector2 tDirtBL = new Vector2(0, 0);
private Vector2 tDirtB = new Vector2(1, 0);
private Vector2 tDirtBR = new Vector2(2, 0);
private Vector2 tDirtL = new Vector2(0, 1);
private Vector2 tDirt = new Vector2(1, 1);
private Vector2 tDirtR = new Vector2(2, 1);
private Vector2 tDirtUL = new Vector2(0, 2);
private Vector2 tDirtU = new Vector2(1, 2);
private Vector2 tDirtUR = new Vector2(2, 2);
private Vector2 tStone = new Vector2(4, 1);
private int squareCount;
public byte[,] blocks;
public List<Vector3> colVertices = new List<Vector3>();
public List<int> colTriangles = new List<int>();
private int colCount;
private EdgeCollider2D col;
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
col = GetComponent<EdgeCollider2D>();
float x = transform.position.x;
float y = transform.position.y;
float z = transform.position.z;
GenTerrain();
BuildMesh();
MeshUpdate();
}
void GenSquare(int x, int y, Vector2 texture) //This creates the blocks I can apply textures to
{
newVertices.Add(new Vector3(x, y, 0));
newVertices.Add(new Vector3(x + 1, y, 0));
newVertices.Add(new Vector3(x + 1, y - 1, 0));
newVertices.Add(new Vector3(x, y - 1, 0));
newTriangles.Add(squareCount * 4);
newTriangles.Add((squareCount * 4) + 1);
newTriangles.Add((squareCount * 4) + 3);
newTriangles.Add((squareCount * 4) + 1);
newTriangles.Add((squareCount * 4) + 2);
newTriangles.Add((squareCount * 4) + 3);
newUV.Add(new Vector2(tUnitX * texture.x, tUnitY * texture.y + tUnitY));
newUV.Add(new Vector2(tUnitX * texture.x + tUnitX, tUnitY * texture.y + tUnitY));
newUV.Add(new Vector2(tUnitX * texture.x + tUnitX, tUnitY * texture.y));
newUV.Add(new Vector2(tUnitX * texture.x, tUnitY * texture.y));
squareCount++;
}
void MeshUpdate() //This merely updates the mesh
{
mesh.Clear();
mesh.vertices = newVertices.ToArray();
mesh.triangles = newTriangles.ToArray();
mesh.uv = newUV.ToArray();
mesh.RecalculateNormals();
squareCount = 0;
newVertices.Clear();
newTriangles.Clear();
newUV.Clear();
Mesh newMesh = new Mesh();
newMesh.vertices = colVertices.ToArray();
newMesh.triangles = colTriangles.ToArray();
col.sharedMaterial = newMesh;
colVertices.Clear();
colTriangles.Clear();
colCount = 0;
}
void GenTerrain() //Generates terrain based on parameters I supply and I can add in
{ //PerlinNoise to create variable terrain instead of flat
blocks = new byte[512, 128];
for (int px = 0; px < blocks.GetLength(0); px++) //Also tells which blocks are which
{
int stone = Noise(px, 0, 80, 15, 1);
stone += Noise(px, 0, 50, 30, 1);
stone += Noise(px, 0, 10, 10, 1);
stone += 75;
print(stone);
int dirt = Noise(px, 0, 25f, 35, 1);
dirt += Noise(px, 100, 50, 30, 1);
dirt += 75;
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (py < stone)
{
blocks[px, py] = 1;
//The next three lines make dirt spots in random places
if (Noise(px, py, 12, 16, 1) > 10)
{
blocks[px, py] = 2;
}
//The next three lines remove dirt and rock to make caves in certain places
if (Noise(px, py * 2, 16, 14, 1) > 10)
{ //Caves
blocks[px, py] = 0;
}
}
else if (py < dirt)
{
blocks[px, py] = 2;
}
}
}
}
void BuildMesh() //Mesh Creation
{
for (int px = 0; px < blocks.GetLength(0); px++)
{
for (int py = 0; py < blocks.GetLength(1); py++)
{
if (blocks[px, py] != 0)
{
GenCollider(px, py);
}
if (blocks[px, py] == 1)
{
GenSquare(px, py, tStone);
}
else if (blocks[px, py] == 2)
{
GenSquare(px, py, tDirtU);
}
}
}
}
void GenCollider(int x, int y)
{
//Top
if (Block(x, y + 1) == 0)
{
colVertices.Add(new Vector3(x, y, 1));
colVertices.Add(new Vector3(x + 1, y, 1));
colVertices.Add(new Vector3(x + 1, y, 0));
colVertices.Add(new Vector3(x, y, 0));
ColliderTriangles();
colCount++;
}
//Bottom
if (Block(x, y - 1) == 0)
{
colVertices.Add(new Vector3(x, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y - 1, 1));
colVertices.Add(new Vector3(x, y - 1, 1));
ColliderTriangles();
colCount++;
}
//Left
if (Block(x - 1, y) == 0)
{
colVertices.Add(new Vector3(x, y - 1, 1));
colVertices.Add(new Vector3(x, y, 1));
colVertices.Add(new Vector3(x, y, 0));
colVertices.Add(new Vector3(x, y - 1, 0));
ColliderTriangles();
colCount++;
}
//Right
if (Block(x + 1, y) == 0)
{
colVertices.Add(new Vector3(x + 1, y, 1));
colVertices.Add(new Vector3(x + 1, y - 1, 1));
colVertices.Add(new Vector3(x + 1, y - 1, 0));
colVertices.Add(new Vector3(x + 1, y, 0));
ColliderTriangles();
colCount++;
}
}
void ColliderTriangles()
{
colTriangles.Add(colCount * 4);
colTriangles.Add((colCount * 4) + 1);
colTriangles.Add((colCount * 4) + 3);
colTriangles.Add((colCount * 4) + 1);
colTriangles.Add((colCount * 4) + 2);
colTriangles.Add((colCount * 4) + 3);
}
byte Block(int x, int y)
{
if (x == -1 || x == blocks.GetLength(0) || y == -1 || y == blocks.GetLength(1))
{
return (byte)1;
}
return blocks[x, y];
}
int Noise(int x, int y, float scale, float mag, float exp)
{
return (int)(Mathf.Pow((Mathf.PerlinNoise(x / scale, y / scale) * mag), (exp)));
}
}
The code has been modified from its original in order to accommodate BoxCollider2D but is giving me an error. This script is used alongside a Mesh Filter, Mesh Collider (Trying to replace this with a box collider for a 2D game), and a Mesh Renderer on an Empty that was created in Unity.
Is there any viable solution I can use, instead of generating a mesh collider alongside the tiles, in order to make a box collider generate with it? If any further details are needed just ask.
I'm building a game using xna and I've created a terrain using VertexElementNormalTexture. I understand I can add position, the normal and texture coordinates to the vertex element but is there anyway to add which texture I want to use. I'm testing it on my Lumia phone so it's being built without the use of custom shaders because they're unsupported by the phone making most of the tutorials on the web out of date. I believe the basiceffects only support one texture per effect so if I make a number of effects how do I get the vertex to know which effect to use? the grasseffect, watereffect, etc.
The code I've got so far is below.
Thank you.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using Microsoft.Devices.Sensors;
namespace WindowsPhoneGame2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
#region // Variables
public struct VertexPositionColorNormal
{
public Vector3 Position;
public Color Color;
public Vector3 Normal;
public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(sizeof(float) * 3, VertexElementFormat.Color, VertexElementUsage.Color, 0),
new VertexElement(sizeof(float) * 3 + 4, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)
);
}
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
RasterizerState rasterizerState;
// terrain
BasicEffect grassEffect;
Texture2D grassTexture;
BasicEffect waterEffect;
Texture2D waterTexture;
VertexDeclaration vertexDeclaration;
VertexPositionNormalTexture[] vertices;
short[] indices;
VertexBuffer myVertexBuffer;
IndexBuffer myIndexBuffer;
private float[,] heightData;
private int terrainWidth;
private int terrainLength;
int nScale = 16;
Vector3 terrainPosition;
Texture2D heightMap;
Matrix viewMatrix;
Matrix projectionMatrix;
Matrix worldMatrix;
// aircraft model
private Model aircraftModel;
private Vector3 aircraftPosition;
Matrix aircraftMatrix;
// shell model
private Model shellModel;
private Vector3 bulletPosition;
Matrix bulletMatrix;
bool blBulletFiring;
private Vector3 torpedoPosition;
Matrix torpedoMatrix;
bool blTorpedoFiring;
float nTorpedoScale;
// display
private SpriteFont font;
float nHeightData;
int nBullets = 0;
int nTorpedoes = 0;
// tilt gesture
private float rollAngle;
private float pitchAngle;
private float yawAngle;
Accelerometer accelerometer;
private const int _num = 5;
private double[] _y = new double[_num];
private int _index = 0;
private const int _num2 = 5;
private double[] _y2 = new double[_num2];
private int _index2 = 0;
Texture2D explosionTexture;
List<ParticleData> particleList = new List<ParticleData>();
public struct ParticleData
{
public float BirthTime;
public float MaxAge;
public Vector2 OrginalPosition;
public Vector2 Accelaration;
public Vector2 Direction;
public Vector2 Position;
public float Scaling;
public Color ModColor;
}
Random random = new Random();
#endregion
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Hold;
loadTextures();
loadAccelerometer();
loadCamera();
loadEffect();
loadTerrain();
}
protected override void UnloadContent()
{
}
private void loadTextures()
{
aircraftModel = Content.Load<Model>("Ship");
shellModel = Content.Load<Model>("Shell");
explosionTexture = Content.Load<Texture2D>("explosion");
heightMap = Content.Load<Texture2D>("heightmap");
grassTexture = Content.Load<Texture2D>("Grass");
waterTexture = Content.Load<Texture2D>("Water");
font = Content.Load<SpriteFont>("DisplayText");
}
private void loadAccelerometer()
{
rollAngle = 0;
if (accelerometer == null)
{
accelerometer = new Accelerometer();
accelerometer.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
accelerometer.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged);
}
accelerometer.Start();
}
private void loadCamera()
{
aircraftPosition = new Vector3(heightMap.Width * nScale * 2 / 2, 0, 400);
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.GraphicsDevice.Viewport.AspectRatio, 10.0f, 8000.0f);
worldMatrix = Matrix.CreateTranslation(0, 0, 0);
terrainPosition = new Vector3(0, 0, 0);
}
private void loadEffect()
{
//grass effect
grassEffect = new BasicEffect(GraphicsDevice);
//grassEffect.VertexColorEnabled = true;
grassEffect.LightingEnabled = true; // Turn on the lighting subsystem.
grassEffect.DirectionalLight0.DiffuseColor = new Vector3(0.1f, 0.1f, 0.1f);
grassEffect.DirectionalLight0.Direction = new Vector3(0, 5, -10);
grassEffect.TextureEnabled = true;
grassEffect.Texture = grassTexture;
//water effect
waterEffect = new BasicEffect(GraphicsDevice);
waterEffect.LightingEnabled = true; // Turn on the lighting subsystem.
waterEffect.DirectionalLight0.DiffuseColor = new Vector3(0.1f, 0.1f, 0.1f);
waterEffect.DirectionalLight0.Direction = new Vector3(0, 5, -10);
waterEffect.TextureEnabled = true;
waterEffect.Texture = waterTexture;
}
private void loadTerrain()
{
LoadHeightData();
SetUpVertices();
SetUpIndices();
CalculateNormals();
CopyToBuffers();
}
private void LoadHeightData()
{
terrainWidth = heightMap.Width;
terrainLength = heightMap.Height;
Color[] heightMapColors = new Color[terrainWidth * terrainLength];
heightMap.GetData(heightMapColors);
heightData = new float[terrainWidth, terrainLength];
for (int x = 0; x < terrainWidth; x++)
for (int y = 0; y < terrainLength; y++)
heightData[x, y] = heightMapColors[x + y * terrainWidth].R / 5.0f;
}
private void SetUpVertices()
{
vertices = new VertexPositionNormalTexture[terrainWidth * terrainLength];
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainLength; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x * nScale * 2, y * nScale, heightData[x, y] * nScale) + terrainPosition;
}
}
}
private void SetUpIndices()
{
indices = new short[(terrainWidth - 1) * (terrainLength - 1) * 6];
int counter = 0;
for (int y = 0; y < terrainLength - 1; y++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
short lowerLeft = (short)(x + y * terrainWidth);
short lowerRight = (short)((x + 1) + y * terrainWidth);
short topLeft = (short)(x + (y + 1) * terrainWidth);
short topRight = (short)((x + 1) + (y + 1) * terrainWidth);
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
}
private void CalculateNormals()
{
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal = new Vector3(0, 0, 0);
for (int i = 0; i < indices.Length / 3; i++)
{
int index1 = indices[i * 3];
int index2 = indices[i * 3 + 1];
int index3 = indices[i * 3 + 2];
Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
Vector3 normal = Vector3.Cross(side1, side2);
vertices[index1].Normal += normal;
vertices[index2].Normal += normal;
vertices[index3].Normal += normal;
}
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal.Normalize();
}
private void CopyToBuffers()
{
myVertexBuffer = new VertexBuffer(graphics.GraphicsDevice, VertexPositionNormalTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
myVertexBuffer.SetData(vertices);
myIndexBuffer = new IndexBuffer(graphics.GraphicsDevice, typeof(short), indices.Length, BufferUsage.WriteOnly);
myIndexBuffer.SetData(indices);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Adjust camera position
viewMatrix = Matrix.CreateLookAt(new Vector3(aircraftPosition.X, aircraftPosition.Y - 10, aircraftPosition.Z + 5), new Vector3(aircraftPosition.X, aircraftPosition.Y + 5, aircraftPosition.Z ), new Vector3(0, 1, 0));
// Adjust aircraft position
aircraftMatrix = Matrix.CreateRotationY(rollAngle) * Matrix.CreateRotationX(pitchAngle) * Matrix.CreateRotationZ(yawAngle) * Matrix.CreateTranslation(aircraftPosition);
aircraftPosition.Y += 10.0f;
// Check against height data for collision
int x = (int)aircraftPosition.X / (nScale * 2);
int y = ((int)aircraftPosition.Y - (int)terrainPosition.Y) / nScale;
if(y < 128) nHeightData = heightData[(int)x, (int)y];
if (nHeightData > aircraftPosition.Z / nScale)
{
aircraftPosition = new Vector3(terrainWidth * nScale * 2 / 2, 0, 400);
terrainPosition = new Vector3(0, 0, 0);
}
// edit weapons fire positions
if (blBulletFiring)
{
bulletPosition.Y += 20;
fireBullet();
}
if (blTorpedoFiring)
{
if (torpedoPosition.Z > 5)
{
torpedoPosition.Z -= 6;
torpedoPosition.Y += 10;
nTorpedoScale = 0.8f;
}
else
{
torpedoPosition.Y += 16;
nTorpedoScale = 1.5f;
}
fireTorpedo();
}
// detect any gestures
while (TouchPanel.IsGestureAvailable)
{
GestureSample gs = TouchPanel.ReadGesture();
switch (gs.GestureType)
{
case GestureType.Tap:
blBulletFiring = true;
bulletPosition = new Vector3(aircraftPosition.X - 0.05f, aircraftPosition.Y, aircraftPosition.Z);
AddExplosion(new Vector2(gs.Position.X, gs.Position.Y), 12, 80.0f, 1500.0f, gameTime);
// AddExplosion(new Vector2(aircraftPosition.X * -1, aircraftPosition.Y * -1), 12, 80.0f, 1500.0f, gameTime);
break;
case GestureType.Hold:
blTorpedoFiring = true;
torpedoPosition = new Vector3(aircraftPosition.X, aircraftPosition.Y, aircraftPosition.Z);
break;
}
}
if (particleList.Count > 0)
UpdateParticles(gameTime);
base.Update(gameTime);
}
private void UpdateParticles(GameTime gameTime)
{
float now = (float)gameTime.TotalGameTime.TotalMilliseconds;
for (int i = particleList.Count - 1; i >= 0; i--)
{
ParticleData particle = particleList[i];
float timeAlive = now - particle.BirthTime;
if (timeAlive > particle.MaxAge)
{
particleList.RemoveAt(i);
}
else
{
float relAge = timeAlive / particle.MaxAge;
particle.Position = 0.5f * particle.Accelaration * relAge * relAge + particle.Direction * relAge + particle.OrginalPosition;
float invAge = 1.0f - relAge;
particle.ModColor = new Color(new Vector4(invAge, invAge, invAge, invAge));
Vector2 positionFromCenter = particle.Position - particle.OrginalPosition;
float distance = positionFromCenter.Length();
particle.Scaling = (50.0f + distance) / 200.0f;
particleList[i] = particle;
}
}
}
private void AddExplosion(Vector2 explosionPos, int numberOfParticles, float size, float maxAge, GameTime gameTime)
{
for (int i = 0; i < numberOfParticles; i++)
AddExplosionParticle(explosionPos, size, maxAge, gameTime);
}
private void AddExplosionParticle(Vector2 explosionPos, float explosionSize, float maxAge, GameTime gameTime)
{
ParticleData particle = new ParticleData();
particle.OrginalPosition = explosionPos;
particle.Position = particle.OrginalPosition;
particle.BirthTime = (float)gameTime.TotalGameTime.TotalMilliseconds;
particle.MaxAge = maxAge;
particle.Scaling = 0.25f;
particle.ModColor = Color.White;
float particleDistance = (float)random.NextDouble() * explosionSize;
Vector2 displacement = new Vector2(particleDistance, 0);
float angle = MathHelper.ToRadians(random.Next(360));
displacement = Vector2.Transform(displacement, Matrix.CreateRotationZ(angle));
particle.Direction = displacement * 2.0f;
particle.Accelaration = -particle.Direction;
particleList.Add(particle);
}
private void fireBullet()
{
nBullets++;
bulletMatrix = Matrix.CreateScale(0.1f) * Matrix.CreateRotationZ(MathHelper.ToRadians(90)) * Matrix.CreateTranslation(bulletPosition);
if (bulletPosition.Y > aircraftPosition.Y + 200)
{
blBulletFiring = false;
}
}
private void fireTorpedo()
{
nTorpedoes++;
torpedoMatrix = Matrix.CreateScale(nTorpedoScale) * Matrix.CreateRotationZ(MathHelper.ToRadians(90)) * Matrix.CreateTranslation(torpedoPosition);
if (torpedoPosition.Y > aircraftPosition.Y + 1000)
{
blTorpedoFiring = false;
}
}
public void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
_y[_index++] = e.SensorReading.Acceleration.Y;
if (_index >= _num) _index = 0;
double ysum = 0.0;
for (int i = 0; i < _num; i++)
ysum += _y[i];
double x = ysum / _num;
// Cap it at -0.5 and 0.5
if (x > 0.1)
{
if (rollAngle >= -0.9)
{
rollAngle -= 0.05f;
yawAngle += 0.02f;
}
if (aircraftPosition.X >= 5) aircraftPosition.X -= rollAngle * -5;
}
else if (x <= -0.1)
{
if (rollAngle <= 0.9)
{
rollAngle += 0.05f;
yawAngle -= 0.02f;
}
if (aircraftPosition.X <= terrainWidth * nScale * 2 - 6) aircraftPosition.X += rollAngle * 5;
}
else
{
if (rollAngle > 0.05)
{
rollAngle -= 0.05f;
yawAngle += 0.02f;
}
else if (rollAngle < -0.05)
{
rollAngle += 0.05f;
yawAngle -= 0.02f;
}
else
{
rollAngle = 0;
yawAngle = 0;
}
}
_y2[_index2++] = e.SensorReading.Acceleration.X;
if (_index2 >= _num2) _index2 = 0;
double ysum2 = 0.0;
for (int i = 0; i < _num2; i++)
ysum2 += _y2[i];
double x2 = ysum2 / _num2;
// Cap it at -0.5 and 0.5
if (x2 > -0.4)
{
if(pitchAngle >= -0.6) pitchAngle -= 0.025f;
if (aircraftPosition.Z > 6) aircraftPosition.Z -= pitchAngle * -10;
}
else if (x2 <= -0.5)
{
if (pitchAngle <= 0.6) pitchAngle += 0.025f;
if (aircraftPosition.Z <= 500) aircraftPosition.Z += pitchAngle * 10;
}
else
{
if (pitchAngle > 0.05) pitchAngle -= 0.05f;
else if (pitchAngle < -0.05) pitchAngle += 0.05f;
else pitchAngle = 0;
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.CornflowerBlue, 1.0f, 0);
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
rasterizerState = new RasterizerState();
rasterizerState.FillMode = FillMode.Solid;
rasterizerState.CullMode = CullMode.None;
GraphicsDevice.RasterizerState = rasterizerState;
Vector3 oldPosition = terrainPosition;
loadTerrain();
DrawTerrain();
terrainPosition.Y += nScale * terrainLength - nScale;
loadTerrain();
DrawTerrain();
terrainPosition.Y += nScale * terrainLength - nScale;
loadTerrain();
DrawTerrain();
terrainPosition.Y -= nScale * terrainLength - nScale;
if (aircraftPosition.Y >= terrainPosition.Y)
{ }
else
{
terrainPosition = oldPosition;
}
if (blBulletFiring) DrawBullet(shellModel, bulletMatrix, viewMatrix, projectionMatrix);
if(blTorpedoFiring) DrawTorpedo(shellModel, torpedoMatrix, viewMatrix, projectionMatrix);
DrawAircraft(aircraftModel, aircraftMatrix, viewMatrix, projectionMatrix);
DrawDisplay();
DrawExplosion();
base.Draw(gameTime);
}
private void DrawTerrain()
{
grassEffect.World = worldMatrix;
grassEffect.View = viewMatrix;
grassEffect.Projection = projectionMatrix;
foreach (EffectPass pass in grassEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.Indices = myIndexBuffer;
GraphicsDevice.SetVertexBuffer(myVertexBuffer);
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3);
}
}
private void DrawBullet(Model model, Matrix world, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.DirectionalLight0.DiffuseColor = new Vector3(0.9f, 0.0f, 0.0f);
effect.DirectionalLight0.Direction = new Vector3(0, 1, -1);
effect.World = world;
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
private void DrawTorpedo(Model model, Matrix world, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.DirectionalLight0.DiffuseColor = new Vector3(0.0f, 0.0f, 0.0f);
effect.DirectionalLight0.Direction = new Vector3(0, 1, -1);
effect.World = world;
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
private void DrawAircraft(Model model, Matrix world, Matrix view, Matrix projection)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
// effect.EnableDefaultLighting();
effect.LightingEnabled = true; // Turn on the lighting subsystem.
effect.DirectionalLight0.DiffuseColor = new Vector3(0.2f, 0.2f, 0.2f);
effect.DirectionalLight0.Direction = new Vector3(0, 5, -10);
//effect.DirectionalLight0.SpecularColor = new Vector3(0, 1, 0); // with green highlights
//effect.AmbientLightColor = new Vector3(0.2f, 0.2f, 0.2f); // Add some overall ambient light.
//effect.EmissiveColor = new Vector3(1, 0, 0); // Sets some strange emmissive lighting. This just looks weird.
effect.World = world;
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
private void DrawDisplay()
{
spriteBatch.Begin();
spriteBatch.DrawString(font, "Position: " + aircraftPosition.ToString(), new Vector2(10, 10), Color.Black);
spriteBatch.DrawString(font, "Ground Height: " + nHeightData.ToString(), new Vector2(10, 25), Color.Black);
spriteBatch.DrawString(font, "Bullet Position: " + nBullets.ToString(), new Vector2(10, 40), Color.Black);
spriteBatch.DrawString(font, "Torpedo Position: " + nTorpedoes.ToString(), new Vector2(10, 55), Color.Black);
spriteBatch.End();
}
private void DrawExplosion()
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
for (int i = 0; i < particleList.Count; i++)
{
ParticleData particle = particleList[i];
spriteBatch.Draw(explosionTexture, particle.Position, null, particle.ModColor, i, new Vector2(256, 256), particle.Scaling, SpriteEffects.None, 1);
}
spriteBatch.End();
}
}
}
Instead of using a BasicEffect, use a DualTextureEffect. I've never used it before because I don't develop for hone but this is supposed to be a very good tutorial. http://blogs.msdn.com/b/shawnhar/archive/2010/08/04/dualtextureeffect.aspx>
I think a little background will help before I get into my question. What I'm doing is creating my own small 2D physics library in xna, for fun nonetheless. This is also my first independent xna project, and my first time working with the 3D tools, so I may be doing things all wacky. Anyway, I'm currently making a triangle class in which the constructor takes three arbitrary points in the form of Vector2s. In the constructor I have to put these points in clockwise order (so they're not culled) and then find the texture coordinates they should correspond to (since I'm using VertexPositionTextures as my vertices). What I've got works, but it seems very long and complicated. I'm looking for any ways to shorten/simplify the code, which is this:
public PTriangle(Vector2 a, Vector2 b, Vector2 c)
: base()
{
//set up vertices
VertexPositionTexture[] vertices = new VertexPositionTexture[3];
//center vertices around origin
Vector2 center = new Vector2((a.X + b.X + c.X) / 3, (a.Y + b.Y + c.Y) / 3);
Vector2 newA = a - center;
Vector2 newB = b - center;
Vector2 newC = c - center;
//get angle of each vertex (clockwise from -x axis)
double angleA = MathHelper.Pi - Math.Atan((double)(newA.Y / newA.X));
double angleB = MathHelper.Pi - Math.Atan((double)(newB.Y / newB.X));
double angleC = MathHelper.Pi - Math.Atan((double)(newC.Y / newC.X));
if (newA.X < 0)
{
if (newA.Y < 0)
{
angleA += MathHelper.Pi;
}
else
{
angleA -= MathHelper.Pi;
}
}
if (newB.X < 0)
{
if (newB.Y < 0)
{
angleB += MathHelper.Pi;
}
else
{
angleB -= MathHelper.Pi;
}
}
if (newC.X < 0)
{
if (newC.Y < 0)
{
angleC += MathHelper.Pi;
}
else
{
angleC -= MathHelper.Pi;
}
}
//order vertices by angle
Vector2[] newVertices = new Vector2[3];
if (angleA < angleB && angleA < angleC)
{
newVertices[0] = newA;
if (angleB < angleC)
{
newVertices[1] = newB;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newB;
}
}
else if (angleB < angleA && angleB < angleC)
{
newVertices[0] = newB;
if (angleA < angleC)
{
newVertices[1] = newA;
newVertices[2] = newC;
}
else
{
newVertices[1] = newC;
newVertices[2] = newA;
}
}
else
{
newVertices[0] = newC;
if (angleA < angleB)
{
newVertices[1] = newA;
newVertices[2] = newB;
}
else
{
newVertices[1] = newB;
newVertices[2] = newA;
}
}
//set positions of vertices
vertices[0].Position = new Vector3(newVertices[0] + center, 0);
vertices[1].Position = new Vector3(newVertices[1] + center, 0);
vertices[2].Position = new Vector3(newVertices[2] + center, 0);
//get width and height of triangle
float minX = 0;
float minY = 0;
float maxX = 0;
float maxY = 0;
foreach (Vector2 vertex in newVertices)
{
if (vertex.X < minX)
{
minX = vertex.X;
}
else if (vertex.X > maxX)
{
maxX = vertex.X;
}
if (vertex.Y < minY)
{
minY = vertex.Y;
}
else if (vertex.Y > maxY)
{
maxY = vertex.Y;
}
}
float width = maxX - minX;
float height = maxY - minY;
//shift triangle so fits in quadrant IV, and set texture coordinates
for (int index = 0; index < newVertices.Length; ++index)
{
newVertices[index].X -= minX;
newVertices[index].Y -= minY;
vertices[index].TextureCoordinate = new Vector2(
newVertices[index].X / width,
1 - (newVertices[index].Y / height));
}
this.Vertices = vertices;
//set up indices
this.Indices = new short[] { 0, 1, 2 };
}
To put the 3 points in clockwise order, you can use counter-clockwise test (or left-turn test) to check the direction of the 3 points.
Pseudocode from Wikipedia
# Three points are a counter-clockwise turn if ccw > 0, clockwise if
# ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
# gives the signed area of the triangle formed by p1, p2 and p3.
function ccw(p1, p2, p3):
return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x)
If the 3 points are counter-clockwise, you can just swap the last 2 points to make the 3 points clockwise order.