Jump to content

  • Log In with Google      Sign In   
  • Create Account

SyncViews

Member Since 06 Feb 2011
Offline Last Active Sep 19 2015 01:00 PM

Topics I've Started

C++ Loading config files, with extend/inheritance and full error handling

29 July 2015 - 03:24 PM

Most of the times I have wanted to load some sort of config file (be it simple key values, JSON, XML, whatever) I tended to have to deal with the type conversions, validations and trying to report useful errors myself and generally this also left a lot of error cases out with people I am working with struggling to understand what they did wrong when things did not quiet work.

 

I also don't really like the fact that as well as defining the data struct/class itself, I then need this other bit of code that needs to be perfectly synced with it, with all the fields and their data types a second time.

 

e.g. I may have some code like below.

In the first simpler case, as well as having to implement this template wrapper around the basic file loading lib to get some basic errors, some errors are just not detected, e.g. if the file contained keys/elements I did not expect, they are slightly ignored. And in the second case, that simple logic does not even guarantee that some property was defined at all anywhere in the inheritance chain, leaving it upto the objects constructor to specify useful defaults for everything or post-validation to detect invalid "default" values.

 

Is there any better way?

 

In java Ive seen this sort of thing done via attributes and reflection to be able to write a generic loader that can use a Java type's public setters and attributes for extra validation/etc info. But trying to think of ways to do something like that in C++ I just ended up with a mess of magic macro's trying to set up template functions and lambda's without even getting as far as how to best handle embedded collections like VehicleType::weapons.

 

I know I could manually add things to the below code like checking the list/set of keys/elements in KeyValues against a "expected_members" constant, and for inheritance I could similarly keep track of a "set_members" collection, but that is yet another thing to manually keep track of (e.g. at the very least another copy of all the member names for each object/struct to keep synced).

 

 

//Basic without inheritance

//... for all basic types read from config
static const char * ConfigTypeName<bool>::name = "boolean";

//basic version, via std::stringstream since most other solutions seemed to
//either miss error cases or threw exceptions around
//return false on error
template<class T> bool parseString(const std::string &str, T *out);
//Special versions for some types, e.g. "true" and "false" literal strings to bool
bool parseString(const std::string &str, bool *out);
bool parseString(const std::string &str, MyEnum *out);
//templated getter, either for some own simple std::map thing, or wrapping the xml/json/whatever lib
//key is just whatever string was previously used, so can format an error message to throw
//e.g. "'10.5' for 'MyTankgun/Mass' in 'Data/Units/HeavyTank.txt' is not a valid integer"
//Plus lots of overloads/variations to deal with optional items, restrictions like min and max, etc.
template<class T> T KeyValues::get(const std::string &name);

void loadVehicleType(VehicleType *obj, const KeyValues &kv)
{
    obj->cost = kv.get<int>("cost", 0, MAX_ITEM_COST);
    obj->myfloat = kv.getRanged<float>("myfloat", 1.0f, 1000.0f);//1.0f <= myfloat <= 1000.0f
    for (auto weaponKv : kv.getSubList("Weapons"))
    {
        obj->weapons.push_back(loadWeaponType(weaponKv));
    }
}

With inheritance example

