Sign in to follow this  

Working C# Teapot Sample

This topic is 4201 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there, This is my first visit here. I am not new to programming but am new to DirectX 9 (April 2006). I read a lot and looked at some samples. I coded a simple Windows Form Teapot Sample - Problem - it is still black. I tried all tricks with lights etc. Does anybody know of a simple working C# Teapot (or generic Mesh) sample with Ambient light. I would be so greatful. I am at the stupid point where I am missing something small and ... it is the world for me ... I hope someone could relate. I promise to post the code of a "damn straight" one page sample - I just need someone to help me turn " the light on" :) Many Many Thanks

Share this post


Link to post
Share on other sites
Well, let's take a look at your code first and see what you've got. Odds says it's something simple that you're just overlooking (don't worry, we've all been there[smile]).

-AJ

Share this post


Link to post
Share on other sites
Hi AJ,

Wow ... somebody answered - Many thanks already!

Just open a standard Windows Froms project.
Delete the default forms and classes.
Add References DirectX v 2.0.0.0 (x64) mine is from April 2006 SDK
Add new Windows Form named "Meshes" and replace all of the code inside with
Result is a turning black teapot on a blue screen.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;

namespace WindowsApplication2
{
public partial class Meshes : Form
{
Device device = null; // Our rendering device
Mesh mesh = null; // Our mesh object in sysmem
PresentParameters presentParams = new PresentParameters();

bool pause = false;

public Meshes()
{
// Set the initial size of our form
this.ClientSize = new System.Drawing.Size(600, 500);
// And its caption
this.Text = "Test Meshes";

}
private void Meshes_Load(object sender, EventArgs e)
{

}

bool InitializeGraphics()
{
// Get the current desktop display mode, so we can set up a back
// buffer of the same format
try
{
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geometry, we will create a device with a zbuffer.
presentParams.IsWindowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;

// Create the D3DDevice
device = new Device(0
, DeviceType.Hardware
, this.Handle
, CreateFlags.SoftwareVertexProcessing
, presentParams);

device.DeviceReset += new System.EventHandler(this.OnResetDevice);
this.OnResetDevice(device, null);
pause = false;
}
catch (DirectXException)
{
return false;
}
return true;
}
public void OnResetDevice(object sender, EventArgs e)
{
mesh = Mesh.Teapot(device);
}

void SetupMatrices()
{
// For our world matrix, we will just leave it as the identity
device.Transform.World = Matrix.RotationY(Environment.TickCount / 1000.0f);

// Set up our view matrix. A view matrix can be defined given an eye point,
// a point to lookat, and a direction for which way is up. Here, we set the
// eye five units back along the z-axis and up three units, look at the
// origin, and define "up" to be in the y-direction.
Vector3 CameraPosition = new Vector3(0, 0.5F, -3);
Vector3 CameraTarget = new Vector3(0, 0.5F, 0);
Vector3 CameraUpVector = new Vector3(0, 1, 0);

device.Transform.View = Matrix.LookAtLeftHanded(CameraPosition, CameraTarget, CameraUpVector);

// For the projection matrix, we set up a perspective transform (which
// transforms geometry from 3D view space to 2D viewport space, with
// a perspective divide making objects smaller in the distance). To build
// a perpsective transform, we need the field of view (1/4 pi is common),
// the aspect ratio, and the near and far clipping planes (which define at
// what distances geometry should be no longer be rendered).
float FieldOfView = (float)(Math.PI / 4);
float aspectRatio = 1.0f;
float zNearPlane = 1.0f;
float zFarPlane = 10.0f;
device.Transform.Projection = Matrix.PerspectiveFieldOfViewLeftHanded(FieldOfView
, aspectRatio
, zNearPlane
, zFarPlane);
}

private void SetupLights()
{
System.Drawing.Color col = System.Drawing.Color.White;

// Set up a material.
Direct3D.Material mtrl = new Direct3D.Material();
mtrl.Diffuse = col;
mtrl.Ambient = col;
device.Material = mtrl;

// Set up a colored directional light
device.Lights[0].LightType = LightType.Directional;
device.Lights[0].Diffuse = System.Drawing.Color.DarkTurquoise;
device.Lights[0].Position = new Vector3(0, 0, -100);
device.Lights[0].InnerConeAngle = (float)(Math.PI / 20);
device.Lights[0].OuterConeAngle = (float)(Math.PI / 14);
device.Lights[0].Range = 500.0f;
device.Lights[0].Direction = new Vector3(0, 0, 1);
device.Lights[0].Direction.Normalize();
device.Lights[0].Update();
device.Lights[0].Enabled = true; // Turn it on

// Finally, turn on some ambient light.
// Ambient light is light that scatters and lights all objects evenly.
device.RenderState.CullMode = Cull.None;
device.RenderState.Ambient = System.Drawing.Color.FromArgb(0x202020);

}

private void Render()
{
if (device == null)
return;

if (pause)
return;

//Clear the backbuffer to a blue color
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);

//Begin the scene
device.BeginScene();

// Setup the world, view, and projection matrices
SetupMatrices();
SetupLights();

mesh.DrawSubset(0);

//End the scene
device.EndScene();
device.Present();

}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
this.Render(); // Render on painting
}
protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
this.Dispose(); // Esc was pressed
}

