graphics fixes and improvements

This commit is contained in:
Eduardo Bart 2012-06-08 13:58:08 -03:00
parent 4f9ca15ef0
commit 1a7f2a44fc
39 changed files with 450 additions and 317 deletions

View File

@ -43,7 +43,6 @@ locale = {
["Auto login"] = "Entrar automaticamente",
["Auto login selected character on next charlist load"] = "Entrar automaticamente com o personagem quando reabrir a lista de personagens",
["Axe Fighting"] = "Combate com Machado",
["Background pane framerate limit: %s"] = "Limite da taxa de quadros no segundo plano: %s",
["Banishment"] = "Banimento",
["Banishment + Final Warning"] = "Banimento + Aviso final",
["Battle"] = "Batalha",
@ -88,9 +87,8 @@ locale = {
["Fishing"] = "Pesca",
["Fist Fighting"] = "Porrada",
["Follow"] = "Seguir",
["Foreground pane framerate limit: %s"] = "Limite da taxa de quadros no primeiro plano: %s",
["Frame rate limit"] = "Limite de FPS",
["Fullscreen"] = "Tela cheia",
["Game framerate limit: %s"] = "Limite da taxa de quadros do jogo: %s",
["General"] = "Geral",
["Graphics"] = "Gráficos",
["Graphics Engine:"] = "Motor Gráfico:",
@ -106,6 +104,7 @@ locale = {
["Hotkeys"] = "Atalhos",
["Ignore capacity"] = "Ignorar capacidade",
["Ignore equipped"] = "Ignorar equipado",
["Interface framerate limit: %s"] = "Limite da taxa de quadros da interface: %s",
["Inventory"] = "Inventário",
["Invite to Party"] = "Convidar para o grupo",
["Invite to private chat"] = "Convidar para o canal privado",

View File

@ -35,7 +35,7 @@ Panel
Label
id: backgroundFrameRateLimitLabel
!text: tr('Background pane framerate limit: %s', 'max')
!text: tr('Game framerate limit: %s', 'max')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
@ -59,12 +59,12 @@ Panel
value = 0
end
self:getParent():getChildById('backgroundFrameRateLimitLabel'):setText(tr('Background pane framerate limit: %s', text))
self:getParent():getChildById('backgroundFrameRateLimitLabel'):setText(tr('Game framerate limit: %s', text))
g_app.setBackgroundPaneMaxFps(value)
Label
id: foregroundFrameRateLimitLabel
!text: tr('Foreground pane framerate limit: %s', '8')
!text: tr('Interface framerate limit: %s', '24')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
@ -78,7 +78,7 @@ Panel
margin-top: 3
minimum: 1
maximum: 61
value: 8
value: 61
step: 1
@onValueChange: |
local value = self:getValue()
@ -88,6 +88,6 @@ Panel
value = 0
end
self:getParent():getChildById('foregroundFrameRateLimitLabel'):setText(tr('Foreground pane framerate limit: %s', text))
self:getParent():getChildById('foregroundFrameRateLimitLabel'):setText(tr('Interface framerate limit: %s', text))
g_app.setForegroundPaneMaxFps(value)

View File

@ -39,6 +39,14 @@ local function setupGraphicsEngines()
g_graphics.selectPainterEngine(2)
end
end
local foregroundFrameRateScrollBar = graphicsPanel:getChildById('foregroundFrameRateScrollBar')
local foregroundFrameRateLimitLabel = graphicsPanel:getChildById('foregroundFrameRateLimitLabel')
if not g_graphics.canCacheBackbuffer() then
foregroundFrameRateScrollBar:setValue(61)
foregroundFrameRateScrollBar:disable()
foregroundFrameRateLimitLabel:disable()
end
end
function Options.init()
@ -50,12 +58,12 @@ function Options.init()
end
end
Keyboard.bindKeyDown('Ctrl+P', Options.toggle)
Keyboard.bindKeyDown('Ctrl+D', Options.toggle)
Keyboard.bindKeyDown('Ctrl+F', function() Options.toggleOption('fullscreen') end)
optionsWindow = displayUI('options.otui')
optionsWindow:hide()
optionsButton = TopMenu.addLeftButton('optionsButton', tr('Options') .. ' (Ctrl+P)', 'options.png', Options.toggle)
optionsButton = TopMenu.addLeftButton('optionsButton', tr('Options') .. ' (Ctrl+D)', 'options.png', Options.toggle)
optionsTabBar = optionsWindow:getChildById('optionsTabBar')
optionsTabBar:setContentWidget(optionsWindow:getChildById('optionsTabContent'))
@ -69,7 +77,7 @@ function Options.init()
end
function Options.terminate()
Keyboard.unbindKeyDown('Ctrl+P')
Keyboard.unbindKeyDown('Ctrl+D')
Keyboard.unbindKeyDown('Ctrl+F')
optionsWindow:destroy()
optionsWindow = nil

View File

@ -83,6 +83,8 @@ TopPanel
cycleEvent(function()
local frameCounter = rootWidget:recursiveGetChildById('frameCounter')
if frameCounter then
frameCounter:setText('FPS: ' .. g_app.getBackgroundPaneFps())
local text = 'FPS: ' .. g_app.getBackgroundPaneFps()
frameCounter:setText(text)
--print(text)
end
end, 250)

View File

@ -63,10 +63,11 @@ local function updateSlider(self)
slider:setMarginLeft(offset)
end
if self.maximum == self.minimum then
self:disable()
else
self:enable()
local status = (self.maximum ~= self.minimum)
self:setOn(status)
for _i,child in pairs(self:getChildren()) do
child:setEnabled(status)
end
end
@ -100,12 +101,10 @@ end
function UIScrollBar:onSetup()
self.setupDone = true
signalcall(self.onValueChange, self, self.value)
addEvent(function()
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
Mouse.bindPressMove(self:getChildById('sliderButton'), function(mousePos, mouseMoved) parseSliderPos(self, mousePos) end)
updateSlider(self)
end)
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
Mouse.bindPressMove(self:getChildById('sliderButton'), function(mousePos, mouseMoved) parseSliderPos(self, mousePos) end)
updateSlider(self)
end
function UIScrollBar:onStyleApply(styleName, styleNode)

View File

@ -41,6 +41,7 @@ end
function UIItem:onHoverChange(hovered)
UIWidget.onHoverChange(self, hovered)
if self:isVirtual() then return end
local draggingWidget = g_ui.getDraggingWidget()

View File

@ -6,10 +6,8 @@ function UIMiniWindow.create()
end
function UIMiniWindow:onSetup()
addEvent(function()
self:getChildById('closeButton').onClick = function() signalcall(self.onClose, self) end
self:getChildById('minimizeButton').onClick = function() signalcall(self.onMinimize, self) end
end)
self:getChildById('closeButton').onClick = function() signalcall(self.onClose, self) end
self:getChildById('minimizeButton').onClick = function() signalcall(self.onMinimize, self) end
end
function UIMiniWindow:onDragEnter(mousePos)

View File

@ -44,6 +44,7 @@ local SayModes = {
local MAX_HISTORY = 1000
local MAX_LINES = 100
local HELP_CHANNEL = 9
local consolePanel
local consoleContentPanel
@ -221,6 +222,7 @@ function Console.init()
-- tibia like hotkeys
Keyboard.bindKeyDown('Ctrl+O', g_game.requestChannels)
Keyboard.bindKeyDown('Ctrl+E', Console.removeCurrentTab)
Keyboard.bindKeyDown('Ctrl+H', Console.openHelp)
end
function Console.terminate()
@ -242,6 +244,7 @@ function Console.terminate()
Keyboard.unbindKeyDown('Ctrl+O')
Keyboard.unbindKeyDown('Ctrl+E')
Keyboard.unbindKeyDown('Ctrl+H')
if channelsWindow then
channelsWindow:destroy()
@ -287,6 +290,10 @@ function Console.setTextEditText(text)
consoleTextEdit:setText(text)
end
function Console.openHelp()
g_game.joinChannel(HELP_CHANNEL)
end
function Console.addTab(name, focus)
local tab = consoleTabBar:addTab(name)
if focus then

View File

@ -12,14 +12,17 @@ function VipList.init()
onVipStateChange = VipList.onVipStateChange })
Keyboard.bindKeyDown('Ctrl+P', VipList.toggle)
vipWindow = displayUI('viplist.otui', GameInterface.getLeftPanel())
vipButton = TopMenu.addGameToggleButton('vipListButton', tr('VIP list'), 'viplist.png', VipList.toggle)
vipButton = TopMenu.addGameToggleButton('vipListButton', tr('VIP list') .. ' (Ctrl+P)', 'viplist.png', VipList.toggle)
vipButton:setOn(true)
VipList.refresh()
end
function VipList.terminate()
Keyboard.unbindKeyDown('Ctrl+P')
disconnect(g_game, { onGameEnd = VipList.clear,
onAddVip = VipList.onAddVip,
onVipStateChange = VipList.onVipStateChange })

View File

@ -167,6 +167,7 @@ void Application::run()
if(!m_initialized)
return;
bool cacheForeground = true;
m_stopping = false;
m_running = true;
@ -189,6 +190,15 @@ void Application::run()
bool redraw = false;
bool updateForeground = false;
bool canCacheForeground = g_graphics.canCacheBackbuffer() && m_foregroundFrameCounter.getMaxFps() != 0;
if(cacheForeground != canCacheForeground) {
cacheForeground = canCacheForeground;
if(cacheForeground)
glColorMask(1,1,1,1);
else
glColorMask(1,1,1,0);
}
if(m_backgroundFrameCounter.shouldProcessNextFrame()) {
redraw = true;
@ -201,29 +211,35 @@ void Application::run()
if(redraw) {
g_graphics.beginRender();
Rect viewportRect(0, 0, g_graphics.getViewportSize());
if(cacheForeground) {
Rect viewportRect(0, 0, g_graphics.getViewportSize());
// draw the foreground into a texture
if(updateForeground) {
// draw the foreground into a texture
if(updateForeground) {
m_foregroundFrameCounter.processNextFrame();
// draw foreground
g_painter->clear(Color::black);
g_ui.render(Fw::ForegroundPane);
// copy the foreground to a texture
m_foreground->copyFromScreen(viewportRect);
}
// draw background (animated stuff)
m_backgroundFrameCounter.processNextFrame();
g_ui.render(Fw::BackgroundPane);
// draw the foreground (steady stuff)
g_painter->setColor(Color::white);
g_painter->setOpacity(1.0);
g_painter->drawTexturedRect(viewportRect, m_foreground, viewportRect);
} else {
m_foregroundFrameCounter.processNextFrame();
// draw foreground
g_painter->clear(Color::black);
g_ui.render(true);
// copy the foreground to a texture
m_foreground->copyFromScreen(viewportRect);
m_backgroundFrameCounter.processNextFrame();
g_ui.render(Fw::BothPanes);
}
// draw background (animated stuff)
m_backgroundFrameCounter.processNextFrame();
g_ui.render(false);
// draw the foreground (steady stuff)
g_painter->setColor(Color::white);
g_painter->setOpacity(1.0);
g_painter->drawTexturedRect(viewportRect, m_foreground, viewportRect);
g_graphics.endRender();
// update screen pixels
@ -237,9 +253,8 @@ void Application::run()
m_foregroundFrameCounter.update();
int sleepMicros = std::min(m_backgroundFrameCounter.getMaximumSleepMicros(), m_foregroundFrameCounter.getMaximumSleepMicros());
if(sleepMicros >= AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP) {
if(sleepMicros >= AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP)
stdext::microsleep(AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP);
}
} else {
// sleeps until next poll to avoid massive cpu usage
@ -285,8 +300,10 @@ void Application::resize(const Size& size)
g_ui.resize(size);
m_onInputEvent = false;
m_foreground = TexturePtr(new Texture(size.width(), size.height()));
m_foreground->setUpsideDown(true);
if(g_graphics.canCacheBackbuffer()) {
m_foreground = TexturePtr(new Texture(size));
m_foreground->setUpsideDown(true);
}
m_mustRepaint = true;
}

View File

@ -262,6 +262,12 @@ namespace Fw
DraggingState = 2048,
LastWidgetState = 4096
};
enum DrawPane {
ForegroundPane = 1,
BackgroundPane = 2,
BothPanes = 3
};
}
#endif

View File

@ -24,7 +24,7 @@
#include "graphics.h"
#include <framework/core/eventdispatcher.h>
/*
AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) :
Texture(),
m_numFrames(numFrames)
@ -48,7 +48,7 @@ AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFra
AnimatedTexture::~AnimatedTexture()
{
glDeleteTextures(m_numFrames, &m_framesTextureId[0]);
m_textureId = 0;
m_id = 0;
}
void AnimatedTexture::enableBilinearFilter()
@ -65,7 +65,7 @@ void AnimatedTexture::processAnimation()
m_currentFrame++;
if(m_currentFrame >= m_numFrames)
m_currentFrame = 0;
m_textureId = m_framesTextureId[m_currentFrame];
m_id = m_framesTextureId[m_currentFrame];
AnimatedTexturePtr self = asAnimatedTexture();
@ -73,3 +73,4 @@ void AnimatedTexture::processAnimation()
if(self.use_count() > 1)
g_eventDispatcher.scheduleEvent(std::bind(&AnimatedTexture::processAnimation, self), m_framesDelay[m_currentFrame]);
}
*/

View File

@ -24,7 +24,7 @@
#define ANIMATEDTEXTURE_H
#include "texture.h"
/*
class AnimatedTexture : public Texture
{
public:
@ -43,5 +43,5 @@ private:
int m_currentFrame;
ticks_t m_lastAnimCheckTicks;
};
*/
#endif

View File

@ -63,7 +63,7 @@ void FrameBuffer::resize(const Size& size)
if(m_texture && m_texture->getSize() == size)
return;
m_texture = TexturePtr(new Texture(size.width(), size.height(), 4));
m_texture = TexturePtr(new Texture(size));
m_texture->setSmooth(true);
m_texture->setUpsideDown(true);
@ -76,8 +76,10 @@ void FrameBuffer::resize(const Size& size)
g_logger.fatal("Unable to setup framebuffer object");
internalRelease();
} else {
m_screenBackup = TexturePtr(new Texture(size.width(), size.height()));
m_screenBackup->setUpsideDown(true);
if(m_backuping) {
m_screenBackup = TexturePtr(new Texture(size));
m_screenBackup->setUpsideDown(true);
}
}
}
@ -126,7 +128,7 @@ void FrameBuffer::internalBind()
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
m_prevBoundFbo = boundFbo;
boundFbo = m_fbo;
} else {
} else if(m_backuping) {
// backup screen color buffer into a texture
m_screenBackup->copyFromScreen(Rect(0, 0, getSize()));
}
@ -145,10 +147,12 @@ void FrameBuffer::internalRelease()
m_texture->copyFromScreen(screenRect);
// restore screen original content
Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
g_painter->setCompositionMode(oldComposition);
if(m_backuping) {
Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
g_painter->setCompositionMode(oldComposition);
}
}
}

