Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: DSFML (a port to the D language)  (Read 49947 times)

0 Members and 1 Guest are viewing this topic.

Redien

  • Newbie
  • *
  • Posts: 30
    • View Profile
DSFML (a port to the D language)
« on: April 15, 2008, 12:40:51 am »
Hi!
I've recently picked up on the D language (http://www.digitalmars.com/d/) and wanted to start developing some games for it. But D can only directly link to C object files and not C++ and because of that the only multimedia libraries availiable have C bindings.

Therefore I decided to create a port of the SFML library with the goal of providing an interface as close to the C++ version as possible. I use version 1.2 as the reference since I wanted a stable interface to port so that the interface wouldn't change as I ported it.

Currently I have as good as ported the whole Window interface aswell as the Win32 implementation. (I haven't implemented joystick support yet, but it would be just a matter of converting the C++ code to D.) The interface is almost identical to the one found in C++. The only differences are the use of .'s instead of :'s and the use of modules and imports instead of header files. Which makes the tutorials still apply with some minor changes.

Some parts of the System package are also ported as they are required by the Window package. Cross-platform threads and mutexes are implemented by the D standard library so there's not much need for porting them.

Currently the Win32 implementation of the Window interface is all I need, so I probably won't be adding other packages to the port unless I find them useful in the future.

If anyone's interested in taking a look or maybe port other parts of SFML I'd happily send it over. :)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
DSFML (a port to the D language)
« Reply #1 on: April 15, 2008, 03:42:28 am »
Hi

Someone is already working on a D binding, and I think it's almost finished. I'll tell its author to come and be part of this discussion ;)
Laurent Gomila - SFML developer

Redien

  • Newbie
  • *
  • Posts: 30
    • View Profile
DSFML (a port to the D language)
« Reply #2 on: April 15, 2008, 08:56:19 am »
Ah, I'm too late then. :)
I'm very interested in seeing how a bind is made possible.

fredreichbier

  • Newbie
  • *
  • Posts: 6
    • View Profile
DSFML (a port to the D language)
« Reply #3 on: April 15, 2008, 10:46:55 am »
Me too. SFML is a nice library and D is a nice language ;)

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #4 on: April 15, 2008, 06:11:07 pm »
Hi all,

i'm the person who's currently working on the D binding for SFML. The objective of this binding, is to preserve the SFML api but, C++ -> D is only possible on the D2 compiler, and D2 is, ATM, at experimental stage (and constantly changed ... you said "transitive const" =p). So, given the fact that D already has a C interface, i think it's the best option to bind through the C SFML interface instead of the C++ one. C use in D is easy and lot of other lib use it, the C++ way is too limited (maybe when D2 switch to stable release).

For the technics, at the beginning, i've used the Derelict way (aka dynamically load .dll or .so and instanciate function pointer) i.e. (from the DerelictSDL trunk) :

 
Code: [Select]
extern(C)
{
    typedef int function(Uint32) pfSDL_Init;
    typedef int function(Uint32) pfSDL_InitSubSystem;
    typedef int function(Uint32) pfSDL_QuitSubSystem;
    typedef int function(Uint32) pfSDL_WasInit;
    typedef void function() pfSDL_Quit;
    pfSDL_Init    SDL_Init;
    pfSDL_InitSubSystem  SDL_InitSubSystem;
    pfSDL_QuitSubSystem  SDL_QuitSubSystem;
    pfSDL_WasInit   SDL_WasInit;
    pfSDL_Quit    SDL_Quit;
}

    bindFunc(SDL_Init)("SDL_Init", lib);
    bindFunc(SDL_InitSubSystem)("SDL_InitSubSystem", lib);
    bindFunc(SDL_QuitSubSystem)("SDL_QuitSubSystem", lib);
    bindFunc(SDL_WasInit)("SDL_WasInit", lib);
    bindFunc(SDL_Quit)("SDL_Quit", lib);
   