protected override void OnResize(System.EventArgs e)
{
pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);
}

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
using (Meshes frm = new Meshes())
{
if (!frm.InitializeGraphics()) // Initialize Direct3D
{
MessageBox.Show("Could not initialize Direct3D. Will exit.");
return;
}
frm.Show();

// While the form is still valid, render and process messages
while (frm.Created)
{
frm.Render();
Application.DoEvents();
}
}
}
}
}

Share this post


Link to post
Share on other sites
Oh ... well ... I got it right after so much pain.

I post the solution here for the frustrated souls out there who are just starting with DirectX.

Please understand the code - It is, as I promised, a "damn straight" representation of the concept ... deliberately kept with hadcoded stuff.
In my case I felt like "Yeah, sure I know how to drive - but where do I put in the damn ignition key" - Hence - elegance of code-writing was not an issue in this case.

It is my first 3D code, where I, in complete desperation, stripped bits and pieces fwom Tom Miller's book (Which DOES NOT compile with the latest DirectX) as well as from the SDK samples.

How to "install" the code - see my previous postings. Just make sure you put somewhere the files teapot.x, tiger.x + tiger.bmp (from Tom Miller and the April DirectX SDK).

This code does compile and run.
Just play with the camera position settings as well as device.Transform.World

I am sharing this with you but please - you are sincerely welcome to critisize the code in case you see improper use of devices, initialization etc.

For example I could not use any of the "device.Lights[0]" settings - If a guru reads this - please feel free to comment.

Anyway - here the code:
##############################################
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;

