Friday, September 30, 2011

LL3DLGLD – 10 – Slice 'Em Up!

I was pretty much decided not to post today. I had enough posts for this month, well over quota and I'll be out of  town for the weekend. But I ended up recording a few videos, so I might as well get it out of my system

I implemented vertical level slicing. You couldn't really play if the world wouldn't be represented as if sliced at your current level, not even with 3D. To make things more manageable, the map cursor is bound to you current level. I really need to implement some visual aid that will guide you on how high up your cursor is when moving it in the air and not on a solid surface. Also, replacing the cursor with something else that is not a white box could help a lot I think.

Normally I would go into detail, giving some instructions on how to do slicing, but this topic is self explanatory, especially since I do not slice in the middle of a level. Even if I would, slicing is very simple since I am using a horizontal plane intersection as the slicing point.

Current level slicing is a fairly common feature found in a number of games. I also implemented a second slicing layer bellow the current level, which is always a fixed amount below the current one. This fixed amount can be changed with Page Up/Page Down, even though these keys would be better suited for level select. This second slice level is not that common and it has two uses: sometimes it can help with keeping you focused on a portion of the map, but the real reason is performance. If you don't have enough juice in your machine, decrease the distance between the planes.

I also smoothed out camera movement and except for an awkward disorientation after changing from top down/isometric to full 3D fly camera, I am fairly happy with it. Tree LOD needs to be adjusted a little.

I added a few statistics on the screen, as FPS, total number of triangles the scene is composed of and the number of triangles that actually get sent to the GPU. As said, currently the engine is not smart enough to cull the triangles that are behind the camera, so all the triangles of the scene are processed. Some are still culled automatically, but once I can exclude these triangles from the entire rendering process I expect a noticeable FPS increase. But FPS is good. FRAPS reduced it to 60 and then 30 in the video, but without it I can't really complain. When I get my hands on a weaker computer I can give you more info.

I also greatly improved stability. The 3D engine used to crash a lot. Now I had hours without a single crash. It used to crash due to something happening in the Irrlicht code and I think it is related to the reference counting memory manager from Irrlicht. Out of all the memory management schemes reference counting is probably the most stupid, and Irrlicht version is particularly poorly designed in my honest opinion as a professional. Reference counting at its best frees you of the burden of manual memory management. But with the scheme from Irrlicht, you exchange manual memory management for manual reference count babysitting. I want to keep this blog family friendly, but I am so close to showing Irrlicht a more interesting vocabulary choice in written form.

With these new changes, the engine is ready to tackle all shape related issues. The stuff I do with the shape (what stone is inside) is not fully implemented yet, but the engine can now handle any world of any shape and the code is ready for the next step: becoming a client of DHCore. DHCore is my semi general use game library specialized in this genre of games (which I want to make available in some form or another someday when I have the time; now I need to catch up on my schedule because of the unplanned 3D conversion). The 3D engine does not use DHCore yet, but DwarvesH (the game) and its editor use it. So once the 3D engine becomes a client of DHCore, all the functionality from DwarvesH should work out of the box with the new 3D engine. Theoretically. I have a feeling that it is going to need an intense QA period. Oh, and I need some 3D models.

Here is a video with the slicing mechanism in action. This will be the last video/content that does not use DHCore:

Thursday, September 29, 2011

LL3DLGLD – 9 – Spycam

Finally! I have a functional camera system. I am not going to talk about the implementation because I would rather forget about it, so I am going to talk about each camera mode.

Top down camera
This camera mode is very similar to the top down 2D mode that was available in the 2D engine. That classical 2D mode will definitely be removed since the 3D one is a lot more powerful. In this mode the user controls the mouse cursor by moving the mouse, directional keys scroll the map and we have smooth zoom. I need to add a camera tilt option, so you can view the scene from a top down but angled view point.

The question is what to show with this mode. I decided that I will center the camera on the currently selected level and keep a constant distance every time you switch the level. Without this, the user would almost always follow a level change operation with a zoom operation, because the perspective causes different levels to have different sizes. So levels above you current level won’t be rendered by default. Rendering levels below the current level has some merit to it, especially if the current level has holes in it, so I’ll render a few levels bellow, but probably not all the way down to level zero.

There is one issue with the entire engine: my LOD algorithms work extremely well with first person cameras that are positioned at the eye level for dwarves. It does not work that well with other camera. If I write a special LOD algorithm that takes advantage of the special square shape of the map in this mode, I could obtain a framerate that is higher than in any other mode.