void loadVehicleType(VehicleType *obj, const KeyValues &kv)
{
    auto extends = kv.get("extends");
    if (extends) loadVehicleType(obj, KeyValues(extends));
    
    kv.getOpt<int>("cost", &obj->cost, 0, MAX_ITEM_COST);
    kv.getOpt<float>("myfloat", &obj->myfloat, 1.0f, 1000.0f);
    auto weapons = kv.getSubList("Weapons");
    if (weapons)
    {
        obj->weapons.clear();
        for (auto weaponKv : weapons)
        {
...

 

 


Advanced font/text rendering options

09 July 2015 - 06:45 AM

I have some existing code using FreeType I did some years ago and have used since, just laying out the glpyhs using the FreeType provided metrics and kerning routine then rendering as mono or 8bit anti-aliased greyscale (some fonts, e.g. Calibri I found will give mono at certain font sizes for most glyphs) then uploading the final result as a D3D or OpenGL texture (so one texture containing the entire block/string of rendered text that I can draw as an alpha blended quad either screen aligned in the UI, or in 3D space).

 

However I now want to improve this to give correct and higher quality results in more cases. Namely correct handling of composed/decomposed codepoints/glpyhs, non-latin scripts and support for subpixel rendering.

Most of these however appear to not be part of FreeType. e.g. the correct placement of the combining mark for á and Á does not appear to have a FreeType API, and kerning seems to be superseded by GPOS which FreeType does not implement. Things like correct subpixel rendering, while a FreeType feature seem to be highly complex and poorly documented (e.g. how to then deal with coloured text and backgrounds). So at least before I start down that highly complex road am looking at what options I have...

 

  • FreeType itself seems to recommend a library called Pango. However this looks like a pretty big and heavy weight library, pulling in a lot of other difficult dependencies (e.g. parts of GTK and Cario) which while may be fine on Linux, does not look promising for MSVC/Windows/Direct3D. I also found very little information on using Pango with Window's and OpenGL/Direct3D or just games in general...
    Is this worth pursuing? It looks like I might be able to do without a lot of GTK and work with in memory bitmaps I can upload to the GPU as textures, but I am not sure if it is really suitable and worth learning about?
  • On Window's there is DirectWrite. Which I understand I can use via Direct2D and Direct3D. Don't really know much about this, and again if it is generally suitable for games using D3D11 on Windows 7 to 10. Also I would need something different for anything I do on Linux which would seem a lot of extra work.
  • Anything else? I had a poke around CEGUI and SDL_ttf, but they seemed to use FreeType in much the same way I did. I am planning to look at CEGUI some more and write a demo to see if it has solutions for my problems, but am not entirely sure it solves everything having read through their FreeType logic and not seen much sign if them implementing the things I did not. Also since I do not plan to port the rest of my UI stuff, mostly just looking for inspiration/examples on fixing up my own FreeType logic.
  • Obviously simple bitmap/sprite fonts don't really solve any of my problems here.

D3D11 Fullscreen Not Sticking

30 January 2015 - 03:07 PM

I am trying to add fullscreen to a D3D11 program I have (not just a borderless window). It seems to attempt to go fullscreen with the monitor going black for a moment but fail and revert to a window. I can't see what would cause this, and it happens if I use DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH and attempt to go fullscreen with Alt+Enter as well. VS debug output shows no complaining from the device.

 

The window is created with "WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU", and I have validated that the backbuffer and depth buffer get my native monitor resolution of 1920x1080. The device is created with "D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_DEBUG"

 

Given that I do not expierence this with any games or other software on my system, I am fairly sure its my code doing something wrong here.

 

Complete working code is below, should compile directly with MSVC (tested on 2013). I striped just the window and D3D creation out (including error handling and cleanup), although its still fairly long.

 
#include <Windows.h>
#include <dxgi.h>
#include <d3d11.h>
#include <stdexcept>
#include <string>

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "DXGI.lib")

HWND hwnd = NULL;
bool run = true;
ID3D11Device *d3d11Device;
ID3D11DeviceContext *d3d11Context;
IDXGISwapChain *d3dSwapChain;
ID3D11Texture2D *d3d11BackBuffer;
ID3D11RenderTargetView *d3d11BackBufferTarget;
ID3D11DepthStencilView *d3d11DepthStencilView;

enum WindowMode
{
    WINDOW_WINDOWED,
    WINDOW_BORDERLESS,
    WINDOW_FULLSCREEN
};


void createWindow(unsigned width, unsigned height, WindowMode mode);
void createD3d11(unsigned width, unsigned height, WindowMode mode);
LRESULT __stdcall wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    
    WindowMode mode = WINDOW_FULLSCREEN;

    createWindow(1920, 1080, mode);

    RECT rect;
    if (mode == WINDOW_WINDOWED)
        GetClientRect(hwnd, &rect);
    else GetWindowRect(hwnd, &rect);
    createD3d11(rect.right - rect.left, rect.bottom - rect.top, mode);

    //loop
    while (run)
    {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        d3d11Context->ClearDepthStencilView(d3d11DepthStencilView, D3D11_CLEAR_DEPTH, 1, 0);
        float col[4] = { 0, 0, 1, 1 };
        d3d11Context->ClearRenderTargetView(d3d11BackBufferTarget, col);
        d3dSwapChain->Present(1, 0);
        Sleep(1000 / 30);
    }
    //exit
    return 0;
}


void createWindow(unsigned width, unsigned height, WindowMode mode)
{
    int style;
    if (mode == WINDOW_BORDERLESS)
        style = WS_POPUP;
    else style = WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;

    //Need a window class
    WNDCLASSEX cls = { 0 };
    cls.cbSize = sizeof(cls);
    cls.style = /*CS_DBLCLKS |*/ CS_OWNDC;
    cls.lpfnWndProc = &wndProc;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = NULL;
    cls.hIcon = NULL;
    cls.hCursor = LoadCursor(NULL, IDC_ARROW);
    cls.hbrBackground = 0; //(HBRUSH)(COLOR_WINDOW+1);
    cls.lpszMenuName = NULL;
    cls.lpszClassName = L"window";
    cls.hIconSm = NULL;

    RegisterClassEx(&cls);
    //Create the window
    DWORD styleEx = 0;
    hwnd = CreateWindowEx(styleEx, L"window", L"Test Window",
        style,
        CW_USEDEFAULT, CW_USEDEFAULT,
        width, height,
        NULL, NULL, NULL, NULL);
    ShowWindow(hwnd, SW_SHOW);
}

LRESULT __stdcall wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        run = false;
        return 0;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
}


