Friday, August 31, 2012

A perfect state of harmony.

I'm still homeless, I know, crazy huh, I have been homeless since January, but hey, just because you're homeless, it doesn't mean you're broke, however, I do happen to be broke. But, let all the quitters with regular jobs judge me all they want, I rest my case on the fact that I'm mortgage free, thirty years is a long time to pay for a wooden building, or in many cases a few rooms inside of a larger wooden building. Market manipulation theories aside, I'm pretty comfortable without a home and mortgage. Anyway, the point of this post is to announce that Total Commitment Games is a truly green, my computer is setup in the great outdoors, I have the wind in my hair and the sun on my face (and monitor screens). It's great, I'm living the dream, nature and game development were always meant to be together, in fact they co-exist in a state of perfect harmony, at a level far beyond any harmony imagined by the Asian mystics - a perfect state of harmony.

Tuesday, August 21, 2012

Testing Level Configurations

As a result of my endless quest to make games which don't suck, I'm spending endless time testing level configurations to find a style which doesn't suck. I blame consumers like you.

Thursday, August 16, 2012

Tutorial: Baked Light Maps for XNA using 3DS Max 2013

Special Note: For those of you without access to 3D Studio max, an educational version is available through autodesk which does not require a license provided through a school.

In this tutorial I will introduce how to bake a light map with 3D Studio Max 2013 (will also work with version 8 and up) and then render your baked light map and level geometry in XNA 4.0 using a custom HLSL shader.

The first step is of course to make your level, or making a test level. Here's mine. Notice the very simple level geometry, simple geometry significantly improves the quality of the light bake and the ease of unwrapping.




For the tutorial I exaggerated the lighting  for the sake of visual demonstration. My lights are a standard Omni and Target Spotlight, their intensity is set to 5.0, Decay is set to Inverse with Decay Start set at 10, I'm using Far Attenuation set to start at 30 and end at 80. I Enabled shadows, using a shadow map. 3D Studio Max can sometimes produces unacceptable poor shadows, especially on physically small scenes like mine, scroll down under "Shadow Map Params" and set the "Bias" to 0.01 and the Size to 1024.

Now to prepare the level for baking. The best and most reliable exports are ones with as few modifiers as possible. Collapse all of your stacks to Editable Poly. Now select whatever model you consider to be the most appropriate main model, in my case it's the ground, now attach all of the other models using Editable Poly's Attach function. When prompted with attach options, select "Match Material IDs to Material" and check "Condense Material and IDs". Now your level should be a single model and each previously separate segment will be an Selection Element.

Now generate your second UV Channel. Select the level model, now under the Modifier List, select Unwrap UVW. Change the Map Channel to 2. Annoyingly a Channel Change Warning Dialog will come up, select Abandon. Now Open the UV Editor, at the lower left of the Edit UVW window, select Face, it's the pink square in 2013.­ Now select all of your geometry in the Edit UVW window, now go up to the top of the window and select Mapping and then Flatten Mapping. A dialog will appear with flatten configuration options which are handy but the default settings are appropriate for our uses in this tutorial. In case you have changed yours, I have attached a screenshot of mine.


Click Ok.

The more complex your level gets the more inefficient texture usage becomes. More complex levels will require adjusting the Flatten options and sometimes hand placement of some UV island. Here is what my flatten looks like.



This now the second UV channel which will be used to bake the light map. You can close the window, you won't be using it again in this tutorial. Now collapse the Modifier Stack Again. To recap, your first UV Channel is for your Textures, your second UV Channel is for your light map and your model is now ready for baking.



However, before baking, we are going to export the level geometry first. For this tutorial, we're going to be using the FBX format, it's awkward and hard to use. Normally I would use kwxport X exporter, which exports the model as a Microsoft X format, however the developer has not updated the exporter to function with 3D Studio Max 2012 or 2013 at the time of writing, however, if you're using an older 3D Studio, say versions 8 to 2011, I highly suggest substituting the FBX format for the X format, his exporter is available at this address, http://www.kwxport.org/



Back to FBX instructions, which you can disregard if you're using kwxport. Honestly, from a game developer's perspective, the FBX exporter sucks, it sucks bad, it sucks so bad I wrote a custom exporter in MAXSCRIPT which is something in-itself worth avoiding. We won't be doing that for this tutorial because writing a MAXSCRIPT exporter and custom content importer for the XNA content pipeline is beyond the scope of this article.



Here is my export configuration, you must copy it exactly for the sake of producing usable content for this tutorial. Do not export Animation,  Cameras, or Lights. You must embed the media, otherwise you will need to edit texture file paths inside of the ASCII file, because for some reason unknown to me, the AUTODESK people saw fit to include a complete file path rather than a local file path, if you are going to be using multiple level segments AFTER completing this tutorial, you will need to replace the texture file path with the texture name and file extension to avoid embedding identical texture content. For example, "c:/users/steve/desktop/dualtextureturorial/metalfloor.png" with "metalfloor.png". But for now, if you Embed Media you can avoid editing the ASCII file. Click okay, and you will likely be presented with an error about turned edges or some related issue, ignore it.


