graphics optimizations/fixes/features

* cache text vertex for StaticText, AnimatedText and Creature names
* improved outfit rendering
* fully compatible with OpenGL 1.1
* enable mipmaping for game sprites again
* Ctrl+W hotkey clean game texts
This commit is contained in:
Eduardo Bart 2012-06-08 21:40:22 -03:00
parent 1a7f2a44fc
commit 95cf7eb788
21 changed files with 180 additions and 155 deletions

View File

@ -53,6 +53,7 @@ function GameInterface.init()
Keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel, 250)
Keyboard.bindKeyDown('Ctrl+Q', GameInterface.tryLogout, gameRootPanel)
Keyboard.bindKeyDown('Ctrl+L', GameInterface.tryLogout, gameRootPanel)
Keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() TextMessage.clearMessages() end, gameRootPanel)
if g_game.isOnline() then
GameInterface.show()

View File

@ -197,6 +197,7 @@ SET(framework_SOURCES ${framework_SOURCES}
# framework graphics
${CMAKE_CURRENT_LIST_DIR}/graphics/font.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/cachedtext.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/fontmanager.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/painter.cpp

View File

@ -167,7 +167,6 @@ void Application::run()
if(!m_initialized)
return;
bool cacheForeground = true;
m_stopping = false;
m_running = true;
@ -190,14 +189,7 @@ 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);
}
bool cacheForeground = g_graphics.canCacheBackbuffer() && m_foregroundFrameCounter.getMaxFps() != 0;
if(m_backgroundFrameCounter.shouldProcessNextFrame()) {
redraw = true;
@ -219,11 +211,15 @@ void Application::run()
m_foregroundFrameCounter.processNextFrame();
// draw foreground
g_painter->clear(Color::black);
g_painter->setAlphaWriting(true);
g_painter->clear(Color::alpha);
g_ui.render(Fw::ForegroundPane);
// copy the foreground to a texture
m_foreground->copyFromScreen(viewportRect);
g_painter->clear(Color::black);
g_painter->setAlphaWriting(false);
}
// draw background (animated stuff)

View File

@ -30,6 +30,7 @@ class Texture;
class Image;
class AnimatedTexture;
class Font;
class CachedText;
class FrameBuffer;
class Shader;
class ShaderProgram;
@ -46,6 +47,7 @@ typedef std::shared_ptr<Image> ImagePtr;
typedef std::shared_ptr<Texture> TexturePtr;
typedef std::shared_ptr<AnimatedTexture> AnimatedTexturePtr;
typedef std::shared_ptr<Font> FontPtr;
typedef std::shared_ptr<CachedText> CachedTextPtr;
typedef std::shared_ptr<FrameBuffer> FrameBufferPtr;
typedef std::shared_ptr<Shader> ShaderPtr;
typedef std::shared_ptr<ShaderProgram> ShaderProgramPtr;

View File

