RSS
 

Archive for the ‘Python’ Category

Building a SlimDX MiniTriangle sample with Direct3D11 and IronPython

26 Aug

I generally don’t post huge code dumps, mainly because I find them more annoying and less helpful than some books/authors might. But you know, I’ve been playing with IronPython/SlimDX recently and decided to do up another SlimDX Sample (demonstrating DX11), except in IronPython this time. This will be in the SlimDX samples sometime soon!

import clr
clr.AddReference('System.Windows.Forms')
clr.AddReference('System.Drawing')
clr.AddReference('SlimDX')
 
from System import *
from System.Drawing import Size
from System.Windows.Forms import Form, Application, MessageBox, FormBorderStyle
from SlimDX import *
from SlimDX.Direct3D11 import *
from SlimDX.DXGI import SwapChainDescription, SwapChainFlags, ModeDescription, SampleDescription, Usage, SwapEffect, Format, PresentFlags, Factory, WindowAssociationFlags
from SlimDX.D3DCompiler import *
from SlimDX.Windows import MessagePump
 
class GameObject:
    def Render(self):
        pass
    def Tick(self):
        pass
 
class GraphicsDevice(IDisposable):
    Context = property(lambda self: self.context)
    Device = property(lambda self: self.device)
    SwapChain = property(lambda self: self.swapChain)
 
    def __init__(self, control, fullscreen):
        self.fullscreen = fullscreen
        self.control = control
 
        control.Resize += lambda sender, args: self.Resize()
 
        swapChainDesc = self.CreateSwapChainDescription();
        success,self.device,self.swapChain = Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, Array[FeatureLevel]([FeatureLevel.Level_11_0, FeatureLevel.Level_10_1, FeatureLevel.Level_10_0]), swapChainDesc)
        self.context = self.Device.ImmediateContext
 
        with self.swapChain.GetParent[Factory]() as factory:
            factory.SetWindowAssociation(self.control.Handle, WindowAssociationFlags.IgnoreAll)
 
        with Resource.FromSwapChain[Texture2D](self.swapChain, 0) as backBuffer:
            self.backBufferRTV = RenderTargetView(self.Device, backBuffer)
 
        self.Resize()        
 
    def CreateSwapChainDescription(self):
        swapChainDesc = SwapChainDescription()
        swapChainDesc.IsWindowed = not self.fullscreen
        swapChainDesc.BufferCount = 1
        swapChainDesc.ModeDescription = ModeDescription(self.control.ClientSize.Width, self.control.ClientSize.Height, Rational(60, 1), Format.R8G8B8A8_UNorm)
        swapChainDesc.Flags = SwapChainFlags.None
        swapChainDesc.SwapEffect = SwapEffect.Discard
        swapChainDesc.Usage = Usage.RenderTargetOutput
        swapChainDesc.SampleDescription = SampleDescription(1, 0)
        swapChainDesc.OutputHandle = self.control.Handle
        return swapChainDesc
 
    def Resize(self):
        self.Context.ClearState()
        self.backBufferRTV.Dispose()
        self.swapChain.ResizeBuffers(1, self.control.ClientSize.Width, self.control.ClientSize.Height, Format.R8G8B8A8_UNorm, SwapChainFlags.None)
        with Resource.FromSwapChain[Texture2D](self.swapChain, 0) as backBuffer:
            self.backBufferRTV = RenderTargetView(self.Device, backBuffer)
        self.Context.Rasterizer.SetViewports(Viewport(0, 0, self.control.ClientSize.Width, self.control.ClientSize.Height, 0.0, 1.0))
 
    def BeginRender(self):
        self.Context.ClearRenderTargetView(self.backBufferRTV, Color4(0, 0, 0, 0))
        self.Context.OutputMerger.SetTargets(self.backBufferRTV)
 
 
    def EndRender(self):
        self.swapChain.Present(0, PresentFlags.None)
 
    def Dispose(self):
        self.backBufferRTV.Dispose()
        self.swapChain.Dispose()
        self.device.Dispose()
 
 
class TriangleObject(GameObject):
    def __init__(self, game):
        self.game = game
        device = game.GraphicsDevice.Device
        context = game.GraphicsDevice.Context
 
        err = clr.Reference[str]()
        with ShaderBytecode.CompileFromFile("SimpleTriangle10.fx", "fx_5_0", ShaderFlags.None, EffectFlags.None, None, None, err) as shaderByteCode:
            self.effect = Effect(device, shaderByteCode)
 
        shaderTechnique = self.effect.GetTechniqueByIndex(0)
        self.shaderPass = shaderTechnique.GetPassByIndex(0)
 
        sig = self.shaderPass.Description.Signature
        self.inputLayout = InputLayout(device, sig, Array[InputElement]([InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)]))
 
        bufferDesc = BufferDescription(3 * 32, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0)
        self.vertexBuffer = Buffer(device, bufferDesc)
 
        stream = context.MapSubresource(self.vertexBuffer, 0, 3 * 32, MapMode.WriteDiscard, MapFlags.None).Data
        data = Array[Vector4]([
            Vector4(0.0, 0.5, 0.5, 1.0), Vector4(1.0, 0.0, 0.0, 1.0),
            Vector4(0.5, -0.5, 0.5, 1.0), Vector4(0.0, 1.0, 0.0, 1.0),
            Vector4(-0.5, -0.5, 0.5, 1.0), Vector4(0.0, 0.0, 1.0, 1.0)
        ])
        stream.WriteRange(data)
        context.UnmapSubresource(self.vertexBuffer, 0)
 
    def Render(self):
        context = self.game.GraphicsDevice.Context
        context.InputAssembler.InputLayout = self.inputLayout
        context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList
        context.InputAssembler.SetVertexBuffers(0, VertexBufferBinding(self.vertexBuffer, 32, 0))
        self.shaderPass.Apply(context)
        context.Draw(3, 0)
 
    def Dispose(self):
        self.effect.Dispose()
        self.inputLayout.Dispose()
        self.vertexBuffer.Dispose()
 