Now to Render your light map. You may use whatever renderer you choose, but I will be using Mental Ray in order to generate an Ambient Occlusion Map.
I prefer that some optional ambient lighting be included in my light map, but of course this part is entirely up to you.


Now to actually bake the light map. Select your model and open the Render to Texture Dialog, this can be done from the Rendering drop down or by pressing the zero key. Don't forget to set your Output path. Set your Mapping Coordinates to "Use Existing Channel", set the Channel to "2" which corresponds to your previously covered UV unwrap.


Scroll down to output and press the "Add..." button. The Add Texture Element dialog screen will appear, select "LightingMap". If you are using Mental Ray you will also have a map option for "Ambient Occlusion (MR)", select that as well, and then click "Add Elements"



Under "Selected Element Common Settings", make sure both your maps are enabled and change both their width and height to 1024, this corresponds to the baked texture's dimensions. If you are also generating an Ambient Occlusion map, under "Selected Element Unique Settings", enter a Max Distance of 10.

Now under baked material, select "Save Source (Create Shell)" and "Duplicate Source to Baked", and slightly further down, also check "Render to Files Only" and "Keep Source Materials", this is essential, the render to texture can make a huge mess of your model and it can be very difficult and time consuming to recover the correct materials.




Now you're ready to render. Select Render at the bottom of the Render To Texture dialog. Here is what my Render window looks like, however, this is NOT what the final light map will look like.



Now, navigate to your output directory.

You will have two images. A lighting map which includes your baked lighting render and the second image will be your Ambient Occlusion render if you earlier chose to bake one. If you did not choose to make an Ambient Occlusion map, your lighting map is ready to go and you can skip this next step. If you did make an Ambient Occlusion map, then I will show you how to combine the two using Paint Dot Net.

Here are my two maps.



Open the lighting map in paint dot net, now drag the ambient occlusion map from windows explorer onto the light map. The Drag and Drop dialog will appear, select "Add Layer". Now open the layers window, double click the Ambient Occlusion layer and the Layer Properties dialog will open, change the blending mode to "multiply", click "OK", now duplicate the Ambient Occlusion layer if you want the Ambient Occlusion to really stand out, I duplicated mine three times to exaggerate the effect for this tutorial. Now save your image. Here is my Final Light Map.









Now Open Up Visual Studio 2010, create a new project XNA Game Studio 4.0 Windows Project.

First, add your level FBX and light map texture to the content project created by the Windows Project template.

XNA 4.0 includes "DualTextureEffect" for the model under Default Effect in the Content Processor, which works, which you must use to accomplish this effect on the windows 7 phone (because custom shaders are disabled), however this tutorial is for Desktop Windows and the XBOX 360 so we will be writing our own custom shader, so leave these settings alone.

Right Click on your Content Project and click add New Item, and then select "Effect File", name it "dualtextureeffect.fx" and then click add. The default shader only requires a few modifications to be acceptable.

Add two textures and two texture samplers. Copy and paste the following under the "TODO: Add Effect Parameters"

texture colorMapTexture;
texture lightMapTexture;


sampler2D lightMap = sampler_state
{
Texture = ;
MagFilter = Anisotropic;
MinFilter = Anisotropic;
MipFilter = Linear;
MaxAnisotropy = 16;
};

sampler2D colorMap = sampler_state
{
Texture = ;
MagFilter = Anisotropic;
MinFilter = Anisotropic;
MipFilter = Linear;
MaxAnisotropy = 16;
};


Now, scroll down to VertexShaderInput, add the UV channels to the vertex declarations, which correspond to your texture and light map channels you created in 3D Studio Max.

float2 TextureCoordinate : TEXCOORD0;
float2 LightMapCoordinate : TEXCOORD1;

Now, scroll down a bit further to VertexShaderOutput, and add the same two channels.

float2 TextureCoordinate : TEXCOORD0;
float2 LightMapCoordinate : TEXCOORD1;

Now, move a little further down to the VertexShaderOutput function. Add some code to copy the vertex channels from the VertexShaderInput to the VertexShaderOutput.

output.TextureCoordinate = input.TextureCoordinate;
output.LightMapCoordinate = input.LightMapCoordinate;

Now to update the PixelShaderFunction. Sample the sample and combine the regular textures with the light map.

Replace "return float4(1,0,0,1)" with the following.

return tex2D(colorMap, input.TextureCoordinate) * tex2D(lightMap, input.LightMapCoordinate);

Now the shader is nearly done, to avoid any mistakes we are also going to set some render states. Under technique Technique1 reads "Pass1" and then a todo about adding render states. Add the following before the vertexshader and pixelshader compile instructions:

ZENABLE = TRUE;
ZWRITEENABLE = TRUE;
CULLMODE = CCW;

Okay, our shader is done, and now all of our content is done. Now it's time to write some XNA code, big win time.

Now add a model, texture and effect to your Game1 object declarations.

//our content
Model levelGeometry;
Texture2D lightmap;
Effect myEffect;

//our camera
Matrix view;
Matrix projection;
float rotation;
Vector3 cameraPosition;