@ -131,13 +131,57 @@ 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)
int iw = m_size.width();
int ih = m_size.height();
if(iw == 1 && ih == 1)
return false;
Size size = m_size / 2;
std::vector<uint8> pixels(size.area()*4, 0xFF);
int ow = iw > 1 ? iw/2 : 1;
int oh = ih > 1 ? ih/2 : 1;
std::vector<uint8> pixels(ow*oh*4, 0xFF);
//FIXME: calculate mipmaps for 8x1, 4x1, 2x1 ...
if(iw != 1 && ih != 1) {
for(int x=0;x<ow;++x) {
for(int y=0;y<oh;++y) {
uint8 *inPixel[4];
inPixel[0] = &m_pixels[((y*2)*iw + (x*2))*4];
inPixel[1] = &m_pixels[((y*2)*iw + (x*2)+1)*4];
inPixel[2] = &m_pixels[((y*2+1)*iw + (x*2))*4];
inPixel[3] = &m_pixels[((y*2+1)*iw + (x*2)+1)*4];
uint8 *outPixel = &pixels[(y*ow + 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;
}
}
}
m_pixels = pixels;
m_size = size;
m_size = Size(ow, oh);
return true;
}

View File

@ -34,6 +34,7 @@ Painter::Painter()
m_compositionMode = CompositionMode_Normal;
m_shaderProgram = nullptr;
m_texture = nullptr;
m_alphaWriting = false;
}
void Painter::resetState()
@ -44,6 +45,7 @@ void Painter::resetState()
resetClipRect();
resetShaderProgram();
resetTexture();
resetAlphaWriting();
}
void Painter::refreshState()
@ -51,6 +53,7 @@ void Painter::refreshState()
updateGlCompositionMode();
updateGlClipRect();
updateGlTexture();
updateGlAlphaWriting();
}
void Painter::saveState()
@ -64,6 +67,7 @@ void Painter::saveState()
m_olderStates[m_oldStateIndex].clipRect = m_clipRect;
m_olderStates[m_oldStateIndex].shaderProgram = m_shaderProgram;
m_olderStates[m_oldStateIndex].texture = m_texture;
m_olderStates[m_oldStateIndex].alphaWriting = m_alphaWriting;
m_oldStateIndex++;
}
@ -137,6 +141,15 @@ void Painter::setTexture(Texture* texture)
}
}
void Painter::setAlphaWriting(bool enable)
{
if(m_alphaWriting == enable)
return;
m_alphaWriting = enable;
updateGlAlphaWriting();
}
void Painter::updateGlTexture()
{
if(m_glTextureId != 0)
@ -177,3 +190,11 @@ void Painter::updateGlClipRect()
glDisable(GL_SCISSOR_TEST);
}
}
void Painter::updateGlAlphaWriting()
{
if(m_alphaWriting)
glColorMask(1,1,1,1);
else
glColorMask(1,1,1,0);
}

View File

@ -52,6 +52,7 @@ public:
Rect clipRect;
Texture *texture;
PainterShaderProgram *shaderProgram;
bool alphaWriting;
};
Painter();
@ -85,6 +86,7 @@ public:
virtual void setClipRect(const Rect& clipRect);
virtual void setShaderProgram(PainterShaderProgram *shaderProgram) { m_shaderProgram = shaderProgram; }
virtual void setTexture(Texture *texture);
void setAlphaWriting(bool enable);
void setShaderProgram(const PainterShaderProgramPtr& shaderProgram) { setShaderProgram(shaderProgram.get()); }
void setTexture(const TexturePtr& texture) { setTexture(texture.get()); }
@ -96,6 +98,7 @@ public:
CompositionMode getCompositionMode() { return m_compositionMode; }
Rect getClipRect() { return m_clipRect; }
PainterShaderProgram *getShaderProgram() { return m_shaderProgram; }
bool getAlphaWriting() { return m_alphaWriting; }
void resetColor() { setColor(Color::white); }
void resetOpacity() { setOpacity(1.0f); }
@ -103,11 +106,13 @@ public:
void resetCompositionMode() { setCompositionMode(CompositionMode_Normal); }
void resetShaderProgram() { setShaderProgram(nullptr); }
void resetTexture() { setTexture(nullptr); }
void resetAlphaWriting() { setAlphaWriting(false); }
protected:
void updateGlTexture();
void updateGlCompositionMode();
void updateGlClipRect();
void updateGlAlphaWriting();
CoordsBuffer m_coordsBuffer;
@ -119,6 +124,7 @@ protected:
Rect m_clipRect;
Texture *m_texture;
PainterShaderProgram *m_shaderProgram;
bool m_alphaWriting;
PainterState m_olderStates[10];
int m_oldStateIndex;

View File

@ -62,7 +62,6 @@ Texture::Texture(const ImagePtr& image, bool buildMipmaps)
bind();
/*
if(buildMipmaps) {
int level = 0;
do {
@ -70,7 +69,6 @@ Texture::Texture(const ImagePtr& image, bool buildMipmaps)
} while(glImage->nextMipmap());
m_hasMipmaps = true;
} else
*/
setupPixels(0, glImage->getSize(), glImage->getPixelData(), glImage->getBpp());
setupWrap();

View File

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

View File

