Thread overview
Messing with OpenGL in D
Dec 06
JN
December 05
I'm fairly new to Dlang, but have learnt the basics. I wondered how I would be able to make an OpenGL-based Engine in D, what libraries would I need? Your help will be much appreciated!

- NCPlayz
December 05
On Wed, Dec 05, 2018 at 07:12:34PM +0000, Nadir Chowdhury via Digitalmars-d-learn wrote:
> I'm fairly new to Dlang, but have learnt the basics. I wondered how I would be able to make an OpenGL-based Engine in D, what libraries would I need?  Your help will be much appreciated!
[...]

All you need is some bindings to your OS's OpenGL libraries and GUI libraries, and you should be able to just call OpenGL functions directly. You could use dstep to translate the system headers into D then import them.  The main tricky part is setting up your GL context so that it plays nicely with your OS's GUI system.

I don't know what OS you're on, but I have a Linux project that does just this:

- Install required libraries: X11 libraries (+ header files), MESA
  (which contains an implementation of GL and GLX needed to make X11 and
  GL work together).

- First, I translated /usr/include/X11/{X,Xlib,Xutil,keysymdef}.h into
  D (I did it by hand -- not the entire header file but just enough to
  call the most basic functions to create an X11 window).

- Then I translated /usr/include/GL/{gl,glx,glext,glxext}.h into D --
  again, not the entire thing but just those OpenGL calls that I need to
  work with. You could probably just use dstep to translate the whole
  thing and not have to worry about it after that.

- Write code to connect to the X server and create a window:
   - XOpenDisplay
   - glXChooseGBConfig
   - glXGetVisualFromFBConfig
   - XCreateColormap
   - XCreateWindow
   - XMapWindow

- If you need any GL extensions:
   - glXQueryExtensionsString
   - glXCreateContextAttribsARB or glXCreateContext

- Once the window is created and mapped, you need an event loop to
  process X11 / GLX events. This may be quite delicate, depending on
  whether you're planning to have a mainly passive GUI (i.e., respond to
  user actions only) or you want to have animation / program-driven
  changes (like game engines).

   - For passive GUIs, all you need is a loop that calls XNextEvent to
     retrieve the next event, and when you get an Expose event, call
     XGetWindowAttributes to get the window dimensions, then use that to
     setup your GL viewport, projection matrices, render the scene,
     etc..

   - For active GUIs (like games), you'll need to handle X events
     asynchronously. You can either use a different timing thread and
     send yourself custom events when you need to redraw a frame, or if
     you need to make it work in a single thread:

     	/* Very rough sketch on what needs to be done -- if you want to
	 * see actual code, ask. */

	// This function handles all X11 events -- the bare minimum is
	// Expose, so that you know when to draw the first frame. You
	// probably also want KeyPress if you need keyboard input,
	// ButtonPress / ButtonRelease for mouse buttons, MotionNotify
	// for mouse motion, etc..
	void handleEvent() {
		XEvent ev;
		XNextEvent(display, &ev);
		switch (ev.type)
		{
			case Expose:
				... // draw first frame
				break;

			... // handle other events here
		}
	}

	// This is needed for processing initial events triggered by
	// window creation and mapping, in order to clear Xlib's queue,
	// otherwise the main loop will have sync problems or stall for
	// the first couple of frames.
	XFlush(display);
	while (XEventsQueued(display, QueuedAlready) > 0)
		handleEvent();

	// File descriptor of X11 socket
	immutable x11fd = ConnectionNumber(display);

	// Main loop
	for (;;) {
		// Use select() or epoll() on x11fd to check when you
		// need to process X11 events.
		// Use the timeout argument on select() or epoll() to
		// schedule your animation.
		if ( /* select or epoll indicates read-ready on x11fd */)
		{
			// Note: be sure to write it exactly this way in
			// order to prevent your main loop from stalling
			// due to Xlib misbehaviour.
			while (XPending(display))
				handleEvent();
		}
		if ( /* timeout expired / time to draw a frame */ )
		{
			// call GL functions to render frame
		}
	}

- When compiling, make sure to pass `-lX11 -lGL` to your linker so that
  it will link the X11 and GL libraries. Otherwise you'll get a bunch of
  undefined symbol errors.

P.S., Yes, this is the manual way, low-down of doing things. There are probably libraries out there that abstract these ugly dirty details away for you.  But I'm putting this out here so that people know how to make things work when the easy way fails.

