Sign in to follow this  
Mike.Popoloski

DirectX in Six Different Languages

Recommended Posts

One of the nice side effects of the SlimDX project is that DirectX has become usable from a wide variety of languages that target .NET. We had never really seen this used much though, except for the occasional VB.Net post every now and then. A month or two ago, I set about rounding up a bunch of people that I knew were experimenting with alternative .NET languages and asked them to write translations for our canonical D3D9 initialization program (which has recently been slimmed down even more). I've posted the results of this experiment below. It's quite interesting to see the same API used from different angles and paradigms. Here is the original example I coded up in C++, which should serve as a base for those of you who don't know any of the other languages.
#include <windows.h>
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_PAINT:
		ValidateRect(hWnd, 0);
		return 0;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(hInstance, IDC_ARROW);
	wcex.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = L"TestWindowClass";
	wcex.hIconSm = wcex.hIcon;

	RegisterClassEx(&wcex);

	HWND hWnd = CreateWindow(L"TestWindowClass", L"SlimDX Comparison", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, 0, 0, hInstance, 0);

	RECT rect = {0, 0, 800, 600};
	AdjustWindowRect(&rect, GetWindowLong(hWnd, GWL_STYLE), FALSE);
	SetWindowPos(hWnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE);

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	LPDIRECT3D9 d3d = Direct3DCreate9(D3D_SDK_VERSION);
	if (!d3d)
	{
		MessageBox(NULL, L"Direct3DCreate9 - Failed", 0, 0);
		return 0;
	}

	D3DPRESENT_PARAMETERS pp;
	pp.BackBufferCount = 1;
	pp.BackBufferFormat = D3DFMT_X8R8G8B8;
	pp.BackBufferWidth = 800;
	pp.BackBufferHeight = 600;
	pp.MultiSampleType = D3DMULTISAMPLE_NONE;
	pp.MultiSampleQuality = 0;
	pp.Windowed = TRUE;
	pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	pp.hDeviceWindow = hWnd;
	pp.EnableAutoDepthStencil = TRUE;
	pp.AutoDepthStencilFormat = D3DFMT_D24X8;
	pp.Flags = 0;
	pp.FullScreen_RefreshRateInHz = 0;
	pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	LPDIRECT3DDEVICE9 device;
	HRESULT hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, &device);
	if (FAILED(hr))
	{
		d3d->Release();
		MessageBox(NULL, L"CreateDevice - Failed", 0, 0);
		return 0;
	}

	MSG msg;
	while (1)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage (&msg);
		}
		else
		{
			device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(255, 0, 0, 0), 1.0f, 0);
			device->BeginScene();

			device->EndScene();
			device->Present(0, 0, 0, 0);
		}
	}

	device->Release();
	d3d->Release();

	return msg.wParam;
}

C#:
using System;
using System.Drawing;
using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.Windows;

namespace Sample
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var form = new RenderForm("SlimDX Comparison");
            var device = new Device(new Direct3D(), 0, DeviceType.Hardware, form.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters()
            {
                BackBufferWidth = form.ClientSize.Width,
                BackBufferHeight = form.ClientSize.Height
            });

            MessagePump.Run(form, () =>
            {
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
                device.BeginScene();

                device.EndScene();
                device.Present();
            });

            foreach (var item in ObjectTable.Objects)
                item.Dispose();
        }
    }
}

IronRuby:
require 'System'
require 'System.Drawing'
require 'C:\tools\slimdx\build\x86\Release\SlimDX.dll' # I don't have SlimDX installed in my GAC

include System;
include System::Drawing;
include SlimDX;
include SlimDX::Direct3D9;
include SlimDX::Windows;

form = RenderForm.new("SlimDX Comparison")

device = Device.new(Direct3D.new, 0, DeviceType.hardware, form.handle, CreateFlags.hardware_vertex_processing, PresentParameters.new.instance_eval {
    back_buffer_width  = form.client_size.width
    back_buffer_height = form.client_size.height
    self
})

MessagePump.run(form, lambda {
    device.clear(ClearFlags.target | ClearFlags.z_buffer, Color.black, 1.0, 0)
    device.begin_scene
    device.end_scene
    device.present
})

ObjectTable.Objects.each { |item| item.dispose }

VB9:
Imports System
Imports System.Drawing

Imports SlimDX
Imports SlimDX.Direct3D9
Imports SlimDX.Windows

Module Module1

    Sub Main()
        Dim form As New RenderForm
        form.Show()
        Dim device As New Device(New Direct3D(), 0, DeviceType.Hardware, form.Handle, CreateFlags.HardwareVertexProcessing, _
        New PresentParameters With { _
            .BackBufferWidth = form.ClientSize.Width, _
            .BackBufferHeight = form.ClientSize.Height _
        })

        MessagePump.Run(form, Function() MainLoop(device))

        For Each item In ObjectTable.Objects
            item.Dispose()
        Next
    End Sub

    Function MainLoop(ByVal device As Device) As Boolean
        device.Clear(ClearFlags.Target Or ClearFlags.ZBuffer, Color.Black, 1.0F, 0)
        device.BeginScene()

        device.EndScene()
        device.Present()
        Return True
    End Function
End Module

F#:
open System
open System.Drawing

open SlimDX
open SlimDX.Direct3D9
open SlimDX.Windows

[<STAThread>]
let main () =
    let form = new RenderForm "SlimDX Comparison"
    let device =
        new Device (
            new Direct3D (), 0, DeviceType.Hardware, form.Handle, CreateFlags.HardwareVertexProcessing,
            PresentParameters (
                BackBufferWidth = form.ClientSize.Width,
                BackBufferHeight = form.ClientSize.Height
            )
        )
    
    let mainLoop () =
        device.Clear (ClearFlags.Target ||| ClearFlags.ZBuffer, Color4 Color.Black, 1.0f, 0) |> ignore
        device.BeginScene () |> ignore
        device.EndScene () |> ignore
        device.Present () |> ignore
    
    MessagePump.Run (form, MainLoop mainLoop)

    ObjectTable.Objects |> Seq.iter (fun o -> o.Dispose ())

main ()

Nemerle:
#pragma indent

using System
using SlimDX.Windows
using System.Drawing
using SlimDX
using SlimDX.Direct3D9

module Program
  [STAThread]
  Main() : void 
            def form = RenderForm("SlimDX Comparison")
            def presentParams =  PresentParameters()
            presentParams.BackBufferWidth = form.ClientSize.Width ;presentParams.BackBufferHeight = form.ClientSize.Height
            def device = Device(Direct3D(), 0, DeviceType.Hardware,form.Handle, CreateFlags.HardwareVertexProcessing, presentParams)            

            MessagePump.Run(form, () =>
            {
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
                device.BeginScene();

                device.EndScene();
                device.Present();
            })

            foreach (item in ObjectTable.Objects)
                item.Dispose() 

I'd still like to have Python and Delphi versions to add to this group, but I think it's a great start. Anyone have any comments, or any other favorite languages they'd like to add?

Share this post


Link to post
Share on other sites
There's at least two Fortran compilers than can target .NET, one sold by Silverfrost and one sold by Lahey. If I'm calculating the exchange rate from British pounds to US dollars correctly, the cheapest of the two still clocks in at about $500 US for a new license. If you're willing to purchase either software package for me, I'd be more than happy to submit a Fortran example.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this