CustomPresenter for embedding Irrlicht in GTK etc

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

CustomPresenter for embedding Irrlicht in GTK etc

Post by chronologicaldot »

I finally figured out how to embed Irrlicht in GTK. No more external window nor messing around with SExposedVideoData.
Image
There are a couple of caveats, as you'll see in my example code below.
1) Irrlicht can be updated along with the GUI element it's in, but this has to be triggered with GWidget::queue_draw(), and I have not yet worked on how to do this in a separate thread, which everyone tells me will require GTK's Dispatcher. However, it's no problem for user-event-driven applications.
2) The red and blue bits are swapped in GDK, so before I copy the bits from Irrlicht to the Gdk::Pixbuf for rendering, I had to swap the red and blue.
3) It only works with the software renderers. GTK doesn't use OpenGL and won't make a window for it. QT can, but I wasn't interested in doing that now.

I had to modify Irrlicht a tiny bit and create a new Device, which turned out to be really easy - a testimony to how great Irrlicht is for modifying.
It uses a semi-clone of the IImagePresenter interface, which is meant to be hidden because not all of the video drivers use it, so I made a clone.
I tried using render targets, but I still needed a device to set and control the video surface size, and render targets are 1) not as fast for copying and 2) are hacky (we cheat because we know the texture of the software renderers is CImage).

Without further ado...

Diffs!

Makefile

Code: Select all

67a68
> CUSTOMPRESDEVICE = CIrrDeviceCustomPresenter.o
74c75
< 	$(BZIP2OBJ) $(EXTRAOBJ)
---
> 	$(BZIP2OBJ) $(CUSTOMPRESDEVICE) $(EXTRAOBJ)
Irrlicht.cpp

Code: Select all

40a41,44
> #ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #include "CIrrDeviceCustomPresenter.h"
> #endif
> 
95a100,104
> #endif
> 
> #ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> 		if (params.DeviceType == EIDT_CUSTOM_PRESENTER)
> 			dev = new CIrrDeviceCustomPresenter(params);
IrrCompileConfig.h

Code: Select all

209a210,215
> //! Define _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_ to compile the Irrlicht Engine with the custom presenter device.
> #define _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #ifdef NO_IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #undef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #endif
> 
IrrlichtDevice.h

Code: Select all

17a18
> #include "SImagePresenter.h"
191a193,197
> 		
> 		//! Set the Image Presenter
> 		/** Only available on the CustomPresenter device.
> 		\return true if successful. */
> 		virtual bool setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId=0 ) { return false; }
Irrlicht.h

Code: Select all

177a178
> #include "SImagePresenter.h"
EDeviceTypes.h

Code: Select all

47a48,51
> 		
> 		//! A device for rendering to a user-defined image
> 		/** Meant for rendering to images of 3rd party GUI frameworks. */
> 		EIDT_CUSTOM_PRESENTER,
In addition, you will need the following new files.

In Irrlicht/include:
SImagePresenter.h

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#ifndef IRR_SIMAGE_PRESENTER_H_INCLUDED
#define IRR_SIMAGE_PRESENTER_H_INCLUDED

#include "IReferenceCounted.h"
#include "dimension2d.h"
#include "rect.h"
#include "IImage.h"

namespace irr
{
namespace video
{
	class SImagePresenter : public IReferenceCounted
	{
	public:
		virtual ~SImagePresenter() {}
		
		//! Render the graphics
		/** \param surface - The image from the backbuffer of the video driver that
		you get to render or copy to wherever you want.
		\param surfaceId - Use this via Custom Presenter to track where you want the
		image to be rendered/copied-to.
		\param src - The rectangular area of the surface that is meant to be drawn/copied. */
		virtual bool present(video::IImage* surface, s32 surfaceId, core::rect<s32>* src=0 ) = 0;
		
		//! Return the surface
		virtual core::dimension2du getSurfaceSize() = 0;
	};
} // end namespace video
} // end namespace irr

#endif // IRR_SIMAGE_PRESENTER_H_INCLUDED
In Irrlicht/source/Irrlicht:

CIrrDeviceCustomPresenter.h

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#ifndef IRR_CUSTOM_PRESENTER_H_INCLUDED
#define IRR_CUSTOM_PRESENTER_H_INCLUDED

#include "IrrCompileConfig.h"

#ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_

#include <position2d.h>
#include <dimension2d.h>
#include <SImagePresenter.h>
#include "IImagePresenter.h"
#include "CIrrDeviceStub.h"

namespace irr
{
	class CIrrDeviceCustomPresenter : public CIrrDeviceStub, public video::IImagePresenter
	{
	public:
		//! Constructor
		CIrrDeviceCustomPresenter(const SIrrlichtCreationParameters& param);

		//! Destructor
		virtual ~CIrrDeviceCustomPresenter();
		
		//! Run the device
		/** This both increments the clock and allows for resizing the window. */
		virtual bool run() IRR_OVERRIDE;

		//! Does nothing for this device.
		/** These methods are only here to fulfill the interface requirements. */		
		virtual void yield() IRR_OVERRIDE {}
		virtual void sleep(u32 timeMs, bool pauseTimer) IRR_OVERRIDE {}
		virtual void setWindowCaption(const wchar_t* text) IRR_OVERRIDE {}
		virtual void closeDevice() IRR_OVERRIDE {}

		//! Checks if the window is active
		/** \return Actually returns whether there is an SImagePresenter for the
		surface/backbuffer to be presented to. */
		virtual bool isWindowActive() const IRR_OVERRIDE;

		//! Checks if the Irrlicht window has the input focus
		/** \return Always false. This device does not support input. */
		virtual bool isWindowFocused() const IRR_OVERRIDE { return false; }

		//! Checks if the Irrlicht window is minimized
		/** \return Always false. This device has no window. */
		virtual bool isWindowMinimized() const IRR_OVERRIDE { return false; }

		//! Checks if the Irrlicht window is running in fullscreen mode
		/** \return Always false. This device has no window. */
		virtual bool isFullscreen() const IRR_OVERRIDE { return false; }

		//! Meant to set if the window should be resizable in windowed mode.
		/** This does nothing as the render surface size is set by the SImagePresenter. */
		virtual void setResizable(bool resize) IRR_OVERRIDE {}

		//! Set the surface size for rendering		
		virtual void setWindowSize(const irr::core::dimension2d<u32>& size) IRR_OVERRIDE;
		
		//! Does nothing for this device.
		/** These methods are only here to fulfill the interface requirements. */
		virtual void minimizeWindow() IRR_OVERRIDE {}
		virtual void maximizeWindow() IRR_OVERRIDE {}
		virtual void restoreWindow() IRR_OVERRIDE {}
		virtual core::position2di getWindowPosition() IRR_OVERRIDE { return core::position2di(0); }
		
		//! Sets the class that receives the backbuffer image created by the video driver
		virtual bool setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId ) IRR_OVERRIDE;
		
		//! Used internally
		virtual bool present(video::IImage* surface, void* windowId=0, core::rect<s32>* src=0 ) IRR_OVERRIDE;

		//! Indicates the type of this device
		virtual E_DEVICE_TYPE getType() const IRR_OVERRIDE
		{
			return EIDT_CUSTOM_PRESENTER;
		}
		
	protected:
		void createDriver();
		void createScene();
		void updateSurfaceSize();
		
	private:
		video::SImagePresenter* Presenter;
		s32 SurfaceId;
		core::dimension2du SurfaceSize;
	};
} // end namespace irr

