[Scummvm-git-logs] scummvm master -> 565cb7c7cb027b8b886205ba677019e64b558a2a
aquadran
aquadran at gmail.com
Wed Oct 20 20:26:53 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:
565cb7c7cb STARK: Added initial TinyGL renderer
Commit: 565cb7c7cb027b8b886205ba677019e64b558a2a
https://github.com/scummvm/scummvm/commit/565cb7c7cb027b8b886205ba677019e64b558a2a
Author: PaweÅ KoÅodziejski (aquadran at gmail.com)
Date: 2021-10-20T22:26:49+02:00
Commit Message:
STARK: Added initial TinyGL renderer
Changed paths:
A engines/stark/gfx/tinygl.cpp
A engines/stark/gfx/tinygl.h
A engines/stark/gfx/tinyglactor.cpp
A engines/stark/gfx/tinyglactor.h
A engines/stark/gfx/tinyglbitmap.cpp
A engines/stark/gfx/tinyglbitmap.h
A engines/stark/gfx/tinyglfade.cpp
A engines/stark/gfx/tinyglfade.h
A engines/stark/gfx/tinyglprop.cpp
A engines/stark/gfx/tinyglprop.h
A engines/stark/gfx/tinyglsurface.cpp
A engines/stark/gfx/tinyglsurface.h
A engines/stark/gfx/tinygltexture.cpp
A engines/stark/gfx/tinygltexture.h
engines/stark/gfx/driver.cpp
engines/stark/module.mk
engines/stark/stark.cpp
diff --git a/engines/stark/gfx/driver.cpp b/engines/stark/gfx/driver.cpp
index 604d88a687..54d2ba0dc9 100644
--- a/engines/stark/gfx/driver.cpp
+++ b/engines/stark/gfx/driver.cpp
@@ -23,6 +23,7 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/opengl.h"
#include "engines/stark/gfx/opengls.h"
+#include "engines/stark/gfx/tinygl.h"
#include "common/config-manager.h"
@@ -73,7 +74,7 @@ Driver *Driver::create() {
}
#endif
if (matchingRendererType == Graphics::kRendererTypeTinyGL) {
- //driver = CreateTinyGLDriver();
+ //driver = new TinyGLDriver();
error("This game does not currently support software rendering");
}
diff --git a/engines/stark/gfx/tinygl.cpp b/engines/stark/gfx/tinygl.cpp
new file mode 100644
index 0000000000..814d0c8e2b
--- /dev/null
+++ b/engines/stark/gfx/tinygl.cpp
@@ -0,0 +1,184 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinygl.h"
+
+#include "common/system.h"
+#include "common/config-manager.h"
+
+#include "math/matrix4.h"
+
+#include "engines/stark/gfx/tinyglactor.h"
+#include "engines/stark/gfx/tinyglprop.h"
+#include "engines/stark/gfx/tinyglsurface.h"
+#include "engines/stark/gfx/tinyglfade.h"
+#include "engines/stark/gfx/tinygltexture.h"
+#include "engines/stark/gfx/tinyglbitmap.h"
+#include "engines/stark/scene.h"
+#include "engines/stark/services/services.h"
+
+#include "graphics/pixelbuffer.h"
+#include "graphics/surface.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGLDriver::TinyGLDriver() {
+}
+
+TinyGLDriver::~TinyGLDriver() {
+}
+
+void TinyGLDriver::init() {
+ computeScreenViewport();
+
+ _fb = new TinyGL::FrameBuffer(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat());
+ TinyGL::glInit(_fb, 512);
+ //tglEnableDirtyRects(ConfMan.getBool("dirtyrects"));
+ tglEnableDirtyRects(false);
+
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadIdentity();
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadIdentity();
+ tglDisable(TGL_LIGHTING);
+}
+
+void TinyGLDriver::setScreenViewport(bool noScaling) {
+ if (noScaling) {
+ _viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
+ _unscaledViewport = _viewport;
+ } else {
+ _viewport = _screenViewport;
+ _unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
+ }
+
+ tglViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
+}
+
+void TinyGLDriver::setViewport(const Common::Rect &rect) {
+ _viewport = Common::Rect(
+ _screenViewport.width() * rect.width() / kOriginalWidth,
+ _screenViewport.height() * rect.height() / kOriginalHeight
+ );
+
+ _viewport.translate(
+ _screenViewport.left + _screenViewport.width() * rect.left / kOriginalWidth,
+ _screenViewport.top + _screenViewport.height() * rect.top / kOriginalHeight
+ );
+
+ _unscaledViewport = rect;
+
+ tglViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
+}
+
+void TinyGLDriver::clearScreen() {
+ tglClear(TGL_COLOR_BUFFER_BIT | TGL_DEPTH_BUFFER_BIT);
+}
+
+void TinyGLDriver::flipBuffer() {
+ TinyGL::tglPresentBuffer();
+ g_system->copyRectToScreen(_fb->getPixelBuffer(), _fb->linesize, 0, 0, _fb->xsize, _fb->ysize);
+ g_system->updateScreen();
+}
+
+Texture *TinyGLDriver::createTexture(const Graphics::Surface *surface, const byte *palette) {
+ TinyGlTexture *texture = new TinyGlTexture();
+
+ if (surface) {
+ texture->update(surface, palette);
+ }
+
+ return texture;
+}
+
+Texture *TinyGLDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
+ TinyGlBitmap *texture = new TinyGlBitmap();
+
+ if (surface) {
+ texture->update(surface, palette);
+ }
+
+ return texture;
+}
+
+VisualActor *TinyGLDriver::createActorRenderer() {
+ return new TinyGLActorRenderer(this);
+}
+
+VisualProp *TinyGLDriver::createPropRenderer() {
+ return new TinyGLPropRenderer(this);
+}
+
+SurfaceRenderer *TinyGLDriver::createSurfaceRenderer() {
+ return new TinyGLSurfaceRenderer(this);
+}
+
+FadeRenderer *TinyGLDriver::createFadeRenderer() {
+ return new TinyGLFadeRenderer(this);
+}
+
+void TinyGLDriver::start2DMode() {
+ // This blend mode prevents color fringes due to filtering.
+ // It requires the textures to have their color values pre-multiplied
+ // with their alpha value. This is the "Premultiplied Alpha" technique.
+ tglBlendFunc(TGL_ONE, TGL_ONE_MINUS_SRC_ALPHA);
+ tglEnable(TGL_BLEND);
+
+ tglDisable(TGL_DEPTH_TEST);
+ tglDepthMask(TGL_FALSE);
+}
+
+void TinyGLDriver::end2DMode() {
+ tglDisable(TGL_BLEND);
+ tglEnable(TGL_DEPTH_TEST);
+ tglDepthMask(TGL_TRUE);
+}
+
+void TinyGLDriver::set3DMode() {
+ tglEnable(TGL_DEPTH_TEST);
+ tglDepthFunc(TGL_LESS);
+}
+
+bool TinyGLDriver::computeLightsEnabled() {
+ return false;
+}
+
+Common::Rect TinyGLDriver::getViewport() const {
+ return _viewport;
+}
+
+Common::Rect TinyGLDriver::getUnscaledViewport() const {
+ return _unscaledViewport;
+}
+
+Graphics::Surface *TinyGLDriver::getViewportScreenshot() const {
+ Graphics::Surface *s = new Graphics::Surface();
+ s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
+ Graphics::PixelBuffer buf(s->format, (byte *)s->getPixels());
+ _fb->copyToBuffer(buf);
+ flipVertical(s);
+ return s;
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinygl.h b/engines/stark/gfx/tinygl.h
new file mode 100644
index 0000000000..0151fac0fe
--- /dev/null
+++ b/engines/stark/gfx/tinygl.h
@@ -0,0 +1,77 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_H
+#define STARK_GFX_TINYGL_H
+
+#include "common/system.h"
+#include "math/vector3d.h"
+
+#include "engines/stark/gfx/driver.h"
+#include "engines/stark/gfx/renderentry.h"
+
+#include "graphics/tinygl/zgl.h"
+
+namespace Stark {
+namespace Gfx {
+
+class TinyGLDriver : public Driver {
+public:
+ TinyGLDriver();
+ ~TinyGLDriver();
+
+ void init() override;
+
+ void setScreenViewport(bool noScaling) override;
+ void setViewport(const Common::Rect &rect) override;
+
+ void clearScreen() override;
+ void flipBuffer() override;
+
+ Texture *createTexture(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
+ Texture *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
+ VisualActor *createActorRenderer() override;
+ VisualProp *createPropRenderer() override;
+ SurfaceRenderer *createSurfaceRenderer() override;
+ FadeRenderer *createFadeRenderer() override;
+
+ void start2DMode();
+ void end2DMode();
+ void set3DMode() override;
+ bool computeLightsEnabled() override;
+
+ Common::Rect getViewport() const;
+ Common::Rect getUnscaledViewport() const;
+ void setupLights(const LightEntryArray &lights);
+
+ Graphics::Surface *getViewportScreenshot() const override;
+
+private:
+ Common::Rect _viewport;
+ Common::Rect _unscaledViewport;
+ TinyGL::FrameBuffer *_fb;
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_H
diff --git a/engines/stark/gfx/tinyglactor.cpp b/engines/stark/gfx/tinyglactor.cpp
new file mode 100644
index 0000000000..008ded91d9
--- /dev/null
+++ b/engines/stark/gfx/tinyglactor.cpp
@@ -0,0 +1,459 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinyglactor.h"
+
+#include "engines/stark/model/model.h"
+#include "engines/stark/model/animhandler.h"
+#include "engines/stark/scene.h"
+#include "engines/stark/services/services.h"
+#include "engines/stark/services/settings.h"
+#include "engines/stark/gfx/texture.h"
+
+#include "math/vector2d.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGLActorRenderer::TinyGLActorRenderer(TinyGLDriver *gfx) :
+ VisualActor(),
+ _gfx(gfx),
+ _faceVBO(nullptr) {
+}
+
+TinyGLActorRenderer::~TinyGLActorRenderer() {
+ clearVertices();
+}
+
+void TinyGLActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
+ if (_modelIsDirty) {
+ clearVertices();
+ uploadVertices();
+ _modelIsDirty = false;
+ }
+
+ // TODO: Move updates outside of the rendering code
+ _animHandler->animate(_time);
+ _model->updateBoundingBox();
+
+ bool drawShadow = false;
+ if (_castsShadow
+ && StarkScene->shouldRenderShadows()
+ && StarkSettings->getBoolSetting(Settings::kShadow)) {
+ drawShadow = true;
+ }
+
+ Math::Vector3d lightDirection;
+
+ _gfx->set3DMode();
+
+ Math::Matrix4 model = getModelMatrix(position, direction);
+ Math::Matrix4 view = StarkScene->getViewMatrix();
+ Math::Matrix4 projection = StarkScene->getProjectionMatrix();
+
+ Math::Matrix4 modelViewMatrix = view * model;
+ modelViewMatrix.transpose(); // TinyGL expects matrices transposed
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadMatrixf(modelViewMatrix.getData());
+
+ Math::Matrix4 projectionMatrix = projection;
+ projectionMatrix.transpose(); // TinyGL expects matrices transposed
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadMatrixf(projectionMatrix.getData());
+
+ Math::Matrix4 normalMatrix;
+ projectionMatrix.transpose();
+ modelViewMatrix.transpose();
+
+ normalMatrix = modelViewMatrix;
+ normalMatrix.invertAffineOrthonormal();
+
+ Math::Matrix4 mvp;
+ if (drawShadow) {
+ mvp = view * model;
+ mvp.transpose();
+ Math::Matrix4 modelInverse = model;
+ modelInverse.inverse();
+ lightDirection = getShadowLightDirection(lights, position, modelInverse.getRotation());
+ }
+
+ tglEnable(TGL_TEXTURE_2D);
+
+ Common::Array<Face *> faces = _model->getFaces();
+ Common::Array<Material *> mats = _model->getMaterials();
+ const Common::Array<BoneNode *> &bones = _model->getBones();
+
+ for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
+ const Material *material = mats[(*face)->materialId];
+ Math::Vector3d color;
+ const Gfx::Texture *tex = resolveTexture(material);
+ auto vertexIndices = _faceEBO[*face];
+ auto numVertexIndices = (*face)->vertexIndices.size();
+ for (uint32 i = 0; i < numVertexIndices; i++) {
+ if (tex) {
+ tex->bind();
+ color = Math::Vector3d(1.0f, 1.0f, 1.0f);
+ } else {
+ tglBindTexture(TGL_TEXTURE_2D, 0);
+ color = Math::Vector3d(material->r, material->g, material->b);
+ }
+ uint32 index = vertexIndices[i];
+ auto vertex = _faceVBO[index];
+ uint32 bone1 = vertex.bone1;
+ uint32 bone2 = vertex.bone2;
+ Math::Vector3d position1 = Math::Vector3d(vertex.pos1x, vertex.pos1y, vertex.pos1z);
+ Math::Vector3d position2 = Math::Vector3d(vertex.pos2x, vertex.pos2y, vertex.pos2z);
+ Math::Vector3d bone1Position = Math::Vector3d(bones[bone1]->_animPos.x(),
+ bones[bone1]->_animPos.y(),
+ bones[bone1]->_animPos.z());
+ Math::Vector3d bone2Position = Math::Vector3d(bones[bone2]->_animPos.x(),
+ bones[bone2]->_animPos.y(),
+ bones[bone2]->_animPos.z());
+ Math::Quaternion bone1Rotation = Math::Quaternion(bones[bone1]->_animRot.x(),
+ bones[bone1]->_animRot.y(),
+ bones[bone1]->_animRot.z(),
+ bones[bone1]->_animRot.w());
+ Math::Quaternion bone2Rotation = Math::Quaternion(bones[bone2]->_animRot.x(),
+ bones[bone2]->_animRot.y(),
+ bones[bone2]->_animRot.z(),
+ bones[bone2]->_animRot.w());
+ float boneWeight = vertex.boneWeight;
+ Math::Vector3d normal = Math::Vector3d(vertex.normalx, vertex.normaly, vertex.normalz);
+
+ // Compute the vertex position in eye-space
+ bone1Rotation.transform(position1);
+ position1 += bone1Position;
+ bone2Rotation.transform(position2);
+ position2 += bone2Position;
+ Math::Vector3d modelPosition = Math::Vector3d::interpolate(position2, position1, boneWeight);
+ vertex.x = modelPosition.x();
+ vertex.y = modelPosition.y();
+ vertex.z = modelPosition.z();
+ Math::Vector4d modelEyePosition;
+ modelEyePosition = modelViewMatrix * Math::Vector4d(modelPosition.x(),
+ modelPosition.y(),
+ modelPosition.z(),
+ 1.0);
+ // Compute the vertex normal in eye-space
+ Math::Vector3d n1 = normal;
+ bone1Rotation.transform(n1);
+ Math::Vector3d n2 = normal;
+ bone2Rotation.transform(n2);
+ Math::Vector3d modelNormal = Math::Vector3d(Math::Vector3d::interpolate(n2, n1, boneWeight)).getNormalized();
+ vertex.nx = modelNormal.x();
+ vertex.ny = modelNormal.y();
+ vertex.nz = modelNormal.z();
+ Math::Vector3d modelEyeNormal;
+ modelEyeNormal = normalMatrix.getRotation() * modelNormal;
+ modelEyeNormal.normalize();
+
+ if (drawShadow) {
+ Math::Vector3d shadowPosition = modelPosition + lightDirection * (-modelPosition.y() / lightDirection.y());
+ vertex.sx = shadowPosition.x();
+ vertex.sy = 0.0f;
+ vertex.sz = shadowPosition.z();
+ }
+
+ static const uint maxLights = 10;
+
+ assert(lights.size() >= 1);
+ assert(lights.size() <= maxLights);
+
+ const LightEntry *ambient = lights[0];
+ assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
+
+ Math::Vector3d lightColor = ambient->color;
+
+ for (uint li = 0; li < lights.size() - 1; li++) {
+ const LightEntry *l = lights[li + 1];
+
+ switch (l->type) {
+ case LightEntry::kPoint: {
+ Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
+
+ float dist = vertexToLight.length();
+ vertexToLight.normalize();
+ float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
+ float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
+ lightColor += l->color * attn * incidence;
+ break;
+ }
+ case LightEntry::kDirectional: {
+ float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
+ lightColor += (l->color * incidence);
+ break;
+ }
+ case LightEntry::kSpot: {
+ Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
+
+ float dist = vertexToLight.length();
+ float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
+
+ vertexToLight.normalize();
+ float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
+
+ float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
+ float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
+
+ lightColor += l->color * attn * incidence * cone;
+ break;
+ }
+ default:
+ break;
+ }
+
+ lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
+ lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
+ lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
+ color = color * lightColor;
+ vertex.r = color.x();
+ vertex.g = color.y();
+ vertex.b = color.z();
+ }
+
+ _faceVBO[index] = vertex;
+ }
+
+ tglEnableClientState(TGL_VERTEX_ARRAY);
+ tglEnableClientState(TGL_COLOR_ARRAY);
+ if (tex)
+ tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+ tglEnableClientState(TGL_NORMAL_ARRAY);
+
+ tglVertexPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].x);
+ if (tex)
+ tglTexCoordPointer(2, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].texS);
+ tglNormalPointer(TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].nx);
+ tglColorPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].r);
+
+ tglDrawElements(TGL_TRIANGLES, numVertexIndices, TGL_UNSIGNED_INT, vertexIndices);
+
+ tglDisableClientState(TGL_VERTEX_ARRAY);
+ tglDisableClientState(TGL_COLOR_ARRAY);
+ tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+ tglDisableClientState(TGL_NORMAL_ARRAY);
+ }
+
+ if (drawShadow) {
+ tglEnable(TGL_BLEND);
+ tglDisable(TGL_TEXTURE_2D);
+
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadMatrixf(projectionMatrix.getData());
+
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadMatrixf(mvp.getData());
+
+ tglColor4f(0.0f, 0.0f, 0.0f, 0.5f);
+
+ for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
+ auto vertexIndices = _faceEBO[*face];
+
+ tglEnableClientState(TGL_VERTEX_ARRAY);
+
+ tglVertexPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].sx);
+
+ tglDrawElements(TGL_TRIANGLES, (*face)->vertexIndices.size(), TGL_UNSIGNED_INT, vertexIndices);
+
+ tglDisableClientState(TGL_VERTEX_ARRAY);
+ }
+
+ tglEnable(TGL_TEXTURE_2D);
+ tglDisable(TGL_BLEND);
+ }
+}
+
+void TinyGLActorRenderer::clearVertices() {
+ delete[] _faceVBO;
+ _faceVBO = nullptr;
+
+ for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
+ delete[] it->_value;
+ }
+
+ _faceEBO.clear();
+}
+
+void TinyGLActorRenderer::uploadVertices() {
+ _faceVBO = createModelVBO(_model);
+
+ Common::Array<Face *> faces = _model->getFaces();
+ for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
+ _faceEBO[*face] = createFaceEBO(*face);
+ }
+}
+
+ActorVertex *TinyGLActorRenderer::createModelVBO(const Model *model) {
+ const Common::Array<VertNode *> &modelVertices = model->getVertices();
+
+ auto vertices = new ActorVertex[modelVertices.size()];
+ // Build a vertex array
+ int i = 0;
+ for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri, i++) {
+ vertices[i].pos1x = (*tri)->_pos1.x();
+ vertices[i].pos1y = (*tri)->_pos1.y();
+ vertices[i].pos1z = (*tri)->_pos1.z();
+ vertices[i].pos2x = (*tri)->_pos2.x();
+ vertices[i].pos2y = (*tri)->_pos2.y();
+ vertices[i].pos2z = (*tri)->_pos2.z();
+ vertices[i].bone1 = (*tri)->_bone1;
+ vertices[i].bone2 = (*tri)->_bone2;
+ vertices[i].boneWeight = (*tri)->_boneWeight;
+ vertices[i].normalx = (*tri)->_normal.x();
+ vertices[i].normaly = (*tri)->_normal.y();
+ vertices[i].normalz = (*tri)->_normal.z();
+ vertices[i].texS = -(*tri)->_texS;
+ vertices[i].texT = (*tri)->_texT;
+ }
+
+ return vertices;
+}
+
+uint32 *TinyGLActorRenderer::createFaceEBO(const Face *face) {
+ auto indices = new uint32[face->vertexIndices.size()];
+ for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
+ indices[index] = face->vertexIndices[index];
+ }
+
+ return indices;
+}
+
+Math::Vector3d TinyGLActorRenderer::getShadowLightDirection(const LightEntryArray &lights,
+ const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot) {
+ Math::Vector3d sumDirection;
+ bool hasLight = false;
+
+ // Compute the contribution from each lights
+ // The ambient light is skipped intentionally
+ for (uint i = 1; i < lights.size(); ++i) {
+ LightEntry *light = lights[i];
+ bool contributes = false;
+
+ Math::Vector3d lightDirection;
+ switch (light->type) {
+ case LightEntry::kPoint:
+ contributes = getPointLightContribution(light, actorPosition, lightDirection);
+ break;
+ case LightEntry::kDirectional:
+ contributes = getDirectionalLightContribution(light, lightDirection);
+ break;
+ case LightEntry::kSpot:
+ contributes = getSpotLightContribution(light, actorPosition, lightDirection);
+ break;
+ case LightEntry::kAmbient:
+ default:
+ break;
+ }
+
+ if (contributes) {
+ sumDirection += lightDirection;
+ hasLight = true;
+ }
+ }
+
+ if (hasLight) {
+ // Clip the horizontal length
+ Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
+ float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
+
+ horizontalProjection.normalize();
+ horizontalProjection *= shadowLength;
+
+ sumDirection.x() = horizontalProjection.getX();
+ sumDirection.y() = horizontalProjection.getY();
+ sumDirection.z() = -1;
+ } else {
+ // Cast from above by default
+ sumDirection.x() = 0;
+ sumDirection.y() = 0;
+ sumDirection.z() = -1;
+ }
+
+ //Transform the direction to the model space and pass to the shader
+ return worldToModelRot * sumDirection;
+}
+
+bool TinyGLActorRenderer::getPointLightContribution(LightEntry *light,
+ const Math::Vector3d &actorPosition, Math::Vector3d &direction, float weight) {
+ float distance = light->position.getDistanceTo(actorPosition);
+
+ if (distance > light->falloffFar) {
+ return false;
+ }
+
+ float factor;
+ if (distance > light->falloffNear) {
+ if (light->falloffFar - light->falloffNear > 1) {
+ factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
+ } else {
+ factor = 0;
+ }
+ } else {
+ factor = 1;
+ }
+
+ float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
+
+ if (factor <= 0 || brightness <= 0) {
+ return false;
+ }
+
+ direction = actorPosition - light->position;
+ direction.normalize();
+ direction *= factor * brightness * weight;
+
+ return true;
+}
+
+bool TinyGLActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
+ float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
+
+ if (brightness <= 0) {
+ return false;
+ }
+
+ direction = light->direction;
+ direction.normalize();
+ direction *= brightness;
+
+ return true;
+}
+
+bool TinyGLActorRenderer::getSpotLightContribution(LightEntry *light,
+ const Math::Vector3d &actorPosition, Math::Vector3d &direction) {
+ Math::Vector3d lightToActor = actorPosition - light->position;
+ lightToActor.normalize();
+
+ float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
+ float cone = (cosAngle - light->innerConeAngle.getCosine()) /
+ MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
+ cone = CLIP(cone, 0.0f, 1.0f);
+
+ if (cone <= 0) {
+ return false;
+ }
+
+ return getPointLightContribution(light, actorPosition, direction, cone);
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinyglactor.h b/engines/stark/gfx/tinyglactor.h
new file mode 100644
index 0000000000..4092ba767c
--- /dev/null
+++ b/engines/stark/gfx/tinyglactor.h
@@ -0,0 +1,106 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_ACTOR_H
+#define STARK_GFX_TINYGL_ACTOR_H
+
+#include "engines/stark/gfx/renderentry.h"
+#include "engines/stark/visual/actor.h"
+#include "engines/stark/gfx/tinygl.h"
+
+#include "graphics/tinygl/zgl.h"
+
+#include "common/hashmap.h"
+#include "common/hash-ptr.h"
+
+namespace Stark {
+namespace Gfx {
+
+class TinyGLDriver;
+
+#include "common/pack-start.h"
+
+struct _ActorVertex {
+ float pos1x;
+ float pos1y;
+ float pos1z;
+ float pos2x;
+ float pos2y;
+ float pos2z;
+ uint32 bone1;
+ uint32 bone2;
+ float boneWeight;
+ float normalx;
+ float normaly;
+ float normalz;
+ float texS;
+ float texT;
+ float x;
+ float y;
+ float z;
+ float nx;
+ float ny;
+ float nz;
+ float sx;
+ float sy;
+ float sz;
+ float r;
+ float g;
+ float b;
+} PACKED_STRUCT;
+typedef _ActorVertex ActorVertex;
+
+#include "common/pack-end.h"
+
+class TinyGLActorRenderer : public VisualActor {
+public:
+ TinyGLActorRenderer(TinyGLDriver *gfx);
+ virtual ~TinyGLActorRenderer();
+
+ void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
+
+protected:
+ typedef Common::HashMap<Face *, uint32 *> FaceBufferMap;
+
+ TinyGLDriver *_gfx;
+
+ ActorVertex *_faceVBO;
+ FaceBufferMap _faceEBO;
+
+ void clearVertices();
+ void uploadVertices();
+ ActorVertex *createModelVBO(const Model *model);
+ uint32 *createFaceEBO(const Face *face);
+ void setLightArrayUniform(const LightEntryArray &lights);
+
+ Math::Vector3d getShadowLightDirection(const LightEntryArray &lights, const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot);
+
+ bool getPointLightContribution(LightEntry *light, const Math::Vector3d &actorPosition,
+ Math::Vector3d &direction, float weight = 1.0f);
+ bool getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction);
+ bool getSpotLightContribution(LightEntry *light, const Math::Vector3d &actorPosition, Math::Vector3d &direction);
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_ACTOR_H
diff --git a/engines/stark/gfx/tinyglbitmap.cpp b/engines/stark/gfx/tinyglbitmap.cpp
new file mode 100644
index 0000000000..e66f068be0
--- /dev/null
+++ b/engines/stark/gfx/tinyglbitmap.cpp
@@ -0,0 +1,78 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinyglbitmap.h"
+
+#include "engines/stark/gfx/driver.h"
+
+#include "graphics/surface.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGlBitmap::TinyGlBitmap() :
+ Texture() {
+ _blitImage = Graphics::tglGenBlitImage();
+}
+
+TinyGlBitmap::~TinyGlBitmap() {
+ tglDeleteBlitImage(_blitImage);
+}
+
+void TinyGlBitmap::bind() const {
+}
+
+void TinyGlBitmap::updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
+ _width = surface->w;
+ _height = surface->h;
+
+ if (surface->format.bytesPerPixel != 4) {
+ // Convert the surface to texture format
+ Graphics::Surface *convertedSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
+ Graphics::tglUploadBlitImage(_blitImage, *convertedSurface, 0, false);
+ convertedSurface->free();
+ delete convertedSurface;
+ } else {
+ assert(surface->format == Driver::getRGBAPixelFormat());
+ Graphics::tglUploadBlitImage(_blitImage, *surface, 0, false);
+ }
+}
+
+void TinyGlBitmap::update(const Graphics::Surface *surface, const byte *palette) {
+ updateLevel(0, surface, palette);
+}
+
+void TinyGlBitmap::setSamplingFilter(Texture::SamplingFilter filter) {
+}
+
+void TinyGlBitmap::setLevelCount(uint32 count) {
+}
+
+void TinyGlBitmap::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
+}
+
+Graphics::BlitImage *TinyGlBitmap::getBlitTexture() const {
+ return _blitImage;
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinyglbitmap.h b/engines/stark/gfx/tinyglbitmap.h
new file mode 100644
index 0000000000..ed722937f5
--- /dev/null
+++ b/engines/stark/gfx/tinyglbitmap.h
@@ -0,0 +1,58 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_BITMAP_H
+#define STARK_GFX_TINYGL_BITMAP_H
+
+#include "engines/stark/gfx/texture.h"
+
+#include "graphics/tinygl/zgl.h"
+
+namespace Stark {
+namespace Gfx {
+
+/**
+ * An TinyGL bitmap wrapper
+ */
+class TinyGlBitmap : public Texture {
+public:
+ TinyGlBitmap();
+ virtual ~TinyGlBitmap();
+
+ // Texture API
+ void bind() const override;
+ Graphics::BlitImage *getBlitTexture() const;
+ void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
+ void setSamplingFilter(SamplingFilter filter) override;
+ void setLevelCount(uint32 count) override;
+ void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) override;
+
+protected:
+ void updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr);
+
+ Graphics::BlitImage *_blitImage;
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_TEXTURE_H
diff --git a/engines/stark/gfx/tinyglfade.cpp b/engines/stark/gfx/tinyglfade.cpp
new file mode 100644
index 0000000000..a4b827d43b
--- /dev/null
+++ b/engines/stark/gfx/tinyglfade.cpp
@@ -0,0 +1,79 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinyglfade.h"
+
+#include "engines/stark/gfx/tinygl.h"
+
+namespace Stark {
+namespace Gfx {
+
+static const TGLfloat fadeVertices[] = {
+ // X Y
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+};
+
+TinyGLFadeRenderer::TinyGLFadeRenderer(TinyGLDriver *gfx) :
+ FadeRenderer(),
+ _gfx(gfx) {
+}
+
+TinyGLFadeRenderer::~TinyGLFadeRenderer() {
+}
+
+void TinyGLFadeRenderer::render(float fadeLevel) {
+ _gfx->start2DMode();
+
+ tglMatrixMode(TGL_PROJECTION);
+ tglPushMatrix();
+ tglLoadIdentity();
+
+ tglMatrixMode(TGL_MODELVIEW);
+ tglPushMatrix();
+ tglLoadIdentity();
+
+ tglDisable(TGL_TEXTURE_2D);
+
+ tglColor4f(0.0f, 0.0f, 0.0f, 1.0f - fadeLevel);
+
+ tglEnableClientState(TGL_VERTEX_ARRAY);
+
+ tglVertexPointer(2, TGL_FLOAT, 2 * sizeof(TGLfloat), &fadeVertices[0]);
+
+ tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
+
+ tglDisableClientState(TGL_VERTEX_ARRAY);
+
+ tglMatrixMode(TGL_MODELVIEW);
+ tglPopMatrix();
+
+ tglMatrixMode(TGL_PROJECTION);
+ tglPopMatrix();
+
+ _gfx->end2DMode();
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinyglfade.h b/engines/stark/gfx/tinyglfade.h
new file mode 100644
index 0000000000..df3b6f21e9
--- /dev/null
+++ b/engines/stark/gfx/tinyglfade.h
@@ -0,0 +1,53 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_FADE_H
+#define STARK_GFX_TINYGL_FADE_H
+
+#include "engines/stark/gfx/faderenderer.h"
+
+#include "graphics/tinygl/zgl.h"
+
+namespace Stark {
+namespace Gfx {
+
+class TinyGLDriver;
+
+/**
+ * An programmable pipeline TinyGL fade screen renderer
+ */
+class TinyGLFadeRenderer : public FadeRenderer {
+public:
+ TinyGLFadeRenderer(TinyGLDriver *gfx);
+ ~TinyGLFadeRenderer();
+
+ // FadeRenderer API
+ void render(float fadeLevel);
+
+private:
+ TinyGLDriver *_gfx;
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_FADE_H
diff --git a/engines/stark/gfx/tinyglprop.cpp b/engines/stark/gfx/tinyglprop.cpp
new file mode 100644
index 0000000000..0067a2ade2
--- /dev/null
+++ b/engines/stark/gfx/tinyglprop.cpp
@@ -0,0 +1,233 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinyglprop.h"
+
+#include "engines/stark/gfx/texture.h"
+#include "engines/stark/formats/biffmesh.h"
+#include "engines/stark/scene.h"
+#include "engines/stark/services/services.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGLPropRenderer::TinyGLPropRenderer(TinyGLDriver *gfx) :
+ VisualProp(),
+ _gfx(gfx),
+ _faceVBO(nullptr),
+ _modelIsDirty(true) {
+}
+
+TinyGLPropRenderer::~TinyGLPropRenderer() {
+ clearVertices();
+}
+
+void TinyGLPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
+ if (_modelIsDirty) {
+ clearVertices();
+ uploadVertices();
+ _modelIsDirty = false;
+ }
+ return;
+ _gfx->set3DMode();
+
+ Math::Matrix4 model = getModelMatrix(position, direction);
+ Math::Matrix4 view = StarkScene->getViewMatrix();
+ Math::Matrix4 projection = StarkScene->getProjectionMatrix();
+
+ Math::Matrix4 modelViewMatrix = view * model;
+ modelViewMatrix.transpose(); // TinyGL expects matrices transposed
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadMatrixf(modelViewMatrix.getData());
+
+ Math::Matrix4 projectionMatrix = projection;
+ projectionMatrix.transpose(); // TinyGL expects matrices transposed
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadMatrixf(projectionMatrix.getData());
+
+ Math::Matrix4 normalMatrix;
+ projectionMatrix.transpose();
+ modelViewMatrix.transpose();
+
+ normalMatrix = modelViewMatrix;
+ normalMatrix.invertAffineOrthonormal();
+
+ const Common::Array<Face> &faces = _model->getFaces();
+ const Common::Array<Material> &materials = _model->getMaterials();
+
+ for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
+ const Material &material = materials[face->materialId];
+ Math::Vector3d color;
+ const Gfx::Texture *tex = _texture->getTexture(material.texture);
+ auto vertexIndices = _faceEBO[face];
+ auto numVertexIndices = (face)->vertexIndices.size();
+ for (uint32 i = 0; i < numVertexIndices; i++) {
+ uint32 index = vertexIndices[i];
+ auto vertex = _faceVBO[index];
+ if (tex) {
+ tex->bind();
+ color = Math::Vector3d(1.0f, 1.0f, 1.0f);
+ if (material.doubleSided) {
+ vertex.texS = vertex.stexS;
+ vertex.texT = 1.0f - vertex.stexT;
+ } else {
+ vertex.texS = 1.0f - vertex.stexS;
+ vertex.texT = 1.0f - vertex.stexT;
+ }
+ } else {
+ tglBindTexture(TGL_TEXTURE_2D, 0);
+ color = Math::Vector3d(material.r, material.g, material.b);
+ }
+
+ Math::Vector4d modelEyePosition = modelViewMatrix * Math::Vector4d(vertex.x, vertex.y, vertex.z, 1.0);
+ Math::Vector3d modelEyeNormal = normalMatrix.getRotation() * Math::Vector3d(vertex.nx, vertex.ny, vertex.nz);
+ modelEyeNormal.normalize();
+
+ static const uint maxLights = 10;
+
+ assert(lights.size() >= 1);
+ assert(lights.size() <= maxLights);
+
+ const LightEntry *ambient = lights[0];
+ assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
+
+ Math::Vector3d lightColor = ambient->color;
+
+ for (uint li = 0; li < lights.size() - 1; li++) {
+ const LightEntry *l = lights[li + 1];
+
+ switch (l->type) {
+ case LightEntry::kPoint: {
+ Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
+
+ float dist = vertexToLight.length();
+ vertexToLight.normalize();
+ float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
+ float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, vertexToLight));
+ lightColor += l->color * attn * incidence;
+ break;
+ }
+ case LightEntry::kDirectional: {
+ float incidence = MAX(0.0f, Math::Vector3d::dotProduct(modelEyeNormal, -l->eyeDirection));
+ lightColor += (l->color * incidence);
+ break;
+ }
+ case LightEntry::kSpot: {
+ Math::Vector3d vertexToLight = l->eyePosition.getXYZ() - modelEyePosition.getXYZ();
+
+ float dist = vertexToLight.length();
+ float attn = CLIP((l->falloffFar - dist) / MAX(0.001f, l->falloffFar - l->falloffNear), 0.0f, 1.0f);
+
+ vertexToLight.normalize();
+ float incidence = MAX(0.0f, modelEyeNormal.dotProduct(vertexToLight));
+
+ float cosAngle = MAX(0.0f, vertexToLight.dotProduct(-l->eyeDirection));
+ float cone = CLIP((cosAngle - l->innerConeAngle.getCosine()) / MAX(0.001f, l->outerConeAngle.getCosine() - l->innerConeAngle.getCosine()), 0.0f, 1.0f);
+
+ lightColor += l->color * attn * incidence * cone;
+ break;
+ }
+ default:
+ break;
+ }
+
+ lightColor.x() = CLIP(lightColor.x(), 0.0f, 1.0f);
+ lightColor.y() = CLIP(lightColor.y(), 0.0f, 1.0f);
+ lightColor.z() = CLIP(lightColor.z(), 0.0f, 1.0f);
+ color = color * lightColor;
+ vertex.r = color.x();
+ vertex.g = color.y();
+ vertex.b = color.z();
+ }
+ _faceVBO[index] = vertex;
+ }
+
+ tglEnableClientState(TGL_VERTEX_ARRAY);
+ tglEnableClientState(TGL_COLOR_ARRAY);
+ if (tex)
+ tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+ tglEnableClientState(TGL_NORMAL_ARRAY);
+
+ tglVertexPointer(3, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].x);
+ if (tex)
+ tglTexCoordPointer(2, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].texS);
+ tglNormalPointer(TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].nx);
+ tglColorPointer(3, TGL_FLOAT, sizeof(PropVertex), &_faceVBO[0].r);
+
+ tglDrawElements(TGL_TRIANGLES, face->vertexIndices.size(), TGL_UNSIGNED_INT, vertexIndices);
+
+ tglDisableClientState(TGL_VERTEX_ARRAY);
+ tglDisableClientState(TGL_COLOR_ARRAY);
+ tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+ tglDisableClientState(TGL_NORMAL_ARRAY);
+ }
+}
+
+void TinyGLPropRenderer::clearVertices() {
+ delete[] _faceVBO;
+ _faceVBO = nullptr;
+
+ for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
+ delete[] it->_value;
+ }
+
+ _faceEBO.clear();
+}
+
+void TinyGLPropRenderer::uploadVertices() {
+ _faceVBO = createFaceVBO();
+
+ const Common::Array<Face> &faces = _model->getFaces();
+ for (Common::Array<Face>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
+ _faceEBO[face] = createFaceEBO(face);
+ }
+}
+
+PropVertex *TinyGLPropRenderer::createFaceVBO() {
+ const Common::Array<Formats::BiffMesh::Vertex> &modelVertices = _model->getVertices();
+ auto vertices = new PropVertex[modelVertices.size()];
+ // Build a vertex array
+ for (uint32 i = 0; i < modelVertices.size(); i++) {
+ vertices[i].x = modelVertices[i].position.x();
+ vertices[i].y = modelVertices[i].position.y();
+ vertices[i].z = modelVertices[i].position.z();
+ vertices[i].nx = modelVertices[i].normal.x();
+ vertices[i].ny = modelVertices[i].normal.y();
+ vertices[i].nz = modelVertices[i].normal.z();
+ vertices[i].stexS = modelVertices[i].texturePosition.x();
+ vertices[i].stexT = modelVertices[i].texturePosition.y();
+ }
+
+ return vertices;
+}
+
+uint32 *TinyGLPropRenderer::createFaceEBO(const Face *face) {
+ auto indices = new uint32[face->vertexIndices.size()];
+ for (uint32 index = 0; index < face->vertexIndices.size(); index++) {
+ indices[index] = face->vertexIndices[index];
+ }
+
+ return indices;
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinyglprop.h b/engines/stark/gfx/tinyglprop.h
new file mode 100644
index 0000000000..2c1324a8d2
--- /dev/null
+++ b/engines/stark/gfx/tinyglprop.h
@@ -0,0 +1,87 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_RENDERED_H
+#define STARK_GFX_TINYGL_RENDERED_H
+
+#include "engines/stark/model/model.h"
+#include "engines/stark/visual/prop.h"
+#include "engines/stark/gfx/tinygl.h"
+
+#include "graphics/tinygl/zgl.h"
+
+#include "common/hashmap.h"
+#include "common/hash-ptr.h"
+
+namespace Stark {
+
+namespace Gfx {
+
+class Driver;
+
+#include "common/pack-start.h"
+
+struct _PropVertex {
+ float x;
+ float y;
+ float z;
+ float nx;
+ float ny;
+ float nz;
+ float stexS;
+ float stexT;
+ float texS;
+ float texT;
+ float r;
+ float g;
+ float b;
+} PACKED_STRUCT;
+typedef _PropVertex PropVertex;
+
+#include "common/pack-end.h"
+
+class TinyGLPropRenderer : public VisualProp {
+public:
+ explicit TinyGLPropRenderer(TinyGLDriver *gfx);
+ ~TinyGLPropRenderer() override;
+
+ void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
+
+protected:
+ typedef Common::HashMap<const Face *, uint32 *> FaceBufferMap;
+
+ TinyGLDriver *_gfx;
+
+ bool _modelIsDirty;
+ PropVertex *_faceVBO;
+ FaceBufferMap _faceEBO;
+
+ void clearVertices();
+ void uploadVertices();
+ PropVertex *createFaceVBO();
+ uint32 *createFaceEBO(const Face *face);
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_S_RENDERED_H
diff --git a/engines/stark/gfx/tinyglsurface.cpp b/engines/stark/gfx/tinyglsurface.cpp
new file mode 100644
index 0000000000..693b9102c6
--- /dev/null
+++ b/engines/stark/gfx/tinyglsurface.cpp
@@ -0,0 +1,81 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinyglsurface.h"
+#include "engines/stark/gfx/tinyglbitmap.h"
+#include "engines/stark/gfx/texture.h"
+
+#include "graphics/tinygl/zblit.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGLSurfaceRenderer::TinyGLSurfaceRenderer(TinyGLDriver *gfx) :
+ SurfaceRenderer(),
+ _gfx(gfx) {
+}
+
+TinyGLSurfaceRenderer::~TinyGLSurfaceRenderer() {
+}
+
+void TinyGLSurfaceRenderer::render(const Texture *texture, const Common::Point &dest) {
+ render(texture, dest, texture->width(), texture->height());
+}
+
+void TinyGLSurfaceRenderer::render(const Texture *texture, const Common::Point &dest, uint width, uint height) {
+ // Destination rectangle with given width and height
+ _gfx->start2DMode();
+
+ Math::Vector2d sizeWH;
+ if (_noScalingOverride) {
+ sizeWH = normalizeCurrentCoordinates(width, height);
+ } else {
+ sizeWH = normalizeOriginalCoordinates(width, height);
+ }
+ auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
+ auto nativeViewport = _gfx->getViewport();
+ auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
+
+ auto blitImage = ((TinyGlBitmap *)const_cast<Texture *>(texture))->getBlitTexture();
+ int blitTextureWidth, blitTextureHeight;
+ Graphics::tglGetBlitImageSize(blitImage, blitTextureWidth, blitTextureHeight);
+ Graphics::BlitTransform transform(viewport.getX() * verOffsetXY.getX(), viewport.getY() * verOffsetXY.getY());
+ transform.sourceRectangle(0, 0, blitTextureWidth, blitTextureHeight);
+ transform.scale(viewport.getX() * sizeWH.getX(), viewport.getY() * sizeWH.getY());
+ transform.tint(1.0, 1.0 - _fadeLevel, 1.0 - _fadeLevel, 1.0 - _fadeLevel);
+ tglBlit(blitImage, transform);
+
+ _gfx->end2DMode();
+}
+
+Math::Vector2d TinyGLSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
+ Common::Rect viewport = _gfx->getUnscaledViewport();
+ return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
+}
+
+Math::Vector2d TinyGLSurfaceRenderer::normalizeCurrentCoordinates(int x, int y) const {
+ Common::Rect viewport = _gfx->getViewport();
+ return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinyglsurface.h b/engines/stark/gfx/tinyglsurface.h
new file mode 100644
index 0000000000..38d9c0e26f
--- /dev/null
+++ b/engines/stark/gfx/tinyglsurface.h
@@ -0,0 +1,62 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_SURFACE_H
+#define STARK_GFX_TINYGL_SURFACE_H
+
+#include "engines/stark/gfx/surfacerenderer.h"
+#include "engines/stark/gfx/tinygl.h"
+
+#include "graphics/tinygl/zgl.h"
+#include "graphics/tinygl/zblit.h"
+
+#include "math/vector2d.h"
+
+namespace Stark {
+namespace Gfx {
+
+class TinyGLDriver;
+class Texture;
+
+/**
+ * An programmable pipeline TinyGL surface renderer
+ */
+class TinyGLSurfaceRenderer : public SurfaceRenderer {
+public:
+ TinyGLSurfaceRenderer(TinyGLDriver *gfx);
+ virtual ~TinyGLSurfaceRenderer();
+
+ // SurfaceRenderer API
+ void render(const Texture *texture, const Common::Point &dest) override;
+ void render(const Texture *texture, const Common::Point &dest, uint width, uint height) override;
+
+private:
+ Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
+ Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
+
+ TinyGLDriver *_gfx;
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_SURFACE_H
diff --git a/engines/stark/gfx/tinygltexture.cpp b/engines/stark/gfx/tinygltexture.cpp
new file mode 100644
index 0000000000..c0d6f17642
--- /dev/null
+++ b/engines/stark/gfx/tinygltexture.cpp
@@ -0,0 +1,108 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/stark/gfx/tinygltexture.h"
+
+#include "engines/stark/gfx/driver.h"
+
+#include "graphics/surface.h"
+
+namespace Stark {
+namespace Gfx {
+
+TinyGlTexture::TinyGlTexture() :
+ Texture(),
+ _id(0),
+ _levelCount(0) {
+ tglGenTextures(1, &_id);
+
+ bind();
+
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_NEAREST);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_NEAREST);
+
+ // NOTE: TinyGL doesn't have issues with white lines so doesn't need use TGL_CLAMP_TO_EDGE
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_REPEAT);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_REPEAT);
+}
+
+TinyGlTexture::~TinyGlTexture() {
+ tglDeleteTextures(1, &_id);
+}
+
+void TinyGlTexture::bind() const {
+ tglBindTexture(TGL_TEXTURE_2D, _id);
+}
+
+void TinyGlTexture::updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
+ if (level == 0) {
+ _width = surface->w;
+ _height = surface->h;
+ }
+
+ if (surface->format.bytesPerPixel != 4) {
+ // Convert the surface to texture format
+ Graphics::Surface *convertedSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
+
+ tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, convertedSurface->w, convertedSurface->h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, (char *)(convertedSurface->getPixels()));
+
+ convertedSurface->free();
+ delete convertedSurface;
+ } else {
+ assert(surface->format == Driver::getRGBAPixelFormat());
+
+ tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, surface->w, surface->h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, const_cast<void *>(surface->getPixels()));
+ }
+}
+
+void TinyGlTexture::update(const Graphics::Surface *surface, const byte *palette) {
+ bind();
+ updateLevel(0, surface, palette);
+}
+
+void TinyGlTexture::setSamplingFilter(Texture::SamplingFilter filter) {
+ assert(_levelCount == 0);
+
+ switch (filter) {
+ case kNearest:
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_NEAREST);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_NEAREST);
+ break;
+ case kLinear:
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
+ break;
+ default:
+ warning("Unhandled sampling filter %d", filter);
+ }
+}
+
+void TinyGlTexture::setLevelCount(uint32 count) {
+ _levelCount = count;
+}
+
+void TinyGlTexture::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
+ assert(level < _levelCount);
+}
+
+} // End of namespace Gfx
+} // End of namespace Stark
diff --git a/engines/stark/gfx/tinygltexture.h b/engines/stark/gfx/tinygltexture.h
new file mode 100644
index 0000000000..387c75339b
--- /dev/null
+++ b/engines/stark/gfx/tinygltexture.h
@@ -0,0 +1,58 @@
+/* ResidualVM - A 3D game interpreter
+ *
+ * ResidualVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the AUTHORS
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef STARK_GFX_TINYGL_TEXTURE_H
+#define STARK_GFX_TINYGL_TEXTURE_H
+
+#include "engines/stark/gfx/texture.h"
+
+#include "graphics/tinygl/zgl.h"
+
+namespace Stark {
+namespace Gfx {
+
+/**
+ * An TinyGL texture wrapper
+ */
+class TinyGlTexture : public Texture {
+public:
+ TinyGlTexture();
+ virtual ~TinyGlTexture();
+
+ // Texture API
+ void bind() const override;
+ void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
+ void setSamplingFilter(SamplingFilter filter) override;
+ void setLevelCount(uint32 count) override;
+ void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) override;
+
+protected:
+ void updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr);
+
+ TGLuint _id;
+ uint32 _levelCount;
+};
+
+} // End of namespace Gfx
+} // End of namespace Stark
+
+#endif // STARK_GFX_TINYGL_TEXTURE_H
diff --git a/engines/stark/module.mk b/engines/stark/module.mk
index 2ce9615d19..a8e44099e6 100644
--- a/engines/stark/module.mk
+++ b/engines/stark/module.mk
@@ -17,6 +17,13 @@ MODULE_OBJS := \
gfx/opengltexture.o \
gfx/renderentry.o \
gfx/surfacerenderer.o \
+ gfx/tinygl.o \
+ gfx/tinyglactor.o \
+ gfx/tinyglbitmap.o \
+ gfx/tinyglfade.o \
+ gfx/tinyglprop.o \
+ gfx/tinyglsurface.o \
+ gfx/tinygltexture.o \
gfx/texture.o \
formats/biff.o \
formats/biffmesh.o \
diff --git a/engines/stark/stark.cpp b/engines/stark/stark.cpp
index 33a527a6f9..c9a3a38049 100644
--- a/engines/stark/stark.cpp
+++ b/engines/stark/stark.cpp
@@ -55,6 +55,7 @@
#include "common/system.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
+#include "graphics/renderer.h"
#include "gui/message.h"
namespace Stark {
@@ -326,10 +327,16 @@ void StarkEngine::checkRecommendedDatafiles() {
}
bool StarkEngine::hasFeature(EngineFeature f) const {
+ // The TinyGL renderer does not support arbitrary resolutions for now
+ Common::String rendererConfig = ConfMan.get("renderer");
+ Graphics::RendererType desiredRendererType = Graphics::parseRendererTypeCode(rendererConfig);
+ Graphics::RendererType matchingRendererType = Graphics::getBestMatchingAvailableRendererType(desiredRendererType);
+ bool softRenderer = matchingRendererType == Graphics::kRendererTypeTinyGL;
+
return
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
- (f == kSupportsArbitraryResolutions) ||
+ (f == kSupportsArbitraryResolutions && !softRenderer) ||
(f == kSupportsReturnToLauncher);
}
More information about the Scummvm-git-logs
mailing list