[Scummvm-git-logs] scummvm master -> 6a591146c8fc5605423f00f1db573f06245b8c8b

aquadran noreply at scummvm.org
Thu Dec 9 20:11:00 UTC 2021


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
6a591146c8 TINYGL: Added stencil buffer implementation


Commit: 6a591146c8fc5605423f00f1db573f06245b8c8b
    https://github.com/scummvm/scummvm/commit/6a591146c8fc5605423f00f1db573f06245b8c8b
Author: Paweł Kołodziejski (aquadran at gmail.com)
Date: 2021-12-09T21:10:53+01:00

Commit Message:
TINYGL: Added stencil buffer implementation

Changed paths:
    engines/grim/gfx_tinygl.cpp
    engines/myst3/gfx_tinygl.cpp
    engines/playground3d/gfx_tinygl.cpp
    engines/stark/gfx/tinygl.cpp
    engines/stark/gfx/tinyglactor.cpp
    graphics/tinygl/Changelog
    graphics/tinygl/api.cpp
    graphics/tinygl/clear.cpp
    graphics/tinygl/clip.cpp
    graphics/tinygl/get.cpp
    graphics/tinygl/gl.h
    graphics/tinygl/init.cpp
    graphics/tinygl/misc.cpp
    graphics/tinygl/opinfo.h
    graphics/tinygl/tinygl.h
    graphics/tinygl/zblit.cpp
    graphics/tinygl/zbuffer.cpp
    graphics/tinygl/zbuffer.h
    graphics/tinygl/zdirtyrect.cpp
    graphics/tinygl/zdirtyrect.h
    graphics/tinygl/zgl.h
    graphics/tinygl/ztriangle.cpp


diff --git a/engines/grim/gfx_tinygl.cpp b/engines/grim/gfx_tinygl.cpp
index 8ddea296ca..61a5c758f3 100644
--- a/engines/grim/gfx_tinygl.cpp
+++ b/engines/grim/gfx_tinygl.cpp
@@ -85,7 +85,7 @@ void GfxTinyGL::setupScreen(int screenW, int screenH) {
 
 	_pixelFormat = g_system->getScreenFormat();
 	debug("INFO: TinyGL front buffer pixel format: %s", _pixelFormat.toString().c_str());
-	TinyGL::createContext(screenW, screenH, _pixelFormat, 256, ConfMan.getBool("dirtyrects"));
+	TinyGL::createContext(screenW, screenH, _pixelFormat, 256, g_grim->getGameType() == GType_GRIM, ConfMan.getBool("dirtyrects"));
 
 	_storedDisplay = new Graphics::Surface;
 	_storedDisplay->create(_gameWidth, _gameHeight, _pixelFormat);
diff --git a/engines/myst3/gfx_tinygl.cpp b/engines/myst3/gfx_tinygl.cpp
index dfa6e51d6a..0cba042349 100644
--- a/engines/myst3/gfx_tinygl.cpp
+++ b/engines/myst3/gfx_tinygl.cpp
@@ -62,7 +62,7 @@ void TinyGLRenderer::init() {
 
 	computeScreenViewport();
 
-	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, ConfMan.getBool("dirtyrects"));
+	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, false, ConfMan.getBool("dirtyrects"));
 
 	tglMatrixMode(TGL_PROJECTION);
 	tglLoadIdentity();
diff --git a/engines/playground3d/gfx_tinygl.cpp b/engines/playground3d/gfx_tinygl.cpp
index 002231dd17..42ae820f64 100644
--- a/engines/playground3d/gfx_tinygl.cpp
+++ b/engines/playground3d/gfx_tinygl.cpp
@@ -88,7 +88,7 @@ void TinyGLRenderer::init() {
 
 	computeScreenViewport();
 
-	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, ConfMan.getBool("dirtyrects"));
+	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, true, ConfMan.getBool("dirtyrects"));
 
 	tglMatrixMode(TGL_PROJECTION);
 	tglLoadIdentity();
diff --git a/engines/stark/gfx/tinygl.cpp b/engines/stark/gfx/tinygl.cpp
index 242ed794e4..d854a5d3dd 100644
--- a/engines/stark/gfx/tinygl.cpp
+++ b/engines/stark/gfx/tinygl.cpp
@@ -51,7 +51,7 @@ TinyGLDriver::~TinyGLDriver() {
 void TinyGLDriver::init() {
 	computeScreenViewport();
 
-	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, ConfMan.getBool("dirtyrects"));
+	TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, true, ConfMan.getBool("dirtyrects"));
 
 	tglMatrixMode(TGL_PROJECTION);
 	tglLoadIdentity();