View File

@ -40,8 +40,11 @@ public:
void draw(const Rect& dest);
void draw(const Rect& dest, const Rect& src);
void setBackuping(bool enabled) { m_backuping = enabled; }
TexturePtr getTexture() { return m_texture; }
Size getSize();
bool isBackuping() { return m_backuping; }
private:
void internalCreate();
@ -53,6 +56,7 @@ private:
Size m_oldViewportSize;
uint m_fbo;
uint m_prevBoundFbo;
Boolean<true> m_backuping;
static uint boundFbo;
};

View File

@ -92,12 +92,6 @@ void Graphics::init()
if(m_maxTextureSize == -1 || m_maxTextureSize > maxTextureSize)
m_maxTextureSize = maxTextureSize;
// check if we have alpha channel in the color buffer, because we need alpha channel for glCopyTexSubImage2D
GLint alphaBits = 0;
glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
if(alphaBits <= 0)
g_logger.fatal("OpenGL visual doesn't have an alpha buffer");
selectPainterEngine(m_prefferedPainterEngine);
m_emptyTexture = TexturePtr(new Texture);
}
@ -143,6 +137,8 @@ bool Graphics::parseOption(const std::string& option)
m_useNonPowerOfTwoTextures = false;
else if(option == "-no-clamp-to-edge")
m_useClampToEdge = false;
else if(option == "-no-backbuffer-cache")
m_cacheBackbuffer = false;
else if(option == "-opengl1")
m_prefferedPainterEngine = Painter_OpenGL1;
else if(option == "-opengl2")
@ -368,3 +364,16 @@ bool Graphics::canUseBlendFuncSeparate()
return true;
#endif
}
bool Graphics::canCacheBackbuffer()
{
#if OPENGL_ES==2
return m_cacheBackbuffer;
#elif OPENGL_ES==1
return false;
#else
if(!GLEW_VERSION_1_4)
return false;
return m_cacheBackbuffer;
#endif
}