#endif // _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
#endif // IRR_CUSTOM_PRESENTER_H_INCLUDED
CIrrDeviceCustomPresenter.cpp

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#include "CIrrDeviceCustomPresenter.h"

#ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_

#include <SIrrCreationParameters.h>
#include "os.h"
#include "CSceneManager.h"

namespace irr
{

CIrrDeviceCustomPresenter::CIrrDeviceCustomPresenter(const SIrrlichtCreationParameters& param)
	: CIrrDeviceStub(param)
	, Presenter(0)
	, SurfaceId(0)
	, SurfaceSize(param.WindowSize)
{
	createDriver();
	createScene();
}

CIrrDeviceCustomPresenter::~CIrrDeviceCustomPresenter()
{
	if (Presenter)
	{
		Presenter->drop();
		Presenter = 0;
	}
}

bool CIrrDeviceCustomPresenter::run()
{
	os::Timer::tick();
	updateSurfaceSize();
	return true;
}

bool CIrrDeviceCustomPresenter::isWindowActive() const
{
	return Presenter != 0;
}

void CIrrDeviceCustomPresenter::setWindowSize(const irr::core::dimension2d<u32>& size)
{
	// Cannot change the surface size because that's determined by the presenter,
	// but we can update it.
	updateSurfaceSize();
}

bool CIrrDeviceCustomPresenter::setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId )
{
	if ( Presenter )
	{
		Presenter->drop();
		Presenter = 0;
	}
	if ( presenter )
	{
		Presenter = presenter;
		Presenter->grab();
	}
	SurfaceId = surfaceId;
	return true;
}

bool CIrrDeviceCustomPresenter::present(video::IImage* surface, void* windowId, core::rect<s32>* src )
{
	// TODO: Future consideration is to allow windowId to have some meaning.
	// According to CSoftwareDriver2 line 986, what is stored is:
	// WindowId = videoData.D3D9.HWnd;
	// and it is this value that is passed to the present() method.
	if (Presenter)
	{
		return Presenter->present(surface, SurfaceId, src);
	}
	return false;
}

void CIrrDeviceCustomPresenter::createDriver()
{
	switch(CreationParams.DriverType)
	{
#ifdef _IRR_COMPILE_WITH_X11_
	case video::EDT_SOFTWARE:
#ifdef _IRR_COMPILE_WITH_SOFTWARE_
		VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
#else
		os::Printer::log("No Software driver support compiled in.", ELL_ERROR);
#endif
		break;
	case video::EDT_BURNINGSVIDEO:
#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
		VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
#else
		os::Printer::log("Burning's video driver was not compiled in.", ELL_ERROR);
#endif
		break;
	case video::EDT_OPENGL:
		os::Printer::log("OpenGL is not supported by this device.", ELL_ERROR);
		break;

	case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:
	case video::EDT_DIRECT3D9:
		os::Printer::log("This driver is not supported by this device.",
			ELL_ERROR);
		break;
	case video::EDT_NULL:
		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
		break;
	default:
		os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
		break;
#else
	case video::EDT_NULL:
		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
		break;
	default:
		os::Printer::log("Operating with no video driver.", ELL_INFORMATION);
		break;
#endif
	}
}

void CIrrDeviceCustomPresenter::createScene()
{
	// create Scene manager
	SceneManager = scene::createSceneManager(VideoDriver, FileSystem, CursorControl, GUIEnvironment);

	setEventReceiver(UserReceiver);
}

void CIrrDeviceCustomPresenter::updateSurfaceSize()
{
	if ( !Presenter )
		return;

	core::dimension2du newSurfaceSize = Presenter->getSurfaceSize();
	if ( SurfaceSize != newSurfaceSize )
	{
		SurfaceSize = newSurfaceSize;
		if (VideoDriver)
			VideoDriver->OnResize(SurfaceSize);
	}
}

} // end namespace irr

#endif // _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_

Example code!

For this example, you will need to include all of the includes and libraries for GTK. I have an Eclipse project file out of which you can see and copy all of the listOptionValue entries for all the includes and libraries:

Eclipse project .cproject file:

Code: Select all

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
	<storageModule moduleId="org.eclipse.cdt.core.settings">
		<cconfiguration id="cdt.managedbuild.config.gnu.exe.debug.1634220638">
			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.debug.1634220638" moduleId="org.eclipse.cdt.core.settings" name="Debug">
				<externalSettings/>
				<extensions>
					<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
				</extensions>
			</storageModule>
			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
				<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.exe.debug.1634220638" name="Debug" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=" parent="cdt.managedbuild.config.gnu.exe.debug">
					<folderInfo id="cdt.managedbuild.config.gnu.exe.debug.1634220638." name="/" resourcePath="">
						<toolChain id="cdt.managedbuild.toolchain.gnu.exe.debug.464294452" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.exe.debug">
							<targetPlatform id="cdt.managedbuild.target.gnu.platform.exe.debug.715602610" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.exe.debug"/>
							<builder buildPath="${workspace_loc:/IrrGTKCustomPresenterTest}/Debug" id="cdt.managedbuild.target.gnu.builder.exe.debug.714763245" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.exe.debug"/>
							<tool id="cdt.managedbuild.tool.gnu.archiver.base.764510329" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
							<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.1156544493" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug">
								<option id="gnu.cpp.compiler.exe.debug.option.optimization.level.1029276948" name="Optimization Level" superClass="gnu.cpp.compiler.exe.debug.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
								<option defaultValue="gnu.cpp.compiler.debugging.level.max" id="gnu.cpp.compiler.exe.debug.option.debugging.level.360095178" name="Debug Level" superClass="gnu.cpp.compiler.exe.debug.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.cpp.compiler.option.include.paths.144829812" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
									<listOptionValue builtIn="false" value="/usr/local/include/irrlicht"/>
									<listOptionValue builtIn="false" value="/usr/include/gtkmm-3.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/gtkmm-3.0/include"/>
									<listOptionValue builtIn="false" value="/usr/include/atkmm-1.6"/>
									<listOptionValue builtIn="false" value="/usr/include/gtk-3.0/unix-print"/>
									<listOptionValue builtIn="false" value="/usr/include/gdkmm-3.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/gdkmm-3.0/include"/>
									<listOptionValue builtIn="false" value="/usr/include/giomm-2.4"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/giomm-2.4/include"/>
									<listOptionValue builtIn="false" value="/usr/include/pangomm-1.4"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/pangomm-1.4/include"/>
									<listOptionValue builtIn="false" value="/usr/include/glibmm-2.4"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/glibmm-2.4/include"/>
									<listOptionValue builtIn="false" value="/usr/include/gtk-3.0"/>
									<listOptionValue builtIn="false" value="/usr/include/at-spi2-atk/2.0"/>
									<listOptionValue builtIn="false" value="/usr/include/at-spi-2.0"/>
									<listOptionValue builtIn="false" value="/usr/include/dbus-1.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/dbus-1.0/include"/>
									<listOptionValue builtIn="false" value="/usr/include/gtk-3.0"/>
									<listOptionValue builtIn="false" value="/usr/include/gio-unix-2.0"/>
									<listOptionValue builtIn="false" value="/usr/include/cairo"/>
									<listOptionValue builtIn="false" value="/usr/include/pango-1.0"/>
									<listOptionValue builtIn="false" value="/usr/include/fribidi"/>
									<listOptionValue builtIn="false" value="/usr/include/harfbuzz"/>
									<listOptionValue builtIn="false" value="/usr/include/atk-1.0"/>
									<listOptionValue builtIn="false" value="/usr/include/cairo"/>
									<listOptionValue builtIn="false" value="/usr/include/cairomm-1.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/cairomm-1.0/include"/>
									<listOptionValue builtIn="false" value="/usr/include/cairo"/>
									<listOptionValue builtIn="false" value="/usr/include/pixman-1"/>
									<listOptionValue builtIn="false" value="/usr/include/uuid"/>
									<listOptionValue builtIn="false" value="/usr/include/freetype2"/>
									<listOptionValue builtIn="false" value="/usr/include/libpng16"/>
									<listOptionValue builtIn="false" value="/usr/include/sigc++-2.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/sigc++-2.0/include"/>
									<listOptionValue builtIn="false" value="/usr/include/gdk-pixbuf-2.0"/>
									<listOptionValue builtIn="false" value="/usr/include/libmount"/>
									<listOptionValue builtIn="false" value="/usr/include/blkid"/>
									<listOptionValue builtIn="false" value="/usr/include/glib-2.0"/>
									<listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu/glib-2.0/include"/>
								</option>
								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.include.files.71342643" name="Include files (-include)" superClass="gnu.cpp.compiler.option.include.files" useByScannerDiscovery="false" valueType="includeFiles"/>
								<option id="gnu.cpp.compiler.option.dialect.std.466233890" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.c++17" valueType="enumerated"/>
								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.6199757" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
							</tool>
							<tool id="cdt.managedbuild.tool.gnu.c.compiler.exe.debug.744156975" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.exe.debug">
								<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.exe.debug.option.optimization.level.185481247" name="Optimization Level" superClass="gnu.c.compiler.exe.debug.option.optimization.level" useByScannerDiscovery="false" valueType="enumerated"/>
								<option defaultValue="gnu.c.debugging.level.max" id="gnu.c.compiler.exe.debug.option.debugging.level.639517101" name="Debug Level" superClass="gnu.c.compiler.exe.debug.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1586742396" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
							</tool>
							<tool id="cdt.managedbuild.tool.gnu.c.linker.exe.debug.1051401144" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.debug"/>
							<tool id="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug.465688178" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug">
								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.cpp.link.option.libs.866297673" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs">
									<listOptionValue builtIn="false" value="Irrlicht"/>
									<listOptionValue builtIn="false" value="GL"/>
									<listOptionValue builtIn="false" value="gtkmm-3.0"/>
									<listOptionValue builtIn="false" value="atkmm-1.6"/>
									<listOptionValue builtIn="false" value="gdkmm-3.0"/>
									<listOptionValue builtIn="false" value="giomm-2.4"/>
									<listOptionValue builtIn="false" value="pangomm-1.4"/>
									<listOptionValue builtIn="false" value="glibmm-2.4"/>
									<listOptionValue builtIn="false" value="gtk-3"/>
									<listOptionValue builtIn="false" value="gdk-3"/>
									<listOptionValue builtIn="false" value="pangocairo-1.0"/>
									<listOptionValue builtIn="false" value="pango-1.0"/>
									<listOptionValue builtIn="false" value="harfbuzz"/>
									<listOptionValue builtIn="false" value="atk-1.0"/>
									<listOptionValue builtIn="false" value="cairo-gobject"/>
									<listOptionValue builtIn="false" value="gio-2.0"/>
									<listOptionValue builtIn="false" value="cairomm-1.0"/>
									<listOptionValue builtIn="false" value="cairo"/>
									<listOptionValue builtIn="false" value="sigc-2.0"/>
									<listOptionValue builtIn="false" value="gdk_pixbuf-2.0"/>
									<listOptionValue builtIn="false" value="gobject-2.0"/>
									<listOptionValue builtIn="false" value="glib-2.0"/>
								</option>
								<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1614118428" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
								</inputType>
							</tool>
							<tool id="cdt.managedbuild.tool.gnu.assembler.exe.debug.523688494" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.exe.debug">
								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.473846942" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
							</tool>
						</toolChain>
					</folderInfo>
					<sourceEntries>
						<entry excluding="src" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
					</sourceEntries>
				</configuration>
			</storageModule>
			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
		</cconfiguration>
	</storageModule>
	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
		<project id="IrrGTKCustomPresenterTest.cdt.managedbuild.target.gnu.exe.2067463168" name="Executable" projectType="cdt.managedbuild.target.gnu.exe"/>
	</storageModule>
	<storageModule moduleId="scannerConfiguration">
		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.1634220638;cdt.managedbuild.config.gnu.exe.debug.1634220638.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.1156544493;cdt.managedbuild.tool.gnu.cpp.compiler.input.6199757">
			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
		</scannerConfigBuildInfo>
		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.823068514;cdt.managedbuild.config.gnu.exe.release.823068514.;cdt.managedbuild.tool.gnu.c.compiler.exe.release.1718865303;cdt.managedbuild.tool.gnu.c.compiler.input.1419537182">
			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
		</scannerConfigBuildInfo>
		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.823068514;cdt.managedbuild.config.gnu.exe.release.823068514.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.988431735;cdt.managedbuild.tool.gnu.cpp.compiler.input.660843686">
			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
		</scannerConfigBuildInfo>
		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.1634220638;cdt.managedbuild.config.gnu.exe.debug.1634220638.;cdt.managedbuild.tool.gnu.c.compiler.exe.debug.744156975;cdt.managedbuild.tool.gnu.c.compiler.input.1586742396">
			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
		</scannerConfigBuildInfo>
	</storageModule>
	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
	<storageModule moduleId="refreshScope" versionNumber="2">
		<configuration configurationName="Debug">
			<resource resourceType="PROJECT" workspacePath="/IrrGTKCustomPresenterTest"/>
		</configuration>
		<configuration configurationName="Release">
			<resource resourceType="PROJECT" workspacePath="/IrrGTKCustomPresenterTest"/>
		</configuration>
	</storageModule>
</cproject>
Now for the example code: main.cpp

Code: Select all

/*
 * main.cpp
 *
 *  Created on: Jan 21, 2023
 *      Author: Nicolaus Anderson
 */

#include <iostream>
#include <gtkmm.h>
#include <irrlicht.h>

/*
gint32 IrrSColorToGdkColor(irr::video::SColor c)
{
	return c.getAlpha() << 24 | c.getBlue() << 16 | c.getGreen() << 8 | c.getRed();
}
*/

irr::video::SColor flipRedAndBlue(irr::video::SColor c)
{
	return irr::video::SColor(
				c.getAlpha(), c.getBlue(), c.getGreen(), c.getRed()
			);
}

class IrrPanel : public Gtk::DrawingArea, public irr::video::SImagePresenter
{
public:
	IrrPanel(guint32 width, guint32 height, guint8 antiAlias = 0, irr::video::SColor bufferColor=irr::video::SColor(0))
		: my_irrParams()
		, my_irrDevice(0)
		, my_bufferColor(bufferColor)
		, my_videoBuffer(0)
		, my_videoSrcRect()
		, my_pixbuf()
	{
		my_irrParams.DeviceType = irr::EIDT_CUSTOM_PRESENTER;
		my_irrParams.DriverType = irr::video::EDT_BURNINGSVIDEO;
		my_irrParams.AntiAlias = antiAlias;
		my_irrParams.Bits = 32;
		my_irrParams.WithAlphaChannel = true;
		my_irrParams.WindowSize.set(width, height);
		my_irrDevice = irr::createDeviceEx(my_irrParams);
		my_irrDevice->setImagePresenter(this);

		set_size_request(width, height);
	}

	irr::scene::ISceneManager* getSceneManager()
	{
		if (my_irrDevice)
			return my_irrDevice->getSceneManager();

		return 0;
	}

