This page has been translated automatically.
Видеоуроки
Interface
Essentials
Advanced
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Professional (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Расширение функционала редактора
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Players
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Плагины
Форматы файлов
Materials and Shaders
Rebuilding the Engine Tools
Интерфейс пользователя (GUI)
Двойная точность координат
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Сэмплы материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Внимание! Эта версия документация УСТАРЕЛА, поскольку относится к более ранней версии SDK! Пожалуйста, переключитесь на самую актуальную документацию для последней версии SDK.
Внимание! Эта версия документации описывает устаревшую версию SDK, которая больше не поддерживается! Пожалуйста, обновитесь до последней версии SDK.

Integrating with Frameworks

Warning
The functionality described in this article is not available in the Community SDK edition.
You should upgrade to Engineering / Sim SDK edition to use it.

To integrate UNIGINE Engine with another system (for example Qt, SDL, WPF, WinForms, etc.), you can use the Unigine.CustomSystemProxy class. Its concept incorporates the definition of available functions (window creation and management, input management, additional functionality such as dialogs, clipboard, etc.) along with all necessary overrides.

The functionality of some engine subsystems is defined depending on the set of functions made available by the user. This class forms the basis for the operation of WindowManager, Input, GUI, Displays, etc.

The CustomSystemProxy-based workflow allows the following:

  • Creating a window without using the platform-dependent code
  • Creating a separate rendering window without any plugins
  • Implementing the window creation functionality, which is common for all applications
  • Obtaining information on the physical configuration of displays
  • Providing full functionality of the main window for other windows (GUI, Input, etc.)
Notice
A separate proxy implementation is required for each integration environment (SystemProxyWPF, etc.).

Integration Workflow#

To correctly use the CustomSystemProxy class, you should do the following:

  1. Create a custom class and inherit it from the Unigine.CustomSystemProxy class.
  2. Define the supported features via the proxy constructor (SYSTEM_PROXY_FEATURES.*).
  3. Override all virtual functions of the class.
  4. Implement the functions according to the list of the supported features, including event handling and rendering into an external window, if required.
Notice
The full-featured example of integrating UNIGINE Engine into the WPF framework can be found in the source/csharp/proxy/wpf folder.
SystemProxyWPF.cs
// ...

using Unigine;

namespace UnigineWPF
{
	// inherit from CustomSystemProxy
	public class SystemProxyWPF : CustomSystemProxy
	{
		// create a proxy instance
		private static readonly SystemProxyWPF instance = new SystemProxyWPF();

		public SystemProxyWPF() : base(
			(int)SYSTEM_PROXY_FEATURES.WINDOWS |
			(int)SYSTEM_PROXY_FEATURES.MOUSE |
			(int)SYSTEM_PROXY_FEATURES.KEYBOARD
			)
		{
			...
		}

		public static SystemProxyWPF Instance => instance;
		public delegate void ExternalRenderDelegate(IntPtr hwnd);
		public event ExternalRenderDelegate onExternalRender;

		public override void onExternalWindowRender(IntPtr hwnd)
		{
			onExternalRender?.Invoke(hwnd);
		}

		// main thread
		protected override bool isEngineActive() { return true; }

		protected override void mainUpdate() {}
		
		// windows
		protected override void createWindow(int width, int height, out WIN_HANDLE out_handle)
		{
			// implementation
		}

		protected override void removeWindow(WIN_HANDLE win_handle)
		{
			// implementation
		}
		protected override void setWindowTitle(WIN_HANDLE win_handle, string title)
		{
			// implementation
		}

		// mouse
		protected override void setGlobalMousePosition(in ivec2 pos)
		{
			// implementation
		}
		protected override ivec2 getGlobalMousePosition()
		{
			// implementation
		}

		// ...

		// displays
		protected override int getDisplayDefaultSystemDPI()
		{
			return 96;
		}

		protected override int getNumDisplays()
		{
			return Screen.AllScreens.Length;
		}
		
		// other
		protected override bool hasClipboardText()
		{
			return System.Windows.Clipboard.ContainsData(System.Windows.DataFormats.Text);
		}
	}
}

Creating System Proxy#

When creating a system proxy, you can specify the features it will support: pass the required SYSTEM_PROXY_FEATURES.* variables to the constructor. For example:

SystemProxyWPF.cs
// create a proxy that can work with the mouse and keyboard and create windows
private SystemProxyWPF() : base(
		(int)SYSTEM_PROXY_FEATURES.WINDOWS |
		(int)SYSTEM_PROXY_FEATURES.MOUSE |
		(int)SYSTEM_PROXY_FEATURES.KEYBOARD
		)
{
	// implementation
}

You can check if the feature is supported by using the corresponding function. Also you can get the supported features via getFeatures().

Event Handling#

Information on data input or window interaction is passed to the engine by using events. There are:

When the events are created, they can be passed to the engine by using the invokeWindowEvent() and invokeInputEvent() methods of the CustomSystemProxy class.

Notice
The events are processed for each window separately.
MainWindow.cs
public partial class MainWindow : Window
{
	// window handle
	private IntPtr windowHandle = IntPtr.Zero;
	private ulong WinId => (ulong)windowHandle;
	
	// mouse position
	private ivec2 MousePos => new ivec2(System.Windows.Forms.Cursor.Position.X, System.Windows.Forms.Cursor.Position.Y);
	// timestamp
	private ulong Timestamp => (ulong)(DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime()).Milliseconds;
	
	// window size
	private ivec2 Size
	{
		get
		{
			PresentationSource source = PresentationSource.FromVisual(this);
			Point screenSize = source.CompositionTarget.TransformToDevice.Transform(new Point(UnigineForm.Width, UnigineForm.Height));
			return new ivec2(screenSize.X, screenSize.Y);
		}
	}
	
	// window position
	private ivec2 Pos
	{
		get
		{
			Point pos = PointToScreen(new Point(0, 0));
			return new ivec2(pos.X, pos.Y);
		}
	}

	// ...

	protected override void OnActivated(EventArgs e)
	{
		if (windowHandle == IntPtr.Zero)
		{
			var wih = new WindowInteropHelper(this);
			windowHandle = wih.Handle;

			windowHandle = UnigineForm.Handle;
		}

		// create the window events and convey them to WindowManager
		SystemProxyWPF.Instance.invokeWindowEvent(new WindowEventGeneric(Timestamp, WinId, MousePos, Pos, Size, WindowEventGeneric.ACTION.SHOWN));
		SystemProxyWPF.Instance.invokeWindowEvent(new WindowEventGeneric(Timestamp, WinId, MousePos, Pos, Size, WindowEventGeneric.ACTION.FOCUS_GAINED));
	}

	protected override void OnRenderSizeChanged(SizeChangedInfo info)
	{
		// create the event that is raised on window resizing and convey it to WindowManager
		SystemProxyWPF.Instance.invokeWindowEvent(new WindowEventGeneric(Timestamp, WinId, MousePos, Pos, Size, WindowEventGeneric.ACTION.RESIZED));
	}

	protected override void OnLocationChanged(EventArgs e)
	{
		// create the event that is raised on window moving and convey it to WindowManager
		SystemProxyWPF.Instance.invokeWindowEvent(new WindowEventGeneric(Timestamp, WinId, MousePos, Pos, Size, WindowEventGeneric.ACTION.MOVED));
	}

	// ...

	protected override void OnKeyUp(KeyEventArgs e)
	{
		Input.KEY key = SystemProxyWPF.Instance.ConvertKey(e.Key);
		// create the input event that is raised on releasing the keyboard button and convey it to WindowManage
		SystemProxyWPF.Instance.invokeInputEvent(new InputEventKeyboard(Timestamp, MousePos, InputEventKeyboard.ACTION.UP, key););
	}

	// ...
}

Rendering to External Window#

UNIGINE allows registering and then using any external window for rendering by using the following methods:

  • initExternalWindowBuffers() initializes the required resources in the engine for rendering to the external window. The function always returns true.
  • resizeExternalWindowBuffers() passes to the engine new window dimensions after its resizing, so that the engine can update rendering resources.
  • shutdownExternalWindowBuffers() deletes all resources used for rendering to the external window upon closing the window (or when rendering to the window is no longer required).

Rendering to the external window is performed by using the following virtual methods:

  • needRenderExternalWindow() checks rendering of the external window. If the window is minimized, occluded by other windows and so on, you can pass this information to the engine (for example, to stop rendering).
  • onExternalWindowRender() — a callback function, which is called on rendering of the external window. It receives the window handle, and you can render to the window at this point.
Notice
As these methods are virtual, you will need to override them.
ExternalWindow.cs
// create a class for the external window
public partial class ExternalWindow : Window
{
	private IntPtr hwnd = IntPtr.Zero;
	private Viewport viewport = null;
	private Camera camera = null;

	public ExternalWindow()
	{
		InitializeComponent();
	}

	protected override void OnActivated(EventArgs e)
	{
		if (hwnd != IntPtr.Zero)
			return;
		
		// get a window handle
		var wih = new WindowInteropHelper(this);
		hwnd = wih.Handle;
		
		// create a viewport for the external window
		viewport = new Viewport();
		// create a camera for the external window
		camera = new Camera();

		Size size = Grid.RenderSize;
		// initialize resources for rendering to the external window
		UnigineWPF.SystemProxyWPF.Instance.initExternalWindowBuffers(hwnd, new ivec2(size.Width, size.Height));
		// subscribe to the onExternalRender event
		UnigineWPF.SystemProxyWPF.Instance.onExternalRender += OnExternalRender;
	}

	protected override void OnClosing(CancelEventArgs e)
	{
		UnigineWPF.SystemProxyWPF.Instance.onExternalRender -= OnExternalRender;

		UnigineWPF.SystemProxyWPF.Instance.shutdownExternalWindowBuffers(hwnd);
	}

	protected override void OnRenderSizeChanged(SizeChangedInfo info)
	{
		UnigineWPF.SystemProxyWPF.Instance.resizeExternalWindowBuffers(hwnd, new ivec2(Grid.RenderSize.Width, Grid.RenderSize.Height));
	}

	// implement logic that will be executed on external window rendering
	private void OnExternalRender(IntPtr externalHwnd)
	{
		if (externalHwnd != hwnd)
			return;

		RenderState.SaveState();
		RenderState.ClearStates();

		RenderState.SetViewport(0, 0, (int)Grid.RenderSize.Width, (int)Grid.RenderSize.Height);
		RenderState.ClearBuffer(Render.ClearBufferMask, vec4.ZERO);

		viewport.RenderEngine(camera);

		RenderState.RestoreState();
	}
}
Last update: 14.12.2022
Build: ()