View File

@ -70,6 +70,7 @@ public:
bool canUseHardwareMipmaps();
bool canUseClampToEdge();
bool canUseBlendFuncSeparate();
bool canCacheBackbuffer();
private:
Size m_viewportSize;
@ -84,6 +85,7 @@ private:
Boolean<true> m_useMipmaps;
Boolean<true> m_useHardwareMipmaps;
Boolean<true> m_useClampToEdge;
Boolean<true> m_cacheBackbuffer;
PainterEngine m_prefferedPainterEngine;
PainterEngine m_selectedPainterEngine;
};

View File

@ -30,7 +30,7 @@ Image::Image(const Size& size, int bpp, uint8 *pixels)
{
m_size = size;
m_bpp = bpp;
m_pixels.resize(size.area() * bpp);
m_pixels.resize(size.area() * bpp, 0);
if(pixels)
memcpy(&m_pixels[0], pixels, m_pixels.size());
}
@ -84,8 +84,10 @@ void Image::overwriteMask(const Color& maskedColor, const Color& insideColor, co
}
}
void Image::append(const Point& dest, const ImagePtr& other)
void Image::blit(const Point& dest, const ImagePtr& other)
{
assert(m_bpp == 4);
if(!other)
return;
@ -103,3 +105,109 @@ void Image::append(const Point& dest, const ImagePtr& other)
}
}
}
void Image::paste(const ImagePtr& other)
{
assert(m_bpp == 4);
if(!other)
return;
uint8* otherPixels = other->getPixelData();
for(int p = 0; p < other->getPixelCount(); ++p) {
int x = p % other->getWidth();
int y = p / other->getWidth();
int pos = (y * m_size.width() + x) * 4;
m_pixels[pos+0] = otherPixels[p*4+0];
m_pixels[pos+1] = otherPixels[p*4+1];
m_pixels[pos+2] = otherPixels[p*4+2];
m_pixels[pos+3] = otherPixels[p*4+3];
}
}
bool Image::nextMipmap()
{
assert(m_bpp == 4);
assert(stdext::is_power_of_two(m_size.width()) && stdext::is_power_of_two(m_size.height()));
if(m_size.width() == 1 || m_size.height() == 1)
return false;
Size size = m_size / 2;
std::vector<uint8> pixels(size.area()*4, 0xFF);
m_pixels = pixels;
m_size = size;
return true;
}
/*
*
void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels)
{
bind();
assert(stdext::is_power_of_two(m_glSize.width()) && stdext::is_power_of_two(m_glSize.height()));
Size inSize = m_glSize;
Size outSize = inSize / 2;
std::vector<uint8> outPixels;
int mipmap = 1;
while(true) {
outPixels.resize(outSize.area()*4, 0);
// this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4];
inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4];
inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4];
inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4];
uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4];
int pixelsSum[4];
for(int i=0;i<4;++i)
pixelsSum[i] = 0;
int usedPixels = 0;
for(int j=0;j<4;++j) {
// ignore colors of complete alpha pixels
if(inPixel[j][3] < 16)
continue;
for(int i=0;i<4;++i)
pixelsSum[i] += inPixel[j][i];
usedPixels++;
}
// try to guess the alpha pixel more accurately
for(int i=0;i<4;++i) {
if(usedPixels > 0)
outPixel[i] = pixelsSum[i] / usedPixels;
else
outPixel[i] = 0;
}
outPixel[3] = pixelsSum[3]/4;
}
}
glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]);
if(inSize.width() == 1 || inSize.height() == 1)
break;
inPixels = std::move(outPixels);
inSize /= 2;
outSize /= 2;
}
if(!m_hasMipmaps) {
m_hasMipmaps = true;
setupFilters();
}
}
*/