	virtual bool present(irr::video::IImage* surface, irr::s32 surfaceId, irr::core::rect<irr::s32>* src )
	{
		my_videoBuffer = surface;
		if ( src )
			my_videoSrcRect = *src;

		if (my_videoBuffer)
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Swap red and blue because GDK has them flipped
			for ( irr::u32 w = 0; w < surfaceSize.Width; ++w)
			{
				for ( irr::u32 h = 0; h < surfaceSize.Height; ++h)
				{
					my_videoBuffer->setPixel(w, h, flipRedAndBlue(my_videoBuffer->getPixel(w, h)), false);
				}
			}
		}

		return true;
	}

	//! Return the surface
	virtual irr::core::dimension2du getSurfaceSize()
	{
		return my_irrParams.WindowSize;
	}

protected:
	bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override
	{
		if (!my_irrDevice)
			return false;

		my_irrDevice->run();
		my_irrDevice->getVideoDriver()->beginScene(true, true, my_bufferColor);
		my_irrDevice->getSceneManager()->drawAll();
		my_irrDevice->getVideoDriver()->endScene();
		// endScene calls present, which sets the video buffer and source rect
		if ( my_videoBuffer )
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Currently, create_from_data only supports colors of 8 bits per sample.
			// Unfortunately, red and blue bits are swapped compared to Irrlicht,
			// so we do that in present()
			my_pixbuf = my_pixbuf->create_from_data(
					(const guint8*) my_videoBuffer->getData(), Gdk::COLORSPACE_RGB, true, 8,
					surfaceSize.Width, surfaceSize.Height, my_videoBuffer->getPitch());
		}

		const int location_x = 0;
		const int location_y = 0;

		Gdk::Cairo::set_source_pixbuf(cr, my_pixbuf, location_x, location_y);
		cr->paint();
		return true;
	}

protected:
	irr::SIrrlichtCreationParameters my_irrParams;
	irr::IrrlichtDevice* my_irrDevice;
	irr::video::SColor my_bufferColor;
	irr::video::IImage* my_videoBuffer;
	irr::core::recti my_videoSrcRect;
	Glib::RefPtr<Gdk::Pixbuf> my_pixbuf;
};


class MainWindow : public Gtk::Window {
public:
	MainWindow()
		: my_width(800)
		, my_height(200)
		, my_vbox(false,0) // No equal space allotment and no spacing between elements
		, my_irrPanel(my_width, 500, 0, irr::video::SColor(0xff0000ff))
		, my_button_addCube("Add Cube")
	{
		set_default_size(my_width, my_height);
		add(my_vbox);
		//my_vbox.pack_start(my_irrPanel);
		my_vbox.pack_end(my_irrPanel);
		my_vbox.pack_end(my_button_addCube, Gtk::PACK_SHRINK);

		my_button_addCube.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::addCubeToScene)); // @suppress("Invalid arguments")
		show_all_children();

		// TODO: Add camera and cube to scene
		if ( my_irrPanel.getSceneManager() )
		{
			my_irrPanel.getSceneManager()->addCameraSceneNode(
					0, irr::core::vector3df(0,2,-2), irr::core::vector3df(0), 0, true);

			my_irrPanel.getSceneManager()->addLightSceneNode(
					0, irr::core::vector3df(0,5,0), irr::video::SColor(0xffffffff), 7, 1);

			//my_irrPanel.getSceneManager()->addCubeSceneNode(
			//		1, 0, 1, irr::core::vector3df(0), irr::core::vector3df(0), irr::core::vector3df(1),
			//		irr::scene::ECMT_1BUF_12VTX_NA);
		}
	}

	void addCubeToScene()
	{
		if ( my_irrPanel.getSceneManager() )
		{
			irr::scene::ISceneNode* cubeNode = my_irrPanel.getSceneManager()->addCubeSceneNode(
					1, 0, 2, irr::core::vector3df(0), irr::core::vector3df(0,20,0), irr::core::vector3df(1),
					irr::scene::ECMT_1BUF_12VTX_NA);

			irr::video::SMaterial& mat = cubeNode->getMaterial(0);
			mat.AmbientColor = irr::video::SColor(0xffff0000);
			mat.DiffuseColor = irr::video::SColor(0xffff0000);
			mat.GouraudShading = true;
			mat.Lighting = true;
			mat.ColorMaterial = irr::video::ECM_NONE; // irr::video::ECM_DIFFUSE_AND_AMBIENT;
			mat.MaterialType = irr::video::EMT_SOLID;
		}
		my_irrPanel.queue_draw();
	}

protected:
	const int my_width;
	const int my_height;
	Gtk::VBox my_vbox;
	IrrPanel my_irrPanel;
	Gtk::Button my_button_addCube;
};

int main(int argc, char* argv[])
{
	Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.irr_fb_test"); // @Suppress("Invalid arguments")

	MainWindow mainWindow;

	return app->run(mainWindow);
}
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by chronologicaldot »

Looks like the first image host didn't work.
Image
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by chronologicaldot »

Update: When you want the Irrlicht window to resize whenever GTK resizes, replace the other getSurfaceSize() in the IrrPanel with this:

Code: Select all

	//! Return the surface
	virtual irr::core::dimension2du getSurfaceSize()
	{
		Gtk::Allocation allocation;
		int baseline = 1;
		get_allocated_size(allocation, baseline);
		irr::core::dimension2du& WindowSize = my_irrParams.WindowSize;
		if ( allocation.get_width() > 0 && allocation.get_height() > 0
				&& allocation.get_width() != WindowSize.Width
				&& allocation.get_height() != WindowSize.Height )
		{
			my_irrParams.WindowSize.set(allocation.get_width(), allocation.get_height());
		}
		return my_irrParams.WindowSize;
	}
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Compiler Errors

Post by livphi »

This code seems like it would work to me :) but I haven't gotten it to compile the main.cpp file. I'm trying to run Irrlicht and use gtk for the gui. I tried compiling this with irrlicht 1.8 and gtkmm-3.0. After removing the IRR_OVERRIDE undefined macros, with the main.cpp file left to compile, my compiler, g++ outputs this:

Code: Select all

../main.cpp: In member function ‘virtual bool IrrPanel::on_draw(const Cairo::RefPtr<Cairo::Context>&)’:
../main.cpp:104:73: error: ‘class irr::video::IImage’ has no member named ‘getData’
  104 |                                         (const guint8*) my_videoBuffer->getData(), Gdk::COLORSPACE_RGB, true, 8,
      |
I checked irr::video::IImage, it does not have a member named getData. https://irrlicht.sourceforge.io/docu/cl ... image.html
Neither does irrlicht 1.7 https://sourceforge.net/p/irrlicht/code ... e/IImage.h

I assume you were using gtkmm-3.0, and irrlicht 1.7, because this was posted in early 2023 and because compiling with gtkmm-3.0 resulted in less compiler errors for me. I dont know why you're calling getData() from irr::video::IImage, or how you got that to work. What version of Irrlicht and gtkmm were you using? irr::video::SColor has a member named getData(), is that the one I should use? https://irrlicht.sourceforge.io/docu/cl ... color.html Did i miss a diff or file?

I'm on artixlinux, using meson to compile irrlicht (I did add CIrrDeviceCustomPresenter.cpp to the meson.build in Irrlicht) Using g++ 13.2.1. Using X11.

In class `IrrPanel`:

Code: Select all

	irr::video::IImage* my_videoBuffer;