Angled camera
This mode is very similar to the 2D isometric engine, with the difference that this time we have perspective.

Zooming now works fast and without using tons of memory like it did with my failed attempt at providing this feature with a software based approach.

I’ll keep the camera focused don the current level, like I’ll do with the top down mode.

Here, my current LOD works against the specifics of this view mode. A more specialized LOD would increase performance and keep objects on the side of the screen from switching over to low LOD, but writing this specialized method will be harder than in the top down mode.

Fly camera
This third mode is the one I have been using in my previous videos demoing the 3D progress. You start from up in the sky looking down and you move like you do in first person shooter, but flying without gravity. This is an exploration mode and right now I have no plans for allowing you to interact with the world.

First person camera
And this is the final mode that will allow you to jump in the body of one of your dwarves and look around with his or her eyes. Time will stop and you won’t be able to interact with the world, but to make it more fun I’ll allow you to walk. This mode is not implemented yet and will be very similar to the Minecraft experience, except for not building anything because it is an exploration mode.

Here is a video demoing these camera modes:

Wednesday, September 28, 2011

56 – 3D Week 1 in review

Today I am going to talk about the state of the engine after one week of development. First I am going to talk about the good parts, then about the bad parts.

The biggest good point is that the engine is fairly stable and can be easily extended to handle a lot of stuff. Performance woes are always towering over me like a huge gassy floating giant monster cow, but it is under control. Performance may not be extraordinarily good compared to the graphics quality, but it is very consistent, running with comparable FPS on different machines. Using this engine it was easy to add real terrain elevation and current level highlighting:

I also did just a little bit of optimization. Check out the south wall of the map in wireframe:

We have a lot of extra unnecessary polygons. I fixed this:

Of course, I wrote the optimization in such a way that it can handle LOD, so I can swap between the lowpoly and highpoly meshes based on distance, but I disabled this. The only problem is that the low poly models create very muddy textures:

There is no way to fix this without sacrificing performance and adding a lot of algorithmic complexity, but it can be improved a little by better texture work. On the other hand, these textures are only visible on the sides and look so ugly only when they are too long, so normally you won’t notice this.

But the thing I am most excited about is the new forests. I grew tired of the clone forest, so now every single tree is unique:

The trees still look ugly as hell, but it is a nice step up from the old trees that were two cubes.

And now comes the bad.

I think I tracked down the visual artifacts I get to Z-buffer fighting. This game uses a very long viewing distance and we do have some very close surfaces that could cause these problems. I will try increasing the Z buffers to 24 bit.

This issue is not helped at all by the way I am doing collisions. I create an extra polygonal plane and cast a ray from the camera position to the mouse cursor. Where it intersects this “hidden” plane is the position my in game 3D cursor should be. Now this work very well and I am very happy with the precision of the cursor. The only problem is that that surface is not hidden. Collision does not work for hidden objects. So I have one extra surface fighting for the Z buffer right beneath the real surface of the terrain. I tried for hours casting the same ray and intersecting it with a false plane, one that is not made out of polygons, but I failed. I worked fine for short distances, but as distances grow so does precision decrease. So for now I have a hack: I reversed the normals for the surface, making it face away from the camera. Collision still works and Z buffer fighting does not occur unless you are looking at the map from underneath. I also made the surface green, as my maps tend to be green, thus lessening the impact of it. At least you can’t accuse me of not giving creative solutions to problems!

But the biggest problem is the camera. I worked for hours trying to achieve a camera angle similar to the 2D one. Programming the camera is a lot harder than the rest of the engine. I was almost ready to give up and only go with a top down but tiltable camera and a first person fly by camera. Then I managed to get some fairly decent results, so I am confident that I can get at least a workable if not optimal camera angle on the scene in an “isometric” fashion.

As for using a real orthographic camera, using that in Irrlicht gives very weird results. Culling is a lot more aggressive making parts of the scene disappear and setting camera position does not work as expected. SO I’ll continue and investigate the camera with perspective.

Monday, September 26, 2011

LL3DLGLD – 8 – Become a real engine

OK, I am done playing around! I think I have gathered enough knowledge and working snippets of code to put them all together. Here is what we get:

Time to take these random pieces of code that I have been tinkering on for a week and that can be considered a solid starting of point and create a real engine.

The first step is to optimize the creation of vertices based on the logical strips. The new system is less flexible, but faster and a lot easier to use.