View File

@ -35,7 +35,9 @@ public:
static ImagePtr loadPNG(const std::string& file);
void overwriteMask(const Color& maskedColor, const Color& insideColor = Color::white, const Color& outsideColor = Color::alpha);
void append(const Point& dest, const ImagePtr &other);
void blit(const Point& dest, const ImagePtr& other);
void paste(const ImagePtr& other);
bool nextMipmap();
std::vector<uint8>& getPixels() { return m_pixels; }
uint8* getPixelData() { return &m_pixels[0]; }

View File

@ -27,104 +27,68 @@
Texture::Texture()
{
m_textureId = 0;
m_id = 0;
}
Texture::Texture(const ImagePtr& image)
Texture::Texture(const Size& size)
{
internalLoadGLTexture(image->getPixelData(), image->getBpp(), image->getWidth(), image->getHeight());
m_id = 0;
if(!setupSize(size))
return;
createTexture();
bind();
setupPixels(0, m_glSize, nullptr, 4);
setupWrap();
setupFilters();
}
Texture::Texture(int width, int height, int channels, uchar *pixels)
Texture::Texture(const ImagePtr& image, bool buildMipmaps)
{
// generate opengl texture
internalLoadGLTexture(pixels, channels, width, height);
m_id = 0;
if(!setupSize(image->getSize(), buildMipmaps))
return;
createTexture();
ImagePtr glImage = image;
if(m_size != m_glSize) {
glImage = ImagePtr(new Image(m_glSize, image->getBpp()));
glImage->paste(image);
} else
glImage = image;
bind();
/*
if(buildMipmaps) {
int level = 0;
do {
setupPixels(level++, glImage->getSize(), glImage->getPixelData(), glImage->getBpp());
} while(glImage->nextMipmap());
m_hasMipmaps = true;
} else
*/
setupPixels(0, glImage->getSize(), glImage->getPixelData(), glImage->getBpp());
setupWrap();
setupFilters();
}
Texture::~Texture()
{
// free texture from gl memory
if(m_textureId > 0)
glDeleteTextures(1, &m_textureId);
if(m_id > 0)
glDeleteTextures(1, &m_id);
}
uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int height)
void Texture::bind()
{
m_size.resize(width, height);
// convert texture pixel data to power of two size, only required for OpenGL 1.5 or older
std::vector<uint8> tmp;
if(!g_graphics.canUseNonPowerOfTwoTextures()) {
int glWidth = 1;
while(glWidth < width)
glWidth = glWidth << 1;
int glHeight = 1;
while(glHeight < height)
glHeight = glHeight << 1;
if(m_size != m_glSize && pixels) {
tmp.resize(glHeight*glWidth*channels, 0);
for(int y=0; y<height; ++y)
for(int x=0; x<width; ++x)
for(int i=0; i<channels; ++i)
tmp[y*glWidth*channels+x*channels+i] = pixels[y*width*channels+x*channels+i];
pixels = &tmp[0];
}
m_glSize.resize(glWidth, glHeight);
} else
m_glSize = m_size;
setupTranformMatrix();
// checks texture max size
if(std::max(m_glSize.width(), m_glSize.height()) > g_graphics.getMaxTextureSize()) {
g_logger.error(stdext::format("loading texture with size %dx%d failed, "
"the maximum size allowed by the graphics card is %dx%d,"
"to prevent crashes the texture will be displayed as a blank texture",
width, height, g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize()));
//TODO: make a workaround, could be bilinear scaling the texture
return 0;
}
// generate gl texture
GLuint id;
glGenTextures(1, &id);
assert(id != 0);
m_textureId = id;
bind();
// detect pixels GL format
GLenum format = 0;
switch(channels) {
case 4:
format = GL_RGBA;
break;
case 3:
format = GL_RGB;
break;
case 2:
format = GL_LUMINANCE_ALPHA;
break;
case 1:
format = GL_LUMINANCE;
break;
}
// load pixels into gl memory
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_glSize.width(), m_glSize.height(), 0, format, GL_UNSIGNED_BYTE, pixels);
GLint texParam = GL_REPEAT;
if(g_graphics.canUseClampToEdge())
texParam = GL_CLAMP_TO_EDGE; // disable texture borders by default
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
setupFilters();
return id;
// must reset painter texture state
g_painter->setTexture(this);
glBindTexture(GL_TEXTURE_2D, m_id);
}
void Texture::copyFromScreen(const Rect& screenRect)
@ -133,25 +97,7 @@ void Texture::copyFromScreen(const Rect& screenRect)
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height());
}
void Texture::bind()
{
// must reset painter texture state
g_painter->setTexture(this);
glBindTexture(GL_TEXTURE_2D, m_textureId);
}
void Texture::generateMipmaps()
{
if(!generateHardwareMipmaps()) {
// fallback to software mipmaps generation, this can be slow
//FIXME: disabled because mipmaps size needs to be in base of 2,
// and the current algorithmn does not support that
//generateSoftwareMipmaps(getPixels());
g_logger.traceError("non power of 2.");
}
}
bool Texture::generateHardwareMipmaps()
bool Texture::buildHardwareMipmaps()
{
if(!g_graphics.canUseHardwareMipmaps())
return false;
@ -188,69 +134,43 @@ void Texture::setUpsideDown(bool upsideDown)
setupTranformMatrix();
}
void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels)
void Texture::createTexture()
{
bind();
glGenTextures(1, &m_id);
assert(m_id != 0);
}
if(!m_hasMipmaps) {
m_hasMipmaps = true;
setupFilters();
bool Texture::setupSize(const Size& size, bool forcePowerOfTwo)
{
Size glSize;
if(!g_graphics.canUseNonPowerOfTwoTextures() || forcePowerOfTwo)
glSize.resize(stdext::to_power_of_two(size.width()), stdext::to_power_of_two(size.height()));
else
glSize = size;
// checks texture max size
if(std::max(glSize.width(), glSize.height()) > g_graphics.getMaxTextureSize()) {
g_logger.error(stdext::format("loading texture with size %dx%d failed, "
"the maximum size allowed by the graphics card is %dx%d,"
"to prevent crashes the texture will be displayed as a blank texture",
size.width(), size.height(), g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize()));
return false;
}
Size inSize = getSize();
Size outSize = inSize / 2;
std::vector<uint8> outPixels;
m_size = size;
m_glSize = glSize;
setupTranformMatrix();
return true;
}
int mipmap = 1;
while(true) {
outPixels.resize(outSize.area()*4);
void Texture::setupWrap()
{
GLint texParam = GL_REPEAT;
if(g_graphics.canUseClampToEdge())
texParam = GL_CLAMP_TO_EDGE; // disable texture borders by default
// this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4];
inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4];
inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4];
inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4];
uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4];
int pixelsSum[4];
for(int i=0;i<4;++i)
pixelsSum[i] = 0;
int usedPixels = 0;
for(int j=0;j<4;++j) {
// ignore colors of complete alpha pixels
if(inPixel[j][3] < 16)
continue;
for(int i=0;i<4;++i)
pixelsSum[i] += inPixel[j][i];
usedPixels++;
}
// try to guess the alpha pixel more accurately
for(int i=0;i<4;++i) {
if(usedPixels > 0)
outPixel[i] = pixelsSum[i] / usedPixels;
else
outPixel[i] = 0;
}
outPixel[3] = pixelsSum[3]/4;
}
}
glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]);
if(inSize.width() == 1 || inSize.height() == 1)
break;
inPixels = std::move(outPixels);
inSize /= 2;
outSize /= 2;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
}
void Texture::setupFilters()
@ -280,3 +200,24 @@ void Texture::setupTranformMatrix()
0.0f, 0.0f, 1.0f };
}
}
void Texture::setupPixels(int level, const Size& size, uchar* pixels, int channels)
{
GLenum format = 0;
switch(channels) {
case 4:
format = GL_RGBA;
break;
case 3:
format = GL_RGB;
break;
case 2:
format = GL_LUMINANCE_ALPHA;
break;
case 1:
format = GL_LUMINANCE;
break;
}
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, pixels);
}