namespace WindowsApplication2
{
public partial class Meshes : Form
{
Device device = null; // Our rendering device
Mesh myMesh = null; // Our mesh object in sysmem
PresentParameters presentParams = new PresentParameters();
Direct3D.Material mtrl;
//CubeTexture environment = null;
RenderToEnvironmentMap rte = null;
const int CubeMapSize = 128;
readonly Matrix ViewMatrix = Matrix.Translation(0.0f, 0.0f, 13.0f);
Material[] meshMaterials = null; // array of materials
BaseTexture[] meshTextures = null; // array of textures, entries are null if no texture specified

bool pause = false;

public Meshes()
{
// Set the initial size of our form
this.ClientSize = new System.Drawing.Size(600, 500);
// And its caption
this.Text = "Test Meshes";

}
private void Meshes_Load(object sender, EventArgs e)
{

}

bool InitializeGraphics()
{
// Get the current desktop display mode, so we can set up a back
// buffer of the same format
try
{
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geometry, we will create a device with a zbuffer.
presentParams.IsWindowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;

// Create the D3DDevice
device = new Device(0
, DeviceType.Hardware
, this.Handle
, CreateFlags.SoftwareVertexProcessing
, presentParams);


device.DeviceReset += new System.EventHandler(this.OnResetDevice);
this.OnResetDevice(device, null);
pause = false;

myMesh = LoadMesh("E:\\Projects\\WindowsApplication3\\WindowsApplication3\\Tiger.x"
,ref meshMaterials
, ref meshTextures );

// Setup the world, view, and projection matrices
SetupMatrices();
SetupLights();
}
catch (DirectXException)
{
return false;
}
return true;
}

public Mesh LoadMesh(string filename, ref Material[] meshMaterials, ref BaseTexture[] meshTextures)
{

GraphicsBuffer adjacency = new GraphicsBuffer(); // Adjacency information
MaterialList materials = new MaterialList();
ExtendedMaterial[] mtrl;
Mesh mesh = new Mesh(device, filename, MeshFlags.Managed, adjacency, materials, null);

CreateMaterials(filename, device, adjacency, materials);

// Make sure there are normals, which are required for the tesselation
// enhancement.
if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)
{
Mesh temporaryMesh = mesh.Clone(device,
mesh.Options.Value,
mesh.VertexFormat | VertexFormats.Normal);

// Compute normals now
temporaryMesh.ComputeNormals();

// Set the mesh
mesh.Dispose();
mesh = temporaryMesh;
}
return mesh;

}

public void CreateMaterials(string folder, Device device, GraphicsBuffer adjacency, MaterialList materials)
{
// Does the mesh have materials?
if ((materials != null) && (materials.Count > 0))
{
// Allocate the arrays for the materials
meshMaterials = new Material[materials.Count];
meshTextures = new Texture[materials.Count];

// Copy each material and create it's texture
for (int i = 0; i < materials.Count; i++)
{
// Copy the material first
meshMaterials[i] = materials[i].Material;

// Is there a texture for this material?
if (string.IsNullOrEmpty(materials[i].TextureFileName))
continue; // No, just continue now

ImageInformation info = new ImageInformation();
string textureFile = "E:\\Projects\\WindowsApplication3\\WindowsApplication3\\tiger.bmp";
// First look for the texture in the same folder as the input folder
info = BaseTexture.GetImageInformationFromFile(textureFile);

switch (info.ResourceType)
{
case ResourceType.Texture:
meshTextures[i] = new Texture(device, textureFile);
break;
case ResourceType.CubeTexture:
meshTextures[i] = new CubeTexture(device, textureFile);
break;
case ResourceType.VolumeTexture:
meshTextures[i] = new VolumeTexture(device, textureFile);
break;
}
}
}
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
rte = new RenderToEnvironmentMap(dev, CubeMapSize, 1, Format.X8B8G8R8, true, DepthFormat.D16);
//environment = new CubeTexture(dev, CubeMapSize, 1, Usage.RenderTarget, Format.X8B8G8R8, Pool.Default);

}

void SetupMatrices()
{
// For our world matrix, we will just leave it as the identity
device.Transform.World = Matrix.RotationY(Environment.TickCount / 1000.0f);
//device.Transform.World = Matrix.Translation(0, 0, 0);

// Set up our view matrix. A view matrix can be defined given an eye point,
// a point to lookat, and a direction for which way is up. Here, we set the
// eye five units back along the z-axis and up three units, look at the
// origin, and define "up" to be in the y-direction.
Vector3 CameraPosition = new Vector3(-5, 0, 0);
Vector3 CameraTarget = new Vector3(0, 0, 0);
Vector3 CameraUpVector = new Vector3(0, 1, 0);

device.Transform.View = Matrix.LookAtLeftHanded(CameraPosition, CameraTarget, CameraUpVector);

// For the projection matrix, we set up a perspective transform (which
// transforms geometry from 3D view space to 2D viewport space, with
// a perspective divide making objects smaller in the distance). To build
// a perpsective transform, we need the field of view (1/4 pi is common),
// the aspect ratio, and the near and far clipping planes (which define at
// what distances geometry should be no longer be rendered).
float FieldOfView = (float)(Math.PI / 4);
float aspectRatio = 1.0f;
float zNearPlane = 1.0f;
float zFarPlane = 1000.0f;
device.Transform.Projection = Matrix.PerspectiveFieldOfViewLeftHanded(FieldOfView
, aspectRatio
, zNearPlane
, zFarPlane);
}

private void SetupLights()
{
System.Drawing.Color col = System.Drawing.Color.White;
device.Lights[0].Enabled = true; // Turn it on

// Set up a material.
mtrl = new Direct3D.Material();
mtrl.Diffuse = col;
mtrl.Ambient = col;
device.Material = mtrl;



// Finally, turn on some ambient light.
// Ambient light is light that scatters and lights all objects evenly.
device.RenderState.CullMode = Cull.None;
device.RenderState.Ambient = System.Drawing.Color.White;
//device.RenderState.FillMode = FillMode.WireFrame;
//device.RenderState.FillMode = FillMode.Point;
device.RenderState.FillMode = FillMode.Solid;

}

private void Render()
{
if (device == null)
return;

if (pause)
return;

//Clear the backbuffer to a blue color
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);

//Begin the scene
device.BeginScene();
//###############################################


SetupMatrices();

for (int i = 0; i < meshMaterials.Length; i++)
{
device.Material = meshMaterials[i];
device.SetTexture(0, meshTextures[i]);
myMesh.DrawSubset(i);
}


//###############################################
//End the scene
device.EndScene();
device.Present();

}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
this.Render(); // Render on painting
}
protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
this.Dispose(); // Esc was pressed
}