@ -28,20 +28,22 @@
AnimatedText::AnimatedText()
{
m_font = g_fonts.getFont("verdana-11px-rounded");
m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_cachedText.setAlign(Fw::AlignLeft);
}
void AnimatedText::draw(const Point& dest, const Rect& visibleRect)
{
Point p = dest;
p.x += 20 - m_textSize.width() / 2;
Size textSize = m_cachedText.getTextSize();
p.x += 20 - textSize.width() / 2;
p.y += (-20 * m_animationTimer.ticksElapsed()) / Otc::ANIMATED_TEXT_DURATION;
Rect rect(p, m_textSize);
Rect rect(p, textSize);
if(visibleRect.contains(rect)) {
//TODO: cache into a framebuffer
g_painter->setColor(m_color);
m_font->drawText(m_text, rect, Fw::AlignLeft);
m_cachedText.draw(rect);
}
}
@ -61,6 +63,5 @@ void AnimatedText::setColor(int color)
void AnimatedText::setText(const std::string& text)
{
m_textSize = m_font->calculateTextRectSize(text);
m_text = text;
m_cachedText.setText(text);
}

View File

@ -26,6 +26,7 @@
#include "thing.h"
#include <framework/graphics/fontmanager.h>
#include <framework/core/timer.h>
#include <framework/graphics/cachedtext.h>
class AnimatedText : public Thing
{
@ -42,11 +43,9 @@ public:
bool isAnimatedText() { return true; }
private:
FontPtr m_font;
Size m_textSize;
std::string m_text;
Color m_color;
Timer m_animationTimer;
CachedText m_cachedText;
};
#endif

View File

