Thursday, December 12, 2013 Eric Richards

Refactoring Rendering Code out of the Terrain Class

Howdy, time for an update.  I’ve mostly gotten my terrain pathfinding code first cut completed; I’m creating the navigation graph, and I’ve got an implementation of A* finished that allows me to create a list of terrain nodes that represents the path between tile A and tile B.  I’m going to hold off a bit on presenting all of that, since I haven’t yet managed to get a nice looking demo to show off the pathfinding yet.  I need to do some more work to create a simple unit class that can follow the path generated by A*, and between work and life stuff, I haven’t gotten the chance to round that out satisfactorily yet.

I’ve also been doing some pretty heavy refactoring on various engine components, both for design and performance reasons.  After the last series of posts on augmenting the Terrain class, and in anticipation of adding even more functionality as I added pathfinding support, I decided to take some time and split out the code that handles Direct3D resources and rendering from the more agnostic logical terrain representation.  I’m not looking to do this at the moment, but this might also make implementing an OpenGL rendering system less painful, potentially.

Going through this, I don’t think I am done splitting things up.  I’m kind of a fan of small, tightly focused classes, but I’m not necessarily an OOP junkie.  Right now, I’m pretty happy with how I have split things out.  I’ve got the Terrain class, which contains mostly the rendering independent logical terrain representation, such as the quad tree and picking code, the terrain heightmap and heightmap generation code, and the global terrain state properties (world-space size, initialization information struct, etc).  The rendering and DirectX resource management code has been split out into the new TerrainRenderer class, which does all of the drawing and creates all of the DirectX vertex buffers and texture resources.

I’ll spare you all the intermediate gyrations that this refactoring push put me through, and just post the resulting two classes.  Resharper was invaluable in this process; if you have access to a full version of Visual Studio, I don’t think there is a better way to spend $100.  I shiver to think of how difficult this would have been without access to its refactoring and renaming tools.

bvh

Terrain Class

public class Terrain : DisposableClass {
        public const int CellsPerPatch = 64;
        private const int TileSize = 2;

        private bool _disposed;
        private MapTile[] _tiles;
        
        public float Width { get { return (Info.HeightMapWidth - 1) * Info.CellSpacing; } }
        public float Depth { get { return (Info.HeightMapHeight - 1) * Info.CellSpacing; } }
        
        public InitInfo Info { get; private set; }
        public HeightMap HeightMap { get; private set; }
        public Image HeightMapImg { get { return HeightMap.Bitmap; } }
        public QuadTree QuadTree { get; private set; }

        private TerrainRenderer _renderer;
        public TerrainRenderer Renderer { get { return _renderer; } }

        public Terrain() {...}
        
        protected override void Dispose(bool disposing) {...}       

        #region Utility Functions

        public float Height(float x, float z) {...}

        private static float H(Point start, Point goal) {... }

        private bool Within(Point p) {...}

        private MapTile GetTile(Point point) { ... }

        private MapTile GetTile(int x, int y) {...}

        private Vector2 GetMinMaxY(Vector2 tl, Vector2 br) {...}
        #endregion

        public void Init(Device device, DeviceContext dc, InitInfo info) {...}

        #region Pathfinding

        private void InitPathfinding() {...}

        private void ResetPathfinding() {...}

        private void SetTilePositionsAndTypes() {...}
        private void CalculateWalkability() {...}
        private void ConnectNeighboringTiles() {...}
        private void CreateTileSets() {...}
        public List<MapTile> GetPath(Point start, Point goal) {...}
        #endregion

        private QuadTreeNode BuildQuadTree(Vector2 topLeft, Vector2 bottomRight) {...}

        #region Intersection tests
        public bool Intersect(Ray ray, ref Vector3 spherePos) {...}
        public bool Intersect(Ray ray, ref Vector3 spherePos, ref MapTile mapPos) {...}
        public bool Intersect(Ray ray, ref MapTile mapPos) {...}
        #endregion

    }

TerrainRenderer Class

public class TerrainRenderer : DisposableClass {
    internal const float MaxDist = 500.0f;
    internal const float MinDist = 20.0f;
    internal const float MaxTess = 6.0f;
    internal const float MinTess = 0.0f;
    private readonly Terrain _terrain;
    private Buffer _quadPatchVB;
    private Buffer _quadPatchIB;
    private readonly List<Patch> _patches;
    private ShaderResourceView _layerMapArraySRV;
    private ShaderResourceView _blendMapSRV;
    private ShaderResourceView _heightMapSRV;
    private int _numPatchVertices;
    private int _numPatchQuadFaces;
    public int NumPatchVertRows;
    public int NumPatchVertCols;
    private Material _material;
    private List<Vector2> _patchBoundsY;
    private bool _useTessellation;
    private readonly List<VertexPC> _bvhVerts = new List<VertexPC>();

    private readonly List<int> _bvhIndices = new List<int>();

    private Buffer _bvhVB;

    private Buffer _bvhIB;

    private int _aabCount;

    private bool _disposed;

    public bool DebugQuadTree { get; set; }

    public bool Shadows { get; set; }
    public Matrix World { get; set; }

    public TerrainRenderer(Material material, Terrain terrain) {...}

    protected override void Dispose(bool disposing) {...}
    public void Init(Device device, DeviceContext dc, Terrain terrain) {...}

    #region DX11 Terrain Mesh Creation
    private void CalcAllPatchBoundsY() {...}
    private void CalcPatchBoundsY(int i, int j) {...}

    private void BuildQuadPatchVB(Device device) {...}

    private void BuildQuadPatchIB(Device device) {...}
    #endregion

    #region DX10 Terrain Mesh Creation
    private void BuildPatches(Device device) {...}
    #endregion

    #region BlendMap
    private static ShaderResourceView CreateBlendMap(HeightMap hm, Device device, Terrain terrain) {...}
    private static void SmoothBlendMap(HeightMap hm, List<Color4> colors, Terrain terrain) {...}
    #endregion

    #region Debug QuadTree
    private void BuildQuadTreeDebugBuffers(Device device) {...}
    private void GetQuadTreeVerticesAndIndices(QuadTreeNode quadTree, int level = 0) {...}
    private void DrawQuadTreeDebugBuffers(DeviceContext dc, CameraBase cam, int offset) {...}

    #endregion

    public void Draw(DeviceContext dc, CameraBase cam, DirectionalLight[] lights) {...}

    public void ComputeSsao(DeviceContext dc, CameraBase cam, Ssao ssao, DepthStencilView depthStencilView) {...}

    public void DrawToShadowMap(DeviceContext dc, ShadowMap sMap, Matrix viewProj) {...}
}

Bookshelf

Hi, I'm Eric, and I'm a biblioholic. Here is a selection of my favorites. All proceeds go to feed my addiction...