protected override void OnResize(System.EventArgs e)
{
pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);
}

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
using (Meshes frm = new Meshes())
{
if (!frm.InitializeGraphics()) // Initialize Direct3D
{
MessageBox.Show("Could not initialize Direct3D. Will exit.");
return;
}
frm.Show();

// While the form is still valid, render and process messages
while (frm.Created)
{
frm.Render();
Application.DoEvents();
}
}
}
}
}

Share this post


Link to post
Share on other sites
Hi, glad you figured it out, but a little helpful tip: use [ source ] and [ /source ] tags (without the spaces), it is incredibly useful. People tend to complain if there arent those tags.

For example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;

namespace WindowsApplication2
{
public partial class Meshes : Form
{
Device device = null; // Our rendering device
Mesh myMesh = null; // Our mesh object in sysmem
PresentParameters presentParams = new PresentParameters();
Direct3D.Material mtrl;
//CubeTexture environment = null;
RenderToEnvironmentMap rte = null;
const int CubeMapSize = 128;
readonly Matrix ViewMatrix = Matrix.Translation(0.0f, 0.0f, 13.0f);
Material[] meshMaterials = null; // array of materials
BaseTexture[] meshTextures = null; // array of textures, entries are null if no texture specified

bool pause = false;

public Meshes()
{
// Set the initial size of our form
this.ClientSize = new System.Drawing.Size(600, 500);
// And its caption
this.Text = "Test Meshes";

}
private void Meshes_Load(object sender, EventArgs e)
{

}

bool InitializeGraphics()
{
// Get the current desktop display mode, so we can set up a back
// buffer of the same format
try
{
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geometry, we will create a device with a zbuffer.
presentParams.IsWindowed = true;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat = DepthFormat.D16;

// Create the D3DDevice
device = new Device(0
, DeviceType.Hardware
, this.Handle
, CreateFlags.SoftwareVertexProcessing
, presentParams);


device.DeviceReset += new System.EventHandler(this.OnResetDevice);
this.OnResetDevice(device, null);
pause = false;

myMesh = LoadMesh("E:\\Projects\\WindowsApplication3\\WindowsApplication3\\Tiger.x"
,ref meshMaterials
, ref meshTextures );

// Setup the world, view, and projection matrices
SetupMatrices();
SetupLights();
}
catch (DirectXException)
{
return false;
}
return true;
}

public Mesh LoadMesh(string filename, ref Material[] meshMaterials, ref BaseTexture[] meshTextures)
{

GraphicsBuffer adjacency = new GraphicsBuffer(); // Adjacency information
MaterialList materials = new MaterialList();
ExtendedMaterial[] mtrl;
Mesh mesh = new Mesh(device, filename, MeshFlags.Managed, adjacency, materials, null);

CreateMaterials(filename, device, adjacency, materials);

// Make sure there are normals, which are required for the tesselation
// enhancement.
if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)
{
Mesh temporaryMesh = mesh.Clone(device,
mesh.Options.Value,
mesh.VertexFormat | VertexFormats.Normal);

// Compute normals now
temporaryMesh.ComputeNormals();

// Set the mesh
mesh.Dispose();
mesh = temporaryMesh;
}
return mesh;

}

public void CreateMaterials(string folder, Device device, GraphicsBuffer adjacency, MaterialList materials)
{
// Does the mesh have materials?
if ((materials != null) && (materials.Count > 0))
{
// Allocate the arrays for the materials
meshMaterials = new Material[materials.Count];
meshTextures = new Texture[materials.Count];

// Copy each material and create it's texture
for (int i = 0; i < materials.Count; i++)
{
// Copy the material first
meshMaterials[i] = materials[i].Material;

// Is there a texture for this material?
if (string.IsNullOrEmpty(materials[i].TextureFileName))
continue; // No, just continue now

ImageInformation info = new ImageInformation();
string textureFile = "E:\\Projects\\WindowsApplication3\\WindowsApplication3\\tiger.bmp";
// First look for the texture in the same folder as the input folder
info = BaseTexture.GetImageInformationFromFile(textureFile);

switch (info.ResourceType)
{
case ResourceType.Texture:
meshTextures[i] = new Texture(device, textureFile);
break;
case ResourceType.CubeTexture:
meshTextures[i] = new CubeTexture(device, textureFile);
break;
case ResourceType.VolumeTexture:
meshTextures[i] = new VolumeTexture(device, textureFile);
break;
}
}
}
}
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
rte = new RenderToEnvironmentMap(dev, CubeMapSize, 1, Format.X8B8G8R8, true, DepthFormat.D16);
//environment = new CubeTexture(dev, CubeMapSize, 1, Usage.RenderTarget, Format.X8B8G8R8, Pool.Default);

}

