Thursday, 4 October 2012

Reason for Recent Radio Silence…

Hello to anyone still following this blog. I just thought I should explain my (more than usual) silence over the last few months. In November we found out that my Mother is terminally ill with cancer. This has made it difficult for me to get into writing posts and helping out as much as I normally do on the site and around the community. As you can imagine this blog and other none family related things have been low on my list of priorities. My mother is still with us, but very ill now.

With that said, I do have some items I want to start writing up and posting, it’s just a matter of me getting time and motivation to do that. I have a few bits to write on WP7 development, I have been doing some Silverlight at work and have a few things to share about that as well as some more GPU postings, from ST:Excalibur to DX10/11.

I hope I have not ruined your day with my sad news, but I just wanted to let those that do follow the blog what’s going on.

XNA a Kinect Component

So, I have been playing about with the Kinect Beta, having finally got a power supply for my Kinect sensor (I got a Slim XBox). I am not doing a great deal with this post, just thought if you have a sensor you might want to start playing with it in XNA, so I have created a component you can use to get the video, depth and skeletal data back from the device.


Forgive the state of my living room, but this shows the video and depth data being retrieved from the device, notice the FPS is at 30, the project will run at 60, but the sensor runs at 30, so it gets a bit jittery if you are running at 60 FPS, so I have restricted it to 30.
So here is the simple component you can use to get data back from the device.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Microsoft.Research.Kinect.Nui;
using Microsoft.Research.Kinect.Audio;

namespace KinectXNA
{
    public class KinectXNAComponent : GameComponent
    {
        public Runtime nui;
        public Texture2D colorMap;
        public Texture2D depthMap;
        public List<Point> renderJoints = new List<Point>();

        const int RED_IDX = 2;
        const int GREEN_IDX = 1;
        const int BLUE_IDX = 0;
        byte[] depthFrame32 = new byte[320 * 240 * 4];

        public ImageResolution VideoStreamSize = ImageResolution.Resolution640x480;
        public ImageResolution DepthStreamSize = ImageResolution.Resolution320x240;       

        public Point depthResolution = new Point();
        public Point videoResolution = new Point();

        public KinectXNAComponent(Game game)
            : base(game)
        {
            game.Services.AddService(this.GetType(), this);
            game.Components.Add(this);
        }

        public override void Initialize()
        {
            base.Initialize();

            nui = new Runtime();
            nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor);

            nui.VideoStream.Open(ImageStreamType.Video, 2, VideoStreamSize, ImageType.Color);
            nui.DepthStream.Open(ImageStreamType.Depth, 2, DepthStreamSize, ImageType.DepthAndPlayerIndex);

            if (depthResolution == new Point())
            {
                switch (nui.DepthStream.Resolution)
                {
                    case ImageResolution.Resolution1280x1024:
                        depthResolution = new Point(1280, 1024);
                        depthFrame32 = new byte[1280 * 1024 * 4];
                        break;
                    case ImageResolution.Resolution320x240:
                        depthResolution = new Point(320, 240);
                        depthFrame32 = new byte[320 * 240 * 4];
                        break;
                    case ImageResolution.Resolution640x480:
                        depthResolution = new Point(640, 480);
                        depthFrame32 = new byte[640 * 480 * 4];
                        break;
                    case ImageResolution.Resolution80x60:
                        depthResolution = new Point(80, 60);
                        depthFrame32 = new byte[80 * 60 * 4];
                        break;
                }
            }