P.S.S. I can share the X11/GL headers I translated to D if you're curious. Be warned, however, that they are highly incomplete, because I only translated what I actually use, not the entire header files. Using dstep for translating your local header files is highly recommended instead.  But looking at my translated headers may give you a good idea of how to mechanically translate C headers to D. It's pretty straightforward once you know how.

P.S.S.S. The stuff about managing the event loop can probably be avoided by using XCB instead, or using a dedicated GUI thread separate from your application logic just to work nicely with Xlib. But since GLX is built on Xlib, you cannot avoid using Xlib at least for setting up the GL context. After that, you might be able to get away with switching to XCB as long as you let XCB know that the X11 connection is "owned" by Xlib (google for the dirty details).

P.S.S.S.S. I highly recommend defining an abstract API for your program logic, so that the above dirty stuff can be kept in its own module and just make calls to the real program logic via the abstract API. You really do not want to be debugging code that has application logic mixed with Xlib handling. For my project, since it's Android based (the X11 driver is just for testing app logic on host PC), I use an API closely modelled after Android's:

	interface Application {
		void initialize();
		void onChange(int width, int height);
		void onDrawFrame();
		void onTouch(float x, float y);
		void onPause();
		void onResume();
		void onStop();
	}

You can add other methods as needed, of course. And you can use D's compile-time features to eliminate those nasty virtual calls, if need be.  But the point is to keep the application logic completely separate from the dirty low-level details of X11 handling. It will help maintain your sanity when it comes time for debugging.


T

-- 
Do not reason with the unreasonable; you lose by definition.
December 05
On Wednesday, 5 December 2018 at 20:06:09 UTC, H. S. Teoh wrote:
> I don't know what OS you're on, but I have a Linux project [...]

I'm on Windows 10, so which parts would differ? Sorry, should've mentioned it in my original post. Thanks for the reply!
December 05
On Wednesday, 5 December 2018 at 20:36:43 UTC, Nadir Chowdhury wrote:
> I'm on Windows 10, so which parts would differ? Sorry, should've mentioned it in my original post. Thanks for the reply!

Basically all of it lol. The principals are the same, but the specific functions are all different.

What version of opengl do you wanna play with?

my lib simpledisplay.d
https://github.com/adamdruppe/arsd/blob/master/simpledisplay.d

depends on color.d
https://github.com/adamdruppe/arsd/blob/master/color.d


but otherwise might help get started. lemme know a few deets and i can get you a sample
December 05
On Wed, Dec 05, 2018 at 08:44:13PM +0000, Adam D. Ruppe via Digitalmars-d-learn wrote:
> On Wednesday, 5 December 2018 at 20:36:43 UTC, Nadir Chowdhury wrote:
> > I'm on Windows 10, so which parts would differ? Sorry, should've mentioned it in my original post. Thanks for the reply!
> 
> Basically all of it lol. The principals are the same, but the specific functions are all different.

Haha yeah.  Pretty much everything I said would be completely different on Windows.  But yeah, the principles are the same:

1) Somehow create a window / screen using your OS's GUI API.

2) Create a GL context for that window / screen (probably can be done as
part of (1)).

3) Create a main loop to handle OS GUI events (mainly forwarding to
application logic).

4) Use GL for rendering whenever it's time to draw a frame.


T

-- 
If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
December 05
On Wednesday, 5 December 2018 at 19:12:34 UTC, Nadir Chowdhury wrote:
> I'm fairly new to Dlang, but have learnt the basics. I wondered how I would be able to make an OpenGL-based Engine in D, what libraries would I need? Your help will be much appreciated!
>
> - NCPlayz

I use BindBC-OpenGL for my game engine.
To handle windows and input you can either use BindBC-GLFW (like me) or BindBC-SDL2.
It's very nice and easy.
I hope it helped.
Good luck!
December 06
On Wednesday, 5 December 2018 at 19:12:34 UTC, Nadir Chowdhury wrote:
> I'm fairly new to Dlang, but have learnt the basics. I wondered how I would be able to make an OpenGL-based Engine in D, what libraries would I need? Your help will be much appreciated!
>
> - NCPlayz

I use Derelict's GLFW bindings - https://github.com/DerelictOrg/DerelictGLFW3 to create the OpenGL context and stuff.

If you need a math library, dlib math from https://code.dlang.org/packages/dlib is probably the best place to start. gl3n is also nice, but it uses row-major matrices, so you have to remember to always transpose them when uploading to OpenGL.