View File

@ -29,41 +29,35 @@ class Texture : public std::enable_shared_from_this<Texture>
{
public:
Texture();
Texture(const ImagePtr& image);
Texture(int width, int height, int channels = 4, uchar* pixels = NULL);
Texture(const Size& size);
Texture(const ImagePtr& image, bool buildMipmaps = false);
virtual ~Texture();
void copyFromScreen(const Rect& screenRect);
void bind();
void copyFromScreen(const Rect& screenRect);
bool buildHardwareMipmaps();
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation
void generateMipmaps();
/// Generate mipmaps via hardware if supported
bool generateHardwareMipmaps();
/// Generate mipmaps via software, which has a special algorithm for combining alpha pixels
void generateSoftwareMipmaps(std::vector<uint8> inPixels);
/// Activate texture anti-aliasing giving a better look when they are resized
void setSmooth(bool smooth);
void setUpsideDown(bool upsideDown);
GLuint getId() { return m_textureId; }
GLuint getId() { return m_id; }
int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); }
const Size& getSize() { return m_size; }
const Size& getGlSize() { return m_glSize; }
const Matrix3& getTransformMatrix() { return m_transformMatrix; }
bool isEmpty() { return m_textureId == 0; }
bool isEmpty() { return m_id == 0; }
bool hasMipmaps() { return m_hasMipmaps; }
protected:
void createTexture();
bool setupSize(const Size& size, bool forcePowerOfTwo = false);
void setupWrap();
void setupFilters();
void setupTranformMatrix();
GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h);
void setupPixels(int level, const Size& size, uchar *pixels, int channels = 4);
GLuint m_textureId;
GLuint m_id;
Size m_size;
Size m_glSize;
Matrix3 m_transformMatrix;