            if (videoResolution == new Point())
            {
                switch (nui.VideoStream.Resolution)
                {
                    case ImageResolution.Resolution1280x1024:
                        videoResolution = new Point(1280, 1024);
                        break;
                    case ImageResolution.Resolution320x240:
                        videoResolution = new Point(320, 240);
                        break;
                    case ImageResolution.Resolution640x480:
                        videoResolution = new Point(640, 480);
                        break;
                    case ImageResolution.Resolution80x60:
                        videoResolution = new Point(80, 60);
                        break;
                }
            }

           
            nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady);
            nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);
            nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_ColorFrameReady);
        }

        void nui_ColorFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            PlanarImage Image = e.ImageFrame.Image;

            if (colorMap == null)
                colorMap = new Texture2D(Game.GraphicsDevice, Image.Width, Image.Height, false, SurfaceFormat.Color);

            byte[] data = Image.Bits;

            // Flip the R with the B
            for (int b = 0; b < data.Length; b+=4)
            {
                byte tmp = data[b];
                data[b] = data[b + 2];
                data[b + 2] = tmp;
            }

            colorMap.SetData<byte>(data);
        }

        void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            PlanarImage Image = e.ImageFrame.Image;

            if (depthMap == null)
                depthMap = new Texture2D(Game.GraphicsDevice, Image.Width, Image.Height, false, SurfaceFormat.Color);

            depthMap.SetData<byte>(convertDepthFrame(Image.Bits));           
        }
       
        // Converts a 16-bit grayscale depth frame which includes player indexes into a 32-bit frame
        // that displays different players in different colors
        byte[] convertDepthFrame(byte[] depthFrame16)
        {
            for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)
            {
                int player = depthFrame16[i16] & 0x07;
                int realDepth = (depthFrame16[i16 + 1] << 5) | (depthFrame16[i16] >> 3);
                // transform 13-bit depth information into an 8-bit intensity appropriate
                // for display (we disregard information in most significant bit)
                byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));

                depthFrame32[i32 + RED_IDX] = 0;
                depthFrame32[i32 + GREEN_IDX] = 0;
                depthFrame32[i32 + BLUE_IDX] = 0;

                // choose different display colors based on player
                switch (player)
                {
                    case 0:
                        depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 2);
                        break;
                    case 1:
                        depthFrame32[i32 + RED_IDX] = intensity;
                        break;
                    case 2:
                        depthFrame32[i32 + GREEN_IDX] = intensity;
                        break;
                    case 3:
                        depthFrame32[i32 + RED_IDX] = (byte)(intensity / 4);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                        break;
                    case 4:
                        depthFrame32[i32 + RED_IDX] = (byte)(intensity);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 4);
                        break;
                    case 5:
                        depthFrame32[i32 + RED_IDX] = (byte)(intensity);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 4);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                        break;
                    case 6:
                        depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
                        break;
                    case 7:
                        depthFrame32[i32 + RED_IDX] = (byte)(255 - intensity);
                        depthFrame32[i32 + GREEN_IDX] = (byte)(255 - intensity);
                        depthFrame32[i32 + BLUE_IDX] = (byte)(255 - intensity);
                        break;
                }
            }
            return depthFrame32;
        }
       
        void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            SkeletonFrame skeletonFrame = e.SkeletonFrame;
           
            renderJoints.Clear();
            foreach (SkeletonData data in skeletonFrame.Skeletons)
            {
                if (SkeletonTrackingState.Tracked == data.TrackingState)
                {
                    foreach (Joint joint in data.Joints)
                    {
                        renderJoints.Add(getDisplayPosition(joint));
                    }
                }
            }
        }

        private Point getDisplayPosition(Joint joint)
        {
            if (depthResolution != new Point() && videoResolution != new Point())
            {
                float depthX, depthY;
                nui.SkeletonEngine.SkeletonToDepthImage(joint.Position,
                    out depthX, out depthY);

                depthX = Math.Max(0, Math.Min(depthX * depthResolution.X, depthResolution.X));
                depthY = Math.Max(0, Math.Min(depthY * depthResolution.Y, depthResolution.Y));

                int colorX, colorY;
                ImageViewArea iv = new ImageViewArea();
                // only ImageResolution.Resolution640x480 is supported at this point
                nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(nui.VideoStream.Resolution, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY);

                return new Point((int)(Game.GraphicsDevice.Viewport.Width * colorX / videoResolution.X), (int)(Game.GraphicsDevice.Viewport.Height * colorY / videoResolution.Y));
            }
            else
                return new Point();
        }
    }
}
So, provided you have a sensor and have the SDK installed you can instantiate the component like this:
            kinect = new KinectXNAComponent(this);
