Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
/* 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 STARK_GFX_BITMAP_H
#define STARK_GFX_BITMAP_H
#include "common/hash-str.h"
namespace Graphics {
struct PixelFormat;
struct Surface;
}
namespace Stark {
namespace Gfx {
/**
* An abstract bitmap
*/
class Bitmap {
public:
Bitmap() : _width(0), _height(0) {}
virtual ~Bitmap() {}
enum SamplingFilter {
kNearest,
kLinear
};
/** Make the texture active */
virtual void bind() const = 0;
/** Define or update the texture pixel data */
virtual void update(const Graphics::Surface *surface, const byte *palette = nullptr) = 0;
/** Set the filter used when sampling the texture */
virtual void setSamplingFilter(SamplingFilter filter) = 0;
/** Get the most ideal pixel format for uploading to a texture */
virtual Graphics::PixelFormat getBestPixelFormat() const = 0;
/** Get the texture width */
uint32 width() const { return _width; }
/** Get the texture height */
uint32 height() const { return _height; }
protected:
uint32 _width;
uint32 _height;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_BITMAP_H

50
engines/stark/gfx/color.h Normal file
View File

@@ -0,0 +1,50 @@
/* 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 STARK_GFX_COLOR_H
#define STARK_GFX_COLOR_H
#include "common/scummsys.h"
namespace Stark {
namespace Gfx {
struct Color {
uint8 r;
uint8 g;
uint8 b;
uint8 a;
Color(uint8 red, uint8 green, uint8 blue, uint8 alpha = 0xFF) :
r(red), g(green), b(blue), a(alpha) {}
bool operator==(const Color &color) const {
return r == color.r &&
g == color.g &&
b == color.b &&
a == color.a;
}
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_COLOR_H

View File

@@ -0,0 +1,168 @@
/* 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/stark/gfx/driver.h"
#include "engines/stark/gfx/opengl.h"
#include "engines/stark/gfx/opengls.h"
#include "engines/stark/gfx/tinygl.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/settings.h"
#include "common/config-manager.h"
#include "graphics/renderer.h"
#include "graphics/surface.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/context.h"
#endif
#include "gui/error.h"
#include "engines/util.h"
namespace Stark {
namespace Gfx {
Driver *Driver::create() {
Common::String rendererConfig = ConfMan.get("renderer");
Graphics::RendererType desiredRendererType = Graphics::Renderer::parseTypeCode(rendererConfig);
Graphics::RendererType matchingRendererType = Graphics::Renderer::getBestMatchingAvailableType(desiredRendererType,
#if defined(USE_OPENGL_GAME)
Graphics::kRendererTypeOpenGL |
#endif
#if defined(USE_OPENGL_SHADERS)
Graphics::kRendererTypeOpenGLShaders |
#endif
#if defined(USE_TINYGL)
Graphics::kRendererTypeTinyGL |
#endif
0);
bool softRenderer = matchingRendererType == Graphics::kRendererTypeTinyGL;
if (!softRenderer) {
initGraphics3d(kOriginalWidth, kOriginalHeight);
} else {
initGraphics(kOriginalWidth, kOriginalHeight, nullptr);
}
#if defined(USE_OPENGL_SHADERS)
if (matchingRendererType == Graphics::kRendererTypeOpenGLShaders) {
return new OpenGLSDriver();
}
#endif
#if defined(USE_OPENGL_GAME)
if (matchingRendererType == Graphics::kRendererTypeOpenGL) {
return new OpenGLDriver();
}
#endif
#if defined(USE_TINYGL)
if (matchingRendererType == Graphics::kRendererTypeTinyGL) {
return new TinyGLDriver();
}
#endif
/* We should never end up here, getBestMatchingRendererType would have failed before */
error("Unable to create a renderer");
}
const Graphics::PixelFormat Driver::getRGBAPixelFormat() {
return Graphics::PixelFormat::createFormatRGBA32();
}
bool Driver::computeScreenViewport() {
int32 screenWidth = g_system->getWidth();
int32 screenHeight = g_system->getHeight();
Common::Rect viewport;
if (g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
// Aspect ratio correction
int32 viewportWidth = MIN<int32>(screenWidth, screenHeight * kOriginalWidth / kOriginalHeight);
int32 viewportHeight = MIN<int32>(screenHeight, screenWidth * kOriginalHeight / kOriginalWidth);
viewport = Common::Rect(viewportWidth, viewportHeight);
// Pillarboxing
viewport.translate((screenWidth - viewportWidth) / 2,
(screenHeight - viewportHeight) / 2);
} else {
// Aspect ratio correction disabled, just stretch
viewport = Common::Rect(screenWidth, screenHeight);
}
if (viewport == _screenViewport) {
return false;
}
_screenViewport = viewport;
return true;
}
Common::Rect Driver::gameViewport() const {
Common::Rect game = Common::Rect(_screenViewport.width(), _screenViewport.height() * kGameViewportHeight / kOriginalHeight);
game.translate(_screenViewport.left, _screenViewport.top + _screenViewport.height() * kTopBorderHeight / kOriginalHeight);
return game;
}
Common::Point Driver::convertCoordinateCurrentToOriginal(const Common::Point &point) const {
// Most of the engine expects 640x480 coordinates
Common::Point scaledPosition = point;
scaledPosition.x -= _screenViewport.left;
scaledPosition.y -= _screenViewport.top;
scaledPosition.x = CLIP<int16>(scaledPosition.x, 0, _screenViewport.width());
scaledPosition.y = CLIP<int16>(scaledPosition.y, 0, _screenViewport.height());
scaledPosition.x *= kOriginalWidth / (float)_screenViewport.width();
scaledPosition.y *= kOriginalHeight / (float)_screenViewport.height();
return scaledPosition;
}
uint Driver::scaleWidthOriginalToCurrent(uint width) const {
return _screenViewport.width() * width / kOriginalWidth;
}
uint Driver::scaleHeightOriginalToCurrent(uint height) const {
return _screenViewport.height() * height / kOriginalHeight;
}
uint Driver::scaleWidthCurrentToOriginal(uint width) const {
return kOriginalWidth * width / _screenViewport.width();
}
uint Driver::scaleHeightCurrentToOriginal(uint height) const {
return kOriginalHeight * height / _screenViewport.height();
}
void Driver::flipVertical(Graphics::Surface *s) {
for (int y = 0; y < s->h / 2; ++y) {
// Flip the lines
byte *line1P = (byte *)s->getBasePtr(0, y);
byte *line2P = (byte *)s->getBasePtr(0, s->h - y - 1);
for (int x = 0; x < s->pitch; ++x)
SWAP(line1P[x], line2P[x]);
}
}
bool Driver::isPosInScreenBounds(const Common::Point &point) const {
return _screenViewport.contains(point);
}
} // End of namespace Gfx
} // End of namespace Stark

165
engines/stark/gfx/driver.h Normal file
View File

@@ -0,0 +1,165 @@
/* 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 STARK_GFX_DRIVER_H
#define STARK_GFX_DRIVER_H
#include "common/rect.h"
#include "graphics/pixelformat.h"
namespace Graphics {
struct Surface;
}
namespace Stark {
class VisualActor;
class VisualProp;
namespace Gfx {
class SurfaceRenderer;
class FadeRenderer;
class Bitmap;
class Texture;
class Driver {
public:
static Driver *create();
virtual ~Driver() {}
virtual void init() = 0;
bool computeScreenViewport();
virtual void setScreenViewport(bool noScaling) = 0; // deprecated
virtual void setViewport(const Common::Rect &rect) = 0;
/** Get the screen viewport in actual resolution */
Common::Rect getScreenViewport() { return _screenViewport; }
Common::Rect gameViewport() const;
virtual void clearScreen() = 0;
virtual void flipBuffer() = 0;
/**
* Create a new texture for 3D
*
* The caller is responsible for freeing it.
*
*/
virtual Texture *createTexture() = 0;
/**
* Create a new bitmap for 2D
*
* The caller is responsible for freeing it.
*
*/
virtual Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) = 0;
/**
* Create a new actor renderer
*
* The caller is responsible for freeing it.
*/
virtual VisualActor *createActorRenderer() = 0;
/**
* Create a new prop renderer
*
* The caller is responsible for freeing it.
*/
virtual VisualProp *createPropRenderer() = 0;
/**
* Create a new surface renderer
*
* The caller is responsible for freeing it.
*/
virtual SurfaceRenderer *createSurfaceRenderer() = 0;
/**
* Create a new fade renderer
*
* The caller is responsible for freeing it.
*/
virtual FadeRenderer *createFadeRenderer() = 0;
/** Checks if a screenpoint coord is within window bounds */
bool isPosInScreenBounds(const Common::Point &point) const;
/** Convert a coordinate from current to original resolution */
Common::Point convertCoordinateCurrentToOriginal(const Common::Point &point) const;
/** Scale a width value from original resolution to current resolution */
uint scaleWidthOriginalToCurrent(uint width) const;
/** Scale a height value from original resolution to current resolution */
uint scaleHeightOriginalToCurrent(uint height) const;
/** Scale a width value from current resolution to original resolution */
uint scaleWidthCurrentToOriginal(uint width) const;
/** Scale a height value from current resolution to original resolution */
uint scaleHeightCurrentToOriginal(uint width) const;
/**
* Textures are expected to be in the RGBA byte order
*
* That is to say bitmaps sent to OpenGL need to have the following layout:
* R G B A R G B A, ...
*
* This method can be used to retrieve what that means in terms
* of pixel format according to the current platform's endianness.
*/
static const Graphics::PixelFormat getRGBAPixelFormat();
/** Grab a screenshot of the currently active viewport as defined by setViewport */
virtual Graphics::Surface *getViewportScreenshot() const = 0;
virtual void set3DMode() = 0;
virtual bool computeLightsEnabled() = 0;
virtual bool supportsModdedAssets() const { return true; }
static const int32 kOriginalWidth = 640;
static const int32 kOriginalHeight = 480;
static const int32 kTopBorderHeight = 36;
static const int32 kGameViewportHeight = 365;
static const int32 kBottomBorderHeight = 79;
static const int32 kGameViewportWidth = 640;
protected:
static void flipVertical(Graphics::Surface *s);
Common::Rect _screenViewport;
bool _computeLights;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_DRIVER_H

View File