GenericLoader DerelictSDL;
static this()
{
    DerelictSDL.setup(
        "sdl.dll",
        "libSDL.so, libSDL.so.0, libSDL-1.2.so, libSDL-1.2.so.0",
        "../Frameworks/SDL.framework/SDL, /Library/Frameworks/SDL.framework/SDL, /System/Library/Frameworks/SDL.framework/SDL",
        &load
    );
}


But i have some problems with this design. After some reading, I understand that OptLink (the Digital Mars D linker for non-D folks =p) can link import lib of DLL but only if they are in OMF format (cl or lib, for visual, generate in COFF format). I tried to regenerate import lib from the C SFML dll in the correct format (with implib, a tool from the DMC utility suite), and it worked ! Now, the D lib looks like (from PostFX external definition) :

Code: [Select]
extern(C)
{
struct sfPostFX {};
   
sfPostFX* sfPostFX_CreateFromFile(char*);
sfPostFX* sfPostFX_CreateFromMemory(char*);
void sfPostFX_Destroy(sfPostFX*);
void sfPostFX_SetParameter1(sfPostFX*, char*, float);
void sfPostFX_SetParameter2(sfPostFX*, char*, float, float);
void sfPostFX_SetParameter3(sfPostFX*, char*, float, float, float);
void sfPostFX_SetParameter4(sfPostFX*, char*, float, float, float, float);
void sfPostFX_SetTexture(sfPostFX*, char*, sfImage*);
bool sfPostFX_CanUsePostFX();
}


No need of function pointers or static loader initialization, just link against the correct import lib. I keep working on the Derelict way because i can only test under Windows system and i don't know if the import lib trick is possible with GDC.

For the current status, all packages are binded, except some system classes (like Thread or Mutex which use the phobos or tangos internal ancapsulated in the SFML api) because thread created outside D are not GC scanned (so the Terminate method become unusable because tango or phobos thread cannot be terminated, this is not a bad news, terminate a thread is not a good practices IMO =) ), and i'm currently testing it. For the openGL headers, i have adaptated headers from the OpenGL Binding project.

As I said to Laurent earlier, the next step is 1) solidify the code (writing some unittest, clean the code, and some optimizing stuffs) 2) finish adapting the documentation from doxygen comments to ddoc one 3) rewrite some samples. I hope i can release a first version (tested for windows) this weekend or early during the next week.

It's screenshot time ! =p (you can't see, but quad move with arrow keys =) ).

The code :

Code: [Select]
module test;

import sfml.window.all;
import sfml.system.all;
import sfml.graphics.all;

import sfml.GL.gl;
import sfml.GL.glu;


GLfloat rx = 0.f;
GLfloat ry = 0.f;
GLfloat xspeed = 0.f;
GLfloat yspeed = 0.f;

Input i;