Code: Select all

	virtual bool present(irr::video::IImage* surface, irr::s32 surfaceId, irr::core::rect<irr::s32>* src )
	{
		my_videoBuffer = surface;
		if ( src )
			my_videoSrcRect = *src;

		if (my_videoBuffer)
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Swap red and blue because GDK has them flipped
			for ( irr::u32 w = 0; w < surfaceSize.Width; ++w)
			{
				for ( irr::u32 h = 0; h < surfaceSize.Height; ++h)
				{
					my_videoBuffer->setPixel(w, h, flipRedAndBlue(my_videoBuffer->getPixel(w, h)), false);
				}
			}
		}

		return true;
	}

Code: Select all

		if ( my_videoBuffer )
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Currently, create_from_data only supports colors of 8 bits per sample.
			// Unfortunately, red and blue bits are swapped compared to Irrlicht,
			// so we do that in present()
			my_pixbuf = my_pixbuf->create_from_data(
					(const guint8*) my_videoBuffer->getData(), Gdk::COLORSPACE_RGB, true, 8,
					surfaceSize.Width, surfaceSize.Height, my_videoBuffer->getPitch());
		}
Thank you,
livphi
Last edited by livphi on Thu Apr 18, 2024 5:51 pm, edited 2 times in total.
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by livphi »

Update, I got it to work with irrlicht 1.8.5 and gtkmm-3.0, by replacing the nonexistent getData() function with the lock() function, in irr::video::IImage.

Code: Select all

			my_pixbuf = my_pixbuf->create_from_data(
					(const guint8*) my_videoBuffer->lock(), Gdk::COLORSPACE_RGB, true, 8,
					surfaceSize.Width, surfaceSize.Height, my_videoBuffer->getPitch());
		my_videoBuffer->unlock();
I might try porting it to gtkmm-4.0, but i noticed there are a few API changes, and also gtk4 uses opengl instead of cairo to draw widgets.
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by livphi »

Updated diffs!

source/Irrlicht/Makefile (unchanged)

Code: Select all

67a68
> CUSTOMPRESDEVICE = CIrrDeviceCustomPresenter.o
74c75
< 	$(BZIP2OBJ) $(EXTRAOBJ)
---
> 	$(BZIP2OBJ) $(CUSTOMPRESDEVICE) $(EXTRAOBJ)
source/Irrlicht/Irrlicht.cpp (unchanged)

Code: Select all

40a41,44
> #ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #include "CIrrDeviceCustomPresenter.h"
> #endif
> 
95a100,104
> #endif
> 
> #ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> 		if (params.DeviceType == EIDT_CUSTOM_PRESENTER)
> 			dev = new CIrrDeviceCustomPresenter(params);

include/IrrCompileConfig.h (unchanged)

Code: Select all

209a210,215
> //! Define _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_ to compile the Irrlicht Engine with the custom presenter device.
> #define _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #ifdef NO_IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #undef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
> #endif
> 
include/IrrlichtDevice.h (unchanged)

Code: Select all

17a18
> #include "SImagePresenter.h"
191a193,197
> 		
> 		//! Set the Image Presenter
> 		/** Only available on the CustomPresenter device.
> 		\return true if successful. */
> 		virtual bool setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId=0 ) { return false; }
include/irrlicht.h (unchanged)

Code: Select all

177a178
> #include "SImagePresenter.h"
include/EDeviceTypes.h (unchanged)

Code: Select all

47a48,51
> 		
> 		//! A device for rendering to a user-defined image
> 		/** Meant for rendering to images of 3rd party GUI frameworks. */
> 		EIDT_CUSTOM_PRESENTER,
Updated new files!

include/SImagePresenter.h (unchanged)

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#ifndef IRR_SIMAGE_PRESENTER_H_INCLUDED
#define IRR_SIMAGE_PRESENTER_H_INCLUDED

#include "IReferenceCounted.h"
#include "dimension2d.h"
#include "rect.h"
#include "IImage.h"

namespace irr
{
namespace video
{
	class SImagePresenter : public IReferenceCounted
	{
	public:
		virtual ~SImagePresenter() {}
		
		//! Render the graphics
		/** \param surface - The image from the backbuffer of the video driver that
		you get to render or copy to wherever you want.
		\param surfaceId - Use this via Custom Presenter to track where you want the
		image to be rendered/copied-to.
		\param src - The rectangular area of the surface that is meant to be drawn/copied. */
		virtual bool present(video::IImage* surface, s32 surfaceId, core::rect<s32>* src=0 ) = 0;
		
		//! Return the surface
		virtual core::dimension2du getSurfaceSize() = 0;
	};
} // end namespace video
} // end namespace irr

#endif // IRR_SIMAGE_PRESENTER_H_INCLUDED
source/Irrlicht/CIrrDeviceCustomPresenter.h (remove IRR_OVERRIDE macro, the other devices don't use this macro, so I don't know why the original author used it.)

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#ifndef IRR_CUSTOM_PRESENTER_H_INCLUDED
#define IRR_CUSTOM_PRESENTER_H_INCLUDED

#include "IrrCompileConfig.h"

#ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_

#include <position2d.h>
#include <dimension2d.h>
#include <SImagePresenter.h>
#include "IImagePresenter.h"
#include "CIrrDeviceStub.h"

namespace irr
{
	class CIrrDeviceCustomPresenter : public CIrrDeviceStub, public video::IImagePresenter
	{
	public:
		//! Constructor
		CIrrDeviceCustomPresenter(const SIrrlichtCreationParameters& param);

		//! Destructor
		virtual ~CIrrDeviceCustomPresenter();
		
		//! Run the device
		/** This both increments the clock and allows for resizing the window. */
		virtual bool run() ;

		//! Does nothing for this device.
		/** These methods are only here to fulfill the interface requirements. */		
		virtual void yield()  {}
		virtual void sleep(u32 timeMs, bool pauseTimer)  {}
		virtual void setWindowCaption(const wchar_t* text)  {}
		virtual void closeDevice()  {}

		//! Checks if the window is active
		/** \return Actually returns whether there is an SImagePresenter for the
		surface/backbuffer to be presented to. */
		virtual bool isWindowActive() const ;

		//! Checks if the Irrlicht window has the input focus
		/** \return Always false. This device does not support input. */
		virtual bool isWindowFocused() const  { return false; }

		//! Checks if the Irrlicht window is minimized
		/** \return Always false. This device has no window. */
		virtual bool isWindowMinimized() const  { return false; }

		//! Checks if the Irrlicht window is running in fullscreen mode
		/** \return Always false. This device has no window. */
		virtual bool isFullscreen() const  { return false; }

		//! Meant to set if the window should be resizable in windowed mode.
		/** This does nothing as the render surface size is set by the SImagePresenter. */
		virtual void setResizable(bool resize)  {}

		//! Set the surface size for rendering		
		virtual void setWindowSize(const irr::core::dimension2d<u32>& size) ;
		
		//! Does nothing for this device.
		/** These methods are only here to fulfill the interface requirements. */
		virtual void minimizeWindow()  {}
		virtual void maximizeWindow()  {}
		virtual void restoreWindow()  {}
		virtual core::position2di getWindowPosition()  { return core::position2di(0); }
		
		//! Sets the class that receives the backbuffer image created by the video driver
		virtual bool setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId ) ;
		
		//! Used internally
		virtual bool present(video::IImage* surface, void* windowId=0, core::rect<s32>* src=0 ) ;

		//! Indicates the type of this device
		virtual E_DEVICE_TYPE getType() const 
		{
			return EIDT_CUSTOM_PRESENTER;
		}
		
