![]() |
|
Spaces home Bryan's spaceProfileFriendsBlogMore ![]() | ![]() |
|
|
July 23 Megatexturing and new UI stuff..I've waylaid megatexturing for now. The work to payoff is not very high. The reason is that, although a very cool feature; for indie games it is not so practical. Especially XNA games which have to be less than < 150 MB. Therefore, i'll be focusing more on other projects.
My current project is creating a windows designer for my UI system. Its very promising, Stephen Styrchak's blog has some great info about getting started.
Before I knew it was even possibel to create a windows designer, I built an editor into my engine, which could be accessed during run time by pressing a key combo (like ctrl-E or something). It let you do some stuff like move and resize elements, and save and load panels from XML.
However, this windows designer stuff is way cooler. You get to leverage things like the Property Editor, toolbox, etc - basically for free. And it serializes your components into code, which is awesome. There is no "magic" happening - whenever you make a change to the UI you can see the corresponding code. In addition, code serializaiton is way faster at run time - the Xbox XML deserialization is very slow, and writing content writers for XML gets tedious. You can't get much faster than code serialization because there is nothing to be loaded (besides the assembly of course) at run time.
So I'm really excited about this. There has been all this talk about .NET enabling "Rapid Application Development" - I'm hoping things like this could bring "Rapid Game Development" to the masses.
i'll have a video soon =) Alternatives to GetDataAs promised (although I don't get many blog hits, haha, so not sure anyone cares =) i'm proposing an alternative to using GetData in certain circumstances.
The problem with GetData is that it is a pipeline stalling function, so it can take more than just a few milliseconds for it to complete. In a fast render loop, this is unacceptable! In general, as with SetData, the advice is to not use GetData in high frequency or critical sections of code. However, what if you really really need to know what the pixel values are?
First, lets establish some motivation. Why would you what to know or care about what pixels are being rendered? You might be thinkning, hey, as long as DX is rendering my scene, thats cool, I don't need any info back. However, when you get into more advanced features, like HDR, you'll need to know some info about the colors your scene is producing.
HDR stands for High Dynamic Range Rendering - and basically it allows you to overcome the limitation of 256 values for each color channel. 256 values for each color is not a very high range. I believe the human eye can perceive a contrast ratio of 10,000 : 1 - which is far greater than a computer monitor can display. I'm not going to go into the nitty gritty details of HDR rendering - just enough to whet your appetite and see why you might need to read data. Basically, the idea with HDR is to take a higher range of values, and then tonemap it to fit the 256 values presented on the monitor. So basically we take a high range of color values - apply a tone map operator - and the result is a range of values we can display on the screen. This is great because we still get detail in light and dark areas but light areas appear really light and dark areas appear really dark.
Now, most tonemapping implementations need to know the average luminance of what you are looking at. Luminance simply means the brightness of a pixel, with no regard for color. To calculate the average luminance for a scene, one way would be to use a floating point render target (to give us more range), draw to that, and then use GetData(). We could then loop through the values for the pixels we get, sum and divide to get an average.
However, as mentioned above, this is SLOW!
The solution presented in the DX SDK for this is to render to multiple floating point rendertargets - first, render to a smaller target, ie one that is 256x256. Then, render that render target to one that is 128x128, etc until you get a render target that is 1x1. You can these use this render target as an input to your HDR tonemapping shader to get the average luminance.
Another, really cool alternative (and this is where the alternative to GetData comes in), is to use Occlusion Queries to check if pixels match a certain criteria. You could use an occlusion query to test how many pixels fall within a certain luminance range, for example, or see which pixels match a color. Obviously, this is not a perfect replacement for GetData, because you need to know exactly what you want - either an exact color or range of colors. But the advantage is that you are working with the GPU instead of against it - the occlusion queries can be run asynchronously. The Source engine uses this technique (where i found out about it, lol) - they keep a runniing histogram of luminance values (they keep track of 16 ranges of luminances), and each frame they update this histogram by updating one bin per frame. For example, at frame 1 they might use an occlusion query to see which pixels have luminance < 0.2, and then frame 2, they might use an occlusion query to see which pixels have luminance between 0.2 and 0.4 and update the histogram. The nice thing about this is you get more data than just taking the average of the scene.
So, if you are thinking GetData(), think about maybe using an occlusion query if possible - if you know exactly the colors or information you need.
Best of luck-
Bryan July 07 Alternatives to SetDataWhile working on the mega texturing, I've had quite a few areas that bottlenecked my approach. At first, it seemed impossible to get things to a real time speed, but I think i'm almost there. I had made assumptions that GetData and SetData were my bottlenecks -and initially this wasn't even true! It wasn't until I started doing some profiling that I realized code inefficiences in the texture fetching and atlasing algorithms were causing severe bottlenecks.
However, once I improved this, it turned out that SetData calls were definitely a bottleneck. Ideally you don't want to use SetData in any sort of real time sense. You should be able to hopefully offload most work to the GPU in places where you would wish to use SetData. However, in the mega texturing, I am streaming texture data from a binary file on disk to a texture - so it seems like there is really no alternative to SetData. I actually used it in two places - one for an index texture, which tells the GPU where to look in a larger Lookup Texture for the actually texture data. If there is no valid lookup texture data, then the index texture has the average color of the lookup data.
So, it appears that you need at least two SetData calls - one to set information in the index texture about where to look in the lookup texture (offsets, sizes, etc), and a second SetData call to send data to the lookup texture. However, this turned out to be exceptionally slow. I tried many schemes, all running from a seperate execution thread:
1) Call SetData only on the affected regions
2) Make many small textures, call SetData on these, and then use a SpriteBatch to draw these over to a lookup texture rendertarget
None of these proved acceptable - SetData just was tooo slow.
An alternative that helped out tremendously was to do the following in place of SetData:
Use a render target, and render a white 1x1 pixel for each pixel in the texture. I could draw only the portions of the lookup texture that had changed this way, and it was MUCH faster than calling SetData. So basically instead of using SetData, I use a SpriteBatch to draw each pixel individually. This works well because not every pixel changes every frame, so in the general case it is a very reasonable amount of spritebath.Draw() calls, and they are all using the same texture, so ideally it should only be 1 extra actual Draw() call. If you need to use SetData in your application, and find it to be a bottleneck, I would suggest trying this approach.
The downfall is that my index texture is actually a HalfVector4 texture instead of a Color texture. The added precision is needed for the offsets and sizes into the lookup texture - 1B isn't enough precision for my current system. So I still need to use a SetData<> call on the HalfVector4 texture as there isn't a good way that I know of to draw to anything but a Color surface using a spriteBatch. Maybe I could do something clever and use a few different pre-set HalfVector4 textures to get all the ranges I need, or maybe with some fine tuning I could get away with 1B of precision. So we'll see!
This isn't a groundbreaking idea or anything but just in case someone is stuck with SetData<> its cool to have some other options to try and see if they help.
Next post: An alternative to GetData<> for certain scenarios! June 21 Working on new game : QuarxMy brother and I have been working on a new XNA game - Quarx. It's basically a Dr. Mario clone. I was a big fan of Dr Mario back in the day, especially the 2-player mode, so i thought it'd be fun to bring it to the 360. My brother has been helping out a ton with the graphics and game screens and everything, so its shaping up to look very nice. We have some details to add & polish, and hopefully you'll see it on the community games pretty soon!
June 19 Virtualized Textures, Part 2!Last blog entry I talked about virtualized texture progress. And its happening. Right now, the system works, but is very slow - so a lot of work is going to be done to performance tune it. Also, it is wasteful right now, in that it always fetches the highest detail level for pixels on the screen. This is ok for testing... actually its not really ok ever... no mip mapping is being done right now, so i should be fetching only the pixels i need and not the max.
The way the system works currently is as follows:
-A mega texture is basically a giant raw texture file that lives on the hard drive
-There is a texture indexing and caching system that reads from the mega texture. The caching is software caching that keeps relevant texture areas in memory. Cache coherency should be pretty high frame to frame.
-A pre-render is done of the scene to see which texels are visible. Based on which texels are visible, higher details are requested to be read in from the file.
Here is a quick screenshot of both the mega texturing in action. It is an unimpressive scene because I am an unimpressive artist. Also, note how much slower the megatextured scene is right now on my PC =) Also, the "room" is from a Quake 3 BSP file, and the pill bottle is from a FBX file, and this is combined using a content pipeline processor that combines geometry & combines textures into a megatexture:
For this image, the megatexture is only 8192 x 8192. This is significantly less than megatextures will be later on.. The index texture is 1024x1024, so each index represents an 8x8 patch on the megatexture. The image on the right is a rendering of the average of each index. The image on the left is fully detailed, with all visible texture areas streamed in. Interesting things to note: 4 FPS (!!) on the megatextured image for now - and much greater FPS on the index texture only. A GetData<> and multiple SetData<> are needed, as well as file streaming, etc - but there is a lot of opportunity for speed up. Also, I'm having a weird bug with the colors where the red and blue textures get flipped. However.. its a start! Hopefully I'll have more impressive scenes down the road =) June 02 Virtualized Texture ProgressI've made some progress in setting up the virtualized textures. Its been a pretty complicated endeavor, with lots of headaches sure to come! But I have had some success too. My system so far consists of the following aspects:
1) A custom importer that simply imports an XML file, and saves model information from it
2) A custom processor that combines all the models into a single uber model. This processor also combines all textures into a giant megatexture. There are some tricky cases, like when u,v are < 0 or > 1, and tiling has to be accounted for. Currently, the megatexture does not support tiled textures - EVERY texture is completely unique.
So basically this processor combines all vertex/index data, remaps the texture coordinates, and combines the textures.
3) The megatexture system that allows for streaming reads of daa.
4) A texture indexing system with a specialized shader that redirects to a lookup texture if a more detailed version of the texture is available.
There are a lot of things that need to be done, though!
-A megatexture caching system. The reads are currently slow, and only read one index location per time. This is a huge waste of resources. It needs to be set up such that reads read larger entries at a t ime, and this data should all be cached. Generally, nearby texture reads should correspond to a nearby piece of geometry (but not always!), so in general, there should be good spatial coherence. This caching/reading system should be much faster, as keeping current data in main memory as opposed to always going to disk would be a huge speedup (and just plain make sense). One interesting thing to note is this virtualized texture system is VERY similar to the multi level memory hierarchy in computers. You have your hard disk, then the RAM, then the L2 cache, L1 cache, etc, with each successive piece of memory being smaller but faster. This is analgous to the virtualized textures: You have the virtualized texture in the hard disk, some portion in RAM, and then some portion on the GPU. Makes sense...
-A way to decide which portions of the textures need to be streamed in. Currently, for testing, I just stream in the first N indices of the index texture, because I've just been focusing on getting the indexing/lookup system working. However, in the engine, the textures should be dynamically requested based on the user's viewpoint. Textures the users see close by should be requested at a higher detail, and textuers the users don't see should be basically forgotten about.
I was debating about different ways to handle this. My first idea was to create an octree of all the triangles in the multi model , and do ray casts into the world and see what indices need to be expanding on based on the triangles the ray hit. However, this probably wouldn't work so hot, as you'd have lots of edge cases with small triangles, and ray casting would be expensive.
My new idea is to do a pre-draw where I draw the texture index on screen. Then, I'll use a GetData<> call to get the data from the screen, loop through and see which indices are present. If index 5 is present 35 times, and my current lookup for index 5 only has 4 pixels, i would send a request for 32 pixels. Each index could be examind this way, and this is great, because visibility is accounted for easily, and its pretty good data. There would be some issues with occlussion, where you might only see 5 pixels of a given index, but it is occluded and very close to you. I'm not sure how to handle this right now.
Some tricky things for this are that GetData<> calls are generally VERY SLOW. I'll have to experiment with ways to handle this. The GetData<> call itself could be done asynchronously, as well as a lot of this processing. It'll take a lot of trial and error to get this working right, but it'll be worth it, as mentioned previously, with the virtualized texture system you basically get "infinite" detail in the models. May 21 Lack of updates...So there hasn't been a lot going on here recently. I just finished school, and have been spending a lot of time hanging out with friends and family. So not a lot of work has gotten done..
I have some really interesting ideas now after hearing about John Carmack's virtualized textures. I think this is really interesting stuff and will probably power the future of great looking games. I'm trying to create my own implementation, and its been a lot of work. The amount of detail though that you can put in something like a virtualized texture would be amazing. If you're not familiar with the technology, you should check out a presentation on it, over at youtube. The idea is that you have a giant megatexture that contains all the texture data for a level, including characters and everything. For his engine, some of the virtualized textures were 20 GB (!!) In one of his presentations, the texture for a characters face was 2K x 2K. Obviously you cannot have all this data in the GPU, thus, the key to the technology is being able to stream textures in on demand and keep the most important textures on the GPU. This allows you to dynamically scale the level of detail.
There are a lot of great things about this technology. The first is that it would enable artists to create environments as detailed as they possibly can. Another big plus is that it would be super scalable - you just could decide how much space you have to swap in textures, and this would dynamically adjust for different computing scenarios. This makes it easy to make the same technology run on a low end or high end card - a high end card would just have more detail. Also, in theory you could use just one draw call for all the opaque geometry, making rendering efficient (of course, in practice more draw calls maybe be needed for various sorts of culling)
You can also prebake a lot of effects such as lighting and such into the virtualized texture.
In the future similar technology could also be used to dynamically load vertex data, and have high detailed models close by while low detail models have lower vertex count.
So this is cool stuff... and i'm excited about trying to get an implementation going in XNA
Fourth XNA Game : Shattered MetalShattered Metal
Action/First Person Shooter
Description: Shattered metal is a first person shooter designed using the XNA framework and also utilizies the Newton physics engine, along with the Brian Peek's Wiimote library. It also features an early version of the SX engine. Shattered Metal was designed by myself along with two other programmers, Jin and Kevin, and a lot of the artwork was done by my brother. Shattered Metal was Kevin's, Jin's, and my major design project for college. We were given 3 months to basically make a "cool" product that showcased our technical skills gained through our studies and also showcase our creativity. Our project was pretty unique, most other groups did web applications, but we decided to do something different.
The inspiration for Shattered Metal came from Johnny Lee's work on headtracking at CMU. Yo can check out a video of his idea here: http://www.youtube.com/watch?v=Jd3-eiid-Uw
Basically, the idea is to use Wiimotes in a new and novel way : to track the user's head motion. We wanted to incorporate this into a first person shooter game, in order to showcase a new level of immersion and interactivity. Our control scheme was to use a wiimote + nunchuck to move around, and then use headtracking to allow the user to crouch, jump, and peek around corners.
We had numerous challenges throughout this project. Making a 3-D first person shooter is an intimidating task. I had experience from Fields of Chaos and some other projects in using XNA and doing graphics programming. However, there was a lot of other stuff to learn, ie how to use the wiimote library, how to interpret the data from the wiimote sensors, getting proper hardware, etc.
Our first version used goggles, much like the youtube video. I just bought some LED goggles from Lowe's, popped in some IR leds, and used that. It worked decently for a while, but it turns out that the max forward voltage for IR LEDs is a lot lower than for standard LED's (1.4V to 3V), and i actually burnt out those LEDs unfortunately. We experimented with other ideas - our fallback was to just tape IR LEDs to a hat. We also tried reflecting infrared light from mirrored sunglasses, but this didn't work well. So in the end we ended up with a simple hardware set up - IR LEDs taped to a hat. Not really ideal... but it worked (somewhat). Unlike the demo, we decided to not allow forward & backward motion - we just let the user move up/down/left/right in the camera plane. This alleviated several game play issues.
Setting up the Wiimotes wasn't too hard. There were some issues though - the Wiimote library is asynchronously updated (ie, it fires an event every time the input changes, and this happens asynchronously). The initial problem we had was aliasing with the wiimote, as sometimes the key state would be lost if multiple asynchronous events fired between the main game event loop. However, the controls turned out to be pretty solid as far as aiming and moving. The math to translate the IR positions wasn't too bad, and the wiimote library was very helpful. It was really easy to incorporate the nunchuck in the design. In the SX engine, I tried to abstract controller functionality was much as possible - such that you could treat a mouse, wii controller, xbox controller pretty much the same. This worked pretty well and allowed us to test and play the game with the mouse and keyboard as well as the wii controller. This was valuable because not all the team members had the equipment necessary.
Besides the wiimote stuff, there were a lot of challenges with the game. We used GTK Radiant and the quake 3 bsp format to store our maps. This was helpful because it was easy to place entities and stuff, but numerous issues were apparent. One thing that was difficult was we had to code a large number of support map entities - like triggers, doors, etc. Unfortunately, these didn't work as well as I would like, but that is mainly due to time constraints. Some other issues are that the newton physics engine sometimes didn't work so great (as with our other game) - things can fall through the level, etc.
There are a lot of features we wish we could've incorporated but didn't have time. One feature would be melee weapons using the Wiimote and grenades that could be thrown using a throwing motion with the Wiimote. Unfortunately we didn't get time to master the accelerometer functionality that is provided by the Wiimote. Although it was easy to read the values, some sort of filtering would have been necessary (maybe like Kalman filtering or something), and we just did not have time to make this robust. Also, we really wanted to implement a split screen multiplayer, but again we just didn't have time. It is entirely feasible as the wiimote can track multiple IR points, but time caught up to us :-D
All in all, this project was a lot of fun and an interesting learning experience. I'm hoping to incorporate a lot of what I learned into the SX engine.
Special thanks to my bro for helping out with the artwork and level design!
Here's some screens:
May 03 More UI stuff and unit testingI've made a lot of progress today on the UI plus an in-game unit testing application. I've been able to resolve a lot of the challenges I was having with the UI - vertical scrollbars now work and are easy to add to controls. The textbox now supports selection properly and handles newline characters, the combo box works, the tree view works, etc. It's been a long haul to get this stuff working but hopefully it'll pay off - i'll be excited to release it down the road! I'm working on a PropertyGrid-like control right now (thats only read only, for now) that I think would be very useful for testing, as well as a write-enabled version that might work great for a level editor type application.
One key feature of my engine that I am excited about is unit testing support, in-engine. I'm a big fan of using test cases to verify code. I like test driven development but don't follow it all the time - I don't think it is ALWAYS appropriate to write test cases first - i think sometimes you need to start designing an idea first to wrap your head around how it should be used, but I'm sure there are people that disagree with me. That being said, I can see cases where starting with a test case is advantageous as it allows you to see how you want the class to work externally, and then focus on the internals down the road. In addition, test cases are awesome for validating code, when you've done major changes or serious refactoring, and want to make sure it still "works". Its amazing how bugs can propagate like you would not expect.
I've used NUnit pretty extensively, had experiencec with JUnit, and a few other testing frameworks. For C# development, I used NUnit, which I am very impressed with. However, if you have tried to use NUnit to run test cases on your game code, you would probably be disappointed. Trying to get a GraphicsDevice up and running in NUnit is a pain (at least it was for me), and that is necessary to have to make sure code works. For example, I wouldn't be able to test my TextBox without a GraphicsDevice, because I would not be able to instiate it, simply because there would be no way to load the graphical content. Sure, there are ways around this, but most of them mean extra work.
My solution, then, is to create an in-engine unit test framework. The functionality will be similar to NUnit, in that it scans the assembly for test cases, and then runs them. It is actually up and running right now. It is not nearly as sophisticated or as advanced as NUnit, but as features are needed, they can always be added. The code is pretty simply and utilizes reflection (C# rocks) to find the test setup/teardown/cases.
Also, if you've had experience games, you'll quickly realize that unit tests aren't the end all solution. Can you really write a unit test to see if your bloom feature works? Can you write a unit test that tests that the scene looks the way you want? It could be difficult, sure, for the bloom, you could maybe send in a simple Texture, have it run the post-screen shader, and then verify the results manually, but this wouldn't be an effective test, as it may not translate well to the whole scene. How can you test if a box renders to the screen ok? Do you verify each pixel manually? This would be infeasible and a waste of time. I don't have a great solution for this, but I have some ideas that I hope might make the development process easier. First, in addition to simple unit tests, i will support "Graphical Unit Tests". These test cases are actually the kind of test cases i usually program into my project (and that are shown in Benjamin Nitschke's book), where you simply have a test case that does something, and you examine the results manually. This works pretty well, its nice to develop a new feature in a test case and then incorporate it into the game when it is ready. However, once you have the results the way you like, there is no way to automatically verify (regression test it) in the future. My idea is to take a snapshot of the graphical unit test, save it, and then when regressing the graphical tests, the snapshot would be compared with the output, and given a certain pixel tolerance (ie, 95% of pixels must match), and the graphical unit test would let you see the results. This would only be useful for regression testing, as you would have to manually take the "correct" snapshot, so it would have to be correct at some point in time. The nice thing, though, is this would protect you from breaking code. For example, if you mess up the render pipeline and turn off the depth buffer at the wrong time, this should catch it early before leading you on a frustrating search for why your render code no longer works properly.
Anyway, thats it for now, heres a screenshot of the ingame unit test UI:
May 01 Third XNA Game : WiiSnowballWiiSnowball
[Requires XNA 2.0]
Game Type : "Family Friendly" First Person Shooter
Project Status: Incomplete / To be completed later
Team: Myself (coding / 2d artwork) & My brother (3d modelling)
WiiSnowball was a project my brother and I came up with over spring break. Originally, for my WiiMotion major design project, we wanted to have a few different games to showcase the potential of the platform. So most of my time over spring break I spent coding this game - and my brother did artwork (the player models, the nice looking level).
Technically, the development time was about 2 weeks - but this leveraged the engine I had already started using. I thought it was going to be a fairly easy project, but it turned out to have some nuances that made it more challenging. It turned out to be fairly fun, however, there wasn't a lot of dynamic to really keep it interesting. Running around and throwing snowballs at other players or bots got old after a while, and my brother and I didn't really have any ideas on how to increase the fun factor. We did try adding a snowmobile vehicle, but it didn't work so great and didn't add much.
In addition, I could leverage the Wiimote library as well, although you can play with the keyboard and mouse too. The game is meant to be played with a Wiimote, and headtracking, although the headtracking was never implemented fully for this game.
The biggest challenges for me were:
All in all it was a pretty fun project. Maybe there will be a Snowball Game 2.0 coming out in the near future, utilizing my new engine in development! A download is not available because it is unfinished, however, here are some screenshots:
April 26 Graduation & Engine UpdatesI'm offiically done with my undergraduate career!
I actually got a lot of work done today on my engine project. In all the time I've used XNA, I have never set up Xbox Live or XNA on my Xbox (which is kind of crazy because thats a lot of the point of XNA). So I set all that up today, and it works great. It'll be nice to test my engine on the 360 and play my games on that.
In addition, I made a lot of progress with the UI. I transferred all the inputs into events - so now instead of each UI component being responsible for querying input devices, the input layers fire events off and each element handles these events. There are a lot of advantages with this route - one being that the input devices and UI are strongly decoupled - anything can be a "mouse" as long as it creates "mouse events" - this will make it easy to finish abstracting the controls, so that it is easy to program the UI for both the mouse, game controller, whatever. Another big advantage is that now a chain of responsibility design pattern can be used - a UI element can decide to pass an event to its children, or if its not handled by any children, it gets passed back up and handled. All in all I think this was a solid design decision. Along with this, the concept of "Focus" has been added - in the screenshot i posted, the front form has focus, while the back one doesn't.
Also, I started implementing multi line text boxes. I figured out a lot of the design problems I was having (how to handle multi lines, word wrapping, and selection), but there is still a lot to be done. I'm working on implementing scroll bars now, as can be seen in the screen, although one of the arrows is upside down.
There is still quite a bit of work to be done. I'm going to finish implementing the scroll bars, create a slider control, and a tree view control. The tree view control is a priority because it will let me set up an "in-engine" unit test framework, which I am excited about.
Another improvement I made was a huge performance improvement. Originally, the forms were being rendered every frame, which is a big waste of resources - why render again if nothing has changed? So now, forms only get render if they have been "invalidated", which is when something on it has changed. This provided a very significant (2x FPS or so) framerate boost.
In addition, I want to create a UI element that renders a 3d scene to a render target and draws that. This could be useful for character select screens, map previews, etc.
Here's the screenshot: Its kind of ugly because the controls are all over the place... but a lot of the core ideas are there:
April 20 UI revamp in SXEI decided to rewrite the user interface code from the engine in Shattered Metal to the new UI in SXE. I've gotten a decent amount of experiencing now in writing a UI framework, and each time my attempts have gotten more and more ambitious and I've learned a lot on the way.
In my first game, PsyOps, the UI code was pretty rudimentary. There was a game screen class and a button class, and that was about it. I did see the value in abstracting away functionality, as it was easy to add a checkbox class later on. Fields of Chaos featured a much more complicated UI, which is surprising because the entire game was developed in 5 weeks - including the UI code. However it featured multiple button types, like a Color button with colors that fade in and out, as well as a button that had the 3 image states. There were also a lot more complicated controls in Fields of Chaos - there was a tab control, which was pretty easy to implement, and a ListBox control, which was definitely the hardest. I had a crappy implementation the first time around on the ListBox, and the second time around, it worked a lot better. The ListBox was used in both the "Find a Server" screen and for the in-game scoreboard panel. It actually turned out to work pretty well. The flexibility of it was shown when I was able to code up network & performance debugging panels within 15 minutes.
WiiMotion featured a similar UI structure to Fields of Chaos. Most of the UI code was simply ported over ,and then cleaned up and improved. WiiMotion did feature a few new ideas - for example, in all my previous UI elements, the elements were stretched to fit. This is cool sometimes but not for things like forms with borders - it looks bad if you have different sized forms with a stretched background. Thus, I designed a panel called a UIBorderPanel, which kept the borders at a fixed size and then stretched the interior of the image, so you could get "real GUI" elements. Also, I designed a special panel that would render objects in 3d and use those. This wasn't featured in Shattered Metal, but it is how the characters are displayed in my WiiSnowball game.
So why the change of pace, if these other UI systems worked ok? Well, one major problem I had was I decided, for ease of use, to make all the coordinate systems "relative" - instead of using pixels as locations, I used the positions 0.0f - 1.0f. So 0.0f, 0.0f was the top left of the screen, and 1.0f,1.0f was the bottom right. I thought this was a great idea because it would allow resolution independence.
Unfortunately, thats the not the case - you could get in situations where you would get rounding errors, and for items like the UIBorderPanel, that needed per-pixel placement of objects, you coudn't guarantee that with the relative coordinates. So my BorderPanel would have overlaps or gaps in it if you scaled it in weird ways - which was bad news. I decided also to model my UI system after the C# GUI class hierarchy, and upon examination, I realized they used per pixel coordinates as well - so thats what I decided to with the SX engine's UI library.
In addition, I didn't realize the value of having a stack of GameScreens until the engine developed for WiiMotion. My implementation was weak, however, in the fact that I didn't have a good way to accomplish transition effects between GameScreens. It might not seem like a big deal, but as far as polish goes, it is important to have a nice transition between your game screens. I have not started coding the GameScreen class for the SX engine, but it will likely render to a rendertarget, so that you can easily transition between the screens.
Also, in the SX engine, I'm planning on having a Forms-like interface as well. I had kind of a ghetto console in Wiimotion that was not very sophisticated. I want to have a console more like the Source engine, with a form you move around, and a textbox with autocomplete - so that is kind of my goal.
I also want to support more controls like a TreeView control or something similar...
One other thing that is important and that i haven't solved yet is how to deal with multiple controllers interacting with the UI. Input abstraction is a big deal in the SX engine - so it shouldn't matter to the UI whether you are interacting with a gamepad, or a mouse, (obviously you can't type with a gamepad). However, I haven't quite solved the issue of how to handle multiple cursors on screen. In addition, I still haven't solved the issue of exactly how I want to propagate UI events. Currently, each UI element queries the mouse (which needs to be abstracted) for its position and button state. The problem with this is, if you have two forms on top of each other, items in both forms can receive the event. Thus, I was considering having a message-passing event system, where when an event gets generated, it gets sent to each form/element, and the form/element returns if it handled it or not. If it handled it, its all good, otherwise, it'll continue propagating until someone handles it or each element has seen it and refused... I'm not sure what the best way is yet, I'm going to keep brainstorming.
Here's an early early peek at my test program today:
April 19 WiiMotion screen shotsThese are some preliminary screenshots of "Shattered Metal":
I'll be releaseing more info/screens/download soon!
This was my major design project. Besides featuring the early alpha of my graphics engine, the SX Engine, this game also uses Wiimote headtracking based on the idea from Johnny Lee at CMU
April 17 Programming at University of MichiganI just downloaded FxCop today to play with it and have it assist me in making sure my code for my engine-in-progress are up to par and readable. I'm really impressed with how thorough it is, and how many good suggestions it has given me in modifying my code. It is an excellent tool that should be in every C# developer's toolbox. I was exposed to it somewhat when I interned in Microsoft Project, but as I had just learned C#, and SQL Server, and lots of other tools, it was a lot to take in, and I didn't really understand as much what was going on with FxCop specifically (I just knew that it would yell at me if I did things wrong) =)
Also, I took went to my very last class at University of Michigan yesterday. It's kind of sad, I'm not sure if I'm ready to be in the "real world", but then again, it'll be nice making money instead of spending money! I'm really happy with the education I received at U of M, most of the professors I've had are really top notch, and truly invest in their students. I've had some professors who would totally take it personally if we did not understand what was going on, and would respect students and do their best to answer questions insofar as possible. I only had one bad professor the whole time i was there, which is a pretty small percentage ;-)
One interesting thing about the curriculum at U of M is that it is almost completely C++. A few courses you take might touch Java a little here, Lisp a little there, but for the most part, the CS department is laser-focused on C++. I don't view this as a bad thing at all, as C++ is a great language and probably the best language to start learning. I did take one class, EECS 381, that really got into C and the differences between C and C++ and C evolved into C++, and that was very valuable, and helped us understand certain things about C++ (like, why there is a 0 termination character instead of storing the string length). That same class also focused on OO design, and design patterns, and those are extremely useful in thinking about structuring big projects.
I think maybe, in this time, it might be valuable to have a course teaching about C# and the associated tools. It seems many companies that I have talked to are moving towards using C# due to several advantages, such as rapid development time when you utilize the .NET framework and great tools. I would imagine that a student coming out of college with experience in .NET and things like FxCop, CLR Profiler, NProf, unit testing, etc would be in high demand in many of those places. However, on the flip side, it wouldn't be a really good idea to replace C++ with C# at this time, as .NET isn't truly portable (many students prefer working in Linux/Mac here, and they would vehemently oppose a move to .NET). In addition, there are certain concepts, such as value & reference objects, that might seem strange in C#, but if you understand how they work in C++, then you really have a better idea what is happening under the hood in C#. Also, learning how to manage memory in C++ or C, and doing pointer arithmethic, does the same thing, in that it gives you a much better idea of what is actually happening when your program exectues (and makes you value things like managed memory and provided helper classes much more!)
U of M seems to have teaching C++ down pretty well. The courses and projects are pretty excellent in teaching concepts, therefore, why make the switch if it works well now? If you know C++, and are comfortable with an object oriented paradigm, you can definitely pick up C# without much issue
April 13 XNA Content Pipeline IssuesIn the past week I've been trying to get familiar with XNA's content pipeline. I've found it really confusing to try and get a grasp on what is going on with it.
At first, I had difficult understanding the process. But it seems to make sense:
First, you import the object and output it as a common Document Object Model.
Then, you process that output into a format that gets written to a .xnb Finally, you write a reader to read back the data from the xnb
This makes sense, and having a common Object Model for importing could definitely save time. I'm writing a BSP importer, and currently targetting Quake 3 BSPs, but if I make a standard DOM, then I should be able to easily add support for other types of BSPs as well, as long as they fit the requirements of my DOM.
The problem I have with the content pipeline so far is that I don't understand exactly how to create a vertexbuffercontent. My Q3BSP loader worked great when I could just make an array of my custom vertex format (with the position, normal, binormal, texture coord, and lightmap coord) and use SetData<> to load the data into the buffer. It was pretty easy. But now I have to deal with all this other stuff, a NodeContent object and a GeometryContent object and specifying all the vertex channels.... It seems kind of silly to me that it has to be so much more convoluted. Why can't I have more control over how the vertex buffer gets created?
I'm almost tempted to just write out the data as an array to the xnb, and read it back in, and then populate the vertex buffer using SetData again... But i'm going to give this stuff a chance, I'm sure I just am ignorant about how powerful it is and how much easier it could make my life.. I'm always frustrated at first when I don't understand something
April 12 Fun with databasesSince starting this blog I've been really excited about showing projects and work that I have been involved in... Kind of treating this like a showcase more than anything. I've had exactly 1 view besides myself so far ( thanks bro
Two summers ago, I worked for a company called GHAFARI Associates. It was truly a pleasure working there, i was an intern in the Project Management department, and I learned a lot about what being a project manager is all about. Also, once they realized I knew how to code, they gave me a lot of interesting assignments in creating databases that the PMs could leverage. I made all my databases in Access using VBA, but had I known better at the time, I would've leveraged Microsoft Project for one of the databases (the resource management - as I didn't know it at the time, but Project did everything I needed to hardcore myself into Access! haha). In addition, for the other DBs, I would've used SQL Server Express/C# Express, because there were some really frustrating issues with database corruption with Access - which usually occurred during multi-user access, but sometimes would happen spontaneously as well in a controlled environment. All in all though I am very proud of the work I did for GHAFARI, thankful for the experience, and hope they still find these useful.
I'm posting some screenshots of the database but most of the content of the database is filtered out for privacy reasons - and I was too lazy to add new content.
Consultant Manager Database:
April 11 Level FormatsOne major weakness about Fields of Chaos (besides the network code
Anyways the level format was a heightmap + objects scattered around the level. This might be OK for games that are mainly outdoors, but for a first person shooter, i don't feel its good enough, as you need some indoor areas to be interesting. However, for that project, it was the easiest thing, as I had already coded up a terrain rendering engine (that was my pet project after finishing PsyOps). And it worked OK for our game, the circle level at least was pretty cool I thought - you kind of got the sense of being in a giant valley with mountains around you.
After Fields of Chaos, I was inspired by a better way to represent levels. I was somewhat familiar with the BSP format (having made a few maps for Half-Life and Half-Life 2), and i used some other level editors (i made levels for Dark Forces 1 & 2 back in the day...) The main thing that attracted me to it was that GTK Radiant was open source, and could make BSPs, and is a solid tool.
Thus, I began my journey to write a BSP renderer for Quake 3 levels. Reading in the data wasn't very hard (although ironically more of a pain than in C++ because I couldn't do a direct read from structs, as far as I know... so I used a BinaryReader and had to read each item individually). Displaying most of the mesh wasn't tooo hard, although the double offsets with the meshverts and everything took a little while to wrap my head around. The bezier patches were more of a pain in the @ss but eventually those got figured out too..
The biggest difficulty I had was in rendering the BSP EFFICIENTLY. The problem is, the BSP "idea" is great for having minimal overdraw... However, graphics hardware today is (somewhat) less sensitive to overdraw, but WAY more sensitive to the number of draw calls you make. I was making a DrawIndexedPrimitive call for every single face - which is extremely slow & bad!
My solution was to combine faces - i would group them together in "facegroups", where faces having the same texture & lightmap would be combined using a depth first traversal of the tree. I set a max limit on the number of faces, but I have never hit it yet, thus it doesn't really matter that I used depth-first as opposed to breadth-first... This yeilded excellent performanec gains. Of course, i kind of lost the benefit of having a BSP in the first place - although the PVS data generated was still extremely valuable in culling entities and non-visible facegroups.
However, my rendering was still a bit slow.. I had experimented with Parallax mapping and Parallax Occlusion mapping previously, but I had turned it off (or so I thought). I had thought that maybe fill rate was the bottle neck (even with PM/POM turned off), but it turns out, on both my GeForce 6800 and ATI X1400, that the vertex shader was the bottleneck. The (more than a few) matrix multiplications needed to calculate the tangent, binormal, normal in object space were tearing up my GPU apparently... oh well... once i fixed that, it again greatly improved performance.
Anyway thats its for now for my level saga. Hopefully that wasn't too boring - consider this "to be continued" Game Engine: CubemappingRecently I added cubemapping to the game engine. For such a simple feature, it can really add a lot of ambience. Also, before I knew about cubemapping, I'll admit t hat I was a little tricked by Half-Life 2 - of course back when I first played that I knew a lot less about graphics =) I was like, oh man, every surface in the game is reflective! Thats amazing! When you learn that it is really a pre-rendered approximation, it loses a little of its magic, but it still is extremely effective in many areas. This will definitely be a keeper, and I would like to expand to include some cubemap-type shadows (i've seen some pics from other games, and its pretty neat). There was one funny bug with it though - the first-person weapon models don't get a view transform applied to them - thus when you rotate your view around, they don't get "rotated" and always reflect the same area in the cubemap. Its pretty noticeable unfortunately... but it will be fixed.
Screenshot for your viewing pleasure:
|