@ -52,7 +52,8 @@ Creature::Creature() : Thing()
m_skull = Otc::SkullNone;
m_shield = Otc::ShieldNone;
m_emblem = Otc::EmblemNone;
m_informationFont = g_fonts.getFont("verdana-11px-rounded");
m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_nameCache.setAlign(Fw::AlignTopCenter);
}
/*
@ -85,21 +86,6 @@ void Creature::draw(const Point& dest, float scaleFactor, bool animate)
void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction)
{
/*
if(!outfitProgram) {
outfitProgram = PainterShaderProgramPtr(new PainterShaderProgram);
outfitProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader);
outfitProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/outfit.frag");
outfitProgram->link();
outfitProgram->bindUniformLocation(HEAD_COLOR_UNIFORM, "headColor");
outfitProgram->bindUniformLocation(BODY_COLOR_UNIFORM, "bodyColor");
outfitProgram->bindUniformLocation(LEGS_COLOR_UNIFORM, "legsColor");
outfitProgram->bindUniformLocation(FEET_COLOR_UNIFORM, "feetColor");
}
*/
int xPattern = 0, yPattern = 0, zPattern = 0;
// outfit is a real creature
if(m_outfit.getCategory() == ThingsType::Creature) {
int animationPhase = animateWalk ? m_walkAnimationPhase : 0;
@ -110,6 +96,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani
}
// xPattern => creature direction
int xPattern;
if(direction == Otc::NorthEast || direction == Otc::SouthEast)
xPattern = Otc::East;
else if(direction == Otc::NorthWest || direction == Otc::SouthWest)
@ -118,55 +105,29 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani
xPattern = direction;
// yPattern => creature addon
for(yPattern = 0; yPattern < getNumPatternsY(); yPattern++) {
for(int yPattern = 0; yPattern < getNumPatternsY(); yPattern++) {
// continue if we dont have this addon.
// continue if we dont have this addon
if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1))))
continue;
/*
g_painter->setShaderProgram(outfitProgram);
m_type->draw(dest, scaleFactor, 0, xPattern, yPattern, 0, animationPhase);
outfitProgram->bind();
outfitProgram->setUniformValue(HEAD_COLOR_UNIFORM, m_outfit.getHeadColor());
outfitProgram->setUniformValue(BODY_COLOR_UNIFORM, m_outfit.getBodyColor());
outfitProgram->setUniformValue(LEGS_COLOR_UNIFORM, m_outfit.getLegsColor());
outfitProgram->setUniformValue(FEET_COLOR_UNIFORM, m_outfit.getFeetColor());
*/
m_type->draw(dest, scaleFactor, 0, xPattern, yPattern, zPattern, animationPhase);
for(int h = 0; h < getDimensionHeight(); h++) {
for(int w = 0; w < getDimensionWidth(); w++) {
// setup texture outfit mask
/*
TexturePtr maskTex;
if(getLayers() > 1)
maskTex = m_type->getSprite(w, h, 1, xPattern, yPattern, zPattern, animationPhase);
outfitProgram->setTexture(maskTex, 1);
*/
Point p = dest + (-Point(w,h)*Otc::TILE_PIXELS)*scaleFactor;
if(getLayers() > 1) {
g_painter->saveState();
g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
g_painter->setColor(m_outfit.getHeadColor());
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::YellowMask);
g_painter->setColor(m_outfit.getBodyColor());
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::RedMask);
g_painter->setColor(m_outfit.getLegsColor());
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::GreenMask);
g_painter->setColor(m_outfit.getFeetColor());
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::BlueMask);
g_painter->restoreSavedState();
}
}
if(getLayers() > 1) {
Color oldColor = g_painter->getColor();
Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
g_painter->setColor(m_outfit.getHeadColor());
m_type->draw(dest, scaleFactor, ThingType::YellowMask, xPattern, yPattern, 0, animationPhase);
g_painter->setColor(m_outfit.getBodyColor());
m_type->draw(dest, scaleFactor, ThingType::RedMask, xPattern, yPattern, 0, animationPhase);
g_painter->setColor(m_outfit.getLegsColor());
m_type->draw(dest, scaleFactor, ThingType::GreenMask, xPattern, yPattern, 0, animationPhase);
g_painter->setColor(m_outfit.getFeetColor());
m_type->draw(dest, scaleFactor, ThingType::BlueMask, xPattern, yPattern, 0, animationPhase);
g_painter->setColor(oldColor);
g_painter->setCompositionMode(oldComposition);
}
//g_painter->resetShaderProgram();
}
// outfit is a creature imitating an item or the invisible effect
} else {
@ -203,6 +164,7 @@ void Creature::drawOutfit(const Rect& destRect, bool resize)
outfitBuffer = FrameBufferPtr(new FrameBuffer(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS)));
outfitBuffer->bind();
g_painter->setAlphaWriting(true);
g_painter->clear(Color::alpha);
internalDrawOutfit(Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South);
outfitBuffer->release();
@ -236,7 +198,8 @@ void Creature::drawInformation(const Point& point, bool useGray, const Rect& par
Rect backgroundRect = Rect(point.x-(13.5), point.y, 27, 4);
backgroundRect.bind(parentRect);
Rect textRect = Rect(point.x - m_nameSize.width() / 2.0, point.y-12, m_nameSize);
Size nameSize = m_nameCache.getTextSize();
Rect textRect = Rect(point.x - nameSize.width() / 2.0, point.y-12, nameSize);
textRect.bind(parentRect);
// distance them
@ -256,8 +219,7 @@ void Creature::drawInformation(const Point& point, bool useGray, const Rect& par
g_painter->setColor(fillColor);
g_painter->drawFilledRect(healthRect);
if(m_informationFont)
m_informationFont->drawText(m_name, textRect, Fw::AlignTopCenter);
m_nameCache.draw(textRect);
if(m_skull != Otc::SkullNone && m_skullTexture) {
g_painter->setColor(Color::white);
@ -447,8 +409,7 @@ void Creature::terminateWalk()
void Creature::setName(const std::string& name)
{
if(m_informationFont)
m_nameSize = m_informationFont->calculateTextRectSize(name);
m_nameCache.setText(name);
m_name = name;
}

View File

@ -28,6 +28,7 @@
#include <framework/core/declarations.h>
#include <framework/core/timer.h>
#include <framework/graphics/fontmanager.h>
#include <framework/graphics/cachedtext.h>
class Creature : public Thing
{
@ -104,7 +105,6 @@ protected:
uint32 m_id;
std::string m_name;
Size m_nameSize;
uint8 m_healthPercent;
Otc::Direction m_direction;
Outfit m_outfit;
@ -124,8 +124,7 @@ protected:
Boolean<false> m_showTimedSquare;
Boolean<false> m_showStaticSquare;
Boolean<false> m_removed;
FontPtr m_informationFont;
CachedText m_nameCache;
Color m_informationColor;
// walk related

View File

@ -124,6 +124,12 @@ void Map::cleanDynamicThings()
for(int i=0;i<=Otc::MAX_Z;++i)
m_floorMissiles[i].clear();
cleanTexts();
}
void Map::cleanTexts()
{
m_animatedTexts.clear();
m_staticTexts.clear();
}

View File

@ -38,6 +38,7 @@ public:
void save();
void clean();
void cleanDynamicThings();
void cleanTexts();
// thing related
void addThing(const ThingPtr& thing, const Position& pos, int stackPos = -1);

View File

@ -28,20 +28,21 @@
StaticText::StaticText()
{
m_font = g_fonts.getFont("verdana-11px-rounded");
m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_cachedText.setAlign(Fw::AlignCenter);
}
void StaticText::draw(const Point& dest, const Rect& parentRect)
{
Rect rect = Rect(dest - Point(m_textSize.width() / 2, m_textSize.height()) + Point(20, 5), m_textSize);
Size textSize = m_cachedText.getTextSize();
Rect rect = Rect(dest - Point(textSize.width() / 2, textSize.height()) + Point(20, 5), textSize);
Rect boundRect = rect;
boundRect.bind(parentRect);
// draw only if the real center is not too far from the parent center, or its a yell
if((boundRect.center() - rect.center()).length() < parentRect.width() / 15 || isYell()) {
//TODO: cache into a framebuffer
g_painter->setColor(m_color);
m_font->drawText(m_text, boundRect, Fw::AlignCenter);
m_cachedText.draw(boundRect);
}
}
@ -116,6 +117,6 @@ void StaticText::compose()
text += "\n";
}
m_text = m_font->wrapText(text, Otc::MAX_STATIC_TEXT_WIDTH);
m_textSize = m_font->calculateTextRectSize(m_text);
m_cachedText.setText(text);
m_cachedText.wrapText(Otc::MAX_STATIC_TEXT_WIDTH);
}

View File

@ -24,6 +24,7 @@
#define STATICTEXT_H
#include "thing.h"
#include <framework/graphics/cachedtext.h>
#include <framework/graphics/fontmanager.h>
class StaticText : public Thing
@ -48,13 +49,12 @@ public:
private:
void compose();
FontPtr m_font;
Size m_textSize;
Boolean<false> m_yell;
std::vector<std::string> m_messages;
std::string m_name, m_text;
std::string m_name;
Otc::SpeakType m_messageType;
Color m_color;
CachedText m_cachedText;
};
#endif