The second step is to greatly optimize the actual strip creation. The new method is roughly as fast as the previous one, but a lot shorter. How short is it? Well, it is SHOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORT! Combining these two methods the engine has lost 300 lines of code and now the terrain supports elevation. Elevation still uses a random and quite poor terrain generator, but next time I’ll plug in the real terrain generator from the 2D engine to get some better results.

I also added the ability to switch on and off the use of low LOD grass and trees. Needless to say, each time you turn off one of these optimizations framerate drops. Grass with or without LOD looks almost the same and I’ll leave it off for strategic and first person camera because you will barely be able to tell the difference, especially with elevation. Trees on the other hand have a larger visual impact. For trees the lowest LOD is just to skip them and here it becomes very clear that not having billboards yet greatly reduces visual quality. I also restructured the engine, using good class structure and design.

Here is video with the engine in its current state (while tomorrow I’ll add the smart terrain generator from classical DwarvesH to see how it works):

Let’s talk a little about this engine. The first thing to notice is that it is ugly. And I am not talking about the placeholder cube trees. Generally speaking. And I also have a contrast problem like I did in the 2D one. Even after I add lighting and shadows, I’m afraid I’ll need the help of a professional artist to make it less ugly.

The current level of optimizations is not enough. It works fine right now, but I expect it to work less good when the real terrain is introduced. The general strip conversion is not perfect yet. Top surface conversion is good enough but sides are horribly unoptimized. And since normally you’ll have a floor on top of a wall in natural environments at every elevation change, I am actually using double the needed polygons in these cases. I removed bottoms for now.

And I couldn’t care less! Normally, I would take a couple of weeks and optimize the hell out of it. But it runs just fine for now. I spent a lot of time creating the perfect 2D engine only to discard it once I reached the limits of 2D. Instead of actively optimizing stuff until it is near perfect, I’ll do it passively. Whenever I have nothing else to do and am in a mood to optimize stuff, I’ll do a short optimization session. Over the time, the gains will add up. So performance is not going to be great in two weeks, but it will be in four months (random example periods). If I maintain the old workflow, I’ll finish the game in 10 years. I would like to get something out a little bit sooner.

Bug fixing I’ll still do in the old rhythm. And there is no use to optimize stuff right now since I have a huge glaring flaw in my LOD algorithms: I am using a distance based approach, not a frustum based approach. You are in the middle of multiple spheres and LOD is computed based on the radius of the spheres and object intersecting them. So that’s right: unless you are high up and looking straight down, you are rendering roughly twice as much as you should: the sections in front of you and the sections behind you. Once I figure out a frustum based optimization that can hide sections that are not on screen, I expect my framerate to almost double.

Sunday, September 25, 2011

A few more random videos

With all the experimenting I am doing right now, I get a lot of extra content that does not fit the schedule and content of my posts. I have tons of FRAPS footage that will never see the light of day.

So I decided to put two of the better videos on YouTube.

The first video is actually related to real life progress with he engine that will make it in a tweaked form in the official engine. I am talking about grass floor progression:

Obviously, the formula needs to be tweaked for 3D. Maybe I'll add a little low poly 3D grass on top that grows in height. But the basics are sane.

The second video is a lot more random. I was experimenting with some wooden plank floors, and ended up with something like a slot machine with a lot of slots, where floor tiles keep spinning and spinning until they all eventually stop, forming a random pattern:

LL3DLGLD – 7 – Grass LOD

I am very close to getting the floors to that point where I can easily integrate the new engine with DwarvesH and the entire floor system with all the possible actions and events will work. And since walls can be considered higher floors, adding full wall support should be a technicality.

What I need is to take the floor layer, and make it support different materials in different cells. This covers grass levels, stones and sand. Up to this moment everything was implemented without cheating, but to achieve this we’ll have to dig deep in the trick basket and there is a chance that we’ll have to change the floor model to handle the requirements and limitations of 3D.

We’ll try and create a texture that is made up out of multiple small textures: 

This is similar to mipmapping, and thus this approach will result in visual artifacts at sub-texture borders, but compensating for this is a subject for a different post. I tried to add numbers in each cell in that texture, but I grew bored of it by the time I reached number two, so I added squiggly lines so I can distinguish the cells when applied to a floor mesh:

The big texture has 8x8 tiles, 64, applied to a surface of 30x30, 900, in order. So on the third row, the first 4 tiles are the last tiles from the texture. Now we use the same texture I used in the 2D isometric engine for grass levels:

Grass levels grow in intensity from zero to 100% in wrapping strips and then start from zero again. This pattern is not a natural one, so let’s randomize it:

This is more like it! The same system I had in the 2D engine, but with the added benefits of camera rotation:

And this is where we encounter our first real obstacle: right now I am only aware of a method for creating these cells by increasing polygon count. When we go with a full map, the large number of polygons reduces the performance a lot:

Not only do we have a low FPS, but camera movevement is now severely lagging. Before I address the FPS issue, it is time to optimize everything! We start with the LOD calculation, making it only rebuild sections if really needed. We also assign a new color coded LOD system:

Normal shading is for full detail, red is level one, green is level two, blue is level three, orange is level 4 and muted/grey is the final level, the lowest quality. Here are two shots where we see the system in action from a pseudo-top town slightly tilted view point which could be one of the default camera angles:

We have the center full detail sections, but as we get farther away from the camera the sections switch fast to low detail. With this LOD calculation and optimization method, camera movement is as smooth as could be. The FPS did not change, because we are not doing anything with the LOD info right now. Time to change that! Let’s try something simple first: we’ll assign full grass texture to the lowest LOD level, ignoring the actual grass levels:

Nice!!! If we try extending this to two more low levels, FPS no longer takes a huge leap, but it is still something you can notice.

The system works wonderfully in both the FPS mode, when you are very close to the action, and in the top down tilted mode. And I guess this is as much as you can ask. Once you start removing the restrictions on the camera, we get a small problem that had a life as long as 3D graphics: pop in! I think it is best to illustrate this with a video:

Saturday, September 24, 2011

LL3DLGLD – 6 – So close

The first stage of my investigation and research is slowly coming to an end. There is one more thing to do before starting stage two: optimizing the hell out of double strips. My performance is not as good as it should be. I have a floor layer that has a top surface, a bottom surface and side surfaces when needed. Sides are optimized (only added where needed), but only one by one, not as a strips. Then I have a wall layer with the same structure. The problem is that if in the same cell we have both a floor and a wall block, the top side of the floor and the bottom side of wall are identical and neither should be created. The bottom side of the wall is invisible normally because it is facing away the camera, but to better illustrate my point, I’ll make it face the camera and remove the side surfaces so you can see what is going on inside:

The extra redundant layer is clearly visible. First, we optimize away this layer. My maps are random, so I can’t give the exact same shot twice, but this one is close enough:

If everything behaves as expected, I have practically reduced to half the number of polygons the wall layers uses (only top + bottom) if the entire floor is intact. But there is still a problem. I may be no longer rendering extra wall bottoms, but I am still rendering extra floor tops. Let’s fix this:

And now, for the final shot, with all sides rendered as needed:

Performance has definitely improved, but there are two downsides. First, I am doing a lot more CPU work while determining what part of the mesh to create. So my map scroll/camera rotate operations are not as smooth as they used to be. This can be improved a lot by creating better algorithms. The second problem is that the visual artifacts have become more apparent and some new ones have appeared. I though it was the same problem that is causing the artifacts mentioned in the previous post (and it may be so), but upon further investigation it seems that an extra strip is added sometimes on top of an existing one. I tried to fix it, but kind of ruined it, so I reverted. Anyway, these problems are not that severe. Quite normal actually for something hacked together in a few days while still learning.

I can’t just stop on this note, so I’ll leave you with an early preview of things to come:

And finally, a close-up:

Performance has degraded considerably, but the trees are really poorly optimized. But the new experimental engine can render pretty much as much as it could the first time I presented a 3D engine, but only render. Last time you could interact with the world normally. I am working just on rendering right now. But the big difference is the performance: while the performance is pretty bad, it is at least a hundred times better than last time. Last time it was so bad, I had to abandon the 3D engine because it was unplayable. A GeForce 8800 GT was struggling to keep a double digit framerate.

So the new engine looks promising!

Friday, September 23, 2011

LL3DLGLD – 5 – (p)LOD

We need to add some final touches for this very early single level map geometry renderer: the wall layer must get as smart as the floor layer. This process involves basically doing what I did for floors again, but this time for walls. It is something that is some work, but not much new can be said about it.

So we’ll take a little detour and talk about LOD. Level of detail is a very important concept. It allows you to render the same entity with varying levels of detail so that you can use high detail when something is close and low detail when something is far. The high detail would not be visible on far objects and it just wastes processing time.