	protected:
		void createDriver();
		void createScene();
		void updateSurfaceSize();
		
	private:
		video::SImagePresenter* Presenter;
		s32 SurfaceId;
		core::dimension2du SurfaceSize;
	};
} // end namespace irr

#endif // _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
#endif // IRR_CUSTOM_PRESENTER_H_INCLUDED
source/Irrlicht/CIrrDeviceCustomPresenter.cpp (remove case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:)

Code: Select all

// (C) 2023 Nicolaus Anderson, Nikolaus Gebhardt
// See irrlicht.h for license

#include "CIrrDeviceCustomPresenter.h"

#ifdef _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_

#include <SIrrCreationParameters.h>
#include "os.h"
#include "CSceneManager.h"

namespace irr
{

CIrrDeviceCustomPresenter::CIrrDeviceCustomPresenter(const SIrrlichtCreationParameters& param)
	: CIrrDeviceStub(param)
	, Presenter(0)
	, SurfaceId(0)
	, SurfaceSize(param.WindowSize)
{
	createDriver();
	createScene();
}

CIrrDeviceCustomPresenter::~CIrrDeviceCustomPresenter()
{
	if (Presenter)
	{
		Presenter->drop();
		Presenter = 0;
	}
}

bool CIrrDeviceCustomPresenter::run()
{
	os::Timer::tick();
	updateSurfaceSize();
	return true;
}

bool CIrrDeviceCustomPresenter::isWindowActive() const
{
	return Presenter != 0;
}

void CIrrDeviceCustomPresenter::setWindowSize(const irr::core::dimension2d<u32>& size)
{
	// Cannot change the surface size because that's determined by the presenter,
	// but we can update it.
	updateSurfaceSize();
}

bool CIrrDeviceCustomPresenter::setImagePresenter( video::SImagePresenter* presenter, s32 surfaceId )
{
	if ( Presenter )
	{
		Presenter->drop();
		Presenter = 0;
	}
	if ( presenter )
	{
		Presenter = presenter;
		Presenter->grab();
	}
	SurfaceId = surfaceId;
	return true;
}

bool CIrrDeviceCustomPresenter::present(video::IImage* surface, void* windowId, core::rect<s32>* src )
{
	// TODO: Future consideration is to allow windowId to have some meaning.
	// According to CSoftwareDriver2 line 986, what is stored is:
	// WindowId = videoData.D3D9.HWnd;
	// and it is this value that is passed to the present() method.
	if (Presenter)
	{
		return Presenter->present(surface, SurfaceId, src);
	}
	return false;
}

void CIrrDeviceCustomPresenter::createDriver()
{
	switch(CreationParams.DriverType)
	{
#ifdef _IRR_COMPILE_WITH_X11_
	case video::EDT_SOFTWARE:
#ifdef _IRR_COMPILE_WITH_SOFTWARE_
		VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
#else
		os::Printer::log("No Software driver support compiled in.", ELL_ERROR);
#endif
		break;
	case video::EDT_BURNINGSVIDEO:
#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
		VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
#else
		os::Printer::log("Burning's video driver was not compiled in.", ELL_ERROR);
#endif
		break;
	case video::EDT_OPENGL:
		os::Printer::log("OpenGL is not supported by this device.", ELL_ERROR);
		break;

	case video::EDT_DIRECT3D9:
		os::Printer::log("This driver is not supported by this device.",
			ELL_ERROR);
		break;
	case video::EDT_NULL:
		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
		break;
	default:
		os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
		break;
#else
	case video::EDT_NULL:
		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
		break;
	default:
		os::Printer::log("Operating with no video driver.", ELL_INFORMATION);
		break;
#endif
	}
}

void CIrrDeviceCustomPresenter::createScene()
{
	// create Scene manager
	SceneManager = scene::createSceneManager(VideoDriver, FileSystem, CursorControl, GUIEnvironment);

	setEventReceiver(UserReceiver);
}

void CIrrDeviceCustomPresenter::updateSurfaceSize()
{
	if ( !Presenter )
		return;

	core::dimension2du newSurfaceSize = Presenter->getSurfaceSize();
	if ( SurfaceSize != newSurfaceSize )
	{
		SurfaceSize = newSurfaceSize;
		if (VideoDriver)
			VideoDriver->OnResize(SurfaceSize);
	}
}

} // end namespace irr

#endif // _IRR_COMPILE_WITH_CUSTOM_PRESENTER_DEVICE_
Example code!

I got this to work on artixlinux (arch) by linking with X11 and Xxf86vm, and using gtkmm-3.0 as a dependency, and of course Irrlicht's dependencies (libjpeg, libpng, zlib, bzip2, gl). Here is the example top directory meson.build file:

Code: Select all

project(
  'irrlicht-custom-presenter',
  'cpp', 'c',
)

irrlicht_include = include_directories('irrlicht/include/')
subdir('irrlicht/source/Irrlicht/')

libs_include = [
    irrlicht_include,
] 

libs = [
    irrlicht,
]

linux_link_args = [
  '-lX11',
  '-lXxf86vm'
]

link_args = [
  linux_link_args,
]

srcs = [
  'main.cpp',
]

executable(
  meson.project_name(),
  srcs,
  include_directories: libs_include,
  dependencies: [dependency('gtkmm-3.0')],
  link_with: libs,
  link_args: link_args
)
main.cpp (remove nonexistent irr::video::IImage::getData() and use irr::video::IImage::lock() and irr::video::IImage::unlock() https://irrlicht.sourceforge.io/docu/cl ... 038e20d204 . Remove nonexistent irr::scene::ECMT_1BUF_12VTX_NA enumeration, I could not find anything like this in the documentation :shock:)

Code: Select all

/*
 * main.cpp
 *
 *  Created on: Jan 21, 2023
 *      Author: Nicolaus Anderson
 */

#include <iostream>
#include <gtkmm.h>
#include <irrlicht.h>

/*
gint32 IrrSColorToGdkColor(irr::video::SColor c)
{
	return c.getAlpha() << 24 | c.getBlue() << 16 | c.getGreen() << 8 | c.getRed();
}
*/

irr::video::SColor flipRedAndBlue(irr::video::SColor c)
{
	return irr::video::SColor(
				c.getAlpha(), c.getBlue(), c.getGreen(), c.getRed()
			);
}

class IrrPanel : public Gtk::DrawingArea, public irr::video::SImagePresenter
{
public:
	IrrPanel(guint32 width, guint32 height, guint8 antiAlias = 0, irr::video::SColor bufferColor=irr::video::SColor(0))
		: my_irrParams()
		, my_irrDevice(0)
		, my_bufferColor(bufferColor)
		, my_videoBuffer(0)
		, my_videoSrcRect()
		, my_pixbuf()
	{
		my_irrParams.DeviceType = irr::EIDT_CUSTOM_PRESENTER;
		my_irrParams.DriverType = irr::video::EDT_BURNINGSVIDEO;
		my_irrParams.AntiAlias = antiAlias;
		my_irrParams.Bits = 32;
		my_irrParams.WithAlphaChannel = true;
		my_irrParams.WindowSize.set(width, height);
		my_irrDevice = irr::createDeviceEx(my_irrParams);
		my_irrDevice->setImagePresenter(this);

		set_size_request(width, height);
	}

	irr::scene::ISceneManager* getSceneManager()
	{
		if (my_irrDevice)
			return my_irrDevice->getSceneManager();

		return 0;
	}

	virtual bool present(irr::video::IImage* surface, irr::s32 surfaceId, irr::core::rect<irr::s32>* src )
	{
		my_videoBuffer = surface;
		if ( src )
			my_videoSrcRect = *src;

		if (my_videoBuffer)
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Swap red and blue because GDK has them flipped
			for ( irr::u32 w = 0; w < surfaceSize.Width; ++w)
			{
				for ( irr::u32 h = 0; h < surfaceSize.Height; ++h)
				{
					my_videoBuffer->setPixel(w, h, flipRedAndBlue(my_videoBuffer->getPixel(w, h)), false);
				}
			}
		}

		return true;
	}

	//! Return the surface
	virtual irr::core::dimension2du getSurfaceSize()
	{
		return my_irrParams.WindowSize;
	}

protected:
	bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override
	{
		if (!my_irrDevice)
			return false;

		my_irrDevice->run();
		my_irrDevice->getVideoDriver()->beginScene(true, true, my_bufferColor);
		my_irrDevice->getSceneManager()->drawAll();
		my_irrDevice->getVideoDriver()->endScene();
		// endScene calls present, which sets the video buffer and source rect
		if ( my_videoBuffer )
		{
			const irr::core::dimension2du surfaceSize = my_videoBuffer->getDimension();
			// Currently, create_from_data only supports colors of 8 bits per sample.
			// Unfortunately, red and blue bits are swapped compared to Irrlicht,
			// so we do that in present()
			my_pixbuf = my_pixbuf->create_from_data(
					(const guint8*) my_videoBuffer->lock(), Gdk::COLORSPACE_RGB, true, 8,
					surfaceSize.Width, surfaceSize.Height, my_videoBuffer->getPitch());
			my_videoBuffer->unlock();
                }

		const int location_x = 0;
		const int location_y = 0;

		Gdk::Cairo::set_source_pixbuf(cr, my_pixbuf, location_x, location_y);
		cr->paint();
		return true;
	}

protected:
	irr::SIrrlichtCreationParameters my_irrParams;
	irr::IrrlichtDevice* my_irrDevice;
	irr::video::SColor my_bufferColor;
	irr::video::IImage* my_videoBuffer;
	irr::core::recti my_videoSrcRect;
	Glib::RefPtr<Gdk::Pixbuf> my_pixbuf;
};


