in

This site is all about the amazingly cool stuff you can do with VB.NET.

The VB.NET XNA Project

  • Iowa Code Camp - XNA Session

    This weekend, I'm speaking at the Iowa Code Camp on "What's This XNA Thing I Keep Hearing About?"

    I'm covering some basic XNA 2.0 concepts and a couple demos.  I'm doing the Spinning Bouncing Ball demo in C# and the Chase Camera demo in VB.NET.

    I was going to show off the Race Car game demo, but my laptop gets about 3 frames per second in that game.

    Posted May 03 2008, 04:21 PM by admin with no comments
    Filed under: , ,
  • Bresenham's Line Algorithm - VB XNA style

     
       1:  Imports Microsoft.Xna.Framework
       2:  Imports System.Math
       3:   
       4:  Public Class Utils
       5:      ''' <summary>
       6:      ''' This function uses Bresenham's Line Algorithm to find the 
       7:      ''' most direct path between two points on a 2D grid and stores 
       8:      ''' all the points in a list.
       9:      ''' </summary>
      10:      ''' <param name="StartPosition">Starting X,Y coordinates</param>
      11:      ''' <param name="EndPosition">Starting X,Y coordinates</param>
      12:      ''' <returns>List(Of Vector2)</returns>
      13:      ''' <remarks></remarks>
      14:      Public Function DeterminePath(ByVal StartPosition As Vector2, _
      15:                     ByVal EndPosition As Vector2) As List(Of Vector2)
      16:          Dim myPoint As Vector2 = StartPosition ' current point
      17:          Dim myPath As New List(Of Vector2) ' collection of path points
      18:   
      19:          ' Get the difference between 2 points
      20:          Dim deltaX As Integer = EndPosition.X - StartPosition.X
      21:          Dim deltaY As Integer = EndPosition.Y - StartPosition.Y
      22:          Dim leftover As Integer
      23:   
      24:          ' Figure out direction based on the +/- value of the deltas
      25:          Dim dirX As Integer = IIf(deltaX < 0, -1, 1)
      26:          Dim dirY As Integer = IIf(deltaY < 0, -1, 1)
      27:   
      28:          ' Get absolute, we'll decide whether to add/subtract later 
      29:          deltaX = Abs(deltaX)
      30:          deltaY = Abs(deltaY)
      31:   
      32:          ' Uncomment this to add the first point to the path (list)
      33:          ' myPath.Add(myPoint)
      34:   
      35:          ' iterate through whichever axis is longest
      36:          If deltaX > deltaY Then
      37:              leftover = (deltaY * 2) - deltaX
      38:              While myPoint.X <> EndPosition.X
      39:                  If leftover >= 0 Then
      40:                      myPoint.Y = myPoint.Y + dirY
      41:                      leftover = leftover - deltaX
      42:                  End If
      43:                  myPoint.X = myPoint.X + dirX
      44:                  leftover = leftover + deltaY
      45:                  myPath.Add(myPoint)
      46:              End While
      47:          Else
      48:              leftover = (deltaX * 2) - deltaY
      49:              While myPoint.Y <> EndPosition.Y
      50:                  If leftover >= 0 Then
      51:                      myPoint.X = myPoint.X + dirX
      52:                      leftover = leftover - deltaY
      53:                  End If
      54:                  myPoint.Y = myPoint.Y + dirY
      55:                  leftover = leftover + deltaX
      56:                  myPath.Add(myPoint)
      57:              End While
      58:          End If
      59:   
      60:          Return myPath
      61:      End Function
      62:  End Class

    While I was working on my EnemyAI demo for code camp, I needed a way to plot the most efficient line between two points. Bresenham's Line Drawing Algorithm is pretty much the industry standard for lines, but I couldn't find a VB implementation I liked, so I wrote my own. I went ahead and used the Vector2 data type from XNA, but if you aren't doing XNA you could easily create a custom data type that contains a pair of integer values.

    The way you use this is pretty simple.  Given two sets of coordinates (begin and end position), this will return a collection of Vector2s.

     

    How it works

    This algorithm compares the delta between the starting and ending X coordinate, and the starting and ending Y coordinate. In order to have the straightest possible line (that isn't purely horizontal or purely vertical) we have a rule that you can't have adjacent pixels on the shortest axis. This provides a much smoother transition along the longer axis.

    Posted Apr 15 2008, 07:22 PM by admin with no comments
    Filed under: , ,
  • Enemy AI

    After December's marathon, I'm taking a short break from converting the Creators Club samples to VB so I can work on something else of interest.

    I'm working on an AI techniques presentation for my XNA user group. I'm doing it all in VB (because I can) and hopefully will turn it into an article once it's all done. I'm using a few XNA datatypes (like Vector2 and Texture2D) so technically its XNA related.

    So the VB Samples will return in a few weeks, and of course, I'll post the AI stuff up here as well when it's done.

    Posted Jan 14 2008, 10:02 PM by admin with no comments
    Filed under: , , ,
  • Heightmap Collision Sample posted

    This one was a real pain, but it's the second sample I've done with a custom ContentProcessor. Grab the vb.net code here.

    Heightmap Collision

    This sample demonstrates how to move objects along a heightmap. It is based on the Generated Geometry sample, which creates a landscape from a bitmap. We build upon that sample, showing how to quickly calculate the height of any point on that heightmap.

    Sample Overview

    The Generated Geometry sample introduced the concept of a heightmap. In that sample, a content processor reads a bitmap and uses the intensity of its pixels as height values on a terrain. A logical next step to this technique is to place objects on the terrain. This sample demonstrates that technique by placing a ball on the generated terrain.

    Minimum Shader Profile

    Vertex Shader Model 1.1 Pixel Shader Model 1.1

    Sample Controls

    This sample uses the following keyboard and gamepad controls.

    Action Keyboard Control Gamepad Control
    Move the ball.

    UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW

    or W, A, S, and D

    Left thumb stick or D-Pad
    Exit the sample. ESC or ALT+F4 BACK

    How the Sample Works

    This sample is based on the Generated Geometry sample. To support the added functionality, the original sample has been modified in several ways. For clarity, the sky has also been removed.

    Changes to the Terrain Processor

    The TerrainProcessor has been modified slightly for this new sample. First, the algorithm that gives the vertices their XZ position has been modified slightly from the generated geometry sample. In the new version, the positions are calculated so that the heightmap is always centered around the origin. This change simplifies the math at run time.

    In addition, the processor is responsible for creating a class called HeightMapInfoContent, which contains data about the height of the points of the heightmap, as well as the distance between them. This information is attached to the finished terrain model's .Tag property, and will be read when the game loads.

    Finally, the SkyProcessor and SkyContent classes have been removed from the pipeline assembly.

    Rolling Around on the Terrain

    The user is now given direct control over a small sphere, which can roll over the terrain. The camera, which used to move in a fixed circular pattern around the terrain, is now tied to follow the sphere. To make it more obvious which code is new, we removed the Sky class from the sample.

    To keep both the sphere and the camera on top of the terrain, we use the GetHeight function on HeightMapInfo. GetHeight is the most complicated part of this sample. This function accepts a position as an argument and returns the height of the heightmap at that position.

    In this diagram, we are trying to calculate the height of the red circle. The grid represents our heightmap. In this example, our heightmap is 4×4. Note that this means that the heightmap's width and height are only 3 * TerrainScale. We'll call the white space between gridlines "cells."

    To calculate the red circle's height at any point on a cell, we use bilinear interpolation. This is a lot simpler than it sounds. To understand how it works, let's first examine the simpler case, linear interpolation.

    In this diagram, we know the (x,y) coordinates of two points on the red line: (2,1) and (8,4). We want to find out the y-coordinate at x = 4. (The diagram isn't to scale at all, so don't get out your rulers; you'll be disappointed.) To find the missing value, we can use linear interpolation. There are several different ways to do this. We'll use a method that, when ported to C# code, lets us make use of MathHelper.Lerp (linear interpolate) and will be fairly efficient.

    The process is relatively straightforward. We know the x-coordinates of all three points, and we can see that the x-coordinate of the center point, which we are trying to find, is 1/3 of the way between the x-coordinate of the other two points. (In other words, the distance from x = 2 to x = 4 is 1/3 of the distance from x = 2 to x = 8.) Since the points are all on a straight line, the y-coordinate must also be 1/3 of the way.

    The distance between the two y-coordinates, y = 1 and y = 4, is 3. So, if the y-coordinate at (4,?) is 1/3 of that distance away from the point on the left, our missing value is:

    1 + (1/3) × 3 = 2

    Simple enough. Bilinear interpolation will extend that principle further.

    In this diagram, we are again on a heightmap, trying to calculate the height of our red circle. We know all of the heights at the corners of the cell, since we read those in from the bitmap, remember? We also know the x- and z-coordinates of the red circle. What we don't know is the height, y.

    So first, we do a linear interpolation on the top edge of the cell. We go from the (left,top) corner to the (left+1,top) corner, and find the height at point A. Then, we do the same thing on the bottom edge of the cell, calculating the height at point B. Now we know the heights at A and B, both of which are on a straight line with the circle! To find the height of the circle, we do one more linear interpolation between A and B, and we've got our height.

    Note that this technique is not perfect: on steep hills, it is still possible that parts of the sphere may clip through the terrain. To avoid this, we would have to perform a much more expensive collision check. Our technique, although imperfect, is inexpensive, and yields results that will suffice for many games.

    Extending the Sample

    • The camera in this sample is fairly simple. Try taking the camera from the Chase Camera sample, and putting it into this one.
    • When moving objects over a heightmap, you may also want to align them to the slope of the terrain. To do this, you'll need to calculate the heightmap's normal vectors in the TerrainProcessor. A GetNormal function would work similarly to GetHeight, except all three components of the vector would have to be interpolated separately and then renormalized. Using this normal vector, you can calculate an orientation matrix for your object.
  • nothing like vacation time to really get some work done.

    The last few days have been incredibly productive for me, in terms of cranking out conversions.

    Not only did I post several new conversions, but I also managed to wrap up two that had been giving me trouble since day one.  The Bloom PostProcess sample and the Billboard sample have both been a real pain to convert, and I had pretty much given up on both of them until just recently.

    I've learned a lot from converting some of the recent samples (I've learned something from all of them really) and something in my brain clicked just enough to make me want to revisit those two samples mentioned above and wouldn't you know it... I was able to find the problems and get them working.

    I hope to knock out the rest of the samples pretty soon so I can get started on converting the starter kits and the rest of the tutorials.

    Posted Dec 27 2007, 02:57 AM by admin with no comments
    Filed under: ,
  • Billboard Sample posted

    This is the vb.net version of the Billboard sample as seen on Creators.XNA.com.  Grab the source code here. This was another one of the more difficult samples to get working. I spent a few weeks on this one, scratching my head trying to figure out why it didnt work. I was initially worried that custom content processors couldnt be done in vb.net, but this sample proves that theory wrong.

    Billboard Sample

    This sample shows how to efficiently render large numbers of billboard sprites, using a vertex shader to perform the billboard computations entirely on the GPU.

    Sample Overview

    A billboard is a way of faking complex objects without bothering to render a full 3D model. The idea is that where the 3D object would have been, you simply render a 2D sprite with a picture of the object textured onto it. If the player moves around to look at the object from a different direction, you rotate the sprite so that it will always face toward the camera, thus preventing them from ever seeing it "edge on" and being able to tell that it is just a 2D image.

    This technique is obviously not as convincing as rendering a true 3D model would be, but it can be much faster, especially for large numbers of objects such as the field of grass shown in this sample. By moving the camera around, you can get an idea of what situations the billboards look good in, and also where the illusion starts to break down.

    Because this billboard implementation runs entirely in the vertex shader, there is no additional CPU load from rendering billboards compared to normal static geometry. This makes it feasible to render very large numbers of billboards at a good framerate.

    Minimum Shader Profile

    Vertex Shader Model 1.1 Pixel Shader Model 1.1

    Sample Controls

    Keyboard:

    • UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW = rotate camera
    • W = forward, S = backward
    • A and D = strafe
    • R = reset
    • ESC or ALT+F4 = exit

    Gamepad:

    • Right thumb stick = rotate camera
    • Left thumb stick = move camera
    • Right thumb stick press = reset camera
    • BACK = exit

    How the Sample Works

    The VegetationProcessor takes in a simple landscape mesh and randomly creates billboard polygons scattered across it. These new polygons are set to render using the Billboard.fx effect, and a number of different billboard textures and sizes are randomly selected.

    In the vertex shader, the billboard effect calculates vertex positions by examining the view matrix to make sure the billboard sprite will always face toward the camera. It also uses a per-billboard random number to make each billboard a slightly different shape, and to randomly mirror the textures on half of the billboards, adding variety to the results. Finally, it uses a sine wave pattern to make the top two vertices of each billboard sway back and forth, simulating the effect of wind on the vegetation. The amount of this swaying is controlled by an effect parameter, so some types of billboard can be set to sway more than others, or to remain entirely still.

    Billboard lighting is calculated using the normal of the underlying landscape geometry. This means that vegetation will be lighter or darker depending on the angle of the hill that it is growing on, and will always be lit consistently with the underlying surface. In a more sophisticated landscape engine, it would be possible to make each billboard sample into the landscape texture to automatically pick up an appropriate color from the ground below it.

    The billboards are rendered in two passes, using different alpha test and depth buffer settings to achieve almost correct depth sorting of alpha blended sprites without requiring them to be depth sorted on the CPU first. The comments at the end of the Billboard.fx file explain this technique in detail.

    Note that although the billboard effect is included as part of the Game Studio project file, its XNA Framework Content property has been set to false. This effect will be built automatically because it is referenced by the VegetationProcessor, so there is no need for it to be duplicated in the project. It was only included here so you can easily locate the file for viewing or editing the effect code.

  • Bloom PostProcess Sample posted

    This one took me a while to convert. There were a few hard to track down issues, but I eventually got it working. Grab the code here.

    BloomPostprocess Sample

    This sample shows how to use bloom post-processing filters to add a glow effect over the top of an existing scene.

    Sample Overview

    Bloom post-processing emulates the visual effect of bright lights and glowing objects. It does this by extracting the brightest parts of an image to a custom render target, blurring these bright areas, and then adding the blurred result back into the original image.

    Because bloom is implemented entirely as a post-process, it can easily be used over the top of any other 2D or 3D rendering techniques. In addition to the more extreme glowing effects, when used subtly it provides a useful softening that can make computer graphics look more organic and help to hide artifacts from elsewhere in your rendering. Particle systems, for example, often look better with a subtle bloom applied over the top.

    Minimum Shader Profile

    Vertex Shader Model 1.1 Pixel Shader Model 2.0

    Sample Controls

    This sample uses the following keyboard and gamepad controls.

    Action Keyboard Control Gamepad Control
    Change the bloom settings. A A
    Toggle bloom on or off. B B
    Show intermediate render target contents. X X
    Exit. ESC or ALT+F4 BACK

    How the Sample Works

    There are three stages to applying a bloom post-process.

    Pass 1 Extract the brightest parts of the scene. This uses the BloomExtract.fx pixel shader, which removes any areas darker than the specified threshold parameter value. Modifying the threshold changes the look of the bloom: higher values produce smaller and better defined glows, while smaller ones produce a softer end result. To see just the result of this first pass, press X until the overlay display shows "PreBloom". Passes 2 and 3 Blur the bright areas. This is done using the GaussianBlur.fx pixel shader—first, to blur the image horizontally, and then again to blur it vertically. The shader does multiple lookups into different parts of the source texture, and then averages the results using a Gaussian curve to weight the importance of each sample. The work is split over two passes because Shader Model 2.0 is not capable of doing enough texture lookups to give a high-quality blur along both the horizontal and vertical axis in a single pass. To see the result of these blur passes, press X until the overlay display shows "BlurredHorizontally" or "BlurredBothWays." Pass 4 Combine the blurred result with the original image. This uses the BloomCombine.fx pixel shader, which has parameters to control how much bloom to include, how much of the original image to retain, and for adjusting the color saturation of both the bloom and original base images. Tweaking these settings can produce a wide range of visual effects.

    Extending the Sample

    Rather than just choosing a fixed set of bloom settings, you could adjust these on the fly depending on what is happening in your game. Perhaps you could make the world go greyer and more blurry when the player has low health, or increase the amount of glow for a second or two when the player gets a power-up.

    You can also adjust bloom settings to mimic how the human eye responds to changing light levels. If the player is in a long tunnel, turn the bloom up very high so any areas of the outside world that are visible past the end of the tunnel will burn-out to white and have a huge glow around them. After they leave the tunnel, gradually fade the bloom down to a level where they will be able to see normally again.

    For a higher-quality bloom, you can render your world using high-dynamic-range (HDR) lighting information, using a floating-point back buffer format to store pixels even brighter than 1.0.

    The Gaussian blur used in this sample could be replaced with any other kind of filter. For example, you could use a blur pass with large sample offsets to create various kinds of lens-flare effects.

    The blur-and-combine pass is a great place to include other kinds of image manipulation. In addition to using the saturation adjustment used in this sample, you could do tone mapping here, or tint the image, or blend in a layer of noise or film grain. This infrastructure is a good starting place for other kinds of post-processing, too, opening up the non-photorealistic world of edge detection, embossing, pencil sketch rendering, and other such techniques.

    Converting to XNA Game Studio 2.0

    The most substantial changes are in the BloomComponent. Here, we must update the sample to use XNA Game Studio 2.0 revised render target APIs. First up, we need to change the type of the field resolveTarget. In version 1.0 when resolving a render target or back buffer, the destination of the resolve had to be a Texture created with the special flag ResourceUsage.ResolveTarget. Many found this pattern confusing, and in XNA Game Studio 2.0 this flag has been removed. Instead, we simply create an instance of the type ResolveTexture2D with no flags at all.

    The next change comes in BloomComponent.Draw. In this function, we used to temporarily disable the depth stencil buffer to avoid writing to it, and then would set it back when finished. This is actually not necessary. The draw function uses SpriteBatch, which draws with the depth buffer disabled. So, we do not need to do anything to "protect" the buffer: it could not have been modified anyway! In the new version of the sample, we avoid changing the depth stencil buffer because doing so leaves the current render target in an undefined state.

    Finally, we must update DrawFullscreenQuad. In the first version of this sample, the function ResolveRenderTarget was used to explicitly resolve the current renderTarget. This method is now considered obsolete and causes a compilation error if used in your code. The actual resolve operation occurs automatically when a new render target is set (for example, a call to SetRenderTarget). So, we replace the call to ResolveRenderTarget(0) with SetRenderTarget(0,nothing). This resolves renderTarget and sets the current render target back to the back buffer.

  • Color Replacement Sample posted

    Grab the vb.net code sample here.

    Color Replacement Sample

    This sample demonstrates how to render parts of an object with a user-defined color.

    Sample Overview

    When it is the Red Team versus the Blue Team or when you want to drive a car painted with your favorite color, you either need to create a texture for each color or perform color replacement programmatically. This sample implements color replacement using an artist-defined alpha map that acts similar to a spray paint stencil. The stencil enables you to have soldiers with any color armor you choose without tinting their faces the same color.

    Minimum Shader Profile

    Vertex Shader Model 1.1 Pixel Shader Model 1.1

    Sample Controls

    This sample uses the following keyboard and gamepad controls.

    Action Keyboard Control Gamepad Control
    Select the red channel (hold). R B
    Select the green channel (hold). G A
    Select the blue channel (hold). B X
    Exit the sample. ESC or Alt+F4 BACK

    How the Sample Works

    The color replacement is performed by the simple high-level shader language (HLSL) effect defined in ReplaceColor.fx. The model's diffuse texture contains the typical red, green, and blue channels for the base color, but also contains an alpha channel. The alpha channel defines which parts of the texture should be color replaced and by how much. The final color before lighting is calculated using a linear interpolation between the base diffuse color and the target color. An alpha value of zero indicates that the base color should be unchanged, while an alpha value of one indicates that the target color should completely replace the base color.

    Posted Dec 24 2007, 07:56 PM by admin with no comments
    Filed under: , , ,
  • 2D Collision Sample #3 (Transformed) posted

    Sample code for part three in the Collision Series (Transformed Collisions) has been posted here. Original tutorial posted at http://creators.xna.com

    Collision Series 3: 2D Collision with Transformed Objects

    This article explains how to perform per-pixel collision detection on sprites by using linear transformations such as rotation or scale.

    Introduction

    In the previous sample, you enhanced your simple obstacle avoidance game by adding per-pixel collisions, which are more accurate than the previously existing bounding rectangle test. The per-pixel technique presented in tutorial 2 accommodates only positioned sprites, without any other transformations. For many games, this is completely sufficient. However, if your game requires objects that are rotated, scaled, or otherwise linearly transformed, you are going to need a more sophisticated per-pixel collision test.

  • 2D Collision Sample #2 (per pixel) posted

    The VB.NET version of the per pixel collision sample. Grab it from the files section here!


    2D Collision Sample 2: Per-Pixel

    This sample demonstrates per-pixel collision detection.

    Introduction

    In the previous example, you created a simple object avoidance game using rectangle collision detection. The rectangles used were only an approximation of the blocks and person drawn into the textures. You may have noticed the exclusive use of rectangles resulted in imprecise behavior for nonrectangular objects.

    In order to achieve the desired behavior, the code must examine every overlapping pixel to determine if there is a collision. This is called per-pixel collision.

  • 2D Collision Sample #1 (Rectangle) posted

    The VB.NET version of the Rectangle Collision Sample.  Grab it from the files section here!


    2D Collision Sample 1: Rectangle

    This sample shows a simple technique for constraining motion and collision detection in 2D.

    Introduction

    Games do not need to be complex to be fun and interesting. Obstacle avoidance games consist of a character and some objects that are on a crash course towards each other. The player must avoid the oncoming objects.

  • Chase and Evade sample posted

    Another VB.NET XNA sample. You can grab it here.  This one is archived in RAR format. See here for explanation.

    Chasing and Evading Sample

    This sample shows how to implement several simple behaviors for AI, including chasing, evading, and wandering.

    Sample Overview

    This sample demonstrates chasing and evading behaviors by using three actors—a cat, a mouse, and a tank. The tank and the mouse are controlled by AI, and the cat is controlled by the player. The tank chases the cat, and the mouse runs from the cat. The tank and the mouse wander around the screen when the cat is not nearby. The "turn toward" functionality from the aiming sample makes it very simple to create these behaviors: chase, evade, and wander.

  • Chase Camera sample posted

    This was the third sample I converted, pretty basic stuff. In addition to converting from C# to VB.NET, it's also been updated for XNA 2.0.  You can grab the archive here.

    Chase Camera Sample

    This sample demonstrates how to create a simple chase camera with spring physics.

    Sample Overview

    In this sample, you can pilot a Spacewar ship from a third-person chase camera view. The motion of both the ship and the camera are governed by simple physics. When the ship speeds away, the camera pulls back. When the ship turns, the camera lags behind in the turn to show a partial profile view of the ship. When the ship stops moving, the camera gradually slides back into place.

    By pressing a button, you can compare and contrast the spring-based camera with a rigid, fixed-offset camera. You will notice that the spring-based camera provides a greater sense of speed and the ship will feel more natural to control.

  • XNA GS 2.0(beta) deployment platform bug (what causes it and how to workaround it)

    First... how to cause the problem.

    1. Create a new C# Windows Game 2.0 project in VS2005.

    If you take a look at this point, in the Configuration Manager, you will see 2 entries. One is for the Content project and the other is for whatever you named your new project. So far, so good.

    2. Add a new VB.NET Class Library project to your solution. (Make sure to add it to the solution, not create a new one.)

    Take a look in the Configuration Manager again. Now you'll see 3 entries, including a new one for the VB.NET Class Library you just added. You should also notice that the Content project's Platform has changed from x86 to Xbox 360. That's a problem.

    Fortunately, fixing it is easy enough.

    3. Click on the Active Solution Platform dropdown box and select x86 (it probably says Mixed Platforms when you look at it.)

    4. Save, Recompile, etc...  you're all set.

    FWIW, this has already been reported to Connect (you're welcome) and has been marked as fixed in the final version. After all, we are using Beta stuff, so it's not entirely unexpected.

     

     

  • Audio3D Sample Posted

    This one took a bit of reworking when the 2.0 beta bits came out, but it's all set now.  One thing that was different in 2.0 is the inclusion of a content project, instead of just a folder, and the old sound content wouldn't load in the new project, so I had to regrab the files. If you crack open the audio.xap file, you'll see the Version number is 16 now.

    One of the other snags I ran into, that may have been my own error, but took a while to figure out was that my content project was targeting an xbox instead of PC.  Once I figured that out, everything ran fine.

    Anyway, you can grab the Audio3D sample here.

    Posted Nov 23 2007, 06:21 PM by admin with no comments
    Filed under: , , ,
More Posts Next page »
Copyright 2008 - ILoveVB.NET
Powered by Community Server (Commercial Edition), by Telligent Systems