You can then render the texture data coming back from the sensor in your draw call like this:
            spriteBatch.Begin(SpriteSortMode.Immediate,BlendState.Opaque);

            if (kinect.depthMap != null)
            {
                spriteBatch.Draw(kinect.depthMap, new Rectangle(GraphicsDevice.Viewport.Width / 2, 0, GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2), Color.White);
            }

            if (kinect.colorMap != null)
            {
                GraphicsDevice.VertexSamplerStates[0] = SamplerState.LinearClamp;
                spriteBatch.Draw(kinect.colorMap, new Rectangle(0, 0, GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2), Color.White);
            }

            if (kinect.renderJoints != null)
            {
                int sc = kinect.renderJoints.Count;
                for (int s = 0; s < sc; s++)
                {
                    spriteBatch.Draw(jointImage, new Rectangle(kinect.renderJoints[s].X, kinect.renderJoints[s].Y, 8, 8), Color.Gold);
                }
            }
           
            spriteBatch.End();
What I love about this, is when you look at the depth data output with two people in it is actually able to differentiate (thanks to the MS method I got of the SDK) between the two different people, it actually colors them different in the map!! ACE!!
In that above sample I am also rendering the joints to the screen, but not to just the video out put but across the whole screen, you will see what I mean if you use it.
I am loving playing with this device, so hope to do some more XNA/Kinect cross over posts :D I am also hoping to put up some sample projects like I have been with my pure XNA samples. Anyway, hope you find this little start useful.




SSAO, a start

So, have now been playing with SSAO (Screen Space Ambient Occlusion) after finding this great article on GameDev.net
http://www.gamedev.net/page/resources/_/reference/programming/140/lighting-an...
I am sure it's still not 100% right, but it has me on the right road I think.

Also, thanks to manzanotti for the music, it's a refreshing change :)

Friday, 13 July 2012

Crepuscular (God) Rays and Web UI Sample

OK, this is two samples rolled into one, the first part of this sample will cover the post processing effect of Crepuscular rays I have created in XNA based on the GPU Gems 3 article Volumetric Light Scattering as a Post-Process. The second part of this post covers the Web UI that is used in the sample. I initially intended to give a short talk on it at the September 2011 XNA-UK meeting but we had a great talk from the guys over at IndieCity.com and so I never got around to it.
Crepuscular Rays Effect Overview
So, after reading the GPU Gems article I thought it should be easy to get the effect working in my Post Processing framework, which, if you missed it, I posted the source for a while ago, have made a few changes in my latest version, but that framework should still fly. I also thought that I could use my existing sun post process, again, if you missedthat, you can find it on stg conker, and incorporate it into the effect. So, the steps used to create this effect are, render the sun to a render target, black out any occluded pixels in that image by matching them against the depth buffer (created when you rendered the scene), pass this image onto the GPU Gems god ray pixel shader, use a bright pass pixel shader (taken from my Bloom shader) to brighten the rays, then render this final texture and blend it back with the scene image.
All in all it’s a 5 pass effect, this could be reduced by having the occlusion pass in with the rendering of the original sun/light source pass, and negating the bright pass. So, lets get into the shaders, probably wont post any C# here as you have the March 2011 talk and code I gave to fall back on, a change you will notice is that I have moved away from using SpriteBatch to render the RT’s as this restricted the post processing framework to Shader model 2.
LightSourceMask.fx (or the old Sun shader tidied up a bit)
#include "PPVertexShader.fxh"
float3 lightPosition;
float4x4 matVP;
float2 halfPixel;
float SunSize = 1500;
texture flare;
sampler Flare = sampler_state
{
    Texture = (flare);
    AddressU = CLAMP;
    AddressV = CLAMP;
};
float4 LightSourceMaskPS(float2 texCoord : TEXCOORD0 ) : COLOR0
{
    texCoord -= halfPixel;
    // Get the scene
    float4 col = 0;
    // Find the suns position in the world and map it to the screen space.
    float4 ScreenPosition = mul(lightPosition,matVP);
    float scale = ScreenPosition.z;
    ScreenPosition.xyz /= ScreenPosition.w;
    ScreenPosition.x = ScreenPosition.x/2.0f+0.5f;
    ScreenPosition.y = (-ScreenPosition.y/2.0f+0.5f);
    // Are we lokoing in the direction of the sun?
    if(ScreenPosition.w > 0)
    {      
        float2 coord;
        float size = SunSize / scale;
        float2 center = ScreenPosition.xy;
        coord = .5 - (texCoord - center) / size * .5;
        col += (pow(tex2D(Flare,coord),2) * 1) * 2;                      
    }
    return col;  
}
technique LightSourceMask
{
    pass p0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 LightSourceMaskPS();
    }
}
You can see at the top there a reference to aPPVertexShader.fxh, this is just a header that has the vertex shader in it, I do this so I don’t have to repeat the shader in shaders that share the same vertex shader.
So, like in the sun shader, we find the point in world space of the light source and render the texture.
So we end up with an image like this:


LightSceneMask.fx
#include "PPVertexShader.fxh"
float3 lightPosition;
float4x4 matVP;
float4x4 matInvVP;
float2 halfPixel;
sampler2D Scene: register(s0){
    AddressU = Mirror;
    AddressV = Mirror;
};
texture depthMap;
sampler2D DepthMap = sampler_state
{
    Texture = <depthMap>;
    MinFilter = Point;
    MagFilter = Point;
    MipFilter = None;
};
float4 LightSourceSceneMaskPS(float2 texCoord : TEXCOORD0) : COLOR0
{
    float depthVal = 1 - (tex2D(DepthMap, texCoord).r);
    float4 scene = tex2D(Scene,texCoord);
    float4 position;
    position.x = texCoord.x * 2.0f - 1.0f;
    position.y = -(texCoord.y * 2.0f - 1.0f);
    position.z = depthVal;
    position.w = 1.0f;
    // Pixel pos in the world
    float4 worldPos = mul(position, matInvVP);
    worldPos /= worldPos.w;
    // Find light pixel position
    float4 ScreenPosition = mul(lightPosition, matVP);
    ScreenPosition.xyz /= ScreenPosition.w;
    ScreenPosition.x = ScreenPosition.x/2.0f+0.5f;
    ScreenPosition.y = (-ScreenPosition.y/2.0f+0.5f);
    // If the pixel is infront of the light source, blank it out..
    if(depthVal < ScreenPosition.z - .00025)
        scene = 0;
    return scene;
}
technique LightSourceSceneMask
{
    pass p0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 LightSourceSceneMaskPS();
    }
}
In this shader we take the renderer light scene and then black out the pixels that are occluded by objects in the scene, which then gives us an image like this:


LightRays.fx
#include "PPVertexShader.fxh"
#define NUM_SAMPLES 128
float3 lightPosition;
float4x4 matVP;
float2 halfPixel;
float Density = .5f;
float Decay = .95f;
float Weight = 1.0f;
float Exposure = .15f;
sampler2D Scene: register(s0){
    AddressU = Clamp;
    AddressV = Clamp;
};
float4 lightRayPS( float2 texCoord : TEXCOORD0 ) : COLOR0
{
    // Find light pixel position
    float4 ScreenPosition = mul(lightPosition, matVP);
    ScreenPosition.xyz /= ScreenPosition.w;
    ScreenPosition.x = ScreenPosition.x/2.0f+0.5f;
    ScreenPosition.y = (-ScreenPosition.y/2.0f+0.5f);
    float2 TexCoord = texCoord - halfPixel;
    float2 DeltaTexCoord = (TexCoord - ScreenPosition.xy);
    DeltaTexCoord *= (1.0f / NUM_SAMPLES * Density);
    DeltaTexCoord = DeltaTexCoord * clamp(ScreenPosition.w * ScreenPosition.z,0,.5f);
    float3 col = tex2D(Scene,TexCoord);
    float IlluminationDecay = 1.0;
    float3 Sample;
    for( int i = 0; i < NUM_SAMPLES; ++i )
    {
        TexCoord -= DeltaTexCoord;
        Sample = tex2D(Scene, TexCoord);
        Sample *= IlluminationDecay * Weight;
        col += Sample;
        IlluminationDecay *= Decay;          
    }
    return float4(col * Exposure,1);
    if(ScreenPosition.w > 0)
        return float4(col * Exposure,1) * (ScreenPosition.w * .0025);
    else
        return 0;
}
technique LightRayFX
{
    pass p0
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 lightRayPS();
    }
}
As you can see, this is pretty much the same shader in the GPU Gems article, but we calculate the onscreen light source position in the shader. This gives an image like this:


Pretty eh :D
BrightPass.fx
#include "PPVertexShader.fxh"
uniform extern float BloomThreshold;
float2 halfPixel;
sampler TextureSampler : register(s0);
float4 BrightPassPS(float2 texCoord : TEXCOORD0) : COLOR0
{
    texCoord -= halfPixel;
    // Look up the original image color.
    float4 c = tex2D(TextureSampler, texCoord);
    // Adjust it to keep only values brighter than the specified threshold.
    return saturate((c - BloomThreshold) / (1 - BloomThreshold));
}
technique BloomExtract
{
    pass P0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 BrightPassPS();
    }
}
This shader just takes the scene and based on a threshold brightens it up like this:


SceneBlend.fx
#include "PPVertexShader.fxh"
float2 halfPixel;
sampler2D Scene: register(s0){
    AddressU = Mirror;
    AddressV = Mirror;
};
texture OrgScene;
sampler2D orgScene = sampler_state
{
    Texture = <OrgScene>;
    AddressU = CLAMP;
    AddressV = CLAMP;
};
float4 BlendPS(float2 texCoord : TEXCOORD0 ) : COLOR0
{
    texCoord -= halfPixel;
    float4 col = tex2D(orgScene,texCoord) * tex2D(Scene,texCoord);
    return col;
}
float4 AditivePS(float2 texCoord : TEXCOORD0 ) : COLOR0
{
    texCoord -= halfPixel;
    float4 col = tex2D(orgScene,texCoord) + tex2D(Scene,texCoord);
    return col;
}
technique Blend
{
    pass p0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 BlendPS();
    }
}
technique Aditive
{
    pass p0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 AditivePS();
    }
}
And Finally we blend this with the original scene, the Additive technique is used in this sample, giving a final image like this:


So there you have the god ray post process.
Web UI
OK, so now onto the UI, I am using a third party library call Awesomium, and it is indeed Awesome, well I think so. It is basically a web renderer, you give it a url, it renders it and spits out a texture, we can then render this texture. Now, if it just did that it would not be much use, thankfully we can wire up call backs to it and pass mouse and keyboard events to it. This means we can, through our game interact with the web page. It means you can create all your UI’s in HTML using great stuff like JQuery and any other web tech you can pile into your game. Now this sample has all it’s web UI local but you could serve the entire game UI from your site.
I first came across this tool while working on ST:Excalibur as we use it to drive the UI and was really impressed with it, so thought I would do a version for XNA. In order to use this tool you need to download the Awsomium source and compile the AwesmiumSharp project, once you have that there are a number of assemblies you will need from that build adding to your project, all the details on how to do this can be found in the ppt what comes with this sample. Once you have all that in place you can create a DrawableGameComponent like this one to handle your Web UI
    public class AwesomiumUIManager : DrawableGameComponent
    {
        public int thisWidth;
        public int thisHeight;
        protected Effect webEffect;
        public WebView webView;
        public Texture2D webRender;
        protected int[] webData;
        public bool TransparentBackground = true;
        protected SpriteBatch spriteBatch
        {
            get { return (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); }
        }
        public string URL;
        public AwesomiumUIManager(Game game, string baseUrl)
            : base(game)
        {
            URL = baseUrl;
            DrawOrder = int.MaxValue;
        }
        protected override void LoadContent()
        {
            WebCore.Config config = new WebCore.Config();
            config.enableJavascript = true;
            config.enablePlugins = true;
            WebCore.Initialize(config);
            thisWidth = Game.GraphicsDevice.PresentationParameters.BackBufferWidth;
            thisHeight = Game.GraphicsDevice.PresentationParameters.BackBufferHeight;
            webView = WebCore.CreateWebview(thisWidth, thisHeight);
            webRender = new Texture2D(GraphicsDevice, thisWidth, thisHeight, false, SurfaceFormat.Color);
            webData = new int[thisWidth * thisHeight];
            webEffect = Game.Content.Load<Effect>("Shaders/webEffect");
            ReLoad();
        }
        public virtual void LoadFile(string file)
        {
            LoadURL(string.Format("file:///{0}\\{1}", Directory.GetCurrentDirectory(), file).Replace("\\", "/"));
        }
        public virtual void LoadURL(string url)
        {
            URL = url;
            webView.LoadURL(url);
            webView.SetTransparent(TransparentBackground);
            webView.Focus();
        }
        public virtual void ReLoad()
        {
            if (URL.Contains("http://") || URL.Contains("file:///"))
                LoadURL(URL);
            else
                LoadFile(URL);
        }
        public virtual void CreateObject(string name)
        {
            webView.CreateObject(name);
        }
        public virtual void CreateObject(string name, string method, WebView.JSCallback callback)
        {
            CreateObject(name);
            webView.SetObjectCallback(name, method, callback);
        }
        public virtual void PushData(string name, string method, params JSValue[] args)
        {
            webView.CallJavascriptFunction(name, method, args);
        }
        public void LeftButtonDown()
        {
            webView.InjectMouseDown(MouseButton.Left);
        }
        public void LeftButtonUp()
        {
            webView.InjectMouseUp(MouseButton.Left);
        }
        public void MouseMoved(int X, int Y)
        {
            webView.InjectMouseMove(X, Y);
        }
        public void ScrollWheel(int delta)
        {
            webView.InjectMouseWheel(delta);
        }
        public void KeyPressed(Keys key)
        {
            WebKeyboardEvent keyEvent = new WebKeyboardEvent();
            keyEvent.type = WebKeyType.Char;
            keyEvent.text = new ushort[] { (ushort)key, 0, 0, 0 };
            webView.InjectKeyboardEvent(keyEvent);
        }
        public override void Update(GameTime gameTime)
        {
            WebCore.Update();
            if (webView.IsDirty())
            {
                Marshal.Copy(webView.Render().GetBuffer(), webData, 0, webData.Length);
                webRender.SetData(webData);
            }
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            if (webRender != null)
            {
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise);
                webEffect.CurrentTechnique.Passes[0].Apply();
                spriteBatch.Draw(webRender, new Rectangle(0, 0, Game.GraphicsDevice.Viewport.Width, Game.GraphicsDevice.Viewport.Height), Color.White);
                spriteBatch.End();
                Game.GraphicsDevice.Textures[0] = null;
            }
        }
        protected void SaveTarget()
        {
            FileStream s = new FileStream("UI.jpg", FileMode.Create);
            webRender.SaveAsJpeg(s, webRender.Width, webRender.Height);
            s.Close();
        }
    }
