[Scummvm-git-logs] scummvm master -> 328a26973fa540d58e4896f6321b359637f8e3fe

aquadran noreply at scummvm.org
Mon Sep 29 17:46:17 UTC 2025


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

Summary:
328a26973f WINTERMUTE: Added initial TinyGL renderer code


Commit: 328a26973fa540d58e4896f6321b359637f8e3fe
    https://github.com/scummvm/scummvm/commit/328a26973fa540d58e4896f6321b359637f8e3fe
Author: Paweł Kołodziejski (aquadran at gmail.com)
Date: 2025-09-29T19:46:10+02:00

Commit Message:
WINTERMUTE: Added initial TinyGL renderer code

Changed paths:
  A engines/wintermute/base/gfx/tinygl/base_render_tinygl.cpp
  A engines/wintermute/base/gfx/tinygl/base_render_tinygl.h
  A engines/wintermute/base/gfx/tinygl/base_surface_tinygl.cpp
  A engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h
  A engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.cpp
  A engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h
  A engines/wintermute/base/gfx/tinygl/meshx_tinygl.cpp
  A engines/wintermute/base/gfx/tinygl/meshx_tinygl.h
  A engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.cpp
  A engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h
    engines/wintermute/base/base_game.cpp
    engines/wintermute/base/gfx/base_renderer.h
    engines/wintermute/base/gfx/base_renderer3d.h
    engines/wintermute/configure.engine
    engines/wintermute/module.mk
    graphics/tinygl/zblit_public.h


diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp
index 101719b8efa..e187fefb3f9 100644
--- a/engines/wintermute/base/base_game.cpp
+++ b/engines/wintermute/base/base_game.cpp
@@ -628,12 +628,13 @@ bool BaseGame::initialize2() { // we know whether we are going to be accelerated
 		_renderer3D = makeOpenGL3DRenderer(this);
 	}
 #endif // defined(USE_OPENGL)
+#if defined(USE_TINYGL)
 	if (!force2dRenderer && matchingRendererType == Graphics::kRendererTypeTinyGL) {
 		if (_playing3DGame) {
-			_renderer3D = nullptr;// TODO: makeTinyGL3DRenderer(this);
-			warning("3D software renderer is not supported yet");
+			_renderer3D = nullptr;//makeTinyGL3DRenderer(this);
 		}
 	}
+#endif // defined(USE_TINYGL)
 	_useD3D = _renderer3D != nullptr;
 	_renderer = _renderer3D;
 
diff --git a/engines/wintermute/base/gfx/base_renderer.h b/engines/wintermute/base/gfx/base_renderer.h
index e912133d717..a724ca5b022 100644
--- a/engines/wintermute/base/gfx/base_renderer.h
+++ b/engines/wintermute/base/gfx/base_renderer.h
@@ -198,6 +198,7 @@ class BaseRenderer3D;
 
 BaseRenderer3D *makeOpenGL3DRenderer(BaseGame *inGame);
 BaseRenderer3D *makeOpenGL3DShaderRenderer(BaseGame *inGame);
+BaseRenderer3D *makeTinyGL3DRenderer(BaseGame *inGame);
 #endif
 
 } // End of namespace Wintermute
diff --git a/engines/wintermute/base/gfx/base_renderer3d.h b/engines/wintermute/base/gfx/base_renderer3d.h
index d606fba40a0..100a14ae959 100644
--- a/engines/wintermute/base/gfx/base_renderer3d.h
+++ b/engines/wintermute/base/gfx/base_renderer3d.h
@@ -167,6 +167,7 @@ protected:
 	float _farClipPlane;
 	TRendererState _state;
 	PostFilter _postFilterMode;
+	bool _flipInProgress;
 
 	virtual void setAmbientLightRenderState() = 0;
 };