I know, I can hardly believe we are finally writing XNA C# code.

Go to the LoadContent function, and load your content. Your file names likely vary, substitute my filenames for your own.

// TODO: use this.Content to load your game content here
levelGeometry = Content.Load<Model>("levelgeo");
lightmap = Content.Load<Texture2D>("lightmap");
myEffect = Content.Load<Effect>("dualtextureeffect");

//setup our projection
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, (float)graphics.PreferredBackBufferWidth / graphics.PreferredBackBufferHeight, .1f, 1000);

Move to the Update function. We are setting our camera up to orbit the level geometry. Add the following.

// TODO: Add your update logic here
rotation += gameTime.ElapsedGameTime.Milliseconds * 0.001f;

cameraPosition = new Vector3((float)Math.Sin(rotation) * 200, 175.0f, -    (float)Math.Cos(rotation) * 200);

view = Matrix.CreateLookAt(cameraPosition, new Vector3(0, 0, 0), Vector3.Up);

Time to render! Finally!

First make a new function with a void return type, call it "DrawDualTextureModel", put a xna model object in for its argument.

private void DrawDualTextureModel(Model m)
{
}

Now, to make sure you're objects are all rendering in the correct location, you need to process the transformations included in the export.

//set mesh transformations
Matrix[] transforms = new Matrix[m.Bones.Count];
m.CopyAbsoluteBoneTransformsTo(transforms);

Now loop through each meshpart contained within each mesh contained in the model object.

//Now, for the rendering code loop. Loop through each mesh contained within the model object.
foreach (ModelMesh mesh in m.Meshes)
{
     //loop through each meshpart contained within each mesh
     foreach (ModelMeshPart meshpart in mesh.MeshParts)
     {
          //Rendering code goes here!
     }
}

Now, before the super coders get on my case, I'm aware that there are a lot of better ways to do the following section, mostly with custom content processors, but like I said above, these types of solutions are outside of the scope of this tutorial. We are going to input shader parameters the regular way, but because we are using a custom shader with a dual texture effect we need to cast each meshpart effect created by the content manager into a basic effect in order to gain access to the textures which were imported with the model. Insert the following code into the innermost section of the loop.

//Set our shader parameters
myEffect.CurrentTechnique = myEffect.Techniques["Technique1"];
myEffect.Parameters["Projection"].SetValue(projection);
myEffect.Parameters["View"].SetValue(view);
myEffect.Parameters["World"].SetValue(transforms[mesh.ParentBone.Index]);
myEffect.Parameters["lightMapTexture"].SetValue(lightmap);
//really tacky, I know. Casting to a basic effect is essential for texture application without a custom processor
BasicEffect tmpBEffect = (BasicEffect)meshpart.Effect;
myEffect.Parameters["colorMapTexture"].SetValue(tmpBEffect.Texture);
//now apply!
myEffect.CurrentTechnique.Passes[0].Apply();

Okay, now we are finally ready to draw. First set the vertex and index buffers and then make a draw indexed primitives call. Inside of the innermost section of the loop immediately under the myEffect.CurrentTechnique.Passes[0].Apply();

//set our vertex and index buffers
GraphicsDevice.SetVertexBuffer(meshpart.VertexBuffer);
GraphicsDevice.Indices = meshpart.IndexBuffer;

//render our geometry
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, meshpart.VertexOffset, 0, meshpart.NumVertices, meshpart.StartIndex, meshpart.PrimitiveCount);

Now move to the XNA template's draw function and insert our dual texture draw call.

// TODO: Add your drawing code here
DrawDualTextureModel(levelGeometry);

Now hit run, yours should look like this. Here is the final image. If it doesn't, you did something wrong, feel free to comment.




Punishing Heat

I HATE the heat, it's awful, it makes me uncomfortable, irritable, unable to focus, heat is a regular witches brew of shit. I haven't gotten anything done in the last week, it's been ninety degrees in the house, and my glitchy freezy crashy computer is only good for as a heater when it gets hot. Anyway, I started on a big level, it's huge actually, I need to make some props, it's too bare.

Wednesday, August 8, 2012

No more limp dick games.

It hit me this morning, a game without guns isn't a game at all, it's something very different than a game. So... I added cooperative support, split screen death match and of course guns. No split screen video yet, I only have one wired controller.

Level Groove

I'm working on developing a unified design style for my levels. Here are some more screen shots.









Tuesday, August 7, 2012

Full, Half and No, Retard Modes

Easy = Full Retard Mode
Medium = Half Retard Mode
Hard = No Retard Mode

Those are the skill levels. Deal with it. Which are you?

I made a 60fps video capture for you. But youtube resampled it to 30, so enjoy. At least it's HD huh.

Monday, August 6, 2012

FULL RETARD MODE

Alright, I'm gonna write a post mortem (this isn't it), Chronodash taught some lessons, first among them is that every game needs a FULL RETARD MODE. True Story, evidenced by the video reviewers complaining about the lack of a save feature while reading the help screen which clearly displays a single button press save feature. So, my Chronodash do-over is going to be RE-TARD PROOF, I sware.