I have no idea what the standard way of doing LOD is and if there is some built in support in the GPU or one must do it all manually within the engine. One thing I do know: I can’t do LOD checks for every single entity (tree, plant, animal, dwarf, …) in the game world. Processing all these entities (which I have tens of thousands, if not hundreds of thousands on the same map) takes enough CPU time with all the things the game does even without trying to render them. Adding a new LOD check for each entity would be impossible unless using high end computers.

So I’ll do a LOD check for each section of the map. Maybe there is a standard way to assign a LOD factor to such a section. Something tried and true. I don’t know. So I’ll be using my own slightly heuristic methods. Method number one could be to measure the distance from the camera to the center of the section. Method number two could be to use the minimum distance from the camera to the four corners of the section. I will be using method number two. So here is only one section, very close to the camera:

As we move away, it becomes darker and darker:

In the final shot, it is almost black. Now if we render then entire 300x300 map, we get these results:

It will take some trial and error to determine how “dark” a section at a given distance should be. First, I must do something with this LOD information. Right now I am only computing it, but the possibilities are endless. Let’s take trees for example. At the highest LOD/closest section, the entire section could be rendered with full high polygon models for the trees. Sections that are a little more distant could be rendered with low poly trees. Even more distant section could be rendered only with two 3D stretched cubes, one for the trunk and one for the canopy, with very low resolution textures. Going even further: billboards. And the most distant sections won’t render trees at all. This is only an example. I don’t know yet how many levels of LOD I’ll be using and how much is overkill.

And now back to business! We take this very simple wall pattern:

Update the algorithm to handle holes like we did for the floors:

And use a better, but still placeholder cliff side border algorithm:

Here are a few extra shots:

55 – Poking some holes

It is time to put what I have learned into practice.

I created a simple floor layer and a fly around first person camera. Pressing space pokes a hole in the floor. Marvel at my advanced technology:

At first this might not seem to be related to DwarvesH, but the act of poking those holes is identical to floor digging. This is the same mesh update operation that will be used in game in a not so distant future. Since I talked a lot about 3D in my other series, I won’t be talking about it here. The only new thing is the collision detection anyway.

One thing is worth mentioning though: once I start poking holes, I get some very strange visual artifacts:

These appear and disappear based on camera movement. I do not know if it is because of me doing something wrong.

LL3DLGLD – 4 – Poke

The saga continues! This time we’ll enhance the existing system in order to bring the engine closer to being able to render a floor layer. For starters, we’ll consider a single object and poke some holes in it:

The holes behave correctly, as we can see by investigating both sides:

This is the perfect opportunity to enable strip color coding and see how we created these holes:

So we have one object consisting out of a set of stripes that fill the surface between holes. The number of vertexes has increased to 1392. And we have 696 triangles. I’ll leave as homework the determination of the relationship between the number of triangles and the number of vertices. Now we can experiment with random patterns:

I’ll enable wireframe mode so you can see the triangles in action:

The most intensive object can be created by alternating between full and empty cells. It is sufficient to only do it on the horizontal axis since strips are only created horizontally. Using this method, we determine that the maximum number of vertices is 3600 for the 30 cell section:

Using this pattern and keeping the number of objects constant, we can see the worst case scenario FPS:

Te number of vertices has grown so much that we only get 60 FPS. This scenario is almost impossible in practice. Sure, a player might try and poke holes in a pattern specially created to reduce framerate, but during normal play you won’t encounter this scenario. And if you do try it, your reward will be reduced framerate.

For the final touches, we add a small rectangle on the south side:

And we continue to add the rest of the sides. I messed up a few texture coordinates or what not and some textured are flipped around, but I won’t worry about that:

Here is the end result, with all borders filled:

Using the worst case scenario, the polygon count is now up to 7320. The generation of sides is not optimized yet and probably it can be reduced. The worst case scenario is also down to 33 FPS. So I optimized it a little and now it is between 7200 and 7260. Random hole maps have worst case 43 FPS and up to 100 (with around 5000 vertices).  And maps without holes have around worst case 400 FPS.

Wednesday, September 21, 2011

LL3DLGLD – 3 – Double strips

Let’s start with a short recap of this series: in part one we showed how you can’t create a world out of a lot of small objects. We used cubes, but this goes for any kind of object. Rendering was extraordinarily slow (a FPS of 3) not because the cubes had a lot of extra faces that were not visible yet entered the rendering process (although this is certainly was a huge performance bottleneck), but because we had a huge amount of objects. Then in part two we started using strips with an arbitrary width, with one strip being one object. We got a FPS of 30.

