Kotlin WIN32 DirectX 9 Off Screen Surface Gradient Direct Pixel Access

Submitted by Dickens A S on Sun, 11/29/2020 - 12:08

Code DirectX 9 using kotlin

Kickstart Kotlin DirectX using WIN32 API's, DirectX SDK 2010

GitHub: https://github.com/dickensas/kotlin-gradle-templates/tree/master/win32-directx

Output window with WIN32 API

Kotlin DirectX 9

Around 150 line of Kotlin code

package plot

import kotlinx.cinterop.*
import platform.windows.*
import directx.*

var PIXMAP_WIDTH = 640
var PIXMAP_HEIGHT = 480

@ExperimentalUnsignedTypes
fun WndProc(hwnd: HWND?, msg: UINT, wParam: WPARAM, lParam: LPARAM) : LRESULT {
    when(msg) {
        WM_COMMAND.toUInt() -> { }
        WM_CLOSE.toUInt() -> DestroyWindow(hwnd)
        WM_DESTROY.toUInt() -> PostQuitMessage(0)
        else -> return (DefWindowProc!!)(hwnd, msg, wParam, lParam)
    }
    return 0;
}

@ExperimentalUnsignedTypes
fun D3DCOLOR_ARGB(a : Int, r: Int, g: Int, b: Int) : UInt {
   return (((a and 0xff) shl 24) or ((r and 0xff) shl 16) or ((g and 0xff) shl 8) or (b and 0xff)).toUInt()
}

@ExperimentalUnsignedTypes
fun main() {
    
    memScoped {
        
        val hInst = GetModuleHandleW(null)
        val lpszClassName = "plot"

        val wc = alloc<WNDCLASSEX>();

        wc.cbSize = sizeOf<WNDCLASSEX>().toUInt();
        wc.hCursor = LoadCursorW(hInst, IDC_ARROW);
        wc.lpfnWndProc = staticCFunction(::WndProc)
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInst;
        wc.hIcon = null;
        wc.lpszMenuName = null;
        wc.hIconSm = null;
        wc.lpszClassName = lpszClassName.wcstr.ptr
        wc.style = 0u;

        if(RegisterClassExW(wc.ptr) == 0u.toUShort()) {
            println("Failed to register!")
            return
        }

        val hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, lpszClassName, "plot",
            (WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX).toUInt(),
            CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, null, null, hInst, NULL
        )

        ShowWindow(hwnd, 1);
        UpdateWindow(hwnd);
        
        val msg = alloc<MSG>();

        var d3d = Direct3DCreate9(D3D_SDK_VERSION)
        var d3ddev = alloc<LPDIRECT3DDEVICE9Var>()
        
        var d3dpp = alloc<D3DPRESENT_PARAMETERS>().apply {
           Windowed = 1;
           SwapEffect = D3DSWAPEFFECT_DISCARD;
           hDeviceWindow = hwnd;
           BackBufferFormat = D3DFMT_UNKNOWN.toUInt();
           BackBufferWidth = PIXMAP_WIDTH.toUInt();
           BackBufferHeight = PIXMAP_HEIGHT.toUInt();
           EnableAutoDepthStencil  = 1;
           AutoDepthStencilFormat  = D3DFMT_D16.toUInt();
        }
        
        var l:IDirect3D9Vtbl = d3d!!.pointed!!.lpVtbl!!.pointed
        
        l!!.CreateDevice!!(
           d3d, 
           D3DADAPTER_DEFAULT.toUInt(), 
           D3DDEVTYPE_HAL.toUInt(), 
           hwnd, 
           D3DCREATE_SOFTWARE_VERTEXPROCESSING.toUInt(), 
           d3dpp.ptr, 
           d3ddev.ptr 
        )
        
        var l1:IDirect3DDevice9Vtbl = d3ddev!!.value!!.pointed!!.lpVtbl!!.pointed
        
        var surf = alloc<LPDIRECT3DSURFACE9Var>()
        l1!!.CreateOffscreenPlainSurface!!(
           d3ddev.value,
           PIXMAP_WIDTH.toUInt(),
           PIXMAP_HEIGHT.toUInt(),
           D3DFMT_X8R8G8B8.toUInt(),
           D3DPOOL_DEFAULT,
           surf.ptr,
           null
        )
        
        var l2:IDirect3DSurface9Vtbl = surf!!.value!!.pointed!!.lpVtbl!!.pointed
        
        var lockRect = alloc<D3DLOCKED_RECT>() 
        
        l2!!.LockRect!!(
           surf.value,
           lockRect.ptr,
           null,
           D3DLOCK_DISCARD.toUInt()
        )
        
        var vdata = UIntArray(PIXMAP_WIDTH * PIXMAP_HEIGHT)
        for (y in 0..PIXMAP_HEIGHT-1) {
           for (x in 0..PIXMAP_WIDTH-1) {
              vdata[ x + PIXMAP_WIDTH*y ] = D3DCOLOR_ARGB(
                 0xff, 
                 255 - 255 * (y+1) / PIXMAP_HEIGHT,
                 0,
                 0
              )
           }
        }
        
        vdata.usePinned { buf ->
            platform.posix.memcpy(lockRect.pBits, buf.addressOf(0), (PIXMAP_WIDTH * PIXMAP_HEIGHT * 4).toULong())
        }
        
        l2!!.UnlockRect!!(
           surf.value
        )
        
        while((GetMessage!!)(msg.ptr, null, 0u, 0u) > 0) {
            TranslateMessage(msg.ptr);
            DispatchMessageW(msg.ptr);
            
            l1!!.Clear!!(d3ddev.value, 0.toUInt(), null, D3DCLEAR_TARGET.toUInt(), D3DCOLOR_ARGB(0xff, 0, 0, 170), 1.0f, 0.toUInt());
            l1!!.BeginScene!!(d3ddev.value)
            
            var backbuffer = alloc<LPDIRECT3DSURFACE9Var>()
            l1!!.GetBackBuffer!!(d3ddev.value, 0.toUInt(), 0.toUInt(), D3DBACKBUFFER_TYPE_MONO, backbuffer.ptr)
            l1!!.StretchRect!!(d3ddev.value, surf.value, null, backbuffer.value, null, D3DTEXF_LINEAR)
            backbuffer.value!!.pointed!!.lpVtbl!!.pointed!!.Release!!(backbuffer.value)
            
            l1!!.EndScene!!(d3ddev.value)
            l1!!.Present!!(d3ddev.value, null, null, null, null);
        }
    }
}

Explanation of l, l1 , l2

Koltin-native does not support C++ 

therefore C based DirectX functions are used

In C based DirectX the "CreateDevice" only can be invoked as

#define IDirect3D9_CreateDevice(p,a,b,c,d,e,f) (p)->lpVtbl->CreateDevice(p,a,b,c,d,e,f)

Unfortunately the macro IDirect3D9_CreateDevice is not detected by Kotlin cinterop

Therefore we directly call the original method as

d3d!!.pointed!!.lpVtbl!!.pointed!!.CreateDevice!!

So to simplify the function calls we create a variable to reduce the length of the code as

var l:IDirect3D9Vtbl = d3d!!.pointed!!.lpVtbl!!.pointed

l!!.CreateDevice!!

That's All!

 

DirectX 10, 11, 12  --- the headers files could not be parsed by C compiler due to duplicate declarations

Therefore we need to create stub methods for all DirectX methods and call them via Kotlin

 

 

 

Add new comment