class Game(IDisposable):
    GraphicsDevice = property(lambda self: self.graphicsDevice)
 
    def __init__(self, width, height, fullscreen = False):
        self.fullscreen = fullscreen
        self.form = GameForm(width, height, fullscreen)
        self.form.Visible = True
        self.graphicsDevice = GraphicsDevice(self.form, self.fullscreen)
        self.gameObjects = [TriangleObject(self)]
 
    def Run(self):
        Application.Idle += self.OnIdle
        Application.Run(self.form)
 
    def OnIdle(self, ea, sender):
        while MessagePump.IsApplicationIdle:
            self.Update()
            self.Render()
 
    def Update(self):
        for i in self.gameObjects:
            i.Tick()
 
    def Render(self):
        self.GraphicsDevice.BeginRender()
        for i in self.gameObjects:
            i.Render()
        self.GraphicsDevice.EndRender()
 
    def Dispose(self):
        self.GraphicsDevice.Dispose()
        for i in self.gameObjects:
            if 'Dispose' in dir(i):
                i.Dispose()
        self.form.Dispose(True)
 
class GameForm(Form):
    def __init__(self, width, height, fullscreen):
        self.ClientSize = Size(width, height)
        if fullscreen:
            self.FormBorderStyle = FormBorderStyle.None
 
if __name__ == "__main__":
    try:
        with Game(640, 480) as game:
            game.Run()
    except Exception as e:
        MessageBox.Show(e.ToString())
 
Comments Off

Posted in .Net, Python, SlimDX

 

I Love Python

19 Jun

I think one could say that I’m somewhat infatuated with Python. It’s a wonderful language really. The language is easy to use, powerful, and developing things in it gets done so much faster than in most other languages I’ve used. It’s also pretty trivial to pickup, and once you’re past the basic strangeness of white-spacing based scope resolution, you quickly will find yourself making what would be severely complex applications in it with but a few lines.

Now, at this point, most people would be pointing out the functional origins of many of Python’s capabilities, and then point to the many functional languages that have many of the same capabilities. Or perhaps they would point to the dynamic typing, and how that makes development so much more flexible… a few would probably point out that many bugs won’t show up till compile-time that a statically typed language would find immediately.

But none of that really matters to me, because the biggest thing that I find with Python is that it encourages readable code. Now, we’re not talking C# readable code, which while it can be made readable still tends to be mixed in with a great deal of language jargon that can confuse the casual reader. Nor am I talking about C++ readable code, which just simply doesn’t exist. No, I’m talking about code that you can sit down, and almost read out loud in a sensible manner. Code that you can look at, and without having to filter out many of the little niggling bits, can simply understand what it does.

items = [
    {'name' : "Bronze Sword", 'value' : 50, 'diceCount' : 1, 'diceSides' : 4},
    {'name' : "Steel Sword", 'value' : 100, 'diceCount' : 2, 'diceSides' : 4},
    {'name' : "Adamantium Sword", 'value' : 200, 'diceCount' : 1, 'diceSides' : 10}
]
 
result = [item for item in items if item['value'] > 50]
print result

Now, looking over the above code what immediately comes to mind is that items is an array containing objects, with properties. Pretty cool in my opinion. A similar C# example could be done, but then you would have to rely on either an explicit Item object, anonymous types, or a dictionary of objects (which would involve typecasting, and other nasty behavior)…

using System;
using System.Collections.Generic;
using System.Linq;
 
static class Program {
    public static void ForEach<T>(this IEnumerable<T> cont, Action<T> action) {
        foreach (var t in cont)
            action(t);
    }
 
    static void Main(string[] args) {
        var items = new[] {
            new { Name = "Bronze Sword", Value = 50, DiceCount = 1, DiceSides = 4 },
            new {Name = "Steel Sword", Value = 100, DiceCount = 2, DiceSides = 4},
            new {Name = "Adamantium Sword", Value =200, DiceCount = 1, DiceSides = 10}
        };
 
        var result = from item in items where item.Value > 50 select item;
        result.ForEach(
            (item) =>
                Console.WriteLine(
                    "{{Name : {0}, Value : {1}, DiceCount : {2}, DiceSides : {3}}}",
                    item.Name, item.Value, item.DiceCount, item.DiceSides
                )
        );
    }
}

Now, think about how much longer that is. Heck, read through it a bit and try and easily understand it. It’s not that difficult to do, but you’re filtering out a lot of useless language garbage. Things like “new” or “var” just get in the way. Heck, look at what we had to do to easily print out the list in a single line! While we could have certainly embedded the foreach explicitly into the main function, the ability to apply that functionality to any query is just too useful to not define an extension method for it.

A C++ version, which I will not provide here, is even worse since it lacks many of the language niceties of C#. This means you end up spending more time doing all that lovely low down dirty work just to get a simple list of items to perform some queries on and then print out the results of.

But how does it encourage readability? Well, first and foremost, there’s the scoping. Since scoping is based on indentation, you have to make sure your code is properly indented. The worst experience one can have is to open someone’s source code and find a lack of, or haphazard indenting. It completely ruins the flow when reading the code. Then there’s the lack of keywords, which require you to interpret them within the context in which they are used, as some keywords in many languages behave differently depending on the context. A trivial example of this is new in C# when allocating a value type versus allocating a reference type. Last, but certainly not least, are all the libraries. These allow you to rapidly build entire applications without having to worry about all the low level nitty gritty stuff. You just get in there, and go.

 
Comments Off

Posted in Python, Software Development