All ideas expressed in this blog are temporal and any resemblance to manifested reality are purely co-incidental. The forgoing is a casual musing from a fellow coder, not an iron clad promise of features and products - The Law
Almost Deferred I was all set to start some deferred rendering code when I realized that my editor still had a few missing ingredients. It could not control the limb visibility of the new instance stamp objects, it did not respect the rotation of the object either and you could not delete the object from the edited scene. Essential features you will agree. Hide, Rotate and Delete It took some time to ensure the base functionality was in place, and then it was a quick step to re-compile the editor, first making sure the limb visibility was set on the instance object before making it into an instance stamp, and then sitting back and watching it happen. It was quite a buzz seeing the logic that once served a different object handling system work nicely with the new one, as internally it could not be more different to what we had before. Hiding limbs and rotating objects, a key need within the editor, where to some degree anticipated and they presented no problems. Delete however was a minefield.. So much of a minefield that it's not entirely perfect (or finished) yet, as this process involves the pain of locating the mesh data within the buffer, deleting it and then shuffling the remaining valid mesh data (vertex and index data). There are also multiple instances in the editor that add and remove the objects so I need to catch each type and ensure the right data is produced. Further, as I expand the capabilities of the instance stamp system further, my ancient reliance on the MAP() array inside the current FPSC code is also starting to appear quite redundant. No sense storing this reference data twice, once in DBP arrays and once in the DBP object engine. My thinking is to detect and remove all reliance on the DBP array side, effectively deleting all references to this map array so that when I choose to increase the level size, whatever that becomes, I don't need to worry about fixed size arrays in the actual DBP engine code. It's a big job though so going to tackle that when other more identifiable tasks have been put to bed. Deferred Sample Selected Many coders learn more from a simple cut and paste example than any amount of books and documentation on the subject, and I am one of them. I have now selected my uber simple demo and it looks something like this:
That's right, the old teapot makes a comeback once again. This little fellow taught me DirectX in the early years :) What this new demo does is produce a very simple four render target example of deferred rendering, and does so almost entirely within a single shader. I have copied the code below, so I hope the author does not mind (credited below), so you can see how simple it really is: //------------------------------------------------------------------------------- // Name: deferred.fx // Author: Nikita Kindt (nk47@bk.ru) // Last Modified: 17/06/06 //------------------------------------------------------------------------------- float4x3 c_mWorld; float4x3 c_mView; float4x3 c_mViewInverse; float4x4 c_mProjection; float4x4 c_mViewProjection; // light parameters float3 c_vLightDir; float4 c_vLightPos; float4 c_vLightDiffuse; float4 c_vLightSpecular; // material parameters float4 c_vMaterialDiffuse; float4 c_vMaterialSpecular; float c_fSpecularPower; // textures texture2D c_tDiffuseMap; texture2D c_tSceneMaterialMap; texture2D c_tSceneNormalMap; texture2D c_tScenePositionXYMap; texture2D c_tScenePositionZMap; sampler DiffuseSampler = sampler_state { Texture = <c_tDiffuseMap>; AddressU = Wrap; AddressV = Wrap; MagFilter = Linear; MinFilter = Linear; MipFilter = Linear; }; // rendertargets sampler SceneMaterialSampler = sampler_state { Texture = <c_tSceneMaterialMap>; MagFilter = Point; MinFilter = Point; }; sampler SceneNormalSampler = sampler_state { Texture = <c_tSceneNormalMap>; MagFilter = Point; MinFilter = Point; }; sampler ScenePositionXYSampler = sampler_state { Texture = <c_tScenePositionXYMap>; MagFilter = Point; MinFilter = Point; }; sampler ScenePositionZSampler = sampler_state { Texture = <c_tScenePositionZMap>; MagFilter = Point; MinFilter = Point; }; struct VS_INPUT_BUILD { float3 vPos : POSITION0; float2 vTex0 : TEXCOORD0; float3 vNrm : NORMAL0; float3 vTan : TANGENT0; float3 vBin : BINORMAL0; }; struct VS_OUTPUT_BUILD { float4vPos: POSITION0; float2 vTex0 : TEXCOORD0; float3vWorldPos: TEXCOORD1; float3vWorldNrm: TEXCOORD2; }; struct PS_OUTPUT_BUILD { float4vMaterial: COLOR0; float4vWorldNrm: COLOR1; float4vWorldPosXY: COLOR2; float4vWorldPosZ: COLOR3; }; VS_OUTPUT_BUILD vsBuild(VS_INPUT_BUILD i) { VS_OUTPUT_BUILD o; o.vWorldPos = mul(float4(i.vPos, 1), c_mWorld); o.vPos = mul(float4(o.vWorldPos, 1), c_mViewProjection); o.vTex0 = i.vTex0; o.vWorldNrm = normalize(mul(float4(i.vNrm, 0), c_mWorld)); return o; }; // psBuild() // put geometry data into render targets PS_OUTPUT_BUILD psBuild(VS_OUTPUT_BUILD i) : COLOR0 { PS_OUTPUT_BUILD o; // material float4 vDiffuseMaterial = tex2D(DiffuseSampler, i.vTex0); o.vMaterial.rgb = vDiffuseMaterial; o.vMaterial.a = 1.0; // convert normal to texture space [-1;+1] -> [0;1] o.vWorldNrm.xyz = i.vWorldNrm * 0.5 + 0.5; o.vWorldNrm.w = 0.0; // position o.vWorldPosXY = float4(i.vWorldPos.xy, 0, 0); o.vWorldPosZ = float4(i.vWorldPos.z, 0, 0, 0); return o; }; struct PS_INPUT_LIGHT { float2 vTex0: TEXCOORD0; }; // psLighting() // uses data from textures (previous render targets) float4 psLighting(PS_INPUT_LIGHT i) : COLOR0 { float3 vDiffuseMaterial = tex2D(SceneMaterialSampler, i.vTex0).rgb; float3 vSpecularMaterial = tex2D(SceneMaterialSampler, i.vTex0).a; // normals are stored in texture space [0,1] -> convert them back to [-1,+1] range float3 vWorldNrm = (tex2D(SceneNormalSampler, i.vTex0) - 0.5) * 2; float3 vWorldPos; vWorldPos.xy = tex2D(ScenePositionXYSampler, i.vTex0).xy; vWorldPos.z = tex2D(ScenePositionZSampler, i.vTex0).x; float3 vLightDir = normalize(c_vLightPos - vWorldPos); float3 vEyeVec = normalize(c_mViewInverse[3].xyz - vWorldPos); float3 vDiffuseIntensity = dot(vLightDir, vWorldNrm); float3 vSpecularIntensity = pow(max(0, dot(vEyeVec, reflect(-vLightDir, vWorldNrm))), c_fSpecularPower); float4 color; color.rgb = vDiffuseIntensity * c_vLightDiffuse.xyz * vDiffuseMaterial + vSpecularIntensity * c_vLightSpecular.xyz * vSpecularMaterial; color.a = 1.0; // here we add color to show how lighting pass affects the scene color.rgb += i.vTex0.rgr * 0.5; return color; }; technique buildPass { pass p0 { VertexShader = compile vs_1_1 vsBuild(); PixelShader = compile ps_2_0 psBuild(); CullMode = ccw; FillMode = solid; Zenable = true; ZWriteEnable = true; ZFunc = less; StencilEnable = false; AlphaBlendEnable = false; AlphaTestEnable = false; ColorWriteEnable = red | green | blue; } } technique lightPass { pass p0 { VertexShader = NULL; PixelShader = compile ps_2_0 psLighting(); CullMode = none; FillMode = solid; Zenable = false; StencilEnable = false; AlphaBlendEnable = true; Srcblend = One; Destblend = One; AlphaTestEnable = false; ColorWriteEnable = red | green | blue; }
};
Well, maybe if you are not 100% schooled on reading shader code, it might seem a little daunting, but for anyone who has written a shader, you will start to ask what all the fuss was about. I am not keen on using FOUR render targets, and the above example is really just a skeleton for what the final shader will end up looking like, but I really like the fact we have a VERY simple template to start from. It is much better to start from a simple shader that works to a complex shader that does not work, or worse an empty shader you have to write from scratch. If anyone knows of a good shader authoring IDE that detects compile errors in real-time and works with DirectX 9.0c shader code (i.e. VS1/PS1/PS2/PS3), send me a few links!
Signing Off Hopefully going to get an early night today so I can be up bright and early for my drive to Crewe and train down to London. Friday I meet the secret FPSC investor and the guy you will all owe a beer to when the Reloaded project is complete. When the Kickstarter campaign failed, this amazing fellow swooped in and backed the whole project personally. The least I can do is buy the fellow a meal in our countries capital (if he lets me pay). I will be staying in London until Saturday so there will be no blog tomorrow, but I will provide a special report on my secret meeting over the weekend (or Monday), but given it's secret there will be precious little facts divulged. I will see if I can take some photos to lighten the blog and provide some relief from the sea of code I am drowning you in.
A Nice New Video I think you will like today's video, direct from the art hub we call Mark, and a massive reveal about just how capable the Reloaded enemy characters are going to be compared to the classic character. Rather than twitter on, the video does a great job of giving you a thorough overview:
As you can see, amazing animation and when coupled with some really intense AI control you will be hard pressed to even find these guys let alone shoot one. As I say, can't wait to get started but priorities place my firmly in level engine land. Level Engine Land I am hotting up to the idea of deferred rendering (and cascade shadows for the outside) which of course leaves the issue of shadows on the inside. One of the links provided to me from the comments section called DeepDeferred made use of what I suspect was geometry shader based shadow volumes to cast shadows based on a single point light in the scene. It was quite dramatic and reminded me of the updated Doom 3 when it first came out. The advantage of baked lights is that you can have fifty shadows cast and crossing all over the place to create a really rich detailed scene. As much as lights contribute to a scene, it's really the shadows that make everything pop, and as some users quite rightly pointed out, deferred rendering does not mean shader rendering, just surface lighting. Shadows is a whole new thought process, so we will leave that particular item alone for a while and let it brew! Signing Off I am now at the stage of adding my first deferred rendering attempt to the instance stamp engine prototype, and although I have a grasp of the theory it's not until you code and run do you get a sense for what it is. I will warn the world in advance that the assets used will be legacy artwork, and not the final Reloaded segment and entity art. It sounds like the majority of you want to see deferred rendering go in, and who am I to fly in the face of what the customer wants, so deferred it is. Now you can argue among yourself exactly how we handle those fifty real-time shadows ;)
Backlog Mountain As I had a long weekend, thanks in part to a UK bank holiday and birthday party, the PC was not touched for the best part of three days and as such, plenty of emails and letters to wade through on my return. I have a few short days to squeeze some coding into, and then I am off Jet-setting again to meet our mysterious Reloaded investor who is in London for a few days. All my travel arrangements are booked, printed out and my car is due for a quick service Wednesday to ensure everything goes smoothly. I am really looking forward to the meeting, and as I am in London, also looking forward to finding a nice Irish pub dispensing the very best Guinness. So even though your friendly neighbourhood Lee has been barely active, the Reloaded universe continues to turn with the small team growing around the project. Tracers Ahoy The tracer prototype is coming along nicely, with blazing streaks of heat whizzing past the players head and scorching the air. The final visuals from mark and the sound contributions in the module really came together and I am excited to add it to the engine when the time comes. The system is also pretty configurable from the gunspec file as well, so modders out there with ambitions to create new Reloaded weaponry will have a great time!
Firstly sorry for the classic art (not representative of Reloaded art), and secondly, check out the white line at the bottom of the screen. This is a tracer that fires from the barrel of specific guns to give you some idea how close that enemy bullet was. The scene will get pretty lively during the action, and tracers will be a valuable element of that visual splendour. Character Accent Another wonderful surprise from the art lab as we get to see more of the versatility of our principal enemy protagonist. Here we see him climb a two segment high ladder, complete with commentary from the man himself:
Of course I am itching at the bit to get stuck in and add this to the engine, but I have to practise patience. I have a level engine to put back together, a lighting model to decide on and some basic physics and occlusion so we can run around a typical level and see some normality again. Signing Off My brain was not entirely idle over the weekend and it did turn once or twice to the request made on one of the comment boards that the level size should be 500x50x500 rather than 20 units high. While it is true such a dimension would not be exploited by the masses, it suggests that at least one game designer wants to see this happen and we are not in the business of limiting the engine before we leave the starting gate. I did have the notion of writing some quick speed prototypes to compare the extra performance hit from turning the two dimensional array access into a hash table access approach. What this means in real money is that instead of a fixed level limit of 500x50x500, you would have an effective level size of around 99999x99999x99999 and a lower memory footprint as we would only expand the hash table as new reference elements where added to the map. Sounds great in theory, but where an array access can be less than 50 cycles to complete, a hash table interrogation could take thousands of cycles, and when you then multiply each write/read by the iterations demanded of the current system which constantly creates the level as you go, it might start to impact on available cycles for the rest of the engine. I am also warming to the benefits of dropping baked lights altogether and opting for a deferred renderer that only uses dynamic directional, spot and point lighting for the entire scene. The biggest win is that the memory footprint will be smaller and the lighting results when editing and playing much better. The potential downside (or disaster) is that performance will be such that only dedicated graphics cards could run with all features switched on. Some good news about referred lighting is that you can gain more performance (quite a bit more) by simply reducing the display resolution as the lighting is based on how many pixels the screen space has to deal with, so you can get some great visuals on an average PC by simply shrinking your display. It also means we spend more time on forward thinking technologies, and less time on legacy solutions, which ultimate future proofs the engine for years to come. The deep thoughts continue...
Inside the Map Editor A small triumph today as I manage to get the Instance Stamp system tentatively working inside the current FPS Creator Reloaded map editor.
What you are seeing is the system that only creates segment renders when the camera needs them operating inside the map editor. You can also see, there is no lighting being applied and they are all using a single texture, but there are no nasty artifacts and you can paint segments as normal. You also cannot delete segments at the moment, though this functionality is now added to the instance stamp system. From this foundation I can start to craft in all the bits needed to get it back to where the map editor was about six weeks ago. Might seem like a giant step backwards, but given a few days the new system will be able to edit segments as before, but now those levels can be 500x20x500 in size and the memory footprint won't go above 1GB unless seriously taxed with polygons. The Long Walk Before turning my attention to editor integration I did a quick test and discovered it would take you just under 5 minutes to jog from one end of the new Reloaded level to the other end, which is a long time when you consider that is in a straight line along the shortest distance of the level edges. Now consider how long it would take you to fill a space 500x20x500 (or even 500x500) with meaningful detailed content and you will realize that by coding the engine for the worst case polygon scenario, the actual usage patterns would hardly scratch the surface and protect both memory and performance levels. There are some unknowns still be to discovered such as the extra memory footprint of the mip-mapped textures, the sound and music, the memory required to store and operate all the AI, the data banks that drive all the logic of a level and who knows what else. I don't think we're going to get infinite worlds here, but we should end up with a level too large to populate in a single sitting whilst remaining inside our maximum memory capacity of 1.8GB. Light and AI It's great to see comments and conversations spring up around these two issues, and as much as AI is an exciting feature to think about the code will have to wait until we have cracked the fundamentals. It looks like the voting scales are ever so slightly leaning towards the deferred approach to rendering, and I am happy to leave the subject bubbling a while longer until my brain leaps out the bath and yell eureka. Quite rightly, Mark warns of severe performance issues when rendering deferred, as you really do need to render the scene at least two times. With the forward render layer trick for transparent and secondary shader elements, that count increases to three. When you add shadows through CSM, you are anywhere from four to six times. I don't need to tell you that all this spells massive slowdown if your graphics card does not have the video memory and GPU grunt to process all that. It could be a quick and dirty prototype is the way to resolve this question, and then try it out on what we all agree would be the Reloaded minimum specification. Signing Off I am taking the weekend off to visit family and friends in my shiny new car (well, new to me) and probably doing to have a beer or three before the weekend comes to a close. For those who asked, the car is a Honda Accord, voted the most reliable manufacturer seven years on the trot. It's bank holiday Monday here in the UK I think but I will be cracking on finishing off Instance Stamping in the editor, getting textures and shaders working there again and ensuring the same functionality exists as before. From there we can look at lighting prototypes and basic physics so we can run around the new instance-stamp universe and jump off crates! If anyone can find some good deferred rendering demos that run on very basic PC's, send me the link so I can try them against my array of devices. See you Monday!
Happy Camper A nice day indeed. I cleared my inbox in about 15 minutes, and was able to dive right in today. Thanks to new improved LOW LOD meshes from Mark, I was able to get the prototype for Instance Stamping back to a nice smooth fluid 120fps experience, and also discovered the stutter was caused not by the iterations to process the vector transforms to get parent mesh data into the buffers, but the constant creation and deletion of buffers that where found to be too small. By predicting the final size of the buffers, and making them larger initially, such a release/recreate was avoided and everything smoothed out again. Hurray! All these issues are pointers to watch out for when it comes time to integrate and refine within actual game and editing scenarios, but it was necessary to go through it. Phone Interview I also had the pleasure of being interviewed for a Case Study on the perils and potential of Perceptual Computing development. Had a great call and imparted lots of little coding gems, so hopefully you will get to read that soon. I will post the link when it goes live. It's being commissioned by Intel and professionally authored by people who do this for a living, so I am looking forward to reading it myself. Surprise Email As you may know Mark's primary mission in the project right now is the creation, rigging and animating of our central character for Reloaded. It's a lot of work, especially as our mandate is to push the envelope. An email dropped in this evening which got me quite excited and shows the potential of the AI system when I finally get around to coding it.
For some reason the Google Blogger is not showing me the YouTube videos associated with my account, so here is the link instead. As you can see, having these as part of the character behaviours will mean making a successful hit has become much more challenging, and if you don't know they are hiding, having them leap over an obstacle and start firing will come as a real shock. Can't wait to get it into the action.. The Lighting Question Thanks for the comments so far. I have done some brief research already and it looks like we don't need to choose between deferred and forward rendering as such. A technique called 'Cascade Shadow Mapping' will allow an entire scene to be shadowed from a single spot of light up in the sky. I say spot as that is essentially what the technique uses (spot light) so would not be ideal for a point light source for an interior scene but perhaps the idea of baked lighting and then CSM for outdoor scenes would be the solution. The research and thoughts continue. I am curious what hardware my readers have in terms of graphics card right now. A major factor in whether or not to 'defer' is how many users have the graphics cards required to run the technique. Deferred rendering relies on drawing a large portion of the scene three times over, as opposed to the forward renderer which can do it in one. For this you need monster graphics horsepower and plenty of video memory, so what do you guys have (and what does your end user have)? Signing Off The evening is not over for me as I want to put another hour into moving the Instance Stamp stuff over to the map editor to see where we are. It never pays to stay too far away from your engine, and as much fun prototypes are you cannot sell them and they are by there very nature disposable. I might also spend an hour looking at the Bullet physics SDK and seeing what kind of demos and games have been created with the technology.
Plenty Code Crunched A full day of coding, both last evening and this evening sees the low and high LOD system implemented. I also added in some test artwork from Mark for the segment visuals to see how the popping would look.
I have run into some logistical issues however as performance is very much tied to the size of a single segment, how many of those segments are in close proximity and the cumulative processing required to fill the buffers as you traverse through the space. If I spread out the segments, all is smooth. When I bunch them up, I get stutter as I move through the level. Not ideal by any means, and something will have to give. Currently my high LOD test segment uses over 3000 polygons so one thing that might give is to set an upper limit for segment art. Thursday will see these logistics put under scrutiny and some rules established so I can have 120fps smooth scrolling through the level with the LOD system in play. I can report the 'LOD popping' is minimal with my test segment which is good, and I have also increased the size of the overall level from 200x20x200 to 500x20x500 as when I walked from one end to the other I felt I needed more distance, and it's only an extra 50MB (with further optimization in memory use possible). The Lighting Issue A new situation has emerged that might affect deadlines, and just like the memory resource bar and massive levels was discussed in the blog, I felt it was a good idea to broke the new subject here as well. I was about to add the real-time light mapping system to the instance stamp code I have been working one when I got an email from Mark suggesting that adding directional light to the level would not work in cases where a soldier or object was placed alongside a wall that would cast a shadow over the subject based on the angle of light. For an outdoor scene, it is common in some FPS games that a single directional light representative of the sun would allow all objects in the scene to cast a shadow. This shadow would render on the floor, walls, characters and objects of the scene. The common technique used is a variation of shadow mapping and uses a second render from the light position and clever shaders to achieve. It was suggested that before I embark on adding my real time light mapper to the new static scene system, we look at the possibility of adding dynamic directional lighting into the scene, and perhaps even deferred rendering.
It must be stresses that starting a series of prototypes to experiment with deferred rendering techniques, and implementing those to replace reliance on baked light mapping would add considerable time to the project deadline. Although I am familiar with the technique, I have not created one personally and there are bound to be a hundred small details that relate to creating a deferred rendering system for an entire game engine. It would also mean we basically throw away the real-time light mapping system developed so far as a dynamic lighting system would not need secondary shadow baking and surface lighting. Rick suggests we implement both, baked lights for interiors and dynamics for exteriors, but this of course means all the work of a deferred renderer, plus implementing, perfecting and then combining these two techniques into the final engine, producing the most amount of work.
One advantage of dropping baked light-maps is that no memory would be required to store the UV data sets and of course the light map textures, which when considering a 500x20x500 level, would amount to quite a sea of lightmap textures and most certainly some form of streaming system to ensure the texture and UV data was streamed in as the level scene required the data. Decisions Decisions I am loathe to drop code that I spent weeks theorizing, coding and perfecting, but at the end of the day it is about creating the best product and if that means smashing the sculpture to pieces and starting again, then these are the hard knocks of game development. I have decided to crack on with things that need coding and not debating such as getting the static LOD system smooth and predictable, adding it to the main map editor so I can edit with the new instance stamps, and then seeing what I can do about physics and player controls. Physics and Player Controls Just to highlight that game engine development is not all peaches and cream, another issue to tackle in my immediate future is the fact that as the geometry is being constructed on the fly as you get near to it, so to does the physics geometry. In the current FPSC all physics level geometry is loaded in at the start and stays solid until you go to the next level. Apart from placing pseudo static boxes, I have never tried generating polygon data and adding it to a physics scene on the fly. My mind tells me such a thing would introduce instability and cause distant objects to start flying out of the scene, and at the very least add more processing overhead as you advance to new parts of the level. A prototype will need to be created to answer these questions before I can introduce physics to the map editor and allow the user to drop down and run around the instance stamp based level. All these things can be worked on without answering the lighting question, so we have some time to debate the pros and cons of creating a whole new shader based lighting system over the traditional baked lighting system we have right now. Its worth noting both COD MW2 and Black Ops are forward rendered using some baked light-maps, and they look just fine. Deferred rendering you would see in games such as the latest Far Cry. I am not pinning my 'alternative flag' to deferred, simply opening up the possibility for developing a new lighting system that does not use baked lights and instead produces all it's lighting from clever shaders and even cleverer techniques. Signing Off I am quite pleased with how the LOD work went, and with some tweaking of buffer sizes, asset sizes and load balancing so not too much happens in the background, I should be onto the next thing soon. If anyone looks at these daily antics from outside the cult of coding, you should begin to realize just how fluid development can be. Where I to stick with a deadline and meet it, I would not be implementing instance stamps, I would probably already be working on physics and player control systems, running around a light-mapped level created under the umbrella and protection of a resource meter. The big question is whether the lighting question is one of those deal breakers, or whether we can stick with baked lighting and return to outdoor directional dynamic shadows and lighting during an update after Christmas. Feel free to post some comments on this!
The Question Of Range As I did most of my backlog yesterday, I have spent most of the afternoon contemplating the meaning, benefits and challenges of LOD. Before I set to work creating code, experience tells me that it's vital I have a very clear picture in my mind how it should work and all the intricate dependencies that will be hung from it. I spent some of Monday and most of today closing my eyes and thinking about exactly how it must work to solve all the issues thrown up. One issue is what the performance will be when you are constantly adding polygons to the LOD levels (both high and low LOD buffers), what the popping will look like if you place the range at 10 tiles, what the view would be from upon high when editing the level from a high vantage point in the map editor, what the trade-offs are between memory consumption, performance of writing polygons on the fly and the final visual. Most of these issues will be solved by experimentation, and trying different things in the prototype until I get a glimpse of the true shape of this sculpture. Do not despair however as I have made a start with some code that was needed as one of the first stepping stones. I needed to know that adding the high level LOD polygons to the visible range of the player's current position would not be too slow. I thought I had solved this before, but when I used a high polygon segment and then painted solid tiles with it in every direction, it started to crawl within a range of 5x4x5 which would be completely unworkable in the real world. Given these new performance measurements, I improved the situation by giving the polygon builder an early exit counter so it would not try to add all the polygons in one go. Instead, it will now add a few meshes to the buffer, then exit to resume other program tasks, and on the next cycle do a few more. This spread out the workload and I ensure the player cannot see this 'slower build' by capping the speed at which the player can move through the world, so you cannot effectively catch up to the edge of the world that is still being build (added to the buffers). I also used SYNC RATE 0 to remove the artificial cap which was causing stutter with the above technique. Removing the sync cap solved the last of the glitches and I was able to run through the level at anywhere between 130fps and 700fps viewing full high polygon segments up to 10x5x10 which was a reasonable distance for the high polygon meshes. Variable LOD Range and Nested Buffers
As I coded the above I also realised it would be super powerful if the range at which high lod turns to low lod should be variable and determined automatically by the load on the system. So when the engine detects lots of load (polygons in the buffers), it will bring in the LOD range to handle and control polygon and draw counts, and will extend it where the load is low allowing high polygon meshes to be left alone even when rendered at some distance. The big code task is ahead of me though, and the technique I am mentally wrestling with is a nested buffer system which will make two passes of the scene to add one lod level and then the other. Separate planes of existence if you will, but overlapped to service the final render. I would then cut a hole into the low lod universe where there are high lod meshes handling the render at that location. I am still not happy about the approach as it leaves some questions open about how the two universes join, the additional performance drain of adding many more buffer writes each time the player moves, and what to do about all the texture render state changes as I attempt to render a potential 40x20x40 view in one go. An alternative was to create a huge universe of low lod and simply hide and show parts of it as the high level lod traverses through the reference data, but this worked out at over 10 million polygons and too many draw calls. I will stick with the first theory as that protects memory, render speed and is the most adaptable solution. I only hope the performance drain can be managed when the time comes. My ace in the hole fall-back is of course the fact not everyone will be filling every 200x20x200 tile grid with a high density segment object 87K in size. I imagine most levels will be large open terrain with walled cities of tightly pressed in buildings, or something :) Signing Off Normally I would have another two hours of coding, but it's just gone 5PM and the sun is shining so I am going to recharge out there for an hour, then chalk off the whole evening to creating some code which reflects the above theory. The only way to know is to do, and so that is my plan before the next blog.
Backlogs and Bitmaps Well after the turmoil of motorway crashes and a weekend of gardening I am back in the saddle to get cracking with code. I had to clear three hours of email backlog, letters and a few tweaks to some existing products, but was able to rescue half the day for Reloaded fun. New Bitmap Font System The current text system in FPSC is pretty crude and relies on the true-type font system built into DBP, which is fine for regular text applications but not for high end super graphical games. For this you need bitmap fonts, and to this end I have created a prototype which can create and use bitmap fonts from the AGK universe.
This was was triggered by the current stage of the inventory panel prototype, which now requires proper text to fill in the gaps. Hopefully with the font renderer in place, I can show a new inventory panel screen shot soon. Static LOD in Instance Stamp As you may recall, we left our Instance Stamp system requiring some LOD magic to reduce the number of polygons the user can see at any one time. This will help control large scenes within the engine, and allow the addition of large meshes that have LOD built into them. This brings me to note the reality that all legacy model packs which do not contain any LOD levels will not use the technique I am about to implement this evening. All Reloaded assets will be developed to include certain number of LOD levels so that this technique, and the dynamic LOD you saw before work out of the box. Old model packs can still be used, but will not take advantage of LOD. You might be thinking that this LOD 'thing' can be done automatically, but it's not that easy and the results can be quite wild. In order to get the best quality visuals and results, I am opting for artist defined LOD. Of course there will be nothing to stop artists who previously authored older model packs to revise their assets by adding LOD, but that will be down to the individual artists. We may also update packs authored by TGC if there is enough community support for this, but might incur some kind of upgrade cost per model pack as there is quite a bit of work involved to do this. Signing Off Once I crack static LOD, I will move swiftly onto getting light mapping in there so I can get back to where I was a few weeks ago, operating from within the main map editor :) I think though that this diversion was well worth it and will make a better product when we finally release to the masses!
Many Roads What a trio of days it has been! Who would have thought picking up a car and having a meeting would take three days. It's also true enough that 800 miles of road takes it's toll on the mind and body, and when I sat down at my keyboard two hours ago it suddenly hit me that I was exhausted. I had planned to blog Wednesday and Thursday night, but 3G is still very expensive in the UK and I did not think a few hastily constructed sentences with little to do with Reloaded would be welcome or of any value. Instead I ate and slept. On the way back up the spine of the UK, there where no less than 3 major accidents on my intended route, and a minor collision that happened right alongside me when a big red van hit a little black car with an explosion of glass. Amazing to witness, but powerless to help as me, the victim, the perpetrator and hundreds of my closest fellow drivers where all parked on what I affectionately called the M6 car park. Reloaded Developments But enough of such things, you read this for juicy reloaded news and the latest is that we've further refined our business strategy to focus even more intensely on the support grid that will hold the Reloaded product to the highest standard. No legacy graphics will be used, only top quality artwork. Every screen shots that comes from the official product and media packs will be top draw from every angle, a simple virtue of being rendered with high resolution textures, high quality light mapping and nifty shader effects. Even a simple specular tint adds so much, and specular is the least of the features we plan to squeeze into our universal shader system. We also discussed a few more ideas, that for the sake of discretion and prudence, I will not reveal today, but you can mark this day in your calender to look back on and say, that was the day Lee didn't tell us about it :) Signing Off Right now I have played catch-up on all the urgent emails, and delegated the rest. My blog is back up to date, and my office once again resembles one. I am absolutely famished and Time Team starts in 20 minutes so I'm going to finish off a few more paper rustling tasks and call it an early night. As the last few days have eaten up valuable coding time, apart from putting some grass feed down in the garden, I will be resuming my work on the LOD system of the Instance Stamp technique which I am almost certain it will work, and then get the light mapping working on this new geometry (no small task). Getting the whole shooting match back into the editor will be the icing on the cake, but I am not falling for the illusion that I can achieve all that in a fragmentary weekend after the longest stint of driving I've done in a long time. Reality may involve a lot more sleep and a lot less code!
The World Of The Small and Distant My work on the Character prototype with respect to LOD rendering naturally lead me back onto the subject of LOD for the static system I currently have. I must have played and replaying the current prototype for 2 hours, wrecking my brain to come up with a system that would allow me to reduce the ENTIRE view of 80x80x20 tiles to a polygon count and draw count that would run fast enough for the editor. Today I cracked it. The Massive Level LOD Technique Using the same concept as the current design, at the same time meshes are added to buffers to create shapes that need rendering, my new system will eventually add LOW LOD polygons to a a series of LOD buffers that overlay the existing level. These LOD buffers will be three times the special size of the current buffers but contain few polygons per tile to avoid overloading them. As they will almost always be rendered in the distance, and draw calls become the bottleneck at this point, having buffers 9x9x1 should not be a problem. I am keeping the vertical slices to one layer per buffer as when editing from top-down, I want to be able to pop the entire layer up and down a LOD level as I move closer and further away. I ran some numbers, and it looks like I can keep within the 1600 draw calls no matter where you look down on the level map. By creating the LOD buffers alongside the main buffers, the performance speed for transitioning between the two will be instantaneous and I only need to transition to LOW LOD when all nine regular buffers that represent it are outside of the range at which polygons pop into high quality. Adding the mesh data to the buffer will also not hit performance as it has become clear the bottleneck at high altitude is simply draw calls per cycle. Many LOD Challenges There are a few bruisers still to solve though, not least of which that every texture and shader will require it's own buffer (or a system to render subsets from within the buffer using different textures). Alternatively, a new texture map can be built dynamically as the LOD buffer is created, that steals texture data from the real texture. I effectively create a patchwork quilt of small textures which means the buffer can be drawn once and all the textures would be visible in the distance in one go. More experiments are required to see if creating these textures would be too much of a hit, but I suspect I can do them in a thread so the textures are gradually refined as you move through the level. Another challenge is to watch out the extra weight of additional LOD polygons does not creep the memory usage back up to former levels. No sense doing all this work only to have the same problem from another direction :) The result of all this work will be an engine that only renders high quality geometry where it's needed, and then paints broad strokes for the rest of the distant scene. This will show best when it comes to performance, and those users concerned about what graphics cards they have will be pleased that LOD is being built into the core of this new technique.
Prototyping these new LOD ideas will be my task for the remainder of this week, with the aim of allowing me to populate an entire level with segments of various polygon counts, and roaming around it from a top view and first person view without the frame rate dropping below 100fps (on my PC that is). This will establish that we have managed to cram everything we wanted into the memory whilst allowing a very fast scene system to render effortlessly. Only once these challenges are met shall I move back to adding light mapping data to the meshes, and modify the DarkLIGHTS module to integrate with this new system.
Signing Off As you may have gleamed in a previous blog reference, my quest to buy a replacement car (for meetings and suchlike) has finally come to an end and I have now made my purchase. I was going to buy a real ropey (but cheap) car but I had a chat with my mechanic friend and he advised me to walk (er, run) away from it. I have settled for something a little more reliable for those long motorway journeys. Be prepared for a much delayed Wednesday's blog as I have chosen that day to go collect the offending vehicle and put some serious motorway miles on it. Let's call it vehicle physics research in case the stretch goals for Reloaded extend that far ;) As a summary until the next blog, really happy to have the opportunity to go through the lowest components of the engine and improve things. We know what we want from a next gen FPSC, and I agree we should aim really really high this time!
Hob-nobbing Day Today I entertained my author friend who writes some of the best books for TGC, and we spent that time discussing all manner of subjects including AGK and Reloaded. When you take out the time it took to eat a lovely meal at the George III and of course my free Segway rides up and down the street where I live, there was not much day light left for actual coding. Oops. Evening Shift To rectify the situation a little, I decided to cram a few hours into the evening and as it happens, I got an email from Mark Blosser throwing me some great advice and links with respect to LOD. This email included a very early test model for the main Reloaded character rig with animation, and I dropped everything. It also included three levels of LOD, built into the object! I whipped up a prototype P005 immediately, and good job too as I discovered that DBP was animating even the invisible LOD meshes. Corrected that, and got the performance improvement I was expecting.
As you can see in the video, when I switch from HIGH LOD to LOW LOD, it goes from 24fps to over 60fps and from over half a million polygons to just under 70K polygons. This is the power of LOD, and something I plan to research in the static geometry system which is still my primary target this week. Aside from the marvelous LOD tool available from the TGC store, which admittedly I entirely forgot about, Mark put me onto a tool called MeshLab which seems to be put together well. I will need to work in the OBJ format but DBP does have some OBJ format capabilities so we will see if I can get some low level LOD in my object and trigger it's use in my monster scene engine. Signing Off It's been a full day for sure, and I also have a full day away from the office schedule on Thursday as well so the week will be a little chopped up. Hopefully Rick won't give me a withering email when he see's the video I sneaked out today. It contains proper good media though and a nice font so hopefully he (and you guys and gals) won't mind. Ignore the 79 other characters in the scene, it's not an attempt to advertise massive enemy numbers in Reloaded, just did that to tax the scene with lots of polygons. For Reloaded, we are going to aim for smart battles with a single combatant out-thinking and out-moving the player, rather than push fifty easily dispatched enemies in your face which does not make for great game play generally. Always a good day when Mark delivers artwork!
We Have Colour That's right boy and girls, we have a texture and shader now rendering the polygons in my massive geometric soup! Here is what I am looking at right now at midnight:
So what you are looking at is quite a lot of magical back-end stuff, and precious little front-end eye candy! The Paradroid System is complete and buffers are now retired until needed again. As I suspected, after a few seconds of roaming the buffers level out at a certain memory usage and stays there which is great. The above scene is a typical 200x20x200 universe with a simple floor segment pasted randomly once every three roles of the dice. As you can see, it stretches from the floor to 20 layers high and off into the distance which I have cropped at 40 segments in any direction. This was the maximum distance you could view in the classic FPSC so should not upset too many people, and until we start adding real assets I am not sure what the final distance should be, and will probably be in the SETUP.INI file so you can change this from game to game. As you can see from the statistics we are really hogging the graphics card which can only manage 22fps with unoccluded polygons, but the critical figure to view is the memory which is only 379MB. That's a third of the potential 800,000 segment tiles filled and rendering in all directions, and it's well within our maximum memory budget. The theory worked! I Could Stop With such a result I could pat myself on the back and have the rest of Friday off, but I am in a coding mood and not tired so will continue onwards. Given what I have now, I need to move it to the next stage of using different kinds of segments. I reduced the default size of buffers from 20,000 vertices to just 200 polygons so I could accommodate such as large scene, but the final system needs to handle buffers of many sizes. Some tiles will just have a floor segment, but others could have a complicated 10K polygon entity duplicated many times, and the system needs to adapt to this scenario. Once this has been dealt with, I can create a more realistic scene with different segments and entities to populate my prototype with. I also need to think about a few levels of LOD while I am in the neighbourhood too. No sense rendering everything within the camera frustum at full detail as a nice carved statue viewed at the very edge of sight would not contribute much to the game except slow it down and hog memory. Signing Off If anyone knows of a good 'free' tool which can take an X file and reduce it down to use fewer polygons, then save it back out as an X file, that would be good. I have used many, but not done so recently, and maybe something new has appeared? Ultimately of course the assets will have professionally prepared LOD meshes for improved in-game rendering. Plenty more to do of course, including putting the light mapping back in, selecting a good universal shader set, dynamic meshes and dynamic lighting, adding physics on the fly, cutting CSG operations directly into the buffer data, the list is endless :) Just like climbing Snowdon, you do it one step at a time!