void main()
{
    RenderWindow w = new RenderWindow(VideoMode(1024, 768, 32), "Test");
    i = w.GetInput();
   
    String hello = new String("Duke, say hello to D folks !"w, "comic.ttf", 30.f);
    hello.SetColor(Color.White);
    hello.SetPosition(50.f, 50.f);
   
    Image img = new Image();
    if (!img.LoadFromFile("DukeNukem-3d.tga"))
        assert(0, "Error");
    img.CreateMaskFromColor(Color.White);
    Sprite sprite = new Sprite();
    sprite.SetImage(img);
    sprite.SetSubRect(new Rect!(int)(0, 0, 42, 85));
    sprite.SetPosition(hello.GetRect().GetWidth() + hello.GetRect().GetLeft() + 30.f, 50.f);
   
   
    glClearDepth(1.f);
    glClearColor(0.f, 0.f, 0.f, 0.f);
    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(90.f, 1.f, 1.f, 500.f);
   

    bool cont = true;

    while (cont)
    {
        Event evt;
        while (w.GetEvent(evt))
        {
            switch (evt.Type)
            {
                case Event.EventType.Closed :
                    cont = false;
                break;
                case Event.EventType.Resized :
                    glViewport(0, 0, evt.Size.Width, evt.Size.Height);
                break;
                case Event.EventType.KeyPressed :
                    if (evt.Key.Code == KeyCode.Escape)
                        cont = false;
                    HandleKey(i);
                break;
                default :
                break;
            }
        }
       
        w.SetCurrent();
         
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
       
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0.f, 0.f, -200.f);
        glRotatef(rx, 1.f, 0.f, 0.f);
        glRotatef(ry, 0.f, 1.f, 0.f);
       
        glBegin(GL_QUADS);
           
            glColor3f(0.f, 1.f, 0.f);
            glVertex3f(-50.f, -50.f, -50.f);
            glVertex3f(-50.f,  50.f, -50.f);
            glVertex3f( 50.f,  50.f, -50.f);
            glVertex3f( 50.f, -50.f, -50.f);
           
            glColor3f(0.f, 1.f, 1.f);
            glVertex3f(-50.f, -50.f, 50.f);
            glVertex3f(-50.f,  50.f, 50.f);
            glVertex3f( 50.f,  50.f, 50.f);
            glVertex3f( 50.f, -50.f, 50.f);
           
            glColor3f(1.f, 1.f, 0.f);
            glVertex3f(-50.f, -50.f, -50.f);
            glVertex3f(-50.f,  50.f, -50.f);
            glVertex3f(-50.f,  50.f,  50.f);
            glVertex3f(-50.f, -50.f,  50.f);

            glColor3f(0.f, 0.5f, 0.f);
            glVertex3f(50.f, -50.f, -50.f);
            glVertex3f(50.f,  50.f, -50.f);
            glVertex3f(50.f,  50.f,  50.f);
            glVertex3f(50.f, -50.f,  50.f);
           
            glColor3f(1.f, 0.f, 0.f);
            glVertex3f(-50.f, -50.f,  50.f);
            glVertex3f(-50.f, -50.f, -50.f);
            glVertex3f( 50.f, -50.f, -50.f);
            glVertex3f( 50.f, -50.f,  50.f);
           
            glColor3f(0.f, 0.f, 1.f);
            glVertex3f(-50.f, 50.f,  50.f);
            glVertex3f(-50.f, 50.f, -50.f);
            glVertex3f( 50.f, 50.f, -50.f);
            glVertex3f( 50.f, 50.f,  50.f);

        glEnd();
       
        w.Draw(hello);
        w.Draw(sprite);
       
        rx += xspeed;
        ry += yspeed;
       
        w.Display();
    }
}

void HandleKey(Input i)
{
    if (i.IsKeyDown(KeyCode.Up))
    {
        xspeed -= 0.1f;
    }
    if (i.IsKeyDown(KeyCode.Down))
    {
        xspeed += 0.1f;
    }
    if (i.IsKeyDown(KeyCode.Left))
    {
        yspeed -= 0.1f;
    }
    if (i.IsKeyDown(KeyCode.Right))
    {
        yspeed += 0.1f;
    }
}


generated with :

Code: [Select]
dmd program.d program.def libd/sfmlsystem.lib libd/sfmlwindow.lib libd/sfmlgraphics.lib lib/dsfml-system.lib lib/dsfml-window.lib lib/dsfml-graphics.lib lib/opengl32d.lib lib/glu32d.lib tango-user-dmd.lib -g -debug -w -v



If you have any question. =)[/code]

Avency

  • Full Member
  • ***
  • Posts: 113
    • View Profile
DSFML (a port to the D language)
« Reply #5 on: April 15, 2008, 10:10:40 pm »
This looks quite interesting.

BTW: Do you know any good D IDE (offering code completition etc.)?
I wasn't able to find one myself when I looked at D last year.
Has the situation changed?

Redien

  • Newbie
  • *
  • Posts: 30
    • View Profile
DSFML (a port to the D language)
« Reply #6 on: April 15, 2008, 10:22:04 pm »
I see, so you are wrapping the C interface with classes? This is what I wanted to avoid, hence the port. ;)

As you said there don't seem to be a good solution until D 2.0 is stable enough. May I ask how you were thinking of linking with the C++ code on 2.0? I haven't really looked at 2.0 yet since I'm just starting out.