@@ -89,7 +89,7 @@ void TinyGLDriver::setViewport(const Common::Rect &rect) {
 }
 
 void TinyGLDriver::clearScreen() {
-	tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT);
+	tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT | TGL_STENCIL_BUFFER_BIT);
 }
 
 void TinyGLDriver::flipBuffer() {
@@ -159,8 +159,8 @@ void TinyGLDriver::set3DMode() {
 
 	// Stencil test are only used in rendering shadows
 	// They are manually enabled and disabled there
-	//tglStencilFunc(TGL_EQUAL, 0, 0xFF);
-	//tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_INCR);
+	tglStencilFunc(TGL_EQUAL, 0, 0xFF);
+	tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_INCR);
 }
 
 bool TinyGLDriver::computeLightsEnabled() {
diff --git a/engines/stark/gfx/tinyglactor.cpp b/engines/stark/gfx/tinyglactor.cpp
index 994c31d1e8..82763035b2 100644
--- a/engines/stark/gfx/tinyglactor.cpp
+++ b/engines/stark/gfx/tinyglactor.cpp
@@ -256,7 +256,7 @@ void TinyGLActorRenderer::render(const Math::Vector3d &position, float direction
 
 	if (drawShadow) {
 		tglEnable(TGL_BLEND);
-		//tglEnable(TGL_STENCIL_TEST);
+		tglEnable(TGL_STENCIL_TEST);
 		tglDisable(TGL_TEXTURE_2D);
 
 		tglColor4f(0.0f, 0.0f, 0.0f, 0.5f);
@@ -273,7 +273,7 @@ void TinyGLActorRenderer::render(const Math::Vector3d &position, float direction
 
 		tglEnable(TGL_TEXTURE_2D);
 		tglDisable(TGL_BLEND);
-		//tglDisable(TGL_STENCIL_TEST);
+		tglDisable(TGL_STENCIL_TEST);
 	}
 }
 
diff --git a/graphics/tinygl/Changelog b/graphics/tinygl/Changelog
index c39c8fe895..42d87ebf81 100644
--- a/graphics/tinygl/Changelog
+++ b/graphics/tinygl/Changelog
@@ -16,11 +16,11 @@ The changes made from the original version of TinyGL 0.4 are:
 * Added additional functions missing, like glColor4ub. (To make the code similar with the GL-code we use)
 * Added simplistic glColorMask implementation, on/off.
 * Applied some C++-isms:
-	* "for(int i = 0;" instead of "int i; for (i = 0;",
-	* struct structName instead of typedef struct {} structName;
-	* pass-by-const-reference instead of by-value when possible
+    - "for(int i = 0;" instead of "int i; for (i = 0;",
+    - struct structName instead of typedef struct {} structName;
+    - pass-by-const-reference instead of by-value when possible
 * Changed the math-functions to use const-pointers if possible.
-* Reformatted the source to use the same code-formatting conventions as the rest of ResidualVM
+* Reformatted the source to use the same code-formatting conventions as the rest of ScummVM
   (indentation-wise, not variable-naming wise)
 * Refactored all the maths code in a C++ fashion, removed some unused functions.
 * Heavily refactored the triangle and line drawing routines
@@ -31,6 +31,7 @@ The changes made from the original version of TinyGL 0.4 are:
 * Added an API that enables the user to perform color and z buffer blitting.
 * Implemented a system that enables to defer draw calls.
 * Implemented dirty rectangle system that prevents redrawing of unchanged region of the screen.
-* Added implementation of tglDrawElements
+* Added implementation of tglDrawElements.
+* Added stencil buffer implementation.
 
 For more information refer to log changes in github: https://github.com/scummvm/scummvm
diff --git a/graphics/tinygl/api.cpp b/graphics/tinygl/api.cpp
index 019adf5981..fcda2d32a1 100644
--- a/graphics/tinygl/api.cpp
+++ b/graphics/tinygl/api.cpp
@@ -199,6 +199,15 @@ void tglDepthMask(int enableWrite) {
 	c->gl_add_op(p);
 }
 
+void tglStencilMask(TGLuint mask) {
+	TinyGL::GLContext *c = TinyGL::gl_get_context();
+	TinyGL::GLParam p[2];
+	p[0].op = TinyGL::OP_StencilMask;
+	p[1].i = mask;
+
+	c->gl_add_op(p);
+}
+
 void tglBlendFunc(TGLenum sfactor, TGLenum dfactor) {
 	TinyGL::GLContext *c = TinyGL::gl_get_context();
 	TinyGL::GLParam p[3];
@@ -230,6 +239,28 @@ void tglDepthFunc(TGLenum func) {
 	c->gl_add_op(p);
 }
 
+void tglStencilFunc(TGLenum func, TGLint ref, TGLuint mask) {
+	TinyGL::GLContext *c = TinyGL::gl_get_context();
+	TinyGL::GLParam p[4];
+	p[0].op = TinyGL::OP_StencilFunc;
+	p[1].i = func;
+	p[2].i = ref;
+	p[3].i = mask;
+
+	c->gl_add_op(p);
+}
+
+void tglStencilOp(TGLenum sfail, TGLenum dpfail, TGLenum dppass) {
+	TinyGL::GLContext *c = TinyGL::gl_get_context();
+	TinyGL::GLParam p[4];
+	p[0].op = TinyGL::OP_StencilOp;
+	p[1].i = sfail;
+	p[2].i = dpfail;
+	p[3].i = dppass;
+
+	c->gl_add_op(p);
+}
+
 void tglPolygonMode(int face, int mode) {
 	TinyGL::GLContext *c = TinyGL::gl_get_context();
 	TinyGL::GLParam p[3];
@@ -569,6 +600,16 @@ void tglClearDepth(double depth) {
 	c->gl_add_op(p);
 }
 
+void tglClearStencil(TGLint s) {
+	TinyGL::GLContext *c = TinyGL::gl_get_context();
+	TinyGL::GLParam p[2];
+
+	p[0].op = TinyGL::OP_ClearStencil;
+	p[1].i = s;
+
+	c->gl_add_op(p);
+}
+
 // textures
 
 void tglTexImage2D(int target, int level, int components, int width, int height, int border, int format, int type, void *pixels) {
diff --git a/graphics/tinygl/clear.cpp b/graphics/tinygl/clear.cpp
index 3b91852ed9..7751d8992a 100644
--- a/graphics/tinygl/clear.cpp
+++ b/graphics/tinygl/clear.cpp
@@ -39,14 +39,21 @@ void GLContext::glopClearDepth(GLParam *p) {
 	clear_depth = p[1].f;
 }
 
+void GLContext::glopClearStencil(GLParam *p) {
+	clear_stencil = p[1].i & 0xFF;
+}
+
 void GLContext::glopClear(GLParam *p) {
 	int mask = p[1].i;
 	int z = (int)(clear_depth * ((1 << ZB_Z_BITS) - 1));
 	int r = (int)(clear_color.X * 255);
 	int g = (int)(clear_color.Y * 255);
 	int b = (int)(clear_color.Z * 255);
+	int s = (int)(clear_stencil);
 
-	issueDrawCall(new ClearBufferDrawCall(mask & TGL_DEPTH_BUFFER_BIT, z, mask & TGL_COLOR_BUFFER_BIT, r, g, b));
+	issueDrawCall(new ClearBufferDrawCall(mask & TGL_DEPTH_BUFFER_BIT, z,
+										  mask & TGL_COLOR_BUFFER_BIT, r, g, b,
+										  mask & TGL_STENCIL_BUFFER_BIT, s));
 }
 
 } // end of namespace TinyGL
diff --git a/graphics/tinygl/clip.cpp b/graphics/tinygl/clip.cpp
index 1db02fc7cf..e3e7dd9ba6 100644
--- a/graphics/tinygl/clip.cpp
+++ b/graphics/tinygl/clip.cpp
@@ -141,7 +141,7 @@ void GLContext::gl_draw_line(GLVertex *p1, GLVertex *p2) {
 		if (render_mode == TGL_SELECT) {
 			gl_add_select1(p1->zp.z, p2->zp.z, p2->zp.z);
 		} else {
-			if (depth_test)
+			if (depth_test_enabled)
 				fb->fillLineZ(&p1->zp, &p2->zp);
 			else
 				fb->fillLine(&p1->zp, &p2->zp);
@@ -171,7 +171,7 @@ void GLContext::gl_draw_line(GLVertex *p1, GLVertex *p2) {
 			gl_transform_to_viewport(&q1);
 			gl_transform_to_viewport(&q2);
 
-			if (depth_test)
+			if (depth_test_enabled)
 				fb->fillLineZ(&q1.zp, &q2.zp);
 			else
 				fb->fillLine(&q1.zp, &q2.zp);
@@ -425,7 +425,7 @@ void GLContext::gl_draw_triangle_fill(GLContext *c, GLVertex *p0, GLVertex *p1,
 // Render a clipped triangle in line mode
 
 void GLContext::gl_draw_triangle_line(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2) {
-	if (c->depth_test) {
+	if (c->depth_test_enabled) {
 		if (p0->edge_flag)
 			c->fb->fillLineZ(&p0->zp, &p1->zp);
 		if (p1->edge_flag)
diff --git a/graphics/tinygl/get.cpp b/graphics/tinygl/get.cpp
index 6fd3c24c07..38222fd3c5 100644
--- a/graphics/tinygl/get.cpp
+++ b/graphics/tinygl/get.cpp
@@ -62,6 +62,12 @@ void tglGetIntegerv(int pname, int *params) {
 	case TGL_ALPHA_TEST:
 		*params = c->alpha_test_enabled;
 		break;
+	case TGL_DEPTH_TEST:
+		*params = c->depth_test_enabled;
+		break;
+	case TGL_STENCIL_TEST:
+		*params = c->stencil_test_enabled;
+		break;
 	default:
 		error("tglGet: option not implemented");
 		break;
diff --git a/graphics/tinygl/gl.h b/graphics/tinygl/gl.h
index 0c6214fc8f..f1db34b9cd 100644
--- a/graphics/tinygl/gl.h
+++ b/graphics/tinygl/gl.h
@@ -328,6 +328,8 @@ enum {
 	TGL_REPLACE                     = 0x1E01,
 	TGL_INCR                        = 0x1E02,
 	TGL_DECR                        = 0x1E03,
+	TGL_INCR_WRAP                   = 0x8507,
+	TGL_DECR_WRAP                   = 0x8508,
 
 	// Buffers, Pixel Drawing/Reading
 	TGL_NONE                        = 0,
@@ -790,6 +792,12 @@ void tglCallList(unsigned int list);
 void tglClear(int mask);
 void tglClearColor(float r, float g, float b, float a);
 void tglClearDepth(double depth);
+void tglClearStencil(TGLint s);
+
+// stencil buffer
+void tglStencilFunc(TGLenum func, TGLint ref, TGLuint mask);
+void tglStencilOp(TGLenum sfail, TGLenum dpfail, TGLenum dppass);
+void tglStencilMask(TGLuint mask);
 
 // selection
 int tglRenderMode(int mode);
diff --git a/graphics/tinygl/init.cpp b/graphics/tinygl/init.cpp
index 5a92844634..573b7f7900 100644
--- a/graphics/tinygl/init.cpp
+++ b/graphics/tinygl/init.cpp
@@ -55,19 +55,18 @@ void GLContext::endSharedState() {
 	gl_free(s->texture_hash_table);
 }
 
-void createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool dirtyRectsEnable) {
+void createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool enableStencilBuffer, bool dirtyRectsEnable) {
 	assert(gl_ctx == nullptr);
-	GLContext *c = new GLContext();
-	gl_ctx = c;
-	c->init(screenW, screenH, pixelFormat, textureSize, dirtyRectsEnable);
+	gl_ctx = new GLContext();
+	gl_ctx->init(screenW, screenH, pixelFormat, textureSize, enableStencilBuffer, dirtyRectsEnable);
 }
 
-void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool dirtyRectsEnable) {
+void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool enableStencilBuffer, bool dirtyRectsEnable) {
 	GLViewport *v;
 
 	_enableDirtyRectangles = dirtyRectsEnable;
 
-	fb = new TinyGL::FrameBuffer(screenW, screenH, pixelFormat);
+	fb = new TinyGL::FrameBuffer(screenW, screenH, pixelFormat, enableStencilBuffer);
 	renderRect = Common::Rect(0, 0, screenW, screenH);
 
 	if ((textureSize & (textureSize - 1)))
@@ -169,20 +168,37 @@ void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat
 	// clear
 	clear_color = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
 	clear_depth = 1.0f;
+	clear_stencil = 0;
 
 	// selection
 	render_mode = TGL_RENDER;
-	select_buffer = NULL;
+	select_buffer = nullptr;
 	name_stack_size = 0;
 
 	// blending
 	blending_enabled = false;
+	source_blending_factor = TGL_ONE;
+	destination_blending_factor = TGL_ZERO;
 
 	// alpha test
 	alpha_test_enabled = false;
+	alpha_test_func = TGL_ALWAYS;
+	alpha_test_ref_val = 0;
 
 	// depth test
-	depth_test = false;
+	depth_test_enabled = false;
+	depth_func = TGL_LESS;
+	depth_write_mask = true;
+
+	// stencil
+	stencil_test_enabled = false;
+	stencil_test_func = TGL_ALWAYS;
+	stencil_ref_val = 0;
+	stencil_mask = 0xff;
+	stencil_write_mask = 0xff;
+	stencil_sfail = TGL_KEEP;
+	stencil_dpfail = TGL_KEEP;
+	stencil_dppass = TGL_KEEP;
 
 	// matrix
 	matrix_mode = 0;
@@ -203,12 +219,6 @@ void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat
 	tglMatrixMode(TGL_MODELVIEW);
 	tglLoadIdentity();
 
-	tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
-
-	tglAlphaFunc(TGL_ALWAYS, 0.f);
-
-	tglDepthFunc(TGL_LESS);
-
 	matrix_model_projection_updated = 1;
 
 	// opengl 1.1 arrays
@@ -223,10 +233,10 @@ void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat
 	shadow_mode = 0;
 
 	// clear the resize callback function pointer
-	gl_resize_viewport = NULL;
+	gl_resize_viewport = nullptr;
 
 	// specular buffer
-	specbuf_first = NULL;
+	specbuf_first = nullptr;
 	specbuf_used_counter = 0;
 	specbuf_num_buffers = 0;
 
@@ -237,7 +247,6 @@ void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat
 	_currentAllocatorIndex = 0;
 	_drawCallAllocator[0].initialize(kDrawCallMemory);
 	_drawCallAllocator[1].initialize(kDrawCallMemory);
-	_enableDirtyRectangles = true;
 	_debugRectsEnabled = false;
 
 	TinyGL::Internal::tglBlitResetScissorRect();
diff --git a/graphics/tinygl/misc.cpp b/graphics/tinygl/misc.cpp
index fca6758b55..fa27aa5007 100644
--- a/graphics/tinygl/misc.cpp
+++ b/graphics/tinygl/misc.cpp
@@ -86,11 +86,14 @@ void GLContext::glopEnableDisable(GLParam *p) {
 		normalize_enabled = v;
 		break;
 	case TGL_DEPTH_TEST:
-		depth_test = v;
+		depth_test_enabled = v;
 		break;
 	case TGL_ALPHA_TEST:
 		alpha_test_enabled = v;
 		break;
+	case TGL_STENCIL_TEST:
+		stencil_test_enabled = v;
+		break;
 	case TGL_BLEND:
 		blending_enabled = v;
 		break;
@@ -148,6 +151,27 @@ void GLContext::glopDepthFunc(GLParam *p) {
 	depth_func = p[1].i;
 }
 
+void GLContext::glopStencilFunc(GLParam *p) {
+	TGLenum func = p[1].i;
+	TGLint ref = p[2].i;
+	TGLuint mask = p[3].i;
+	if (func < TGL_NEVER || func > TGL_ALWAYS)
+		return;
+	if (ref < 0)
+		ref = 0;
+	else if (ref > 255)
+		ref = 255;
+	stencil_test_func = func;
+	stencil_ref_val = ref;
+	stencil_mask = mask;
+}
+
+void GLContext::glopStencilOp(GLParam *p) {
+	stencil_sfail = p[1].i;
+	stencil_dpfail = p[2].i;
+	stencil_dppass = p[3].i;
+}
+
 void GLContext::glopShadeModel(GLParam *p) {
 	int code = p[1].i;
 	current_shade_model = code;
@@ -197,7 +221,11 @@ void GLContext::glopColorMask(GLParam *p) {
 }
 
 void GLContext::glopDepthMask(GLParam *p) {
-	depth_write = p[1].i;
+	depth_write_mask = p[1].i;
+}
+
+void GLContext::glopStencilMask(TinyGL::GLParam *p) {
+	stencil_write_mask = p[1].i;
 }
 
 } // end of namespace TinyGL
diff --git a/graphics/tinygl/opinfo.h b/graphics/tinygl/opinfo.h
index 137b5e6732..424f44a981 100644
--- a/graphics/tinygl/opinfo.h
+++ b/graphics/tinygl/opinfo.h
@@ -60,6 +60,7 @@ ADD_OP(LightModel, 5, "%C %f %f %f %f")
 ADD_OP(Clear, 1, "%d")
 ADD_OP(ClearColor, 4, "%f %f %f %f")
 ADD_OP(ClearDepth, 1, "%f")
+ADD_OP(ClearStencil, 1, "%d")
 
 ADD_OP(InitNames, 0, "")
 ADD_OP(PushName, 1, "%d")
@@ -78,9 +79,12 @@ ADD_OP(FrontFace, 1, "%C")
 ADD_OP(PolygonMode, 2, "%C %C")
 ADD_OP(ColorMask, 1, "%08x")
 ADD_OP(DepthMask, 1, "%d")
+ADD_OP(StencilMask, 1, "%d")
 ADD_OP(BlendFunc, 2, "%d %d")
 ADD_OP(AlphaFunc, 2, "%d %f")
 ADD_OP(DepthFunc, 1, "%d")
+ADD_OP(StencilFunc, 3, "%C %d %d")
+ADD_OP(StencilOp, 3, "%C %C %C")
 
 ADD_OP(CallList, 1, "%d")
 ADD_OP(Hint, 2, "%C %C")
diff --git a/graphics/tinygl/tinygl.h b/graphics/tinygl/tinygl.h
index b4103c883f..c22010fd7a 100644
--- a/graphics/tinygl/tinygl.h
+++ b/graphics/tinygl/tinygl.h
@@ -30,7 +30,7 @@
 
 namespace TinyGL {
 
-void createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool dirtyRectsEnable = true);
+void createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool enableStencilBuffer, bool dirtyRectsEnable = true);
 void destroyContext();
 void presentBuffer();
 void getSurfaceRef(Graphics::Surface &surface);
diff --git a/graphics/tinygl/zblit.cpp b/graphics/tinygl/zblit.cpp
index d29646679a..30d6fe99a7 100644
--- a/graphics/tinygl/zblit.cpp
+++ b/graphics/tinygl/zblit.cpp
@@ -200,7 +200,7 @@ public:
 		int fbWidth = c->fb->getPixelBufferWidth();
 
 		Graphics::PixelBuffer srcBuf(_surface.format, (byte *)const_cast<void *>(_surface.getPixels())); // Blit image buffer
-		Graphics::PixelBuffer dstBuf(_surface.format, (byte *)c->fb->getZBuffer()); // TinyGL z buffer
+		Graphics::PixelBuffer dstBuf(_surface.format, (byte *)const_cast<uint *>(c->fb->getZBuffer())); // TinyGL z buffer
 
 		srcBuf.shiftBy(srcY * _surface.w);
 
diff --git a/graphics/tinygl/zbuffer.cpp b/graphics/tinygl/zbuffer.cpp
index 55fca6688a..83ad77037b 100644
--- a/graphics/tinygl/zbuffer.cpp
+++ b/graphics/tinygl/zbuffer.cpp
@@ -80,17 +80,20 @@ static void memset_l(void *adr, int val, int count) {
 		*p++ = val;
 }
 
-FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelFormat &format) {
+FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelFormat &format, bool enableStencilBuffer) {
 	_pbufWidth = width;
 	_pbufHeight = height;
 	_pbufFormat = format;
 	_pbufBpp = _pbufFormat.bytesPerPixel;
 	_pbufPitch = (_pbufWidth * _pbufBpp + 3) & ~3;
 
-	_offscreenBuffer.zbuf = _zbuf = (uint *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(uint));
-
 	_pbuf.set(_pbufFormat, new byte[_pbufHeight * _pbufPitch]);
+	_zbuf = (uint *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(uint));
+	if (enableStencilBuffer)
+		_sbuf = (byte *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(byte));
+
 	_offscreenBuffer.pbuf = _pbuf.getRawBuffer();
+	_offscreenBuffer.zbuf = _zbuf;
 
 	_currentTexture = nullptr;
 }
@@ -98,12 +101,14 @@ FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelFormat &for
 FrameBuffer::~FrameBuffer() {
 	_pbuf.free();
 	gl_free(_zbuf);
+	if (_sbuf)
+		gl_free(_sbuf);
 }
 
 Buffer *FrameBuffer::genOffscreenBuffer() {
 	Buffer *buf = (Buffer *)gl_malloc(sizeof(Buffer));
-	buf->pbuf = (byte *)gl_malloc(_pbufHeight * _pbufPitch);
-	buf->zbuf = (unsigned int *)gl_malloc(_pbufWidth * _pbufHeight * sizeof(uint));
+	buf->pbuf = (byte *)gl_zalloc(_pbufHeight * _pbufPitch);
+	buf->zbuf = (uint *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(uint));
 	return buf;
 }
 
@@ -113,7 +118,8 @@ void FrameBuffer::delOffscreenBuffer(Buffer *buf) {
 	gl_free(buf);
 }
 
-void FrameBuffer::clear(int clearZ, int z, int clearColor, int r, int g, int b) {
+void FrameBuffer::clear(int clearZ, int z, int clearColor, int r, int g, int b,
+                        bool clearStencil, int stencilValue) {
 	if (clearZ) {
 		const uint8 *zc = (const uint8 *)&z;
 		unsigned int i;
@@ -149,9 +155,13 @@ void FrameBuffer::clear(int clearZ, int z, int clearColor, int r, int g, int b)
 			}
 		}
 	}
+	if (_sbuf && clearStencil) {
+		memset(_sbuf, stencilValue, _pbufWidth * _pbufHeight);
+	}
 }
 
-void FrameBuffer::clearRegion(int x, int y, int w, int h, int clearZ, int z, int clearColor, int r, int g, int b) {
+void FrameBuffer::clearRegion(int x, int y, int w, int h, bool clearZ, int z,
+                              bool clearColor, int r, int g, int b, bool clearStencil, int stencilValue) {
 	if (clearZ) {
 		int height = h;
 		unsigned int *zbuf = _zbuf + (y * _pbufWidth);
@@ -202,6 +212,13 @@ void FrameBuffer::clearRegion(int x, int y, int w, int h, int clearZ, int z, int
 			}
 		}
 	}
+	if (_sbuf && clearStencil) {
+		byte *pp = _sbuf + y * _pbufWidth + x;
+		for (int i = y; i < y + h; i++) {
+			memset(pp, stencilValue, w);
+			pp += _pbufWidth;
+		}
+	}
 }
 
 inline static void blitPixel(uint8 offset, unsigned int *from_z, unsigned int *to_z, unsigned int z_length, byte *from_color, byte *to_color, unsigned int color_length) {
diff --git a/graphics/tinygl/zbuffer.h b/graphics/tinygl/zbuffer.h
index c2dd9cc63c..370d05947a 100644
--- a/graphics/tinygl/zbuffer.h
+++ b/graphics/tinygl/zbuffer.h
@@ -78,7 +78,7 @@ static const int DRAW_SHADOW = 4;
 
 struct Buffer {
 	byte *pbuf;
-	unsigned int *zbuf;
+	uint *zbuf;
 	bool used;
 };
 
@@ -103,7 +103,7 @@ struct ZBufferPoint {
 };
 
 struct FrameBuffer {
-	FrameBuffer(int xsize, int ysize, const Graphics::PixelFormat &format);
+	FrameBuffer(int width, int height, const Graphics::PixelFormat &format, bool enableStencilBuffer);
 	~FrameBuffer();
 
 	Graphics::PixelFormat getPixelFormat() {
@@ -212,6 +212,72 @@ private:
 		return false;
 	}
 
+	FORCEINLINE bool stencilTest(byte sSrc) {
+		switch (_stencilTestFunc) {
+		case TGL_NEVER:
+			break;
+		case TGL_LESS:
+			if ((_stencilRefVal & _stencilMask) < (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_LEQUAL:
+			if ((_stencilRefVal & _stencilMask) <= (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_GREATER:
+			if ((_stencilRefVal & _stencilMask) > (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_GEQUAL:
+			if ((_stencilRefVal & _stencilMask) >= (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_EQUAL:
+			if ((_stencilRefVal & _stencilMask) == (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_NOTEQUAL:
+			if ((_stencilRefVal & _stencilMask) != (sSrc & _stencilMask))
+				return true;
+			break;
+		case TGL_ALWAYS:
+			return true;
+		}
+		return false;
+	}
+
+	FORCEINLINE void stencilOp(bool stencilTestResult, bool depthTestResult, byte *sDst) {
+		int op = !stencilTestResult ? _stencilSfail : !depthTestResult ? _stencilDpfail : _stencilDppass;
+		byte value = *sDst;
+		switch (op) {
+		case TGL_KEEP:
+			return;
+		case TGL_ZERO:
+			value = 0;
+			break;
+		case TGL_REPLACE:
+			value = _stencilRefVal;
+			break;
+		case TGL_INCR:
+			if (value < 255)
+				value++;
+			break;
+		case TGL_INCR_WRAP:
+			value++;
+			break;
+		case TGL_DECR:
+			if (value > 0)
+				value--;
+			break;
+		case TGL_DECR_WRAP:
+			value--;
+			break;
+		case TGL_INVERT:
+			value = ~value;
+		}
+		*sDst = value & _stencilWriteMask;
+	}
+
 	template <bool kEnableAlphaTest, bool kBlendingEnabled>
 	FORCEINLINE void writePixel(int pixel, int value) {
 		writePixel<kEnableAlphaTest, kBlendingEnabled, false>(pixel, value, 0);
@@ -240,25 +306,25 @@ private:
 		}
 	}
 
-	template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
-	FORCEINLINE void putPixelFlat(FrameBuffer *buffer, int buf, unsigned int *pz, int _a,
+	template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
+	FORCEINLINE void putPixelFlat(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a,
 								  int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a, int &dzdx);
 
-	template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
-	FORCEINLINE void putPixelSmooth(FrameBuffer *buffer, int buf, unsigned int *pz, int _a,
+	template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
+	FORCEINLINE void putPixelSmooth(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a,
 									int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
 									int &dzdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx);
 
-	template <bool kDepthWrite, bool kEnableScissor>
-	FORCEINLINE void putPixelDepth(FrameBuffer *buffer, int buf, unsigned int *pz, int _a, int x, int y, unsigned int &z, int &dzdx);
+	template <bool kDepthWrite, bool kEnableScissor, bool kStencilEnabled>
+	FORCEINLINE void putPixelDepth(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a, int x, int y, unsigned int &z, int &dzdx);
 
 	template <bool kDepthWrite, bool kAlphaTestEnabled, bool kEnableScissor, bool kBlendingEnabled>
 	FORCEINLINE void putPixelShadow(FrameBuffer *buffer, int buf, unsigned int *pz, int _a, int x, int y, unsigned int &z,
 									unsigned int &r, unsigned int &g, unsigned int &b, int &dzdx, unsigned char *pm);
 
-	template <bool kDepthWrite, bool kLightsMode, bool kSmoothMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
+	template <bool kDepthWrite, bool kLightsMode, bool kSmoothMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
 	FORCEINLINE void putPixelTextureMappingPerspective(FrameBuffer *buffer, int buf, const Graphics::TexelBuffer *texture,
-													   unsigned int wrap_s, unsigned int wrap_t, unsigned int *pz, int _a,
+													   unsigned int wrap_s, unsigned int wrap_t, unsigned int *pz, byte *ps, int _a,
 													   int x, int y, unsigned int &z, int &t, int &s,
 													   unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
 													   int &dzdx, int &dsdx, int &dtdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx);
@@ -420,8 +486,10 @@ private:
 
 public:
 
-	void clear(int clear_z, int z, int clear_color, int r, int g, int b);
-	void clearRegion(int x, int y, int w, int h,int clear_z, int z, int clear_color, int r, int g, int b);
+	void clear(int clear_z, int z, int clear_color, int r, int g, int b,
+	           bool clearStencil, int stencilValue);
+	void clearRegion(int x, int y, int w, int h, bool clearZ, int z,
+					 bool clearColor, int r, int g, int b, bool clearStencil, int stencilValue);
 
 	FORCEINLINE void setScissorRectangle(const Common::Rect &rect) {
 		_clipRectangle = rect;
@@ -472,6 +540,26 @@ public:
 		_depthWrite = enable;
 	}
 
+	FORCEINLINE void enableStencilTest(bool enable) {
+		_stencilTestEnabled = enable;
+	}
+
+	FORCEINLINE void setStencilWriteMask(uint stencilWriteMask) {
+		_stencilWriteMask = stencilWriteMask;
+	}
+
+	FORCEINLINE void setStencilTestFunc(int stencilFunc, int stencilValue, uint stencilMask) {
+		_stencilTestFunc = stencilFunc;
+		_stencilRefVal = stencilValue;
+		_stencilMask = stencilMask;
+	}
+
+	FORCEINLINE void setStencilOp(int stencilSfail, int stencilDpfail, int stencilDppass) {
+		_stencilSfail = stencilSfail;
+		_stencilDpfail = stencilDpfail;
+		_stencilDppass = stencilDppass;
+	}
+	
 	FORCEINLINE void setOffsetStates(int offsetStates) {
 		_offsetStates = offsetStates;
 	}
@@ -508,7 +596,10 @@ private:
 	void selectOffscreenBuffer(Buffer *buffer);
 	void clearOffscreenBuffer(Buffer *buffer);
 
-	template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawLogic, bool kDepthWrite, bool enableAlphaTest, bool kEnableScissor, bool enableBlending>
+	template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawLogic, bool kDepthWrite, bool enableAlphaTest, bool kEnableScissor, bool kBlendingEnabled, bool kStencilEnabled>
+	void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
+
+	template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawLogic, bool kDepthWrite, bool enableAlphaTest, bool kEnableScissor, bool kBlendingEnabled>
 	void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
 
 	template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawMode, bool kDepthWrite, bool enableAlphaTest, bool kEnableScissor>
@@ -560,9 +651,6 @@ private:
 	FORCEINLINE void drawLine(const ZBufferPoint *p1, const ZBufferPoint *p2);
 
 	Buffer _offscreenBuffer;
-
-	unsigned int *_zbuf;
-
 	Graphics::PixelBuffer _pbuf;
 	int _pbufWidth;
 	int _pbufHeight;
@@ -570,6 +658,10 @@ private:
 	Graphics::PixelFormat _pbufFormat;
 	int _pbufBpp;
 
+	uint *_zbuf;
+	byte *_sbuf;
+
+	bool _enableStencil;
 	int _textureSize;
 	int _textureSizeMask;
 
@@ -590,6 +682,14 @@ private:
 	int _alphaTestRefVal;
 	bool _depthTestEnabled;
 	bool _depthWrite;
+	bool _stencilTestEnabled;
+	int _stencilTestFunc;
+	int _stencilRefVal;
+	uint _stencilMask;
+	uint _stencilWriteMask;
+	int _stencilSfail;
+	int _stencilDpfail;
+	int _stencilDppass;
 	int _depthFunc;
 	int _offsetStates;
 	float _offsetFactor;
diff --git a/graphics/tinygl/zdirtyrect.cpp b/graphics/tinygl/zdirtyrect.cpp
index b22f7814ca..7bf80e9d2c 100644
--- a/graphics/tinygl/zdirtyrect.cpp
+++ b/graphics/tinygl/zdirtyrect.cpp
@@ -423,21 +423,36 @@ void RasterizationDrawCall::execute(bool restoreState) const {
 RasterizationDrawCall::RasterizationState RasterizationDrawCall::captureState() const {
 	RasterizationState state;
 	GLContext *c = gl_get_context();
-	state.alphaTest = c->alpha_test_enabled;
+	state.enableBlending = c->blending_enabled;
 	state.sfactor = c->source_blending_factor;
 	state.dfactor = c->destination_blending_factor;
-	state.enableBlending = c->blending_enabled;
+	state.alphaTestEnabled = c->alpha_test_enabled;
 	state.alphaFunc = c->alpha_test_func;
 	state.alphaRefValue = c->alpha_test_ref_val;
+	state.depthTestEnabled = c->depth_test_enabled;
+	state.depthFunction = c->depth_func;
+	state.depthWriteMask = c->depth_write_mask;
+	state.stencilTestEnabled = c->stencil_test_enabled;
+	state.stencilTestFunc = c->stencil_test_func;
+	state.stencilValue = c->stencil_ref_val;
+	state.stencilMask = c->stencil_mask;
+	state.stencilWriteMask = c->stencil_write_mask;
+	state.stencilSfail = c->stencil_sfail;
+	state.stencilDpfail = c->stencil_dpfail;
+	state.stencilDppass = c->stencil_dppass;
+	state.offsetStates = c->offset_states;
+	state.offsetFactor = c->offset_factor;
+	state.offsetUnits = c->offset_units;
+	state.shadowMaskBuf = c->shadow_mask_buf;
+	state.shadowColorR = c->shadow_color_r;
+	state.shadowColorG = c->shadow_color_g;
+	state.shadowColorB = c->shadow_color_b;
+
 	state.cullFaceEnabled = c->cull_face_enabled;
 	state.beginType = c->begin_type;
 	state.colorMask = c->color_mask;
 	state.currentFrontFace = c->current_front_face;
 	state.currentShadeModel = c->current_shade_model;
-	state.depthTest = c->depth_test;
-	state.offsetStates = c->offset_states;
-	state.offsetFactor = c->offset_factor;
-	state.offsetUnits = c->offset_units;
 	state.polygonModeBack = c->polygon_mode_back;
 	state.polygonModeFront = c->polygon_mode_front;
 	state.shadowMode = c->shadow_mode;
@@ -445,14 +460,7 @@ RasterizationDrawCall::RasterizationState RasterizationDrawCall::captureState()
 	state.texture = c->current_texture;
 	state.wrapS = c->texture_wrap_s;
 	state.wrapT = c->texture_wrap_t;
-	state.shadowMaskBuf = c->shadow_mask_buf;
-	state.shadowColorR = c->shadow_color_r;
-	state.shadowColorG = c->shadow_color_g;
-	state.shadowColorB = c->shadow_color_b;
-	state.depthFunction = c->depth_func;
-	state.depthWrite = c->depth_write;
 	state.lightingEnabled = c->lighting_enabled;
-	state.depthTestEnabled = c->depth_test;
 	if (c->current_texture != nullptr)
 		state.textureVersion = c->current_texture->versionNumber;
 
@@ -466,11 +474,15 @@ void RasterizationDrawCall::applyState(const RasterizationDrawCall::Rasterizatio
 	GLContext *c = gl_get_context();
 	c->fb->enableBlending(state.enableBlending);
 	c->fb->setBlendingFactors(state.sfactor, state.dfactor);
-	c->fb->enableAlphaTest(state.alphaTest);
+	c->fb->enableAlphaTest(state.alphaTestEnabled);
 	c->fb->setAlphaTestFunc(state.alphaFunc, state.alphaRefValue);
 	c->fb->setDepthFunc(state.depthFunction);
-	c->fb->enableDepthWrite(state.depthWrite);
+	c->fb->enableDepthWrite(state.depthWriteMask);
 	c->fb->enableDepthTest(state.depthTestEnabled);
+	c->fb->enableStencilTest(state.stencilTestEnabled);
+	c->fb->setStencilWriteMask(state.stencilWriteMask);
+	c->fb->setStencilTestFunc(state.stencilTestFunc, state.stencilValue, state.stencilMask);
+	c->fb->setStencilOp(state.stencilSfail, state.stencilDpfail, state.stencilDppass);
 	c->fb->setOffsetStates(state.offsetStates);
 	c->fb->setOffsetFactor(state.offsetFactor);
 	c->fb->setOffsetUnits(state.offsetUnits);
@@ -480,12 +492,20 @@ void RasterizationDrawCall::applyState(const RasterizationDrawCall::Rasterizatio
 	c->blending_enabled = state.enableBlending;
 	c->source_blending_factor = state.sfactor;
 	c->destination_blending_factor = state.dfactor;
-	c->alpha_test_enabled = state.alphaTest;
+	c->alpha_test_enabled = state.alphaTestEnabled;
 	c->alpha_test_func = state.alphaFunc;
 	c->alpha_test_ref_val = state.alphaRefValue;
-	c->depth_test = state.depthTest;
+	c->depth_test_enabled = state.depthTestEnabled;
 	c->depth_func = state.depthFunction;
-	c->depth_write = state.depthWrite;
+	c->depth_write_mask = state.depthWriteMask;
+	c->stencil_test_enabled = state.stencilTestEnabled;
+	c->stencil_test_func = state.stencilTestFunc;
+	c->stencil_ref_val = state.stencilValue;
+	c->stencil_mask = state.stencilMask;
+	c->stencil_write_mask = state.stencilWriteMask;
+	c->stencil_sfail = state.stencilSfail;
+	c->stencil_dpfail = state.stencilDpfail;
+	c->stencil_dppass = state.stencilDppass;
 	c->offset_states = state.offsetStates;
 	c->offset_factor = state.offsetFactor;
 	c->offset_units = state.offsetUnits;
@@ -591,7 +611,7 @@ BlittingDrawCall::BlittingState BlittingDrawCall::captureState() const {
 	state.alphaTest = c->alpha_test_enabled;
 	state.alphaFunc = c->alpha_test_func;
 	state.alphaRefValue = c->alpha_test_ref_val;
-	state.depthTestEnabled = c->depth_test;
+	state.depthTestEnabled = c->depth_test_enabled;
 	return state;
 }
 
@@ -609,7 +629,7 @@ void BlittingDrawCall::applyState(const BlittingState &state) const {
 	c->alpha_test_enabled = state.alphaTest;
 	c->alpha_test_func = state.alphaFunc;
 	c->alpha_test_ref_val = state.alphaRefValue;
-	c->depth_test = state.depthTestEnabled;
+	c->depth_test_enabled = state.depthTestEnabled;
 }
 
 void BlittingDrawCall::computeDirtyRegion() {
@@ -643,16 +663,21 @@ void BlittingDrawCall::computeDirtyRegion() {
 }
 
 bool BlittingDrawCall::operator==(const BlittingDrawCall &other) const {
-	return	_mode == other._mode &&
-			_image == other._image &&
-			_transform == other._transform &&
-			_blitState == other._blitState &&
-			_imageVersion == tglGetBlitImageVersion(other._image);
+	return
+		_mode == other._mode &&
+		_image == other._image &&
+		_transform == other._transform &&
+		_blitState == other._blitState &&
+		_imageVersion == tglGetBlitImageVersion(other._image);
 }
 
 
-ClearBufferDrawCall::ClearBufferDrawCall(bool clearZBuffer, int zValue, bool clearColorBuffer, int rValue, int gValue, int bValue)
-	: _clearZBuffer(clearZBuffer), _clearColorBuffer(clearColorBuffer), _zValue(zValue), _rValue(rValue), _gValue(gValue), _bValue(bValue), DrawCall(DrawCall_Clear) {
+ClearBufferDrawCall::ClearBufferDrawCall(bool clearZBuffer, int zValue,
+	                                 bool clearColorBuffer, int rValue, int gValue, int bValue,
+	                                 bool clearStencilBuffer, int stencilValue)
+	: _clearZBuffer(clearZBuffer), _clearColorBuffer(clearColorBuffer), _zValue(zValue),
+	  _rValue(rValue), _gValue(gValue), _bValue(bValue), _clearStencilBuffer(clearStencilBuffer),
+	  _stencilValue(stencilValue), DrawCall(DrawCall_Clear) {
 	TinyGL::GLContext *c = gl_get_context();
 	if (c->_enableDirtyRectangles) {
 		_dirtyRegion = c->renderRect;
@@ -661,58 +686,71 @@ ClearBufferDrawCall::ClearBufferDrawCall(bool clearZBuffer, int zValue, bool cle
 
 void ClearBufferDrawCall::execute(bool restoreState) const {
 	TinyGL::GLContext *c = gl_get_context();
-	c->fb->clear(_clearZBuffer, _zValue, _clearColorBuffer, _rValue, _gValue, _bValue);
+	c->fb->clear(_clearZBuffer, _zValue, _clearColorBuffer, _rValue, _gValue, _bValue, _clearStencilBuffer, _stencilValue);
 }
 
 void ClearBufferDrawCall::execute(const Common::Rect &clippingRectangle, bool restoreState) const {
 	TinyGL::GLContext *c = gl_get_context();
 	Common::Rect clearRect = clippingRectangle.findIntersectingRect(getDirtyRegion());
-	c->fb->clearRegion(clearRect.left, clearRect.top, clearRect.width(), clearRect.height(), _clearZBuffer, _zValue, _clearColorBuffer, _rValue, _gValue, _bValue);
+	c->fb->clearRegion(clearRect.left, clearRect.top, clearRect.width(), clearRect.height(),
+	                   _clearZBuffer, _zValue, _clearColorBuffer, _rValue, _gValue, _bValue,
+	                   _clearStencilBuffer, _stencilValue);
 }
 
 bool ClearBufferDrawCall::operator==(const ClearBufferDrawCall &other) const {
-	return	_clearZBuffer == other._clearZBuffer &&
-			_clearColorBuffer == other._clearColorBuffer &&
-			_rValue == other._rValue &&
-			_gValue == other._gValue &&
-			_bValue == other._bValue &&
-			_zValue == other._zValue;
+	return
+		_clearZBuffer == other._clearZBuffer &&
+		_clearColorBuffer == other._clearColorBuffer &&
+		_clearStencilBuffer == other._clearStencilBuffer &&
+		_rValue == other._rValue &&
+		_gValue == other._gValue &&
+		_bValue == other._bValue &&
+		_zValue == other._zValue &&
+		_stencilValue == other._stencilValue;
 }
 
 
 bool RasterizationDrawCall::RasterizationState::operator==(const RasterizationState &other) const {
-	return	beginType == other.beginType &&
-			currentFrontFace == other.currentFrontFace &&
-			cullFaceEnabled == other.cullFaceEnabled &&
-			colorMask == other.colorMask &&
-			depthTest == other.depthTest &&
-			offsetStates == other.offsetStates &&
-			offsetFactor == other.offsetFactor &&
-			offsetUnits == other.offsetUnits &&
-			depthFunction == other.depthFunction &&
-			depthWrite == other.depthWrite &&
-			shadowMode == other.shadowMode &&
-			texture2DEnabled == other.texture2DEnabled &&
-			currentShadeModel == other.currentShadeModel &&
-			polygonModeBack == other.polygonModeBack &&
-			polygonModeFront == other.polygonModeFront &&
-			lightingEnabled == other.lightingEnabled &&
-			enableBlending == other.enableBlending &&
-			sfactor == other.sfactor &&
-			dfactor == other.dfactor &&
-			alphaTest == other.alphaTest &&
-			alphaFunc == other.alphaFunc &&
-			alphaRefValue == other.alphaRefValue &&
-			depthTestEnabled == other.depthTestEnabled &&
-			texture == other.texture &&
-			textureVersion == texture->versionNumber &&
-			shadowMaskBuf == other.shadowMaskBuf &&
-			viewportTranslation[0] == other.viewportTranslation[0] &&
-			viewportTranslation[1] == other.viewportTranslation[1] &&
-			viewportTranslation[2] == other.viewportTranslation[2] &&
-			viewportScaling[0] == other.viewportScaling[0] &&
-			viewportScaling[1] == other.viewportScaling[1] &&
-			viewportScaling[2] == other.viewportScaling[2];
+	return
+		enableBlending == other.enableBlending &&
+		sfactor == other.sfactor &&
+		dfactor == other.dfactor &&
+		alphaTestEnabled == other.alphaTestEnabled &&
+		alphaFunc == other.alphaFunc &&
+		alphaRefValue == other.alphaRefValue &&
+		depthTestEnabled == other.depthTestEnabled &&
+		depthFunction == other.depthFunction &&
+		depthWriteMask == other.depthWriteMask &&
+		stencilTestEnabled == other.stencilTestEnabled &&
+		stencilTestFunc == other.stencilTestFunc &&
+		stencilValue == other.stencilValue &&
+		stencilMask == other.stencilMask &&
+		stencilWriteMask == other.stencilWriteMask &&
+		stencilSfail == other.stencilSfail &&
+		stencilDpfail == other.stencilDpfail &&
+		stencilDppass == other.stencilDppass &&
+		offsetStates == other.offsetStates &&
+		offsetFactor == other.offsetFactor &&
+		offsetUnits == other.offsetUnits &&
+		shadowMaskBuf == other.shadowMaskBuf &&
+		lightingEnabled == other.lightingEnabled &&
+		cullFaceEnabled == other.cullFaceEnabled &&
+		beginType == other.beginType &&
+		colorMask == other.colorMask &&
+		currentFrontFace == other.currentFrontFace &&
+		currentShadeModel == other.currentShadeModel &&
+		polygonModeBack == other.polygonModeBack &&
+		polygonModeFront == other.polygonModeFront &&
+		shadowMode == other.shadowMode &&
+		texture2DEnabled == other.texture2DEnabled &&
+		texture == other.texture &&
+		textureVersion == texture->versionNumber &&
+		viewportTranslation[0] == other.viewportTranslation[0] &&
+		viewportTranslation[1] == other.viewportTranslation[1] &&
+		viewportTranslation[2] == other.viewportTranslation[2] &&
+		viewportScaling[0] == other.viewportScaling[0] &&
+		viewportScaling[1] == other.viewportScaling[1] &&
+		viewportScaling[2] == other.viewportScaling[2];
 }
 
 void *Internal::allocateFrame(int size) {
diff --git a/graphics/tinygl/zdirtyrect.h b/graphics/tinygl/zdirtyrect.h
index 92fd8b189d..68c9d50c68 100644
--- a/graphics/tinygl/zdirtyrect.h
+++ b/graphics/tinygl/zdirtyrect.h
@@ -23,6 +23,7 @@
 #ifndef GRAPHICS_TINYGL_ZRECT_H
 #define GRAPHICS_TINYGL_ZRECT_H
 
+#include "common/types.h"
 #include "common/rect.h"
 #include "common/array.h"
 
@@ -65,7 +66,7 @@ private:
 
 class ClearBufferDrawCall : public DrawCall {
 public:
-	ClearBufferDrawCall(bool clearZBuffer, int zValue, bool clearColorBuffer, int rValue, int gValue, int bValue);
+	ClearBufferDrawCall(bool clearZBuffer, int zValue, bool clearColorBuffer, int rValue, int gValue, int bValue, bool clearStencilBuffer, int stencilValue);
 	virtual ~ClearBufferDrawCall() { }
 	bool operator==(const ClearBufferDrawCall &other) const;
 	virtual void execute(bool restoreState) const;
@@ -77,8 +78,8 @@ public:
 
 	void operator delete(void *p) { }
 private:
-	bool _clearZBuffer, _clearColorBuffer;
-	int _rValue, _gValue, _bValue, _zValue;
+	bool _clearZBuffer, _clearColorBuffer, _clearStencilBuffer;
+	int _rValue, _gValue, _bValue, _zValue, _stencilValue;
 };
 
 // Encapsulate a rasterization call: it might execute either a triangle or line rasterization.
@@ -107,32 +108,41 @@ private:
 		int currentFrontFace;
 		int cullFaceEnabled;
 		int colorMask;
-		int depthTest;
+		bool depthTestEnabled;
 		int depthFunction;
-		int depthWrite;
+		int depthWriteMask;
 		int shadowMode;
 		int shadowColorR;
 		int shadowColorG;
 		int shadowColorB;
-		int texture2DEnabled;
+		bool texture2DEnabled;
 		int currentShadeModel;
 		int polygonModeBack;
 		int polygonModeFront;
 		int lightingEnabled;
 		bool enableBlending;
-		int sfactor, dfactor;
+		int sfactor;
+		int dfactor;
 		int textureVersion;
-		int depthTestEnabled;
 		int offsetStates;
 		float offsetFactor;
 		float offsetUnits;
 		float viewportTranslation[3];
 		float viewportScaling[3];
-		bool alphaTest;
-		int alphaFunc, alphaRefValue;
+		bool alphaTestEnabled;
+		int alphaFunc;
+		int alphaRefValue;
+		bool stencilTestEnabled;
+		int stencilTestFunc;
+		int stencilValue;
+		uint stencilMask;
+		uint stencilWriteMask;
+		int stencilSfail;
+		int stencilDpfail;
+		int stencilDppass;
 		TinyGL::GLTexture *texture;
-		unsigned int wrapS, wrapT;
-		unsigned char *shadowMaskBuf;
+		uint wrapS, wrapT;
+		byte *shadowMaskBuf;
 
 		bool operator==(const RasterizationState &other) const;
 	};
@@ -181,13 +191,14 @@ private:
 		int depthTestEnabled;
 
 		bool operator==(const BlittingState &other) const {
-			return	enableBlending == other.enableBlending &&
-					sfactor == other.sfactor &&
-					dfactor == other.dfactor &&
-					alphaTest == other.alphaTest &&
-					alphaFunc == other.alphaFunc &&
-					alphaRefValue == other.alphaRefValue &&
-					depthTestEnabled == other.depthTestEnabled;
+			return
+				enableBlending == other.enableBlending &&
+				sfactor == other.sfactor &&
+				dfactor == other.dfactor &&
+				alphaTest == other.alphaTest &&
+				alphaFunc == other.alphaFunc &&
+				alphaRefValue == other.alphaRefValue &&
+				depthTestEnabled == other.depthTestEnabled;
 		}
 	};
 
diff --git a/graphics/tinygl/zgl.h b/graphics/tinygl/zgl.h
index 78cd9aa8f6..d1f6295d9f 100644
--- a/graphics/tinygl/zgl.h
+++ b/graphics/tinygl/zgl.h
@@ -359,6 +359,7 @@ struct GLContext {
 	// clear
 	float clear_depth;
 	Vector4 clear_color;
+	int clear_stencil;
 
 	// current vertex state
 	Vector4 current_color;
@@ -410,9 +411,19 @@ struct GLContext {
 	int (*gl_resize_viewport)(int *xsize, int *ysize);
 
 	// depth test
-	bool depth_test;
+	bool depth_test_enabled;
 	int depth_func;
-	bool depth_write;
+	bool depth_write_mask;
+
+	// stencil
+	bool stencil_test_enabled;
+	int stencil_test_func;
+	int stencil_ref_val;
+	uint stencil_mask;
+	uint stencil_write_mask;
+	int stencil_sfail;
+	int stencil_dpfail;
+	int stencil_dppass;
 
 	int color_mask;
 
@@ -479,7 +490,7 @@ public:
 	void initSharedState();
 	void endSharedState();
 
-	void init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool dirtyRectsEnable = true);
+	void init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize, bool enableStencilBuffer, bool dirtyRectsEnable = true);
 	void deinit();
 };
 
diff --git a/graphics/tinygl/ztriangle.cpp b/graphics/tinygl/ztriangle.cpp
index d96634dda0..7c986878b7 100644
--- a/graphics/tinygl/ztriangle.cpp
+++ b/graphics/tinygl/ztriangle.cpp
@@ -35,20 +35,48 @@ namespace TinyGL {
 
 static const int NB_INTERP = 8;
 
-template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
-FORCEINLINE void FrameBuffer::putPixelFlat(FrameBuffer *buffer, int buf, unsigned int *pz, int _a,
-	                             int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a, int &dzdx) {
-	if ((!kEnableScissor || !buffer->scissorPixel(x + _a, y)) && buffer->compareDepth(z, pz[_a])) {
+template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
+FORCEINLINE void FrameBuffer::putPixelFlat(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a,
+	                                   int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a, int &dzdx) {
+	if (kEnableScissor && buffer->scissorPixel(x + _a, y)) {
+		return;
+	}
+	if (kStencilEnabled) {
+		bool stencilResult = stencilTest(ps[_a]);
+		if (!stencilResult) {
+			stencilOp(false, true, ps + _a);
+			return;
+		}
+	}
+	bool depthTestResult = buffer->compareDepth(z, pz[_a]);
+	if (kStencilEnabled) {
+		stencilOp(true, depthTestResult, ps + _a);
+	}
+	if (depthTestResult) {
 		buffer->writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite>(buf + _a, a >> (ZB_POINT_ALPHA_BITS - 8), r >> (ZB_POINT_RED_BITS - 8), g >> (ZB_POINT_GREEN_BITS - 8), b >> (ZB_POINT_BLUE_BITS - 8), z);
 	}
 	z += dzdx;
 }
 
-template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
-FORCEINLINE void FrameBuffer::putPixelSmooth(FrameBuffer *buffer, int buf, unsigned int *pz, int _a,
-	                               int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
-	                               int &dzdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx) {
-	if ((!kEnableScissor || !buffer->scissorPixel(x + _a, y)) && buffer->compareDepth(z, pz[_a])) {
+template <bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
+FORCEINLINE void FrameBuffer::putPixelSmooth(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a,
+	                                     int x, int y, unsigned int &z, unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
+	                                     int &dzdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx) {
+	if (kEnableScissor && buffer->scissorPixel(x + _a, y)) {
+		return;
+	}
+	if (kStencilEnabled) {
+		bool stencilResult = stencilTest(ps[_a]);
+		if (!stencilResult) {
+			stencilOp(false, true, ps + _a);
+			return;
+		}
+	}
+	bool depthTestResult = buffer->compareDepth(z, pz[_a]);
+	if (kStencilEnabled) {
+		stencilOp(true, depthTestResult, ps + _a);
+	}
+	if (depthTestResult) {
 		buffer->writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite>(buf + _a, a >> (ZB_POINT_ALPHA_BITS - 8), r >> (ZB_POINT_RED_BITS - 8), g >> (ZB_POINT_GREEN_BITS - 8), b >> (ZB_POINT_BLUE_BITS - 8), z);
 	}
 	z += dzdx;
@@ -58,32 +86,54 @@ FORCEINLINE void FrameBuffer::putPixelSmooth(FrameBuffer *buffer, int buf, unsig
 	b += dbdx;
 }
 
-template <bool kDepthWrite, bool kEnableScissor>
-FORCEINLINE void FrameBuffer::putPixelDepth(FrameBuffer *buffer, int buf, unsigned int *pz, int _a, int x, int y, unsigned int &z, int &dzdx) {
-	if ((!kEnableScissor || !buffer->scissorPixel(x + _a, y)) && buffer->compareDepth(z, pz[_a])) {
-		if (kDepthWrite) {
-			pz[_a] = z;
+template <bool kDepthWrite, bool kEnableScissor, bool kStencilEnabled>
+FORCEINLINE void FrameBuffer::putPixelDepth(FrameBuffer *buffer, int buf, unsigned int *pz, byte *ps, int _a, int x, int y, unsigned int &z, int &dzdx) {
+	if (kEnableScissor && buffer->scissorPixel(x + _a, y)) {
+		return;
+	}
+	if (kStencilEnabled) {
+		bool stencilResult = stencilTest(ps[_a]);
+		if (!stencilResult) {
+			stencilOp(false, true, ps + _a);
+			return;
 		}
 	}
+	if (kDepthWrite && buffer->compareDepth(z, pz[_a])) {
+		pz[_a] = z;
+	}
 	z += dzdx;
 }
 
 template <bool kDepthWrite, bool kAlphaTestEnabled, bool kEnableScissor, bool kBlendingEnabled>
 FORCEINLINE void FrameBuffer::putPixelShadow(FrameBuffer *buffer, int buf, unsigned int *pz, int _a, int x, int y, unsigned int &z,
-								             unsigned int &r, unsigned int &g, unsigned int &b, int &dzdx, unsigned char *pm) {
+	                                     unsigned int &r, unsigned int &g, unsigned int &b, int &dzdx, unsigned char *pm) {
 	if ((!kEnableScissor || !buffer->scissorPixel(x + _a, y)) && buffer->compareDepth(z, pz[_a]) && pm[_a]) {
 		buffer->writePixel<kAlphaTestEnabled, kBlendingEnabled, kDepthWrite>(buf + _a, 255, r >> (ZB_POINT_RED_BITS - 8), g >> (ZB_POINT_GREEN_BITS - 8), b >> (ZB_POINT_BLUE_BITS - 8), z);
 	}
 	z += dzdx;
 }
 
-template <bool kDepthWrite, bool kLightsMode, bool kSmoothMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
+template <bool kDepthWrite, bool kLightsMode, bool kSmoothMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
 FORCEINLINE void FrameBuffer::putPixelTextureMappingPerspective(FrameBuffer *buffer, int buf, const Graphics::TexelBuffer *texture,
-	                                                            unsigned int wrap_s, unsigned int wrap_t, unsigned int *pz, int _a,
-	                                                            int x, int y, unsigned int &z, int &t, int &s,
-																unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
-	                                                            int &dzdx, int &dsdx, int &dtdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx) {
-	if ((!kEnableScissor || !buffer->scissorPixel(x + _a, y)) && buffer->compareDepth(z, pz[_a])) {
+	                                                        unsigned int wrap_s, unsigned int wrap_t, unsigned int *pz, byte *ps, int _a,
+	                                                        int x, int y, unsigned int &z, int &t, int &s,
+	                                                        unsigned int &r, unsigned int &g, unsigned int &b, unsigned int &a,
+	                                                        int &dzdx, int &dsdx, int &dtdx, int &drdx, int &dgdx, int &dbdx, unsigned int dadx) {
+	if (kEnableScissor && buffer->scissorPixel(x + _a, y)) {
+		return;
+	}
+	if (kStencilEnabled) {
+		bool stencilResult = stencilTest(ps[_a]);
+		if (!stencilResult) {
+			stencilOp(false, true, ps + _a);
+			return;
+		}
+	}
+	bool depthTestResult = buffer->compareDepth(z, pz[_a]);
+	if (kStencilEnabled) {
+		stencilOp(true, depthTestResult, ps + _a);
+	}
+	if (depthTestResult) {
 		uint8 c_a, c_r, c_g, c_b;
 		texture->getARGBAt(wrap_s, wrap_t, s, t, c_a, c_r, c_g, c_b);
 		if (kLightsMode) {
@@ -109,15 +159,16 @@ FORCEINLINE void FrameBuffer::putPixelTextureMappingPerspective(FrameBuffer *buf
 	}
 }
 
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawLogic, bool kDepthWrite, bool kAlphaTestEnabled, bool kEnableScissor, bool kBlendingEnabled>
+template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawLogic, bool kDepthWrite, bool kAlphaTestEnabled, bool kEnableScissor, bool kBlendingEnabled, bool kStencilEnabled>
 void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
 	const Graphics::TexelBuffer *texture;
 	float fdzdx = 0, fndzdx = 0, ndszdx = 0, ndtzdx = 0;
 
 	ZBufferPoint *tp, *pr1 = 0, *pr2 = 0, *l1 = 0, *l2 = 0;
 	float fdx1, fdx2, fdy1, fdy2, fz0, d1, d2;
-	unsigned int *pz1 = NULL;
-	unsigned char *pm1 = NULL;
+	uint *pz1 = nullptr;
+	byte *pm1 = nullptr;
+	byte *ps1 = nullptr;
 	int part, update_left = 1, update_right = 1;
 
 	int nb_lines, dx1, dy1, tmp, dx2, dy2, y;
@@ -243,6 +294,9 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 
 	int pp1 = _pbufWidth * p0->y;
 	pz1 = _zbuf + p0->y * _pbufWidth;
+	if (kStencilEnabled) {
+		ps1 = _sbuf + p0->y * _pbufWidth;
+	}
 
 	switch (kDrawLogic) {
 	case DRAW_SHADOW_MASK:
@@ -375,6 +429,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 					int pp;
 					int n;
 					unsigned int *pz;
+					byte *ps = nullptr;
 					unsigned int z, a;
 					int buf = pp1 + x1;
 					unsigned int r = r1;
@@ -386,41 +441,50 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 						pz = pz1 + x1;
 						z = z1;
 					}
+					if (kStencilEnabled) {
+						ps = ps1 + x1;
+					}
 					if (kDrawLogic == DRAW_FLAT) {
 						a = a1;
 					}
 					while (n >= 3) {
 						if (kDrawLogic == DRAW_DEPTH_ONLY) {
-							putPixelDepth<kDepthWrite, kEnableScissor>(this, buf, pz, 0, x, y, z, dzdx);
-							putPixelDepth<kDepthWrite, kEnableScissor>(this, buf, pz, 1, x, y, z, dzdx);
-							putPixelDepth<kDepthWrite, kEnableScissor>(this, buf, pz, 2, x, y, z, dzdx);
-							putPixelDepth<kDepthWrite, kEnableScissor>(this, buf, pz, 3, x, y, z, dzdx);
+							putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled>(this, buf, pz, ps, 0, x, y, z, dzdx);
+							putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled>(this, buf, pz, ps, 1, x, y, z, dzdx);
+							putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled>(this, buf, pz, ps, 2, x, y, z, dzdx);
+							putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled>(this, buf, pz, ps, 3, x, y, z, dzdx);
 							buf += 4;
 						}
 						if (kDrawLogic == DRAW_FLAT) {
-							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, pp, pz, 0, x, y, z, r, g, b, a, dzdx);
-							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, pp, pz, 1, x, y, z, r, g, b, a, dzdx);
-							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, pp, pz, 2, x, y, z, r, g, g, a, dzdx);
-							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, pp, pz, 3, x, y, z, r, g, b, a, dzdx);
+							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, pp, pz, ps, 0, x, y, z, r, g, b, a, dzdx);
+							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, pp, pz, ps, 1, x, y, z, r, g, b, a, dzdx);
+							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, pp, pz, ps, 2, x, y, z, r, g, g, a, dzdx);
+							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, pp, pz, ps, 3, x, y, z, r, g, b, a, dzdx);
 						}
 						if (kInterpZ) {
 							pz += 4;
 						}
+						if (kStencilEnabled) {
+							ps += 4;
+						}
 						pp += 4;
 						n -= 4;
 						x += 4;
 					}
 					while (n >= 0) {
 						if (kDrawLogic == DRAW_DEPTH_ONLY) {
-							putPixelDepth<kDepthWrite, kEnableScissor>(this, buf, pz, 0, x, y, z, dzdx);
+							putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled>(this, buf, pz, ps, 0, x, y, z, dzdx);
 							buf ++;
 						}
 						if (kDrawLogic == DRAW_FLAT) {
-							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, pp, pz, 0, x, y, z, r, g, b, a, dzdx);
+							putPixelFlat<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, pp, pz, ps, 0, x, y, z, r, g, b, a, dzdx);
 						}
 						if (kInterpZ) {
 							pz += 1;
 						}
+						if (kStencilEnabled) {
+							ps += 1;
+						}
 						pp += 1;
 						n -= 1;
 						x += 1;
@@ -483,35 +547,46 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 					}
 				} else if (kDrawLogic == DRAW_SMOOTH && !(kInterpST || kInterpSTZ)) {
 					unsigned int *pz;
+					byte *ps = nullptr;
 					int buf = pp1 + x1;
 					unsigned int z, r, g, b, a;
 					int n;
 					n = (x2 >> 16) - x1;
 					pz = pz1 + x1;
+					if (kStencilEnabled) {
+						ps = ps1 + x1;
+					}
 					z = z1;
 					r = r1;
 					g = g1;
 					b = b1;
 					a = a1;
 					while (n >= 3) {
-						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, pz, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
-						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, pz, 1, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
-						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, pz, 2, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
-						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, pz, 3, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
+						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, buf, pz, ps, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
+						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, buf, pz, ps, 1, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
+						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, buf, pz, ps, 2, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
+						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, buf, pz, ps, 3, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
 						pz += 4;
+						if (kStencilEnabled) {
+							ps += 4;
+						}
 						buf += 4;
 						n -= 4;
 						x += 4;
 					}
 					while (n >= 0) {
-						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, pz, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
+						putPixelSmooth<kDepthWrite, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>(this, buf, pz, ps, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx);
 						buf += 1;
 						pz += 1;
+						if (kStencilEnabled) {
+							ps += 1;
+						}
 						n -= 1;
 						x += 1;
 					}
 				} else if (kInterpST || kInterpSTZ) {
 					unsigned int *pz;
+					byte *ps = nullptr;
 					int s, t;
 					unsigned int z, r, g, b, a;
 					int n;
@@ -525,6 +600,9 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 					int buf = pp1 + x1;
 
 					pz = pz1 + x1;
+					if (kStencilEnabled) {
+						ps = ps1 + x1;
+					}
 					z = z1;
 					sz = sz1;
 					tz = tz1;
@@ -545,10 +623,13 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 							zinv = (float)(1.0 / fz);
 						}
 						for (int _a = 0; _a < NB_INTERP; _a++) {
-							putPixelTextureMappingPerspective<kDepthWrite, kInterpRGB, kDrawLogic == DRAW_SMOOTH, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, texture, _wrapS, _wrapT,
-							                           pz, _a, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx);
+							putPixelTextureMappingPerspective<kDepthWrite, kInterpRGB, kDrawLogic == DRAW_SMOOTH, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>
+							                                  (this, buf, texture, _wrapS, _wrapT, pz, ps, _a, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx);
 						}
 						pz += NB_INTERP;
+						if (kStencilEnabled) {
+							ps += NB_INTERP;
+						}
 						buf += NB_INTERP;
 						n -= NB_INTERP;
 						x += NB_INTERP;
@@ -567,9 +648,12 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 					}
 
 					while (n >= 0) {
-						putPixelTextureMappingPerspective<kDepthWrite, kInterpRGB, kDrawLogic == DRAW_SMOOTH, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled>(this, buf, texture, _wrapS, _wrapT,
-						                           pz, 0, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx);
+						putPixelTextureMappingPerspective<kDepthWrite, kInterpRGB, kDrawLogic == DRAW_SMOOTH, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled>
+						                                  (this, buf, texture, _wrapS, _wrapT, pz, ps, 0, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx);
 						pz += 1;
+						if (kStencilEnabled) {
+							ps += 1;
+						}
 						buf += 1;
 						n -= 1;
 						x += 1;
@@ -620,15 +704,27 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
 			// screen coordinates
 			pp1 += _pbufWidth;
 			pz1 += _pbufWidth;
+			if (kStencilEnabled) {
+				ps1 += _pbufWidth;
+			}
 
 			if (kDrawLogic == DRAW_SHADOW || kDrawLogic == DRAW_SHADOW_MASK)
-				pm1 = pm1 + _pbufWidth;
+				pm1 += _pbufWidth;
 			nb_lines--;
 			y++;
 		}
 	}
 }
 
+template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawMode, bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
+void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
+	if (_sbuf && _stencilTestEnabled) {
+		fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kDrawMode, kDepthWrite, kEnableAlphaTest, kEnableScissor, kEnableBlending, true>(p0, p1, p2);
+	} else {
+		fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kDrawMode, kDepthWrite, kEnableAlphaTest, kEnableScissor, kEnableBlending, false>(p0, p1, p2);
+	}
+}
+
 template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, int kDrawMode, bool kDepthWrite, bool kEnableAlphaTest, bool kEnableScissor>
 void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
 	if (_blendingEnabled) {




More information about the Scummvm-git-logs mailing list