void SetupMatrices()
{
// For our world matrix, we will just leave it as the identity
device.Transform.World = Matrix.RotationY(Environment.TickCount / 1000.0f);
//device.Transform.World = Matrix.Translation(0, 0, 0);

// Set up our view matrix. A view matrix can be defined given an eye point,
// a point to lookat, and a direction for which way is up. Here, we set the
// eye five units back along the z-axis and up three units, look at the
// origin, and define "up" to be in the y-direction.
Vector3 CameraPosition = new Vector3(-5, 0, 0);
Vector3 CameraTarget = new Vector3(0, 0, 0);
Vector3 CameraUpVector = new Vector3(0, 1, 0);

device.Transform.View = Matrix.LookAtLeftHanded(CameraPosition, CameraTarget, CameraUpVector);

// For the projection matrix, we set up a perspective transform (which
// transforms geometry from 3D view space to 2D viewport space, with
// a perspective divide making objects smaller in the distance). To build
// a perpsective transform, we need the field of view (1/4 pi is common),
// the aspect ratio, and the near and far clipping planes (which define at
// what distances geometry should be no longer be rendered).
float FieldOfView = (float)(Math.PI / 4);
float aspectRatio = 1.0f;
float zNearPlane = 1.0f;
float zFarPlane = 1000.0f;
device.Transform.Projection = Matrix.PerspectiveFieldOfViewLeftHanded(FieldOfView
, aspectRatio
, zNearPlane
, zFarPlane);
}

private void SetupLights()
{
System.Drawing.Color col = System.Drawing.Color.White;
device.Lights[0].Enabled = true; // Turn it on

// Set up a material.
mtrl = new Direct3D.Material();
mtrl.Diffuse = col;
mtrl.Ambient = col;
device.Material = mtrl;



// Finally, turn on some ambient light.
// Ambient light is light that scatters and lights all objects evenly.
device.RenderState.CullMode = Cull.None;
device.RenderState.Ambient = System.Drawing.Color.White;
//device.RenderState.FillMode = FillMode.WireFrame;
//device.RenderState.FillMode = FillMode.Point;
device.RenderState.FillMode = FillMode.Solid;

}

private void Render()
{
if (device == null)
return;

if (pause)
return;

//Clear the backbuffer to a blue color
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);

//Begin the scene
device.BeginScene();
//###############################################


SetupMatrices();

for (int i = 0; i < meshMaterials.Length; i++)
{
device.Material = meshMaterials[i];
device.SetTexture(0, meshTextures[i]);
myMesh.DrawSubset(i);
}


//###############################################
//End the scene
device.EndScene();
device.Present();

}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
this.Render(); // Render on painting
}
protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
{
if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
this.Dispose(); // Esc was pressed
}

protected override void OnResize(System.EventArgs e)
{
pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);
}

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
using (Meshes frm = new Meshes())
{
if (!frm.InitializeGraphics()) // Initialize Direct3D
{
MessageBox.Show("Could not initialize Direct3D. Will exit.");
return;
}
frm.Show();

// While the form is still valid, render and process messages
while (frm.Created)
{
frm.Render();
Application.DoEvents();
}
}
}
}
}

Share this post


Link to post
Share on other sites
Oh yeah - I see - didn't know - thanks :)
And thanks for actually implementing it.

[Edited by - Moni on June 14, 2006 10:12:23 AM]

Share this post


Link to post
Share on other sites

This topic is 4201 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this