But your way is probably the best one for now as it's easier to maintain when going from one SFML version to another.

I think I'll keep my version though, since I only need the Windows package for now. And the interfaces seem to be identical.

At least it's good to see that there's an interest in maintaining a D binding. :)

Edit: Did you consider how to create a shared sf namespace for all the packages? Not that it's a big of a problem but it would help creating a consistent interface with the other bindings.

Edit2: You mentioned you use Derelict to load symbols from the DLL. How does this affect the license? Isn't Derelict licensed as LGPL? This was also one of the reasons I decided to port SFML.

Edit3: Ah sorry I probably should have looked that up. Looks like the Util library uses a modified zlib/libpng license?

Redien

  • Newbie
  • *
  • Posts: 30
    • View Profile
DSFML (a port to the D language)
« Reply #7 on: April 15, 2008, 10:26:32 pm »
Quote from: "Avency"
BTW: Do you know any good D IDE (offering code completition etc.)?


Code::Blocks support D to an extent but unfortunately lacks support for code-completion for it.

Although I haven't used it, Poseidon seem to be still developed on and have quite a following. http://www.dsource.org/projects/poseidon
AFAIK it supports code-completion.

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #8 on: April 15, 2008, 11:57:58 pm »
Hi,

for the IDE, ATM, there are no good IDE with good code completion so I keep using PsPad, but IMO the best autocompletion support is Descent (Eclipse based).

For C++ support, even on D2, the ability to interop with C++ is pretty limited. You cannot call non virtual methods (non virtual is implicit on C++), don"t have access to fields (only via Get Set methods) => here. So, for the moment, i don't want to use D2.

For the namespace, it's an internal probleme of D (which hasn't), but we can use selective renamed import to avoid collision :

Code: [Select]
import sfml.graphics.string : SfString = String;

We can use static import too, but no C++ ns with using etc ...

For the licence, I said I have only tested Derelict way, but in fact, i didn't use it at all and don't need it because tango and phobos now have their own dll loader (std.loader or tango.system.SharedLib) if i need to use dynamic loading later. I must see that with the prior project developper, but for me, i want to keep the original licence of SFML (aka zlib/libpng), I don't like LGPL or GPL stuff.

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #9 on: April 20, 2008, 02:52:45 am »
Hi all,