class MainWindow : public Gtk::Window {
public:
	MainWindow()
		: my_width(800)
		, my_height(200)
		, my_vbox(false,0) // No equal space allotment and no spacing between elements
		, my_irrPanel(my_width, 500, 0, irr::video::SColor(0xff0000ff))
		, my_button_addCube("Add Cube")
	{
		set_default_size(my_width, my_height);
		add(my_vbox);
		//my_vbox.pack_start(my_irrPanel);
		my_vbox.pack_end(my_irrPanel);
		my_vbox.pack_end(my_button_addCube, Gtk::PACK_SHRINK);

		my_button_addCube.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::addCubeToScene)); // @suppress("Invalid arguments")
		show_all_children();

		// TODO: Add camera and cube to scene
		if ( my_irrPanel.getSceneManager() )
		{
			my_irrPanel.getSceneManager()->addCameraSceneNode(
					0, irr::core::vector3df(0,2,-2), irr::core::vector3df(0), 0, true);

			my_irrPanel.getSceneManager()->addLightSceneNode(
					0, irr::core::vector3df(0,5,0), irr::video::SColor(0xffffffff), 7, 1);

			//my_irrPanel.getSceneManager()->addCubeSceneNode(
			//		1, 0, 1, irr::core::vector3df(0), irr::core::vector3df(0), irr::core::vector3df(1),
			//		irr::scene::ECMT_1BUF_12VTX_NA);
		}
	}

	void addCubeToScene()
	{
		if ( my_irrPanel.getSceneManager() )
		{
			irr::scene::ISceneNode* cubeNode = my_irrPanel.getSceneManager()->addCubeSceneNode(
					1, 0, 2, irr::core::vector3df(0), irr::core::vector3df(0,20,0), irr::core::vector3df(1));

			irr::video::SMaterial& mat = cubeNode->getMaterial(0);
			mat.AmbientColor = irr::video::SColor(0xffff0000);
			mat.DiffuseColor = irr::video::SColor(0xffff0000);
			mat.GouraudShading = true;
			mat.Lighting = true;
			mat.ColorMaterial = irr::video::ECM_NONE; // irr::video::ECM_DIFFUSE_AND_AMBIENT;
			mat.MaterialType = irr::video::EMT_SOLID;
		}
		my_irrPanel.queue_draw();
	}

protected:
	const int my_width;
	const int my_height;
	Gtk::VBox my_vbox;
	IrrPanel my_irrPanel;
	Gtk::Button my_button_addCube;
};

int main(int argc, char* argv[])
{
	Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.irr_fb_test"); // @Suppress("Invalid arguments")

	MainWindow mainWindow;

	return app->run(mainWindow);
}
With irrlicht 1.8.5 and gtkmm-3.0, this should compile fine. If not, please reply to me here. This should work on Windows, you just might have to change the linking (no X11 on windows).
chronologicaldot
Competition winner
Posts: 688
Joined: Mon Sep 10, 2012 8:51 am

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by chronologicaldot »

Actually, to be honest, I used the latest version of Irrlicht from the repo, which is why there is an IImage::getData() method. But thanks for posting the diffs - They should help anyone still using the stable release of Irrlicht.
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by livphi »

Of course :) I was also able to port it to gtkmm-4.0, and I made a few modifications. I figured out how to update the Irrlicht widget every frame using a tick callback, and I added a few more buttons. Here is the source:

main.cpp

Code: Select all

/*
 * main.cpp
 *
 *  Created on: Jan 21, 2023
 *      Author: Nicolaus Anderson
 *  
 *  Modified by Olivia May on Apr 20, 2024
 */

#include <gtkmm.h>
#include <irrlicht.h>
#include <iostream>

/*
gint32 IrrSColorToGdkColor(irr::video::SColor c)
{
	return c.getAlpha() << 24 | c.getBlue() << 16 | c.getGreen() << 8 | c.getRed();
}
*/

irr::video::SColor flipRedAndBlue(irr::video::SColor c) {
  return irr::video::SColor(c.getAlpha(), c.getBlue(), c.getGreen(),
                            c.getRed());
}

class IrrPanel : public Gtk::DrawingArea, public irr::video::SImagePresenter {
 public:
  IrrPanel(guint32 width, guint32 height, guint8 antiAlias = 0,
           irr::video::SColor bufferColor = irr::video::SColor(0))
      : my_irrParams(),
        my_irrDevice(0),
        my_bufferColor(bufferColor),
        my_videoBuffer(0),
        my_videoSrcRect(),
        my_pixbuf() {
    my_irrParams.DeviceType = irr::EIDT_CUSTOM_PRESENTER;
    my_irrParams.DriverType = irr::video::EDT_BURNINGSVIDEO;
    my_irrParams.AntiAlias = antiAlias;
    my_irrParams.Bits = 32;
    my_irrParams.WithAlphaChannel = true;
    my_irrParams.WindowSize.set(width, height);
    my_irrDevice = irr::createDeviceEx(my_irrParams);

    if (!my_irrDevice)
      std::exit(1);

    my_irrDevice->setImagePresenter(this);

    set_size_request(width, height);

    // Use gtk's mainloop to redraw the irrlicht panel like
    // it's an animation
    add_tick_callback(sigc::mem_fun(*this, &IrrPanel::on_tick));

    set_draw_func(sigc::mem_fun(*this, &IrrPanel::on_draw));
  }

  irr::scene::ISceneManager* getSceneManager() {
    return my_irrDevice->getSceneManager();
  }

