Initial commit
This commit is contained in:
74
engines/stark/gfx/bitmap.h
Normal file
74
engines/stark/gfx/bitmap.h
Normal 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
50
engines/stark/gfx/color.h
Normal 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
|
||||
168
engines/stark/gfx/driver.cpp
Normal file
168
engines/stark/gfx/driver.cpp
Normal 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
165
engines/stark/gfx/driver.h
Normal 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
|
||||
45
engines/stark/gfx/faderenderer.h
Normal file
45
engines/stark/gfx/faderenderer.h
Normal 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
|
||||
287
engines/stark/gfx/opengl.cpp
Normal file
287
engines/stark/gfx/opengl.cpp
Normal 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)
|
||||
79
engines/stark/gfx/opengl.h
Normal file
79
engines/stark/gfx/opengl.h
Normal 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
|
||||
489
engines/stark/gfx/openglactor.cpp
Normal file
489
engines/stark/gfx/openglactor.cpp
Normal 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)
|
||||
106
engines/stark/gfx/openglactor.h
Normal file
106
engines/stark/gfx/openglactor.h
Normal 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
|
||||
122
engines/stark/gfx/openglbitmap.cpp
Normal file
122
engines/stark/gfx/openglbitmap.cpp
Normal 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)
|
||||
60
engines/stark/gfx/openglbitmap.h
Normal file
60
engines/stark/gfx/openglbitmap.h
Normal 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
|
||||
81
engines/stark/gfx/openglfade.cpp
Normal file
81
engines/stark/gfx/openglfade.cpp
Normal 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)
|
||||
56
engines/stark/gfx/openglfade.h
Normal file
56
engines/stark/gfx/openglfade.h
Normal 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
|
||||
267
engines/stark/gfx/openglprop.cpp
Normal file
267
engines/stark/gfx/openglprop.cpp
Normal 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)
|
||||
87
engines/stark/gfx/openglprop.h
Normal file
87
engines/stark/gfx/openglprop.h
Normal 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)
|
||||
250
engines/stark/gfx/opengls.cpp
Normal file
250
engines/stark/gfx/opengls.cpp
Normal 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)
|
||||
94
engines/stark/gfx/opengls.h
Normal file
94
engines/stark/gfx/opengls.h
Normal 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
|
||||
431
engines/stark/gfx/openglsactor.cpp
Normal file
431
engines/stark/gfx/openglsactor.cpp
Normal 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)
|
||||
81
engines/stark/gfx/openglsactor.h
Normal file
81
engines/stark/gfx/openglsactor.h
Normal 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
|
||||
57
engines/stark/gfx/openglsfade.cpp
Normal file
57
engines/stark/gfx/openglsfade.cpp
Normal 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)
|
||||
61
engines/stark/gfx/openglsfade.h
Normal file
61
engines/stark/gfx/openglsfade.h
Normal 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
|
||||
193
engines/stark/gfx/openglsprop.cpp
Normal file
193
engines/stark/gfx/openglsprop.cpp
Normal 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)
|
||||
76
engines/stark/gfx/openglsprop.h
Normal file
76
engines/stark/gfx/openglsprop.h
Normal 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)
|
||||
113
engines/stark/gfx/openglssurface.cpp
Normal file
113
engines/stark/gfx/openglssurface.cpp
Normal 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)
|
||||
68
engines/stark/gfx/openglssurface.h
Normal file
68
engines/stark/gfx/openglssurface.h
Normal 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
|
||||
166
engines/stark/gfx/openglsurface.cpp
Normal file
166
engines/stark/gfx/openglsurface.cpp
Normal 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)
|
||||
69
engines/stark/gfx/openglsurface.h
Normal file
69
engines/stark/gfx/openglsurface.h
Normal 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
|
||||
107
engines/stark/gfx/opengltexture.cpp
Normal file
107
engines/stark/gfx/opengltexture.cpp
Normal 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)
|
||||
59
engines/stark/gfx/opengltexture.h
Normal file
59
engines/stark/gfx/opengltexture.h
Normal 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
|
||||
234
engines/stark/gfx/renderentry.cpp
Normal file
234
engines/stark/gfx/renderentry.cpp
Normal 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
|
||||
134
engines/stark/gfx/renderentry.h
Normal file
134
engines/stark/gfx/renderentry.h
Normal 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
|
||||
49
engines/stark/gfx/surfacerenderer.cpp
Normal file
49
engines/stark/gfx/surfacerenderer.cpp
Normal 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
|
||||
85
engines/stark/gfx/surfacerenderer.h
Normal file
85
engines/stark/gfx/surfacerenderer.h
Normal 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
|
||||
55
engines/stark/gfx/texture.cpp
Normal file
55
engines/stark/gfx/texture.cpp
Normal 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
|
||||
85
engines/stark/gfx/texture.h
Normal file
85
engines/stark/gfx/texture.h
Normal 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
|
||||
188
engines/stark/gfx/tinygl.cpp
Normal file
188
engines/stark/gfx/tinygl.cpp
Normal 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
|
||||
78
engines/stark/gfx/tinygl.h
Normal file
78
engines/stark/gfx/tinygl.h
Normal 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
|
||||
453
engines/stark/gfx/tinyglactor.cpp
Normal file
453
engines/stark/gfx/tinyglactor.cpp
Normal 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
|
||||
101
engines/stark/gfx/tinyglactor.h
Normal file
101
engines/stark/gfx/tinyglactor.h
Normal 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
|
||||
70
engines/stark/gfx/tinyglbitmap.cpp
Normal file
70
engines/stark/gfx/tinyglbitmap.cpp
Normal 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
|
||||
54
engines/stark/gfx/tinyglbitmap.h
Normal file
54
engines/stark/gfx/tinyglbitmap.h
Normal 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
|
||||
77
engines/stark/gfx/tinyglfade.cpp
Normal file
77
engines/stark/gfx/tinyglfade.cpp
Normal 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
|
||||
52
engines/stark/gfx/tinyglfade.h
Normal file
52
engines/stark/gfx/tinyglfade.h
Normal 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
|
||||
236
engines/stark/gfx/tinyglprop.cpp
Normal file
236
engines/stark/gfx/tinyglprop.cpp
Normal 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
|
||||
82
engines/stark/gfx/tinyglprop.h
Normal file
82
engines/stark/gfx/tinyglprop.h
Normal 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
|
||||
157
engines/stark/gfx/tinyglsurface.cpp
Normal file
157
engines/stark/gfx/tinyglsurface.cpp
Normal 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
|
||||
67
engines/stark/gfx/tinyglsurface.h
Normal file
67
engines/stark/gfx/tinyglsurface.h
Normal 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
|
||||
86
engines/stark/gfx/tinygltexture.cpp
Normal file
86
engines/stark/gfx/tinygltexture.cpp
Normal 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
|
||||
55
engines/stark/gfx/tinygltexture.h
Normal file
55
engines/stark/gfx/tinygltexture.h
Normal 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
|
||||
Reference in New Issue
Block a user