View File

@ -146,6 +146,7 @@ bool ThingsType::parseThingType(const FileStreamPtr& fin, ThingType& thingType)
thingType.m_textures.resize(thingType.m_dimensions[ThingType::AnimationPhases]);
thingType.m_texturesFramesRects.resize(thingType.m_dimensions[ThingType::AnimationPhases]);
thingType.m_texturesFramesOriginRects.resize(thingType.m_dimensions[ThingType::AnimationPhases]);
thingType.m_texturesFramesOffsets.resize(thingType.m_dimensions[ThingType::AnimationPhases]);
return true;

View File

@ -41,8 +41,15 @@ void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPatte
const TexturePtr& texture = getTexture(animationPhase); // texture might not exists, neither its rects.
int frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
Point textureOffset = m_texturesFramesOffsets[animationPhase][frameIndex];
Rect textureRect = m_texturesFramesRects[animationPhase][frameIndex];
Point textureOffset;
Rect textureRect;
if(scaleFactor != 1.0f) {
textureRect = m_texturesFramesOriginRects[animationPhase][frameIndex];
} else {
textureOffset = m_texturesFramesOffsets[animationPhase][frameIndex];
textureRect = m_texturesFramesRects[animationPhase][frameIndex];
}
Point displacement(m_parameters[DisplacementX], m_parameters[DisplacementY]);
Rect screenRect(dest + (-displacement + textureOffset - Point(m_dimensions[Width] - 1, m_dimensions[Height] - 1) * Otc::TILE_PIXELS) * scaleFactor,
@ -51,68 +58,45 @@ void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPatte
g_painter->drawTexturedRect(screenRect, texture, textureRect);
}
void ThingType::drawMask(const Point& dest, float scaleFactor, int w, int h, int xPattern, int yPattern, int zPattern, int layer, int animationPhase, ThingType::SpriteMask mask)
{
int scaledSize = Otc::TILE_PIXELS * scaleFactor;
Point displacement(m_parameters[DisplacementX], m_parameters[DisplacementY]);
Rect drawRect(dest - displacement*scaleFactor, Size(scaledSize, scaledSize));
g_painter->drawTexturedRect(drawRect, getSpriteMask(w, h, layer, xPattern, yPattern, zPattern, animationPhase, mask));
}
TexturePtr& ThingType::getSpriteMask(int w, int h, int l, int x, int y, int z, int a, ThingType::SpriteMask mask)
{
if(m_spritesMask.size() == 0)
m_spritesMask.resize(m_spritesIndex.size());
uint index = getSpriteIndex(w,h,l,x,y,z,a);
TexturePtr& maskTexture = m_spritesMask[index][mask];
if(!maskTexture) {
ImagePtr maskImage = g_sprites.getSpriteImage(m_spritesIndex[index]);
if(!maskImage)
maskTexture = g_graphics.getEmptyTexture();
else {
static Color maskColors[LastMask] = { Color::yellow, Color::red, Color::green, Color::blue };
maskImage->overwriteMask(maskColors[mask]);
maskTexture = TexturePtr(new Texture(maskImage, true));
}
}
return maskTexture;
}
TexturePtr& ThingType::getTexture(int animationPhase)
{
TexturePtr& animationPhaseTexture = m_textures[animationPhase];
if(!animationPhaseTexture) {
int textureLayers = m_dimensions[Layers];
if(m_category != ThingsType::Creature) // we dont need layers in texture. they can be 'rendered' now.
textureLayers = 1;
// we don't need layers in common items, they will be pre-drawn
int textureLayers = 1;
int numLayers = m_dimensions[Layers];
if(m_category == ThingsType::Creature && m_dimensions[Layers] >= 2) {
// 5 layers: outfit base, red mask, green mask, blue mask, yellow mask
textureLayers = 5;
numLayers = 5;
}
int indexSize = textureLayers * m_dimensions[PatternX] * m_dimensions[PatternY] * m_dimensions[PatternZ];
Size textureSize = getBestDimension(m_dimensions[Width], m_dimensions[Height], indexSize);
ImagePtr fullImage = ImagePtr(new Image(textureSize * Otc::TILE_PIXELS));
m_texturesFramesRects[animationPhase].resize(indexSize);
m_texturesFramesOriginRects[animationPhase].resize(indexSize);
m_texturesFramesOffsets[animationPhase].resize(indexSize);
for(int z = 0; z < m_dimensions[PatternZ]; ++z) {
for(int y = 0; y < m_dimensions[PatternY]; ++y) {
for(int x = 0; x < m_dimensions[PatternX]; ++x) {
for(int l = 0; l < m_dimensions[Layers]; ++l) {
for(int l = 0; l < numLayers; ++l) {
bool spriteMask = (m_category == ThingsType::Creature && l > 0);
int frameIndex = getTextureIndex(l % textureLayers, x, y, z);
Point framePos = Point(frameIndex % (textureSize.width() / m_dimensions[Width]) * m_dimensions[Width],
frameIndex / (textureSize.width() / m_dimensions[Width]) * m_dimensions[Height]) * Otc::TILE_PIXELS;
for(int h = 0; h < m_dimensions[Height]; ++h) {
for(int w = 0; w < m_dimensions[Width]; ++w) {
uint spriteIndex = getSpriteIndex(w, h, l, x, y, z, animationPhase);
uint spriteIndex = getSpriteIndex(w, h, spriteMask ? 1 : l, x, y, z, animationPhase);
ImagePtr spriteImage = g_sprites.getSpriteImage(m_spritesIndex[spriteIndex]);
if(spriteImage) {
if(spriteMask) {
static Color maskColors[] = { Color::red, Color::green, Color::blue, Color::yellow };
spriteImage->overwriteMask(maskColors[l - 1]);
}
Point spritePos = Point(m_dimensions[Width] - w - 1,
m_dimensions[Height] - h - 1) * Otc::TILE_PIXELS;
@ -121,33 +105,35 @@ TexturePtr& ThingType::getTexture(int animationPhase)
}
}
Rect drawRect(framePos + Point(m_dimensions[Width], m_dimensions[Height]) * Otc::TILE_PIXELS, framePos);
Rect drawRect(framePos + Point(m_dimensions[Width], m_dimensions[Height]) * Otc::TILE_PIXELS - Point(1,1), framePos);
for(int x = framePos.x; x < framePos.x + m_dimensions[Width] * Otc::TILE_PIXELS; ++x) {
for(int y = framePos.y; y < framePos.y + m_dimensions[Height] * Otc::TILE_PIXELS; ++y) {
uint8 *p = fullImage->getPixel(x,y);
if(p[3] != 0x00) {
drawRect.setTop(std::min(y, (int)drawRect.top()));
drawRect.setLeft(std::min(x, (int)drawRect.left()));
drawRect.setTop (std::min(y, (int)drawRect.top()));
drawRect.setLeft (std::min(x, (int)drawRect.left()));
drawRect.setBottom(std::max(y, (int)drawRect.bottom()));
drawRect.setRight(std::max(x, (int)drawRect.right()));
drawRect.setRight (std::max(x, (int)drawRect.right()));
}
}
}
m_texturesFramesRects[animationPhase][frameIndex] = drawRect;
m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft() - framePos + Point(1,1);
m_texturesFramesOriginRects[animationPhase][frameIndex] = Rect(framePos, Size(m_dimensions[Width], m_dimensions[Height]) * Otc::TILE_PIXELS);
m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft() - framePos;
}
}
}
}
animationPhaseTexture = TexturePtr(new Texture(fullImage, true));
animationPhaseTexture->setSmooth(true);
}
return animationPhaseTexture;
}
Size ThingType::getBestDimension(int w, int h, int count)
{
const int MAX = 16;
const int MAX = 32;
int k = 1;
while(k < w)

View File

@ -99,10 +99,10 @@ struct ThingType
};
enum SpriteMask {
YellowMask = 0,
RedMask,
RedMask = 1,
GreenMask,
BlueMask,
YellowMask,
LastMask
};
@ -146,10 +146,10 @@ private:
std::array<int, LastParameter> m_parameters;
std::array<bool, LastProperty> m_properties;
std::vector<int> m_spritesIndex;
std::vector<std::array<TexturePtr, LastMask>> m_spritesMask;
std::vector<TexturePtr> m_textures;
std::vector<std::vector<Rect> > m_texturesFramesRects;
std::vector<std::vector<Rect> > m_texturesFramesOriginRects;
std::vector<std::vector<Point> > m_texturesFramesOffsets;
friend class ThingsType;

View File

@ -68,6 +68,7 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassStaticFunction("g_map", "removeThingByPos", std::bind(&Map::removeThingByPos, &g_map, std::placeholders::_1, std::placeholders::_2));
g_lua.bindClassStaticFunction("g_map", "removeThing", std::bind(&Map::removeThing, &g_map, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_map", "cleanTile", std::bind(&Map::cleanTile, &g_map, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_map", "cleanTexts", std::bind(&Map::cleanTexts, &g_map));
g_lua.bindClassStaticFunction("g_map", "getTile", std::bind(&Map::getTile, &g_map, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_map", "setCentralPosition", std::bind(&Map::setCentralPosition, &g_map, std::placeholders::_1));
g_lua.bindClassStaticFunction("g_map", "getCentralPosition", std::bind(&Map::getCentralPosition, &g_map));