void createD3d11(unsigned width, unsigned height, WindowMode mode)
{
    IDXGIFactory1 *dxgiFactory = NULL;
    UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_DEBUG;

    D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
    unsigned featureLevelsLen = (unsigned)(sizeof(featureLevels) / sizeof(featureLevels[0]));
    D3D_FEATURE_LEVEL featureLevel;

    CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&dxgiFactory);
    D3D11CreateDevice(
        NULL,
        D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        flags,
        featureLevels,
        featureLevelsLen,
        D3D11_SDK_VERSION,//7
        &d3d11Device,
        &featureLevel,
        &d3d11Context);

    //Swap chain
    DXGI_SAMPLE_DESC renderTargetSampleDesc;
    renderTargetSampleDesc.Count = 4;
    renderTargetSampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;

    DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
    swapChainDesc.BufferCount = 1;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    //TODO: This needs to be either user defined or taken from the monitor
    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    swapChainDesc.BufferDesc.Width = width;
    swapChainDesc.BufferDesc.Height = height;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.OutputWindow = hwnd;
    swapChainDesc.SampleDesc = renderTargetSampleDesc;
    swapChainDesc.Windowed = mode != WINDOW_FULLSCREEN;
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    dxgiFactory->CreateSwapChain(d3d11Device, &swapChainDesc, &d3dSwapChain);
    //Depth stencil
    ID3D11Texture2D *d3d11DepthStencilTexture = NULL;
    D3D11_TEXTURE2D_DESC depthStencilTextureDesc;
    memset(&depthStencilTextureDesc, 0, sizeof(depthStencilTextureDesc));
    depthStencilTextureDesc.Width = width;
    depthStencilTextureDesc.Height = height;
    depthStencilTextureDesc.MipLevels = 1;
    depthStencilTextureDesc.ArraySize = 1;
    depthStencilTextureDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthStencilTextureDesc.SampleDesc = renderTargetSampleDesc;
    depthStencilTextureDesc.Usage = D3D11_USAGE_DEFAULT;
    depthStencilTextureDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthStencilTextureDesc.CPUAccessFlags = 0;
    depthStencilTextureDesc.MiscFlags = 0;
    d3d11Device->CreateTexture2D(&depthStencilTextureDesc, NULL, &d3d11DepthStencilTexture);

    D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
    memset(&depthStencilViewDesc, 0, sizeof(depthStencilViewDesc));
    depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
    depthStencilViewDesc.Texture2D.MipSlice = 0;
    d3d11Device->CreateDepthStencilView(d3d11DepthStencilTexture, &depthStencilViewDesc, &d3d11DepthStencilView);


    //render target
    d3dSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&d3d11BackBuffer);
    d3d11Device->CreateRenderTargetView(d3d11BackBuffer, NULL, &d3d11BackBufferTarget);

    dxgiFactory->Release();


    d3d11Context->OMSetRenderTargets(1, &d3d11BackBufferTarget, d3d11DepthStencilView);
    //viewport
    D3D11_VIEWPORT viewport;
    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    viewport.Width = (float)width;
    viewport.Height = (float)height;
    viewport.MinDepth = 0;
    viewport.MaxDepth = 1;
    d3d11Context->RSSetViewports(1, &viewport);
}


