This sample is based on the XPLM300 window sample. It demonstrates the use of the modelview, viewport, and projection matrix datarefs (new to X-Plane 11.10) for applying scissoring to new-style plugin-created windows.

If you build the plugin, but comment out the call the glScissor(), you’ll see the fill pattern goes all the way to the edge of the green rectangle.

#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMDataAccess.h"
#include <string.h>
#include <stdio.h>
#if IBM
	#include <windows.h>
#endif
#if LIN
	#include <GL/gl.h>
#elif __GNUC__
	#include <OpenGL/gl.h>
	#include <OpenGL/glu.h>
#else
	#include <GL/gl.h>
#endif

#ifndef XPLM300
	#error This is made to be compiled against the XPLM300 SDK
#endif

static XPLMWindowID	g_window;

void				draw(XPLMWindowID in_window_id, void * in_refcon);

int					dummy_mouse_handler(XPLMWindowID in_window_id, int x, int y, int is_down, void * in_refcon) { return 0; }
XPLMCursorStatus	dummy_cursor_status_handler(XPLMWindowID in_window_id, int x, int y, void * in_refcon) { return xplm_CursorDefault; }
int					dummy_wheel_handler(XPLMWindowID in_window_id, int x, int y, int wheel, int clicks, void * in_refcon) { return 0; }
void				dummy_key_handler(XPLMWindowID in_window_id, char key, XPLMKeyFlags flags, char virtual_key, void * in_refcon, int losing_focus) { }

PLUGIN_API int XPluginStart(
							char *		outName,
							char *		outSig,
							char *		outDesc)
{
	strcpy(outName, "ScissorsSamplePlugin");
	strcpy(outSig, "xpsdk.examples.scissorsampleplugin");
	strcpy(outDesc, "A test plug-in that demonstrates using GL scissors within an X-Plane 11 GUI window.");
	
	// We're not guaranteed that the main monitor's lower left is at (0, 0)... we'll need to query for the global desktop bounds!
	int global_desktop_bounds[4]; // left, bottom, right, top
	XPLMGetScreenBoundsGlobal(&global_desktop_bounds[0], &global_desktop_bounds[3], &global_desktop_bounds[2], &global_desktop_bounds[1]);
	
	XPLMCreateWindow_t params;
	params.structSize = sizeof(params);
	params.left = global_desktop_bounds[0] + 50;
	params.bottom = global_desktop_bounds[1] + 150;
	params.right = global_desktop_bounds[0] + 350;
	params.top = global_desktop_bounds[1] + 450;
	params.visible = 1;
	params.drawWindowFunc = draw;
	params.handleMouseClickFunc = dummy_mouse_handler;
	params.handleRightClickFunc = dummy_mouse_handler;
	params.handleMouseWheelFunc = dummy_wheel_handler;
	params.handleKeyFunc = dummy_key_handler;
	params.handleCursorFunc = dummy_cursor_status_handler;
	params.refcon = NULL;
	params.layer = xplm_WindowLayerFloatingWindows;
	params.decorateAsFloatingWindow = 1;
	
	g_window = XPLMCreateWindowEx(&params);
	XPLMSetWindowTitle(g_window, "Sample Window");
	return (g_window != NULL);
}

PLUGIN_API void	XPluginStop(void)
{
	// Since we created the window, we'll be good citizens and clean it up
	XPLMDestroyWindow(g_window);
	g_window = NULL;
}

PLUGIN_API void XPluginDisable(void) { }
PLUGIN_API int  XPluginEnable(void)  { return 1; }
PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam) { }

static void multMatrixVec4f(GLfloat dst[4], const GLfloat m[16], const GLfloat v[4])
{
	dst[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
	dst[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
	dst[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
	dst[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
}

static void modelview_to_window_coords(int out_w[2], const GLfloat in_mv[4], const GLfloat mv[16], const GLfloat pr[16], const GLint viewport[4])
{
	GLfloat eye[4], ndc[4];
	multMatrixVec4f(eye, mv, in_mv);
	multMatrixVec4f(ndc, pr, eye);
	ndc[3] = 1.0f / ndc[3];
	ndc[0] *= ndc[3];
	ndc[1] *= ndc[3];
	
	out_w[0] = (ndc[0] * 0.5f + 0.5f) * viewport[2] + viewport[0];
	out_w[1] = (ndc[1] * 0.5f + 0.5f) * viewport[3] + viewport[1];
}

void draw(XPLMWindowID in_window_id, void * in_refcon)
{
	XPLMSetGraphicsState(
						 0 /* no fog */,
						 0 /* 0 texture units */,
						 0 /* no lighting */,
						 0 /* no alpha testing */,
						 1 /* do alpha blend */,
						 1 /* do depth testing */,
						 0 /* no depth writing */
						 );

	int l, t, r, b;
	XPLMGetWindowGeometry(in_window_id, &l, &t, &r, &b);
	
	// First, draw a green box at the edges of the window to illustrate the *full* bounds.
	glColor4f(0, 1, 0, 1);
	glBegin(GL_LINE_LOOP);
	{
		glVertex2f(l, b);
		glVertex2f(l, t);
		glVertex2f(r, t);
		glVertex2f(r, b);
	}
	glEnd();
	
	static XPLMDataRef mv_dref = XPLMFindDataRef("sim/graphics/view/modelview_matrix");
	static XPLMDataRef vp_dref = XPLMFindDataRef("sim/graphics/view/viewport");
	static XPLMDataRef pr_dref = XPLMFindDataRef("sim/graphics/view/projection_matrix");
	
	// Get the current modelview matrix, viewport, and projection matrix from X-Plane
	float mv[16], pr[16];
	int vp[4];
	XPLMGetDatavf(mv_dref, mv, 0, 16);
	XPLMGetDatavf(pr_dref, pr, 0, 16);
	XPLMGetDatavi(vp_dref, vp, 0, 4);
	
	// Our new modelview bounds: we'll bring the window in by 10 bx on all sides
	GLfloat top_right_modelview[4] = { r - 10, t - 10, 0, 1 };
	GLfloat btm_left_modelview[4] =  { l + 10, b + 10, 0, 1 };
	
	// Get our top-right and bottom-left window coordinates
	int top_right_window[2], btm_left_window[2];
	modelview_to_window_coords(top_right_window, top_right_modelview, mv, pr, vp);
	modelview_to_window_coords(btm_left_window,  btm_left_modelview,  mv, pr, vp);
	
	glEnable(GL_SCISSOR_TEST);
	glScissor(btm_left_window[0], btm_left_window[1], top_right_window[0] - btm_left_window[0], top_right_window[1] - btm_left_window[1]);
	{
		// Draw a fill pattern
		// If the scissors were disabled, this would fill the entire window, not just a subsection.
		glColor4f(1, 1, 1, 0.2);
		int x, y;
		glBegin(GL_LINES);
		for(x = l; x < r; x += 3)
		{
			glVertex2f(x, b);
			glVertex2f(x, t);
		}
		for(y = b; y < t; y += 3)
		{
			glVertex2f(l, y);
			glVertex2f(r, y);
		}
		glEnd();
	}
	glDisable(GL_SCISSOR_TEST);
}

4 comments on “SDK 3.0 Window Scissoring Sample

    1. That sounds normal! On a 2-D monitor (i.e., not in VR), drawing in 2-D, the scaling to the display is all in the projection matrix, so the modelview matrix will be identity. If you launch in VR, you’ll get a different modelview matrix.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please do not report bugs in the blog comments.
Only bugs reported via the X-Plane Bug Reporter are tracked.