View File

@ -23,6 +23,7 @@
#include "texturemanager.h"
#include "animatedtexture.h"
#include "graphics.h"
#include "image.h"
#include <framework/core/resourcemanager.h>
#include <framework/thirdparty/apngloader.h>
@ -75,10 +76,13 @@ TexturePtr TextureManager::loadPNG(std::stringstream& file)
apng_data apng;
if(load_apng(file, &apng) == 0) {
if(apng.num_frames > 1) { // animated texture
uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
} else
texture = TexturePtr(new Texture(apng.width, apng.height, apng.bpp, apng.pdata));
//uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
//texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
g_logger.error("animated textures is disabled for a while");
} else {
ImagePtr image = ImagePtr(new Image(Size(apng.width, apng.height), apng.bpp, apng.pdata));
texture = TexturePtr(new Texture(image));
}
free_apng(&apng);
}

View File

@ -554,6 +554,7 @@ void Application::registerLuaFunctions()
g_lua.registerStaticClass("g_graphics");
g_lua.bindClassStaticFunction("g_graphics", "isPainterEngineAvailable", std::bind(&Graphics::isPainterEngineAvailable, &g_graphics, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_graphics", "selectPainterEngine", std::bind(&Graphics::selectPainterEngine, &g_graphics, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_graphics", "canCacheBackbuffer", std::bind(&Graphics::canCacheBackbuffer, &g_graphics));
g_lua.bindClassStaticFunction("g_graphics", "getPainterEngine", std::bind(&Graphics::getPainterEngine, &g_graphics));
g_lua.bindClassStaticFunction("g_graphics", "getViewportSize", std::bind(&Graphics::getViewportSize, &g_graphics));
g_lua.bindClassStaticFunction("g_graphics", "getVendor", std::bind(&Graphics::getVendor, &g_graphics));

View File

@ -319,7 +319,6 @@ void WIN32Window::internalChooseGLVisual()
if(!pixelFormat)
g_logger.fatal("Could not find a suitable pixel format");
pfd.cStencilBits = 8;
if(!SetPixelFormat(m_deviceContext, pixelFormat, &pfd))
g_logger.fatal("Could not set the pixel format");
}

View File

@ -370,10 +370,10 @@ void X11Window::internalChooseGLVisual()
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
#endif
EGL_RED_SIZE, 4,
EGL_GREEN_SIZE, 4,
EGL_BLUE_SIZE, 4,
EGL_ALPHA_SIZE, 4,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_NONE
};

View File