D3D11 Frustum Culling issue

09 September 2014 - 05:02 PM

This seems to be occasionally culling things that are visible on screen when rendering with all world coordinates within the AABB and the same view and projection matrix... The code is pretty straight forward, so not sure what the issue is?

XMMATRIX xmproj = XMMatrixPerspectiveFovLH(fov, aspectRatio, 0.1f, 1000.0f);

XMMATRIX xmview;
xmview = XMMatrixTranslation(-x, -y, -z);
xmview *= XMMatrixRotationY(-yaw);
xmview *= XMMatrixRotationX(-pitch);
...
class Frustum
{
public:
    void update(XMMATRIX projection, XMMATRIX view)
    {
        frustum.CreateFromMatrix(frustum, projection);
        frustum.Transform(frustum, view);
    }
    bool contains(const AxisAlignedBoundaryBox3F &aabb)const
    {
        //AxisAlignedBoundaryBox3 is (min,max) but DirectX::BoundingBox is (centre,extents)!
        Vector3F p1 = aabb.p1;
        Vector3F p2 = aabb.p2;
        
        Vector3F size = p2 - p1;
        Vector3F centre = p1 + size / 2.f;
        
        XMFLOAT3 xsize(size.x, size.y, size.z);
        XMFLOAT3 xcentre(centre.x, centre.y, centre.z);
        bool ret = frustum.Contains(BoundingBox(xcentre, xsize)) != DISJOINT;
        assert(ret || frustum.Intersects(BoundingBox(xcentre, xsize)) == DISJOINT);
        return ret;
    }
private:
    BoundingFrustum frustum;
};

Current options for dynamic 3D lighting in a dynamic world

30 August 2014 - 05:26 PM

Currently I have something similar to Minecraft's lighting in my dynamic world, which I am considering rewriting yet again to fix issues related to chunk/region borders and performance issues when lights or voxels change...

 

 

But wondering if there are any other practical options? In the past I have mostly just used whatever the engine provided, and even that often had a lot of caveats that are not suitable here (slow/expensive light map creation process, a limit on dynamic lights in an area, no support for day-night cycles, little/no indirect lighting, etc.) so not really sure if there is much better out there.

 

At the very least a fairly comprehensive explanation of the minecraft style voxel lighting would be nice (how to minimise how much needs to be recalculated, how to handle chunk boundaries without issues, etc.), but if there is a generic lighting system these days I am much more inclined to put effort into implementing/integrating that since I really would like angled lights (rather than only vertical sunlight for example), full colored lighting, dynamic/moving lights, etc.

 

My main concern with just going straight to shadow mapping etc. is the potentially large number of light sources in each region, and the relatively large area covered by a single draw call, since it currently groups and batches by texture, but that results in each draw call being fairly spread out, with many having a very low "density" faces, and with the majority of faces behind hidden behind something, whereas with non-voxel games with BSP and such the engines seemed very capable of only rendering local/visible stuff, and not things hidden behind walls etc..


PARTNERS