diff --git a/engines/wintermute/base/gfx/tinygl/base_render_tinygl.cpp b/engines/wintermute/base/gfx/tinygl/base_render_tinygl.cpp
new file mode 100644
index 00000000000..a9f4719a36d
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/base_render_tinygl.cpp
@@ -0,0 +1,1058 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "engines/wintermute/ad/ad_block.h"
+#include "engines/wintermute/ad/ad_game.h"
+#include "engines/wintermute/ad/ad_generic.h"
+#include "engines/wintermute/ad/ad_scene.h"
+#include "engines/wintermute/ad/ad_scene_geometry.h"
+#include "engines/wintermute/ad/ad_walkplane.h"
+#include "engines/wintermute/ad/ad_waypoint_group3d.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+#include "engines/wintermute/base/gfx/3dcamera.h"
+#include "engines/wintermute/base/gfx/3dlight.h"
+#include "engines/wintermute/platform_osystem.h"
+
+#include "common/config-manager.h"
+
+#include "engines/util.h"
+
+#if defined(USE_TINYGL)
+
+#include "engines/wintermute/base/gfx/3dutils.h"
+#include "engines/wintermute/base/gfx/tinygl/base_render_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/meshx_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h"
+
+namespace Wintermute {
+
+BaseRenderer3D *makeTinyGL3DRenderer(BaseGame *inGame) {
+	return new BaseRenderTinyGL(inGame);
+}
+
+BaseRenderTinyGL::BaseRenderTinyGL(BaseGame *inGame) : BaseRenderer3D(inGame) {
+	_flipInProgress = false;
+}
+
+BaseRenderTinyGL::~BaseRenderTinyGL() {
+	_camera = nullptr; // ref only
+	TinyGL::destroyContext();
+}
+
+bool BaseRenderTinyGL::initRenderer(int width, int height, bool windowed) {
+	_simpleShadow[0].x = -1.0f;
+	_simpleShadow[0].y = 0.0f;
+	_simpleShadow[0].z = 1.0f;
+	_simpleShadow[0].nx = 0.0f;
+	_simpleShadow[0].ny = 1.0f;
+	_simpleShadow[0].nz = 0.0f;
+	_simpleShadow[0].u = 0.0f;
+	_simpleShadow[0].v = 1.0f;
+
+	_simpleShadow[1].x = -1.0f;
+	_simpleShadow[1].y = 0.0f;
+	_simpleShadow[1].z = -1.0f;
+	_simpleShadow[1].nx = 0.0f;
+	_simpleShadow[1].ny = 1.0f;
+	_simpleShadow[1].nz = 0.0f;
+	_simpleShadow[1].u = 1.0f;
+	_simpleShadow[1].v = 1.0f;
+
+	_simpleShadow[2].x = 1.0f;
+	_simpleShadow[2].y = 0.0f;
+	_simpleShadow[2].z = 1.0f;
+	_simpleShadow[2].nx = 0.0f;
+	_simpleShadow[2].ny = 1.0f;
+	_simpleShadow[2].nz = 0.0f;
+	_simpleShadow[2].u = 0.0f;
+	_simpleShadow[2].v = 0.0f;
+
+	_simpleShadow[3].x = 1.0f;
+	_simpleShadow[3].y = 0.0f;
+	_simpleShadow[3].z = -1.0f;
+	_simpleShadow[3].nx = 0.0f;
+	_simpleShadow[3].ny = 1.0f;
+	_simpleShadow[3].nz = 0.0f;
+	_simpleShadow[3].u = 1.0f;
+	_simpleShadow[3].v = 0.0f;
+
+	Graphics::PixelFormat pixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+	initGraphics(width, height, &pixelFormat);
+	if (g_system->getScreenFormat() != pixelFormat) {
+		warning("Couldn't setup GFX-backend for %dx%dx%d", width, height, pixelFormat.bytesPerPixel * 8);
+		return false;
+	}
+
+	debug(2, "INFO: TinyGL front buffer pixel format: %s", pixelFormat.toString().c_str());
+	TinyGL::createContext(width, height, pixelFormat, 512, true, false/*ConfMan.getBool("dirtyrects")*/, 64 * 1024 * 1024);
+
+	setSpriteBlendMode(Graphics::BLEND_NORMAL, true);
+
+	_windowed = !ConfMan.getBool("fullscreen");
+	_width = width;
+	_height = height;
+
+	g_system->showMouse(false);
+
+	setViewport(0, 0, width, height);
+
+	setProjection();
+
+	_postFilterMode = kPostFilterOff;
+
+	_active = true;
+
+	_game->_supportsRealTimeShadows = true;
+
+	setDefaultAmbientLightColor();
+	_lightPositions.resize(getMaxActiveLights());
+	_lightDirections.resize(getMaxActiveLights());
+
+	return true;
+}
+
+bool BaseRenderTinyGL::flip() {
+	if (_flipInProgress) {
+		return false;
+	}
+	_flipInProgress = true;
+
+	_lastTexture = nullptr;
+
+	postfilter();
+
+	Common::List<Common::Rect> dirtyAreas;
+	TinyGL::presentBuffer(dirtyAreas);
+
+	Graphics::Surface glBuffer;
+	TinyGL::getSurfaceRef(glBuffer);
+
+	if (!dirtyAreas.empty()) {
+		for (Common::List<Common::Rect>::iterator itRect = dirtyAreas.begin(); itRect != dirtyAreas.end(); ++itRect) {
+			g_system->copyRectToScreen(glBuffer.getBasePtr((*itRect).left, (*itRect).top), glBuffer.pitch, (*itRect).left, (*itRect).top, (*itRect).width(), (*itRect).height());
+		}
+	}
+
+	g_system->updateScreen();
+
+	_state = RSTATE_NONE;
+
+	_flipInProgress = false;
+	return true;
+}
+
+bool BaseRenderTinyGL::clear() {
+	if(!_game->_editorMode) {
+		tglViewport(0, _height, _width, _height);
+	}
+	tglClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+	tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT | TGL_STENCIL_BUFFER_BIT);
+	return true;
+}
+
+bool BaseRenderTinyGL::setup2D(bool force) {
+	if (_state != RSTATE_2D || force) {
+		_state = RSTATE_2D;
+
+		tglDisable(TGL_LIGHTING);
+		tglDisable(TGL_DEPTH_TEST);
+
+		tglEnable(TGL_BLEND);
+		setSpriteBlendMode(Graphics::BLEND_NORMAL);
+
+		tglEnable(TGL_ALPHA_TEST);
+		tglAlphaFunc(TGL_GEQUAL, 0.0f);
+
+		tglFrontFace(TGL_CCW);  // WME DX have CW
+		tglEnable(TGL_CULL_FACE);
+		tglDisable(TGL_STENCIL_TEST);
+
+		tglDisable(TGL_FOG);
+	}
+
+	return true;
+}
+
+bool BaseRenderTinyGL::setup3D(Camera3D *camera, bool force) {
+	if (_state != RSTATE_3D || force) {
+		_state = RSTATE_3D;
+
+		tglEnable(TGL_NORMALIZE);
+
+		tglEnable(TGL_DEPTH_TEST);
+		tglEnable(TGL_LIGHTING);
+		tglEnable(TGL_ALPHA_TEST);
+		// WME uses 8 as a reference value and Direct3D expects it to be in the range [0, 255]
+		tglAlphaFunc(TGL_GEQUAL, 8 / 255.0f);
+
+		setAmbientLightRenderState();
+
+		if (camera)
+			_camera = camera;
+		if (_camera) {
+			DXMatrix viewMatrix;
+			_camera->getViewMatrix(&viewMatrix);
+			setViewTransform(viewMatrix);
+
+			_fov = _camera->_fov;
+
+			if (_camera->_nearClipPlane >= 0.0f) {
+				_nearClipPlane = _camera->_nearClipPlane;
+			} else {
+				_nearClipPlane = DEFAULT_NEAR_PLANE;
+			}
+
+			if (_camera->_farClipPlane >= 0.0f) {
+				_farClipPlane = _camera->_farClipPlane;
+			} else {
+				_farClipPlane = DEFAULT_FAR_PLANE;
+			}
+		} else {
+			_nearClipPlane = DEFAULT_NEAR_PLANE;
+			_farClipPlane = DEFAULT_FAR_PLANE;
+		}
+
+		// lighting
+		tglEnable(TGL_LIGHTING);
+
+		for (int i = 0; i < getMaxActiveLights(); ++i) {
+			tglLightfv(TGL_LIGHT0 + i, TGL_POSITION, _lightPositions[i]);
+			tglLightfv(TGL_LIGHT0 + i, TGL_SPOT_DIRECTION, _lightDirections[i]);
+		}
+
+		// fog
+		bool fogEnabled;
+		uint32 fogColor;
+		float fogStart, fogEnd;
+		_game->getFogParams(&fogEnabled, &fogColor, &fogStart, &fogEnd);
+		if (fogEnabled) {
+			tglEnable(TGL_FOG);
+			TGLfloat color[4] = { RGBCOLGetR(fogColor) / 255.0f,
+			                      RGBCOLGetG(fogColor) / 255.0f,
+			                      RGBCOLGetB(fogColor) / 255.0f,
+			                      RGBCOLGetA(fogColor) / 255.0f };
+			tglFogfv(TGL_FOG_COLOR, color);
+			tglFogi(TGL_FOG_MODE, TGL_LINEAR);
+			tglFogf(TGL_FOG_START, fogStart);
+			tglFogf(TGL_FOG_END, fogEnd);
+
+		} else {
+			tglDisable(TGL_FOG);
+		}
+
+		setProjection();
+	}
+
+	return true;
+}
+
+void BaseRenderTinyGL::setAmbientLightRenderState() {
+	byte a = 0;
+	byte r = 0;
+	byte g = 0;
+	byte b = 0;
+
+	if (_ambientLightOverride) {
+		a = RGBCOLGetA(_ambientLightColor);
+		r = RGBCOLGetR(_ambientLightColor);
+		g = RGBCOLGetG(_ambientLightColor);
+		b = RGBCOLGetB(_ambientLightColor);
+	} else {
+		uint32 color = _game->getAmbientLightColor();
+
+		a = RGBCOLGetA(color);
+		r = RGBCOLGetR(color);
+		g = RGBCOLGetG(color);
+		b = RGBCOLGetB(color);
+	}
+
+	float value[] = { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
+	tglLightModelfv(TGL_LIGHT_MODEL_AMBIENT, value);
+}
+
+bool BaseRenderTinyGL::setupLines() {
+	if (_state != RSTATE_LINES) {
+		_state = RSTATE_LINES;
+
+		float value[] = { 0, 0, 0, 0 };
+
+		tglDisable(TGL_LIGHTING);
+		tglDisable(TGL_DEPTH_TEST);
+		tglFrontFace(TGL_CW); // WME DX have CCW
+		tglEnable(TGL_CULL_FACE);
+		tglEnable(TGL_BLEND);
+		tglEnable(TGL_ALPHA_TEST);
+		tglLightModelfv(TGL_LIGHT_MODEL_AMBIENT, value);
+
+		tglBindTexture(TGL_TEXTURE_2D, 0);
+		tglDisable(TGL_TEXTURE_2D);
+		_lastTexture = nullptr;
+	}
+
+	return true;
+}
+
+bool BaseRenderTinyGL::drawSpriteEx(BaseSurface *tex, const Common::Rect32 &rect,
+	                            const DXVector2 &pos, const DXVector2 &rot,
+	                            const DXVector2 &scale,
+	                            float angle, uint32 color, bool alphaDisable,
+	                            Graphics::TSpriteBlendMode blendMode,
+	                            bool mirrorX, bool mirrorY) {
+	BaseSurfaceTinyGL *texture = dynamic_cast<BaseSurfaceTinyGL *>(tex);
+	if (!texture)
+		return false;
+
+	if (_forceAlphaColor != 0) {
+		color = _forceAlphaColor;
+	}
+
+	float width = (rect.right - rect.left) * scale._x;
+	float height = (rect.bottom - rect.top) * scale._y;
+
+	float texLeft = (float)rect.left / (float)width;
+	float texTop = (float)rect.top / (float)height;
+	float texRight = (float)rect.right / (float)width;
+	float texBottom = (float)rect.bottom / (float)height;
+
+	if (mirrorX) {
+		SWAP(texLeft, texRight);
+	}
+
+	if (mirrorY) {
+		SWAP(texTop, texBottom);
+	}
+
+	SpriteVertex vertices[4];
+
+	// texture coords
+	vertices[0].u = texLeft;
+	vertices[0].v = texBottom;
+
+	vertices[1].u = texLeft;
+	vertices[1].v = texTop;
+
+	vertices[2].u = texRight;
+	vertices[2].v = texBottom;
+
+	vertices[3].u = texRight;
+	vertices[3].v = texTop;
+
+	// position coords
+	vertices[0].x = pos._x;
+	vertices[0].y = pos._y + height;
+	vertices[0].z = 0.9f;
+
+	vertices[1].x = pos._x;
+	vertices[1].y = pos._y;
+	vertices[1].z = 0.9f;
+
+	vertices[2].x = pos._x + width;
+	vertices[2].y = pos._y + height;
+	vertices[2].z = 0.9f;
+
+	vertices[3].x = pos._x + width;
+	vertices[3].y = pos._y;
+	vertices[3].z = 0.9f;
+
+	if (angle != 0) {
+		DXVector2 sc(1.0f, 1.0f);
+		DXVector2 rotation(rot._x, rot._y);
+		transformVertices(vertices, &rotation, &sc, angle);
+	}
+
+	for (int i = 0; i < 4; i++) {
+		vertices[i].x += _drawOffsetX;
+		vertices[i].y += _drawOffsetY;
+	}
+
+	byte a = RGBCOLGetA(color);
+	byte r = RGBCOLGetR(color);
+	byte g = RGBCOLGetG(color);
+	byte b = RGBCOLGetB(color);
+
+	for (int i = 0; i < 4; ++i) {
+		vertices[i].r = r / 255.0f;
+		vertices[i].g = g / 255.0f;
+		vertices[i].b = b / 255.0f;
+		vertices[i].a = a / 255.0f;
+	}
+
+	setSpriteBlendMode(blendMode);
+	if (alphaDisable) {
+		tglDisable(TGL_ALPHA_TEST);
+		tglDisable(TGL_BLEND);
+	}
+
+	if (_lastTexture != texture) {
+		_lastTexture = texture;
+	}
+
+	tglViewport(0, 0, _width, _height);
+	setProjection2D();
+
+	tglFrontFace(TGL_CW);
+
+	TinyGL::BlitImage *blitImage = texture->getBlitImage();
+	int blitImageWidth, blitImageHeight;
+	tglGetBlitImageSize(blitImage, blitImageWidth, blitImageHeight);
+
+	int posX = _viewportRect.left + vertices[1].x;
+	int posY = _viewportRect.top + vertices[1].y;
+	TinyGL::BlitTransform transform(posX, posY);
+
+	int srcX = (int)(texLeft * width);
+	int srcY = (int)(texTop * height);
+	int srcW = (int)((texRight - texLeft) * width);
+	int srcH = (int)((texBottom - texTop) * height);
+
+	transform.sourceRectangle(srcX, srcY, srcW, srcH);
+	if (scale._x != 1.0f || scale._y != 1.0f) {
+		transform.scale(width, rect.bottom - rect.top);
+	}
+	tglBlit(blitImage, transform);
+
+	if (alphaDisable) {
+		tglEnable(TGL_ALPHA_TEST);
+		tglEnable(TGL_BLEND);
+	}
+
+	return true;
+}
+
+bool BaseRenderTinyGL::commitSpriteBatch() {
+	// nothing to implement
+	return true;
+}
+
+bool BaseRenderTinyGL::startSpriteBatch() {
+	// nothing to implement
+	return true;
+}
+
+bool BaseRenderTinyGL::endSpriteBatch() {
+	// nothing to implement
+	return true;
+}
+
+DXMatrix *BaseRenderTinyGL::buildMatrix(DXMatrix* out, const DXVector2 *centre, const DXVector2 *scaling, float angle) {
+	DXMatrix matrices[5];
+
+	DXMatrixTranslation(&matrices[0], -centre->_x, -centre->_y, 0);
+	DXMatrixScaling(&matrices[1], scaling->_x, scaling->_y, 1);
+	DXMatrixIdentity(&matrices[2]);
+	DXMatrixIdentity(&matrices[3]);
+	DXMatrixRotationZ(&matrices[2], angle);
+	DXMatrixTranslation(&matrices[3], centre->_x, centre->_y, 0);
+
+	matrices[4] = matrices[0] * matrices[1] * matrices[2] * matrices[3];
+	*out = matrices[4];
+
+	return out;
+}
+
+void BaseRenderTinyGL::transformVertices(struct SpriteVertex *vertices, const DXVector2 *centre, const DXVector2 *scaling, float angle) {
+	DXMatrix matTransf, matVerts, matNew;
+
+	buildMatrix(&matTransf, centre, scaling, angle);
+
+	int cr;
+	for (cr = 0; cr < 4; cr++) {
+		matVerts(cr, 0) = vertices[cr].x;
+		matVerts(cr, 1) = vertices[cr].y;
+		matVerts(cr, 2) = vertices[cr].z;
+		matVerts(cr, 3) = 1.0f;
+	}
+
+	matNew = matVerts * matTransf;
+
+	for (cr = 0; cr < 4; cr++) {
+		vertices[cr].x = matNew(cr, 0);
+		vertices[cr].y = matNew(cr, 1);
+		vertices[cr].z = matNew(cr, 2);
+	}
+}
+
+bool BaseRenderTinyGL::setProjection() {
+	DXMatrix matProj;
+
+	float resWidth, resHeight;
+	float layerWidth, layerHeight;
+	float modWidth, modHeight;
+	bool customViewport;
+	getProjectionParams(&resWidth, &resHeight, &layerWidth, &layerHeight, &modWidth, &modHeight, &customViewport);
+
+	Common::Rect32 rc;
+	_game->getCurrentViewportRect(&rc);
+	float viewportWidth = (float)rc.right - (float)rc.left;
+	float viewportHeight = (float)rc.bottom - (float)rc.top;
+
+	// margins
+	int mleft = rc.left;
+	int mright = resWidth - viewportWidth - rc.left;
+	int mtop = rc.top;
+	int mbottom = resHeight - viewportHeight - rc.top;
+
+	DXMatrixPerspectiveFovLH(&matProj, _fov, viewportWidth / viewportHeight, _nearClipPlane, _farClipPlane);
+
+	float scaleMod = resHeight / viewportHeight;
+	float scaleRatio = MAX(layerWidth / resWidth, layerHeight / resHeight) /** 1.05*/;
+
+	float offsetX = (float)_game->_offsetX;
+	float offsetY = (float)_game->_offsetY;
+
+	if (!customViewport) {
+		offsetX -= _drawOffsetX;
+		offsetY -= _drawOffsetY;
+	}
+
+	matProj.matrix._11 *= scaleRatio * scaleMod;
+	matProj.matrix._22 *= scaleRatio * scaleMod;
+	matProj.matrix._31 = -(offsetX + (mleft - mright) / 2 - modWidth) / viewportWidth * 2.0f;
+	matProj.matrix._32 =  (offsetY + (mtop - mbottom) / 2 - modHeight) / viewportHeight * 2.0f;
+
+	return setProjectionTransform(matProj);
+}
+
+bool BaseRenderTinyGL::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
+	setupLines();
+
+	x1 += _drawOffsetX;
+	x2 += _drawOffsetX;
+	y1 += _drawOffsetY;
+	y2 += _drawOffsetY;
+
+	// position coords
+	RectangleVertex vertices[2];
+	vertices[0].x = x1;
+	vertices[0].y = y1;
+	vertices[0].z = 0.9f;
+	vertices[1].x = x2;
+	vertices[1].y = y2;
+	vertices[1].z = 0.9f;
+
+	byte a = RGBCOLGetA(color);
+	byte r = RGBCOLGetR(color);
+	byte g = RGBCOLGetG(color);
+	byte b = RGBCOLGetB(color);
+
+	tglViewport(0, 0, _width, _height);
+	setProjection2D();
+
+	tglColor4ub(r, g, b, a);
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
+
+	tglDrawArrays(TGL_LINES, 0, 2);
+
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+
+	return true;
+}
+
+bool BaseRenderTinyGL::fillRect(int x, int y, int w, int h, uint32 color) {
+	setupLines();
+
+	x += _drawOffsetX;
+	y += _drawOffsetY;
+
+	// position coords
+	RectangleVertex vertices[4];
+	vertices[0].x = x;
+	vertices[0].y = y + h;
+	vertices[0].z = 0.9f;
+	vertices[1].x = x;
+	vertices[1].y = y;
+	vertices[1].z = 0.9f;
+	vertices[2].x = x + w;
+	vertices[2].y = y + h;
+	vertices[2].z = 0.9f;
+	vertices[3].x = x + w;
+	vertices[3].y = y;
+	vertices[3].z = 0.9f;
+
+	byte a = RGBCOLGetA(color);
+	byte r = RGBCOLGetR(color);
+	byte g = RGBCOLGetG(color);
+	byte b = RGBCOLGetB(color);
+
+	tglViewport(0, 0, _width, _height);
+	setProjection2D();
+
+	tglColor4ub(r, g, b, a);
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
+
+	tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
+
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+
+	setup2D();
+	return true;
+}
+
+bool BaseRenderTinyGL::fadeToColor(byte r, byte g, byte b, byte a) {
+	float left, right, bottom, top;
+
+	left = _viewportRect.left;
+	right = _viewportRect.right;
+	bottom = _viewportRect.bottom;
+	top = _viewportRect.top;
+
+	// position coords
+	RectangleVertex vertices[4];
+	vertices[0].x = left;
+	vertices[0].y = bottom;
+	vertices[0].z = 0.0f;
+	vertices[1].x = left;
+	vertices[1].y = top;
+	vertices[1].z = 0.0f;
+	vertices[2].x = right;
+	vertices[2].y = bottom;
+	vertices[2].z = 0.0f;
+	vertices[3].x = right;
+	vertices[3].y = top;
+	vertices[3].z = 0.0f;
+
+
+	tglEnable(TGL_BLEND);
+	setSpriteBlendMode(Graphics::BLEND_NORMAL);
+
+	tglDisable(TGL_DEPTH_TEST);
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+	_lastTexture = nullptr;
+
+	tglViewport(0, 0, _width, _height);
+	setProjection2D();
+
+	tglColor4ub(r, g, b, a);
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
+
+	tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
+
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+
+	setup2D(true);
+
+	return true;
+}
+
+BaseImage *BaseRenderTinyGL::takeScreenshot(int newWidth, int newHeight) {
+	BaseImage *screenshot = new BaseImage();
+	Graphics::Surface *surface = TinyGL::copyFromFrameBuffer(Graphics::PixelFormat::createFormatRGBA32());
+	screenshot->copyFrom(surface, newWidth, newHeight, Graphics::FLIP_V);
+	delete surface;
+	return screenshot;
+}
+
+bool BaseRenderTinyGL::enableShadows() {
+	return true;
+}
+
+bool BaseRenderTinyGL::disableShadows() {
+	return true;
+}
+
+void BaseRenderTinyGL::displaySimpleShadow(BaseObject *object) {
+	if (!_ready || !object)
+		return;
+
+	BaseSurface *shadowImage;
+	if (object->_shadowImage) {
+		shadowImage = object->_shadowImage;
+	} else {
+		shadowImage = _game->_shadowImage;
+	}
+
+	if (!shadowImage) {
+		return;
+	}
+
+	DXMatrix scale, trans, rot, finalm;
+	DXMatrixScaling(&scale, object->_shadowSize * object->_scale3D, 1.0f, object->_shadowSize * object->_scale3D);
+	DXMatrixRotationY(&rot, degToRad(object->_angle));
+	DXMatrixTranslation(&trans, object->_posVector._x, object->_posVector._y, object->_posVector._z);
+	DXMatrixMultiply(&finalm, &scale, &rot);
+	DXMatrixMultiply(&finalm, &finalm, &trans);
+	setWorldTransform(finalm);
+
+	tglFrontFace(TGL_CCW);
+
+	tglDepthMask(TGL_FALSE);
+	tglEnable(TGL_TEXTURE_2D);
+	static_cast<BaseSurfaceTinyGL *>(shadowImage)->setTexture();
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+	tglEnableClientState(TGL_NORMAL_ARRAY);
+	tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].x);
+	tglNormalPointer(TGL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].nx);
+	tglTexCoordPointer(2, TGL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].u);
+
+	tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
+
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+	tglDisableClientState(TGL_NORMAL_ARRAY);
+	tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+
+	tglDisable(TGL_TEXTURE_2D);
+	tglDepthMask(TGL_TRUE);
+}
+
+void BaseRenderTinyGL::setSpriteBlendMode(Graphics::TSpriteBlendMode blendMode, bool forceChange) {
+	if (blendMode == _blendMode && !forceChange)
+		return;
+
+	_blendMode = blendMode;
+
+	switch (_blendMode) {
+	case Graphics::BLEND_NORMAL:
+		tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+		break;
+
+	case Graphics::BLEND_ADDITIVE:
+		tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE);
+		break;
+
+	case Graphics::BLEND_SUBTRACTIVE:
+		tglBlendFunc(TGL_ZERO, TGL_ONE_MINUS_SRC_COLOR);
+		break;
+
+	default:
+		break;
+	}
+}
+
+bool BaseRenderTinyGL::stencilSupported() {
+	// assume that we have a stencil buffer
+	return true;
+}
+
+int BaseRenderTinyGL::getMaxActiveLights() {
+	TGLint maxLightCount = 0;
+	tglGetIntegerv(TGL_MAX_LIGHTS, &maxLightCount);
+	return maxLightCount;
+}
+
+bool BaseRenderTinyGL::invalidateTexture(BaseSurface *texture) {
+	if (_lastTexture == texture)
+		_lastTexture = nullptr;
+
+	return true;
+}
+
+bool BaseRenderTinyGL::invalidateDeviceObjects() {
+	return STATUS_OK;
+}
+
+bool BaseRenderTinyGL::restoreDeviceObjects() {
+	return STATUS_OK;
+}
+
+bool BaseRenderTinyGL::resetDevice() {
+	return STATUS_OK;
+}
+
+// implements D3D LightEnable()
+void BaseRenderTinyGL::lightEnable(int index, bool enable) {
+	if (enable)
+		tglEnable(TGL_LIGHT0 + index);
+	else
+		tglDisable(TGL_LIGHT0 + index);
+}
+
+// backend layer 3DLight::SetLight
+void BaseRenderTinyGL::setLightParameters(int index, const DXVector3 &position, const DXVector3 &direction, const DXVector4 &diffuse, bool spotlight) {
+	float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+	tglLightfv(TGL_LIGHT0 + index, TGL_DIFFUSE, diffuse);
+	tglLightfv(TGL_LIGHT0 + index, TGL_AMBIENT, zero);
+	tglLightfv(TGL_LIGHT0 + index, TGL_SPECULAR, zero);
+
+	_lightPositions[index]._x = position._x;
+	_lightPositions[index]._y = position._y;
+	_lightPositions[index]._z = position._z;
+	_lightPositions[index]._w = 1.0f;
+
+	if (spotlight) {
+		_lightDirections[index] = direction;
+		tglLightfv(TGL_LIGHT0 + index, TGL_SPOT_DIRECTION, direction);
+
+		tglLightf(TGL_LIGHT0 + index, TGL_SPOT_EXPONENT, 0.0f);
+		// WME sets the theta angle to 0.5 (radians) and (28.64789 degree) - inner cone
+		// WME sets the phi angle to 1.0 (radians) and (57.29578 degree) - outer cone
+		// inner cone - angle within which the spotlight has maximum intensity
+		// outer cone - angle at the edge of the spotlight's cone
+		// 0 <-> 28.64789 - maximum light intensity
+		// 28.64789 <-> 57.29578 - light fades smoothly to zero
+		// 57.29578 <-> 90 - there is no light
+		// The smooth transition between inner code and outer cone create soft spotlight
+		// There is no replacement for smooth transition in fixed OpenGL lights.
+		// So, inner cone angle is used instead for better visual match
+		tglLightf(TGL_LIGHT0 + index, TGL_SPOT_CUTOFF, 0.5f * (180.0f / (float)M_PI));
+	} else {
+		tglLightf(TGL_LIGHT0 + index, TGL_SPOT_CUTOFF, 180.0f);
+	}
+}
+
+// backend layer AdSceneGeometry::Render
+void BaseRenderTinyGL::renderSceneGeometry(const BaseArray<AdWalkplane *> &planes, const BaseArray<AdBlock *> &blocks,
+	                                   const BaseArray<AdGeneric *> &generics, const BaseArray<Light3D *> &lights, Camera3D *camera) {
+	DXMatrix matIdentity;
+	DXMatrixIdentity(&matIdentity);
+
+	if (camera)
+		_game->_renderer3D->setup3D(camera, true);
+
+	setWorldTransform(matIdentity);
+
+	tglDisable(TGL_LIGHTING);
+	tglDisable(TGL_DEPTH_TEST);
+	tglFrontFace(TGL_CW); // WME DX have CCW
+	tglEnable(TGL_BLEND);
+	tglDisable(TGL_ALPHA_TEST);
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+
+	tglColorMaterial(TGL_FRONT_AND_BACK, TGL_DIFFUSE);
+	tglEnable(TGL_COLOR_MATERIAL);
+
+
+	// render walk planes
+	for (int32 i = 0; i < planes.getSize(); i++) {
+		if (planes[i]->_active) {
+			planes[i]->_mesh->render(true);
+		}
+	}
+
+	// render blocks
+	for (int32 i = 0; i < blocks.getSize(); i++) {
+		if (blocks[i]->_active) {
+			blocks[i]->_mesh->render(true);
+		}
+	}
+
+	// render generic objects
+	for (int32 i = 0; i < generics.getSize(); i++) {
+		if (generics[i]->_active) {
+			generics[i]->_mesh->render(true);
+		}
+	}
+
+	// render waypoints
+	AdScene *scene = ((AdGame *)_game)->_scene;
+	AdSceneGeometry *geom = scene->_geom;
+	if (geom && geom->_wptMarker) {
+		DXMatrix viewMat, projMat, worldMat;
+		DXVector3 vec2d(0.0f, 0.0f, 0.0f);
+
+		getViewTransform(&viewMat);
+		getProjectionTransform(&projMat);
+		DXMatrixIdentity(&worldMat);
+
+		DXViewport vport = getViewPort();
+
+		setup2D();
+
+		for (int32 i = 0; i < geom->_waypointGroups.getSize(); i++) {
+			for (int32 j = 0; j < geom->_waypointGroups[i]->_points.getSize(); j++) {
+				DXVec3Project(&vec2d, geom->_waypointGroups[i]->_points[j], &vport, &projMat, &viewMat, &worldMat);
+				geom->_wptMarker->display(vec2d._x + scene->getOffsetLeft() - _drawOffsetX, vec2d._y + scene->getOffsetTop() - _drawOffsetY);
+			}
+		}
+	}
+
+	tglDisable(TGL_COLOR_MATERIAL);
+}
+
+// backend layer 3DShadowVolume::Render()
+void BaseRenderTinyGL::renderShadowGeometry(const BaseArray<AdWalkplane *> &planes,
+	                                    const BaseArray<AdBlock *> &blocks,
+	                                    const BaseArray<AdGeneric *> &generics, Camera3D *camera) {
+	DXMatrix matIdentity;
+	DXMatrixIdentity(&matIdentity);
+
+	if (camera)
+		_game->_renderer3D->setup3D(camera, true);
+
+	setWorldTransform(matIdentity);
+
+	// disable color write
+	setSpriteBlendMode(Graphics::BLEND_UNKNOWN);
+	tglBlendFunc(TGL_ZERO, TGL_ONE);
+	tglColorMask(TGL_FALSE, TGL_FALSE, TGL_FALSE, TGL_FALSE);
+
+	// no texture
+	_lastTexture = nullptr;
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+
+	tglFrontFace(TGL_CW); // WME DX have CCW
+
+	// render blocks
+	for (int32 i = 0; i < blocks.getSize(); i++) {
+		if (blocks[i]->_active && blocks[i]->_receiveShadows) {
+			blocks[i]->_mesh->render();
+		}
+	}
+
+	// render walk planes
+	for (int32 i = 0; i < planes.getSize(); i++) {
+		if (planes[i]->_active && planes[i]->_receiveShadows) {
+			planes[i]->_mesh->render();
+		}
+	}
+
+	// render generic objects
+	for (int32 i = 0; i < generics.getSize(); i++) {
+		if (generics[i]->_active && generics[i]->_receiveShadows) {
+			generics[i]->_mesh->render();
+		}
+	}
+
+	setSpriteBlendMode(Graphics::BLEND_NORMAL);
+	tglColorMask(TGL_TRUE, TGL_TRUE, TGL_TRUE, TGL_TRUE);
+}
+
+// implements D3D SetRenderState() D3DRS_CULLMODE - CCW
+void BaseRenderTinyGL::enableCulling() {
+	tglFrontFace(TGL_CW); // WME DX have CCW
+	tglEnable(TGL_CULL_FACE);
+}
+
+// implements D3D SetRenderState() D3DRS_CULLMODE - NONE
+void BaseRenderTinyGL::disableCulling() {
+	tglDisable(TGL_CULL_FACE);
+}
+
+// implements D3D SetViewport() for 2D renderer
+bool BaseRenderTinyGL::setViewport(int left, int top, int right, int bottom) {
+	BasePlatform::setRect(&_viewportRect, left, top, right, bottom);
+	_viewport._x = left;
+	_viewport._y = top;
+	_viewport._width = right - left;
+	_viewport._height = bottom - top;
+	tglViewport(left, top, right - left, bottom - top);
+	return true;
+}
+
+// implements D3D SetViewport() for 3D renderer
+bool BaseRenderTinyGL::setViewport3D(DXViewport *viewport) {
+	_viewport = *viewport;
+	tglViewport(_viewport._x, _height - _viewport._height, _viewport._width, _viewport._height);
+	return true;
+}
+
+bool BaseRenderTinyGL::setProjection2D() {
+	DXMatrix matrix2D;
+	DXMatrixIdentity(&matrix2D);
+	DXMatrixOrthoOffCenterLH(&matrix2D, 0, _width, _height, 0, 0.0f, 1.0f);
+
+	// convert DX [0, 1] depth range to OpenGL [-1, 1] depth range.
+	matrix2D.matrix._33 = 2.0f;
+	matrix2D.matrix._43 = -1.0f;
+
+	tglMatrixMode(TGL_PROJECTION);
+	tglLoadMatrixf(matrix2D);
+
+	tglMatrixMode(TGL_MODELVIEW);
+	tglLoadIdentity();
+
+	return true;
+}
+
+// implements SetTransform() D3DTS_WORLD
+bool BaseRenderTinyGL::setWorldTransform(const DXMatrix &transform) {
+	_worldMatrix = transform;
+	DXMatrix newModelViewTransform, world = transform;
+	DXMatrixMultiply(&newModelViewTransform, &world, &_viewMatrix);
+	tglMatrixMode(TGL_MODELVIEW);
+	tglLoadMatrixf(newModelViewTransform);
+	return true;
+}
+
+// implements SetTransform() D3DTS_WIEW
+bool BaseRenderTinyGL::setViewTransform(const DXMatrix &transform) {
+	_viewMatrix = transform;
+	tglMatrixMode(TGL_MODELVIEW);
+	tglLoadMatrixf(transform);
+	return true;
+}
+
+// implements SetTransform() D3DTS_PROJECTION
+bool BaseRenderTinyGL::setProjectionTransform(const DXMatrix &transform) {
+	_projectionMatrix = transform;
+
+	// convert DX [0, 1] depth range to OpenGL [-1, 1] depth range.
+	DXMatrix finalMatrix = transform;
+	float range = 2.0f / (_farClipPlane - _nearClipPlane);
+	finalMatrix.matrix._33 = range;
+	finalMatrix.matrix._43 = -(_nearClipPlane + _farClipPlane) * range / 2;
+
+	tglMatrixMode(TGL_PROJECTION);
+	tglLoadMatrixf(finalMatrix);
+
+	tglMatrixMode(TGL_MODELVIEW);
+
+	tglViewport(_viewportRect.left, _height - _viewportRect.bottom, _viewportRect.width(), _viewportRect.height());
+
+	return true;
+}
+
+void BaseRenderTinyGL::postfilter() {
+	if (_postFilterMode == kPostFilterOff)
+		return;
+
+	// not supproted under TinyGL
+}
+
+BaseSurface *BaseRenderTinyGL::createSurface() {
+	return new BaseSurfaceTinyGL(_game, this);
+}
+
+Mesh3DS *BaseRenderTinyGL::createMesh3DS() {
+	return new Mesh3DSTinyGL(_game);
+}
+
+XMesh *BaseRenderTinyGL::createXMesh() {
+	return new XMeshTinyGL(_game);
+}
+
+ShadowVolume *BaseRenderTinyGL::createShadowVolume() {
+	return new ShadowVolumeTinyGL(_game);
+}
+
+// ScummVM specific ends <--
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
diff --git a/engines/wintermute/base/gfx/tinygl/base_render_tinygl.h b/engines/wintermute/base/gfx/tinygl/base_render_tinygl.h
new file mode 100644
index 00000000000..748086401a6
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/base_render_tinygl.h
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef WINTERMUTE_BASE_RENDER_TINYGL_H
+#define WINTERMUTE_BASE_RENDER_TINYGL_H
+
+#include "engines/wintermute/base/gfx/base_renderer3d.h"
+#include "engines/wintermute/dctypes.h"
+
+#include "graphics/transform_struct.h"
+#include "graphics/tinygl/tinygl.h"
+
+#if defined(USE_TINYGL)
+
+namespace Wintermute {
+
+class BaseSurfaceTinyGL;
+
+class BaseRenderTinyGL : public BaseRenderer3D {
+	friend class BaseSurfaceTinyGL;
+	friend class Mesh3DSTinyGL;
+	friend class XMeshTinyGL;
+	friend class ShadowVolumeTinyGL;
+
+	struct SpriteVertex {
+		float x;
+		float y;
+		float z;
+		float u;
+		float v;
+		float r;
+		float g;
+		float b;
+		float a;
+	};
+
+	struct RectangleVertex {
+		float x;
+		float y;
+		float z;
+	};
+
+	struct SimpleShadowVertex {
+		float nx;
+		float ny;
+		float nz;
+		float x;
+		float y;
+		float z;
+		float u;
+		float v;
+	};
+
+public:
+	BaseRenderTinyGL(BaseGame *inGame = nullptr);
+	~BaseRenderTinyGL() override;
+
+	bool invalidateTexture(BaseSurface *texture) override;
+
+	bool invalidateDeviceObjects() override;
+	bool restoreDeviceObjects() override;
+
+	bool resetDevice() override;
+
+	void setSpriteBlendMode(Graphics::TSpriteBlendMode blendMode, bool forceChange = false) override;
+
+	void setAmbientLightRenderState() override;
+
+	int getMaxActiveLights() override;
+	void lightEnable(int index, bool enable) override;
+	void setLightParameters(int index, const DXVector3 &position, const DXVector3 &direction, const DXVector4 &diffuse, bool spotlight) override;
+
+	void enableCulling() override;
+	void disableCulling() override;
+
+	bool enableShadows() override;
+	bool disableShadows() override;
+	bool stencilSupported() override;
+
+	BaseImage *takeScreenshot(int newWidth = 0, int newHeight = 0) override;
+	bool fadeToColor(byte r, byte g, byte b, byte a) override;
+
+	bool flip() override;
+	bool clear() override;
+
+	bool setViewport(int left, int top, int right, int bottom) override;
+	bool drawLine(int x1, int y1, int x2, int y2, uint32 color) override;
+	bool fillRect(int x, int y, int w, int h, uint32 color) override;
+
+	DXMatrix *buildMatrix(DXMatrix* out, const DXVector2 *centre, const DXVector2 *scaling, float angle);
+	void transformVertices(struct SpriteVertex *vertices, const DXVector2 *centre, const DXVector2 *scaling, float angle);
+
+	bool setProjection() override;
+	bool setProjection2D();
+	bool setWorldTransform(const DXMatrix &transform) override;
+	bool setViewTransform(const DXMatrix &transform) override;
+	bool setProjectionTransform(const DXMatrix &transform) override;
+
+	bool initRenderer(int width, int height, bool windowed) override;
+	bool setup2D(bool force = false) override;
+	bool setup3D(Camera3D *camera, bool force = false) override;
+
+	Common::String getName() const override {
+		return "TinyGL software renderer";
+	};
+	bool displayDebugInfo() override {
+		return STATUS_FAILED;
+	};
+	bool drawShaderQuad() override {
+		return STATUS_FAILED;
+	}
+	
+	float getScaleRatioX() const override {
+		return 1.0f;
+	}
+	float getScaleRatioY() const override {
+		return 1.0f;
+	}
+	
+	BaseSurface *createSurface() override;
+	
+	bool startSpriteBatch() override;
+	bool endSpriteBatch() override;
+	bool commitSpriteBatch() override;
+
+	bool drawSpriteEx(BaseSurface *texture, const Common::Rect32 &rect, const DXVector2 &pos, const DXVector2 &rot, const DXVector2 &scale,
+	                  float angle, uint32 color, bool alphaDisable, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) override;
+
+	void renderSceneGeometry(const BaseArray<AdWalkplane *> &planes, const BaseArray<AdBlock *> &blocks,
+	                         const BaseArray<AdGeneric *> &generics, const BaseArray<Light3D *> &lights, Camera3D *camera) override;
+	void renderShadowGeometry(const BaseArray<AdWalkplane *> &planes, const BaseArray<AdBlock *> &blocks, const BaseArray<AdGeneric *> &generics, Camera3D *camera) override;
+
+	Mesh3DS *createMesh3DS() override;
+	XMesh *createXMesh() override;
+	ShadowVolume *createShadowVolume() override;
+
+	bool setViewport3D(DXViewport *viewport) override;
+
+	void postfilter() override;
+	void setPostfilter(PostFilter postFilter) override { _postFilterMode = postFilter; };
+
+private:
+	bool setupLines();
+	void displaySimpleShadow(BaseObject *object) override;
+
+	Graphics::TSpriteBlendMode _blendMode;
+	SimpleShadowVertex _simpleShadow[4];
+	Common::Array<DXVector4> _lightPositions;
+	Common::Array<DXVector3> _lightDirections;
+	TGLuint _postfilterTexture;
+};
+
+} // wintermute namespace
+
+#endif // defined(USE_TINYGL)
+
+#endif
diff --git a/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.cpp b/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.cpp
new file mode 100644
index 00000000000..3a4a951d3c2
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.cpp
@@ -0,0 +1,418 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/algorithm.h"
+
+#include "graphics/transform_tools.h"
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+#include "engines/wintermute/base/gfx/base_image.h"
+
+#if defined(USE_TINYGL)
+
+#include "engines/wintermute/base/gfx/3dutils.h"
+#include "engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/base_render_tinygl.h"
+
+namespace Wintermute {
+
+BaseSurfaceTinyGL::BaseSurfaceTinyGL(BaseGame *game, BaseRenderer3D *renderer)
+: BaseSurface(game), _renderer(renderer), _imageData(nullptr), _maskData(nullptr), _pixelOpReady(false), _surfaceModified(false) {
+	_blitImage = tglGenBlitImage();
+}
+
+BaseSurfaceTinyGL::~BaseSurfaceTinyGL() {
+	_renderer->invalidateTexture(this);
+
+	if (_imageData) {
+		_imageData->free();
+		delete _imageData;
+		_imageData = nullptr;
+	}
+
+	if (_maskData) {
+		_maskData->free();
+		delete _maskData;
+		_maskData = nullptr;
+	}
+
+	tglDeleteBlitImage(_blitImage);
+}
+
+bool BaseSurfaceTinyGL::invalidate() {
+	_renderer->invalidateTexture(this);
+
+	if (_imageData) {
+		_imageData->free();
+		delete _imageData;
+		_imageData = nullptr;
+	}
+
+	_valid = false;
+	_surfaceModified = false;
+	return true;
+}
+
+bool BaseSurfaceTinyGL::prepareToDraw() {
+	_lastUsedTime = _game->_liveTimer;
+
+	if (!_valid) {
+		loadImage();
+	}
+
+	return true;
+}
+
+bool BaseSurfaceTinyGL::displayTransZoom(int x, int y, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+	prepareToDraw();
+
+	_renderer->drawSprite(dynamic_cast<BaseSurface *>(this), rect, zoomX, zoomY, DXVector2(x, y), alpha, false, blendMode, mirrorX, mirrorY);
+	return true;
+}
+
+bool BaseSurfaceTinyGL::displayTrans(int x, int y, Common::Rect32 rect, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) {
+	prepareToDraw();
+
+	_renderer->drawSprite(dynamic_cast<BaseSurface *>(this), rect, 100, 100, DXVector2(x + offsetX, y + offsetY), alpha, false, blendMode, mirrorX, mirrorY);
+	return true;
+}
+
+bool BaseSurfaceTinyGL::display(int x, int y, Common::Rect32 rect, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+	prepareToDraw();
+
+	_renderer->drawSprite(dynamic_cast<BaseSurface *>(this), rect, 100, 100, DXVector2(x, y), 0xFFFFFFFF, true, blendMode, mirrorX, mirrorY);
+	return true;
+}
+
+bool BaseSurfaceTinyGL::displayTransRotate(int x, int y, float rotate, int32 hotspotX, int32 hotspotY, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
+	prepareToDraw();
+
+	x -= hotspotX;
+	y -= hotspotY;
+
+	DXVector2 position(x, y);
+	DXVector2 rotation;
+	rotation._x = x + hotspotX * (zoomX / 100.0f);
+	rotation._y = y + hotspotY * (zoomY / 100.0f);
+	DXVector2 scale(zoomX / 100.0f, zoomY / 100.0f);
+	float angle = degToRad(rotate);
+
+	_renderer->drawSpriteEx(dynamic_cast<BaseSurface *>(this), rect, position, rotation, scale, angle, alpha, false, blendMode, mirrorX, mirrorY);
+	return true;
+}
+
+bool BaseSurfaceTinyGL::displayTiled(int x, int y, Common::Rect32 rect, int numTimesX, int numTimesY) {
+	prepareToDraw();
+
+	DXVector2 scale(numTimesX, numTimesY);
+	_renderer->drawSpriteEx(dynamic_cast<BaseSurface *>(this), rect, DXVector2(x, y), DXVector2(0, 0), scale, 0, 0xFFFFFFFF, false, Graphics::BLEND_NORMAL, false, false);
+	return true;
+}
+
+bool BaseSurfaceTinyGL::create(const char *filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
+	if (defaultCK) {
+		ckRed = 255;
+		ckGreen = 0;
+		ckBlue = 255;
+	}
+
+	Common::String surfacefilename = filename;
+	BaseImage img = BaseImage();
+	if (!img.getImageInfo(surfacefilename, _width, _height)) {
+		return false;
+	}
+
+	if (lifeTime != -1 && _lifeTime == 0) {
+		_valid = false;
+	}
+
+	_ckDefault = defaultCK;
+	_ckRed = ckRed;
+	_ckGreen = ckGreen;
+	_ckBlue = ckBlue;
+
+	if (!_filename || scumm_stricmp(_filename, filename) != 0) {
+		setFilename(filename);
+	}
+
+	if (_lifeTime == 0 || lifeTime == -1 || lifeTime > _lifeTime) {
+		_lifeTime = lifeTime;
+	}
+
+	_keepLoaded = keepLoaded;
+	if (_keepLoaded) {
+		_lifeTime = -1;
+	}
+
+	return true;
+}
+
+bool BaseSurfaceTinyGL::loadImage() {
+	if (!_filename || !_filename[0]) {
+		return false;
+	}
+	Common::String filename = _filename;
+
+	BaseImage img = BaseImage();
+	if (!img.loadFile(filename)) {
+		return false;
+	}
+
+	if (img.getSurface()->format.bytesPerPixel == 1 && img.getPalette() == nullptr) {
+		return false;
+	}
+
+	bool needsColorKey = false;
+	bool replaceAlpha = true;
+
+	if (_imageData) {
+		_imageData->free();
+		delete _imageData;
+		_imageData = nullptr;
+	}
+
+	_imageData = img.getSurface()->convertTo(Graphics::PixelFormat::createFormatRGBA32(), img.getPalette(), img.getPaletteCount());
+
+	if (filename.matchString("savegame:*g", true)) {
+		uint8 r, g, b, a;
+		for (int x = 0; x < _imageData->w; x++) {
+			for (int y = 0; y < _imageData->h; y++) {
+				_imageData->format.colorToARGB(_imageData->getPixel(x, y), a, r, g, b);
+				uint8 grey = (uint8)((0.2126f * r + 0.7152f * g + 0.0722f * b) + 0.5f);
+				_imageData->setPixel(x, y, _imageData->format.ARGBToColor(a, grey, grey, grey));
+			}
+		}
+	}
+
+	if (filename.hasSuffix(".bmp")) {
+		// Ignores alpha channel for BMPs
+		needsColorKey = true;
+	} else if (filename.hasSuffix(".jpg")) {
+		// Ignores alpha channel for JPEGs
+		needsColorKey = true;
+	} else if (BaseEngine::instance().getTargetExecutable() < WME_LITE) {
+		// WME 1.x always use colorkey, even for images with transparency
+		needsColorKey = true;
+		replaceAlpha = false;
+	} else if (BaseEngine::instance().isFoxTail()) {
+		// FoxTail does not use colorkey
+		needsColorKey = false;
+	} else if (img.getSurface()->format.aBits() == 0) {
+		// generic WME Lite does not use colorkey for non-BMPs with transparency
+		needsColorKey = true;
+	}
+
+	if (needsColorKey) {
+		// We set the pixel color to transparent black,
+		// like D3DX, if it matches the color key.
+		_imageData->applyColorKey(_ckRed, _ckGreen, _ckBlue, replaceAlpha, 0, 0, 0);
+	}
+
+	// Bug #6572 WME: Rosemary - Sprite flaw on going upwards
+	// Some Rosemary sprites have non-fully transparent pixels
+	// In original WME it wasn't seen because sprites were downscaled
+	// Let's set alpha to 0 if it is smaller then some treshold
+	if (BaseEngine::instance().getGameId() == "rosemary" && filename.hasPrefix("actors") && _imageData->format.bytesPerPixel == 4) {
+		uint32 mask = _imageData->format.ARGBToColor(255, 0, 0, 0);
+		uint32 treshold = _imageData->format.ARGBToColor(16, 0, 0, 0);
+		uint32 blank = _imageData->format.ARGBToColor(0, 0, 0, 0);
+
+		for (int x = 0; x < _imageData->w; x++) {
+			for (int y = 0; y < _imageData->h; y++) {
+				uint32 pixel = _imageData->getPixel(x, y);
+				if ((pixel & mask) > blank && (pixel & mask) < treshold) {
+					_imageData->setPixel(x, y, blank);
+				}
+			}
+		}
+	}
+
+	putSurface(*_imageData);
+
+	/* TODO: Delete _imageData if we no longer need to access the pixel data? */
+
+	_valid = true;
+
+	return true;
+}
+
+bool BaseSurfaceTinyGL::create(int width, int height) {
+	_width = width;
+	_height = height;
+
+	_valid = true;
+	return true;
+}
+
+bool BaseSurfaceTinyGL::putSurface(const Graphics::Surface &surface, bool hasAlpha) {
+	if (!_imageData) {
+		_imageData = new Graphics::Surface();
+	}
+
+	if (_imageData && _imageData != &surface) {
+		_imageData->copyFrom(surface);
+		writeAlpha(_imageData, _maskData);
+	}
+
+	_width = surface.w;
+	_height = surface.h;
+
+	tglUploadBlitImage(_blitImage, *_imageData, 0, false);
+	_valid = true;
+
+	return true;
+}
+
+bool BaseSurfaceTinyGL::putPixel(int x, int y, byte r, byte g, byte b, byte a) {
+	if (!_pixelOpReady) {
+		return false;
+	}
+
+	if (x < 0 || y < 0 || x >= _width || y >= _height) {
+		return false;
+	}
+
+	if (_imageData == nullptr) {
+		return false;
+	}
+
+	_imageData->setPixel(x, y, _imageData->format.ARGBToColor(a, r, g, b));
+
+	_surfaceModified = true;
+
+	return true;
+}
+
+bool BaseSurfaceTinyGL::getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a) const {
+	if (!_pixelOpReady) {
+		return false;
+	}
+
+	if (x < 0 || y < 0 || x >= _width || y >= _height) {
+		return false;
+	}
+
+	if (_imageData == nullptr) {
+		return false;
+	}
+
+	uint8 alpha, red, green, blue;
+	_imageData->format.colorToARGB(_imageData->getPixel(x, y), alpha, red, green, blue);
+	*r = red;
+	*g = green;
+	*b = blue;
+	*a = alpha;
+	return true;
+}
+
+bool BaseSurfaceTinyGL::startPixelOp() {
+	if (!prepareToDraw())
+		return false;
+	_pixelOpReady = true;
+	return true;
+}
+
+bool BaseSurfaceTinyGL::endPixelOp() {
+	_pixelOpReady = false;
+	if (_surfaceModified) {
+		tglUploadBlitImage(_blitImage, *_imageData, 0, false);
+		_surfaceModified = false;
+	}
+	return true;
+}
+
+bool BaseSurfaceTinyGL::isTransparentAtLite(int x, int y) const {
+	if (!_pixelOpReady) {
+		return false;
+	}
+
+	if (x < 0 || y < 0 || x >= _width || y >= _height) {
+		return false;
+	}
+
+	if (_imageData == nullptr) {
+		return false;
+	}
+
+	uint8 a, r, g, b;
+	_imageData->format.colorToARGB(_imageData->getPixel(x, y), a, r, g, b);
+	return a == 0;
+}
+
+void BaseSurfaceTinyGL::setTexture() {
+	prepareToDraw();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool BaseSurfaceTinyGL::setAlphaImage(const char *filename) {
+	BaseImage *alphaImage = new BaseImage();
+	if (!alphaImage->loadFile(filename)) {
+		delete alphaImage;
+		return false;
+	}
+
+	if (_maskData) {
+		_maskData->free();
+		delete _maskData;
+		_maskData = nullptr;
+	}
+
+	Graphics::AlphaType type = alphaImage->getSurface()->detectAlpha();
+	if (type != Graphics::ALPHA_OPAQUE) {
+		_maskData = alphaImage->getSurface()->convertTo(Graphics::PixelFormat::createFormatRGBA32());
+	}
+
+	delete alphaImage;
+
+	return true;
+}
+
+void BaseSurfaceTinyGL::writeAlpha(Graphics::Surface *surface, const Graphics::Surface *mask) {
+	if (mask && surface->w == mask->w && surface->h == mask->h) {
+		assert(mask->pitch == mask->w * 4);
+		assert(mask->format.bytesPerPixel == 4);
+		assert(surface->pitch == surface->w * 4);
+		assert(surface->format.bytesPerPixel == 4);
+		const byte *alphaData = (const byte *)mask->getPixels();
+#ifdef SCUMM_LITTLE_ENDIAN
+		int alphaPlace = (mask->format.aShift / 8);
+#else
+		int alphaPlace = 3 - (mask->format.aShift / 8);
+#endif
+		alphaData += alphaPlace;
+		byte *imgData = (byte *)surface->getPixels();
+#ifdef SCUMM_LITTLE_ENDIAN
+		imgData += (surface->format.aShift / 8);
+#else
+		imgData += 3 - (surface->format.aShift / 8);
+#endif
+		for (int i = 0; i < surface->w * surface->h; i++) {
+			*imgData = *alphaData;
+			alphaData += 4;
+			imgData += 4;
+		}
+	}
+}
+
+} // End of namespace Wintermute
+
+#endif // defined(USE_TINYGL)
diff --git a/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h b/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h
new file mode 100644
index 00000000000..0a47129f317
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef WINTERMUTE_BASE_SURFACE_TINYGL_H
+#define WINTERMUTE_BASE_SURFACE_TINYGL_H
+
+#include "engines/wintermute/base/gfx/base_surface.h"
+
+#if defined(USE_TINYGL)
+
+#include "graphics/tinygl/tinygl.h"
+
+namespace Wintermute {
+
+class BaseGame;
+class BaseRenderer3D;
+
+class BaseSurfaceTinyGL : public BaseSurface {
+public:
+	BaseSurfaceTinyGL(BaseGame *game, BaseRenderer3D *renderer);
+	~BaseSurfaceTinyGL();
+
+	bool invalidate() override;
+	bool prepareToDraw() override;
+
+	bool displayTransRotate(int x, int y, float rotate, int32 hotspotX, int32 hotspotY, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
+	bool displayTransZoom(int x, int y, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
+	bool displayTrans(int x, int y, Common::Rect32 rect, uint32 alpha = 0xFFFFFFFF, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0) override;
+	bool display(int x, int y, Common::Rect32 rect, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
+	bool displayTiled(int x, int y, Common::Rect32 rect, int numTimesX, int numTimesY) override;
+	bool create(const char *filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false) override;
+	bool create(int width, int height) override;
+	bool setAlphaImage(const char *filename) override;
+	bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false) override;
+	bool putPixel(int x, int y, byte r, byte g, byte b, byte a) override;
+	bool getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a = nullptr) const override;
+	bool startPixelOp() override;
+	bool endPixelOp() override;
+	bool isTransparentAtLite(int x, int y) const override;
+
+	void setTexture();
+
+	int getWidth() override {
+		return _width;
+	}
+
+	int getHeight() override {
+		return _height;
+	}
+
+	TinyGL::BlitImage *getBlitImage() {
+		return _blitImage;
+	}
+
+private:
+	BaseRenderer3D *_renderer;
+	Graphics::Surface *_imageData;
+	Graphics::Surface *_maskData;
+	bool _pixelOpReady;
+	bool _surfaceModified;
+	TinyGL::BlitImage *_blitImage;
+
+	bool loadImage();
+	void writeAlpha(Graphics::Surface *surface, const Graphics::Surface *mask);
+};
+
+} // End of namespace Wintermute
+
+#endif // defined(USE_TINYGL)
+
+#endif
diff --git a/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.cpp b/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.cpp
new file mode 100644
index 00000000000..32ed62dd8c9
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.cpp
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "engines/wintermute/wintypes.h"
+
+#include "graphics/opengl/system_headers.h"
+
+#if defined(USE_TINYGL)
+
+#include "engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h"
+
+namespace Wintermute {
+
+Mesh3DSTinyGL::Mesh3DSTinyGL(BaseGame *inGame) : Mesh3DS(inGame) {
+	_vertexCount = 0;
+	_vertexData = nullptr;
+}
+
+Mesh3DSTinyGL::~Mesh3DSTinyGL() {
+}
+
+void Mesh3DSTinyGL::fillVertexBuffer() {
+	_vertexCount = _numFaces * 3;
+	_vertexData = (Mesh3DSVertex *)_vb.ptr();
+}
+
+void Mesh3DSTinyGL::render(bool color) {
+	if (_vertexCount == 0)
+		return;
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+	tglEnableClientState(TGL_COLOR_ARRAY);
+	tglVertexPointer(3, TGL_FLOAT, sizeof(Mesh3DSVertex), &_vertexData[0]._x);
+	tglColorPointer(4, TGL_FLOAT, sizeof(Mesh3DSVertex), &_vertexData[0]._r);
+	tglDrawArrays(TGL_TRIANGLES, 0, _vertexCount);
+	tglDisableClientState(TGL_COLOR_ARRAY);
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+}
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
diff --git a/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h b/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h
new file mode 100644
index 00000000000..0f45e714b79
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/mesh3ds_tinygl.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef WINTERMUTE_MESH_TINYGL_H
+#define WINTERMUTE_MESH_TINYGL_H
+
+#include "engines/wintermute/base/gfx/3dmesh.h"
+
+#if defined(USE_TINYGL)
+
+#include "graphics/tinygl/tinygl.h"
+
+namespace Wintermute {
+
+class Mesh3DSTinyGL : public Mesh3DS {
+public:
+	Mesh3DSTinyGL(BaseGame *inGame);
+	~Mesh3DSTinyGL();
+	void fillVertexBuffer() override;
+	void render(bool color) override;
+
+private:
+	Mesh3DSVertex *_vertexData;
+	uint16 _vertexCount;
+};
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
+
+#endif
diff --git a/engines/wintermute/base/gfx/tinygl/meshx_tinygl.cpp b/engines/wintermute/base/gfx/tinygl/meshx_tinygl.cpp
new file mode 100644
index 00000000000..74f783e383c
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/meshx_tinygl.cpp
@@ -0,0 +1,262 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#include "engines/wintermute/base/gfx/xmaterial.h"
+#include "engines/wintermute/base/gfx/3deffect.h"
+#include "engines/wintermute/base/gfx/3deffect_params.h"
+#include "engines/wintermute/base/gfx/skin_mesh_helper.h"
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/base/base_engine.h"
+
+#if defined(USE_TINYGL)
+
+#include "engines/wintermute/base/gfx/tinygl/base_surface_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/base_render_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/meshx_tinygl.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+XMeshTinyGL::XMeshTinyGL(BaseGame *inGame) : XMesh(inGame) {
+}
+
+//////////////////////////////////////////////////////////////////////////
+XMeshTinyGL::~XMeshTinyGL() {
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool XMeshTinyGL::render(XModel *model) {
+	if (!_blendedMesh)
+		return false;
+
+	// For WME DX, mesh model is not visible, possible it's clipped.
+	// For OpenGL, mesh is visible, skip draw it here instead in core.
+	if (!_game->_renderer3D->_camera)
+		return false;
+
+	auto fvf = _blendedMesh->getFVF();
+	uint32 vertexSize = DXGetFVFVertexSize(fvf) / sizeof(float);
+	float *vertexData = (float *)_blendedMesh->getVertexBuffer().ptr();
+	if (vertexData == nullptr) {
+		return false;
+	}
+	uint32 offset = 0, normalOffset = 0, textureOffset = 0;
+	if (fvf & DXFVF_XYZ) {
+		offset += sizeof(DXVector3) / sizeof(float);
+	}
+	if (fvf & DXFVF_NORMAL) {
+		normalOffset = offset;
+		offset += sizeof(DXVector3) / sizeof(float);
+	}
+	if (fvf & DXFVF_DIFFUSE) {
+		offset += sizeof(DXColorValue) / sizeof(float);
+	}
+	if (fvf & DXFVF_TEX1) {
+		textureOffset = offset;
+	}
+	uint32 *indexData = (uint32 *)_blendedMesh->getIndexBuffer().ptr();
+
+	bool noAttrs = false;
+	auto attrsTable = _blendedMesh->getAttributeTable();
+	uint32 numAttrs = attrsTable->_size;
+	DXAttributeRange *attrs;
+	if (numAttrs == 0) {
+		noAttrs = true;
+		numAttrs = 1;
+		attrs = new DXAttributeRange[numAttrs];
+	} else {
+		attrs = attrsTable->_ptr;
+	}
+
+	if (noAttrs) {
+		attrs[0]._attribId = 0;
+		attrs[0]._vertexStart = attrs[0]._faceStart = 0;
+		attrs[0]._vertexCount = _blendedMesh->getNumVertices();
+		attrs[0]._faceCount = _blendedMesh->getNumFaces();
+	}
+
+	for (uint32 i = 0; i < numAttrs; i++) {
+		Material *mat = _materials[attrs[i]._attribId];
+		bool textureEnable = false;
+		if (mat->getSurface()) {
+			textureEnable = true;
+			tglEnable(TGL_TEXTURE_2D);
+			static_cast<BaseSurfaceTinyGL *>(mat->getSurface())->setTexture();
+			tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
+			tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR_MIPMAP_LINEAR);
+			//tglGenerateMipmap(TGL_TEXTURE_2D);
+		} else {
+			tglBindTexture(TGL_TEXTURE_2D, 0);
+			tglDisable(TGL_TEXTURE_2D);
+		}
+
+		if (mat->getEffect()) {
+			renderEffect(mat);
+		} else {
+			tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, mat->_material._diffuse._data);
+			tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, mat->_material._diffuse._data);
+			tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, mat->_material._specular._data);
+			tglMaterialfv(TGL_FRONT_AND_BACK, TGL_EMISSION, mat->_material._emissive._data);
+			tglMaterialf(TGL_FRONT_AND_BACK, TGL_SHININESS, mat->_material._power);
+		}
+
+		tglEnableClientState(TGL_VERTEX_ARRAY);
+		tglEnableClientState(TGL_NORMAL_ARRAY);
+		if (textureEnable)
+			tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+
+		tglVertexPointer(3, TGL_FLOAT, vertexSize * sizeof(float), vertexData);
+		tglNormalPointer(TGL_FLOAT, vertexSize * sizeof(float), vertexData + normalOffset);
+		if (textureEnable)
+			tglTexCoordPointer(2, TGL_FLOAT, vertexSize * sizeof(float), vertexData + textureOffset);
+
+		tglDrawElements(TGL_TRIANGLES, attrsTable->_ptr[i]._faceCount * 3, TGL_UNSIGNED_INT, indexData + attrsTable->_ptr[i]._faceStart * 3);
+
+		tglDisableClientState(TGL_VERTEX_ARRAY);
+		tglDisableClientState(TGL_NORMAL_ARRAY);
+		tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+	}
+
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+
+	if (noAttrs) {
+		delete[] attrs;
+	}
+
+	return true;
+}
+
+bool XMeshTinyGL::renderFlatShadowModel(uint32 shadowColor) {
+	if (!_blendedMesh)
+		return false;
+
+	// For WME DX, mesh model is not visible, possible it's clipped.
+	// For OpenGL, mesh is visible, skip draw it here instead in core.
+	if (!_game->_renderer3D->_camera)
+		return false;
+
+	// W/A for the scene with the table in the laboratory where the engine switches to flat shadows.
+	// Presumably, it's supposed to disable shadows.
+	// Instead, OpenGL draws graphical glitches.
+	// Original DX version does not have this issue due to rendering shadows differently.
+	if (BaseEngine::instance().getGameId() == "alphapolaris")
+		return false;
+
+	uint32 vertexSize = DXGetFVFVertexSize(_blendedMesh->getFVF()) / sizeof(float);
+	float *vertexData = (float *)_blendedMesh->getVertexBuffer().ptr();
+	if (vertexData == nullptr) {
+		return false;
+	}
+	uint32 *indexData = (uint32 *)_blendedMesh->getIndexBuffer().ptr();
+
+	bool noAttrs = false;
+	auto attrsTable = _blendedMesh->getAttributeTable();
+	uint32 numAttrs = attrsTable->_size;
+	DXAttributeRange *attrs;
+	if (numAttrs == 0) {
+		noAttrs = true;
+		numAttrs = 1;
+		attrs = new DXAttributeRange[numAttrs];
+	} else {
+		attrs = attrsTable->_ptr;
+	}
+
+	if (noAttrs) {
+		attrs[0]._attribId = 0;
+		attrs[0]._vertexStart = attrs[0]._faceStart = 0;
+		attrs[0]._vertexCount = _blendedMesh->getNumVertices();
+		attrs[0]._faceCount = _blendedMesh->getNumFaces();
+	}
+
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+
+	tglDisable(TGL_LIGHTING);
+	tglShadeModel(TGL_FLAT);
+
+	tglColorMask(TGL_FALSE, TGL_FALSE, TGL_FALSE, TGL_FALSE);
+	tglDepthMask(TGL_FALSE);
+
+	tglEnable(TGL_STENCIL_TEST);
+	tglStencilFunc(TGL_ALWAYS, 1, (TGLuint)~0);
+	tglStencilOp(TGL_REPLACE, TGL_REPLACE, TGL_REPLACE);
+
+	for (uint32 i = 0; i < numAttrs; i++) {
+		tglEnableClientState(TGL_VERTEX_ARRAY);
+
+		tglVertexPointer(3, TGL_FLOAT, vertexSize * sizeof(float), vertexData);
+
+		tglDrawElements(TGL_TRIANGLES, attrsTable->_ptr[i]._faceCount * 3, TGL_UNSIGNED_INT, indexData + attrsTable->_ptr[i]._faceStart * 3);
+
+		tglDisableClientState(TGL_VERTEX_ARRAY);
+	}
+
+
+	tglStencilFunc(TGL_EQUAL, 1, (TGLuint)~0);
+	tglStencilOp(TGL_ZERO, TGL_ZERO, TGL_ZERO);
+
+	tglColor4ub(RGBCOLGetR(shadowColor), RGBCOLGetG(shadowColor), RGBCOLGetB(shadowColor), RGBCOLGetA(shadowColor));
+	tglColorMask(TGL_TRUE, TGL_TRUE, TGL_TRUE, TGL_TRUE);
+	tglEnable(TGL_BLEND);
+	tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+
+	tglDepthMask(TGL_TRUE);
+
+	for (uint32 i = 0; i < numAttrs; i++) {
+		tglEnableClientState(TGL_VERTEX_ARRAY);
+
+		tglVertexPointer(3, TGL_FLOAT, vertexSize * sizeof(float), vertexData);
+
+		tglDrawElements(TGL_TRIANGLES, attrsTable->_ptr[i]._faceCount * 3, TGL_UNSIGNED_INT, indexData + attrsTable->_ptr[i]._faceStart * 3);
+
+		tglDisableClientState(TGL_VERTEX_ARRAY);
+	}
+
+	if (noAttrs) {
+		delete[] attrs;
+	}
+
+	tglDisable(TGL_BLEND);
+	tglDisable(TGL_STENCIL_TEST);
+	tglShadeModel(TGL_SMOOTH);
+	tglEnable(TGL_LIGHTING);
+
+	return true;
+}
+
+void XMeshTinyGL::renderEffect(Material *material) {
+	tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, material->_material._diffuse._data);
+	tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, material->_material._diffuse._data);
+	tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, material->_material._specular._data);
+	tglMaterialfv(TGL_FRONT_AND_BACK, TGL_EMISSION, material->_material._emissive._data);
+	tglMaterialf(TGL_FRONT_AND_BACK, TGL_SHININESS, material->_material._power);
+}
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
diff --git a/engines/wintermute/base/gfx/tinygl/meshx_tinygl.h b/engines/wintermute/base/gfx/tinygl/meshx_tinygl.h
new file mode 100644
index 00000000000..b1b15f94c2e
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/meshx_tinygl.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#ifndef WINTERMUTE_XMESH_TINYGL_H
+#define WINTERMUTE_XMESH_TINYGL_H
+
+#include "engines/wintermute/base/gfx/xmesh.h"
+
+class Effect3D;
+class Effect3DParams;
+
+#if defined(USE_TINYGL)
+
+#include "graphics/tinygl/tinygl.h"
+
+namespace Wintermute {
+
+class XMeshTinyGL : public XMesh {
+public:
+	XMeshTinyGL(BaseGame *inGame);
+	~XMeshTinyGL() override;
+
+	bool render(XModel *model) override;
+	bool renderFlatShadowModel(uint32 shadowColor) override;
+
+private:
+	void renderEffect(Material *material);
+};
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
+
+#endif
diff --git a/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.cpp b/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.cpp
new file mode 100644
index 00000000000..51d2a7ea85f
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.cpp
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#include "engines/wintermute/base/base_game.h"
+#include "engines/wintermute/dcgf.h"
+
+#if defined(USE_TINYGL)
+
+#include "engines/wintermute/base/gfx/tinygl/base_render_tinygl.h"
+#include "engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h"
+
+namespace Wintermute {
+
+//////////////////////////////////////////////////////////////////////////
+ShadowVolumeTinyGL::ShadowVolumeTinyGL(BaseGame *inGame) : ShadowVolume(inGame) {
+}
+
+//////////////////////////////////////////////////////////////////////////
+ShadowVolumeTinyGL::~ShadowVolumeTinyGL() {
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ShadowVolumeTinyGL::render() {
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDisable(TGL_TEXTURE_2D);
+	_game->_renderer3D->_lastTexture = nullptr;
+
+	tglColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+	tglVertexPointer(3, TGL_FLOAT, 0, _vertices.getData());
+	tglDrawArrays(TGL_TRIANGLES, 0, _vertices.getSize());
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ShadowVolumeTinyGL::renderToStencilBuffer() {
+	// Disable z-buffer writes (note: z-testing still occurs), and enable the
+	// stencil-buffer
+	tglDepthMask(TGL_FALSE);
+	tglDisable(TGL_TEXTURE_2D);
+	tglDisable(TGL_LIGHTING);
+	tglEnable(TGL_STENCIL_TEST);
+	tglEnable(TGL_CULL_FACE);
+
+	// Set up stencil compare fuction, reference value, and masks.
+	// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
+	// Note: since we set up the stencil-test to always pass, the STENCILFAIL
+	// renderstate is really not needed.
+	tglStencilFunc(TGL_ALWAYS, 0x1, 0xFFFFFFFF);
+
+	tglShadeModel(TGL_FLAT);
+	// Make sure that no pixels get drawn to the frame buffer
+	tglEnable(TGL_BLEND);
+	tglBlendFunc(TGL_ZERO, TGL_ONE);
+
+	tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_INCR);
+
+	// Draw back-side of shadow volume in stencil/z only
+	tglFrontFace(TGL_CCW);
+	render();
+
+	// Decrement stencil buffer value
+	tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_DECR);
+
+	// Draw front-side of shadow volume in stencil/z only
+	tglFrontFace(TGL_CW);
+	render();
+
+	// Restore render states
+	tglEnable(TGL_LIGHTING);
+	tglFrontFace(TGL_CCW);
+	tglShadeModel(TGL_SMOOTH);
+	tglDepthMask(TGL_TRUE);
+	tglDisable(TGL_STENCIL_TEST);
+	tglDisable(TGL_BLEND);
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ShadowVolumeTinyGL::renderToScene() {
+	initMask();
+
+	tglDisable(TGL_DEPTH_TEST);
+	tglEnable(TGL_STENCIL_TEST);
+	tglEnable(TGL_BLEND);
+	tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+
+	// Only write where stencil val >= 1 (count indicates # of shadows that overlap that pixel)
+	tglStencilFunc(TGL_LEQUAL, 0x1, 0xFFFFFFFF);
+	tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_KEEP);
+
+	tglDisable(TGL_FOG);
+	tglDisable(TGL_LIGHTING);
+	tglDisable(TGL_ALPHA_TEST);
+
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+
+	BaseRenderTinyGL *renderer = dynamic_cast<BaseRenderTinyGL *>(_game->_renderer3D);
+	renderer->setProjection2D();
+
+	tglFrontFace(TGL_CW);
+
+	tglEnableClientState(TGL_COLOR_ARRAY);
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+
+	// Draw a big, gray square
+	tglVertexPointer(3, TGL_FLOAT, sizeof(ShadowVertex), &_shadowMask[0].x);
+	tglColorPointer(4, TGL_UNSIGNED_BYTE, sizeof(ShadowVertex), &_shadowMask[0].r);
+
+	tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
+
+	tglDisableClientState(TGL_COLOR_ARRAY);
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+
+	// Restore render states
+	tglEnable(TGL_DEPTH_TEST);
+	tglDisable(TGL_STENCIL_TEST);
+
+	_game->_renderer3D->setup3D(nullptr, true);
+
+	// clear stencil buffer
+	tglClearStencil(0);
+	tglClear(TGL_STENCIL_BUFFER_BIT);
+
+	return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool ShadowVolumeTinyGL::initMask() {
+	auto *rend = _game->_renderer3D;
+
+	// bottom left
+	_shadowMask[0].x = 0.0f;
+	_shadowMask[0].y = rend->getHeight();
+	_shadowMask[0].z = 1.0f;
+
+	// top left
+	_shadowMask[1].x = 0.0f;
+	_shadowMask[1].y = 0.0f;
+	_shadowMask[1].z = 1.0f;
+
+	// bottom right
+	_shadowMask[2].x = rend->getWidth();
+	_shadowMask[2].y = rend->getHeight();
+	_shadowMask[2].z = 1.0f;
+
+	// top right
+	_shadowMask[3].x = rend->getWidth();
+	_shadowMask[3].y = 0.0f;
+	_shadowMask[3].z = 1.0f;
+
+	byte a = RGBCOLGetA(_color);
+	byte r = RGBCOLGetR(_color);
+	byte g = RGBCOLGetG(_color);
+	byte b = RGBCOLGetB(_color);
+
+	for (int i = 0; i < 4; ++i) {
+		_shadowMask[i].r = r;
+		_shadowMask[i].g = g;
+		_shadowMask[i].b = b;
+		_shadowMask[i].a = a;
+	}
+
+	return true;
+}
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
diff --git a/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h b/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h
new file mode 100644
index 00000000000..bed0bb3801c
--- /dev/null
+++ b/engines/wintermute/base/gfx/tinygl/shadow_volume_tinygl.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This file is based on WME.
+ * http://dead-code.org/redir.php?target=wme
+ * Copyright (c) 2003-2013 Jan Nedoma and contributors
+ */
+
+#ifndef WINTERMUTE_SHADOW_VOLUME_TINYGL_H
+#define WINTERMUTE_SHADOW_VOLUME_TINYGL_H
+
+#include "engines/wintermute/base/gfx/3dshadow_volume.h"
+
+#if defined(USE_TINYGL)
+
+#include "graphics/tinygl/tinygl.h"
+
+namespace Wintermute {
+
+class ShadowVolumeTinyGL : public ShadowVolume {
+public:
+	ShadowVolumeTinyGL(BaseGame *inGame);
+	virtual ~ShadowVolumeTinyGL();
+
+	bool renderToStencilBuffer() override;
+	bool renderToScene() override;
+
+private:
+	bool render();
+	ShadowVertex _shadowMask[4]{};
+	bool initMask() override;
+};
+
+} // namespace Wintermute
+
+#endif // defined(USE_TINYGL)
+
+#endif
diff --git a/engines/wintermute/configure.engine b/engines/wintermute/configure.engine
index 132964bfbf4..13d8fd4707a 100644
--- a/engines/wintermute/configure.engine
+++ b/engines/wintermute/configure.engine
@@ -1,6 +1,6 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine wintermute "Wintermute" yes "foxtail herocraft wme3d" "" "16bit highres jpeg png" "theoradec"
+add_engine wintermute "Wintermute" yes "foxtail herocraft wme3d" "" "16bit highres jpeg png" "theoradec tinygl"
 add_engine wme3d "Wintermute3D" yes "" "" "3d"
 add_engine foxtail "FoxTail" yes
 add_engine herocraft "HeroCraft" yes
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index b222cea95eb..44154c6febf 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -193,8 +193,19 @@ MODULE_OBJS += \
 	ext/wme_blackandwhite.o \
 	ext/wme_shadowmanager.o \
 	ext/wme_vlink.o
+
+ifdef USE_TINYGL
+MODULE_OBJS += \
+	base/gfx/tinygl/base_render_tinygl.o \
+	base/gfx/tinygl/base_surface_tinygl.o \
+	base/gfx/tinygl/mesh3ds_tinygl.o \
+	base/gfx/tinygl/meshx_tinygl.o \
+	base/gfx/tinygl/shadow_volume_tinygl.o
 endif
 
+endif
+
+
 MODULE_DIRS += \
 	engines/wintermute
 
diff --git a/graphics/tinygl/zblit_public.h b/graphics/tinygl/zblit_public.h
index c91054162bc..1de6729fc1b 100644
--- a/graphics/tinygl/zblit_public.h
+++ b/graphics/tinygl/zblit_public.h
@@ -48,12 +48,12 @@ struct BlitTransform {
 		_gTint = gTint;
 		_bTint = bTint;
 	}
-/*
+
 	void scale(int width, int height) {
 		_destinationRectangle.setWidth(width);
 		_destinationRectangle.setHeight(height);
 	}
-*/
+
 	void rotate(int rotation, int originX, int originY) {
 		_rotation = rotation;
 		_originX = originX;




More information about the Scummvm-git-logs mailing list