@@ -0,0 +1,45 @@
/* 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 STARK_GFX_FADE_RENDERER_H
#define STARK_GFX_FADE_RENDERER_H
namespace Stark {
namespace Gfx {
/**
* A renderer to draw fade screen to the current viewport
*/
class FadeRenderer {
public:
virtual ~FadeRenderer() { }
/**
* Draw the fade screen at the provided fade level
*/
virtual void render(float fadeLevel) = 0;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_FADE_RENDERER_H

View File

@@ -0,0 +1,287 @@
/* 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/stark/gfx/opengl.h"
#include "common/system.h"
#include "math/matrix4.h"
#if defined(USE_OPENGL_GAME)
#include "engines/stark/gfx/openglactor.h"
#include "engines/stark/gfx/openglbitmap.h"
#include "engines/stark/gfx/openglprop.h"
#include "engines/stark/gfx/openglsurface.h"
#include "engines/stark/gfx/openglfade.h"
#include "engines/stark/gfx/opengltexture.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/services.h"
#include "graphics/surface.h"
namespace Stark {
namespace Gfx {
OpenGLDriver::OpenGLDriver() {
_computeLights = true;
}
OpenGLDriver::~OpenGLDriver() {
}
void OpenGLDriver::init() {
computeScreenViewport();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
}
void OpenGLDriver::setScreenViewport(bool noScaling) {
if (noScaling) {
_viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
_unscaledViewport = _viewport;
} else {
_viewport = _screenViewport;
_unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
}
glViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
}
void OpenGLDriver::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;
glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
}
void OpenGLDriver::clearScreen() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void OpenGLDriver::flipBuffer() {
g_system->updateScreen();
}
void OpenGLDriver::setupLights(const LightEntryArray &lights) {
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::Matrix4 viewMatrix = StarkScene->getViewMatrix();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
for (uint i = 0; i < lights.size(); i++) {
const LightEntry *l = lights[i];
GLfloat ambientColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat lightColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat lightDir[] = { 0.0f, 0.0f, -1.0f };
GLfloat cutoff = 180.0f;
GLfloat spotExp = 0.0f;
GLfloat c_attenuation = 1.0f;
GLfloat l_attenuation = 0.0f;
GLfloat q_attenuation = 0.0f;
Math::Vector4d worldPosition;
worldPosition.x() = l->position.x();
worldPosition.y() = l->position.y();
worldPosition.z() = l->position.z();
worldPosition.w() = 1.0;
Math::Vector4d eyePosition = viewMatrix * worldPosition;
Math::Vector3d worldDirection = l->direction;
Math::Vector3d eyeDirection = viewMatrix.getRotation() * worldDirection;
eyeDirection.normalize();
switch (l->type) {
case LightEntry::kPoint:
lightColor[0] = (GLfloat)l->color.x();
lightColor[1] = (GLfloat)l->color.y();
lightColor[2] = (GLfloat)l->color.z();
lightPos[0] = (GLfloat)eyePosition.x();
lightPos[1] = (GLfloat)eyePosition.y();
lightPos[2] = (GLfloat)eyePosition.z();
break;
case LightEntry::kDirectional:
lightColor[0] = (GLfloat)l->color.x();
lightColor[1] = (GLfloat)l->color.y();
lightColor[2] = (GLfloat)l->color.z();
lightPos[0] = (GLfloat)-eyeDirection.x();
lightPos[1] = (GLfloat)-eyeDirection.y();
lightPos[2] = (GLfloat)-eyeDirection.z();
lightPos[3] = 0;
break;
case LightEntry::kSpot:
lightColor[0] = (GLfloat)l->color.x();
lightColor[1] = (GLfloat)l->color.y();
lightColor[2] = (GLfloat)l->color.z();
lightPos[0] = (GLfloat)eyePosition.x();
lightPos[1] = (GLfloat)eyePosition.y();
lightPos[2] = (GLfloat)eyePosition.z();
lightDir[0] = (GLfloat)eyeDirection.x();
lightDir[1] = (GLfloat)eyeDirection.y();
lightDir[2] = (GLfloat)eyeDirection.z();
cutoff = (l->outerConeAngle.getDegrees() + l->innerConeAngle.getDegrees()) / 2.26f;
break;
case LightEntry::kAmbient:
lightColor[0] = (GLfloat)l->color.x();
lightColor[1] = (GLfloat)l->color.y();
lightColor[2] = (GLfloat)l->color.z();
break;
default:
break;
}
glLightfv(GL_LIGHT0 + i, GL_AMBIENT, ambientColor);
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lightColor);
glLightfv(GL_LIGHT0 + i, GL_POSITION, lightPos);
glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, lightDir);
glLightf(GL_LIGHT0 + i, GL_SPOT_EXPONENT, spotExp);
glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, c_attenuation);
glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, l_attenuation);
glLightf(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, q_attenuation);
glEnable(GL_LIGHT0 + i);
}
for (uint i = lights.size() - 1; i < maxLights; i++) {
// Make sure unused lights are disabled
glDisable(GL_LIGHT0 + i + 1);
}
}
Texture *OpenGLDriver::createTexture() {
return new OpenGlTexture();
}
Bitmap *OpenGLDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
OpenGlBitmap *bitmap = new OpenGlBitmap();
if (surface) {
bitmap->update(surface, palette);
}
return bitmap;
}
VisualActor *OpenGLDriver::createActorRenderer() {
return new OpenGLActorRenderer(this);
}
VisualProp *OpenGLDriver::createPropRenderer() {
return new OpenGLPropRenderer(this);
}
SurfaceRenderer *OpenGLDriver::createSurfaceRenderer() {
return new OpenGLSurfaceRenderer(this);
}
FadeRenderer *OpenGLDriver::createFadeRenderer() {
return new OpenGLFadeRenderer(this);
}
void OpenGLDriver::start2DMode() {
// Enable alpha blending
glEnable(GL_BLEND);
//glBlendEquation(GL_FUNC_ADD); // It's the default
// 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.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
if (!_computeLights)
glDisable(GL_LIGHTING);
}
void OpenGLDriver::end2DMode() {
// Disable alpha blending
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
}
void OpenGLDriver::set3DMode() {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
// Blending and stencil test are only used in rendering shadows
// They are manually enabled and disabled there
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_EQUAL, 0, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
if (!_computeLights)
glEnable(GL_LIGHTING);
}
bool OpenGLDriver::computeLightsEnabled() {
return _computeLights;
}
Common::Rect OpenGLDriver::getViewport() const {
return _viewport;
}
Common::Rect OpenGLDriver::getUnscaledViewport() const {
return _unscaledViewport;
}
Graphics::Surface *OpenGLDriver::getViewportScreenshot() const {
Graphics::Surface *s = new Graphics::Surface();
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
glReadPixels(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height(),
GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
flipVertical(s);
return s;
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,79 @@
/* 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 STARK_GFX_OPENGL_H
#define STARK_GFX_OPENGL_H
#include "common/system.h"
#include "math/vector3d.h"
#if defined(USE_OPENGL_GAME)
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/renderentry.h"
#include "graphics/opengl/system_headers.h"
namespace Stark {
namespace Gfx {
class OpenGLDriver : public Driver {
public:
OpenGLDriver();
~OpenGLDriver();
void init() override;
void setScreenViewport(bool noScaling) override;
void setViewport(const Common::Rect &rect) override;
void clearScreen() override;
void flipBuffer() override;
Texture *createTexture() override;
Bitmap *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;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME)
#endif // STARK_GFX_OPENGL_H

View File

@@ -0,0 +1,489 @@
/* 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/stark/gfx/openglactor.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"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
OpenGLActorRenderer::OpenGLActorRenderer(OpenGLDriver *gfx) :
VisualActor(),
_gfx(gfx),
_faceVBO(nullptr) {
}
OpenGLActorRenderer::~OpenGLActorRenderer() {
clearVertices();
}
void OpenGLActorRenderer::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();
if (!_gfx->computeLightsEnabled())
_gfx->setupLights(lights);
Math::Matrix4 model = getModelMatrix(position, direction);
Math::Matrix4 view = StarkScene->getViewMatrix();
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
Math::Matrix4 modelViewMatrix = view * model;
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(modelViewMatrix.getData());
Math::Matrix4 projectionMatrix = projection;
projectionMatrix.transpose(); // OpenGL expects matrices transposed
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projectionMatrix.getData());
Math::Matrix4 normalMatrix;
if (_gfx->computeLightsEnabled()) {
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());
}
Common::Array<Face *> faces = _model->getFaces();
Common::Array<Material *> mats = _model->getMaterials();
const Common::Array<BoneNode *> &bones = _model->getBones();
if (!_gfx->computeLightsEnabled()) {
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
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);
if (tex) {
tex->bind();
glEnable(GL_TEXTURE_2D);
} else {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
auto vertexIndices = _faceEBO[*face];
auto numVertexIndices = (*face)->vertexIndices.size();
for (uint32 i = 0; i < numVertexIndices; i++) {
if (tex) {
if (_gfx->computeLightsEnabled())
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
else
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
} else {
if (_gfx->computeLightsEnabled())
color = Math::Vector3d(material->r, material->g, material->b);
else
glColor4f(material->r, material->g, material->b, 1.0f);
}
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;
if (_gfx->computeLightsEnabled()) {
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;
if (_gfx->computeLightsEnabled()) {
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();
}
if (_gfx->computeLightsEnabled()) {
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();
vertex.a = 1.0f; /* needed for compatibility with OpenGL ES 1.x */
}
_faceVBO[index] = vertex;
}
glEnableClientState(GL_VERTEX_ARRAY);
if (_gfx->computeLightsEnabled())
glEnableClientState(GL_COLOR_ARRAY);
if (tex)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].x);
if (tex)
glTexCoordPointer(2, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].texS);
glNormalPointer(GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].nx);
if (_gfx->computeLightsEnabled())
glColorPointer(4, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].r);
glDrawElements(GL_TRIANGLES, numVertexIndices, GL_UNSIGNED_INT, vertexIndices);
glDisableClientState(GL_VERTEX_ARRAY);
if (_gfx->computeLightsEnabled())
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
if (!_gfx->computeLightsEnabled())
glDisable(GL_COLOR_MATERIAL);
if (drawShadow) {
glEnable(GL_BLEND);
glEnable(GL_STENCIL_TEST);
glDisable(GL_TEXTURE_2D);
if (!_gfx->computeLightsEnabled())
glDisable(GL_LIGHTING);
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].sx);
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, _faceEBO[*face]);
glDisableClientState(GL_VERTEX_ARRAY);
}
if (!_gfx->computeLightsEnabled())
glEnable(GL_LIGHTING);
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
}
}
void OpenGLActorRenderer::clearVertices() {
delete[] _faceVBO;
_faceVBO = nullptr;
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
delete[] it->_value;
}
_faceEBO.clear();
}
void OpenGLActorRenderer::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 *OpenGLActorRenderer::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 *OpenGLActorRenderer::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 OpenGLActorRenderer::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 OpenGLActorRenderer::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 OpenGLActorRenderer::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 OpenGLActorRenderer::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
#endif // defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,106 @@
/* 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 STARK_GFX_OPENGL_ACTOR_H
#define STARK_GFX_OPENGL_ACTOR_H
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/visual/actor.h"
#include "engines/stark/gfx/opengl.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
class OpenGLDriver;
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;
float a;
};
typedef _ActorVertex ActorVertex;
class OpenGLActorRenderer : public VisualActor {
public:
OpenGLActorRenderer(OpenGLDriver *gfx);
virtual ~OpenGLActorRenderer();
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
protected:
typedef Common::HashMap<Face *, uint32 *> FaceBufferMap;
OpenGLDriver *_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 // defined(USE_OPENGL_GAME)
#endif // STARK_GFX_OPENGL_ACTOR_H

View File

@@ -0,0 +1,122 @@
/* 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/stark/gfx/openglbitmap.h"
#include "engines/stark/gfx/driver.h"
#include "graphics/surface.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/context.h"
namespace Stark {
namespace Gfx {
OpenGlBitmap::OpenGlBitmap() :
Bitmap(),
_id(0) {
glGenTextures(1, &_id);
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
OpenGlBitmap::~OpenGlBitmap() {
glDeleteTextures(1, &_id);
}
void OpenGlBitmap::bind() const {
glBindTexture(GL_TEXTURE_2D, _id);
}
void OpenGlBitmap::update(const Graphics::Surface *surface, const byte *palette) {
bind();
const Graphics::Surface *rgbaSurface = surface;
if (surface->format != Driver::getRGBAPixelFormat()) {
// Convert the surface to texture format
rgbaSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
}
_width = rgbaSurface->w;
_height = rgbaSurface->h;
GLfloat s, t;
if (!OpenGLContext.NPOTSupported) {
uint32 texWidth = Common::nextHigher2(rgbaSurface->w);
uint32 texHeight = Common::nextHigher2(rgbaSurface->h);
s = (GLfloat)_width / texWidth;
t = (GLfloat)_height / texHeight;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgbaSurface->w, rgbaSurface->h,
GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
} else {
s = t = 1.f;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rgbaSurface->w, rgbaSurface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
}
_texCoords[0*2+0] = 0.0f;
_texCoords[0*2+1] = 0.0f;
_texCoords[1*2+0] = s;
_texCoords[1*2+1] = 0.0f;
_texCoords[2*2+0] = 0.0f;
_texCoords[2*2+1] = t;
_texCoords[3*2+0] = s;
_texCoords[3*2+1] = t;
if (rgbaSurface != surface) {
const_cast<Graphics::Surface *>(rgbaSurface)->free();
delete rgbaSurface;
}
}
void OpenGlBitmap::setSamplingFilter(Bitmap::SamplingFilter filter) {
switch (filter) {
case kNearest:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case kLinear:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
default:
warning("Unhandled sampling filter %d", filter);
}
}
Graphics::PixelFormat OpenGlBitmap::getBestPixelFormat() const {
return Driver::getRGBAPixelFormat();
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)

View File

@@ -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/>.
*
*/
#ifndef STARK_GFX_OPENGL_BITMAP_H
#define STARK_GFX_OPENGL_BITMAP_H
#include "engines/stark/gfx/bitmap.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
namespace Stark {
namespace Gfx {
/**
* An OpenGL texture wrapper for 2D elements
*/
class OpenGlBitmap : public Bitmap {
public:
OpenGlBitmap();
virtual ~OpenGlBitmap();
// Bitmap API
void bind() const override;
void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
void setSamplingFilter(SamplingFilter filter) override;
Graphics::PixelFormat getBestPixelFormat() const override;
const GLfloat *getTexCoords() const { return _texCoords; }
protected:
GLuint _id;
GLfloat _texCoords[2*4];
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGL_BITMAP_H

View File

@@ -0,0 +1,81 @@
/* 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/stark/gfx/openglfade.h"
#include "engines/stark/gfx/opengl.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
static const GLfloat fadeVertices[] = {
// X Y
-1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
};
OpenGLFadeRenderer::OpenGLFadeRenderer(OpenGLDriver *gfx) :
FadeRenderer(),
_gfx(gfx) {
}
OpenGLFadeRenderer::~OpenGLFadeRenderer() {
}
void OpenGLFadeRenderer::render(float fadeLevel) {
_gfx->start2DMode();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(0.0f, 0.0f, 0.0f, 1.0f - fadeLevel);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), &fadeVertices[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
_gfx->end2DMode();
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,56 @@
/* 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 STARK_GFX_OPENGL_FADE_H
#define STARK_GFX_OPENGL_FADE_H
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_GAME)
#include "engines/stark/gfx/faderenderer.h"
namespace Stark {
namespace Gfx {
class OpenGLDriver;
/**
* A programmable pipeline OpenGL fade screen renderer
*/
class OpenGLFadeRenderer : public FadeRenderer {
public:
OpenGLFadeRenderer(OpenGLDriver *gfx);
~OpenGLFadeRenderer();
// FadeRenderer API
void render(float fadeLevel);
private:
OpenGLDriver *_gfx;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME)
#endif // STARK_GFX_OPENGL_FADE_H

View File

@@ -0,0 +1,267 @@
/* 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/stark/gfx/openglprop.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/formats/biffmesh.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/services.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
OpenGLPropRenderer::OpenGLPropRenderer(OpenGLDriver *gfx) :
VisualProp(),
_gfx(gfx),
_faceVBO(nullptr),
_modelIsDirty(true) {
}
OpenGLPropRenderer::~OpenGLPropRenderer() {
clearVertices();
}
void OpenGLPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
if (_modelIsDirty) {
clearVertices();
uploadVertices();
_modelIsDirty = false;
}
_gfx->set3DMode();
if (!_gfx->computeLightsEnabled())
_gfx->setupLights(lights);
Math::Matrix4 model = getModelMatrix(position, direction);
Math::Matrix4 view = StarkScene->getViewMatrix();
Math::Matrix4 projection = StarkScene->getProjectionMatrix();
Math::Matrix4 modelViewMatrix = view * model;
modelViewMatrix.transpose(); // OpenGL expects matrices transposed
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(modelViewMatrix.getData());
Math::Matrix4 projectionMatrix = projection;
projectionMatrix.transpose(); // OpenGL expects matrices transposed
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projectionMatrix.getData());
Math::Matrix4 normalMatrix;
if (_gfx->computeLightsEnabled()) {
projectionMatrix.transpose();
modelViewMatrix.transpose();
normalMatrix = modelViewMatrix;
normalMatrix.invertAffineOrthonormal();
}
const Common::Array<Face> &faces = _model->getFaces();
const Common::Array<Material> &materials = _model->getMaterials();
if (!_gfx->computeLightsEnabled())
glEnable(GL_COLOR_MATERIAL);
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);
if (tex) {
tex->bind();
glEnable(GL_TEXTURE_2D);
} else {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
auto vertexIndices = _faceEBO[face];
auto numVertexIndices = (face)->vertexIndices.size();
if (!_gfx->computeLightsEnabled()) {
if (material.doubleSided)
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
else
glColorMaterial(GL_FRONT, GL_DIFFUSE);
}
for (uint32 i = 0; i < numVertexIndices; i++) {
uint32 index = vertexIndices[i];
auto vertex = _faceVBO[index];
if (tex) {
if (_gfx->computeLightsEnabled())
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
else
glColor4f(1.0f, 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 {
if (_gfx->computeLightsEnabled())
color = Math::Vector3d(material.r, material.g, material.b);
else
glColor4f(material.r, material.g, material.b, 1.0f);
}
if (_gfx->computeLightsEnabled()) {
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();
vertex.a = 1.0f; /* needed for compatibility with OpenGL ES 1.x */
}
_faceVBO[index] = vertex;
}
glEnableClientState(GL_VERTEX_ARRAY);
if (_gfx->computeLightsEnabled())
glEnableClientState(GL_COLOR_ARRAY);
if (tex)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].x);
if (tex)
glTexCoordPointer(2, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].texS);
glNormalPointer(GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].nx);
if (_gfx->computeLightsEnabled())
glColorPointer(4, GL_FLOAT, sizeof(PropVertex), &_faceVBO[0].r);
glDrawElements(GL_TRIANGLES, face->vertexIndices.size(), GL_UNSIGNED_INT, vertexIndices);
glDisableClientState(GL_VERTEX_ARRAY);
if (_gfx->computeLightsEnabled())
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
if (!_gfx->computeLightsEnabled())
glDisable(GL_COLOR_MATERIAL);
}
void OpenGLPropRenderer::clearVertices() {
delete[] _faceVBO;
_faceVBO = nullptr;
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
delete[] it->_value;
}
_faceEBO.clear();
}
void OpenGLPropRenderer::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 *OpenGLPropRenderer::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 *OpenGLPropRenderer::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
#endif // defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,87 @@
/* 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 STARK_GFX_OPENGL_RENDERED_H
#define STARK_GFX_OPENGL_RENDERED_H
#include "engines/stark/model/model.h"
#include "engines/stark/visual/prop.h"
#include "engines/stark/gfx/opengl.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
class Driver;
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;
float a;
};
typedef _PropVertex PropVertex;
class OpenGLPropRenderer : public VisualProp {
public:
explicit OpenGLPropRenderer(OpenGLDriver *gfx);
~OpenGLPropRenderer() override;
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
protected:
typedef Common::HashMap<const Face *, uint32 *> FaceBufferMap;
OpenGLDriver *_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_OPENGL_S_RENDERED_H
#endif // defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,250 @@
/* 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/stark/gfx/opengls.h"
#include "common/system.h"
#include "math/matrix4.h"
#if defined(USE_OPENGL_SHADERS)
#include "engines/stark/gfx/openglsactor.h"
#include "engines/stark/gfx/openglbitmap.h"
#include "engines/stark/gfx/openglsprop.h"
#include "engines/stark/gfx/openglssurface.h"
#include "engines/stark/gfx/openglsfade.h"
#include "engines/stark/gfx/opengltexture.h"
#include "graphics/surface.h"
#include "graphics/opengl/shader.h"
namespace Stark {
namespace Gfx {
static const GLfloat surfaceVertices[] = {
// XS YT
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
static const GLfloat fadeVertices[] = {
// XS YT
-1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
};
OpenGLSDriver::OpenGLSDriver() :
_surfaceShader(nullptr),
_surfaceFillShader(nullptr),
_actorShader(nullptr),
_fadeShader(nullptr),
_shadowShader(nullptr),
_surfaceVBO(0),
_fadeVBO(0) {
}
OpenGLSDriver::~OpenGLSDriver() {
OpenGL::Shader::freeBuffer(_surfaceVBO);
OpenGL::Shader::freeBuffer(_fadeVBO);
delete _surfaceFillShader;
delete _surfaceShader;
delete _actorShader;
delete _fadeShader;
delete _shadowShader;
}
void OpenGLSDriver::init() {
computeScreenViewport();
static const char* attributes[] = { "position", "texcoord", nullptr };
_surfaceShader = OpenGL::Shader::fromFiles("stark_surface", attributes);
_surfaceVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(surfaceVertices), surfaceVertices);
_surfaceShader->enableVertexAttribute("position", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
_surfaceShader->enableVertexAttribute("texcoord", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
static const char* fillAttributes[] = { "position", nullptr };
_surfaceFillShader = OpenGL::Shader::fromFiles("stark_surface_fill", fillAttributes);
_surfaceFillShader->enableVertexAttribute("position", _surfaceVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
static const char* actorAttributes[] = { "position1", "position2", "bone1", "bone2", "boneWeight", "normal", "texcoord", nullptr };
_actorShader = OpenGL::Shader::fromFiles("stark_actor", actorAttributes);
static const char* shadowAttributes[] = { "position1", "position2", "bone1", "bone2", "boneWeight", nullptr };
_shadowShader = OpenGL::Shader::fromFiles("stark_shadow", shadowAttributes);
static const char* fadeAttributes[] = { "position", nullptr };
_fadeShader = OpenGL::Shader::fromFiles("stark_fade", fadeAttributes);
_fadeVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(fadeVertices), fadeVertices);
_fadeShader->enableVertexAttribute("position", _fadeVBO, 2, GL_FLOAT, GL_TRUE, 2 * sizeof(float), 0);
}
void OpenGLSDriver::setScreenViewport(bool noScaling) {
if (noScaling) {
_viewport = Common::Rect(g_system->getWidth(), g_system->getHeight());
_unscaledViewport = _viewport;
} else {
_viewport = _screenViewport;
_unscaledViewport = Common::Rect(kOriginalWidth, kOriginalHeight);
}
glViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
}
void OpenGLSDriver::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;
glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
}
void OpenGLSDriver::clearScreen() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void OpenGLSDriver::flipBuffer() {
g_system->updateScreen();
}
Texture *OpenGLSDriver::createTexture() {
return new OpenGlTexture();
}
Bitmap *OpenGLSDriver::createBitmap(const Graphics::Surface *surface, const byte *palette) {
OpenGlBitmap *bitmap = new OpenGlBitmap();
if (surface) {
bitmap->update(surface, palette);
}
return bitmap;
}
VisualActor *OpenGLSDriver::createActorRenderer() {
return new OpenGLSActorRenderer(this);
}
VisualProp *OpenGLSDriver::createPropRenderer() {
return new OpenGLSPropRenderer(this);
}
SurfaceRenderer *OpenGLSDriver::createSurfaceRenderer() {
return new OpenGLSSurfaceRenderer(this);
}
FadeRenderer *OpenGLSDriver::createFadeRenderer() {
return new OpenGLSFadeRenderer(this);
}
void OpenGLSDriver::start2DMode() {
// Enable alpha blending
glEnable(GL_BLEND);
//glBlendEquation(GL_FUNC_ADD); // It's the default
// 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.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
}
void OpenGLSDriver::end2DMode() {
// Disable alpha blending
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
}
void OpenGLSDriver::set3DMode() {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
// Blending and stencil test are only used in rendering shadows
// They are manually enabled and disabled there
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_EQUAL, 0, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
}
bool OpenGLSDriver::computeLightsEnabled() {
return false;
}
Common::Rect OpenGLSDriver::getViewport() const {
return _viewport;
}
Common::Rect OpenGLSDriver::getUnscaledViewport() const {
return _unscaledViewport;
}
OpenGL::Shader *OpenGLSDriver::createActorShaderInstance() {
return _actorShader->clone();
}
OpenGL::Shader *OpenGLSDriver::createSurfaceShaderInstance() {
return _surfaceShader->clone();
}
OpenGL::Shader *OpenGLSDriver::createSurfaceFillShaderInstance() {
return _surfaceFillShader->clone();
}
OpenGL::Shader *OpenGLSDriver::createFadeShaderInstance() {
return _fadeShader->clone();
}
OpenGL::Shader *OpenGLSDriver::createShadowShaderInstance() {
return _shadowShader->clone();
}
Graphics::Surface *OpenGLSDriver::getViewportScreenshot() const {
Graphics::Surface *s = new Graphics::Surface();
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
glReadPixels(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height(),
GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());
flipVertical(s);
return s;
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,94 @@
/* 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 STARK_GFX_OPENGLS_H
#define STARK_GFX_OPENGLS_H
#include "common/system.h"
#if defined(USE_OPENGL_SHADERS)
#include "engines/stark/gfx/driver.h"
#include "graphics/opengl/system_headers.h"
namespace OpenGL {
class Shader;
}
namespace Stark {
namespace Gfx {
class OpenGLSDriver : public Driver {
public:
OpenGLSDriver();
~OpenGLSDriver();
void init() override;
void setScreenViewport(bool noScaling) override;
void setViewport(const Common::Rect &rect) override;
void clearScreen() override;
void flipBuffer() override;
Texture *createTexture() override;
Bitmap *createBitmap(const Graphics::Surface *surface = nullptr, const byte *palette = nullptr) override;
VisualActor *createActorRenderer() override;
VisualProp *createPropRenderer() override;
SurfaceRenderer *createSurfaceRenderer() override;
FadeRenderer *createFadeRenderer() override;
OpenGL::Shader *createActorShaderInstance();
OpenGL::Shader *createSurfaceShaderInstance();
OpenGL::Shader *createSurfaceFillShaderInstance();
OpenGL::Shader *createFadeShaderInstance();
OpenGL::Shader *createShadowShaderInstance();
void start2DMode();
void end2DMode();
void set3DMode() override;
bool computeLightsEnabled() override;
Common::Rect getViewport() const;
Common::Rect getUnscaledViewport() const;
Graphics::Surface *getViewportScreenshot() const override;
private:
Common::Rect _viewport;
Common::Rect _unscaledViewport;
OpenGL::Shader *_surfaceShader;
OpenGL::Shader *_surfaceFillShader;
OpenGL::Shader *_actorShader;
OpenGL::Shader *_fadeShader;
OpenGL::Shader *_shadowShader;
GLuint _surfaceVBO;
GLuint _fadeVBO;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGLS_H

View File

@@ -0,0 +1,431 @@
/* 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/stark/gfx/openglsactor.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/opengls.h"
#include "engines/stark/gfx/texture.h"
#if defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/shader.h"
namespace Stark {
namespace Gfx {
OpenGLSActorRenderer::OpenGLSActorRenderer(OpenGLSDriver *gfx) :
VisualActor(),
_gfx(gfx),
_faceVBO(0) {
_shader = _gfx->createActorShaderInstance();
_shadowShader = _gfx->createShadowShaderInstance();
}
OpenGLSActorRenderer::~OpenGLSActorRenderer() {
clearVertices();
delete _shader;
delete _shadowShader;
}
void OpenGLSActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
if (_modelIsDirty) {
// Update the OpenGL Buffer Objects if required
clearVertices();
uploadVertices();
_modelIsDirty = false;
}
// TODO: Move updates outside of the rendering code
_animHandler->animate(_time);
_model->updateBoundingBox();
_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(); // OpenGL expects matrices transposed
Math::Matrix4 projectionMatrix = projection;
projectionMatrix.transpose(); // OpenGL expects matrices transposed
Math::Matrix4 normalMatrix = modelViewMatrix;
normalMatrix.invertAffineOrthonormal();
_shader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
_shader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
_shader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
_shader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
_shader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
_shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 36);
_shader->enableVertexAttribute("texcoord", _faceVBO, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 48);
_shader->use(true);
_shader->setUniform("modelViewMatrix", modelViewMatrix);
_shader->setUniform("projectionMatrix", projectionMatrix);
_shader->setUniform("normalMatrix", normalMatrix.getRotation());
setBoneRotationArrayUniform(_shader, "boneRotation");
setBonePositionArrayUniform(_shader, "bonePosition");
setLightArrayUniform(lights);
Common::Array<Face *> faces = _model->getFaces();
Common::Array<Material *> mats = _model->getMaterials();
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
// For each face draw its vertices from the VBO, indexed by the EBO
const Material *material = mats[(*face)->materialId];
const Gfx::Texture *tex = resolveTexture(material);
if (tex) {
tex->bind();
} else {
glBindTexture(GL_TEXTURE_2D, 0);
}
_shader->setUniform("textured", tex != nullptr);
_shader->setUniform("color", Math::Vector3d(material->r, material->g, material->b));
GLuint ebo = _faceEBO[*face];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
}
_shader->unbind();
if (_castsShadow &&
StarkScene->shouldRenderShadows() &&
StarkSettings->getBoolSetting(Settings::kShadow)) {
glEnable(GL_BLEND);
glEnable(GL_STENCIL_TEST);
_shadowShader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
_shadowShader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
_shadowShader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
_shadowShader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
_shadowShader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
_shadowShader->use(true);
Math::Matrix4 mvp = projection * view * model;
mvp.transpose();
_shadowShader->setUniform("mvp", mvp);
setBoneRotationArrayUniform(_shadowShader, "boneRotation");
setBonePositionArrayUniform(_shadowShader, "bonePosition");
Math::Matrix4 modelInverse = model;
modelInverse.inverse();
setShadowUniform(lights, position, modelInverse.getRotation());
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
GLuint ebo = _faceEBO[*face];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
}
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
_shadowShader->unbind();
}
}
void OpenGLSActorRenderer::clearVertices() {
OpenGL::Shader::freeBuffer(_faceVBO); // Zero names are silently ignored
_faceVBO = 0;
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
OpenGL::Shader::freeBuffer(it->_value);
}
_faceEBO.clear();
}
void OpenGLSActorRenderer::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);
}
}
GLuint OpenGLSActorRenderer::createModelVBO(const Model *model) {
const Common::Array<VertNode *> &modelVertices = model->getVertices();
float *vertices = new float[14 * modelVertices.size()];
float *vertPtr = vertices;
// Build a vertex array
for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri) {
*vertPtr++ = (*tri)->_pos1.x();
*vertPtr++ = (*tri)->_pos1.y();
*vertPtr++ = (*tri)->_pos1.z();
*vertPtr++ = (*tri)->_pos2.x();
*vertPtr++ = (*tri)->_pos2.y();
*vertPtr++ = (*tri)->_pos2.z();
*vertPtr++ = (*tri)->_bone1;
*vertPtr++ = (*tri)->_bone2;
*vertPtr++ = (*tri)->_boneWeight;
*vertPtr++ = (*tri)->_normal.x();
*vertPtr++ = (*tri)->_normal.y();
*vertPtr++ = (*tri)->_normal.z();
*vertPtr++ = -(*tri)->_texS;
*vertPtr++ = (*tri)->_texT;
}
GLuint vbo = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 14 * modelVertices.size(), vertices);
delete[] vertices;
return vbo;
}
GLuint OpenGLSActorRenderer::createFaceEBO(const Face *face) {
return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices[0]);
}
void OpenGLSActorRenderer::setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform) {
const Common::Array<BoneNode *> &bones = _model->getBones();
GLint pos = shader->getUniformLocation(uniform);
if (pos == -1) {
error("No uniform named '%s'", uniform);
}
float *positions = new float[3 * bones.size()];
float *positionsPtr = positions;
for (uint i = 0; i < bones.size(); i++) {
*positionsPtr++ = bones[i]->_animPos.x();
*positionsPtr++ = bones[i]->_animPos.y();
*positionsPtr++ = bones[i]->_animPos.z();
}
glUniform3fv(pos, bones.size(), positions);
delete[] positions;
}
void OpenGLSActorRenderer::setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform) {
const Common::Array<BoneNode *> &bones = _model->getBones();
GLint rot = shader->getUniformLocation(uniform);
if (rot == -1) {
error("No uniform named '%s'", uniform);
}
float *rotations = new float[4 * bones.size()];
float *rotationsPtr = rotations;
for (uint i = 0; i < bones.size(); i++) {
*rotationsPtr++ = bones[i]->_animRot.x();
*rotationsPtr++ = bones[i]->_animRot.y();
*rotationsPtr++ = bones[i]->_animRot.z();
*rotationsPtr++ = bones[i]->_animRot.w();
}
glUniform4fv(rot, bones.size(), rotations);
delete[] rotations;
}
void OpenGLSActorRenderer::setLightArrayUniform(const LightEntryArray &lights) {
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
_shader->setUniform("ambientColor", ambient->color);
Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
for (uint i = 0; i < lights.size() - 1; i++) {
const LightEntry *l = lights[i + 1];
Math::Vector4d worldPosition;
worldPosition.x() = l->position.x();
worldPosition.y() = l->position.y();
worldPosition.z() = l->position.z();
worldPosition.w() = 1.0;
Math::Vector4d eyePosition = viewMatrix * worldPosition;
// The light type is stored in the w coordinate of the position to save an uniform slot
eyePosition.w() = l->type;
Math::Vector3d worldDirection = l->direction;
Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
eyeDirection.normalize();
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
_shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
_shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
Math::Vector4d params;
params.x() = l->falloffNear;
params.y() = l->falloffFar;
params.z() = l->innerConeAngle.getCosine();
params.w() = l->outerConeAngle.getCosine();
_shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
}
for (uint i = lights.size() - 1; i < maxLights; i++) {
// Make sure unused lights are disabled
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
}
}
void OpenGLSActorRenderer::setShadowUniform(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
sumDirection = worldToModelRot * sumDirection;
_shadowShader->setUniform("lightDirection", sumDirection);
}
bool OpenGLSActorRenderer::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 OpenGLSActorRenderer::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 OpenGLSActorRenderer::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
#endif // defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,81 @@
/* 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 STARK_GFX_OPENGL_S_ACTOR_H
#define STARK_GFX_OPENGL_S_ACTOR_H
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/visual/actor.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_SHADERS)
namespace OpenGL {
class Shader;
}
namespace Stark {
namespace Gfx {
class OpenGLSDriver;
class OpenGLSActorRenderer : public VisualActor {
public:
OpenGLSActorRenderer(OpenGLSDriver *gfx);
virtual ~OpenGLSActorRenderer();
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
protected:
typedef Common::HashMap<Face *, GLuint> FaceBufferMap;
OpenGLSDriver *_gfx;
OpenGL::Shader *_shader, *_shadowShader;
GLuint _faceVBO;
FaceBufferMap _faceEBO;
void clearVertices();
void uploadVertices();
GLuint createModelVBO(const Model *model);
GLuint createFaceEBO(const Face *face);
void setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform);
void setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform);
void setLightArrayUniform(const LightEntryArray &lights);
void setShadowUniform(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 // defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGL_S_ACTOR_H

View File

@@ -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/>.
*
*/
#include "engines/stark/gfx/openglsfade.h"
#include "engines/stark/gfx/opengls.h"
#if defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/shader.h"
namespace Stark {
namespace Gfx {
OpenGLSFadeRenderer::OpenGLSFadeRenderer(OpenGLSDriver *gfx) :
FadeRenderer(),
_gfx(gfx) {
_shader = _gfx->createFadeShaderInstance();
}
OpenGLSFadeRenderer::~OpenGLSFadeRenderer() {
delete _shader;
}
void OpenGLSFadeRenderer::render(float fadeLevel) {
_gfx->start2DMode();
_shader->use();
_shader->setUniform1f("alphaLevel", 1.0 - fadeLevel);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
_shader->unbind();
_gfx->end2DMode();
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,61 @@
/* 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 STARK_GFX_OPENGL_S_FADE_H
#define STARK_GFX_OPENGL_S_FADE_H
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_SHADERS)
#include "engines/stark/gfx/faderenderer.h"
namespace OpenGL {
class Shader;
}
namespace Stark {
namespace Gfx {
class OpenGLSDriver;
/**
* A programmable pipeline OpenGL fade screen renderer
*/
class OpenGLSFadeRenderer : public FadeRenderer {
public:
OpenGLSFadeRenderer(OpenGLSDriver *gfx);
~OpenGLSFadeRenderer();
// FadeRenderer API
void render(float fadeLevel);
private:
OpenGLSDriver *_gfx;
OpenGL::Shader *_shader;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGL_S_FADE_H

View File

@@ -0,0 +1,193 @@
/* 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/stark/gfx/openglsprop.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/formats/biffmesh.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/services.h"
#if defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/shader.h"
namespace Stark {
namespace Gfx {
OpenGLSPropRenderer::OpenGLSPropRenderer(Driver *gfx) :
VisualProp(),
_gfx(gfx),
_faceVBO(0),
_modelIsDirty(true) {
static const char* attributes[] = { "position", "normal", "texcoord", nullptr };
_shader = OpenGL::Shader::fromFiles("stark_prop", attributes);
}
OpenGLSPropRenderer::~OpenGLSPropRenderer() {
clearVertices();
delete _shader;
}
void OpenGLSPropRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
if (_modelIsDirty) {
// Update the OpenGL Buffer Objects if required
clearVertices();
uploadVertices();
_modelIsDirty = false;
}
_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(); // OpenGL expects matrices transposed
Math::Matrix4 projectionMatrix = projection;
projectionMatrix.transpose(); // OpenGL expects matrices transposed
Math::Matrix4 normalMatrix = modelViewMatrix;
normalMatrix.invertAffineOrthonormal();
_shader->enableVertexAttribute("position", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 0);
_shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 12);
_shader->enableVertexAttribute("texcoord", _faceVBO, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 24);
_shader->use(true);
_shader->setUniform("modelViewMatrix", modelViewMatrix);
_shader->setUniform("projectionMatrix", projectionMatrix);
_shader->setUniform("normalMatrix", normalMatrix.getRotation());
setLightArrayUniform(lights);
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];
// For each face draw its vertices from the VBO, indexed by the EBO
const Gfx::Texture *tex = _texture->getTexture(material.texture);
if (tex) {
tex->bind();
} else {
glBindTexture(GL_TEXTURE_2D, 0);
}
_shader->setUniform("textured", tex != nullptr);
_shader->setUniform("color", Math::Vector3d(material.r, material.g, material.b));
_shader->setUniform("doubleSided", material.doubleSided ? 1 : 0);
GLuint ebo = _faceEBO[face];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, face->vertexIndices.size(), GL_UNSIGNED_INT, 0);
}
_shader->unbind();
}
void OpenGLSPropRenderer::clearVertices() {
OpenGL::Shader::freeBuffer(_faceVBO);
for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
OpenGL::Shader::freeBuffer(it->_value);
}
_faceEBO.clear();
}
void OpenGLSPropRenderer::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);
}
}
GLuint OpenGLSPropRenderer::createFaceVBO() {
const Common::Array<Formats::BiffMesh::Vertex> &vertices = _model->getVertices();
return OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 9 * vertices.size(), &vertices.front());
}
GLuint OpenGLSPropRenderer::createFaceEBO(const Face *face) {
return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices.front());
}
void OpenGLSPropRenderer::setLightArrayUniform(const LightEntryArray &lights) {
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
_shader->setUniform("ambientColor", ambient->color);
Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
for (uint i = 0; i < lights.size() - 1; i++) {
const LightEntry *l = lights[i + 1];
Math::Vector4d worldPosition;
worldPosition.x() = l->position.x();
worldPosition.y() = l->position.y();
worldPosition.z() = l->position.z();
worldPosition.w() = 1.0;
Math::Vector4d eyePosition = viewMatrix * worldPosition;
// The light type is stored in the w coordinate of the position to save an uniform slot
eyePosition.w() = l->type;
Math::Vector3d worldDirection = l->direction;
Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
eyeDirection.normalize();
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
_shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
_shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
Math::Vector4d params;
params.x() = l->falloffNear;
params.y() = l->falloffFar;
params.z() = l->innerConeAngle.getCosine();
params.w() = l->outerConeAngle.getCosine();
_shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
}
for (uint i = lights.size() - 1; i < maxLights; i++) {
// Make sure unused lights are disabled
_shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
}
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,76 @@
/* 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 STARK_GFX_OPENGL_S_RENDERED_H
#define STARK_GFX_OPENGL_S_RENDERED_H
#include "engines/stark/model/model.h"
#include "engines/stark/visual/prop.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_SHADERS)
namespace OpenGL {
class Shader;
}
namespace Stark {
namespace Gfx {
class Driver;
class OpenGLSPropRenderer : public VisualProp {
public:
explicit OpenGLSPropRenderer(Driver *gfx);
~OpenGLSPropRenderer() override;
void render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) override;
protected:
typedef Common::HashMap<const Face *, GLuint> FaceBufferMap;
Driver *_gfx;
OpenGL::Shader *_shader;
bool _modelIsDirty;
GLuint _faceVBO;
FaceBufferMap _faceEBO;
void clearVertices();
void uploadVertices();
GLuint createFaceVBO();
GLuint createFaceEBO(const Face *face);
void setLightArrayUniform(const LightEntryArray &lights);
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_OPENGL_S_RENDERED_H
#endif // defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,113 @@
/* 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/stark/gfx/openglssurface.h"
#include "engines/stark/gfx/opengls.h"
#include "engines/stark/gfx/bitmap.h"
#include "engines/stark/gfx/color.h"
#if defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/shader.h"
namespace Stark {
namespace Gfx {
OpenGLSSurfaceRenderer::OpenGLSSurfaceRenderer(OpenGLSDriver *gfx) :
SurfaceRenderer(),
_gfx(gfx) {
_shader = _gfx->createSurfaceShaderInstance();
_shaderFill = _gfx->createSurfaceFillShaderInstance();
}
OpenGLSSurfaceRenderer::~OpenGLSSurfaceRenderer() {
delete _shaderFill;
delete _shader;
}
void OpenGLSSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
render(bitmap, dest, bitmap->width(), bitmap->height());
}
void OpenGLSSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
// Destination rectangle with given width and height
_gfx->start2DMode();
_shader->use();
_shader->setUniform1f("fadeLevel", _fadeLevel);
_shader->setUniform("snapToGrid", _snapToGrid ? 1 : 0);
_shader->setUniform("verOffsetXY", normalizeOriginalCoordinates(dest.x, dest.y));
if (_noScalingOverride) {
_shader->setUniform("verSizeWH", normalizeCurrentCoordinates(width, height));
} else {
_shader->setUniform("verSizeWH", normalizeOriginalCoordinates(width, height));
}
Common::Rect nativeViewport = _gfx->getViewport();
_shader->setUniform("viewport", Math::Vector2d(nativeViewport.width(), nativeViewport.height()));
bitmap->bind();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
_shader->unbind();
_gfx->end2DMode();
}
void OpenGLSSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
// Destination rectangle with given width and height
_gfx->start2DMode();
_shaderFill->use();
_shaderFill->setUniform1f("fadeLevel", _fadeLevel);
_shaderFill->setUniform("snapToGrid", _snapToGrid ? 1 : 0);
_shaderFill->setUniform("verOffsetXY", normalizeOriginalCoordinates(dest.x, dest.y));
if (_noScalingOverride) {
_shaderFill->setUniform("verSizeWH", normalizeCurrentCoordinates(width, height));
} else {
_shaderFill->setUniform("verSizeWH", normalizeOriginalCoordinates(width, height));
}
Common::Rect nativeViewport = _gfx->getViewport();
_shaderFill->setUniform("viewport", Math::Vector2d(nativeViewport.width(), nativeViewport.height()));
_shaderFill->setUniform("color", Math::Vector4d(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
_shaderFill->unbind();
_gfx->end2DMode();
}
Math::Vector2d OpenGLSSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
Common::Rect viewport = _gfx->getUnscaledViewport();
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
}
Math::Vector2d OpenGLSSurfaceRenderer::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
#endif // if defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,68 @@
/* 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 STARK_GFX_OPENGL_S_SURFACE_H
#define STARK_GFX_OPENGL_S_SURFACE_H
#include "engines/stark/gfx/surfacerenderer.h"
#include "math/vector2d.h"
#if defined(USE_OPENGL_SHADERS)
namespace OpenGL {
class Shader;
}
namespace Stark {
namespace Gfx {
class OpenGLSDriver;
class Bitmap;
/**
* A programmable pipeline OpenGL surface renderer
*/
class OpenGLSSurfaceRenderer : public SurfaceRenderer {
public:
OpenGLSSurfaceRenderer(OpenGLSDriver *gfx);
virtual ~OpenGLSSurfaceRenderer();
// SurfaceRenderer API
void render(const Bitmap *bitmap, const Common::Point &dest) override;
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
void fill(const Color &color, 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;
OpenGLSDriver *_gfx;
OpenGL::Shader *_shader;
OpenGL::Shader *_shaderFill;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGL_S_SURFACE_H

View File

@@ -0,0 +1,166 @@
/* 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/stark/gfx/openglsurface.h"
#include "engines/stark/gfx/openglbitmap.h"
#include "engines/stark/gfx/color.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
OpenGLSurfaceRenderer::OpenGLSurfaceRenderer(OpenGLDriver *gfx) :
SurfaceRenderer(),
_gfx(gfx) {
}
OpenGLSurfaceRenderer::~OpenGLSurfaceRenderer() {
}
void OpenGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
render(bitmap, dest, bitmap->width(), bitmap->height());
}
void OpenGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
// Destination rectangle with given width and height
_gfx->start2DMode();
SurfaceVertex vertices[4] = {};
convertToVertices(vertices, dest, width, height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glDisableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(float), ((const OpenGlBitmap *)bitmap)->getTexCoords());
glColor4f(1.0f - _fadeLevel, 1.0f - _fadeLevel, 1.0f - _fadeLevel, 1.0f);
bitmap->bind();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
_gfx->end2DMode();
}
void OpenGLSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
_gfx->start2DMode();
SurfaceVertex vertices[4] = {};
convertToVertices(vertices, dest, width, height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
glColor4f((color.r / 255.0f) - _fadeLevel, (color.g / 255.0f) - _fadeLevel, (color.b / 255.0f) - _fadeLevel, color.a / 255.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
_gfx->end2DMode();
}
void OpenGLSurfaceRenderer::convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const {
const Math::Vector2d surfaceVertices[] = {
// X Y
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 0.0f, 1.0f },
{ 1.0f, 1.0f },
};
Math::Vector2d verSizeWH;
if (_noScalingOverride) {
verSizeWH = normalizeCurrentCoordinates(width, height);
} else {
verSizeWH = normalizeOriginalCoordinates(width, height);
}
auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
auto nativeViewport = _gfx->getViewport();
auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
for (int32 v = 0; v < 4; v++) {
Math::Vector2d pos = verOffsetXY + (surfaceVertices[v] * verSizeWH);
if (_snapToGrid) {
// Align vertex coordinates to the native pixel grid
// This ensures text does not get garbled by nearest neighbors scaling
pos.setX(floor(pos.getX() * viewport.getX() + 0.5) / viewport.getX());
pos.setY(floor(pos.getY() * viewport.getY() + 0.5) / viewport.getY());
}
// position coords
vertices[v].x = pos.getX() * 2.0 - 1.0;
vertices[v].y = -1.0 * (pos.getY() * 2.0 - 1.0);
}
}
Math::Vector2d OpenGLSurfaceRenderer::normalizeOriginalCoordinates(int x, int y) const {
Common::Rect viewport = _gfx->getUnscaledViewport();
return Math::Vector2d(x / (float)viewport.width(), y / (float)viewport.height());
}
Math::Vector2d OpenGLSurfaceRenderer::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
#endif // if defined(USE_OPENGL_GAME)

View File

@@ -0,0 +1,69 @@
/* 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 STARK_GFX_OPENGL_SURFACE_H
#define STARK_GFX_OPENGL_SURFACE_H
#include "engines/stark/gfx/surfacerenderer.h"
#include "engines/stark/gfx/opengl.h"
#include "math/vector2d.h"
#if defined(USE_OPENGL_GAME)
namespace Stark {
namespace Gfx {
class OpenGLDriver;
class Bitmap;
/**
* A programmable pipeline OpenGL surface renderer
*/
class OpenGLSurfaceRenderer : public SurfaceRenderer {
public:
OpenGLSurfaceRenderer(OpenGLDriver *gfx);
virtual ~OpenGLSurfaceRenderer();
// SurfaceRenderer API
void render(const Bitmap *bitmap, const Common::Point &dest) override;
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
void fill(const Color &color, const Common::Point &dest, uint width, uint height) override;
private:
struct SurfaceVertex {
float x;
float y;
};
Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
void convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const;
OpenGLDriver *_gfx;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME)
#endif // STARK_GFX_OPENGL_SURFACE_H

View File

@@ -0,0 +1,107 @@
/* 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/stark/gfx/opengltexture.h"
#include "engines/stark/gfx/driver.h"
#include "graphics/surface.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "graphics/opengl/context.h"
namespace Stark {
namespace Gfx {
OpenGlTexture::OpenGlTexture() :
Texture(),
_id(0),
_levelCount(0) {
glGenTextures(1, &_id);
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
OpenGlTexture::~OpenGlTexture() {
glDeleteTextures(1, &_id);
}
void OpenGlTexture::bind() const {
glBindTexture(GL_TEXTURE_2D, _id);
}
void OpenGlTexture::updateLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
const Graphics::Surface *rgbaSurface = surface;
if (surface->format != Driver::getRGBAPixelFormat()) {
// Convert the surface to texture format
rgbaSurface = surface->convertTo(Driver::getRGBAPixelFormat(), palette);
}
// Stark textures (not bitmaps!) are always POT
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, rgbaSurface->w, rgbaSurface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaSurface->getPixels());
if (rgbaSurface != surface) {
const_cast<Graphics::Surface *>(rgbaSurface)->free();
delete rgbaSurface;
}
}
void OpenGlTexture::setLevelCount(uint32 count) {
_levelCount = count;
if (count >= 1) {
// GLES1 and GLES2 do not allow setting the max provided mipmap level.
// It expects all the levels to be provided, which is not the case in TLJ.
// FIXME: Enable mipmapping on GLES without this extension
if (OpenGLContext.textureMaxLevelSupported) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
// TODO: Provide a fallback if this isn't available.
if (OpenGLContext.textureMirrorRepeatSupported) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
}
}
}
void OpenGlTexture::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
assert(level < _levelCount);
if (level == 0 || OpenGLContext.textureMaxLevelSupported) {
updateLevel(level, surface, palette);
}
}
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)

View File

@@ -0,0 +1,59 @@
/* 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 STARK_GFX_OPENGL_TEXTURE_H
#define STARK_GFX_OPENGL_TEXTURE_H
#include "engines/stark/gfx/texture.h"
#include "graphics/opengl/system_headers.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
namespace Stark {
namespace Gfx {
/**
* An OpenGL texture wrapper
*/
class OpenGlTexture : public Texture {
public:
OpenGlTexture();
virtual ~OpenGlTexture();
// Texture API
void bind() const 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);
GLuint _id;
uint32 _levelCount;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#endif // STARK_GFX_OPENGL_TEXTURE_H

View File

@@ -0,0 +1,234 @@
/* 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/stark/gfx/renderentry.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/visual/actor.h"
#include "engines/stark/visual/effects/bubbles.h"
#include "engines/stark/visual/effects/fireflies.h"
#include "engines/stark/visual/effects/fish.h"
#include "engines/stark/visual/image.h"
#include "engines/stark/visual/prop.h"
#include "engines/stark/visual/smacker.h"
#include "engines/stark/visual/text.h"
#include "engines/stark/visual/visual.h"
namespace Stark {
namespace Gfx {
RenderEntry::RenderEntry(Resources::ItemVisual *owner, const Common::String &name) :
_visual(nullptr),
_name(name),
_owner(owner),
_direction3D(0.0),
_sortKey(0.0),
_clickable(true) {
}
void RenderEntry::render(const LightEntryArray &lights) {
if (!_visual) {
// warning("No visual for render entry '%s'", _name.c_str());
return;
}
VisualImageXMG *imageXMG = _visual->get<VisualImageXMG>();
if (imageXMG) {
imageXMG->render(_position, true);
}
VisualActor *actor = _visual->get<VisualActor>();
if (actor) {
actor->render(_position3D, _direction3D, lights);
}
VisualProp *prop = _visual->get<VisualProp>();
if (prop) {
prop->render(_position3D, _direction3D, lights);
}
VisualSmacker *smacker = _visual->get<VisualSmacker>();
if (smacker) {
smacker->render(_position);
}
VisualText *text = _visual->get<VisualText>();
if (text) {
text->render(_position);
}
VisualEffectBubbles *bubbles = _visual->get<VisualEffectBubbles>();
if (bubbles) {
bubbles->render(_position);
}
VisualEffectFireFlies *fireflies = _visual->get<VisualEffectFireFlies>();
if (fireflies) {
fireflies->render(_position);
}
VisualEffectFish *fish = _visual->get<VisualEffectFish>();
if (fish) {
fish->render(_position);
}
}
void RenderEntry::setVisual(Visual *visual) {
_visual = visual;
}
void RenderEntry::setPosition(const Common::Point &position) {
_position = position;
}
void RenderEntry::setPosition3D(const Math::Vector3d &position, float direction) {
_position3D = position;
_direction3D = direction;
}
void RenderEntry::setSortKey(float sortKey) {
_sortKey = sortKey;
}
void RenderEntry::setClickable(bool clickable) {
_clickable = clickable;
}
bool RenderEntry::compare(const RenderEntry *x, const RenderEntry *y) {
if (x->_sortKey != y->_sortKey) {
return x->_sortKey < y->_sortKey;
} else if (x->_owner && y->_owner) {
// The original used a stable sort. Common::sort is not.
// This should ensure the items remain in the same order if they have the same sort key
return x->_owner->getIndex() < y->_owner->getIndex();
} else {
return (x->_owner < y->_owner);
}
}
bool RenderEntry::containsPoint(const Common::Point &position, Common::Point &relativePosition, const Common::Rect &cursorRect) const {
if (!_visual || !_clickable) {
return false;
}
VisualImageXMG *image = _visual->get<VisualImageXMG>();
if (image) {
Common::Rect imageRect = Common::Rect(image->getWidth(), image->getHeight());
imageRect.translate(_position.x, _position.y);
imageRect.translate(-image->getHotspot().x, -image->getHotspot().y);
relativePosition.x = position.x - imageRect.left;
relativePosition.y = position.y - imageRect.top;
if (imageRect.contains(position) && image->isPointSolid(relativePosition)) {
return true;
}
if (imageRect.width() < 32 && imageRect.height() < 32
&& !cursorRect.isEmpty() && cursorRect.intersects(imageRect)) {
// If the item in the scene is way smaller than the cursor,
// use the whole cursor as a hit rectangle.
relativePosition.x = 1 - image->getHotspot().x;
relativePosition.y = 1 - image->getHotspot().y;
return true;
}
}
VisualSmacker *smacker = _visual->get<VisualSmacker>();
if (smacker) {
Common::Point smackerPosition = smacker->getPosition();
smackerPosition -= _position;
Common::Rect smackerRect = Common::Rect(smacker->getWidth(), smacker->getHeight());
smackerRect.translate(smackerPosition.x, smackerPosition.y);
relativePosition.x = position.x - smackerRect.left;
relativePosition.y = position.y - smackerRect.top;
if (smackerRect.contains(position) && smacker->isPointSolid(relativePosition)) {
return true;
}
}
VisualText *text = _visual->get<VisualText>();
if (text) {
Common::Rect textRect = text->getRect();
textRect.translate(_position.x, _position.y);
relativePosition.x = position.x - textRect.left;
relativePosition.y = position.y - textRect.top;
if (textRect.contains(position)) {
return true;
}
}
return false;
}
bool RenderEntry::intersectRay(const Math::Ray &ray) const {
if (!_visual || !_clickable) {
return false;
}
VisualActor *actor = _visual->get<VisualActor>();
if (actor) {
return actor->intersectRay(ray, _position3D, _direction3D);
}
VisualProp *prop = _visual->get<VisualProp>();
if (prop) {
return prop->intersectRay(ray, _position3D, _direction3D);
}
return false;
}
VisualImageXMG *RenderEntry::getImage() const {
if (!_visual) {
return nullptr;
}
return _visual->get<VisualImageXMG>();
}
VisualText *RenderEntry::getText() const {
if (!_visual) {
return nullptr;
}
return _visual->get<VisualText>();
}
Common::Rect RenderEntry::getBoundingRect() const {
if (!_visual) {
return Common::Rect();
}
VisualActor *actor = _visual->get<VisualActor>();
if (actor) {
return actor->getBoundingRect(_position3D, _direction3D);
}
warning("RenderEntry::getBoundingRect is not implemented for '%s'", _name.c_str());
return Common::Rect();
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,134 @@
/* 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 STARK_GFX_RENDER_ENTRY_H
#define STARK_GFX_RENDER_ENTRY_H
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
#include "math/ray.h"
#include "math/vector3d.h"
namespace Stark {
class Visual;
class VisualImageXMG;
class VisualText;
namespace Resources {
class ItemVisual;
}
namespace Gfx {
struct LightEntry {
enum Type {
kAmbient = 0,
kPoint = 1,
kDirectional = 2,
kSpot = 4
};
Type type;
Math::Vector3d color;
Math::Vector3d position;
Math::Vector3d direction;
Math::Angle innerConeAngle;
Math::Angle outerConeAngle;
float falloffNear;
float falloffFar;
Math::Vector4d worldPosition;
Math::Vector4d eyePosition;
Math::Vector3d eyeDirection;
};
typedef Common::Array<LightEntry *> LightEntryArray;
class RenderEntry {
public:
RenderEntry(Resources::ItemVisual *owner, const Common::String &name);
virtual ~RenderEntry() {}
void render(const LightEntryArray &lights = LightEntryArray());
void setVisual(Visual *visual);
void setPosition(const Common::Point &position);
void setPosition3D(const Math::Vector3d &position, float direction);
void setSortKey(float sortKey);
void setClickable(bool clickable);
/** Gets the position */
Common::Point getPosition() const { return _position; }
/** Gets the owner-object */
Resources::ItemVisual *getOwner() const { return _owner; }
/** Gets the entry's name */
const Common::String &getName() const { return _name; }
/** Obtain the underlying image visual, if any */
VisualImageXMG *getImage() const;
/** Obtain the underlying text visual, if any */
VisualText *getText() const;
/**
* Mouse picking test for 2D items
*
* @param position game window coordinates to test
* @param relativePosition successful hit item relative coordinates
* @param cursorRect cursor rectangle to be used to test small world items
* @return successful hit
*/
bool containsPoint(const Common::Point &position, Common::Point &relativePosition, const Common::Rect &cursorRect) const;
/** Mouse picking test for 3D items */
bool intersectRay(const Math::Ray &ray) const;
/** Compare two render entries by their sort keys */
static bool compare(const RenderEntry *x, const RenderEntry *y);
/**
* Compute the 2D screen space bounding rect for the item,
* in original game view coordinates.
*/
Common::Rect getBoundingRect() const;
protected:
Common::String _name;
Resources::ItemVisual *_owner;
Visual *_visual;
Common::Point _position;
Math::Vector3d _position3D;
float _direction3D;
float _sortKey;
bool _clickable;
};
typedef Common::Array<RenderEntry *> RenderEntryArray;
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_RENDER_ENTRY_H

View File

@@ -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/>.
*
*/
#include "engines/stark/gfx/surfacerenderer.h"
namespace Stark {
namespace Gfx {
SurfaceRenderer::SurfaceRenderer() :
_noScalingOverride(false),
_fadeLevel(0),
_snapToGrid(false) {
}
SurfaceRenderer::~SurfaceRenderer() {
}
void SurfaceRenderer::setNoScalingOverride(bool noScalingOverride) {
_noScalingOverride = noScalingOverride;
}
void SurfaceRenderer::setFadeLevel(float fadeLevel) {
_fadeLevel = fadeLevel;
}
void SurfaceRenderer::setSnapToGrid(bool snapToGrid) {
_snapToGrid = snapToGrid;
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,85 @@
/* 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 STARK_GFX_SURFACE_RENDERER_H
#define STARK_GFX_SURFACE_RENDERER_H
#include "common/rect.h"
namespace Stark {
namespace Gfx {
class Bitmap;
struct Color;
/**
* A renderer to draw textures as two dimensional surfaces to the current viewport
*/
class SurfaceRenderer {
public:
SurfaceRenderer();
virtual ~SurfaceRenderer();
/**
* Draw a 2D surface from the specified bitmap
*/
virtual void render(const Bitmap *bitmap, const Common::Point &dest) = 0;
/**
* Draw a 2D surface from the specified bitmap with given width and height
*/
virtual void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) = 0;
/**
* Draw a filled 2D rectangle using the specified color
*/
virtual void fill(const Color &color, const Common::Point &dest, uint width, uint height) = 0;
/**
* When this is set to true, the texture size is expected to be in current
* coordinates, and is to be drawn without scaling.
*
* This setting does not affect the destination point coordinates
*/
void setNoScalingOverride(bool noScalingOverride);
/**
* The fade level is added to the color value of each pixel
*
* It is a value between -1 and 1
*/
void setFadeLevel(float fadeLevel);
/**
* Align vertex coordinates to the native pixel grid
*/
void setSnapToGrid(bool snapToGrid);
protected:
bool _noScalingOverride;
float _fadeLevel;
bool _snapToGrid;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_SURFACE_RENDERER_H

View File

@@ -0,0 +1,55 @@
/* 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/stark/gfx/texture.h"
#include "graphics/surface.h"
namespace Stark {
namespace Gfx {
TextureSet::TextureSet() {
}
TextureSet::~TextureSet() {
for (TextureMap::iterator it = _texMap.begin(); it != _texMap.end(); ++it) {
delete it->_value;
}
}
void TextureSet::addTexture(const Common::String &name, Texture *texture) {
if (_texMap.contains(name)) {
error("A texture with the name '%s' already exists in the set.", name.c_str());
}
_texMap.setVal(name, texture);
}
const Texture *TextureSet::getTexture(const Common::String &name) const {
TextureMap::const_iterator it = _texMap.find(name);
if (it != _texMap.end())
return it->_value;
return nullptr;
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,85 @@
/* 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 STARK_GFX_TEXTURE_H
#define STARK_GFX_TEXTURE_H
#include "common/hash-str.h"
namespace Graphics {
struct Surface;
}
namespace Stark {
namespace Gfx {
/**
* An abstract texture
*/
class Texture {
public:
Texture() {}
virtual ~Texture() {}
/** Make the texture active */
virtual void bind() const = 0;
/**
* Define the total number of levels of details
*
* Must be called before adding levels
*/
virtual void setLevelCount(uint32 count) = 0;
/**
* Add a detail level to the texture
*/
virtual void addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette = nullptr) = 0;
};
/**
* A collection of textures referenced by their names
*/
class TextureSet {
public:
TextureSet();
~TextureSet();
/**
* Add a texture to the set
*/
void addTexture(const Common::String &name, Texture *texture);
/**
* Retrieve a texture from the set
*/
const Texture *getTexture(const Common::String &name) const;
private:
typedef Common::HashMap<Common::String, Texture *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TextureMap;
TextureMap _texMap;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_TEXTURE_H

View File

@@ -0,0 +1,188 @@
/* 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/system.h"
#include "common/config-manager.h"
#include "math/matrix4.h"
#include "engines/stark/gfx/tinygl.h"
#include "engines/stark/gfx/tinyglactor.h"
#include "engines/stark/gfx/tinyglbitmap.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/scene.h"
#include "engines/stark/services/services.h"
#include "graphics/surface.h"
namespace Stark {
namespace Gfx {
TinyGLDriver::TinyGLDriver() {
}
TinyGLDriver::~TinyGLDriver() {
TinyGL::destroyContext();
}
void TinyGLDriver::init() {
computeScreenViewport();
TinyGL::createContext(kOriginalWidth, kOriginalHeight, g_system->getScreenFormat(), 512, true, ConfMan.getBool("dirtyrects"));
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 | TGL_STENCIL_BUFFER_BIT);
}
void TinyGLDriver::flipBuffer() {
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();
}
Texture *TinyGLDriver::createTexture() {
return new TinyGlTexture();
}
Bitmap *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);
// Stencil test are only used in rendering shadows
// They are manually enabled and disabled there
tglStencilFunc(TGL_EQUAL, 0, 0xFF);
tglStencilOp(TGL_KEEP, TGL_KEEP, TGL_INCR);
}
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 *tmp = TinyGL::copyFromFrameBuffer(getRGBAPixelFormat());
Graphics::Surface *s = new Graphics::Surface();
s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());
byte *src = (byte *)tmp->getPixels();
s->copyRectToSurface(src + tmp->pitch * _viewport.top + _viewport.left * tmp->format.bytesPerPixel,
tmp->pitch, 0, 0, _viewport.width(), _viewport.height());
tmp->free();
delete tmp;
return s;
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,78 @@
/* 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 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/tinygl.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() override;
Bitmap *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;
bool supportsModdedAssets() const override { return false; }
private:
Common::Rect _viewport;
Common::Rect _unscaledViewport;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_TINYGL_H

View File

@@ -0,0 +1,453 @@
/* 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/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());
}
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);
if (tex) {
tex->bind();
tglEnable(TGL_TEXTURE_2D);
} else {
tglBindTexture(TGL_TEXTURE_2D, 0);
tglDisable(TGL_TEXTURE_2D);
}
auto vertexIndices = _faceEBO[*face];
auto numVertexIndices = (*face)->vertexIndices.size();
for (uint32 i = 0; i < numVertexIndices; i++) {
if (tex) {
color = Math::Vector3d(1.0f, 1.0f, 1.0f);
} else {
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);
tglEnable(TGL_STENCIL_TEST);
tglDisable(TGL_TEXTURE_2D);
tglColor4f(0.0f, 0.0f, 0.0f, 0.5f);
for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
tglEnableClientState(TGL_VERTEX_ARRAY);
tglVertexPointer(3, TGL_FLOAT, sizeof(ActorVertex), &_faceVBO[0].sx);
tglDrawElements(TGL_TRIANGLES, (*face)->vertexIndices.size(), TGL_UNSIGNED_INT, _faceEBO[*face]);
tglDisableClientState(TGL_VERTEX_ARRAY);
}
tglEnable(TGL_TEXTURE_2D);
tglDisable(TGL_BLEND);
tglDisable(TGL_STENCIL_TEST);
}
}
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

View File

@@ -0,0 +1,101 @@
/* 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 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/tinygl.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
namespace Stark {
namespace Gfx {
class TinyGLDriver;
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;
};
typedef _ActorVertex ActorVertex;
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

View File

@@ -0,0 +1,70 @@
/* 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/stark/gfx/tinyglbitmap.h"
#include "engines/stark/gfx/driver.h"
#include "common/system.h"
#include "graphics/surface.h"
namespace Stark {
namespace Gfx {
TinyGlBitmap::TinyGlBitmap() :
Bitmap() {
_blitImage = tglGenBlitImage();
}
TinyGlBitmap::~TinyGlBitmap() {
tglDeleteBlitImage(_blitImage);
}
void TinyGlBitmap::bind() const {
}
void TinyGlBitmap::update(const Graphics::Surface *surface, const byte *palette) {
_width = surface->w;
_height = surface->h;
if (palette) {
// TinyGL doesn't currently support images with palettes, so we handle conversion here.
Graphics::Surface *convertedSurface = surface->convertTo(getBestPixelFormat(), palette);
tglUploadBlitImage(_blitImage, *convertedSurface, 0, false);
convertedSurface->free();
delete convertedSurface;
} else {
tglUploadBlitImage(_blitImage, *surface, 0, false);
}
}
void TinyGlBitmap::setSamplingFilter(Bitmap::SamplingFilter filter) {
}
Graphics::PixelFormat TinyGlBitmap::getBestPixelFormat() const {
return g_system->getScreenFormat();
}
TinyGL::BlitImage *TinyGlBitmap::getBlitImage() const {
return _blitImage;
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,54 @@
/* 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 STARK_GFX_TINYGL_BITMAP_H
#define STARK_GFX_TINYGL_BITMAP_H
#include "engines/stark/gfx/bitmap.h"
#include "graphics/tinygl/tinygl.h"
namespace Stark {
namespace Gfx {
/**
* A TinyGL bitmap wrapper
*/
class TinyGlBitmap : public Bitmap {
public:
TinyGlBitmap();
virtual ~TinyGlBitmap();
// Bitmap API
void bind() const override;
TinyGL::BlitImage *getBlitImage() const;
void update(const Graphics::Surface *surface, const byte *palette = nullptr) override;
void setSamplingFilter(SamplingFilter filter) override;
Graphics::PixelFormat getBestPixelFormat() const override;
protected:
TinyGL::BlitImage *_blitImage;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_TINYGL_TEXTURE_H

View File

@@ -0,0 +1,77 @@
/* 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/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

View File

@@ -0,0 +1,52 @@
/* 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 STARK_GFX_TINYGL_FADE_H
#define STARK_GFX_TINYGL_FADE_H
#include "engines/stark/gfx/faderenderer.h"
#include "graphics/tinygl/tinygl.h"
namespace Stark {
namespace Gfx {
class TinyGLDriver;
/**
* A 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

View File

@@ -0,0 +1,236 @@
/* 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/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;
}
_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);
if (tex) {
tex->bind();
tglEnable(TGL_TEXTURE_2D);
} else {
tglBindTexture(TGL_TEXTURE_2D, 0);
tglDisable(TGL_TEXTURE_2D);
}
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) {
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 {
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

View File

@@ -0,0 +1,82 @@
/* 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 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/tinygl.h"
#include "common/hashmap.h"
#include "common/hash-ptr.h"
namespace Stark {
namespace Gfx {
class Driver;
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;
};
typedef _PropVertex PropVertex;
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

View File

@@ -0,0 +1,157 @@
/* 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/stark/gfx/tinyglsurface.h"
#include "engines/stark/gfx/tinyglbitmap.h"
#include "engines/stark/gfx/color.h"
#include "graphics/tinygl/tinygl.h"
namespace Stark {
namespace Gfx {
TinyGLSurfaceRenderer::TinyGLSurfaceRenderer(TinyGLDriver *gfx) :
SurfaceRenderer(),
_gfx(gfx) {
}
TinyGLSurfaceRenderer::~TinyGLSurfaceRenderer() {
}
void TinyGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest) {
render(bitmap, dest, bitmap->width(), bitmap->height());
}
void TinyGLSurfaceRenderer::render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) {
if (width == 0 || height == 0)
return;
_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<Bitmap *>(bitmap))->getBlitImage();
int blitImageWidth, blitImageHeight;
tglGetBlitImageSize(blitImage, blitImageWidth, blitImageHeight);
int posX = viewport.getX() * verOffsetXY.getX() + nativeViewport.left;
int posY = viewport.getY() * verOffsetXY.getY() + nativeViewport.top;
TinyGL::BlitTransform transform(posX, posY);
// W/A for not clipped bitmaps in prompt dialog
if (width == 256 && height == 256) {
blitImageHeight = viewport.getY() - dest.y;
blitImageWidth = viewport.getX() - dest.x;
}
transform.sourceRectangle(0, 0, blitImageWidth, blitImageHeight);
transform.tint(1.0, 1.0 - _fadeLevel, 1.0 - _fadeLevel, 1.0 - _fadeLevel);
tglBlit(blitImage, transform);
_gfx->end2DMode();
}
void TinyGLSurfaceRenderer::fill(const Color &color, const Common::Point &dest, uint width, uint height) {
_gfx->start2DMode();
SurfaceVertex vertices[4] = {};
convertToVertices(vertices, dest, width, height);
tglMatrixMode(TGL_PROJECTION);
tglPushMatrix();
tglLoadIdentity();
tglMatrixMode(TGL_MODELVIEW);
tglPushMatrix();
tglLoadIdentity();
tglDisable(TGL_TEXTURE_2D);
tglEnableClientState(TGL_VERTEX_ARRAY);
tglVertexPointer(2, TGL_FLOAT, sizeof(SurfaceVertex), &vertices[0].x);
tglColor4f((color.r / 255.0f) - _fadeLevel, (color.g / 255.0f) - _fadeLevel, (color.b / 255.0f) - _fadeLevel, color.a / 255.0f);
tglDrawArrays(TGL_TRIANGLE_STRIP, 0, 4);
tglDisableClientState(TGL_VERTEX_ARRAY);
tglMatrixMode(TGL_MODELVIEW);
tglPopMatrix();
tglMatrixMode(TGL_PROJECTION);
tglPopMatrix();
_gfx->end2DMode();
}
void TinyGLSurfaceRenderer::convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const {
const Math::Vector2d surfaceVertices[] = {
// X Y
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 0.0f, 1.0f },
{ 1.0f, 1.0f },
};
Math::Vector2d verSizeWH;
if (_noScalingOverride) {
verSizeWH = normalizeCurrentCoordinates(width, height);
} else {
verSizeWH = normalizeOriginalCoordinates(width, height);
}
auto verOffsetXY = normalizeOriginalCoordinates(dest.x, dest.y);
auto nativeViewport = _gfx->getViewport();
auto viewport = Math::Vector2d(nativeViewport.width(), nativeViewport.height());
for (int32 v = 0; v < 4; v++) {
Math::Vector2d pos = verOffsetXY + (surfaceVertices[v] * verSizeWH);
if (_snapToGrid) {
// Align vertex coordinates to the native pixel grid
// This ensures text does not get garbled by nearest neighbors scaling
pos.setX(floor(pos.getX() * viewport.getX() + 0.5) / viewport.getX());
pos.setY(floor(pos.getY() * viewport.getY() + 0.5) / viewport.getY());
}
// position coords
vertices[v].x = pos.getX() * 2.0 - 1.0;
vertices[v].y = -1.0 * (pos.getY() * 2.0 - 1.0);
}
}
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

View File

@@ -0,0 +1,67 @@
/* 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 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/tinygl.h"
#include "math/vector2d.h"
namespace Stark {
namespace Gfx {
class TinyGLDriver;
class Bitmap;
/**
* A programmable pipeline TinyGL surface renderer
*/
class TinyGLSurfaceRenderer : public SurfaceRenderer {
public:
TinyGLSurfaceRenderer(TinyGLDriver *gfx);
virtual ~TinyGLSurfaceRenderer();
// SurfaceRenderer API
void render(const Bitmap *bitmap, const Common::Point &dest) override;
void render(const Bitmap *bitmap, const Common::Point &dest, uint width, uint height) override;
void fill(const Color &color, const Common::Point &dest, uint width, uint height) override;
private:
struct SurfaceVertex {
float x;
float y;
};
Math::Vector2d normalizeOriginalCoordinates(int x, int y) const;
Math::Vector2d normalizeCurrentCoordinates(int x, int y) const;
void convertToVertices(SurfaceVertex *vertices, const Common::Point &dest, uint width, uint height) const;
TinyGLDriver *_gfx;
};
} // End of namespace Gfx
} // End of namespace Stark
#endif // STARK_GFX_TINYGL_SURFACE_H

View File

@@ -0,0 +1,86 @@
/* 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/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);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_CLAMP_TO_EDGE);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_CLAMP_TO_EDGE);
}
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 (surface->format != Driver::getRGBAPixelFormat()) {
// 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 {
// Convert the surface to texture format
tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, surface->w, surface->h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, const_cast<void *>(surface->getPixels()));
}
}
void TinyGlTexture::setLevelCount(uint32 count) {
_levelCount = count;
if (count >= 1) {
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_MIRRORED_REPEAT);
tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_MIRRORED_REPEAT);
}
}
void TinyGlTexture::addLevel(uint32 level, const Graphics::Surface *surface, const byte *palette) {
assert(level < _levelCount);
if (level == 0) {
updateLevel(level, surface, palette);
}
}
} // End of namespace Gfx
} // End of namespace Stark

View File

@@ -0,0 +1,55 @@
/* 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 STARK_GFX_TINYGL_TEXTURE_H
#define STARK_GFX_TINYGL_TEXTURE_H
#include "engines/stark/gfx/texture.h"
#include "graphics/tinygl/tinygl.h"
namespace Stark {
namespace Gfx {
/**
* A TinyGL texture wrapper
*/
class TinyGlTexture : public Texture {
public:
TinyGlTexture();
virtual ~TinyGlTexture();
// Texture API
void bind() const 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