Today we’ll start by breaking the convention of one strip equals one object. By object I mean multiple things: a logical abstraction to help me organize stuff, a mesh and a single mesh push operation. This means that every object will submit its entire mesh in one operation to the hardware. Using these objects that contain multiple strips, we render the 300x300 world again:

Wow: 500 FPS! We went from 30 to 500 FPS, while using the same amount of polygons, but reducing object count. Using this method, we’ll divide our map into small rectangular sections. The engine will analyze that section of the map and create a mesh for it. Every time a change happens in a given section, the associated mesh will be updated, and if is not possible, recreated from scratch. To better illustrate this, I’ll use a new color coding scheme, where each section has its own color (rather than continuing to color code strips):

As one can see in the above picture, the world is now created with the help of only 100 objects. Each object has multiple strips and each strip has a polygon formed out of two triangles. The question one must answer now is how large should a section be? In a 3D engine, it is customary to define two cut-off planes, set at set distances from the camera. Everything that is outside the area constrained by the planes is not rendered. A very useful optimization! The problem is that the check is done on a per polygon basis, so even if one single point of you polygon is outside the area, the entire polygon is skipped. So if you choose an appropriately small section size, the polygons on the extremities, the ones close to the “horizon” will be skipped. You want this, because those polygons won’t be visible and there is no use rendering them. But if the section is too large, this skipping process might bleed over to close to the camera and create holes in your world. In the early days of 3D engines, developers use to a not too distant fog to hide the fact that you needed to have a relatively close cut-off plane to maintain performance. 

Another thing one must consider is that because an object is a mesh, there is maximum absolute number of vertices and triangles that can be added to that object. This is a hardware limitation. So one must either choose a section size that is guaranteed to never go over this limit, or you detect that you are about to go over this limit and create multiple objects. 

A third thing to consider is that DwarvesH is very different from other games that use 3D. While in most games the meshes forming the world are fairly static, the very nature of what I am trying to achieve with this game implies change to the world. There is a good chance that every cycle some small change happens. And thanks to time compression that skips ahead if nothing happens, this chance pretty much becomes 100%. So basically, every cycle you will be updating a mesh. So you must select a section size that can be updated in a short enough period of CPU time so that you game runs smoothly.

And a final consideration: large sections work well with the camera far away from the action, while close up shots favor smaller sections because the engine can do culling and eliminate small objects that are not visible.

There is a sweet spot, a section size that offers a good compromise between all these factors. Unfortunately, this sweet spot varies from engine requirement to engine requirement and you can only determine it experimentally and verify it by testing on a varied range of hardware. For starters, I’ll go with a 30x30 section, because this way I can easily build a 300x300 world. But section size must not be static. You can create an engine that dynamically creates sections of different sizes based on how much stuff you have in that area of the map.

But enough ranting! Take a look at this picture:

It looks the same, yet the framerate dropped a lot. This is because I am rendering now two planes:

The next step is to invert the order of vertices. This way we signal the hardware not to render the polygons facing away from the camera, thus invisible. The second lane is no longer visible:

But if we fly over to the other side, we see our second plane, while the first one becomes invisible:

The problem is that even though always only one of the planes is visible, this still impacts our framerate. This is where we need to be clever and can’t rely on the GPU. Since the planes are parallel to the X0Y plane and we know the position of the camera, we could compute the angle and determine which of the planes is visible. We could do this every time the user changes camera position and show or hide the appropriate plane. But this is more advanced stuff and won’t be bothering with it right now.

One last thing to note: even though we now have one extra plane, the number of objects remains the same. We now have double the polygons. In case you were wondering, every object now has 240 vertices. This number won’t stay so low and constant for long.

Now we’ll add a very simple wall layer, simulating a section though a cliff side:

I kept the shape simple and coinciding with section boundaries. Next time we’ll work on this. Framerate took another hit, because the wall layer again uses two planes. This is where in the future the analyze step of the mesh conversion will figure out that two strips are in the same place, but with opposite facing direction and eliminate them both.

Now we’ll remove the color coding and apply a different texture to the wall. Unfortunately, creating a mesh that uses two textures is far more complicated than what systems I have right now, so I’ll be creating a new object for the wall sections:

The FOV and camera position are not well suited so it is not that easy to tell what is going on. It also does not help that I only render top and bottom.

I need to figure out a way to use multiple materials in the same mesh.