@ -52,9 +52,9 @@ void UIManager::terminate()
m_pressedWidget = nullptr;
}
void UIManager::render(bool foregroundPane)
void UIManager::render(Fw::DrawPane drawPane)
{
m_rootWidget->draw(m_rootWidget->getRect(), foregroundPane);
m_rootWidget->draw(m_rootWidget->getRect(), drawPane);
}
void UIManager::resize(const Size& size)

View File

@ -33,7 +33,7 @@ public:
void init();
void terminate();
void render(bool foregroundPane);
void render(Fw::DrawPane drawPane);
void resize(const Size& size);
void inputEvent(const InputEvent& event);

View File

@ -41,9 +41,9 @@ UITextEdit::UITextEdit()
blinkCursor();
}
void UITextEdit::drawSelf(bool foregroundPane)
void UITextEdit::drawSelf(Fw::DrawPane drawPane)
{
if(!foregroundPane)
if((drawPane & Fw::ForegroundPane) == 0)
return;
drawBackground(m_rect);

View File

@ -30,7 +30,7 @@ class UITextEdit : public UIWidget
public:
UITextEdit();
void drawSelf(bool foregroundPane);
void drawSelf(Fw::DrawPane drawPane);
private:
void update();

View File

@ -52,27 +52,27 @@ UIWidget::~UIWidget()
#endif
}
void UIWidget::draw(const Rect& visibleRect, bool foregroundPane)
void UIWidget::draw(const Rect& visibleRect, Fw::DrawPane drawPane)
{
if(m_clipping)
g_painter->setClipRect(visibleRect);
drawSelf(foregroundPane);
drawSelf(drawPane);
if(m_children.size() > 0) {
if(m_clipping)
g_painter->setClipRect(visibleRect.intersection(getPaddingRect()));
drawChildren(visibleRect, foregroundPane);
drawChildren(visibleRect, drawPane);
}
if(m_clipping)
g_painter->resetClipRect();
}
void UIWidget::drawSelf(bool foregroundPane)
void UIWidget::drawSelf(Fw::DrawPane drawPane)
{
if(!foregroundPane)
if((drawPane & Fw::ForegroundPane) == 0)
return;
// draw style components in order
@ -88,7 +88,7 @@ void UIWidget::drawSelf(bool foregroundPane)
drawText(m_rect);
}
void UIWidget::drawChildren(const Rect& visibleRect, bool foregroundPane)
void UIWidget::drawChildren(const Rect& visibleRect, Fw::DrawPane drawPane)
{
// draw children
for(const UIWidgetPtr& child : m_children) {
@ -107,10 +107,10 @@ void UIWidget::drawChildren(const Rect& visibleRect, bool foregroundPane)
if(child->getOpacity() < oldOpacity)
g_painter->setOpacity(child->getOpacity());
child->draw(childVisibleRect, foregroundPane);
child->draw(childVisibleRect, drawPane);
// debug draw box
if(foregroundPane && g_ui.isDrawingDebugBoxes()) {
if(g_ui.isDrawingDebugBoxes() && drawPane & Fw::ForegroundPane) {
g_painter->setColor(Color::green);
g_painter->drawBoundingRect(child->getRect());
}
@ -497,7 +497,10 @@ void UIWidget::applyStyle(const OTMLNodePtr& styleNode)
callLuaField("onStyleApply", styleNode->tag(), styleNode);
if(m_firstOnStyle) {
callLuaField("onSetup");
auto self = asUIWidget();
g_eventDispatcher.addEvent([self] {
self->callLuaField("onSetup");
});
// always focus new child
if(isFocusable() && isExplicitlyVisible() && isExplicitlyEnabled())
focus();

View File

@ -52,9 +52,9 @@ public:
virtual ~UIWidget();
protected:
virtual void draw(const Rect& visibleRect, bool foregroundPane);
virtual void drawSelf(bool foregroundPane);
virtual void drawChildren(const Rect& visibleRect, bool foregroundPane);
virtual void draw(const Rect& visibleRect, Fw::DrawPane drawPane);
virtual void drawSelf(Fw::DrawPane drawPane);
virtual void drawChildren(const Rect& visibleRect, Fw::DrawPane drawPane);
friend class UIManager;

View File

@ -76,11 +76,7 @@ TexturePtr& ThingType::getSpriteMask(int w, int h, int l, int x, int y, int z, i
static Color maskColors[LastMask] = { Color::yellow, Color::red, Color::green, Color::blue };
maskImage->overwriteMask(maskColors[mask]);
maskTexture = TexturePtr(new Texture(maskImage));
maskTexture->setSmooth(true);
if(g_graphics.canUseMipmaps())
maskTexture->generateSoftwareMipmaps(maskImage->getPixels());
maskTexture = TexturePtr(new Texture(maskImage, true));
}
}
@ -120,7 +116,7 @@ TexturePtr& ThingType::getTexture(int animationPhase)
Point spritePos = Point(m_dimensions[Width] - w - 1,
m_dimensions[Height] - h - 1) * Otc::TILE_PIXELS;
fullImage->append(framePos + spritePos, spriteImage);
fullImage->blit(framePos + spritePos, spriteImage);
}
}
}
@ -139,16 +135,12 @@ TexturePtr& ThingType::getTexture(int animationPhase)
}
m_texturesFramesRects[animationPhase][frameIndex] = drawRect;
m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft() - framePos;
m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft() - framePos + Point(1,1);
}
}
}
}
animationPhaseTexture = TexturePtr(new Texture(fullImage));
animationPhaseTexture->setSmooth(true);
//if(g_graphics.canUseMipmaps())
//animationPhaseTexture->generateSoftwareMipmaps(fullImage->getPixels());
animationPhaseTexture = TexturePtr(new Texture(fullImage, true));
}
return animationPhaseTexture;
}