  virtual bool present(irr::video::IImage* surface, irr::s32 surfaceId,
                       irr::core::rect<irr::s32>* src) {
    my_videoBuffer = surface;
    if (src)
      my_videoSrcRect = *src;

    if (my_videoBuffer) {
      const irr::core::dimension2du surfaceSize =
          my_videoBuffer->getDimension();
      // Swap red and blue because GDK has them flipped
      for (irr::u32 w = 0; w < surfaceSize.Width; ++w) {
        for (irr::u32 h = 0; h < surfaceSize.Height; ++h) {
          my_videoBuffer->setPixel(
              w, h, flipRedAndBlue(my_videoBuffer->getPixel(w, h)), false);
        }
      }
    }

    return true;
  }

  //! Return the surface
  virtual irr::core::dimension2du getSurfaceSize() {
    return my_irrParams.WindowSize;
  }

 protected:
  void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {

    my_irrDevice->run();
    my_irrDevice->getVideoDriver()->beginScene(true, true, my_bufferColor);
    my_irrDevice->getSceneManager()->drawAll();
    my_irrDevice->getVideoDriver()->endScene();
    // endScene calls present, which sets the video buffer and source rect
    if (my_videoBuffer) {
      const irr::core::dimension2du surfaceSize =
          my_videoBuffer->getDimension();
      // Currently, create_from_data only supports colors of 8 bits per sample.
      // Unfortunately, red and blue bits are swapped compared to Irrlicht,
      // so we do that in present()
      my_pixbuf = my_pixbuf->create_from_data(
          (const guint8*)my_videoBuffer->lock(), Gdk::Colorspace::RGB, true, 8,
          surfaceSize.Width, surfaceSize.Height, my_videoBuffer->getPitch());
      my_videoBuffer->unlock();
    }

    const int location_x = 0;
    const int location_y = 0;

    Gdk::Cairo::set_source_pixbuf(cr, my_pixbuf, location_x, location_y);
    cr->paint();
  }

  bool on_tick(const Glib::RefPtr<Gdk::FrameClock>& frame_clock) {

    queue_draw();
    return true;
  }

  irr::SIrrlichtCreationParameters my_irrParams;
  irr::IrrlichtDevice* my_irrDevice;
  irr::video::SColor my_bufferColor;
  irr::video::IImage* my_videoBuffer;
  irr::core::recti my_videoSrcRect;
  Glib::RefPtr<Gdk::Pixbuf> my_pixbuf;
};

class MainWindow : public Gtk::Window {
 public:
  MainWindow()
      : my_width(800),
        my_height(600),
        my_container(),
        my_irrPanel(my_width, my_height, 0, irr::video::SColor(0xff0000ff)),
        my_box(Gtk::Orientation::VERTICAL, 0),
        my_button_addCube("Add Cube"),
        my_button_removeCube("Remove Cube"),
        my_button_example("Example Button") {

    set_default_size(my_width, my_height);
    set_child(my_container);

    my_container.put(my_irrPanel, 0, 0);
    my_box.set_size_request(400, 100);
    my_container.put(my_box, 20, 20);
    my_box.append(my_button_addCube);
    my_box.append(my_button_removeCube);
    my_box.append(my_button_example);

    my_button_addCube.signal_clicked().connect(sigc::mem_fun(
        *this, &MainWindow::addCubeToScene));  // @suppress("Invalid arguments")
    my_button_removeCube.signal_clicked().connect(sigc::mem_fun(
        *this,
        &MainWindow::removeCubeFromScene));  // @suppress("Invalid arguments")

    present();

    my_irrPanel.getSceneManager()->addCameraSceneNode(
        0, irr::core::vector3df(0, 2, -2), irr::core::vector3df(0), 0, true);

    my_irrPanel.getSceneManager()->addLightSceneNode(
        0, irr::core::vector3df(0, 5, 0), irr::video::SColor(0xffffffff), 7, 1);
  }

  void addCubeToScene() {
    if (!cubeNode) {
      cubeNode = my_irrPanel.getSceneManager()->addCubeSceneNode(
          1, 0, 2, irr::core::vector3df(0), irr::core::vector3df(0, 20, 0),
          irr::core::vector3df(1));

      irr::video::SMaterial& mat = cubeNode->getMaterial(0);
      mat.AmbientColor = irr::video::SColor(0xffff0000);
      mat.DiffuseColor = irr::video::SColor(0xffff0000);
      mat.GouraudShading = true;
      mat.Lighting = true;
      mat.ColorMaterial =
          irr::video::ECM_NONE;  // irr::video::ECM_DIFFUSE_AND_AMBIENT;
      mat.MaterialType = irr::video::EMT_SOLID;
    }
  }
  void removeCubeFromScene() {
    if (cubeNode) {
      cubeNode->remove();
      cubeNode = nullptr;
    }
  }

 protected:
  const int my_width, my_height;
  Gtk::Fixed my_container;
  IrrPanel my_irrPanel;
  Gtk::Box my_box;
  Gtk::Button my_button_addCube, my_button_removeCube, my_button_example;
  irr::scene::ISceneNode* cubeNode = nullptr;
};

int main(int argc, char* argv[]) {

  Glib::RefPtr<Gtk::Application> app =
      Gtk::Application::create("org.gtkmm.irr_fb_test");

  return app->make_window_and_run<MainWindow>(argc, argv);
}

I'm trying to figure out how to use the Irrlicht device with the opengl driver with gtk4 or gtkmm-4.0. Gtk4 uses opengl to render its widgets, so it might be possible to embed opengl Irrlicht in it. So far I haven't had any luck, if I figure it out I'll make a post.
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by livphi »

chronologicaldot wrote: Mon Apr 22, 2024 5:11 pm Actually, to be honest, I used the latest version of Irrlicht from the repo, which is why there is an IImage::getData() method. But thanks for posting the diffs - They should help anyone still using the stable release of Irrlicht.
Ah, I see now, sorry for the confusion.
https://sourceforge.net/p/irrlicht/code ... age.h#l180
thenewkgb
Posts: 59
Joined: Thu Jan 25, 2024 6:54 pm
Location: UK

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by thenewkgb »

Can I ask a question about Qt since you mentioned it? I had heard about Qt being a great tool for GUI. I never thought I would need it as Windows has plenty of ways with their tools, so when I eventually got around to visiting the Qt site I was rather shocked. It's great if you plan on creating OSS but otherwise there's a hefty price tag on it. I wouldn't be able to afford Qt any time soon - so would you agree to open source Qt to use it?
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by CuteAlien »

Qt is open source. You just have to be fine with gpl or lgpl 3.0 license: https://www.qt.io/download-open-source
In short - as long as your software is also open source you are free to use it. KDE desktop on Linux does use it for example.

edit: Actually the parts under lgpl should also be useable in proprietary projects. They try to hide and confuse this somewhat and I don't know if/how much is uder lgpl these days.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
livphi
Posts: 7
Joined: Tue Apr 16, 2024 10:10 pm

Re: CustomPresenter for embedding Irrlicht in GTK etc

Post by livphi »

The thing I don't like is that Qt uses utf-16 (widechar) strings. I know irrlicht also does for its GUI. It would be nice if you could use Irrlicht with the utf-8 strings GTK uses for its GUI, so pretty much everything would be char (utf-8). See: https://utf8everywhere.org/ I understand Irrlicht and Qt use wchar_t for historical reasons. I've also been learning GTK4 and its a lot simpler imo than Irrlicht's gui, and more modern looking.
Post Reply