So, this call enables you to use the Awesomium WebView object, create objects that reside on the UI that can then call back into our C# code as well as access functions on the UI that we can call from our C# code. Again, the set up of these elements are described in the ppt and accompanying solution.
Effectively, i have created a html page in the Content project, made sure all it’s elements are not compiled and are copied over if newer, I can then tell my AwesomiumUIManager to go and get that html page in the constructor like this
            HUD = new AwesomiumUIManager(this, "Content\\UI\\MyUI.html");
In this sample I am not adding it to the Components list as I don’t want it included in the post processing, so I have to initialize, update and draw it my self in the respective Game methods.
In the Game.LoadContent method I set up two script objects in the HUD, these can then be called from the html to pass data back to my C# code, one for click events and one for the slider events.
            HUD.CreateObject("UIEventmanager", "click", webEventManager);
            HUD.CreateObject("UIEventmanager", "slide", webEventManager);
In my Game.Update I can push data back to the UI, the push method calls the two methods passing the param to them
            HUD.PushData("", "ShowSunPosition", new JSValue(sunPosition.X), new JSValue(sunPosition.Y), new JSValue(sunPosition.Z));
            HUD.PushData("", "SetVars", new JSValue(GodRays.BrightThreshold), new JSValue(GodRays.Decay), new JSValue(GodRays.Density), new JSValue(GodRays.Exposure), new JSValue(GodRays.Weight));
Also in the Game.Update method I have to ensure the WebView is getting the mouse and keyboard events
            // Manage the mouse and keyboard for the UI
            if (thisMouseState.LeftButton == ButtonState.Pressed)
                HUD.LeftButtonDown();
            if (thisMouseState.LeftButton == ButtonState.Released && lastMouseState.LeftButton == ButtonState.Pressed)
                HUD.LeftButtonUp();
            HUD.MouseMoved(thisMouseState.X, thisMouseState.Y);
            HUD.ScrollWheel(thisMouseState.ScrollWheelValue - lastMouseState.ScrollWheelValue);
            if (thisKBState.GetPressedKeys().Length > 0)
                HUD.KeyPressed(thisKBState.GetPressedKeys()[0]);
You may also notice in the Draw call of the AwesomiumUIManager I am using a shader, this is because  the texture returned from the WebView is in bgra format so I have to switch the channels around in a shader like this
uniform extern texture sceneMap;
sampler screen = sampler_state
{
    texture = <sceneMap>;  
};
struct PS_INPUT
{
    float2 TexCoord    : TEXCOORD0;
};
float4 Render(PS_INPUT Input) : COLOR0
{
    float4 col = tex2D(screen, Input.TexCoord).bgra;  
    return col;
}
technique PostInvert
{
    pass P0
    {
        PixelShader = compile ps_2_0 Render();
    }
}
So, that’s the end of this mammoth post, hope you find the content useful, as ever, let me know if you have any questions or issues. Oh, and before anyone else points it out, I know my web skill’s ain’t all that :P
The code samples for both this post, and the talk I was to give can be found here.




























Crepuscular Rays Post Process

So, I have been meaning to do god rays for ages and while doing the ST Excalibur post processing framework in SlimDX, thought I would give them a go.
This clip is of my XNA engine, I still have an issue or two with the render in SlimDX, but I am sure, once I have time, I'll write up an XNA blog post about how I did this and the references I used.
Sorry the clip is a bit messy, and forgive my jerky camera work, hope you like how it looks.

September 2011 Talk Preview

OK, so the next XNA UK meeting will be on the 7th of September and I will be giving a short talk on using Web UI's in XNA, I thought I would put up a quick post with a picture to show the sort of thing you will be able to do.


See you in September :D

XNA UK UG April 2011 Talk

I have finally got the source and ppt up for the talk I gave on the 6th of April.

It was just covering the UI I created for the talk the month before, have expanded on it slightly so that you can skin it.

You can find the down load here.