View File

@ -51,20 +51,45 @@ void OTClient::init(const std::vector<std::string>& args)
"Compiled by: ", BUILD_COMPILER, "\n",
"Build type: ", BUILD_TYPE, "\n");
return;
} else if(arg == "-help" || arg == "--help" || arg == "-h") {
/*
* if(option == "-no-draw-arrays")
m_useDrawArrays = false;
else if(option == "-no-fbos")
m_useFBO = false;
else if(option == "-no-mipmaps")
m_useMipmaps = false;
else if(option == "-no-hardware-mipmaps")
m_useHardwareMipmaps = false;
else if(option == "-no-smooth")
m_useBilinearFiltering = false;
else if(option == "-hardware-buffers")
m_useHardwareBuffers = true;
else if(option == "-no-non-power-of-two-textures")
m_useNonPowerOfTwoTextures = false;
else if(option == "-no-clamp-to-edge")
m_useClampToEdge = false;
else if(option == "-no-backbuffer-cache")
m_cacheBackbuffer = false;
else if(option == "-opengl1")
m_prefferedPainterEngine = Painter_OpenGL1;
else if(option == "-opengl2")
*/
} else if(arg == "-help" || arg == "--help" || arg == "-h" || arg == "-?" || arg == "/?") {
stdext::print(
"Usage: ", args[0], " [options]\n"
"Options:\n"
" -help Display this information and exit\n"
" -version Display version and exit\n"
"\n"
" -no-fbos Disable usage of opengl framebuffer objects\n"
" -no-mipmapping Disable texture mipmaping\n"
" -no-smoothing Disable texture smoothing (bilinear filter)\n"
" -no-hardware-buffering Disable buffering vertex arrays in hardware\n"
" -realtime-mipmapping Improve framebuffer smoothing quality by\n"
" generating mipmaps in realtime when hardware\n"
" mipmap generation implementation is available\n");
" -help Display this information and exit\n"
" -version Display version and exit\n"
" \n"
" -no-fbos Disable usage of opengl framebuffer objects\n"
" -no-mipmaps Disable texture mipmaping\n"
" -no-smooth Disable texture smoothing (bilinear filter)\n"
" -no-non-power-of-two-textures Use only power of two textures\n"
" -no-clamp-to-edge Don't use GL_CLAMP_TO_EDGE\n"
" -no-backbuffer-cache Don't allow backbuffer caching\n"
" -hardware-buffers Cache vertex arrays in hardware\n"
" -opengl1 Use OpenGL 1.x painter\n"
" -opengl2 Use OpenGL 2.0 painter\n");
return;
} else {
stdext::println("Unrecognized option '", arg, "', please see -help for available options list");

View File

@ -24,12 +24,12 @@
#include <framework/otml/otml.h>
#include <framework/graphics/graphics.h>
void UICreature::drawSelf(bool foregroundPane)
void UICreature::drawSelf(Fw::DrawPane drawPane)
{
if(!foregroundPane)
if((drawPane & Fw::ForegroundPane) == 0)
return;
UIWidget::drawSelf(foregroundPane);
UIWidget::drawSelf(drawPane);
if(m_creature) {
Rect drawRect = getPaddingRect();

View File

@ -30,7 +30,7 @@
class UICreature : public UIWidget
{
public:
void drawSelf(bool foregroundPane);
void drawSelf(Fw::DrawPane drawPane);
void setCreature(const CreaturePtr& creature) { m_creature = creature; }
void setFixedCreatureSize(bool fixed) { m_fixedCreatureSize = fixed; }

View File

@ -30,9 +30,9 @@ UIItem::UIItem()
m_dragable = true;
}
void UIItem::drawSelf(bool foregroundPane)
void UIItem::drawSelf(Fw::DrawPane drawPane)
{
if(!foregroundPane)
if((drawPane & Fw::ForegroundPane) == 0)
return;
// draw style components in order

View File

@ -31,7 +31,7 @@ class UIItem : public UIWidget
{
public:
UIItem();
void drawSelf(bool foregroundPane);
void drawSelf(Fw::DrawPane drawPane);
void setItemId(int id);
void setItemCount(int count) { if(m_item) m_item->setCount(count); }

View File

@ -45,21 +45,25 @@ UIMap::~UIMap()
g_map.removeMapView(m_mapView);
}
void UIMap::drawSelf(bool foregroundPane)
void UIMap::drawSelf(Fw::DrawPane drawPane)
{
if(foregroundPane) {
UIWidget::drawSelf(foregroundPane);
UIWidget::drawSelf(drawPane);
if(drawPane & Fw::ForegroundPane) {
// draw map border
g_painter->setColor(Color::black);
g_painter->drawBoundingRect(m_mapRect.expanded(1));
g_painter->saveState();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->setColor(Color::alpha);
g_painter->drawFilledRect(m_mapRect);
g_painter->restoreSavedState();
} else {
if(drawPane != Fw::BothPanes) {
g_painter->saveState();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->setColor(Color::alpha);
g_painter->drawFilledRect(m_mapRect);
g_painter->restoreSavedState();
}
}
if(drawPane & Fw::BackgroundPane) {
g_painter->setColor(Color::white);
m_mapView->draw(m_mapRect);
}

View File

@ -35,7 +35,7 @@ public:
UIMap();
~UIMap();
void drawSelf(bool foregroundPane);
void drawSelf(Fw::DrawPane drawPane);
bool setZoom(int zoom);
bool zoomIn();