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?