just a little update, binding is almost finished (Currently this is a bind of SFML 1.2. 1.3 modification is not included, I'm waiting Laurent released it, but I already begin to adapt the binding for 1.3).

I did some tests based on Laurent Benchmark SDL (C) / SFML (C++) with Derelict and the SFML D binding. Results are identical of the initial benchmark.

Quote
1/ Test : sprites
Derelict SDL  displayed 8 frames
D SFML displayed 213 frames
--> SFML is 2562% faster than SDL
2/ Test : alpha-blended sprites
Derelict SDL  displayed 5 frames
D SFML displayed 150 frames
--> SFML is 2900% faster than SDL
3/ Test : static text
Derelict SDL  displayed 795 frames
D SFML displayed 5983 frames
--> SFML is 652% faster than SDL
4/ Test : dynamic text
Derelict SDL  displayed 616 frames
D SFML displayed 792 frames
--> SFML is 28% faster than SDL


The test with SDL_gfx was not run because Derelict has no official gfx bind and i cannot donwload the other contribution i find.

After some tests, i found the runtime dll loading (with function pointer etc ...) easier to handle, so this is the default build of the binding, you just need to initialize the SFML binding with :

Code: [Select]
import sfml.dsfml; // only one import

void main()
{
    DSFML.init(); //for all modules or ...

    //...

    DSFML.init(GRAPHICS | WINDOW | SYSTEM); // for selective init (only initialized module are loaded)
}


DLL loading is handled by Sharedlib classes of Phobos or Tango (by the way, this binding works on Phobos and Tango). But dynamic loading have some issues, by example, you cannot use unit tests or static constructor because they are run before the main entry point (so before the call to init). That's why, i keep a second build option to generate the library. External function is only external(C) and the lib must be linked with import library of the C SFML dll. With this option, unit test and static constructor doesn't cause problems and the init is not necessary because DLL loading is handled by the OS, but you need to provide OMF import lib to the linker (generated by implib or others).

Actually, only DLL linking of the SFML is supported.

Redien

  • Newbie
  • *
  • Posts: 30
    • View Profile
DSFML (a port to the D language)
« Reply #10 on: April 20, 2008, 03:33:34 pm »
Looks good. I'm looking forward to trying it out. :)
Regarding the linking, what benefits other than being able to use the library without an import library does the first build option give, if any?
In what way was dynamic loading of symbols easier to handle? Personally I don't mind creating an import library or a bit of more hassle if that gives me better functionality.

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #11 on: April 22, 2008, 12:19:51 am »
In fact, you're right ... =p

I'm trying to keep the original purpose of SFML (easy and powerful), and i thought that linking import lib would be difficult for people who actually use SFML. But, this doesn't make an huge difference between import or dynamic :

Quote
(import lib version)
dmd [project_files(.d)] [sfml-modules.lib] [import_libray]

(dynamic loading version)
dmd [project_files(.d)] [sfml-modules.lib] -version=Dynamic


and, dynamic loading have no advantages compared to the import linking.

So I keep the dynamic loading (i don't know how linking things are handled by GCC (?) on linux and mac system), but import libs will be the default build.

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #12 on: May 18, 2008, 06:18:41 pm »
Hi,

just to give some updates,

DSFML is now hosted on the SFML svn (cf. wiki if you don't know how to checkout). It will be released with the the upcoming 1.3 sfml. I suggest you to use DSSS and rebuild. To build lib  dsss build in import folder to build the lib, dsss build in samples/dsfml folder for the samples. If you want to compile your own project, just add -I[path of DSFML import] in your dss.conf (or rebuild command line).

If you use it on windows just build SFML (release static) and CSFML (release dll) and place it into the clib/win32 folder of DSFML, but don't overwrite actual .lib (dmd cannot link against visual import lib). For linux-users, just build and install SFML and CSFML. To compile your project, add -S[path of clib/win32]. And place csfml dll in your exe folder.

the documentation isn't on the svn (but will be in the release =) ), if you need it go into the import folder and dsss build -version=Ddoc and you could find multiple html files in doc folder.

Very quick start :

Code: [Select]

module veryquickstart;

import dsfml.system.all;
import dsfml.window.all;
import dsfml.graphics.all;

void main()
{
    RenderWindow app = new RenderWindow(VideoMode(800, 600, 32), "It works !");
    Shape s = Shape.circle(300.f, 300.f, 100.f, Color(120, 120, 120, 120));
    app.setFramerateLimit(100);
   
    Event evt;  
    while (app.isOpened())
    {
        while (app.getEvent(evt))
        {
            // close the window
            if ((evt.Type == Event.EventType.CLOSED) ||
               ((evt.Type == Event.EventType.KEYPRESSED) && (evt.Key.Code == KeyCode.ESCAPE)))
            {
                app.close();
                break;
            }        
        }

        s.move(0.1f, 0);
       
        app.draw(s);
        app.display();
    }
   
}


Currently, i'm "tracking" last remaining bugs, but lib must be usable. =)

fredreichbier

  • Newbie
  • *
  • Posts: 6
    • View Profile
DSFML (a port to the D language)
« Reply #13 on: July 19, 2008, 05:18:04 pm »
Hello!

just wanted to say thanks for the binding. Great work! :)

I had to to dsss build -L-lopenal to compile the samples - is that normal?

SirJulio

  • Full Member
  • ***
  • Posts: 241
    • View Profile
DSFML (a port to the D language)
« Reply #14 on: July 19, 2008, 09:37:08 pm »
Hi Fredreichbier,

no it's not.

Do you use the release or the svn ? Which sample need to be linked with OpenAL ?

 

anything