From 9b92a7bc14bc6699310b0876ae19bfc3021e4b3d Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Wed, 15 Jan 2014 03:20:05 +0100 Subject: [PATCH 01/27] Documentation: Move mkxp.conf description into sample conf --- README.md | 21 +------- mkxp.conf.sample | 128 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 116 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index fd2ecb1..9e6f12e 100644 --- a/README.md +++ b/README.md @@ -63,26 +63,7 @@ To run mkxp, you should have a graphics card capable of at least **OpenGL 2.0** ## Configuration -mkxp reads configuration data from the file "mkxp.conf" contained in the current directory. The format is ini-style. Do *not* use quotes around file paths (spaces won't break). Lines starting with '#' are comments. Following entries are interpreted: - -| Key | Type | Default | Description | -| ---------------- | ------ | ------- | ------------------------------------------------------------------------------- | -| debugMode | bool | false | Log OpenGL debug information to the console | -| winResizable | bool | false | Game window is resizable | -| fullscreen | bool | false | Start game in fullscreen (this can always be toggled with Alt-Enter at runtime) | -| fixedAspectRatio | bool | true | Don't stretch the game screen to fit the window size | -| smoothScaling | bool | false | Apply linear interpolation when game screen is stretched | -| vsync | bool | false | Sync screen redraws to the monitor refresh rate | -| defScreenW | int | 640 | Width the game window starts in (this is **not** the game resolution) | -| defScreenH | int | 480 | Height the game window starts in | -| fixedFramerate | int | 0 | FPS will be fixed to this amount. Ignored if 0. | -| frameSkip | bool | true | Skip frames to catch up (useful to disable eg. with Valgrind) | -| solidFonts | bool | false | Don't use alpha blending for fonts | -| gameFolder | string | "." | mkxp will look for all game related files here | -| allowSymlinks | bool | false | Allow symlinks to be followed in the game folder. | -| pathCache | bool | true | Scan and cache asset paths at startup. Allows for case insensitive paths. | -| customScript | string | "" | Execute a raw ruby script file instead of an RPG Maker game. | -| RTP | string | "" | Path to a Run Time Package to be used. Can be specified multiple times. | +mkxp reads configuration data from the file "mkxp.conf" contained in the current directory. The format is ini-style. Do *not* use quotes around file paths (spaces won't break). Lines starting with '#' are comments. See 'mkxp.conf.sample' for a list of accepted entries. ## RTPs diff --git a/mkxp.conf.sample b/mkxp.conf.sample index dbf9f5d..11fb146 100644 --- a/mkxp.conf.sample +++ b/mkxp.conf.sample @@ -1,13 +1,115 @@ -# This is a comment -debugMode=false -winResizable=false -fullscreen=false -fixedAspectRatio=true -smoothScaling=false -vsync=false -defScreenW=640 -defScreenH=480 -solidFonts=false -gameFolder=. -customScript= -RTPs= +# Lines starting with '#' are comments + +# Create a debug context and log +# OpenGL debug information to the console +# (default: disabled) +# +# debugMode=false + + +# Game window is resizable +# (default: disabled) +# +# winResizable=false + + +# Start game in fullscreen (this can +# always be toggled with Alt-Enter at runtime) +# (default: disabled) +# +# fullscreen=false + + +# Preserve game screen aspect ratio, +# as opposed to stretch-to-fill +# (default: enabled) +# +# fixedAspectRatio=true + + +# Apply linear interpolation when game screen +# is upscaled +# (default: disabled) +# +# smoothScaling=false + + +# Sync screen redraws to the monitor refresh rate +# (default: disabled) +# +# vsync=false + + +# Create the window with 640 in width at startup +# +# defScreenW=640 + + +# Create the window with 480 in height at startup +# +# defScreenH=480 + + +# Enforce a static frame rate +# (0 = disabled) +# +# fixedFramerate=0 + + +# Skip (don't draw) frames when behind +# (default: enabled) +# +# frameSkip=true + + +# Don't use alpha blending when rendering text +# (default: disabled) +# +# solidFonts=false + + +# Set the base path of the game to '/path/to/game' +# (default: executable directory) +# +# gameFolder=/path/to/game + + +# Use either right or left Alt + Enter to toggle +# fullscreen +# (default: disabled) +# +# anyAltToggleFS=false + + +# Allow symlinks for game assets to be followed +# (default: disabled) +# +# allowSymlinks=false + + +# Set the game window icon to 'path/to/icon.png' +# (default: none) +# +# iconPath=/path/to/icon.png + + +# Instead of playing an RPG Maker game, +# execute a single plain text script instead +# (default: none) +# +# customScript=/path/to/script.rb + + +# Index all accesible assets via their lower case path +# (emulates windows case insensitivity) +# (default: enabled) +# +# pathCache=true + +# Add 'rtp1', 'rtp2.zip' and 'game.rgssad' to the +# asset search path (multiple allowed) +# (default: none) +# +# RTP=/path/to/rtp1 +# RTP=/path/to/rtp2.zip +# RTP=/path/to/game.rgssad From 91efcfa06d6d319ae52c2750a0990a265a23833a Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 16 Jan 2014 01:06:59 +0100 Subject: [PATCH 02/27] FileSystem: More stringent RGSSAD header checks --- src/filesystem.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/filesystem.cpp b/src/filesystem.cpp index d679a41..3072ca3 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -297,8 +297,12 @@ RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite) /* Check header */ uint32_t header1, header2; - readUint32(io, header1); - readUint32(io, header2); + + if (!readUint32(io, header1)) + return 0; + + if (!readUint32(io, header2)) + return 0; if (header1 != RGSS_HEADER_1 || header2 != RGSS_HEADER_2) return 0; From 316457b9884ad4fd87e1c89af953d2d7ef177166 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 16 Jan 2014 04:19:22 +0100 Subject: [PATCH 03/27] Refactoring --- src/filesystem.cpp | 176 +++++++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 79 deletions(-) diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 3072ca3..b66a8b3 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -500,21 +500,20 @@ static const PHYSFS_Archiver RGSS_Archiver = struct FileSystemPrivate { - /* All keys are lower case */ + /* Maps: lower case filename, To: actual (mixed case) filename. + * This is for compatibility with games that take Windows' + * case insensitivity for granted */ BoostHash pathCache; + bool havePathCache; std::vector extensions[FileSystem::Undefined+1]; - // FIXME Need to find a better way to do this.. - char pathBuffer[512]; - bool havePathCache; - /* Attempt to locate an extension string in a filename. * Either a pointer into the input string pointing at the * extension, or null is returned */ const char *findExt(const char *filename) { - int len; + size_t len; for (len = strlen(filename); len > 0; --len) { @@ -528,105 +527,124 @@ struct FileSystemPrivate return 0; } - const char *completeFileName(const char *filename, - FileSystem::FileType type, - const char **foundExt) + /* Complete filename via regular physfs lookup */ + bool completeFilenameReg(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) { - if (!havePathCache) + /* Try supplementing extensions to find an existing path */ + const std::vector &extList = extensions[type]; + + for (size_t i = 0; i < extList.size(); ++i) { - if (PHYSFS_exists(filename)) + const char *ext = extList[i].c_str(); + + snprintf(outBuffer, outN, "%s.%s", filename, ext); + + if (PHYSFS_exists(outBuffer)) { if (foundExt) - *foundExt = findExt(filename); + *foundExt = ext; - return filename; + return true; } - - const std::vector &extList = extensions[type]; - for (size_t i = 0; i < extList.size(); ++i) - { - const char *ext = extList[i].c_str(); - - snprintf(pathBuffer, sizeof(pathBuffer), "%s.%s", filename, ext); - - if (PHYSFS_exists(pathBuffer)) - { - if (foundExt) - *foundExt = ext; - - return pathBuffer; - } - } - - // Is this even necessary? - if (foundExt) - *foundExt = 0; - - return 0; } - char buff[512]; - size_t i; - - for (i = 0; i < sizeof(buff) && filename[i]; ++i) - buff[i] = tolower(filename[i]); - - buff[i] = '\0'; - - std::string key(buff); - - /* If the path was already complete, - * we are done at this point */ - if (pathCache.contains(key)) + /* Doing the check without supplemented extension + * fits the usage pattern of RMXP games */ + if (PHYSFS_exists(filename)) { - /* The extension might already be included here, - * so try to find it */ + strncpy(outBuffer, filename, outN); + if (foundExt) *foundExt = findExt(filename); - return pathCache[key].c_str(); + return true; } - char buff2[512]; + return false; + } - /* Try supplementing extensions - * to find an existing path */ - if (type != FileSystem::Undefined) + /* Complete filename via path cache */ + bool completeFilenamePC(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) + { + size_t i; + char lowCase[512]; + + for (i = 0; i < sizeof(lowCase)-1 && filename[i]; ++i) + lowCase[i] = tolower(filename[i]); + + lowCase[i] = '\0'; + + std::string key; + + const std::vector &extList = extensions[type]; + + for (size_t i = 0; i < extList.size(); ++i) { - std::vector &extList = extensions[type]; - for (size_t i = 0; i < extList.size(); ++i) + const char *ext = extList[i].c_str(); + + snprintf(outBuffer, outN, "%s.%s", lowCase, ext); + key = outBuffer; + + if (pathCache.contains(key)) { - const char *ext = extList[i].c_str(); + strncpy(outBuffer, pathCache[key].c_str(), outN); - snprintf(buff2, sizeof(buff2), "%s.%s", buff, ext); - key = buff2; + if (foundExt) + *foundExt = ext; - if (pathCache.contains(key)) - { - if (foundExt) - *foundExt = ext; - - return pathCache[key].c_str(); - } + return true; } } - if (foundExt) - *foundExt = 0; + key = lowCase; - return 0; + if (pathCache.contains(key)) + { + strncpy(outBuffer, pathCache[key].c_str(), outN); + + if (foundExt) + *foundExt = findExt(filename); + + return true; + } + + return false; } - PHYSFS_File *openReadInt(const char *filename, - FileSystem::FileType type, - const char **foundExt) + /* Try to complete 'filename' with file extensions + * based on 'type'. If no combination could be found, + * returns false, and 'foundExt' is untouched */ + bool completeFileName(const char *filename, + FileSystem::FileType type, + char *outBuffer, + size_t outN, + const char **foundExt) { - const char *foundName = completeFileName(filename, type, foundExt); + if (havePathCache) + return completeFilenamePC(filename, type, outBuffer, outN, foundExt); + else + return completeFilenameReg(filename, type, outBuffer, outN, foundExt); + } - if (!foundName) + PHYSFS_File *openReadHandle(const char *filename, + FileSystem::FileType type, + const char **foundExt) + { + char found[512]; + + if (!completeFileName(filename, type, found, sizeof(found), foundExt)) throw Exception(Exception::NoFileError, "%s", filename); - PHYSFS_File *handle = PHYSFS_openRead(foundName); + PHYSFS_File *handle = PHYSFS_openRead(found); + if (!handle) throw Exception(Exception::PHYSFSError, "PhysFS: %s", PHYSFS_getLastError()); @@ -826,7 +844,7 @@ void FileSystem::openRead(SDL_RWops &ops, bool freeOnClose, const char **foundExt) { - PHYSFS_File *handle = p->openReadInt(filename, type, foundExt); + PHYSFS_File *handle = p->openReadHandle(filename, type, foundExt); ops.size = SDL_RWopsSize; ops.seek = SDL_RWopsSeek; @@ -844,7 +862,7 @@ void FileSystem::openRead(SDL_RWops &ops, bool FileSystem::exists(const char *filename, FileType type) { - const char *foundName = p->completeFileName(filename, type, 0); + char found[512]; - return (foundName != 0); + return p->completeFileName(filename, type, found, sizeof(found), 0); } From b0a41a23e0b92ec4ccd1227fc5ef335c88507092 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 16 Jan 2014 19:16:09 +0100 Subject: [PATCH 04/27] Rename typedef and fix wrong documenting comment --- src/tileatlas.h | 9 +++++---- src/tilemap.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tileatlas.h b/src/tileatlas.h index 458b9b5..04acf6a 100644 --- a/src/tileatlas.h +++ b/src/tileatlas.h @@ -46,16 +46,17 @@ struct Blit {} }; -typedef std::vector BlitList; +typedef std::vector BlitVec; /* Calculates the minimum atlas size required to hold * a tileset of height 'tilesetH'. If the required dimensions * exceed 'maxAtlasSize', Vec2i(-1, -1) is returned. */ Vec2i minSize(int tilesetH, int maxAtlasSize); -/* Calculates a series of blits necessary to fill dstRows - * with srcRows without wasting any space */ -BlitList calcBlits(int tilesetH, const Vec2i &atlasSize); +/* Calculates a series of blits necessary to fill an atlas + * of size 'atlasSize' with a tileset of height 'tilesetH'. + * Usually fed results from 'minSize()'. */ +BlitVec calcBlits(int tilesetH, const Vec2i &atlasSize); /* Translates a tile coordinate (not pixel!) to a physical * pixel coordinate in the atlas */ diff --git a/src/tilemap.cpp b/src/tilemap.cpp index e6b7fd1..c1bd2b9 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -630,7 +630,7 @@ struct TilemapPrivate for (size_t i = 0; i < atlas.usableATs.size(); ++i) autotiles[atlas.usableATs[i]]->flush(); - TileAtlas::BlitList blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size); + TileAtlas::BlitVec blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size); /* Clear atlas */ FBO::bind(atlas.gl.fbo, FBO::Draw); From f39d1239bbc2efc0cd753a7853f76643b879f9c3 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 16 Jan 2014 19:17:46 +0100 Subject: [PATCH 05/27] TileAtlas: Slightly optimize out some allocation User vector instead of list for colums.s --- src/tileatlas.cpp | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/tileatlas.cpp b/src/tileatlas.cpp index 4f781ef..bbfe85c 100644 --- a/src/tileatlas.cpp +++ b/src/tileatlas.cpp @@ -21,8 +21,6 @@ #include "tileatlas.h" -#include - namespace TileAtlas { @@ -37,8 +35,7 @@ struct Column {} }; -// FIXME: this can be optimized to a vector -typedef std::list ColumnList; +typedef std::vector ColumnVec; /* Autotile area width */ static const int atAreaW = 96*4; @@ -79,9 +76,10 @@ Vec2i minSize(int tilesetH, int maxAtlasSize) return Vec2i(-1, -1); } -static ColumnList calcSrcCols(int tilesetH) +static ColumnVec calcSrcCols(int tilesetH) { - ColumnList cols; + ColumnVec cols; + cols.reserve(2); cols.push_back(Column(0, 0, tilesetH)); cols.push_back(Column(tsLaneW, 0, tilesetH)); @@ -89,9 +87,10 @@ static ColumnList calcSrcCols(int tilesetH) return cols; } -static ColumnList calcDstCols(int atlasW, int atlasH) +static ColumnVec calcDstCols(int atlasW, int atlasH) { - ColumnList cols; + ColumnVec cols; + cols.reserve(3); /* Columns below the autotile area */ const int underAt = atlasH - atAreaH; @@ -110,19 +109,21 @@ static ColumnList calcDstCols(int atlasW, int atlasH) return cols; } -static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) +static BlitVec calcBlitsInt(ColumnVec &srcCols, ColumnVec &dstCols) { - BlitList blits; + BlitVec blits; - while (!srcCols.empty()) + /* Using signed indices here is safer, as we + * might decrement dstI while it is zero. */ + int dstI = 0; + + for (size_t srcI = 0; srcI < srcCols.size(); ++srcI) { - Column srcCol = srcCols.front(); - srcCols.pop_front(); + Column &srcCol = srcCols[srcI]; - while (!dstCols.empty() && srcCol.h > 0) + for (; dstI < (int) dstCols.size() && srcCol.h > 0; ++dstI) { - Column dstCol = dstCols.front(); - dstCols.pop_front(); + Column &dstCol = dstCols[dstI]; if (srcCol.h > dstCol.h) { @@ -141,7 +142,10 @@ static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) dstCol.y += srcCol.h; dstCol.h -= srcCol.h; - dstCols.push_front(dstCol); + + /* Queue this column up again for processing */ + --dstI; + srcCol.h = 0; } else @@ -156,10 +160,10 @@ static BlitList calcBlitsInt(ColumnList &srcCols, ColumnList &dstCols) return blits; } -BlitList calcBlits(int tilesetH, const Vec2i &atlasSize) +BlitVec calcBlits(int tilesetH, const Vec2i &atlasSize) { - ColumnList srcCols = calcSrcCols(tilesetH); - ColumnList dstCols = calcDstCols(atlasSize.x, atlasSize.y); + ColumnVec srcCols = calcSrcCols(tilesetH); + ColumnVec dstCols = calcDstCols(atlasSize.x, atlasSize.y); return calcBlitsInt(srcCols, dstCols); } From f3572f5ba42649d74115f02ee921842b7b3f3686 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 20 Jan 2014 00:53:58 +0100 Subject: [PATCH 06/27] Bitmap: Don't unbind shader after render op Since all rendering is now shader based, there's normally no reason to ever unbind a shader program. --- src/bitmap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bitmap.cpp b/src/bitmap.cpp index e72ab01..3943b63 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -748,8 +748,6 @@ void Bitmap::hueChange(int hue) p->blitQuad(quad); - shader.unbind(); - p->popViewport(); TEX::unbind(); From e93e1fd292fec3de8e26e70a29b45668d87ca006 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 20 Jan 2014 00:54:42 +0100 Subject: [PATCH 07/27] debugwriter.h: Use std::newl instead of '\n' --- src/debugwriter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debugwriter.h b/src/debugwriter.h index 19495e3..27244d1 100644 --- a/src/debugwriter.h +++ b/src/debugwriter.h @@ -46,7 +46,7 @@ public: ~Debug() { - std::clog << buf.str() << "\n"; + std::clog << buf.str() << std::endl; } private: From b729da249b4f6bbb3894c9a16957dae1323ed20c Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 20 Jan 2014 00:56:00 +0100 Subject: [PATCH 08/27] Graphics: Don't unbind shader --- src/graphics.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 22236ff..fa02a9c 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -191,7 +191,6 @@ public: screenQuad.draw(); glState.blendMode.pop(); - shader.unbind(); } #ifdef RGSS2 From 87c1e376b9a11f7b1c47bfb1a94667577e3e4544 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 20 Jan 2014 00:57:40 +0100 Subject: [PATCH 09/27] GLState: Add bound shader program to managed state Squishes a handful of redundant binds per frame. --- src/glstate.cpp | 7 +++++++ src/glstate.h | 6 ++++++ src/shader.cpp | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/glstate.cpp b/src/glstate.cpp index f9c3557..2f1c7fe 100644 --- a/src/glstate.cpp +++ b/src/glstate.cpp @@ -20,6 +20,7 @@ */ #include "glstate.h" +#include "shader.h" #include "etc.h" #include @@ -95,6 +96,11 @@ void GLViewport::apply(const IntRect &value) glViewport(value.x, value.y, value.w, value.h); } +void GLProgram::apply(const unsigned int &value) +{ + glUseProgram(value); +} + GLState::Caps::Caps() { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); @@ -110,4 +116,5 @@ GLState::GLState() scissorTest.init(false); scissorBox.init(IntRect(0, 0, 640, 480)); texture2D.init(true); + program.init(0); } diff --git a/src/glstate.h b/src/glstate.h index 9ef66de..613c555 100644 --- a/src/glstate.h +++ b/src/glstate.h @@ -99,6 +99,11 @@ class GLViewport : public GLProperty void apply(const IntRect &value); }; +class GLProgram : public GLProperty /* GLuint */ +{ + void apply(const unsigned int &value); +}; + class GLState { @@ -109,6 +114,7 @@ public: GLTexture2D texture2D; GLBlendMode blendMode; GLViewport viewport; + GLProgram program; struct Caps { diff --git a/src/shader.cpp b/src/shader.cpp index f11e799..9aefb3b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -99,13 +99,13 @@ Shader::~Shader() void Shader::bind() { - glUseProgram(program); + glState.program.set(program); } void Shader::unbind() { glActiveTexture(GL_TEXTURE0); - glUseProgram(0); + glState.program.set(0); } void Shader::init(const unsigned char *vert, int vertSize, From 53718d0428ed024680543fc3171d347f53640d8f Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Sat, 25 Jan 2014 09:30:05 +0100 Subject: [PATCH 10/27] README: Mention cmake and fix some wording --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e6f12e..3904257 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ This binding only exists for testing purposes and does nothing (the engine quits * pixman * zlib (only ruby bindings) -mkxp employs Qt's qmake build system, so you'll need to install that beforehand. +mkxp employs Qt's qmake build system, so you'll need to install that beforehand. Alternatively, you can build with cmake (FIXME: add cmake instructions). qmake will use pkg-config to locate the respective include/library paths. If you installed any dependencies into non-standard prefixes, make sure to adjust your `PKG_CONFIG_PATH` variable accordingly. @@ -89,5 +89,5 @@ If a requested font is not found, no error is generated. Instead, a built-in fon To alleviate possible porting of heavily Win32API reliant scripts, I have added certain functionality that you won't find in the RGSS spec. Currently this amounts to the following: * The `Input.press?` family of functions accepts three additional button constants: `::MOUSELEFT`, `::MOUSEMIDDLE` and `::MOUSERIGHT` for the respective mouse buttons. -* The `Input` module has two additional functions, `#mouse_x` and `#mouse_y` to query the mouse pointer position relative to the game window. +* The `Input` module has two additional functions, `#mouse_x` and `#mouse_y` to query the mouse pointer position relative to the game screen. * The `Graphics` module has two additional properties: `fullscreen` represents the current fullscreen mode (`true` = fullscreen, `false` = windowed), `show_cursor` hides the system cursor inside the game window when `false`. From b28863d8eb17a82df04d63a1d82f6863c22e0ca7 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Sat, 25 Jan 2014 09:30:48 +0100 Subject: [PATCH 11/27] Add comment --- src/audio.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/audio.cpp b/src/audio.cpp index 04364ec..ead1e6a 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -366,6 +366,7 @@ struct SDLSoundSource : ALDataSource ~SDLSoundSource() { + /* This also closes 'srcOps' */ Sound_FreeSample(sample); } From f956f21ce18389d494aa7781d0a8697b0a640a86 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Sun, 26 Jan 2014 08:06:54 +0100 Subject: [PATCH 12/27] Revert "Font: Adhere to spec and set Font.default_name to "MS PGothic"" This reverts commit c9423164d48b4a383b046873a883e6242fe98825. Turns out the spec was not adjusted correctly for the English RGSS version; the de facto default is "Arial" after all. --- src/font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/font.cpp b/src/font.cpp index 11c37c7..3dc8c10 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -173,7 +173,7 @@ struct FontPrivate {} }; -std::string FontPrivate::defaultName = "MS PGothic"; +std::string FontPrivate::defaultName = "Arial"; int FontPrivate::defaultSize = 22; bool FontPrivate::defaultBold = false; bool FontPrivate::defaultItalic = false; From b6a299541f1ebf03f044ceb51aac2b8e4279aa4a Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Tue, 28 Jan 2014 16:34:28 +0100 Subject: [PATCH 13/27] Bitmap: Fix #get_pixel returning bogus values We never bound the Bitmap FBO to the READ binding point, so glReadPixels was blindly reading data off of whatever was last bound there. --- src/bitmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 3943b63..68b2ac8 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -693,7 +693,7 @@ Vec4 Bitmap::getPixel(int x, int y) const flush(); - p->bindFBO(); + FBO::bind(p->gl.fbo, FBO::Read); glState.viewport.pushSet(IntRect(0, 0, width(), height())); Vec4 pixel = FBO::getPixel(x, y); From 1b0eb2797def67c67001a00269ec79ce54716504 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 31 Jan 2014 10:08:05 +0100 Subject: [PATCH 14/27] BltShader: Correct algorithm as provided my /cremno The color calculation is now actually 99% correct! Fixes #14. --- shader/bitmapBlit.frag | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/shader/bitmapBlit.frag b/shader/bitmapBlit.frag index 1913c5b..18be173 100644 --- a/shader/bitmapBlit.frag +++ b/shader/bitmapBlit.frag @@ -20,18 +20,14 @@ void main() vec4 resFrag; - float ab = opacity; - float as = srcFrag.a; - float ad = dstFrag.a; + float co1 = srcFrag.a * opacity; + float co2 = dstFrag.a * (1.0 - co1); + resFrag.a = co1 + co2; - float at = ab*as; - resFrag.a = at + ad - ad*at; - - // Sigh... - if (ad == 0.0) + if (resFrag.a == 0.0) resFrag.rgb = srcFrag.rgb; else - resFrag.rgb = as*srcFrag.rgb + (1.0-at) * ad * dstFrag.rgb; + resFrag.rgb = (co1*srcFrag.rgb + co2*dstFrag.rgb) / resFrag.a; gl_FragColor = resFrag; } From e0a4dfe3729ad8c4787eb30d8b11f9efa052a21b Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 31 Jan 2014 10:19:16 +0100 Subject: [PATCH 15/27] Bitmap: Make #get_pixel/#set_pixel more accurate This gets rid of the "batch/flush" semantics for #set_pixel and instead just directly uploads the pixel color to the texture, circumventing the float conversion entirely. Also makes a lot of code simpler in many places as calling 'flush()' is no longer required for bitmaps. --- binding-mri/bitmap-binding.cpp | 4 +- binding-mruby/bitmap-binding.cpp | 4 +- src/bitmap.cpp | 74 ++++++++------------------------ src/bitmap.h | 7 +-- src/gl-util.h | 9 ---- src/plane.cpp | 3 -- src/quadarray.h | 63 --------------------------- src/sprite.cpp | 3 -- src/tilemap.cpp | 7 --- src/window.cpp | 6 --- 10 files changed, 23 insertions(+), 157 deletions(-) diff --git a/binding-mri/bitmap-binding.cpp b/binding-mri/bitmap-binding.cpp index f87d8e2..4454bd1 100644 --- a/binding-mri/bitmap-binding.cpp +++ b/binding-mri/bitmap-binding.cpp @@ -202,7 +202,7 @@ RB_METHOD(bitmapGetPixel) return Qnil; ) - Vec4 value; + Color value; GUARD_EXC( value = b->getPixel(x, y); ); Color *color = new Color(value); @@ -223,7 +223,7 @@ RB_METHOD(bitmapSetPixel) color = getPrivateDataCheck(colorObj, ColorType); - GUARD_EXC( b->setPixel(x, y, color->norm); ); + GUARD_EXC( b->setPixel(x, y, *color); ); return self; } diff --git a/binding-mruby/bitmap-binding.cpp b/binding-mruby/bitmap-binding.cpp index f000b07..9010d02 100644 --- a/binding-mruby/bitmap-binding.cpp +++ b/binding-mruby/bitmap-binding.cpp @@ -194,7 +194,7 @@ MRB_METHOD(bitmapGetPixel) return mrb_nil_value(); ) - Vec4 value; + Color value; GUARD_EXC( value = b->getPixel(x, y); ) Color *color = new Color(value); @@ -215,7 +215,7 @@ MRB_METHOD(bitmapSetPixel) color = getPrivateDataCheck(mrb, colorObj, ColorType); - GUARD_EXC( b->setPixel(x, y, color->norm); ) + GUARD_EXC( b->setPixel(x, y, *color); ) return mrb_nil_value(); } diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 68b2ac8..0e3b21d 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -55,10 +55,6 @@ struct BitmapPrivate { TEXFBO gl; - /* 'setPixel()' calls are cached and executed - * in batches on 'flush()' */ - PointArray pointArray; - Font *font; /* "Mega surfaces" are a hack to allow Tilesets to be used @@ -154,32 +150,9 @@ struct BitmapPrivate glState.blendMode.pop(); } - void flushPoints() - { - if (pointArray.count() == 0) - return; - - SimpleColorShader &shader = shState->shaders().simpleColor; - shader.bind(); - shader.setTranslation(Vec2i()); - - bindFBO(); - pushSetViewport(shader); - glState.blendMode.pushSet(BlendNone); - - pointArray.commit(); - pointArray.draw(); - pointArray.reset(); - - glState.blendMode.pop(); - popViewport(); - } - void fillRect(const IntRect &rect, const Vec4 &color) { - flushPoints(); - bindFBO(); glState.scissorTest.pushSet(true); @@ -271,7 +244,6 @@ Bitmap::Bitmap(const Bitmap &other) p->gl = shState->texPool().request(other.width(), other.height()); - other.flush(); blt(0, 0, other, rect()); } @@ -368,8 +340,6 @@ void Bitmap::stretchBlt(const IntRect &destRect, if (opacity == 255 && !p->touchesTaintedArea(destRect)) { /* Fast blit */ - flush(); - FBO::bind(source.p->gl.fbo, FBO::Read); FBO::bind(p->gl.fbo, FBO::Draw); @@ -379,8 +349,6 @@ void Bitmap::stretchBlt(const IntRect &destRect, else { /* Fragment pipeline */ - flush(); - float normOpacity = (float) opacity / 255.0f; TEXFBO &gpTex = shState->gpTexFBO(destRect.w, destRect.h); @@ -666,9 +634,6 @@ void Bitmap::clear() GUARD_MEGA; - /* Any queued points won't be visible after this anyway */ - p->pointArray.reset(); - p->bindFBO(); glState.clearColor.pushSet(Vec4()); @@ -682,7 +647,7 @@ void Bitmap::clear() modified(); } -Vec4 Bitmap::getPixel(int x, int y) const +Color Bitmap::getPixel(int x, int y) const { GUARD_DISPOSED; @@ -691,24 +656,34 @@ Vec4 Bitmap::getPixel(int x, int y) const if (x < 0 || y < 0 || x >= width() || y >= height()) return Vec4(); - flush(); - FBO::bind(p->gl.fbo, FBO::Read); glState.viewport.pushSet(IntRect(0, 0, width(), height())); - Vec4 pixel = FBO::getPixel(x, y); + + uint8_t pixel[4]; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + glState.viewport.pop(); - return pixel; + return Color(pixel[0], pixel[1], pixel[2], pixel[3]); } -void Bitmap::setPixel(int x, int y, const Vec4 &color) +void Bitmap::setPixel(int x, int y, const Color &color) { GUARD_DISPOSED; GUARD_MEGA; - p->pointArray.append(Vec2(x+.5, y+.5), color); + uint8_t pixel[] = + { + (uint8_t) clamp(color.red, 0, 255), + (uint8_t) clamp(color.green, 0, 255), + (uint8_t) clamp(color.blue, 0, 255), + (uint8_t) clamp(color.alpha, 0, 255) + }; + + TEX::bind(p->gl.tex); + TEX::uploadSubImage(x, y, 1, 1, &pixel, GL_RGBA); p->addTaintedArea(IntRect(x, y, 1, 1)); @@ -724,8 +699,6 @@ void Bitmap::hueChange(int hue) if ((hue % 360) == 0) return; - flush(); - TEXFBO newTex = shState->texPool().request(width(), height()); FloatRect texRect(rect()); @@ -777,8 +750,6 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align) if (str[0] == ' ' && str[1] == '\0') return; - flush(); - TTF_Font *font = p->font->getSdlFont(); Color *fontColor = p->font->getColor(); @@ -1009,17 +980,6 @@ IntRect Bitmap::textSize(const char *str) DEF_ATTR_SIMPLE(Bitmap, Font, Font*, p->font) -void Bitmap::flush() const -{ - if (isDisposed()) - return; - - if (p->megaSurface) - return; - - p->flushPoints(); -} - TEXFBO &Bitmap::getGLTypes() { return p->gl; diff --git a/src/bitmap.h b/src/bitmap.h index 0ac418b..86ca4c2 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -81,8 +81,8 @@ public: void clear(); - Vec4 getPixel(int x, int y) const; - void setPixel(int x, int y, const Vec4 &color); + Color getPixel(int x, int y) const; + void setPixel(int x, int y, const Color &color); void hueChange(int hue); @@ -105,9 +105,6 @@ public: DECL_ATTR(Font, Font*) /* */ - /* Warning: Flushing might change the current - * FBO binding (so don't call it during 'draw()' routines */ - void flush() const; TEXFBO &getGLTypes(); SDL_Surface *megaSurface() const; void ensureNonMega() const; diff --git a/src/gl-util.h b/src/gl-util.h index a00c119..d283bf5 100644 --- a/src/gl-util.h +++ b/src/gl-util.h @@ -216,15 +216,6 @@ namespace FBO blit(srcX, srcY, srcW, srcH, dstX, dstY, srcW, srcH, mode); } - inline Vec4 getPixel(int x, int y) - { - Vec4 pixel; - - glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, &pixel.x); - - return pixel; - } - inline void clear() { glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/plane.cpp b/src/plane.cpp index 8a876d8..cb76967 100644 --- a/src/plane.cpp +++ b/src/plane.cpp @@ -93,9 +93,6 @@ struct PlanePrivate updateQuadSource(); quadSourceDirty = false; } - - if (bitmap) - bitmap->flush(); } }; diff --git a/src/quadarray.h b/src/quadarray.h index 42c0c81..249bf8e 100644 --- a/src/quadarray.h +++ b/src/quadarray.h @@ -110,67 +110,4 @@ struct ColorQuadArray } }; -struct PointArray -{ - std::vector vertices; - VBO::ID vbo; - VAO::ID vao; - - PointArray() - { - vbo = VBO::gen(); - vao = VAO::gen(); - - VAO::bind(vao); - VBO::bind(vbo); - - glEnableVertexAttribArray(Shader::Color); - glEnableVertexAttribArray(Shader::Position); - - glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); - glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); - - VAO::unbind(); - VBO::unbind(); - } - - ~PointArray() - { - VBO::del(vbo); - VAO::del(vao); - } - - void append(const Vec2 &pos, const Vec4 &color) - { - Vertex vert; - vert.pos = pos; - vert.color = color; - vertices.push_back(vert); - } - - void commit() - { - VBO::bind(vbo); - VBO::uploadData(vertices.size() * sizeof(Vertex), &vertices[0]); - VBO::unbind(); - } - - void reset() - { - vertices.clear(); - } - - void draw() - { - VAO::bind(vao); - glDrawArrays(GL_POINTS, 0, count()); - VAO::unbind(); - } - - int count() - { - return vertices.size(); - } -}; - #endif // QUADARRAY_H diff --git a/src/sprite.cpp b/src/sprite.cpp index 4a797d9..21bdf92 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -163,9 +163,6 @@ struct SpritePrivate void prepare() { - if (bitmap) - bitmap->flush(); - updateVisibility(); } }; diff --git a/src/tilemap.cpp b/src/tilemap.cpp index c1bd2b9..88cab6e 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -511,8 +511,6 @@ struct TilemapPrivate usableATs.push_back(i); - autotiles[i]->flush(); - if (autotiles[i]->width() > autotileW) animatedATs.push_back(i); } @@ -623,13 +621,8 @@ struct TilemapPrivate /* Assembles atlas from tileset and autotile bitmaps */ void buildAtlas() { - tileset->flush(); - updateAutotileInfo(); - for (size_t i = 0; i < atlas.usableATs.size(); ++i) - autotiles[atlas.usableATs[i]]->flush(); - TileAtlas::BlitVec blits = TileAtlas::calcBlits(atlas.efTilesetH, atlas.size); /* Clear atlas */ diff --git a/src/window.cpp b/src/window.cpp index 0fd7198..4eb3a88 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -508,12 +508,6 @@ struct WindowPrivate { bool updateBaseQuadArray = false; - if (windowskin) - windowskin->flush(); - - if (contents) - contents->flush(); - if (baseVertDirty) { buildBaseVert(); From bb8f2351ccb70620b4a0dfc8787634516e83cac8 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Sun, 2 Feb 2014 23:36:01 +0100 Subject: [PATCH 16/27] Bitmap: Remove leftover 'flush()' calls --- src/bitmap.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 0e3b21d..62821e4 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -429,8 +429,6 @@ void Bitmap::gradientFillRect(const IntRect &rect, GUARD_MEGA; - flush(); - SimpleColorShader &shader = shState->shaders().simpleColor; shader.bind(); shader.setTranslation(Vec2i()); @@ -488,8 +486,6 @@ void Bitmap::blur() GUARD_MEGA; - flush(); - Quad &quad = shState->gpQuad(); FloatRect rect(0, 0, width(), height()); quad.setTexPosRect(rect, rect); @@ -535,8 +531,6 @@ void Bitmap::radialBlur(int angle, int divisions) GUARD_MEGA; - flush(); - angle = clamp(angle, 0, 359); divisions = clamp(divisions, 2, 100); From 5a6c0c14ede2a27927254c0016b15f2ef90d782e Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 3 Feb 2014 14:07:08 +0100 Subject: [PATCH 17/27] MRuby-Binding: Fix etc accessors not using getter/setter functions --- binding-mruby/etc-binding.cpp | 70 ++++++++++++++++------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/binding-mruby/etc-binding.cpp b/binding-mruby/etc-binding.cpp index fcbad46..6fae107 100644 --- a/binding-mruby/etc-binding.cpp +++ b/binding-mruby/etc-binding.cpp @@ -24,22 +24,22 @@ #include "binding-types.h" #include "serializable-binding.h" -#define ATTR_RW(Type, attr, arg_type, mrb_val, arg_t_s) \ - MRB_METHOD(Type##Get_##attr) \ +#define ATTR_RW(Type, Attr, arg_type, mrb_val, arg_t_s) \ + MRB_METHOD(Type##Get##Attr) \ { \ Type *p = getPrivateData(mrb, self); \ \ - return mrb_##mrb_val##_value(p->attr); \ + return mrb_##mrb_val##_value(p->get##Attr()); \ } \ \ - MRB_METHOD(Type##Set_##attr) \ + MRB_METHOD(Type##Set##Attr) \ { \ Type *p = getPrivateData(mrb, self); \ \ arg_type arg; \ mrb_get_args(mrb, arg_t_s, &arg); \ \ - p->attr = arg; \ + p->set##Attr(arg); \ UPDATE_F \ \ return mrb_##mrb_val##_value(arg); \ @@ -64,22 +64,22 @@ #define ATTR_INT_RW(Type, attr) ATTR_RW(Type, attr, mrb_int, fixnum, "i") #define UPDATE_F p->updateInternal(); -ATTR_FLOAT_RW(Color, red) -ATTR_FLOAT_RW(Color, green) -ATTR_FLOAT_RW(Color, blue) -ATTR_FLOAT_RW(Color, alpha) +ATTR_FLOAT_RW(Color, Red) +ATTR_FLOAT_RW(Color, Green) +ATTR_FLOAT_RW(Color, Blue) +ATTR_FLOAT_RW(Color, Alpha) -ATTR_FLOAT_RW(Tone, red) -ATTR_FLOAT_RW(Tone, green) -ATTR_FLOAT_RW(Tone, blue) -ATTR_FLOAT_RW(Tone, gray) +ATTR_FLOAT_RW(Tone, Red) +ATTR_FLOAT_RW(Tone, Green) +ATTR_FLOAT_RW(Tone, Blue) +ATTR_FLOAT_RW(Tone, Gray) #undef UPDATE_F #define UPDATE_F -ATTR_INT_RW(Rect, x) -ATTR_INT_RW(Rect, y) -ATTR_INT_RW(Rect, width) -ATTR_INT_RW(Rect, height) +ATTR_INT_RW(Rect, X) +ATTR_INT_RW(Rect, Y) +ATTR_INT_RW(Rect, Width) +ATTR_INT_RW(Rect, Height) EQUAL_FUN(Color) EQUAL_FUN(Tone) @@ -163,15 +163,9 @@ CLONE_FUN(Tone) CLONE_FUN(Color) CLONE_FUN(Rect) -#define MRB_ATTR_R(Class, attr) mrb_define_method(mrb, klass, #attr, Class##Get_##attr, MRB_ARGS_NONE()) -#define MRB_ATTR_W(Class, attr) mrb_define_method(mrb, klass, #attr "=", Class##Set_##attr, MRB_ARGS_REQ(1)) -#define MRB_ATTR_RW(Class, attr) { MRB_ATTR_R(Class, attr); MRB_ATTR_W(Class, attr); } - -#define MRB_ATTR_RW_A(Class, attr, alias) \ -{ \ - mrb_define_method(mrb, klass, #alias, Class##Get_##attr, MRB_ARGS_NONE()); \ - mrb_define_method(mrb, klass, #alias "=", Class##Set_##attr, MRB_ARGS_REQ(1)); \ -} +#define MRB_ATTR_R(Class, Attr, sym) mrb_define_method(mrb, klass, sym, Class##Get##Attr, MRB_ARGS_NONE()) +#define MRB_ATTR_W(Class, Attr, sym) mrb_define_method(mrb, klass, sym "=", Class##Set##Attr, MRB_ARGS_REQ(1)) +#define MRB_ATTR_RW(Class, Attr, sym) { MRB_ATTR_R(Class, Attr, sym); MRB_ATTR_W(Class, Attr, sym); } #define INIT_BIND(Klass) \ { \ @@ -191,21 +185,21 @@ void etcBindingInit(mrb_state *mrb) RClass *klass; INIT_BIND(Color); - MRB_ATTR_RW(Color, red); - MRB_ATTR_RW(Color, green); - MRB_ATTR_RW(Color, blue); - MRB_ATTR_RW(Color, alpha); + MRB_ATTR_RW(Color, Red, "red" ); + MRB_ATTR_RW(Color, Green, "green"); + MRB_ATTR_RW(Color, Blue, "blue" ); + MRB_ATTR_RW(Color, Alpha, "alpha"); INIT_BIND(Tone); - MRB_ATTR_RW(Tone, red); - MRB_ATTR_RW(Tone, green); - MRB_ATTR_RW(Tone, blue); - MRB_ATTR_RW(Tone, gray); + MRB_ATTR_RW(Tone, Red, "red" ); + MRB_ATTR_RW(Tone, Green, "green"); + MRB_ATTR_RW(Tone, Blue, "blue" ); + MRB_ATTR_RW(Tone, Gray, "gray" ); INIT_BIND(Rect); - MRB_ATTR_RW(Rect, x); - MRB_ATTR_RW(Rect, y); - MRB_ATTR_RW(Rect, width); - MRB_ATTR_RW(Rect, height); + MRB_ATTR_RW(Rect, X, "x" ); + MRB_ATTR_RW(Rect, Y, "y" ); + MRB_ATTR_RW(Rect, Width, "width" ); + MRB_ATTR_RW(Rect, Height, "height"); mrb_define_method(mrb, klass, "empty", RectEmpty, MRB_ARGS_NONE()); } From 64f1e32fdc619028d3cdc60864c99edf7d737fcb Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 3 Feb 2014 15:04:57 +0100 Subject: [PATCH 18/27] Input: Implement RGSS3 functionality in bindings Ie. using symbols instead of Input:: constants to query button states. --- binding-mri/binding-util.cpp | 18 +++++++++++++ binding-mri/binding-util.h | 2 +- binding-mri/input-binding.cpp | 47 ++++++++++++++++++--------------- binding-mruby/input-binding.cpp | 40 +++++++++++++++++----------- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/binding-mri/binding-util.cpp b/binding-mri/binding-util.cpp index aa798bc..c85a86a 100644 --- a/binding-mri/binding-util.cpp +++ b/binding-mri/binding-util.cpp @@ -237,6 +237,24 @@ rb_get_args(int argc, VALUE *argv, const char *format, ...) break; } + case 'n' : + { + if (argI >= argc) + break; + + ID *sym = va_arg(ap, ID*); + + VALUE symVal = *arg++; + + if (!SYMBOL_P(symVal)) + rb_raise(rb_eTypeError, "Argument %d: Expected symbol", argI); + + *sym = SYM2ID(symVal); + ++argI; + + break; + } + case '|' : opt = true; break; diff --git a/binding-mri/binding-util.h b/binding-mri/binding-util.h index ff14af6..d99ce0f 100644 --- a/binding-mri/binding-util.h +++ b/binding-mri/binding-util.h @@ -131,7 +131,7 @@ wrapNilProperty(VALUE self, const char *iv) rb_iv_set(self, iv, Qnil); } -/* Implemented: oSszfib| */ +/* Implemented: oSszfibn| */ int rb_get_args(int argc, VALUE *argv, const char *format, ...); diff --git a/binding-mri/input-binding.cpp b/binding-mri/input-binding.cpp index b224da9..6ff3a7c 100644 --- a/binding-mri/input-binding.cpp +++ b/binding-mri/input-binding.cpp @@ -33,40 +33,45 @@ RB_METHOD(inputUpdate) return Qnil; } +static int getButtonArg(VALUE self, int argc, VALUE *argv) +{ + int num; + +#ifdef RGSS3 + ID sym; + rb_get_args(argc, argv, "n", &sym RB_ARG_END); + + if (rb_const_defined(self, sym)) + num = FIX2INT(rb_const_get(self, sym)); + else + num = 0; +#else + (void) self; + rb_get_args(argc, argv, "i", &num RB_ARG_END); +#endif + + return num; +} + RB_METHOD(inputPress) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isPressed(bc)); + return rb_bool_new(shState->input().isPressed(num)); } RB_METHOD(inputTrigger) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isTriggered(bc)); + return rb_bool_new(shState->input().isTriggered(num)); } RB_METHOD(inputRepeat) { - RB_UNUSED_PARAM; + int num = getButtonArg(self, argc, argv); - int num; - rb_get_args(argc, argv, "i", &num RB_ARG_END); - - Input::ButtonCode bc = (Input::ButtonCode) num; - - return rb_bool_new(shState->input().isRepeated(bc)); + return rb_bool_new(shState->input().isRepeated(num)); } RB_METHOD(inputDir4) diff --git a/binding-mruby/input-binding.cpp b/binding-mruby/input-binding.cpp index 7231c9b..e415e6f 100644 --- a/binding-mruby/input-binding.cpp +++ b/binding-mruby/input-binding.cpp @@ -33,34 +33,44 @@ MRB_FUNCTION(inputUpdate) return mrb_nil_value(); } -MRB_FUNCTION(inputPress) +static mrb_int getButtonArg(mrb_state *mrb, mrb_value self) { mrb_int num; + +#ifdef RGSS3 + mrb_sym sym; + mrb_get_args(mrb, "n", &sym); + + if (mrb_const_defined(mrb, self, sym)) + num = mrb_fixnum(mrb_const_get(mrb, self, sym)); + else + num = 0; +#else mrb_get_args(mrb, "i", &num); +#endif - Input::ButtonCode bc = (Input::ButtonCode) num; - - return mrb_bool_value(shState->input().isPressed(bc)); + return num; } -MRB_FUNCTION(inputTrigger) +MRB_METHOD(inputPress) { - mrb_int num; - mrb_get_args(mrb, "i", &num); + mrb_int num = getButtonArg(mrb, self); - Input::ButtonCode bc = (Input::ButtonCode) num; - - return mrb_bool_value(shState->input().isTriggered(bc)); + return mrb_bool_value(shState->input().isPressed(num)); } -MRB_FUNCTION(inputRepeat) +MRB_METHOD(inputTrigger) { - mrb_int num; - mrb_get_args(mrb, "i", &num); + mrb_int num = getButtonArg(mrb, self); - Input::ButtonCode bc = (Input::ButtonCode) num; + return mrb_bool_value(shState->input().isTriggered(num)); +} - return mrb_bool_value(shState->input().isRepeated(bc)); +MRB_METHOD(inputRepeat) +{ + mrb_int num = getButtonArg(mrb, self); + + return mrb_bool_value(shState->input().isRepeated(num)); } MRB_FUNCTION(inputDir4) From 42b10fd2ee9801ff74cccb61e77e6821e1da0cd8 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 3 Feb 2014 15:24:10 +0100 Subject: [PATCH 19/27] mkxp.pro: Add RGSS3 config define --- mkxp.pro | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mkxp.pro b/mkxp.pro index 40492ce..6c59957 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -44,6 +44,11 @@ RGSS2 { DEFINES += RGSS2 } +# Requires RGSS2 +RGSS3 { + DEFINES += RGSS3 +} + unix { CONFIG += link_pkgconfig PKGCONFIG += sigc++-2.0 glew pixman-1 zlib physfs \ From af9039f58d2ee82d45e2777b730f2634f1f76f17 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Mon, 3 Feb 2014 15:32:50 +0100 Subject: [PATCH 20/27] Sprite: Implement wave effect (RGSS2) This initial implementation emulates the way RMVX splits the sprite into "chunks" of about 8 pixels, which it then scrolls left/right on a vertical sine wave. It even replicates the weird behavior when wave_amp < 0, namely "shrinking" the src_rect horizontally. As with bush_opacity, this effect in combination with rotation will render differently from RMVX. --- binding-mri/sprite-binding.cpp | 43 +++++++ binding-mruby/sprite-binding.cpp | 39 +++++++ src/flashable.h | 2 +- src/quadarray.h | 74 +++++++++--- src/sprite.cpp | 194 ++++++++++++++++++++++++++++++- src/sprite.h | 15 ++- 6 files changed, 346 insertions(+), 21 deletions(-) diff --git a/binding-mri/sprite-binding.cpp b/binding-mri/sprite-binding.cpp index e29c6a4..2d515cd 100644 --- a/binding-mri/sprite-binding.cpp +++ b/binding-mri/sprite-binding.cpp @@ -69,6 +69,39 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_B(Sprite, Mirror) +#ifdef RGSS2 + +RB_METHOD(spriteWidth) +{ + RB_UNUSED_PARAM; + + Sprite *s = getPrivateData(self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return rb_fix_new(value); +} + +RB_METHOD(spriteHeight) +{ + RB_UNUSED_PARAM; + + Sprite *s = getPrivateData(self); + + int value; + GUARD_EXC( value = s->getHeight(); ) + + return rb_fix_new(value); +} + +DEF_PROP_I(Sprite, WaveAmp) +DEF_PROP_I(Sprite, WaveLength) +DEF_PROP_I(Sprite, WaveSpeed) +DEF_PROP_F(Sprite, WavePhase) + +#endif + void spriteBindingInit() { @@ -98,4 +131,14 @@ spriteBindingInit() INIT_PROP_BIND( Sprite, BlendType, "blend_type" ); INIT_PROP_BIND( Sprite, Color, "color" ); INIT_PROP_BIND( Sprite, Tone, "tone" ); + +#ifdef RGSS2 + _rb_define_method(klass, "width", spriteWidth); + _rb_define_method(klass, "height", spriteHeight); + + INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" ); + INIT_PROP_BIND( Sprite, WaveLength, "wave_length" ); + INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" ); + INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" ); +#endif } diff --git a/binding-mruby/sprite-binding.cpp b/binding-mruby/sprite-binding.cpp index b37feb1..2eabc71 100644 --- a/binding-mruby/sprite-binding.cpp +++ b/binding-mruby/sprite-binding.cpp @@ -68,6 +68,35 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_B(Sprite, Mirror) +#ifdef RGSS2 + +MRB_METHOD(spriteWidth) +{ + Sprite *s = getPrivateData(mrb, self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return mrb_fixnum_value(value); +} + +MRB_METHOD(spriteHeight) +{ + Sprite *s = getPrivateData(mrb, self); + + int value; + GUARD_EXC( value = s->getHeight(); ) + + return mrb_fixnum_value(value); +} + +DEF_PROP_I(Sprite, WaveAmp) +DEF_PROP_I(Sprite, WaveLength) +DEF_PROP_I(Sprite, WaveSpeed) +DEF_PROP_F(Sprite, WavePhase) + +#endif + void spriteBindingInit(mrb_state *mrb) { @@ -95,5 +124,15 @@ spriteBindingInit(mrb_state *mrb) INIT_PROP_BIND( Sprite, Color, "color" ); INIT_PROP_BIND( Sprite, Tone, "tone" ); +#ifdef RGSS2 + mrb_define_method(mrb, klass, "width", spriteWidth, MRB_ARGS_NONE()); + mrb_define_method(mrb, klass, "height", spriteHeight, MRB_ARGS_NONE()); + + INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" ); + INIT_PROP_BIND( Sprite, WaveLength, "wave_length" ); + INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" ); + INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" ); +#endif + mrb_define_method(mrb, klass, "inspect", inspectObject, MRB_ARGS_NONE()); } diff --git a/src/flashable.h b/src/flashable.h index e32c89f..bd7e21d 100644 --- a/src/flashable.h +++ b/src/flashable.h @@ -55,7 +55,7 @@ public: flashAlpha = flashColor.w; } - void update() + virtual void update() { if (!flashing) return; diff --git a/src/quadarray.h b/src/quadarray.h index 249bf8e..0d6d4b8 100644 --- a/src/quadarray.h +++ b/src/quadarray.h @@ -34,17 +34,41 @@ typedef uint32_t index_t; #define _GL_INDEX_TYPE GL_UNSIGNED_INT -struct ColorQuadArray +/* A small hack to get mutable QuadArray constructors */ +inline void initBufferBindings(Vertex *) { - std::vector vertices; + glEnableVertexAttribArray(Shader::Color); + glEnableVertexAttribArray(Shader::Position); + glEnableVertexAttribArray(Shader::TexCoord); + + glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); + glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); + glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset()); +} + +inline void initBufferBindings(SVertex *) +{ + glEnableVertexAttribArray(Shader::Position); + glEnableVertexAttribArray(Shader::TexCoord); + + glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::posOffset()); + glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::texPosOffset()); +} + +template +struct QuadArray +{ + std::vector vertices; VBO::ID vbo; VAO::ID vao; int quadCount; + GLsizeiptr vboSize; - ColorQuadArray() - : quadCount(0) + QuadArray() + : quadCount(0), + vboSize(-1) { vbo = VBO::gen(); vao = VAO::gen(); @@ -53,20 +77,16 @@ struct ColorQuadArray VBO::bind(vbo); shState->bindQuadIBO(); - glEnableVertexAttribArray(Shader::Color); - glEnableVertexAttribArray(Shader::Position); - glEnableVertexAttribArray(Shader::TexCoord); - - glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset()); - glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset()); - glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset()); + /* Call correct implementation here via overloading */ + VertexType *dummy = 0; + initBufferBindings(dummy); VAO::unbind(); IBO::unbind(); VBO::unbind(); } - ~ColorQuadArray() + ~QuadArray() { VBO::del(vbo); VAO::del(vao); @@ -78,15 +98,36 @@ struct ColorQuadArray quadCount = size; } + void clear() + { + vertices.clear(); + quadCount = 0; + } + /* This needs to be called after the final 'append()' call * and previous to the first 'draw()' call. */ void commit() { VBO::bind(vbo); - VBO::uploadData(vertices.size() * sizeof(Vertex), &vertices[0], GL_DYNAMIC_DRAW); - VBO::unbind(); - shState->ensureQuadIBO(quadCount); + GLsizeiptr size = vertices.size() * sizeof(VertexType); + + if (size > vboSize) + { + /* New data exceeds already allocated size. + * Reallocate VBO. */ + VBO::uploadData(size, &vertices[0], GL_DYNAMIC_DRAW); + vboSize = size; + + shState->ensureQuadIBO(quadCount); + } + else + { + /* New data fits in allocated size */ + VBO::uploadSubData(0, size, &vertices[0]); + } + + VBO::unbind(); } void draw(size_t offset, size_t count) @@ -110,4 +151,7 @@ struct ColorQuadArray } }; +typedef QuadArray ColorQuadArray; +typedef QuadArray SimpleQuadArray; + #endif // QUADARRAY_H diff --git a/src/sprite.cpp b/src/sprite.cpp index 21bdf92..b5888c1 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -32,6 +32,9 @@ #include "transform.h" #include "shader.h" #include "glstate.h" +#include "quadarray.h" + +#include #include @@ -63,6 +66,22 @@ struct SpritePrivate Color *color; Tone *tone; +#ifdef RGSS2 + struct + { + int amp; + int length; + int speed; + float phase; + + /* Wave effect is active (amp != 0) */ + bool active; + /* qArray needs updating */ + bool dirty; + SimpleQuadArray qArray; + } wave; +#endif + EtcTemps tmp; sigc::connection prepareCon; @@ -87,6 +106,13 @@ struct SpritePrivate prepareCon = shState->prepareDraw.connect (sigc::mem_fun(this, &SpritePrivate::prepare)); + +#ifdef RGSS2 + wave.amp = 0; + wave.length = 180; + wave.speed = 360; + wave.phase = 0.0; +#endif } ~SpritePrivate() @@ -117,6 +143,10 @@ struct SpritePrivate quad.setPosRect(IntRect(0, 0, srcRect->width, srcRect->height)); recomputeBushDepth(); + +#ifdef RGSS2 + wave.dirty = true; +#endif } void updateSrcRectCon() @@ -141,6 +171,16 @@ struct SpritePrivate if (!opacity) return; +#ifdef RGSS2 + if (wave.active) + { + /* Don't do expensive wave bounding box + * calculations */ + isVisible = true; + return; + } +#endif + /* Compare sprite bounding box against the scene */ /* If sprite is zoomed/rotated, just opt out for now @@ -161,8 +201,102 @@ struct SpritePrivate isVisible = SDL_HasIntersection(&self, &sceneRect); } +#ifdef RGSS2 + void emitWaveChunk(SVertex *&vert, float phase, int width, + float zoomY, int chunkY, int chunkLength) + { + float wavePos = phase + (chunkY / (float) wave.length) * M_PI * 2; + float chunkX = sin(wavePos) * wave.amp; + + FloatRect tex(0, chunkY / zoomY, width, chunkLength / zoomY); + FloatRect pos = tex; + pos.x = chunkX; + + Quad::setTexPosRect(vert, tex, pos); + vert += 4; + } + + void updateWave() + { + if (!bitmap) + return; + + if (wave.amp == 0) + { + wave.active = false; + return; + } + + wave.active = true; + + int width = srcRect->width; + int height = srcRect->height; + float zoomY = trans.getScale().y; + + if (wave.amp < -(width / 2)) + { + wave.qArray.resize(0); + wave.qArray.commit(); + + return; + } + + /* RMVX does this, and I have no fucking clue why */ + if (wave.amp < 0) + { + wave.qArray.resize(1); + + int x = -wave.amp; + int w = width - x * 2; + + FloatRect tex(x, srcRect->y, w, srcRect->height); + + Quad::setTexPosRect(&wave.qArray.vertices[0], tex, tex); + wave.qArray.commit(); + + return; + } + + /* The length of the sprite as it appears on screen */ + int visibleLength = height * zoomY; + + /* First chunk length (aligned to 8 pixel boundary */ + int firstLength = ((int) trans.getPosition().y) % 8; + + /* Amount of full 8 pixel chunks in the middle */ + int chunks = (visibleLength - firstLength) / 8; + + /* Final chunk length */ + int lastLength = (visibleLength - firstLength) % 8; + + wave.qArray.resize(!!firstLength + chunks + !!lastLength); + SVertex *vert = &wave.qArray.vertices[0]; + + float phase = (wave.phase * M_PI) / 180.f; + + if (firstLength > 0) + emitWaveChunk(vert, phase, width, zoomY, 0, firstLength); + + for (int i = 0; i < chunks; ++i) + emitWaveChunk(vert, phase, width, zoomY, firstLength + i * 8, 8); + + if (lastLength > 0) + emitWaveChunk(vert, phase, width, zoomY, firstLength + chunks * 8, lastLength); + + wave.qArray.commit(); + } +#endif + void prepare() { +#ifdef RGSS2 + if (wave.dirty) + { + updateWave(); + wave.dirty = false; + } +#endif + updateVisibility(); } }; @@ -193,14 +327,21 @@ DEF_ATTR_RD_SIMPLE(Sprite, Angle, float, p->trans.getRotation()) DEF_ATTR_RD_SIMPLE(Sprite, Mirror, bool, p->mirrored) DEF_ATTR_RD_SIMPLE(Sprite, BushDepth, int, p->bushDepth) DEF_ATTR_RD_SIMPLE(Sprite, BlendType, int, p->blendType) -DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width) -DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height) DEF_ATTR_SIMPLE(Sprite, BushOpacity, int, p->bushOpacity) DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity) DEF_ATTR_SIMPLE(Sprite, Color, Color*, p->color) DEF_ATTR_SIMPLE(Sprite, Tone, Tone*, p->tone) +#ifdef RGSS2 +DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width) +DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height) +DEF_ATTR_RD_SIMPLE(Sprite, WaveAmp, int, p->wave.amp) +DEF_ATTR_RD_SIMPLE(Sprite, WaveLength, int, p->wave.length) +DEF_ATTR_RD_SIMPLE(Sprite, WaveSpeed, int, p->wave.speed) +DEF_ATTR_RD_SIMPLE(Sprite, WavePhase, float, p->wave.phase) +#endif + void Sprite::setBitmap(Bitmap *bitmap) { GUARD_DISPOSED @@ -218,6 +359,10 @@ void Sprite::setBitmap(Bitmap *bitmap) *p->srcRect = bitmap->rect(); p->onSrcRectChange(); p->quad.setPosRect(p->srcRect->toFloatRect()); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setSrcRect(Rect *rect) @@ -252,6 +397,10 @@ void Sprite::setY(int value) return; p->trans.setPosition(Vec2(getX(), value)); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setOX(int value) @@ -293,6 +442,10 @@ void Sprite::setZoomY(float value) p->trans.setScale(Vec2(getZoomX(), value)); p->recomputeBushDepth(); + +#ifdef RGSS2 + p->wave.dirty = true; +#endif } void Sprite::setAngle(float value) @@ -346,6 +499,36 @@ void Sprite::setBlendType(int type) } } +#ifdef RGSS2 + +#define DEF_WAVE_SETTER(Name, name, type) \ + void Sprite::setWave##Name(type value) \ + { \ + GUARD_DISPOSED; \ + if (p->wave.name == value) \ + return; \ + p->wave.name = value; \ + p->wave.dirty = true; \ + } + +DEF_WAVE_SETTER(Amp, amp, int) +DEF_WAVE_SETTER(Length, length, int) +DEF_WAVE_SETTER(Speed, speed, int) +DEF_WAVE_SETTER(Phase, phase, float) + +#undef DEF_WAVE_SETTER + +/* Flashable */ +void Sprite::update() +{ + Flashable::update(); + + p->wave.phase += p->wave.speed / 180; + p->wave.dirty = true; +} + +#endif + /* Disposable */ void Sprite::releaseResources() { @@ -407,7 +590,14 @@ void Sprite::draw() p->bitmap->bindTex(*base); +#ifdef RGSS2 + if (p->wave.active) + p->wave.qArray.draw(); + else + p->quad.draw(); +#else p->quad.draw(); +#endif glState.blendMode.pop(); } diff --git a/src/sprite.h b/src/sprite.h index fd2f631..22ee697 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -41,9 +41,6 @@ public: Sprite(Viewport *viewport = 0); ~Sprite(); - int getWidth() const; - int getHeight() const; - DECL_ATTR( Bitmap, Bitmap* ) DECL_ATTR( SrcRect, Rect* ) DECL_ATTR( X, int ) @@ -61,6 +58,18 @@ public: DECL_ATTR( Color, Color* ) DECL_ATTR( Tone, Tone* ) +#ifdef RGSS2 + int getWidth() const; + int getHeight() const; + + DECL_ATTR( WaveAmp, int ) + DECL_ATTR( WaveLength, int ) + DECL_ATTR( WaveSpeed, int ) + DECL_ATTR( WavePhase, float ) + + void update(); +#endif + private: SpritePrivate *p; From 4219b91bbbe9636a6a11857efd08fa93dee3e810 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 6 Feb 2014 04:27:17 +0100 Subject: [PATCH 21/27] al-util.h: Add include fix for older OpenAL releases Apparently older versions of 'alext.h' didn't include 'alc.h' despite dependencies on ALC types. --- src/al-util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/al-util.h b/src/al-util.h index c083c9a..7b1a85d 100644 --- a/src/al-util.h +++ b/src/al-util.h @@ -23,6 +23,7 @@ #define ALUTIL_H #include +#include #include namespace AL From 8203e32900e02912fa8507d05a25510077d25a07 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 11 Apr 2014 13:31:09 +0200 Subject: [PATCH 22/27] MRI-Binding: Define 'msgbox' p/print aliases (RGSS3) --- binding-mri/binding-mri.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index 168f2fc..bbe95a8 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -88,8 +88,13 @@ static void mriBindingInit() fileIntBindingInit(); +#ifdef RGSS3 + _rb_define_module_function(rb_mKernel, "msgbox", mriPrint); + _rb_define_module_function(rb_mKernel, "msgbox_p", mriP); +#else _rb_define_module_function(rb_mKernel, "print", mriPrint); _rb_define_module_function(rb_mKernel, "p", mriP); +#endif rb_eval_string(module_rpg); From 6dfa4f6b7d676e4ca0f113385f3b0cf6bf569eea Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 11 Apr 2014 13:34:19 +0200 Subject: [PATCH 23/27] MRI-Binding: Add 'Graphics.width/height' bindings (RGSS2) --- binding-mri/graphics-binding.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/binding-mri/graphics-binding.cpp b/binding-mri/graphics-binding.cpp index df92610..a972fb8 100644 --- a/binding-mri/graphics-binding.cpp +++ b/binding-mri/graphics-binding.cpp @@ -99,6 +99,24 @@ RB_METHOD(graphicsFrameReset) DEF_GRA_PROP_I(FrameRate) DEF_GRA_PROP_I(FrameCount) +#ifdef RGSS2 + +RB_METHOD(graphicsWidth) +{ + RB_UNUSED_PARAM; + + return rb_fix_new(shState->graphics().width()); +} + +RB_METHOD(graphicsHeight) +{ + RB_UNUSED_PARAM; + + return rb_fix_new(shState->graphics().height()); +} + +#endif + DEF_GRA_PROP_B(Fullscreen) DEF_GRA_PROP_B(ShowCursor) @@ -120,6 +138,11 @@ void graphicsBindingInit() INIT_GRA_PROP_BIND( FrameRate, "frame_rate" ); INIT_GRA_PROP_BIND( FrameCount, "frame_count" ); +#ifdef RGSS2 + _rb_define_module_function(module, "width", graphicsWidth); + _rb_define_module_function(module, "height", graphicsHeight); +#endif + INIT_GRA_PROP_BIND( Fullscreen, "fullscreen" ); INIT_GRA_PROP_BIND( ShowCursor, "show_cursor" ); } From 31626c9accbd544bed26600add4e53999c10f30b Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Wed, 16 Apr 2014 13:06:16 +0200 Subject: [PATCH 24/27] Spacing --- src/filesystem.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/filesystem.cpp b/src/filesystem.cpp index b66a8b3..bf38cea 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -480,22 +480,22 @@ RGSS_noop2(void*, const char*) static const PHYSFS_Archiver RGSS_Archiver = { 0, - { - "RGSSAD", - "RGSS encrypted archive format", - "Jonas Kulla ", - "http://k-du.de/rgss/rgss.html", - 0 /* symlinks not supported */ - }, - RGSS_openArchive, - RGSS_enumerateFiles, - RGSS_openRead, - RGSS_noop1, /* openWrite */ - RGSS_noop1, /* openAppend */ - RGSS_noop2, /* remove */ - RGSS_noop2, /* mkdir */ - RGSS_stat, - RGSS_closeArchive + { + "RGSSAD", + "RGSS encrypted archive format", + "Jonas Kulla ", + "http://k-du.de/rgss/rgss.html", + 0 /* symlinks not supported */ + }, + RGSS_openArchive, + RGSS_enumerateFiles, + RGSS_openRead, + RGSS_noop1, /* openWrite */ + RGSS_noop1, /* openAppend */ + RGSS_noop2, /* remove */ + RGSS_noop2, /* mkdir */ + RGSS_stat, + RGSS_closeArchive }; struct FileSystemPrivate From 1ef6e04520139a37ff972a5116ad8fedfbadaf2e Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Fri, 11 Apr 2014 13:37:14 +0200 Subject: [PATCH 25/27] Font: Overhaul font asset discovery Previously, any font names requested by RGSS would be translated directly to filenames by lowercasing and replacing spaces with underscores (and finally doing some extension substitution). To make this whole thing work smoother as well as get closer to how font discovery is done in VX, we now scan the "Fonts/" folder at startup and index all present font assets by their family name; now, if an "Open Sans" font is present in "Fonts/", it will be used regardless of filename. Font assets with "Regular" style are preferred, but in their absence, mkxp will make use of any other style it can find for the respective family. This is not the exact same behavior as VX, but it should cover 95% of use cases. Previously, one could substitute fonts via filenames, ie. to substitute "Arial" with "Open Sans", one would just rename "OpenSans.ttf" to "arial.ttf" and put it in "Fonts/". With the above change, this is no longer possible. As an alternative, one can now explicitly specify font family substitutions via mkxp.conf; eg. for the above case, one would add fontSub=Arial>Open Sans to the configuration file. Multiple such rules can be specified. In the process, I also added the ability to provide 'Font.(default_)name' with an array of font families to search for the first existing one instead of a plain string. This makes the behavior closer to RMXP; however, it doesn't work 100% the same: when a reference to the 'Font.name' array is held and additional strings are added to it without re-assignig the array to 'Font.name', those will be ignored. --- binding-mri/bitmap-binding.cpp | 12 +- binding-mri/font-binding.cpp | 86 +++++++++-- mkxp.conf.sample | 15 ++ src/config.cpp | 3 + src/config.h | 2 + src/filesystem.cpp | 252 +++++++++++++++++++++------------ src/filesystem.h | 12 +- src/font.cpp | 174 ++++++++++++++++------- src/font.h | 46 ++++-- src/sharedstate.cpp | 7 +- src/sharedstate.h | 4 +- src/tilemap.cpp | 7 - src/util.h | 8 ++ 13 files changed, 443 insertions(+), 185 deletions(-) diff --git a/binding-mri/bitmap-binding.cpp b/binding-mri/bitmap-binding.cpp index 4454bd1..ad06d6f 100644 --- a/binding-mri/bitmap-binding.cpp +++ b/binding-mri/bitmap-binding.cpp @@ -52,12 +52,14 @@ RB_METHOD(bitmapInitialize) setPrivateData(self, b); /* Wrap properties */ - Font *font = new Font(); - b->setFont(font); - font->setColor(new Color(*font->getColor())); + VALUE fontKlass = rb_const_get(rb_cObject, rb_intern("Font")); + VALUE fontObj = rb_obj_alloc(fontKlass); + rb_obj_call_init(fontObj, 0, 0); - VALUE fontProp = wrapProperty(self, font, "font", FontType); - wrapProperty(fontProp, font->getColor(), "color", ColorType); + Font *font = getPrivateData(fontObj); + b->setFont(font); + + rb_iv_set(self, "font", fontObj); return self; } diff --git a/binding-mri/font-binding.cpp b/binding-mri/font-binding.cpp index eacea9a..b270170 100644 --- a/binding-mri/font-binding.cpp +++ b/binding-mri/font-binding.cpp @@ -23,6 +23,9 @@ #include "binding-util.h" #include "binding-types.h" #include "exception.h" +#include "sharedstate.h" + +#include DEF_TYPE(Font); @@ -36,14 +39,16 @@ RB_METHOD(fontDoesExist) return rb_bool_new(Font::doesExist(name)); } +RB_METHOD(FontSetName); + RB_METHOD(fontInitialize) { - const char *name = 0; + VALUE name = Qnil; int size = 0; - rb_get_args(argc, argv, "|zi", &name, &size RB_ARG_END); + rb_get_args(argc, argv, "|oi", &name, &size RB_ARG_END); - Font *f = new Font(name, size); + Font *f = new Font(0, size); setPrivateData(self, f); @@ -51,6 +56,13 @@ RB_METHOD(fontInitialize) f->setColor(new Color(*f->getColor())); wrapProperty(self, f->getColor(), "color", ColorType); + if (NIL_P(name)) + name = rb_iv_get(rb_obj_class(self), "default_name"); + + /* Going over the 'name=' function automatically causes + * a possbile name array to be re-verified for existing fonts */ + FontSetName(1, &name, self); + return self; } @@ -77,21 +89,63 @@ RB_METHOD(FontGetName) { RB_UNUSED_PARAM; - Font *f = getPrivateData(self); + return rb_iv_get(self, "name"); +} - return rb_str_new_cstr(f->getName()); +static void +fontSetNameHelper(VALUE self, int argc, VALUE *argv, + const char *nameIv, char *outBuf, size_t outLen) +{ + rb_check_argc(argc, 1); + + VALUE arg = argv[0]; + int type = rb_type(arg); + + // Fixme: in RGSS3, specifying "" (and only that) as font name results in + // no text being drawn (everything else is substituted with Arial I think) + strncpy(outBuf, "", outLen); + + if (type == RUBY_T_STRING) + { + strncpy(outBuf, RSTRING_PTR(arg), outLen); + } + else if (type == RUBY_T_ARRAY) + { + for (long i = 0; i < RARRAY_LEN(arg); ++i) + { + VALUE str = rb_ary_entry(arg, i); + + /* Non-string objects are tolerated (ignored) */ + if (rb_type(str) != RUBY_T_STRING) + continue; + + const char *family = RSTRING_PTR(str); + + /* We only set the core Font object's name attribute + * to the actually existing font name */ + if (!shState->fontState().fontPresent(family)) + continue; + + strncpy(outBuf, family, outLen); + } + } + + /* RMXP doesn't even care if the argument type is + * something other than string/array. Whatever... */ + rb_iv_set(self, nameIv, arg); } RB_METHOD(FontSetName) { Font *f = getPrivateData(self); - VALUE name; - rb_get_args(argc, argv, "S", &name RB_ARG_END); + char result[256]; + fontSetNameHelper(self, argc, argv, "default_name", + result, sizeof(result)); - f->setName(RSTRING_PTR(name)); + f->setName(result); - return name; + return argv[0]; } #undef DEF_PROP_CHK_DISP @@ -124,18 +178,19 @@ DEF_KLASS_PROP(Font, bool, DefaultItalic, "b", rb_bool_new) RB_METHOD(FontGetDefaultName) { RB_UNUSED_PARAM; - return rb_str_new_cstr(Font::getDefaultName()); + + return rb_iv_get(self, "default_name"); } RB_METHOD(FontSetDefaultName) { - RB_UNUSED_PARAM; - VALUE nameObj; - rb_get_args(argc, argv, "S", &nameObj RB_ARG_END); + char result[256]; + fontSetNameHelper(self, argc, argv, "default_name", + result, sizeof(result)); - Font::setDefaultName(RSTRING_PTR(nameObj)); + Font::setDefaultName(result); - return nameObj; + return argv[0]; } RB_METHOD(FontGetDefaultColor) @@ -174,6 +229,7 @@ fontBindingInit() Font::setDefaultColor(new Color(*Font::getDefaultColor())); wrapProperty(klass, Font::getDefaultColor(), "default_color", ColorType); + rb_iv_set(klass, "default_name", rb_str_new_cstr(Font::getDefaultName())); INIT_KLASS_PROP_BIND(Font, DefaultName, "default_name"); INIT_KLASS_PROP_BIND(Font, DefaultSize, "default_size"); diff --git a/mkxp.conf.sample b/mkxp.conf.sample index 11fb146..bdf1a1b 100644 --- a/mkxp.conf.sample +++ b/mkxp.conf.sample @@ -113,3 +113,18 @@ # RTP=/path/to/rtp1 # RTP=/path/to/rtp2.zip # RTP=/path/to/game.rgssad + + +# Font substitutions allow drop-in replacements of fonts +# to be used without changing the RGSS scripts, +# eg. providing 'Open Sans' when the game thinkgs it's +# using 'Arial'. Font family to be substituted and +# replacement family are separated by one sole '>'. +# Be careful not to include any spaces. +# This is not connected to the built-in font, which is +# always used when a non-existing font family is +# requested by RGSS. +# (default: none) +# +# fontSub=Arial>Open Sans +# fontSub=Times New Roman>Liberation Serif diff --git a/src/config.cpp b/src/config.cpp index 7e3091f..4f15a51 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -83,6 +83,7 @@ void Config::read(int argc, char *argv[]) podesc.add_options() PO_DESC_ALL ("RTP", po::value()->composing()) + ("fontSub", po::value()->composing()) ; po::variables_map vm; @@ -115,6 +116,8 @@ void Config::read(int argc, char *argv[]) GUARD_ALL( rtps = vm["RTP"].as(); ); + GUARD_ALL( fontSubs = vm["fontSub"].as(); ); + #undef PO_DESC #undef PO_DESC_ALL } diff --git a/src/config.h b/src/config.h index 411ce51..2854672 100644 --- a/src/config.h +++ b/src/config.h @@ -54,6 +54,8 @@ struct Config std::string customScript; std::vector rtps; + std::vector fontSubs; + /* Game INI contents */ struct { std::string scripts; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index bf38cea..6a731ce 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -21,6 +21,7 @@ #include "filesystem.h" +#include "font.h" #include "util.h" #include "exception.h" #include "boost-hash.h" @@ -498,6 +499,99 @@ static const PHYSFS_Archiver RGSS_Archiver = RGSS_closeArchive }; + +static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops) +{ + return static_cast(ops->hidden.unknown.data1); +} + +static Sint64 SDL_RWopsSize(SDL_RWops *ops) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + return PHYSFS_fileLength(f); +} + +static Sint64 SDL_RWopsSeek(SDL_RWops *ops, int64_t offset, int whence) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + int64_t base; + + switch (whence) + { + default: + case RW_SEEK_SET : + base = 0; + break; + case RW_SEEK_CUR : + base = PHYSFS_tell(f); + break; + case RW_SEEK_END : + base = PHYSFS_fileLength(f); + break; + } + + int result = PHYSFS_seek(f, base + offset); + + return (result != 0) ? PHYSFS_tell(f) : -1; +} + +static size_t SDL_RWopsRead(SDL_RWops *ops, void *buffer, size_t size, size_t maxnum) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return 0; + + PHYSFS_sint64 result = PHYSFS_readBytes(f, buffer, size*maxnum); + + return (result != -1) ? (result / size) : 0; +} + +static size_t SDL_RWopsWrite(SDL_RWops *ops, const void *buffer, size_t size, size_t num) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return 0; + + PHYSFS_sint64 result = PHYSFS_writeBytes(f, buffer, size*num); + + return (result != -1) ? (result / size) : 0; +} + +static int SDL_RWopsClose(SDL_RWops *ops) +{ + PHYSFS_File *f = sdlPHYS(ops); + + if (!f) + return -1; + + int result = PHYSFS_close(f); + + f = 0; + + return (result != 0) ? 0 : -1; +} + +static int SDL_RWopsCloseFree(SDL_RWops *ops) +{ + int result = SDL_RWopsClose(ops); + + SDL_FreeRW(ops); + + return result; +} + +const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10; + struct FileSystemPrivate { /* Maps: lower case filename, To: actual (mixed case) filename. @@ -650,6 +744,24 @@ struct FileSystemPrivate return handle; } + + void initReadOps(PHYSFS_File *handle, + SDL_RWops &ops, + bool freeOnClose) + { + ops.size = SDL_RWopsSize; + ops.seek = SDL_RWopsSeek; + ops.read = SDL_RWopsRead; + ops.write = SDL_RWopsWrite; + + if (freeOnClose) + ops.close = SDL_RWopsCloseFree; + else + ops.close = SDL_RWopsClose; + + ops.type = SDL_RWOPS_PHYSFS; + ops.hidden.unknown.data1 = handle; + } }; FileSystem::FileSystem(const char *argv0, @@ -746,98 +858,59 @@ void FileSystem::createPathCache() p->havePathCache = true; } -static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops) +static void strToLower(std::string &str) { - return static_cast(ops->hidden.unknown.data1); + for (size_t i = 0; i < str.size(); ++i) + str[i] = tolower(str[i]); } -static Sint64 SDL_RWopsSize(SDL_RWops *ops) +struct FontSetsCBData { - PHYSFS_File *f = sdlPHYS(ops); + FileSystemPrivate *p; + SharedFontState *sfs; +}; - if (!f) - return -1; +static void fontSetEnumCB(void *data, const char *, + const char *fname) +{ + FontSetsCBData *d = static_cast(data); + FileSystemPrivate *p = d->p; - return PHYSFS_fileLength(f); + /* Only consider filenames with font extensions */ + const char *ext = p->findExt(fname); + + if (!ext) + return; + + std::string lower(ext); + strToLower(lower); + + if (!contains(p->extensions[FileSystem::Font], lower)) + return; + + std::string filename("Fonts/"); + filename += fname; + + PHYSFS_File *handle = PHYSFS_openRead(filename.c_str()); + + if (!handle) + return; + + SDL_RWops ops; + p->initReadOps(handle, ops, false); + + d->sfs->initFontSetCB(ops, filename); + + SDL_RWclose(&ops); } -static Sint64 SDL_RWopsSeek(SDL_RWops *ops, int64_t offset, int whence) +void FileSystem::initFontSets(SharedFontState &sfs) { - PHYSFS_File *f = sdlPHYS(ops); + FontSetsCBData d = { p, &sfs }; - if (!f) - return -1; - - int64_t base; - - switch (whence) - { - default: - case RW_SEEK_SET : - base = 0; - break; - case RW_SEEK_CUR : - base = PHYSFS_tell(f); - break; - case RW_SEEK_END : - base = PHYSFS_fileLength(f); - break; - } - - int result = PHYSFS_seek(f, base + offset); - - return (result != 0) ? PHYSFS_tell(f) : -1; + PHYSFS_enumerateFilesCallback("Fonts", fontSetEnumCB, &d); } -static size_t SDL_RWopsRead(SDL_RWops *ops, void *buffer, size_t size, size_t maxnum) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return 0; - - PHYSFS_sint64 result = PHYSFS_readBytes(f, buffer, size*maxnum); - - return (result != -1) ? (result / size) : 0; -} - -static size_t SDL_RWopsWrite(SDL_RWops *ops, const void *buffer, size_t size, size_t num) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return 0; - - PHYSFS_sint64 result = PHYSFS_writeBytes(f, buffer, size*num); - - return (result != -1) ? (result / size) : 0; -} - -static int SDL_RWopsClose(SDL_RWops *ops) -{ - PHYSFS_File *f = sdlPHYS(ops); - - if (!f) - return -1; - - int result = PHYSFS_close(f); - - f = 0; - - return (result != 0) ? 0 : -1; -} - -static int SDL_RWopsCloseFree(SDL_RWops *ops) -{ - int result = SDL_RWopsClose(ops); - - SDL_FreeRW(ops); - - return result; -} - -const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10; - void FileSystem::openRead(SDL_RWops &ops, const char *filename, FileType type, @@ -846,18 +919,17 @@ void FileSystem::openRead(SDL_RWops &ops, { PHYSFS_File *handle = p->openReadHandle(filename, type, foundExt); - ops.size = SDL_RWopsSize; - ops.seek = SDL_RWopsSeek; - ops.read = SDL_RWopsRead; - ops.write = SDL_RWopsWrite; + p->initReadOps(handle, ops, freeOnClose); +} - if (freeOnClose) - ops.close = SDL_RWopsCloseFree; - else - ops.close = SDL_RWopsClose; +void FileSystem::openReadRaw(SDL_RWops &ops, + const char *filename, + bool freeOnClose) +{ + PHYSFS_File *handle = PHYSFS_openRead(filename); + assert(handle); - ops.type = SDL_RWOPS_PHYSFS; - ops.hidden.unknown.data1 = handle; + p->initReadOps(handle, ops, freeOnClose); } bool FileSystem::exists(const char *filename, FileType type) diff --git a/src/filesystem.h b/src/filesystem.h index a9ab2ef..b2230c8 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -25,6 +25,7 @@ #include struct FileSystemPrivate; +struct SharedFontState; class FileSystem { @@ -35,9 +36,13 @@ public: void addPath(const char *path); - /* Call this after the last 'addPath()' */ + /* Call these after the last 'addPath()' */ void createPathCache(); + /* Scans "Fonts/" and creates inventory of + * available font assets */ + void initFontSets(SharedFontState &sfs); + /* For extension supplementing */ enum FileType { @@ -53,6 +58,11 @@ public: bool freeOnClose = false, const char **foundExt = 0); + /* Circumvents extension supplementing */ + void openReadRaw(SDL_RWops &ops, + const char *filename, + bool freeOnClose = false); + bool exists(const char *filename, FileType type = Undefined); diff --git a/src/font.cpp b/src/font.cpp index 3dc8c10..a5dbed5 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -26,6 +26,7 @@ #include "exception.h" #include "boost-hash.h" #include "util.h" +#include "config.h" #include #include @@ -45,88 +46,151 @@ typedef std::pair FontKey; -static void strToLower(std::string &str) -{ - for (size_t i = 0; i < str.size(); ++i) - str[i] = tolower(str[i]); -} - -struct FontPoolPrivate -{ - BoostHash hash; -}; - -FontPool::FontPool() -{ - p = new FontPoolPrivate; -} - -FontPool::~FontPool() -{ - BoostHash::const_iterator iter; - for (iter = p->hash.cbegin(); iter != p->hash.cend(); ++iter) - TTF_CloseFont(iter->second); - - delete p; -} - static SDL_RWops *openBundledFont() { return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT)); } -_TTF_Font *FontPool::request(const char *filename, - int size) +struct FontSet { - // FIXME Find out how font path resolution is done in VX/Ace - std::string nameKey(filename); - strToLower(nameKey); - strReplace(nameKey, ' ', '_'); + /* 'Regular' style */ + std::string regular; - bool useBundled = false; - std::string path = std::string("Fonts/") + nameKey; - if (!shState->fileSystem().exists(path.c_str(), FileSystem::Font)) + /* Any other styles (used in case no 'Regular' exists) */ + std::string other; +}; + +struct SharedFontStatePrivate +{ + /* Maps: font family name, To: substituted family name, + * as specified via configuration file / arguments */ + BoostHash subs; + + /* Maps: font family name, To: set of physical + * font filenames located in "Fonts/" */ + BoostHash sets; + + /* Pool of already opened fonts; once opened, they are reused + * and never closed until the termination of the program */ + BoostHash pool; +}; + +SharedFontState::SharedFontState(const Config &conf) +{ + p = new SharedFontStatePrivate; + + /* Parse font substitutions */ + for (size_t i = 0; i < conf.fontSubs.size(); ++i) { - /* Use the same name key for the bundled font - * even when it resulted from multiple different - * font name requests. The space at the front is - * to prevent collisions (spaces are normally - * replaced with '_' */ - useBundled = true; - nameKey = " bundled"; + const std::string &raw = conf.fontSubs[i]; + size_t sepPos = raw.find_first_of('>'); + + if (sepPos == std::string::npos) + continue; + + std::string from = raw.substr(0, sepPos); + std::string to = raw.substr(sepPos+1); + + p->subs.insert(from, to); + } +} + +SharedFontState::~SharedFontState() +{ + BoostHash::const_iterator iter; + for (iter = p->pool.cbegin(); iter != p->pool.cend(); ++iter) + TTF_CloseFont(iter->second); + + delete p; +} + +void SharedFontState::initFontSetCB(SDL_RWops &ops, + const std::string &filename) +{ + TTF_Font *font = TTF_OpenFontRW(&ops, 0, 0); + + if (!font) + return; + + std::string family = TTF_FontFaceFamilyName(font); + std::string style = TTF_FontFaceStyleName(font); + + TTF_CloseFont(font); + + FontSet &set = p->sets[family]; + + if (style == "Regular") + set.regular = filename; + else + set.other = filename; +} + +_TTF_Font *SharedFontState::getFont(std::string family, + int size) +{ + /* Check for substitutions */ + if (p->subs.contains(family)) + family = p->subs[family]; + + /* Find out if the font asset exists */ + const FontSet &req = p->sets[family]; + + if (req.regular.empty() && req.other.empty()) + { + /* Doesn't exist; use built-in font */ + family = ""; } - FontKey key(nameKey, size); + FontKey key(family, size); - TTF_Font *font = p->hash.value(key, 0); + TTF_Font *font = p->pool.value(key); if (font) return font; - /* Not in hash, open */ + /* Not in pool; open new handle */ SDL_RWops *ops; - if (useBundled) + if (family.empty()) { + /* Built-in font */ ops = openBundledFont(); } else { + /* Use 'other' path as alternative in case + * we have no 'regular' styled font asset */ + const char *path = !req.regular.empty() + ? req.regular.c_str() : req.other.c_str(); + ops = SDL_AllocRW(); - shState->fileSystem().openRead(*ops, path.c_str(), FileSystem::Font, true); + shState->fileSystem().openReadRaw(*ops, path, true); } // FIXME 0.9 is guesswork at this point - font = TTF_OpenFontRW(ops, 1, (float) size * .90); +// float gamma = (96.0/45.0)*(5.0/14.0)*(size-5); +// font = TTF_OpenFontRW(ops, 1, gamma /** .90*/); + font = TTF_OpenFontRW(ops, 1, size* .90); if (!font) - throw Exception(Exception::SDLError, "SDL: %s", SDL_GetError()); + throw Exception(Exception::SDLError, "%s", SDL_GetError()); - p->hash.insert(key, font); + p->pool.insert(key, font); return font; } +bool SharedFontState::fontPresent(std::string family) +{ + /* Check for substitutions */ + if (p->subs.contains(family)) + family = p->subs[family]; + + const FontSet &set = p->sets[family]; + + return !(set.regular.empty() && set.other.empty()); +} + struct FontPrivate { @@ -183,9 +247,7 @@ Color FontPrivate::defaultColorTmp(255, 255, 255, 255); bool Font::doesExist(const char *name) { - std::string path = std::string("Fonts/") + std::string(name); - - return shState->fileSystem().exists(path.c_str(), FileSystem::Font); + return shState->fontState().fontPresent(name); } Font::Font(const char *name, @@ -223,6 +285,10 @@ void Font::setSize(int value) if (p->size == value) return; + /* Catch illegal values (according to RMXP) */ + if (value < 6 || value > 96) + throw Exception(Exception::ArgumentError, "%s", "bad value for size"); + p->size = value; p->sdlFont = 0; } @@ -253,8 +319,8 @@ void Font::setDefaultName(const char *value) _TTF_Font *Font::getSdlFont() { if (!p->sdlFont) - p->sdlFont = shState->fontPool().request(p->name.c_str(), - p->size); + p->sdlFont = shState->fontState().getFont(p->name.c_str(), + p->size); int style = TTF_STYLE_NORMAL; diff --git a/src/font.h b/src/font.h index 3c73b54..df25001 100644 --- a/src/font.h +++ b/src/font.h @@ -25,22 +25,50 @@ #include "etc.h" #include "util.h" +struct SDL_RWops; struct _TTF_Font; -struct FontPoolPrivate; +struct Config; -class FontPool +struct SharedFontStatePrivate; + +class SharedFontState { public: - FontPool(); - ~FontPool(); + SharedFontState(const Config &conf); + ~SharedFontState(); - _TTF_Font *request(const char *filename, + /* Called from FileSystem during font cache initialization + * (when "Fonts/" is scanned for available assets). + * 'ops' is an opened handle to a possible font file, + * 'filename' is the corresponding path */ + void initFontSetCB(SDL_RWops &ops, + const std::string &filename); + + _TTF_Font *getFont(std::string family, int size); + bool fontPresent(std::string family); + private: - FontPoolPrivate *p; + SharedFontStatePrivate *p; }; +/* Concerning Font::name/defaultName : + * In RGSS, this is not actually a string; any type of + * object is accepted, however anything but strings and + * arrays is ignored (and text drawing turns blank). + * Single strings are interpreted as font family names, + * and directly passed to the underlying C++ object; + * arrays however are searched for the first string + * object corresponding to a valid font family name, + * and rendering is done with that. In mkxp, we pass + * this first valid font family as the 'name' attribute + * back to the C++ object on assignment and object + * creation (in case Font.default_name is also an array). + * Invalid parameters (things other than strings or + * arrays not containing any valid family name) are + * passed back as "". */ + struct FontPrivate; class Font @@ -57,9 +85,9 @@ public: const char *getName() const; void setName(const char *value); - DECL_ATTR( Size, int ) - DECL_ATTR( Bold, bool ) - DECL_ATTR( Italic, bool ) + DECL_ATTR( Size, int ) + DECL_ATTR( Bold, bool ) + DECL_ATTR( Italic, bool ) DECL_ATTR( Color, Color* ) DECL_ATTR_STATIC( DefaultName, const char* ) diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index 7d0b587..c681776 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -67,8 +67,8 @@ struct SharedStatePrivate ShaderSet shaders; TexPool texPool; - FontPool fontPool; + SharedFontState fontState; Font *defaultFont; TEX::ID globalTex; @@ -90,6 +90,7 @@ struct SharedStatePrivate rtData(*threadData), config(threadData->config), graphics(threadData), + fontState(threadData->config), stampCounter(0) { if (!config.gameFolder.empty()) @@ -121,6 +122,8 @@ struct SharedStatePrivate if (config.pathCache) fileSystem.createPathCache(); + fileSystem.initFontSets(fontState); + globalTexW = 128; globalTexH = 64; @@ -206,8 +209,8 @@ GSATT(Audio&, audio) GSATT(GLState&, _glState) GSATT(ShaderSet&, shaders) GSATT(TexPool&, texPool) -GSATT(FontPool&, fontPool) GSATT(Quad&, gpQuad) +GSATT(SharedFontState&, fontState) void SharedState::setBindingData(void *data) { diff --git a/src/sharedstate.h b/src/sharedstate.h index c389f1f..9b1c22a 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -43,8 +43,8 @@ class Input; class Audio; class GLState; class TexPool; -class FontPool; class Font; +class SharedFontState; struct GlobalIBO; struct Config; struct Vec2i; @@ -74,8 +74,8 @@ struct SharedState ShaderSet &shaders(); TexPool &texPool(); - FontPool &fontPool(); + SharedFontState &fontState(); Font &defaultFont(); sigc::signal prepareDraw; diff --git a/src/tilemap.cpp b/src/tilemap.cpp index 88cab6e..2cdf6ba 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -48,13 +48,6 @@ extern const StaticRect autotileRects[]; typedef std::vector SVVector; typedef struct { SVVector v[4]; } TileVBuffer; -/* Check if [C]ontainer contains [V]alue */ -template -inline bool contains(const C &c, const V &v) -{ - return std::find(c.begin(), c.end(), v) != c.end(); -} - static const int tilesetW = 8 * 32; static const int autotileW = 3 * 32; static const int autotileH = 4 * 32; diff --git a/src/util.h b/src/util.h index 6a77ac2..17f76e4 100644 --- a/src/util.h +++ b/src/util.h @@ -24,6 +24,7 @@ #include #include +#include static inline int wrapRange(int value, int min, int max) @@ -93,6 +94,13 @@ inline void strReplace(std::string &str, str[i] = after; } +/* Check if [C]ontainer contains [V]alue */ +template +inline bool contains(const C &c, const V &v) +{ + return std::find(c.begin(), c.end(), v) != c.end(); +} + #define ARRAY_SIZE(obj) (sizeof(obj) / sizeof((obj)[0])) #define elementsN(obj) const int obj##N = ARRAY_SIZE(obj) From f11cc182df9b1647a7c2959938f0e87df62dab97 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Wed, 16 Apr 2014 13:48:45 +0200 Subject: [PATCH 26/27] Remove screenshot functionality It was only meant for debugging and brought with it unneeded platform dependant issues. --- src/config.cpp | 2 -- src/config.h | 1 - src/eventthread.cpp | 3 --- src/eventthread.h | 5 +---- src/graphics.cpp | 40 ---------------------------------------- 5 files changed, 1 insertion(+), 50 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 4f15a51..20e3da4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -35,7 +35,6 @@ namespace po = boost::program_options; Config::Config() : debugMode(false), - screenshots(false), winResizable(false), fullscreen(false), fixedAspectRatio(true), @@ -56,7 +55,6 @@ void Config::read(int argc, char *argv[]) { #define PO_DESC_ALL \ PO_DESC(debugMode, bool) \ - PO_DESC(screenshots, bool) \ PO_DESC(winResizable, bool) \ PO_DESC(fullscreen, bool) \ PO_DESC(fixedAspectRatio, bool) \ diff --git a/src/config.h b/src/config.h index 2854672..9bc7b6c 100644 --- a/src/config.h +++ b/src/config.h @@ -28,7 +28,6 @@ struct Config { bool debugMode; - bool screenshots; bool winResizable; bool fullscreen; diff --git a/src/eventthread.cpp b/src/eventthread.cpp index acdb161..fa671ad 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -209,9 +209,6 @@ void EventThread::process(RGSSThreadData &rtData) break; } - if (event.key.keysym.scancode == SDL_SCANCODE_F3 && rtData.config.screenshots) - rtData.rqScreenshot = true; - keyStates[event.key.keysym.scancode] = true; break; diff --git a/src/eventthread.h b/src/eventthread.h index 5ed27c1..79ac199 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -178,8 +178,6 @@ struct RGSSThreadData std::string rgssErrorMsg; - volatile bool rqScreenshot; - RGSSThreadData(EventThread *ethread, const char *argv0, SDL_Window *window, @@ -190,8 +188,7 @@ struct RGSSThreadData argv0(argv0), window(window), sizeResoRatio(1, 1), - config(newconf), - rqScreenshot(false) + config(newconf) {} }; diff --git a/src/graphics.cpp b/src/graphics.cpp index fa02a9c..d87277a 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -507,44 +507,6 @@ struct GraphicsPrivate swapGLBuffer(); } - - void writeScreenshot(const char *filename) - { - int bpp; - uint32_t rm, gm, bm, am; - SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &rm, &gm, &bm, &am); - - /* Discard alpha channel because it might have bogus values */ - SDL_Surface *screenshot = - SDL_CreateRGBSurface(0, scRes.x, scRes.y, bpp, rm, gm, bm, 0); - - screen.getPP().bindLastBuffer(); - - glReadPixels(0, 0, scRes.x, scRes.y, GL_RGBA, GL_UNSIGNED_BYTE, screenshot->pixels); - - IMG_SavePNG(screenshot, filename); - - SDL_FreeSurface(screenshot); - } - - void checkScreenshotRq() - { - if (!threadData->rqScreenshot) - return; - - threadData->rqScreenshot = false; - - struct timeval tv; - gettimeofday(&tv, 0); - struct tm tm = *localtime(&tv.tv_sec); - - char filename[32]; - - snprintf(filename, sizeof(filename), "%d%02d%02d-%02d%02d%02d.png", - tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - - writeScreenshot(filename); - } }; Graphics::Graphics(RGSSThreadData *data) @@ -568,8 +530,6 @@ void Graphics::update() // p->cpuTimer->endTiming(); // p->gpuTimer->startTiming(); - p->checkScreenshotRq(); - if (p->frozen) return; From 4cb1c10c3afaa8612ec9742e130519e426074870 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Wed, 16 Apr 2014 20:54:29 +0200 Subject: [PATCH 27/27] Minor declaration fix --- src/filesystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filesystem.h b/src/filesystem.h index b2230c8..08d573f 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -25,7 +25,7 @@ #include struct FileSystemPrivate; -struct SharedFontState; +class SharedFontState; class FileSystem {