I have a Shape class that has 4 points (Vector2). This way, the shape can be manipulated for whatever purpose. I'm wanting to be able to fill this shape with a color of choice, but am not sure where to begin. I found references to something called "VertexPositionColorTexture" but soon realized it was for 3D space only. My program is 2D only and I am at a loss.
If someone has suggestions to get to where I am trying, I will appreciate the help.
public class CShape
{
public Vector2 PointA { get; set; }
public Vector2 PointB { get; set; }
public Vector2 PointC { get; set; }
public Vector2 PointD { get; set; }
public Color Color { get; set; }
public float GetWidth()
{
Vector2 Left, Right;
Left = PointA;
if (PointB.X < Left.X) { Left = PointB; }
if (PointC.X < Left.X) { Left = PointC; }
if (PointD.X < Left.X) { Left = PointD; }
Right = PointA;
if (PointB.X > Right.X) { Right = PointB; }
if (PointC.X > Right.X) { Right = PointC; }
if (PointD.X > Right.X) { Right = PointD; }
return (Left.X - Right.X);
}
public float GetHeight()
{
Vector2 Top, Bottom;
Top = PointA;
if (PointB.Y < Top.Y) { Top = PointB; }
if (PointC.Y < Top.Y) { Top = PointC; }
if (PointD.Y < Top.Y) { Top = PointD; }
Bottom = PointA;
if (PointB.Y > Bottom.Y) { Bottom = PointB; }
if (PointC.Y > Bottom.Y) { Bottom = PointC; }
if (PointD.Y > Bottom.Y) { Bottom = PointD; }
return (Top.Y - Bottom.Y);
}
public CShape(Vector2 Location, int Width, int Height, Color Color)
{
PointA = Location;
PointB = new Vector2(PointA.X + Width, PointA.Y);
PointC = new Vector2(PointA.X, PointA.Y + Height);
PointD = new Vector2(PointA.X + Width, PointA.Y + Height);
this.Color = Color;
}
public void Move(Vector2 Velocity)
{
PointA = new Vector2(PointA.X + Velocity.X, PointA.Y + Velocity.Y);
PointB = new Vector2(PointB.X + Velocity.X, PointB.Y + Velocity.Y);
PointC = new Vector2(PointC.X + Velocity.X, PointC.Y + Velocity.Y);
PointD = new Vector2(PointD.X + Velocity.X, PointD.Y + Velocity.Y);
}
public void Draw(SpriteBatch sb)
{
sb.Begin();
SpriteBatchEx.DrawLine(sb, PointA, PointB, Color, 3);
SpriteBatchEx.DrawLine(sb, PointB, PointD, Color, 3);
SpriteBatchEx.DrawLine(sb, PointD, PointC, Color, 3);
SpriteBatchEx.DrawLine(sb, PointC, PointA, Color, 3);
sb.End();
}
}
A common approach is simply to draw in 3D but using a 2D projection, it's really simple once setup and there are benefits in doing so (TODO find out more on that topic, there are plenty tutorials out there) !
So here's a simple code that draws squares anywhere and you can tint them:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Game1
{
public class Game1 : Game
{
private BasicEffect _basicEffect;
private GraphicsDeviceManager _graphics;
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
_basicEffect = new BasicEffect(GraphicsDevice) {VertexColorEnabled = true};
base.Initialize();
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
private void DrawSquare(BasicEffect basicEffect, Vector2 position, Vector2 size, Color tint)
{
// square made out of 2 triangles
var colors = new[]
{
new VertexPositionColor(new Vector3(-0.5f, -0.5f, 0.0f), Color.White),
new VertexPositionColor(new Vector3(+0.5f, -0.5f, 0.0f), Color.White),
new VertexPositionColor(new Vector3(+0.5f, +0.5f, 0.0f), Color.White),
new VertexPositionColor(new Vector3(-0.5f, -0.5f, 0.0f), Color.White),
new VertexPositionColor(new Vector3(+0.5f, +0.5f, 0.0f), Color.White),
new VertexPositionColor(new Vector3(-0.5f, +0.5f, 0.0f), Color.White)
};
basicEffect.World = // NOTE: the correct order for matrices is SRT (scale, rotate, translate)
Matrix.CreateTranslation(0.5f, 0.5f, 0.0f)* // offset by half pixel to get pixel perfect rendering
Matrix.CreateScale(size.X, size.Y, 1.0f)* // set size
Matrix.CreateTranslation(position.X, position.Y, 0.0f)* // set position
Matrix.CreateOrthographicOffCenter // 2d projection
(
0.0f,
GraphicsDevice.Viewport.Width, // NOTE : here not an X-coordinate (i.e. width - 1)
GraphicsDevice.Viewport.Height,
0.0f,
0.0f,
1.0f
);
// tint it however you like
basicEffect.DiffuseColor = tint.ToVector3();
var passes = _basicEffect.CurrentTechnique.Passes;
foreach (var pass in passes)
{
pass.Apply();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, colors, 0, colors.Length/3);
}
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
DrawSquare(_basicEffect, new Vector2(10, 10), new Vector2(100, 100), Color.Red);
DrawSquare(_basicEffect, new Vector2(200, 200), new Vector2(50, 50), Color.Green);
base.Draw(gameTime);
}
}
}
You can still use the good'ol xblig sample :
http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives
Related
I am trying to implement fur in Unity with the Shells technique. The Fins technique is purposely left out because I want this to run on low end mobiles (mostly Android devices) and that requires OpenGL ES 3.0 and above while Shells technique only requires OpenGL ES 2.0.
There is an example on the Shell technique based on XNA and I made an attempt to port that into Unity but it failed to work. Here is the article with the XNA project.
The XNA shader:
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
texture FurTexture;
sampler FurSampler = sampler_state
{
Texture = (FurTexture);
MinFilter = Point;
MagFilter = Point;
MipFilter = Point;
AddressU = Wrap;
AddressV = Wrap;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
VertexShaderOutput FurVertexShader(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
float4 worldPosition = mul(float4(pos,1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.TexCoord = input.TexCoord;
return output;
}
float4 FurPixelShader(VertexShaderOutput input) : COLOR0
{
return tex2D(FurSampler, input.TexCoord);
}
technique Fur
{
pass Pass1
{
AlphaBlendEnable = true;
SrcBlend = SRCALPHA;
DestBlend = INVSRCALPHA;
CullMode = None;
VertexShader = compile vs_2_0 FurVertexShader();
PixelShader = compile ps_2_0 FurPixelShader();
}
}
The XNA C# script that controls the shader:
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
//simple camera for use in the game
Camera camera;
//texture containing fur data
Texture2D furTexture;
//effect for fur shaders
Effect furEffect;
//number of layers of fur
int nrOfLayers = 60;
//total length of the hair
float maxHairLength = 2.0f;
//density of hair
float density = 0.2f;
Texture2D furColorTexture;
//movement vectors
Vector3 gravity = new Vector3(0, -1.0f, 0);
Vector3 forceDirection = Vector3.Zero;
//final displacement for hair
Vector3 displacement;
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
camera = new Camera(this);
Components.Add(camera);
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
//generate the geometry
GenerateGeometry();
//load the effect
furEffect = Content.Load<Effect>("FurEffect");
//create the texture
furTexture = new Texture2D(GraphicsDevice,
256, 256, 1,
TextureUsage.None,
SurfaceFormat.Color);
//fill the texture
FillFurTexture(furTexture, density);
furColorTexture = Content.Load<Texture2D>("bigtiger");
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
forceDirection.X = (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds) * 0.5f;
displacement = gravity + forceDirection;
furEffect.Parameters["Displacement"].SetValue(displacement);
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
furEffect.Parameters["World"].SetValue(Matrix.CreateTranslation(0, -10, 0));
furEffect.Parameters["View"].SetValue(camera.View);
furEffect.Parameters["Projection"].SetValue(camera.Projection);
furEffect.Parameters["MaxHairLength"].SetValue(maxHairLength);
furEffect.Parameters["FurTexture"].SetValue(furTexture);
furEffect.Parameters["Texture"].SetValue(furColorTexture);
furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
furEffect.Parameters["CurrentLayer"].SetValue((float)i / nrOfLayers);
furEffect.CommitChanges();
furEffect.CurrentTechnique.Passes[0].Begin();
DrawGeometry();
furEffect.CurrentTechnique.Passes[0].End();
}
furEffect.End();
base.Draw(gameTime);
}
/// <summary>
/// This functions prepares a texture to be used for fur rendering
/// </summary>
/// <param name="furTexture">This will contain the final texture</param>
/// <param name="density">Hair density in [0..1] range </param>
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.Width;
int height = furTexture.Height;
int totalPixels = width * height;
//an array to hold our pixels
Color[] colors;
colors = new Color[totalPixels];
//random number generator
Random rand = new Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = Color.TransparentBlack;
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//compute the number of strands that stop at each layer
int strandsPerLayer = nrStrands / nrOfLayers;
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//compute max layer
int max_layer = i / strandsPerLayer;
//normalize into [0..1] range
float max_layer_n = (float)max_layer / (float)nrOfLayers;
//put color (which has an alpha value of 255, i.e. opaque)
//max_layer_n needs to be multiplied by 255 to achieve a color in [0..255] range
colors[x * width + y] = new Color((byte)(max_layer_n * 255), 0, 0, 255);
}
//set the pixels on the texture.
furTexture.SetData<Color>(colors);
}
VertexPositionNormalTexture[] vertices;
private void GenerateGeometry()
{
vertices = new VertexPositionNormalTexture[6];
vertices[0] = new VertexPositionNormalTexture(
new Vector3(-10, 0, 0),
-Vector3.UnitZ,
new Vector2(0, 0));
vertices[1] = new VertexPositionNormalTexture(
new Vector3(10, 20, 0),
-Vector3.UnitZ,
new Vector2(1, 1));
vertices[2] = new VertexPositionNormalTexture(
new Vector3(-10, 20, 0),
-Vector3.UnitZ,
new Vector2(0, 1));
vertices[3] = vertices[0];
vertices[4] = new VertexPositionNormalTexture(
new Vector3(10, 0, 0),
-Vector3.UnitZ,
new Vector2(1, 0));
vertices[5] = vertices[1];
}
private void DrawGeometry()
{
using (VertexDeclaration vdecl = new VertexDeclaration(
GraphicsDevice,
VertexPositionNormalTexture.VertexElements))
{
GraphicsDevice.VertexDeclaration = vdecl;
GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, 2);
}
}
}
I carefully ported the both the shader and the control script line by line to Unity.
The Ported Unity shader:
Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
//_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
Blend SrcAlpha One
Blend DstAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog
#include "UnityCG.cginc"
//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
//Test variable/delete after
float4 _TintColor;
//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
float4 worldPosition = mul(float4(pos, 1), World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.TexCoord = input.TexCoord;
return output;
}
float4 frag(VertexShaderOutput i) : COLOR0
{
return tex2D(_MainTex, i.TexCoord);
}
ENDCG
}
}
}
The ported Unity C# script that controls the shader:
public class Game1 : MonoBehaviour
{
public Material material;
public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);
//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;
//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);
void Start()
{
Initialize();
GenerateGeometry();
}
public void Update()
{
Draw();
}
void Initialize()
{
//Initialize the camera
camera = Camera.main;
//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
furTexture.filterMode = FilterMode.Point;
//fill the texture
FillFurTexture(furTexture, density);
/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/
if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
}
bool firstDraw = true;
protected void Draw()
{
camera.backgroundColor = CornflowerBlue();
Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;
material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear
material.SetFloat("MaxHairLength", maxHairLength);
if (firstDraw)
material.SetTexture("_MainTex", furTexture);
//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
material.SetFloat("CurrentLayer", (float)i / nrOfLayers);
DrawGeometry();
}
if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
if (firstDraw)
firstDraw = false;
}
void DrawGeometry()
{
Quaternion rotation = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rotation, material, 0, camera);
}
private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;
private void GenerateGeometry()
{
verticesPText = new VertexPositionNormalTexture[6];
verticesPText[0] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ(),
new Vector2(0, 0));
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ(),
new Vector2(1, 1));
verticesPText[2] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ(),
new Vector2(0, 1));
verticesPText[3] = verticesPText[0];
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ(),
new Vector2(1, 0));
verticesPText[5] = verticesPText[1];
verticesMesh = VertexPositionNormalTextureToUnityMesh(verticesPText);
}
Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];
int[] triangles = new int[vpnt.Length];
//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;
triangles[i] = i;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;
mesh.triangles = triangles;
return mesh;
}
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;
//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];
//random number generator
System.Random rand = new System.Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
colors[x * width + y] = Gold();
}
//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}
Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}
Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}
Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}
public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}
The ported VertexPositionNormalTexture struct for Unity
public struct VertexPositionNormalTexture
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TextureCoordinate;
//public static readonly VertexDeclaration VertexDeclaration;
public VertexPositionNormalTexture(Vector3 position, Vector3 normal, Vector2 textureCoordinate)
{
this.Position = position;
this.Normal = normal;
this.TextureCoordinate = textureCoordinate;
}
public override int GetHashCode()
{
// TODO: FIc gethashcode
return 0;
}
public override string ToString()
{
return string.Format("{{Position:{0} Normal:{1} TextureCoordinate:{2}}}", new object[] { this.Position, this.Normal, this.TextureCoordinate });
}
public static bool operator ==(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return (((left.Position == right.Position) && (left.Normal == right.Normal)) && (left.TextureCoordinate == right.TextureCoordinate));
}
public static bool operator !=(VertexPositionNormalTexture left, VertexPositionNormalTexture right)
{
return !(left == right);
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != base.GetType())
{
return false;
}
return (this == ((VertexPositionNormalTexture)obj));
}
}
The ported Unity work is not working properly. No shells and the output image is flat.
This is the expected result in XNA (Works fine):
But this is what I see in Unity (no shells):
The final image supposed to look like the image below but I can't go on with the porting work since the basic implementation is not working properly in Unity.
My script public variable settings:
Why is the the ported Unity result flat? Did I miss anything?
EDIT:
Leo mentioned about possible backface problem because Unity uses the left-handed coordinate system while XNA uses the right-handed coordinate system.
I flipped the UnitZ() value and also tried reversing the mesh vertices but there was nothing on the screen. This is not likely the issue.
Unity is doing a batch optimization on the material. You can see this in the frame debugger. Each DrawGeometry call is using the same value for CurrentLayer. You need to use a propertyblock for each call to DrawMesh.
Setting a new material causes some flickering.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace foo {
public class FurBehavior : MonoBehaviour
{
public Material material;
public Vector3 pos = new Vector3(0f, 0.98f, -9.54f);
//simple camera for use in the game
private new Camera camera;
//texture containing fur data
public Texture2D furTexture;
//effect for fur shaders
//Effect furEffect;
//number of layers of fur
public int nrOfLayers = 40;
//total length of the hair
public float maxHairLength = 2.0f;
//density of hair
public float density = 0.2f;
//[Space(20)]
//public Vector3 dirWorldVal = new Vector3(0, -10, 0);
void Start()
{
this.transform.position = new Vector3(0f, 0.98f, -9.54f);
this.transform.rotation = Quaternion.Euler(0, 180, 0);
Initialize();
GenerateGeometry();
}
public void Update()
{
Draw();
}
void Initialize()
{
//Initialize the camera
camera = Camera.main;
//create the texture
furTexture = new Texture2D(256, 256, TextureFormat.ARGB32, false);
furTexture.wrapModeU = TextureWrapMode.Repeat;
furTexture.wrapModeV = TextureWrapMode.Repeat;
//furTexture.filterMode = FilterMode.Point;
//fill the texture
FillFurTexture(furTexture, density);
/*XNA's SurfaceFormat.Color is ARGB.
//https://gamedev.stackexchange.com/a/6442/98839*/
if (material.mainTexture != null)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
// material.mainTexture.filterMode = FilterMode.Point;
}
}
bool firstDraw = true;
protected void Draw()
{
var pos = this.transform.position;
camera.backgroundColor = CornflowerBlue();
Matrix4x4 worldValue = Matrix4x4.Translate(pos);
Matrix4x4 viewValue = camera.projectionMatrix;
// viewValue = camera.worldToCameraMatrix;
Matrix4x4 projectionValue = camera.projectionMatrix;
material.SetMatrix("World", worldValue);
material.SetMatrix("View", viewValue);
material.SetMatrix("Projection", projectionValue); //Causes object to disappear
material.SetFloat("MaxHairLength", maxHairLength);
//if (firstDraw)
material.SetTexture("_MainTex", furTexture);
//furEffect.Begin();
for (int i = 0; i < nrOfLayers; i++)
{
var propertyBlock = new MaterialPropertyBlock();
var layer = (float)i / (float)nrOfLayers;
propertyBlock.SetFloat("CurrentLayer", layer);
propertyBlock.SetFloat("MaxHairLength", maxHairLength);
propertyBlock.SetColor("_TintColor", new Color(layer, layer, layer, layer));
DrawGeometry(propertyBlock);
}
if (firstDraw)
{
material.mainTexture.wrapModeU = TextureWrapMode.Repeat;
material.mainTexture.wrapModeV = TextureWrapMode.Repeat;
material.mainTexture.filterMode = FilterMode.Point;
}
if (firstDraw)
firstDraw = false;
}
void DrawGeometry(MaterialPropertyBlock props)
{
var rot = Quaternion.Euler(0, 180, 0);
Graphics.DrawMesh(verticesMesh, pos, rot, material, 0, camera, 0, props);
}
private VertexPositionNormalTexture[] verticesPText;
public Mesh verticesMesh;
private void GenerateGeometry()
{
var UnitZ = new Vector3(0, 0, 1);
var verticesPText = new VertexPositionNormalTexture[6];
verticesPText[5] = new VertexPositionNormalTexture(new Vector3(-10, 0, 0),
-UnitZ,
new Vector2(0, 0));
verticesPText[4] = new VertexPositionNormalTexture(new Vector3(10, 20, 0),
-UnitZ,
new Vector2(1, 1));
verticesPText[3] = new VertexPositionNormalTexture(new Vector3(-10, 20, 0),
-UnitZ,
new Vector2(0, 1));
verticesPText[2] = verticesPText[5];
verticesPText[1] = new VertexPositionNormalTexture(new Vector3(10, 0, 0),
-UnitZ,
new Vector2(1, 0));
verticesPText[0] = verticesPText[4];
}
Mesh VertexPositionNormalTextureToUnityMesh(VertexPositionNormalTexture[] vpnt)
{
Vector3[] vertices = new Vector3[vpnt.Length];
Vector3[] normals = new Vector3[vpnt.Length];
Vector2[] uvs = new Vector2[vpnt.Length];
int[] triangles = new int[vpnt.Length];
//Copy variables to create a mesh
for (int i = 0; i < vpnt.Length; i++)
{
vertices[i] = vpnt[i].Position;
normals[i] = vpnt[i].Normal;
uvs[i] = vpnt[i].TextureCoordinate;
triangles[i] = i;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.normals = normals;
mesh.uv = uvs;
mesh.MarkDynamic();
mesh.triangles = triangles;
mesh.UploadMeshData(false);
return mesh;
}
private void FillFurTexture(Texture2D furTexture, float density)
{
//read the width and height of the texture
int width = furTexture.width;
int height = furTexture.height;
int totalPixels = width * height;
//an array to hold our pixels
Color32[] colors = new Color32[totalPixels];
//random number generator
System.Random rand = new System.Random();
//initialize all pixels to transparent black
for (int i = 0; i < totalPixels; i++)
colors[i] = TransparentBlack();
//compute the number of opaque pixels = nr of hair strands
int nrStrands = (int)(density * totalPixels);
//fill texture with opaque pixels
for (int i = 0; i < nrStrands; i++)
{
int x, y;
//random position on the texture
x = rand.Next(height);
y = rand.Next(width);
//put color (which has an alpha value of 255, i.e. opaque)
// colors[x * width + y] = new Color32((byte)255, (byte)x, (byte)y, (byte)255);
colors[x * width + y] = Gold();
}
//set the pixels on the texture.
furTexture.SetPixels32(colors);
// actually apply all SetPixels, don't recalculate mip levels
furTexture.Apply();
}
Color32 TransparentBlack()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_TransparentBlack
Color32 color = new Color32(0, 0, 0, 0);
return color;
}
Color32 Gold()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_Gold
Color32 color = new Color32(255, 215, 0, 255);
return color;
}
Color32 CornflowerBlue()
{
//http://www.monogame.net/documentation/?page=P_Microsoft_Xna_Framework_Color_CornflowerBlue
Color32 color = new Color32(100, 149, 237, 255);
return color;
}
public static Vector3 UnitZ()
{
return new Vector3(0f, 0f, 1f);
}
}
}
I also modified the shader to visualize the shells.
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with
'UnityObjectToClipPos(*)'
Shader "Programmer/Fur Shader"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_TintColor("Tint Color", Color) = (1,1,1,1)
}
SubShader
{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 100
//Blend SrcAlpha One
//Blend DstAlpha OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
//#pragma multi_compile_fog
#include "UnityCG.cginc"
//In
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//Out
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float2 TexCoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Tint: COLOR1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
//Test variable/delete after
float4 _TintColor;
//The variables
float4x4 World;
float4x4 View;
float4x4 Projection;
float CurrentLayer; //value between 0 and 1
float MaxHairLength; //maximum hair length
VertexShaderOutput vert(VertexShaderInput input)
{
VertexShaderOutput output;
float3 pos;
pos = input.Position + input.Normal * MaxHairLength * CurrentLayer;
//float4 worldPosition = mul(float4(pos, 1), World);
//float4 viewPosition = mul(worldPosition, View);
output.Position = UnityObjectToClipPos(pos);
output.TexCoord = input.TexCoord;
output.Tint = float4(CurrentLayer, CurrentLayer, 0, 1);
return output;
}
float4 frag(VertexShaderOutput i) : COLOR0
{
float4 t = tex2D(_MainTex, i.TexCoord) * i.Tint;
return t;//float4(t, i.x, i.y, 1);
}
ENDCG
}
}
}
Here's what it looks like after messing with the parameters and moving the camera a bit.
How do i find center of rotation for a cube made using vertex buffers?
The cube is currently rotating on a vertex and I've been stuck all week trying to figure out how to adjust it to the center.
Here is my code for rendering a cube:
class RenderCube
{
KeyboardState currentKeys;
GamePadState currentGamepad;
//Transform later to have static v and i buffers.
private VertexBuffer vBuffer;
public VertexBuffer VBuffer
{ get { return vBuffer; } set { vBuffer = value; } }
private IndexBuffer iBuffer;
public IndexBuffer IBuffer
{ get { return iBuffer; } set { iBuffer = value; } }
private BasicEffect bEffect;
public BasicEffect BEffect
{ get { return bEffect; } set { bEffect = value; } }
private Matrix world;
public Matrix World
{ get { return world; } set { world = value; } }
private Matrix view;
public Matrix View
{ get { return view; } set { view = value; } }
private Matrix projection;
private Matrix Projection
{ get { return projection; } set { projection = value; } }
private Color color;
public Color Color
{ get { return color; } set { color = value; } }
private Vector3 position;
public Vector3 Position
{ get { return position; } set { position = value; } }
//Need to change this eventually to use textures.
private VertexPositionColor[] vertices;
short[] indices;
private GraphicsDevice device;
//constructors!
public RenderCube(Color col, Vector3 pos, GraphicsDevice dev)
{
device = dev;
this.color = col;
this.position = pos;
SetUpVertices();
SetUpIndices();
world = Matrix.CreateTranslation(position);
//world = Matrix.CreateTranslation(0, 0, 0);
bEffect = new BasicEffect(device);
bEffect.World = world;
bEffect.VertexColorEnabled = true;
//bEffect.EnableDefaultLighting();
}
public void Render(Camera cam)
{
bEffect.View = cam.view;
bEffect.Projection = cam.projection;
bEffect.World *= cam.rotX;
bEffect.World *= cam.rotY;
bEffect.World *= cam.rotZ;
var rotationCenter = new Vector3(0.5f, 0.5f, 0.5f);
device.SetVertexBuffer(vBuffer);
device.Indices = IBuffer;
foreach (EffectPass pass in bEffect.CurrentTechnique.Passes)
{
pass.Apply();
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
}
}
/// <summary>
/// Sets up the vertices for a cube using 8 unique vertices.
/// Build order is front to back, left to up to right to down.
/// </summary>
private void SetUpVertices()
{
vertices = new VertexPositionColor[8];
//front left bottom corner
vertices[0] = new VertexPositionColor(new Vector3(0, 0, 0), color);
//front left upper corner
vertices[1] = new VertexPositionColor(new Vector3(0, 100, 0), color);
//front right upper corner
vertices[2] = new VertexPositionColor(new Vector3(100, 100, 0), color);
//front lower right corner
vertices[3] = new VertexPositionColor(new Vector3(100, 0, 0), color);
//back left lower corner
vertices[4] = new VertexPositionColor(new Vector3(0, 0, -100), color);
//back left upper corner
vertices[5] = new VertexPositionColor(new Vector3(0, 100, -100), color);
//back right upper corner
vertices[6] = new VertexPositionColor(new Vector3(100, 100, -100), color);
//back right lower corner
vertices[7] = new VertexPositionColor(new Vector3(100, 0, -100), color);
vBuffer = new VertexBuffer(device, typeof(VertexPositionColor), 8, BufferUsage.WriteOnly);
vBuffer.SetData<VertexPositionColor>(vertices);
}
/// <summary>
/// Sets up the indices for a cube. Has 36 positions that match up
/// to the element numbers of the vertices created earlier.
/// Valid range is 0-7 for each value.
/// </summary>
private void SetUpIndices()
{
indices = new short[36];
//Front face
//bottom right triangle
indices[0] = 0;
indices[1] = 3;
indices[2] = 2;
//top left triangle
indices[3] = 2;
indices[4] = 1;
indices[5] = 0;
//back face
//bottom right triangle
indices[6] = 4;
indices[7] = 7;
indices[8] = 6;
//top left triangle
indices[9] = 6;
indices[10] = 5;
indices[11] = 4;
//Top face
//bottom right triangle
indices[12] = 1;
indices[13] = 2;
indices[14] = 6;
//top left triangle
indices[15] = 6;
indices[16] = 5;
indices[17] = 1;
//bottom face
//bottom right triangle
indices[18] = 4;
indices[19] = 7;
indices[20] = 3;
//top left triangle
indices[21] = 3;
indices[22] = 0;
indices[23] = 4;
//left face
//bottom right triangle
indices[24] = 4;
indices[25] = 0;
indices[26] = 1;
//top left triangle
indices[27] = 1;
indices[28] = 5;
indices[29] = 4;
//right face
//bottom right triangle
indices[30] = 3;
indices[31] = 7;
indices[32] = 6;
//top left triangle
indices[33] = 6;
indices[34] = 2;
indices[35] = 3;
iBuffer = new IndexBuffer(device, IndexElementSize.SixteenBits, sizeof(short) * indices.Length, BufferUsage.WriteOnly);
iBuffer.SetData(indices);
}
}
The basic idea is to introduce a translation matrix that pushes the cube to the origin, perform the rotation and undo the translation:
public void Render(Camera cam)
{
//...
//push the cube to the origin
bEffect.World *= Matrix.CreateTranslation(-50, -50, 50);
//perform the rotation
bEffect.World *= cam.rotX;
bEffect.World *= cam.rotY;
bEffect.World *= cam.rotZ;
//undo the translation
bEffect.World *= Matrix.CreateTranslation(50, 50, -50);
//...
I'm trying to get some primitives working for XNA, including the ability to make outlined rectangles that can rotate. This is pretty useful, as you can do some sweet dynamic content creation with it, as well as it serving a debugging purpose.
The problem I am having can be seen here
Essentially, what is happening is, when drawing an outlined rectangle, and turning the thickness of the line up past one, the lines that make up the rectangle move farther and farther away.
It's very strange but, I'm 90% sure that the problem lies in the origin.
I'm hoping someone could take a look and spot my mistake.
I am rendering the rectangle by drawing rotated lines by setting the origin of the line to be what the center of the rectangle is. To achieve the bottom and right parts of the rectangle, I copy and rotate the top and left lines 180 degrees. (Pi if you are working in radians, which I am)
The biggest weirdness is that when I debug the Draw method I find the values are all exactly as expected!
Here's the rectangle class, the inherited classes are also below in case you need them.
public class Rectangle : Primitive
{
public float X { get { return Body.X; } set { Body.X = value; } }
public float Y { get { return Body.Y; } set { Body.Y = value; } }
public float Width { get { return Body.Width; } set { Body.Width = value; } }
public float Height { get { return Body.Height; } set { Body.Height = value; } }
public float Angle { get { return Body.Angle; } set { Body.Angle = value; } }
public override Vector2 Bounds { get { return new Vector2((Width) * Scale.X, (Height) * Scale.Y);}}
public override Microsoft.Xna.Framework.Rectangle DrawRect
{
get
{
return new Microsoft.Xna.Framework.Rectangle((int) (X - Thickness/2), (int) (Y - Thickness/2),
(int) ((X + Width + Thickness) * Scale.X),
(int) ((Y + Height + Thickness)* Scale.Y));
}
}
public bool Fill;
public Rectangle(Entity parent, string name, float x, float y, float width, float height, bool fill = false)
: base(parent, name)
{
X = x;
Y = y;
Width = width;
Height = height;
Fill = fill;
Origin = new Vector2(.5f,.5f);
}
public Rectangle(IComponent parent, string name, Body body, bool fill) : base(parent, name, body)
{
Fill = fill;
Origin = new Vector2(.5f, .5f);
}
public override void Draw(SpriteBatch sb)
{
base.Draw(sb);
if (!Fill)
{
float minx = X + (Thickness/2) + Origin.X;
float miny = Y + (Thickness/2) +Origin.Y;
//TODO: Fix origin issue
//Draw our top line
sb.Draw(Assets.Pixel,
new Vector2(minx, miny), null, Color * Alpha, Angle, new Vector2(Origin.X, Origin.Y * Bounds.Y), new Vector2(Bounds.X, Thickness * Scale.Y), Flip, Layer);
//Left line
sb.Draw(Assets.Pixel,
new Vector2(minx, miny), null, Color * Alpha, Angle, new Vector2(Origin.X * Bounds.X, Origin.Y), new Vector2(Thickness * Scale.X, Bounds.Y), Flip, Layer);
//Essentially these are the same as the top and bottom just rotated 180 degrees
//I have to do it this way instead of setting the origin to a negative value because XNA
//seems to ignore origins when they are negative
//Right Line
sb.Draw(Assets.Pixel,
new Vector2(minx + 1, miny), null, Color * Alpha, Angle + MathHelper.Pi, new Vector2(Origin.X * Bounds.X, Origin.Y), new Vector2(Thickness * Scale.X, Bounds.Y), Flip, Layer);
//Bottom Line
sb.Draw(Assets.Pixel,
new Vector2(minx, miny + 1), null, Color * Alpha, Angle + MathHelper.Pi, new Vector2(Origin.X, Origin.Y * Bounds.Y), new Vector2(Bounds.X, Thickness * Scale.Y), Flip, Layer);
}
else
{
sb.Draw(Assets.Pixel, new Vector2(X + Origin.X*Width, Y + Origin.Y*Height), null, Color * Alpha, Angle, Origin, Bounds - new Vector2(Thickness), Flip, Layer);
}
}
Primitive
public abstract class Primitive : Render
{
public Body Body;
public float Thickness = 1;
protected Primitive(IComponent parent, string name) : base(parent, name)
{
Body = new Body(this, "Primitive.Body");
}
protected Primitive(IComponent parent, string name, Vector2 pos)
: base(parent, name)
{
Body = new Body(this, "Primitive.Body", pos);
}
protected Primitive(IComponent parent, string name, Body body) : base(parent, name)
{
Body = body;
}
public static void DrawLine(SpriteBatch sb, Vector2 p1, Vector2 p2, float thickness, float layer, Color color)
{
float angle = (float)System.Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
float length = Vector2.Distance(p1, p2);
sb.Draw(Assets.Pixel, p1, null, color,
angle, Vector2.Zero, new Vector2(length, thickness),
SpriteEffects.None, layer);
}
}
Render class. This one isn't as important as it is just there to give some sort of polymorphism to any rendering class.
using EntityEngineV4.Engine;
using EntityEngineV4.PowerTools;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace EntityEngineV4.Components.Rendering
{
public abstract class Render : Component
{
public float Alpha = 1f;
public Color Color = Color.White;
public SpriteEffects Flip = SpriteEffects.None;
public float Layer;
public Vector2 Scale = Vector2.One;
public Vector2 Origin;
/// <summary>
/// Handy rectangle for getting the drawing position
/// </summary>
public virtual Rectangle DrawRect { get; set; }
/// <summary>
/// Source rectangle of the texture
/// </summary>
public virtual Rectangle SourceRect { get; set; }
/// <summary>
/// Bounds of the DrawRect
/// </summary>
public virtual Vector2 Bounds { get; set; }
protected Render(IComponent parent, string name)
: base(parent, name)
{
}
public override void Update(GameTime gt)
{
base.Update(gt);
}
public override void Draw(SpriteBatch sb = null)
{
base.Draw(sb);
}
}
}
If you have any questions, don't be afraid to ask!
You should divide origin by the scale.
var scale = new Vector2(Bounds.X, Thickness * Scale.Y);
var origin = new Vector2(Origin.X, Origin.Y * Bounds.Y) / scale;
sb.Draw(Assets.Pixel, new Vector2(minx, miny), null, Color * Alpha,
Angle, origin, scale, Flip, Layer);
Though I think you are doing things a bit complicated... I would use a transform matrix to transform the drawed rectangle...
public void DrawBorder( Rectangle Bounds, int ThickNess, float angle, Color color ) {
Vector3 Origin = new Vector3( Bounds.Width, Bounds.Height, 0 ) * 0.5f;
Vector3 Pos = new Vector3( Bounds.X, Bounds.Y, 0 );
Matrix transform = Matrix.CreateTranslation( -Origin )
* Matrix.CreateRotationZ( angle )
* Matrix.CreateTranslation( Pos );
Batch.Begin( SpriteSortMode.Immediate, BlendState.AlphaBlend, transform );
Bounds.X = 0;
Bounds.Y = 0;
Rectangle aux = Bounds;
Bounds.X -= ThickNess;
Bounds.Width = ThickNess;
Batch.Draw( Batch.WhiteTexture, Bounds, color );
Bounds.X = aux.Right;
Batch.Draw( Batch.WhiteTexture, Bounds, color );
Bounds.Y = aux.Top - ThickNess;
Bounds.X = aux.Left - ThickNess;
Bounds.Width = aux.Width + 2 * ThickNess;
Bounds.Height = ThickNess;
Batch.Draw( Batch.WhiteTexture, Bounds, color );
Bounds.Y = aux.Bottom;
Batch.Draw( Batch.WhiteTexture, Bounds, color );
Batch.End( );
}
I want to draw multiple players on the client. It works fine without the foreach rotations, but when i add the foreach rotations it updates both players with eachothers rotation instead of the player who the rotation is meant to be for.
foreach (var kvp in positions)
{
foreach (var kvr in rotations)
{
// draw player
spriteBatch.Draw(texture, kvp.Value, null, Color.White,
kvr.Value, new Vector2(texture.Width / 2, texture.Height / 2), 1f,
SpriteEffects.None, 1f);
}
}
Each player has an unique ID which I store as key inside BOTH dictionaries.
Is there a way that i can combine these dictionaries to achieve the correct result?
Right now Dictonary positions contains (long,Vector2(x,y)) where long is the unique user ID
and the rotations contains (long, float) where long is the same user ID as in positions and float is the rotation.
Edit:
This works fine but doesn't update the rotation as it's set.
foreach (var kvp in positions)
{
// draw player
spriteBatch.Draw(texture, kvp.Value, null, Color.White, 1f,
new Vector2(texture.Width / 2, texture.Height / 2), 1f,
SpriteEffects.None, 1f);
}
Why don't you create a Player class that contains all of the information about a player.
class Player
{
public Vector2 Position { get; set; }
public float Rotation { get; set; }
}
and just keep a single dictionary of that using the loop, here I'll show you a very simple game class that draws 5 players with the rotations, hopefully it'll help :)
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Dictionary<int, Player> players;
Texture2D texture;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("player");
Random rnd = new Random();
players = new Dictionary<int, Player>()
{
// Add 5 new players, their location is random and so is their rotation.
{0, new Player() { Position = new Vector2(rnd.Next(1024),rnd.Next(768)), Rotation = rnd.Next(360), Size = new Vector2(texture.Width, texture.Height)}},
{1, new Player() { Position = new Vector2(rnd.Next(1024),rnd.Next(768)), Rotation = rnd.Next(360), Size = new Vector2(texture.Width, texture.Height)}},
{2, new Player() { Position = new Vector2(rnd.Next(1024),rnd.Next(768)), Rotation = rnd.Next(360), Size = new Vector2(texture.Width, texture.Height)}},
{3, new Player() { Position = new Vector2(rnd.Next(1024),rnd.Next(768)), Rotation = rnd.Next(360), Size = new Vector2(texture.Width, texture.Height)}},
{4, new Player() { Position = new Vector2(rnd.Next(1024),rnd.Next(768)), Rotation = rnd.Next(360), Size = new Vector2(texture.Width, texture.Height)}},
};
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
for (int i = 0; i < players.Count; i++)
{
Player targetPlayer = players[i];
spriteBatch.Draw(texture, targetPlayer.PlayerRectangle, null, Color.White, targetPlayer.Rotation, targetPlayer.Centre, SpriteEffects.None, 0.0f);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
class Player
{
Rectangle playerRectangle;
Vector2 position;
Vector2 size;
Vector2 center;
public Vector2 Position { get { return position; } set { position = value; UpdateRectangleAndCentre(); } }
public Vector2 Size { get { return size; } set { size = value; UpdateRectangleAndCentre(); } }
public float Rotation { get; set; }
public Rectangle PlayerRectangle { get { return playerRectangle; } }
public Vector2 Centre { get { return center; } }
void UpdateRectangleAndCentre()
{
playerRectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
center = new Vector2(size.X / 2, size.Y / 2);
}
}
Is it not as simple as:
foreach (var kvp in positions)
{
kvr = rotations[kvp.Key];
// draw player
spriteBatch.Draw(texture, kvp.Value, null, Color.White,
kvr.Value, new Vector2(texture.Width / 2, texture.Height / 2), 1f,
SpriteEffects.None, 1f);
}
I'm attempting to render a textured quad using the example located here.
I can successfully render the quad, but the texture information appears to be lost. The quad takes the color of the underlying texture, though.
I've checked the obvious problems ("Does the BasicEffect rendering the quad have the TextureEnabled property set to true?") and can't immediately see the problem.
Code below:
public class Quad
{
public VertexPositionNormalTexture[] Vertices;
public Vector3 Origin;
public Vector3 Up;
public Vector3 Normal;
public Vector3 Left;
public Vector3 UpperLeft;
public Vector3 UpperRight;
public Vector3 LowerLeft;
public Vector3 LowerRight;
public int[] Indexes;
public Quad(Vector3 origin, Vector3 normal, Vector3 up,
float width, float height)
{
this.Vertices = new VertexPositionNormalTexture[4];
this.Indexes = new int[6];
this.Origin = origin;
this.Normal = normal;
this.Up = up;
// Calculate the quad corners
this.Left = Vector3.Cross(normal, this.Up);
Vector3 uppercenter = (this.Up * height / 2) + origin;
this.UpperLeft = uppercenter + (this.Left * width / 2);
this.UpperRight = uppercenter - (this.Left * width / 2);
this.LowerLeft = this.UpperLeft - (this.Up * height);
this.LowerRight = this.UpperRight - (this.Up * height);
this.FillVertices();
}
private void FillVertices()
{
Vector2 textureUpperLeft = new Vector2(0.0f, 0.0f);
Vector2 textureUpperRight = new Vector2(1.0f, 0.0f);
Vector2 textureLowerLeft = new Vector2(0.0f, 1.0f);
Vector2 textureLowerRight = new Vector2(1.0f, 1.0f);
for (int i = 0; i < this.Vertices.Length; i++)
{
this.Vertices[i].Normal = this.Normal;
}
this.Vertices[0].Position = this.LowerLeft;
this.Vertices[0].TextureCoordinate = textureLowerLeft;
this.Vertices[1].Position = this.UpperLeft;
this.Vertices[1].TextureCoordinate = textureUpperLeft;
this.Vertices[2].Position = this.LowerRight;
this.Vertices[2].TextureCoordinate = textureLowerRight;
this.Vertices[3].Position = this.UpperRight;
this.Vertices[3].TextureCoordinate = textureUpperRight;
this.Indexes[0] = 0;
this.Indexes[1] = 1;
this.Indexes[2] = 2;
this.Indexes[3] = 2;
this.Indexes[4] = 1;
this.Indexes[5] = 3;
}
}
this.quadEffect = new BasicEffect(this.GraphicsDevice, null);
this.quadEffect.AmbientLightColor = new Vector3(0.8f, 0.8f, 0.8f);
this.quadEffect.LightingEnabled = true;
this.quadEffect.World = Matrix.Identity;
this.quadEffect.View = this.View;
this.quadEffect.Projection = this.Projection;
this.quadEffect.TextureEnabled = true;
this.quadEffect.Texture = someTexture;
this.quad = new Quad(Vector3.Zero, Vector3.UnitZ, Vector3.Up, 2, 2);
this.quadVertexDecl = new VertexDeclaration(this.GraphicsDevice, VertexPositionNormalTexture.VertexElements);
public override void Draw(GameTime gameTime)
{
this.GraphicsDevice.Textures[0] = this.SpriteDictionary["B1S1I800"];
this.GraphicsDevice.VertexDeclaration = quadVertexDecl;
quadEffect.Begin();
foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
{
pass.Begin();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(
PrimitiveType.TriangleList,
beamQuad.Vertices, 0, 4,
beamQuad.Indexes, 0, 2);
pass.End();
}
quadEffect.End();
}
From what I can see, this should work. The only thing I can imagine, which isn't in this code, is that the loading of the texture goes wrong somewhere. I also can't quite visualize what you mean that the quad has the underlying color of the texture? Do you have a screenshot for us?
Also, if something does show up, a very distorted version of your texture for example, it could be possible that the rendering of other stuff has effect on the rendering of the quad. For example if you draw the quad while the graphicsdevice has another vertex declaration on it, or if the previous thing rendered set some exotic rendering state, or if you're drawing the quad within the drawing code of something else. Try isolating this code, into a fresh project or something, or disable the rendering of everything else.