diff --git a/jni/love/src/libraries/luasocket/libluasocket/lua.h b/jni/love/src/libraries/luasocket/libluasocket/lua.h index e97bc2ac..a280df84 100644 --- a/jni/love/src/libraries/luasocket/libluasocket/lua.h +++ b/jni/love/src/libraries/luasocket/libluasocket/lua.h @@ -1,3 +1,6 @@ +#ifndef LUA_WRAP_H +#define LUA_WRAP_H + #define LUA_COMPAT_ALL #include #include @@ -10,3 +13,5 @@ extern int luax_typerror(lua_State *L, int narg, const char *type); #endif + +#endif // LUA_WRAP_H diff --git a/jni/love/src/modules/audio/openal/Pool.cpp b/jni/love/src/modules/audio/openal/Pool.cpp index f8e61339..8d239f4d 100644 --- a/jni/love/src/modules/audio/openal/Pool.cpp +++ b/jni/love/src/modules/audio/openal/Pool.cpp @@ -61,11 +61,13 @@ Pool::Pool() // Make all sources available initially. for (int i = 0; i < totalSources; i++) { +#ifdef AL_SOFT_direct_channels if (hasext) { // Bypass virtualization of speakers for multi-channel sources in OpenAL Soft. alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE); } +#endif available.push(sources[i]); } diff --git a/jni/love/src/modules/graphics/Graphics.cpp b/jni/love/src/modules/graphics/Graphics.cpp index 64f4f280..28fa14e4 100644 --- a/jni/love/src/modules/graphics/Graphics.cpp +++ b/jni/love/src/modules/graphics/Graphics.cpp @@ -176,6 +176,7 @@ StringMap::Entry Graphics::suppor { "dxt", Graphics::SUPPORT_DXT }, { "bc5", Graphics::SUPPORT_BC5 }, { "instancing", Graphics::SUPPORT_INSTANCING }, + { "srgb", Graphics::SUPPORT_SRGB }, }; StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); diff --git a/jni/love/src/modules/graphics/Graphics.h b/jni/love/src/modules/graphics/Graphics.h index f9ca36a2..cc3396f2 100644 --- a/jni/love/src/modules/graphics/Graphics.h +++ b/jni/love/src/modules/graphics/Graphics.h @@ -95,6 +95,7 @@ public: SUPPORT_DXT, SUPPORT_BC5, SUPPORT_INSTANCING, + SUPPORT_SRGB, SUPPORT_MAX_ENUM }; @@ -135,7 +136,7 @@ public: * @param width The viewport width. * @param height The viewport height. **/ - virtual bool setMode(int width, int height) = 0; + virtual bool setMode(int width, int height, bool &sRGB) = 0; /** * Un-sets the current graphics display mode (uninitializing objects if diff --git a/jni/love/src/modules/graphics/Texture.cpp b/jni/love/src/modules/graphics/Texture.cpp index 25034a3d..0a8d8554 100644 --- a/jni/love/src/modules/graphics/Texture.cpp +++ b/jni/love/src/modules/graphics/Texture.cpp @@ -109,6 +109,16 @@ bool Texture::getConstant(WrapMode in, const char *&out) return wrapModes.find(in, out); } +bool Texture::getConstant(const char *in, Format &out) +{ + return formats.find(in, out); +} + +bool Texture::getConstant(Format in, const char *&out) +{ + return formats.find(in, out); +} + StringMap::Entry Texture::filterModeEntries[] = { { "linear", Texture::FILTER_LINEAR }, @@ -125,6 +135,15 @@ StringMap::Entry Texture::wrapModeEnt StringMap Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries)); +StringMap::Entry Texture::formatEntries[] = +{ + {"normal", Texture::FORMAT_NORMAL}, + {"hdr", Texture::FORMAT_HDR}, + {"srgb", Texture::FORMAT_SRGB}, +}; + +StringMap Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries)); + } // graphics } // love diff --git a/jni/love/src/modules/graphics/Texture.h b/jni/love/src/modules/graphics/Texture.h index 7b5a525a..d9c83171 100644 --- a/jni/love/src/modules/graphics/Texture.h +++ b/jni/love/src/modules/graphics/Texture.h @@ -55,6 +55,14 @@ public: FILTER_MAX_ENUM }; + enum Format + { + FORMAT_NORMAL, + FORMAT_HDR, + FORMAT_SRGB, + FORMAT_MAX_ENUM + }; + struct Filter { Filter(); @@ -111,6 +119,9 @@ public: static bool getConstant(const char *in, WrapMode &out); static bool getConstant(WrapMode in, const char *&out); + static bool getConstant(const char *in, Format &out); + static bool getConstant(Format in, const char *&out); + protected: int width; @@ -132,6 +143,9 @@ private: static StringMap::Entry wrapModeEntries[]; static StringMap wrapModes; + static StringMap::Entry formatEntries[]; + static StringMap formats; + }; // Texture } // graphics diff --git a/jni/love/src/modules/graphics/opengl/Canvas.cpp b/jni/love/src/modules/graphics/opengl/Canvas.cpp index efed8598..4d9f0c1d 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/Canvas.cpp @@ -389,7 +389,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT } }; -FramebufferStrategy *strategy = NULL; +FramebufferStrategy *strategy = nullptr; FramebufferStrategy strategyNone; @@ -401,8 +401,9 @@ FramebufferStrategyPackedEXT strategyPackedEXT; FramebufferStrategyEXT strategyEXT; -Canvas *Canvas::current = NULL; +Canvas *Canvas::current = nullptr; OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport(); +bool Canvas::screenHasSRGB = false; static void getStrategy() { @@ -421,16 +422,13 @@ static void getStrategy() } } -static int maxFBOColorAttachments = 0; -static int maxDrawBuffers = 0; - -Canvas::Canvas(int width, int height, TextureType texture_type, int fsaa) +Canvas::Canvas(int width, int height, Texture::Format format, int fsaa) : fbo(0) , resolve_fbo(0) , texture(0) , fsaa_buffer(0) , depth_stencil(0) - , texture_type(texture_type) + , format(format) , fsaa_samples(fsaa) , fsaa_dirty(false) { @@ -534,15 +532,19 @@ bool Canvas::loadVolatile() GLint internalformat; GLenum textype; - switch (texture_type) + switch (format) { - case TYPE_HDR: + case Texture::FORMAT_HDR: internalformat = GL_RGBA16F; textype = GL_FLOAT; break; - case TYPE_NORMAL: + case Texture::FORMAT_SRGB: + internalformat = GL_SRGB_ALPHA; + textype = GL_UNSIGNED_BYTE; + break; + case Texture::FORMAT_NORMAL: default: - internalformat = GL_RGBA8; + internalformat = GL_RGBA; textype = GL_UNSIGNED_BYTE; } @@ -682,20 +684,30 @@ void Canvas::setupGrab() if (current == this) return; - // cleanup after previous fbo - if (current != NULL) - current->stopGrab(); + // cleanup after previous Canvas + if (current != nullptr) + { + systemViewport = current->systemViewport; + current->stopGrab(true); + } + else + systemViewport = gl.getViewport(); + + // indicate we are using this Canvas. + current = this; // bind the framebuffer object. - systemViewport = gl.getViewport(); strategy->bindFBO(fbo); gl.setViewport(OpenGL::Viewport(0, 0, width, height)); // Set up orthographic view (no depth) gl.matrices.projection.push(Matrix::ortho(0.0, width, 0.0, height)); - // indicate we are using this fbo - current = this; + // Make sure the correct sRGB setting is used when drawing to the canvas. + if (format == FORMAT_SRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else if (screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); if (fsaa_buffer != 0) fsaa_dirty = true; @@ -712,7 +724,7 @@ void Canvas::startGrab(const std::vector &canvases) if (!isMultiCanvasSupported()) throw love::Exception("Multi-canvas rendering is not supported on this system."); - if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments)) + if ((int) canvases.size() + 1 > gl.getMaxRenderTargets()) throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1); if (fsaa_samples != 0) @@ -724,8 +736,8 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height) throw love::Exception("All canvas arguments must have the same dimensions."); - if (canvases[i]->getTextureType() != texture_type) - throw love::Exception("All canvas arguments must have the same texture type."); + if (canvases[i]->getTextureFormat() != format) + throw love::Exception("All canvas arguments must have the same texture format."); if (canvases[i]->getFSAA() != 0) throw love::Exception("Multi-canvas rendering is not supported with FSAA."); @@ -769,7 +781,7 @@ void Canvas::startGrab() attachedCanvases.clear(); } -void Canvas::stopGrab() +void Canvas::stopGrab(bool switchingToOtherCanvas) { // i am not grabbing. leave me alone if (current != this) @@ -782,13 +794,25 @@ void Canvas::stopGrab() glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments); } - // bind default - strategy->bindFBO(gl.getDefaultFBO()); + if (switchingToOtherCanvas) + { + if (format == FORMAT_SRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + } + else + { + // bind the default framebuffer. + strategy->bindFBO(gl.getDefaultFBO()); + current = nullptr; + gl.setViewport(systemViewport); + + if (format == FORMAT_SRGB && !screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + else if (format != FORMAT_SRGB && screenHasSRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + } + gl.matrices.projection.pop(); - - current = nullptr; - - gl.setViewport(systemViewport); } void Canvas::clear(Color c) @@ -959,6 +983,18 @@ bool Canvas::isHDRSupported() return GLAD_VERSION_3_0 || (isSupported() && GLAD_ARB_texture_float); } +bool Canvas::isSRGBSupported() +{ + if (GLAD_VERSION_3_0) + return true; + + if (!isSupported()) + return false; + + return (GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) + && GLAD_EXT_texture_sRGB; +} + bool Canvas::isMultiCanvasSupported() { // system must support at least 4 simultanious active canvases. @@ -971,23 +1007,6 @@ void Canvas::bindDefaultCanvas() current->stopGrab(); } -bool Canvas::getConstant(const char *in, Canvas::TextureType &out) -{ - return textureTypes.find(in, out); -} - -bool Canvas::getConstant(Canvas::TextureType in, const char *&out) -{ - return textureTypes.find(in, out); -} - -StringMap::Entry Canvas::textureTypeEntries[] = -{ - {"normal", Canvas::TYPE_NORMAL}, - {"hdr", Canvas::TYPE_HDR}, -}; -StringMap Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries)); - } // opengl } // graphics } // love diff --git a/jni/love/src/modules/graphics/opengl/Canvas.h b/jni/love/src/modules/graphics/opengl/Canvas.h index f643294f..2d87b0bb 100644 --- a/jni/love/src/modules/graphics/opengl/Canvas.h +++ b/jni/love/src/modules/graphics/opengl/Canvas.h @@ -39,14 +39,7 @@ class Canvas : public Texture { public: - enum TextureType - { - TYPE_NORMAL, - TYPE_HDR, - TYPE_MAX_ENUM - }; - - Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL, int fsaa = 0); + Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); virtual ~Canvas(); // Implements Volatile. @@ -69,7 +62,7 @@ public: **/ void startGrab(const std::vector &canvases); void startGrab(); - void stopGrab(); + void stopGrab(bool switchingToOtherCanvas = false); void clear(Color c); @@ -92,9 +85,9 @@ public: return status; } - inline TextureType getTextureType() const + inline Texture::Format getTextureFormat() const { - return texture_type; + return format; } inline int getFSAA() const @@ -106,17 +99,18 @@ public: static bool isSupported(); static bool isHDRSupported(); + static bool isSRGBSupported(); static bool isMultiCanvasSupported(); - static bool getConstant(const char *in, TextureType &out); - static bool getConstant(TextureType in, const char *&out); - static Canvas *current; static void bindDefaultCanvas(); // The viewport dimensions of the system (default) framebuffer. static OpenGL::Viewport systemViewport; + // Whether the main screen should have linear -> sRGB conversions enabled. + static bool screenHasSRGB; + private: bool createFSAAFBO(GLenum internalformat); @@ -128,7 +122,7 @@ private: GLuint fsaa_buffer; GLuint depth_stencil; - TextureType texture_type; + Format format; GLenum status; @@ -140,9 +134,7 @@ private: void setupGrab(); void drawv(const Matrix &t, const Vertex *v); - static StringMap::Entry textureTypeEntries[]; - static StringMap textureTypes; -}; +}; // Canvas } // opengl } // graphics diff --git a/jni/love/src/modules/graphics/opengl/Graphics.cpp b/jni/love/src/modules/graphics/opengl/Graphics.cpp index b19935fe..81893170 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/Graphics.cpp @@ -56,8 +56,13 @@ Graphics::Graphics() { currentWindow = love::window::sdl::Window::createSingleton(); + int w, h; + love::window::WindowSettings wsettings; + + currentWindow->getWindow(w, h, wsettings); + if (currentWindow->isCreated()) - setMode(currentWindow->getWidth(), currentWindow->getHeight()); + setMode(w, h, wsettings.sRGB); } Graphics::~Graphics() @@ -104,6 +109,8 @@ DisplayState Graphics::saveState() for (int i = 0; i < 4; i++) s.colorMask[i] = colorMask[i]; + wireframe = isWireframe(); + return s; } @@ -121,6 +128,7 @@ void Graphics::restoreState(const DisplayState &s) else setScissor(); setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]); + setWireframe(s.wireframe); } void Graphics::setViewportSize(int width, int height) @@ -151,7 +159,7 @@ void Graphics::setViewportSize(int width, int height) c->startGrab(c->getAttachedCanvases()); } -bool Graphics::setMode(int width, int height) +bool Graphics::setMode(int width, int height, bool &sRGB) { this->width = width; this->height = height; @@ -181,9 +189,6 @@ bool Graphics::setMode(int width, int height) // Enable blending glEnable(GL_BLEND); - // "Normal" blending - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // Enable all color component writes. setColorMask(true, true, true, true); @@ -228,6 +233,19 @@ bool Graphics::setMode(int width, int height) Shader::defaultShader->attach(); } + // Set whether drawing converts input from linear -> sRGB colorspace. + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) + { + if (sRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else + glDisable(GL_FRAMEBUFFER_SRGB); + } + else + sRGB = false; + + Canvas::screenHasSRGB = sRGB; + bool enabledebug = false; if (GLAD_VERSION_3_0) @@ -238,6 +256,8 @@ bool Graphics::setMode(int width, int height) enabledebug = (flags & GL_CONTEXT_FLAG_DEBUG_BIT) != 0; } + // FIXME: workaround for KHR_debug being supported but function pointers + // resolving to null on Ouya. if (!GLAD_ES_VERSION_2_0) setDebug(enabledebug); @@ -313,6 +333,9 @@ void Graphics::setDebug(bool enable) glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE); + if (GLAD_VERSION_4_3 || GLAD_KHR_debug) + glEnable(GL_DEBUG_OUTPUT); + ::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n"); } @@ -431,10 +454,10 @@ void Graphics::discardStencil() glDisable(GL_STENCIL_TEST); } -Image *Graphics::newImage(love::image::ImageData *data) +Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format) { // Create the image. - Image *image = new Image(data); + Image *image = new Image(data, format); if (!isCreated()) return image; @@ -458,10 +481,10 @@ Image *Graphics::newImage(love::image::ImageData *data) return image; } -Image *Graphics::newImage(love::image::CompressedData *cdata) +Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format) { // Create the image. - Image *image = new Image(cdata); + Image *image = new Image(cdata, format); if (!isCreated()) return image; @@ -505,11 +528,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size) return new ParticleSystem(texture, size); } -Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type, int fsaa) +Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa) { - if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported()) + if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported()) throw Exception("HDR Canvases are not supported by your OpenGL implementation"); + if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported()) + throw Exception("sRGB Canvases are not supported by your OpenGL implementation"); + if (width > gl.getMaxTextureSize()) throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width); else if (height > gl.getMaxTextureSize()) @@ -518,7 +544,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t while (GL_NO_ERROR != glGetError()) /* clear opengl error flag */; - Canvas *canvas = new Canvas(width, height, texture_type, fsaa); + Canvas *canvas = new Canvas(width, height, format, fsaa); GLenum err = canvas->getStatus(); // everything ok, return canvas (early out) @@ -630,22 +656,16 @@ const bool *Graphics::getColorMask() const void Graphics::setBlendMode(Graphics::BlendMode mode) { - const int gl_1_4 = GLAD_VERSION_1_4; - - GLenum func = GL_FUNC_ADD; - GLenum src_rgb = GL_ONE; - GLenum src_a = GL_ONE; - GLenum dst_rgb = GL_ZERO; - GLenum dst_a = GL_ZERO; + OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; switch (mode) { case BLEND_ALPHA: - if (gl_1_4 || GLAD_ES_VERSION_2_0 || GLAD_EXT_blend_func_separate) + if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4 || GLAD_EXT_blend_func_separate) { - src_rgb = GL_SRC_ALPHA; - src_a = GL_ONE; - dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA; + state.srcRGB = GL_SRC_ALPHA; + state.srcA = GL_ONE; + state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; } else { @@ -653,100 +673,56 @@ void Graphics::setBlendMode(Graphics::BlendMode mode) // This will most likely only be used for the Microsoft software renderer and // since it's still stuck with OpenGL 1.1, the only expected difference is a // different alpha value when reading back the default framebuffer (newScreenshot). - src_rgb = src_a = GL_SRC_ALPHA; - dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA; + state.srcRGB = state.srcA = GL_SRC_ALPHA; + state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; } break; case BLEND_MULTIPLICATIVE: - src_rgb = src_a = GL_DST_COLOR; - dst_rgb = dst_a = GL_ZERO; + state.srcRGB = state.srcA = GL_DST_COLOR; + state.dstRGB = state.dstA = GL_ZERO; break; case BLEND_PREMULTIPLIED: - src_rgb = src_a = GL_ONE; - dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA; + state.srcRGB = state.srcA = GL_ONE; + state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; break; case BLEND_SUBTRACTIVE: - func = GL_FUNC_REVERSE_SUBTRACT; + state.func = GL_FUNC_REVERSE_SUBTRACT; case BLEND_ADDITIVE: - src_rgb = src_a = GL_SRC_ALPHA; - dst_rgb = dst_a = GL_ONE; + state.srcRGB = state.srcA = GL_SRC_ALPHA; + state.dstRGB = state.dstA = GL_ONE; break; case BLEND_REPLACE: default: - src_rgb = src_a = GL_ONE; - dst_rgb = dst_a = GL_ZERO; + state.srcRGB = state.srcA = GL_ONE; + state.dstRGB = state.dstA = GL_ZERO; break; } - if (gl_1_4 || GLAD_ES_VERSION_2_0) - glBlendEquation(func); - else if (GLAD_EXT_blend_minmax && GLAD_EXT_blend_subtract) - glBlendEquationEXT(func); - else - { - if (func == GL_FUNC_REVERSE_SUBTRACT) - throw Exception("This graphics card does not support the subtractive blend mode!"); - // GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work. - } - - if (src_rgb == src_a && dst_rgb == dst_a) - glBlendFunc(src_rgb, dst_rgb); - else - { - if (gl_1_4 || GLAD_ES_VERSION_2_0) - glBlendFuncSeparate(src_rgb, dst_rgb, src_a, dst_a); - else if (GLAD_EXT_blend_func_separate) - glBlendFuncSeparateEXT(src_rgb, dst_rgb, src_a, dst_a); - else - throw Exception("This graphics card does not support separated rgb and alpha blend functions!"); - } + gl.setBlendState(state); } Graphics::BlendMode Graphics::getBlendMode() const { - const int gl_1_4 = GLAD_VERSION_1_4; + OpenGL::BlendState state = gl.getBlendState(); - GLint src_rgb, src_a, dst_rgb, dst_a; - GLint equation = GL_FUNC_ADD; - - if (gl_1_4 || GLAD_ES_VERSION_2_0 || GLAD_EXT_blend_func_separate) - { - glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb); - glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_a); - glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb); - glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_a); - } - else - { - glGetIntegerv(GL_BLEND_SRC, &src_rgb); - glGetIntegerv(GL_BLEND_DST, &dst_rgb); - src_a = src_rgb; - dst_a = dst_rgb; - } - - if (GLAD_ES_VERSION_2_0) - glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation); - else if (gl_1_4 || (GLAD_EXT_blend_minmax && GLAD_EXT_blend_subtract)) - glGetIntegerv(GL_BLEND_EQUATION, &equation); - - if (equation == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE + if (state.func == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE return BLEND_SUBTRACTIVE; // Everything else has equation == GL_FUNC_ADD. - else if (src_rgb == src_a && dst_rgb == dst_a) + else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA) { - if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE) + if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE) return BLEND_ADDITIVE; - else if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE_MINUS_SRC_ALPHA) + else if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) return BLEND_ALPHA; // alpha blend mode fallback for very old OpenGL versions. - else if (src_rgb == GL_DST_COLOR && dst_rgb == GL_ZERO) + else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO) return BLEND_MULTIPLICATIVE; - else if (src_rgb == GL_ONE && dst_rgb == GL_ONE_MINUS_SRC_ALPHA) + else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) return BLEND_PREMULTIPLIED; - else if (src_rgb == GL_ONE && dst_rgb == GL_ZERO) + else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO) return BLEND_REPLACE; } - else if (src_rgb == GL_SRC_ALPHA && src_a == GL_ONE && - dst_rgb == GL_ONE_MINUS_SRC_ALPHA && dst_a == GL_ONE_MINUS_SRC_ALPHA) + else if (state.srcRGB == GL_SRC_ALPHA && state.srcA == GL_ONE && + state.dstRGB == GL_ONE_MINUS_SRC_ALPHA && state.dstA == GL_ONE_MINUS_SRC_ALPHA) return BLEND_ALPHA; throw Exception("Unknown blend mode"); @@ -836,6 +812,21 @@ Graphics::PointStyle Graphics::getPointStyle() const return POINT_ROUGH; } +void Graphics::setWireframe(bool enable) +{ + // Not supported in OpenGL ES. + if (GLAD_ES_VERSION_2_0) + return; + + wireframe = enable; + glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL); +} + +bool Graphics::isWireframe() const +{ + return wireframe; +} + void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) { if (currentFont != nullptr) diff --git a/jni/love/src/modules/graphics/opengl/Graphics.h b/jni/love/src/modules/graphics/opengl/Graphics.h index 9ddf7488..0fc614b9 100644 --- a/jni/love/src/modules/graphics/opengl/Graphics.h +++ b/jni/love/src/modules/graphics/opengl/Graphics.h @@ -81,6 +81,8 @@ struct DisplayState // Color mask. bool colorMask[4]; + bool wireframe; + // Default values. DisplayState() { @@ -93,6 +95,7 @@ struct DisplayState pointStyle = Graphics::POINT_SMOOTH; scissor = false; colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true; + wireframe = false; } }; @@ -112,7 +115,7 @@ public: void restoreState(const DisplayState &s); virtual void setViewportSize(int width, int height); - virtual bool setMode(int width, int height); + virtual bool setMode(int width, int height, bool &sRGB); virtual void unSetMode(); void setDebug(bool enable); @@ -191,8 +194,8 @@ public: /** * Creates an Image object with padding and/or optimization. **/ - Image *newImage(love::image::ImageData *data); - Image *newImage(love::image::CompressedData *cdata); + Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); + Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); Quad *newQuad(Quad::Viewport v, float sw, float sh); @@ -205,7 +208,7 @@ public: ParticleSystem *newParticleSystem(Texture *texture, int size); - Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL, int fsaa = 0); + Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); Shader *newShader(const Shader::ShaderSources &sources); @@ -334,6 +337,19 @@ public: **/ PointStyle getPointStyle() const; + /** + * Sets whether graphics will be drawn as wireframe lines instead of filled + * triangles (has no effect for drawn points.) + * This should only be used as a debugging tool. The wireframe lines do not + * behave the same as regular love.graphics lines. + **/ + void setWireframe(bool enable); + + /** + * Gets whether wireframe drawing mode is enabled. + **/ + bool isWireframe() const; + /** * Draws text at the specified coordinates, with rotation and * scaling along both axes. @@ -459,6 +475,7 @@ private: float lineWidth; size_t matrixLimit; bool colorMask[4]; + bool wireframe; int width; int height; diff --git a/jni/love/src/modules/graphics/opengl/Image.cpp b/jni/love/src/modules/graphics/opengl/Image.cpp index 4be217fb..64923cab 100644 --- a/jni/love/src/modules/graphics/opengl/Image.cpp +++ b/jni/love/src/modules/graphics/opengl/Image.cpp @@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f; Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE; float Image::defaultMipmapSharpness = 0.0f; -Image::Image(love::image::ImageData *data) +Image::Image(love::image::ImageData *data, Texture::Format format) : data(data) , cdata(nullptr) , paddedWidth(width) @@ -45,6 +45,7 @@ Image::Image(love::image::ImageData *data) , mipmapSharpness(defaultMipmapSharpness) , mipmapsCreated(false) , compressed(false) + , format(format) , usingDefaultTexture(false) { width = data->getWidth(); @@ -54,7 +55,7 @@ Image::Image(love::image::ImageData *data) preload(); } -Image::Image(love::image::CompressedData *cdata) +Image::Image(love::image::CompressedData *cdata, Texture::Format format) : data(nullptr) , cdata(cdata) , paddedWidth(width) @@ -63,6 +64,7 @@ Image::Image(love::image::CompressedData *cdata) , mipmapSharpness(defaultMipmapSharpness) , mipmapsCreated(false) , compressed(true) + , format(format) , usingDefaultTexture(false) { width = cdata->getWidth(0); @@ -328,6 +330,9 @@ void Image::unload() bool Image::loadVolatile() { + if (format == FORMAT_SRGB && !hasSRGBSupport()) + throw love::Exception("sRGB images are not supported on this system."); + if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat())) { const char *str; @@ -400,9 +405,10 @@ void Image::uploadTexturePadded() } else if (data) { + GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB_ALPHA : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, + iformat, (GLsizei)paddedWidth, (GLsizei)paddedHeight, 0, @@ -437,9 +443,10 @@ void Image::uploadTexture() } else if (data) { + GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB_ALPHA : GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, + iformat, (GLsizei)width, (GLsizei)height, 0, @@ -497,6 +504,11 @@ bool Image::refresh() return true; } +Texture::Format Image::getFormat() const +{ + return format; +} + void Image::uploadDefaultTexture() { usingDefaultTexture = true; @@ -561,16 +573,27 @@ bool Image::isCompressed() const return compressed; } -GLenum Image::getCompressedFormat(image::CompressedData::Format format) const +GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const { - switch (format) + bool srgb = format == FORMAT_SRGB; + + switch (cformat) { case image::CompressedData::FORMAT_DXT1: - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; + else + return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; case image::CompressedData::FORMAT_DXT3: - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + else + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case image::CompressedData::FORMAT_DXT5: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + if (srgb) + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + else + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case image::CompressedData::FORMAT_BC4: return GL_COMPRESSED_RED_RGTC1; case image::CompressedData::FORMAT_BC4s: @@ -580,7 +603,10 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format format) const case image::CompressedData::FORMAT_BC5s: return GL_COMPRESSED_SIGNED_RG_RGTC2; default: - return GL_RGBA; + if (srgb) + return GL_SRGB_ALPHA; + else + return GL_RGBA; } } @@ -624,6 +650,11 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format) return false; } +bool Image::hasSRGBSupport() +{ + return GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB || GLAD_EXT_sRGB; +} + } // opengl } // graphics } // love diff --git a/jni/love/src/modules/graphics/opengl/Image.h b/jni/love/src/modules/graphics/opengl/Image.h index d6da44f2..4872b818 100644 --- a/jni/love/src/modules/graphics/opengl/Image.h +++ b/jni/love/src/modules/graphics/opengl/Image.h @@ -56,14 +56,14 @@ public: * * @param data The data from which to load the image. **/ - Image(love::image::ImageData *data); + Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); /** * Creates a new Image with compressed image data. * * @param cdata The compressed data from which to load the image. **/ - Image(love::image::CompressedData *cdata); + Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); /** * Destructor. Deletes the hardware texture and other resources. @@ -120,6 +120,8 @@ public: **/ bool refresh(); + Texture::Format getFormat() const; + static void setDefaultMipmapSharpness(float sharpness); static float getDefaultMipmapSharpness(); static void setDefaultMipmapFilter(FilterMode f); @@ -132,6 +134,8 @@ public: static bool hasCompressedTextureSupport(image::CompressedData::Format format); + static bool hasSRGBSupport(); + private: void uploadDefaultTexture(); @@ -161,6 +165,9 @@ private: // Whether this Image is using a compressed texture. bool compressed; + // The format to interpret the texture's data as. + Texture::Format format; + // True if the image wasn't able to be properly created and it had to fall // back to a default texture. bool usingDefaultTexture; @@ -179,7 +186,7 @@ private: static FilterMode defaultMipmapFilter; static float defaultMipmapSharpness; - GLenum getCompressedFormat(image::CompressedData::Format format) const; + GLenum getCompressedFormat(image::CompressedData::Format cformat) const; }; // Image diff --git a/jni/love/src/modules/graphics/opengl/Mesh.cpp b/jni/love/src/modules/graphics/opengl/Mesh.cpp index a9bdb6ed..51562685 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.cpp +++ b/jni/love/src/modules/graphics/opengl/Mesh.cpp @@ -44,7 +44,6 @@ Mesh::Mesh(const std::vector &verts, Mesh::DrawMode mode) , range_max(-1) , texture(nullptr) , colors_enabled(false) - , wireframe(false) { setVertices(verts); } @@ -59,7 +58,6 @@ Mesh::Mesh(int vertexcount, Mesh::DrawMode mode) , range_max(-1) , texture(nullptr) , colors_enabled(false) - , wireframe(false) { if (vertexcount < 1) throw love::Exception("Invalid number of vertices."); @@ -278,16 +276,6 @@ bool Mesh::hasVertexColors() const return colors_enabled; } -void Mesh::setWireframe(bool enable) -{ - wireframe = enable; -} - -bool Mesh::isWireframe() const -{ - return wireframe; -} - void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t pos_offset = offsetof(Vertex, x); @@ -326,9 +314,6 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset)); } - if (wireframe && GLAD_VERSION_1_0) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - GLenum mode = getGLDrawMode(draw_mode); gl.prepareDraw(); @@ -377,9 +362,6 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo gl.disableVertexAttribArray(OpenGL::ATTRIB_POS); gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD); - if (wireframe && GLAD_VERSION_1_0) - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - if (hasVertexColors()) { gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR); diff --git a/jni/love/src/modules/graphics/opengl/Mesh.h b/jni/love/src/modules/graphics/opengl/Mesh.h index 3a5029a4..5e6e974f 100644 --- a/jni/love/src/modules/graphics/opengl/Mesh.h +++ b/jni/love/src/modules/graphics/opengl/Mesh.h @@ -162,15 +162,6 @@ public: void setVertexColors(bool enable); bool hasVertexColors() const; - /** - * Sets whether the Mesh will be drawn as wireframe lines instead of filled - * triangles (has no effect for DRAW_MODE_POINTS.) - * This should only be used as a debugging tool. The wireframe lines do not - * behave the same as regular love.graphics lines. - **/ - void setWireframe(bool enable); - bool isWireframe() const; - // Implements Drawable. void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); @@ -201,8 +192,6 @@ private: // Whether the per-vertex colors are used when drawing. bool colors_enabled; - bool wireframe; - static StringMap::Entry drawModeEntries[]; static StringMap drawModes; diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.cpp b/jni/love/src/modules/graphics/opengl/OpenGL.cpp index 47837a03..95768b81 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.cpp +++ b/jni/love/src/modules/graphics/opengl/OpenGL.cpp @@ -127,6 +127,9 @@ bool OpenGL::initContext() if (Canvas::isSupported()) glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint *) &state.defaultFBO); + BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; + setBlendState(blend); + initMaxValues(); createDefaultTexture(); @@ -492,6 +495,40 @@ OpenGL::Viewport OpenGL::getScissor() const return state.scissor; } +void OpenGL::setBlendState(const BlendState &blend) +{ + if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4) + glBlendEquation(blend.func); + else if (GLAD_EXT_blend_minmax && GLAD_EXT_blend_subtract) + glBlendEquationEXT(blend.func); + else + { + if (blend.func == GL_FUNC_REVERSE_SUBTRACT) + throw love::Exception("This graphics card does not support the subtractive blend mode!"); + // GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work. + } + + if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA) + glBlendFunc(blend.srcRGB, blend.dstRGB); + else + { + if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4) + glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA); + else if (GLAD_EXT_blend_func_separate) + glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA); + else + throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!"); + } + + state.blend = blend; +} + +OpenGL::BlendState OpenGL::getBlendState() const +{ + return state.blend; +} + + void OpenGL::setPointSize(float size) { if (GLAD_VERSION_1_0) diff --git a/jni/love/src/modules/graphics/opengl/OpenGL.h b/jni/love/src/modules/graphics/opengl/OpenGL.h index c802b148..69675e11 100644 --- a/jni/love/src/modules/graphics/opengl/OpenGL.h +++ b/jni/love/src/modules/graphics/opengl/OpenGL.h @@ -103,6 +103,13 @@ public: } }; + struct BlendState + { + GLenum srcRGB, srcA; + GLenum dstRGB, dstA; + GLenum func; + }; + // Transformation matrix stacks. struct { @@ -202,6 +209,17 @@ public: **/ Viewport getScissor() const; + /** + * Sets blending functionality. + * Note: This does not globally enable or disable blending. + **/ + void setBlendState(const BlendState &blend); + + /** + * Gets the currently set blending functionality. + **/ + BlendState getBlendState() const; + /** * Sets the global point size. **/ @@ -332,6 +350,8 @@ private: GLuint defaultFBO; + BlendState blend; + // The last ID value used for pseudo-instancing. int lastPseudoInstanceID; diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp index 9c9fb0c4..4979fc69 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.cpp @@ -95,6 +95,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size) , spinVariation(0) , offsetX(float(texture->getWidth())*0.5f) , offsetY(float(texture->getHeight())*0.5f) + , relativeRotation(false) { if (size == 0 || size > MAX_PARTICLES) throw love::Exception("Invalid ParticleSystem size."); @@ -146,6 +147,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p) , offsetX(p.offsetX) , offsetY(p.offsetY) , colors(p.colors) + , relativeRotation(p.relativeRotation) { setBufferSize(maxParticles); @@ -170,7 +172,7 @@ void ParticleSystem::createBuffers(size_t size) { try { - pFree = pMem = new particle[size]; + pFree = pMem = new Particle[size]; particleVerts = new love::Vertex[size * 4]; ibo = new VertexIndex(size); maxParticles = (uint32) size; @@ -222,7 +224,7 @@ void ParticleSystem::addParticle(float t) return; // Gets a free particle and updates the allocation pointer. - particle *p = pFree++; + Particle *p = pFree++; initParticle(p, t); switch (insertMode) @@ -242,7 +244,7 @@ void ParticleSystem::addParticle(float t) activeParticles++; } -void ParticleSystem::initParticle(particle *p, float t) +void ParticleSystem::initParticle(Particle *p, float t) { float min,max; @@ -308,10 +310,14 @@ void ParticleSystem::initParticle(particle *p, float t) p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation); p->rotation = (float) rng.random(min, max); + p->angle = p->rotation; + if (relativeRotation) + p->angle += atan2f(p->speed.y, p->speed.x); + p->color = colors[0]; } -void ParticleSystem::insertTop(particle *p) +void ParticleSystem::insertTop(Particle *p) { if (pHead == nullptr) { @@ -327,7 +333,7 @@ void ParticleSystem::insertTop(particle *p) pTail = p; } -void ParticleSystem::insertBottom(particle *p) +void ParticleSystem::insertBottom(Particle *p) { if (pTail == nullptr) { @@ -343,7 +349,7 @@ void ParticleSystem::insertBottom(particle *p) pHead = p; } -void ParticleSystem::insertRandom(particle *p) +void ParticleSystem::insertRandom(Particle *p) { // Nonuniform, but 64-bit is so large nobody will notice. Hopefully. uint64 pos = rng.rand() % ((int64) activeParticles + 1); @@ -351,7 +357,7 @@ void ParticleSystem::insertRandom(particle *p) // Special case where the particle gets inserted before the head. if (pos == activeParticles) { - particle *pA = pHead; + Particle *pA = pHead; if (pA) pA->prev = p; p->prev = nullptr; @@ -361,8 +367,8 @@ void ParticleSystem::insertRandom(particle *p) } // Inserts the particle after the randomly selected particle. - particle *pA = pMem + pos; - particle *pB = pA->next; + Particle *pA = pMem + pos; + Particle *pB = pA->next; pA->next = p; if (pB) pB->prev = p; @@ -372,12 +378,12 @@ void ParticleSystem::insertRandom(particle *p) p->next = pB; } -ParticleSystem::particle *ParticleSystem::removeParticle(particle *p) +ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p) { // The linked list is updated in this function and old pointers may be // invalidated. The returned pointer will inform the caller of the new // pointer to the next particle. - particle *pNext = nullptr; + Particle *pNext = nullptr; // Removes the particle from the linked list. if (p->prev) @@ -725,6 +731,16 @@ std::vector ParticleSystem::getColor() const return ncolors; } +void ParticleSystem::setRelativeRotation(bool enable) +{ + relativeRotation = enable; +} + +bool ParticleSystem::hasRelativeRotation() const +{ + return relativeRotation; +} + uint32 ParticleSystem::getCount() const { return activeParticles; @@ -812,13 +828,13 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo const Vertex *textureVerts = texture->getVertices(); Vertex *pVerts = particleVerts; - particle *p = pHead; + Particle *p = pHead; // set the vertex data for each particle (transformation, texcoords, color) while (p) { // particle vertices are image vertices transformed by particle information - t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f); + t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f); t.transform(pVerts, textureVerts, 4); // set the texture coordinate and color data for particle vertices @@ -871,7 +887,7 @@ void ParticleSystem::update(float dt) return; // Traverse all particles and update. - particle *p = pHead; + Particle *p = pHead; while (p) { @@ -916,7 +932,12 @@ void ParticleSystem::update(float dt) const float t = 1.0f - p->life / p->lifetime; // Rotate. - p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt; + p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt; + + p->angle = p->rotation; + + if (relativeRotation) + p->angle += atan2f(p->speed.y, p->speed.x); // Change size according to given intervals: // i = 0 1 2 3 n-1 diff --git a/jni/love/src/modules/graphics/opengl/ParticleSystem.h b/jni/love/src/modules/graphics/opengl/ParticleSystem.h index 7373d4bf..ba1805fd 100644 --- a/jni/love/src/modules/graphics/opengl/ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/ParticleSystem.h @@ -422,6 +422,12 @@ public: **/ std::vector getColor() const; + /** + * sets whether particle angles & rotations are relative to their velocities. + **/ + void setRelativeRotation(bool enable); + bool hasRelativeRotation() const; + /** * Returns the amount of particles that are currently active in the system. **/ @@ -495,11 +501,12 @@ public: static bool getConstant(InsertMode in, const char *&out); protected: + // Represents a single particle. - struct particle + struct Particle { - particle *prev; - particle *next; + Particle *prev; + Particle *next; float lifetime; float life; @@ -519,7 +526,8 @@ protected: float sizeOffset; float sizeIntervalSize; - float rotation; + float rotation; // Amount of rotation applied to the final angle. + float angle; float spinStart; float spinEnd; @@ -527,16 +535,16 @@ protected: }; // Pointer to the beginning of the allocated memory. - particle *pMem; + Particle *pMem; // Pointer to a free particle. - particle *pFree; + Particle *pFree; // Pointer to the start of the linked list. - particle *pHead; + Particle *pHead; // Pointer to the end of the linked list. - particle *pTail; + Particle *pTail; // array of transformed vertex data for all particles, for drawing Vertex *particleVerts; @@ -621,17 +629,19 @@ protected: // Color. std::vector colors; + bool relativeRotation; + void createBuffers(size_t size); void deleteBuffers(); void addParticle(float t); - particle *removeParticle(particle *p); + Particle *removeParticle(Particle *p); // Called by addParticle. - void initParticle(particle *p, float t); - void insertTop(particle *p); - void insertBottom(particle *p); - void insertRandom(particle *p); + void initParticle(Particle *p, float t); + void insertTop(Particle *p); + void insertBottom(Particle *p); + void insertRandom(Particle *p); static StringMap::Entry distributionsEntries[]; static StringMap distributions; diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp index ad2c5b3c..a10f07a7 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.cpp @@ -105,6 +105,8 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl if ((index == -1 && next >= size) || index < -1 || index >= size) return -1; + Vertex sprite[4]; + // Needed for colors. memcpy(sprite, texture->getVertices(), sizeof(Vertex) * 4); @@ -131,6 +133,8 @@ int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy, if ((index == -1 && next >= size) || index < -1 || index >= next) return -1; + Vertex sprite[4]; + // Needed for colors. memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4); diff --git a/jni/love/src/modules/graphics/opengl/SpriteBatch.h b/jni/love/src/modules/graphics/opengl/SpriteBatch.h index ae79db8d..7339c7c7 100644 --- a/jni/love/src/modules/graphics/opengl/SpriteBatch.h +++ b/jni/love/src/modules/graphics/opengl/SpriteBatch.h @@ -135,8 +135,6 @@ private: // The next free element. int next; - Vertex sprite[4]; - // Current color. This color, if present, will be applied to the next // added sprite. Color *color; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp index cbc9b6ed..a3c31b7f 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -110,12 +110,14 @@ int w_Canvas_clear(lua_State *L) return 0; } -int w_Canvas_getType(lua_State *L) +int w_Canvas_getFormat(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - Canvas::TextureType type = canvas->getTextureType(); + Texture::Format format = canvas->getTextureFormat(); const char *str; - Canvas::getConstant(type, str); + if (!Texture::getConstant(format, str)) + return luaL_error(L, "Unknown texture format."); + lua_pushstring(L, str); return 1; } @@ -142,8 +144,11 @@ static const luaL_Reg functions[] = { "getImageData", w_Canvas_getImageData }, { "getPixel", w_Canvas_getPixel }, { "clear", w_Canvas_clear }, - { "getType", w_Canvas_getType }, + { "getFormat", w_Canvas_getFormat }, { "getFSAA", w_Canvas_getFSAA }, + + // Deprecated since 0.9.1. + { "getType", w_Canvas_getFormat }, { 0, 0 } }; diff --git a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h index 89c7952f..4e48ffe0 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Canvas.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Canvas.h @@ -39,7 +39,7 @@ int w_Canvas_renderTo(lua_State *L); int w_Canvas_getImageData(lua_State *L); int w_Canvas_getPixel(lua_State * L); int w_Canvas_clear(lua_State *L); -int w_Canvas_getType(lua_State *L); +int w_Canvas_getFormat(lua_State *L); int w_Canvas_getFSAA(lua_State *L); extern "C" int luaopen_canvas(lua_State *L); diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp index ca76ef85..48180f93 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -152,8 +152,17 @@ int w_getMaxTextureSize(lua_State *L) int w_newImage(lua_State *L) { - love::image::ImageData *data = 0; - love::image::CompressedData *cdata = 0; + love::image::ImageData *data = nullptr; + love::image::CompressedData *cdata = nullptr; + + Texture::Format format = Texture::FORMAT_NORMAL; + const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2); + + if (fstr != nullptr && !Texture::getConstant(fstr, format)) + return luaL_error(L, "Invalid texture format: %s", fstr); + + if (format == Texture::FORMAT_HDR) // For now... + return luaL_error(L, "HDR images are not supported."); // Convert to FileData, if necessary. if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T)) @@ -185,15 +194,15 @@ int w_newImage(lua_State *L) return luaL_error(L, "Error creating image."); // Create the image. - Image *image = 0; + Image *image = nullptr; EXCEPT_GUARD( if (cdata) - image = instance->newImage(cdata); + image = instance->newImage(cdata, format); else if (data) - image = instance->newImage(data); + image = instance->newImage(data, format); ) - if (image == 0) + if (image == nullptr) return luaL_error(L, "Could not load image."); // Push the type. @@ -325,12 +334,12 @@ int w_newCanvas(lua_State *L) const char *str = luaL_optstring(L, 3, "normal"); int fsaa = luaL_optint(L, 4, 0); - Canvas::TextureType texture_type; - if (!Canvas::getConstant(str, texture_type)) - return luaL_error(L, "Invalid canvas type: %s", str); + Texture::Format format; + if (!Texture::getConstant(str, format)) + return luaL_error(L, "Invalid texture format: %s", str); Canvas *canvas = nullptr; - EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type, fsaa);) + EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);) if (canvas == nullptr) return luaL_error(L, "Canvas not created, but no error thrown. I don't even..."); @@ -835,6 +844,18 @@ int w_getMaxPointSize(lua_State *L) return 1; } +int w_setWireframe(lua_State *L) +{ + instance->setWireframe(luax_toboolean(L, 1)); + return 0; +} + +int w_isWireframe(lua_State *L) +{ + luax_pushboolean(L, instance->isWireframe()); + return 1; +} + int w_newScreenshot(lua_State *L) { love::image::Image *image = luax_getmodule(L, "image", MODULE_IMAGE_T); @@ -1029,6 +1050,10 @@ int w_isSupported(lua_State *L) if (!(GLAD_ARB_draw_instanced || (GLAD_ES_VERSION_2_0 && GLAD_EXT_draw_instanced))) supported = false; break; + case Graphics::SUPPORT_SRGB: + if (!Canvas::isSRGBSupported()) + supported = false; + break; default: supported = false; } @@ -1405,6 +1430,8 @@ static const luaL_Reg functions[] = { "setPointStyle", w_setPointStyle }, { "getPointSize", w_getPointSize }, { "getPointStyle", w_getPointStyle }, + { "setWireframe", w_setWireframe }, + { "isWireframe", w_isWireframe }, { "newScreenshot", w_newScreenshot }, { "setCanvas", w_setCanvas }, { "getCanvas", w_getCanvas }, diff --git a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h index 5c85c819..88a5ad4d 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Graphics.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Graphics.h @@ -85,6 +85,8 @@ int w_setPointStyle(lua_State *L); int w_getPointSize(lua_State *L); int w_getPointStyle(lua_State *L); int w_getMaxPointSize(lua_State *L); +int w_setWireframe(lua_State *L); +int w_isWireframe(lua_State *L); int w_newScreenshot(lua_State *L); int w_setCanvas(lua_State *L); int w_getCanvas(lua_State *L); diff --git a/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp b/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp index 4548030a..e3e8800b 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -361,20 +361,6 @@ int w_Mesh_hasVertexColors(lua_State *L) return 1; } -int w_Mesh_setWireframe(lua_State *L) -{ - Mesh *t = luax_checkmesh(L, 1); - t->setWireframe(luax_toboolean(L, 2)); - return 0; -} - -int w_Mesh_isWireframe(lua_State *L) -{ - Mesh *t = luax_checkmesh(L, 1); - luax_pushboolean(L, t->isWireframe()); - return 1; -} - static const luaL_Reg functions[] = { { "setVertex", w_Mesh_setVertex }, @@ -394,8 +380,6 @@ static const luaL_Reg functions[] = { "getDrawRange", w_Mesh_getDrawRange }, { "setVertexColors", w_Mesh_setVertexColors }, { "hasVertexColors", w_Mesh_hasVertexColors }, - { "setWireframe", w_Mesh_setWireframe }, - { "isWireframe", w_Mesh_isWireframe }, // Deprecated since 0.9.1. { "setImage", w_Mesh_setTexture }, diff --git a/jni/love/src/modules/graphics/opengl/wrap_Mesh.h b/jni/love/src/modules/graphics/opengl/wrap_Mesh.h index 9b4cdc77..aa7c0f08 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_Mesh.h +++ b/jni/love/src/modules/graphics/opengl/wrap_Mesh.h @@ -51,8 +51,6 @@ int w_Mesh_setDrawRange(lua_State *L); int w_Mesh_getDrawRange(lua_State *L); int w_Mesh_setVertexColors(lua_State *L); int w_Mesh_hasVertexColors(lua_State *L); -int w_Mesh_setWireframe(lua_State *L); -int w_Mesh_isWireframe(lua_State *L); extern "C" int luaopen_mesh(lua_State *L); diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp index 6e875b64..6cb3c041 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.cpp @@ -569,6 +569,20 @@ int w_ParticleSystem_getColors(lua_State *L) return colors.size(); } +int w_ParticleSystem_setRelativeRotation(lua_State *L) +{ + ParticleSystem *t = luax_checkparticlesystem(L, 1); + t->setRelativeRotation(luax_toboolean(L, 2)); + return 0; +} + +int w_ParticleSystem_hasRelativeRotation(lua_State *L) +{ + ParticleSystem *t = luax_checkparticlesystem(L, 1); + luax_pushboolean(L, t->hasRelativeRotation()); + return 1; +} + int w_ParticleSystem_getCount(lua_State *L) { ParticleSystem *t = luax_checkparticlesystem(L, 1); @@ -687,6 +701,8 @@ static const luaL_Reg functions[] = { "getColors", w_ParticleSystem_getColors }, { "setOffset", w_ParticleSystem_setOffset }, { "getOffset", w_ParticleSystem_getOffset }, + { "setRelativeRotation", w_ParticleSystem_setRelativeRotation }, + { "hasRelativeRotation", w_ParticleSystem_hasRelativeRotation }, { "getCount", w_ParticleSystem_getCount }, { "start", w_ParticleSystem_start }, { "stop", w_ParticleSystem_stop }, diff --git a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h index 08948c38..b3c96a54 100644 --- a/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h +++ b/jni/love/src/modules/graphics/opengl/wrap_ParticleSystem.h @@ -77,6 +77,8 @@ int w_ParticleSystem_setColors(lua_State *L); int w_ParticleSystem_getColors(lua_State *L); int w_ParticleSystem_setOffset(lua_State *L); int w_ParticleSystem_getOffset(lua_State *L); +int w_ParticleSystem_setRelativeRotation(lua_State *L); +int w_ParticleSystem_hasRelativeRotation(lua_State *L); int w_ParticleSystem_getCount(lua_State *L); int w_ParticleSystem_start(lua_State *L); int w_ParticleSystem_stop(lua_State *L); diff --git a/jni/love/src/modules/math/MathModule.cpp b/jni/love/src/modules/math/MathModule.cpp index 9ffefe83..ed096ebd 100644 --- a/jni/love/src/modules/math/MathModule.cpp +++ b/jni/love/src/modules/math/MathModule.cpp @@ -193,5 +193,35 @@ bool Math::isConvex(const std::vector &polygon) return true; } +/** + * http://en.wikipedia.org/wiki/SRGB#The_reverse_transformation + **/ +float Math::gammaToLinear(float c) const +{ + if (c > 1.0) + return 1.0; + else if (c < 0.0) + return 0.0; + else if (c <= 0.04045) + return c / 12.92; + else + return powf((c + 0.055) / 1.055, 2.4); +} + +/** + * http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29 + **/ +float Math::linearToGamma(float c) const +{ + if (c > 1.0) + return 1.0; + else if (c < 0.0) + return 0.0; + else if (c < 0.0031308) + return c * 12.92; + else + return 1.055 * powf(c, 0.41666) - 0.055; +} + } // math } // love diff --git a/jni/love/src/modules/math/MathModule.h b/jni/love/src/modules/math/MathModule.h index 70f42051..e9df716a 100644 --- a/jni/love/src/modules/math/MathModule.h +++ b/jni/love/src/modules/math/MathModule.h @@ -53,26 +53,6 @@ public: virtual ~Math() {} - inline void setRandomSeed(RandomGenerator::Seed seed) - { - rng.setSeed(seed); - } - - inline void setRandomSeed(uint32 low, uint32 high) - { - rng.setSeed(low, high); - } - - inline RandomGenerator::Seed getRandomSeed() const - { - return rng.getSeed(); - } - - inline void getRandomSeed(uint32 &low, uint32 &high) const - { - rng.getSeed(low, high); - } - /** * @copydoc RandomGenerator::random() **/ @@ -105,6 +85,26 @@ public: return rng.randomNormal(stddev); } + inline void setRandomSeed(RandomGenerator::Seed seed) + { + rng.setSeed(seed); + } + + inline RandomGenerator::Seed getRandomSeed() const + { + return rng.getSeed(); + } + + inline void setRandomState(const std::string &statestr) + { + rng.setState(statestr); + } + + inline std::string getRandomState() const + { + return rng.getState(); + } + /** * Create a new random number generator. **/ @@ -136,6 +136,16 @@ public: **/ bool isConvex(const std::vector &polygon); + /** + * Converts a value from the sRGB (gamma) colorspace to linear RGB. + **/ + float gammaToLinear(float c) const; + + /** + * Converts a value from linear RGB to the sRGB (gamma) colorspace. + **/ + float linearToGamma(float c) const; + /** * Calculate Simplex noise for the specified coordinate(s). * diff --git a/jni/love/src/modules/math/RandomGenerator.cpp b/jni/love/src/modules/math/RandomGenerator.cpp index 1f7b58a4..0e26d3e8 100644 --- a/jni/love/src/modules/math/RandomGenerator.cpp +++ b/jni/love/src/modules/math/RandomGenerator.cpp @@ -20,8 +20,13 @@ #include "RandomGenerator.h" -// STL +// C++ #include +#include +#include + +// C +#include namespace love { @@ -36,25 +41,10 @@ RandomGenerator::RandomGenerator() { // because it is too big for some compilers to handle ... if you know what // i mean -#ifdef LOVE_BIG_ENDIAN - seed.b32.a = 0x0139408D; - seed.b32.b = 0xCBBF7A44; -#else - seed.b32.b = 0x0139408D; - seed.b32.a = 0xCBBF7A44; -#endif - - rng_state = seed; -} - -void RandomGenerator::setSeed(RandomGenerator::Seed newseed) -{ - // 0 xor 0 is still 0, so Xorshift can't generate new numbers. - if (newseed.b64 == 0) - throw love::Exception("Invalid random seed."); - - seed = newseed; - rng_state = seed; + Seed newseed; + newseed.b32.low = 0xCBBF7A44; + newseed.b32.high = 0x0139408D; + setSeed(newseed); } uint64 RandomGenerator::rand() @@ -83,5 +73,71 @@ double RandomGenerator::randomNormal(double stddev) return r * sin(phi) * stddev; } +void RandomGenerator::setSeed(RandomGenerator::Seed newseed) +{ + // 0 xor 0 is still 0, so Xorshift can't generate new numbers. + if (newseed.b64 == 0) + throw love::Exception("Invalid random seed."); + + seed = newseed; + rng_state = seed; +} + +RandomGenerator::Seed RandomGenerator::getSeed() const +{ + return seed; +} + +void RandomGenerator::setState(const std::string &statestr) +{ + // For this implementation we'll accept a hex string representing the + // 64-bit state integer xorshift uses. + + Seed state = {}; + + // Hex string must start with 0x. + if (statestr.find("0x") != 0 || statestr.size() < 3) + throw love::Exception("Invalid random state."); + + // standardized strtoull (or 64 bit integer support for stringstream) + // requires C++11's standard library, which we can't use yet. + // I use strtol like this not because it's the best solution, but because + // it's "good enough". + + // Convert the hex string to the state integer character-by-character. + for (size_t i = 2; i < statestr.size(); i++) + { + char hex[2] = {statestr[i], 0}; + char *end = nullptr; + + // Convert the current hex character to a number. + int nibble = strtol(hex, &end, 16); + + // Check if strtol failed to convert it. + if (end != nullptr && *end != 0) + throw love::Exception("Invalid random state."); + + state.b64 = (state.b64 << 4) + nibble; + } + + rng_state = state; +} + +std::string RandomGenerator::getState() const +{ + // For this implementation we'll return a hex string representing the 64-bit + // state integer xorshift uses. + + std::stringstream ss; + + ss << "0x"; + + // Again with the stringstream not dealing with 64 bit integers... + ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.high; + ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.low; + + return ss.str(); +} + } // math } // love diff --git a/jni/love/src/modules/math/RandomGenerator.h b/jni/love/src/modules/math/RandomGenerator.h index 830a9506..aab0f927 100644 --- a/jni/love/src/modules/math/RandomGenerator.h +++ b/jni/love/src/modules/math/RandomGenerator.h @@ -28,8 +28,9 @@ #include "common/int.h" #include "common/Object.h" -// STL +// C++ #include +#include namespace love { @@ -45,54 +46,19 @@ public: uint64 b64; struct { - uint32 a; - uint32 b; +#ifdef LOVE_BIG_ENDIAN + uint32 high; + uint32 low; +#else + uint32 low; + uint32 high; +#endif } b32; }; RandomGenerator(); virtual ~RandomGenerator() {} - /** - * Set pseudo-random seed. - * It's up to the implementation how to use this. - **/ - void setSeed(Seed seed); - - /** - * Separately set the low and high bits of the pseudo-random seed. - **/ - inline void setSeed(uint32 low, uint32 high) - { - Seed newseed; - -#ifdef LOVE_BIG_ENDIAN - newseed.b32.a = high; - newseed.b32.b = low; -#else - newseed.b32.b = high; - newseed.b32.a = low; -#endif - - setSeed(newseed); - } - - inline Seed getSeed() const - { - return seed; - } - - inline void getSeed(uint32 &low, uint32 &high) const - { -#ifdef LOVE_BIG_ENDIAN - high = seed.b32.a; - low = seed.b32.b; -#else - high = seed.b32.b; - low = seed.b32.a; -#endif - } - /** * Return uniformly distributed pseudo random integer. * @@ -138,6 +104,28 @@ public: **/ double randomNormal(double stddev); + /** + * Set pseudo-random seed. + * It's up to the implementation how to use this. + **/ + void setSeed(Seed seed); + + /** + * Get the previously set pseudo-random seed. + **/ + Seed getSeed() const; + + /** + * Set the internal implementation-dependent state value based on a string. + **/ + void setState(const std::string &statestr); + + /** + * Get a string representation of the implementation-dependent internal + * state value. + **/ + std::string getState() const; + private: Seed seed; diff --git a/jni/love/src/modules/math/wrap_Math.cpp b/jni/love/src/modules/math/wrap_Math.cpp index 95cf3794..50b78f95 100644 --- a/jni/love/src/modules/math/wrap_Math.cpp +++ b/jni/love/src/modules/math/wrap_Math.cpp @@ -32,21 +32,6 @@ namespace love namespace math { -int w_setRandomSeed(lua_State *L) -{ - EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));) - return 0; -} - -int w_getRandomSeed(lua_State *L) -{ - uint32 low = 0, high = 0; - Math::instance.getRandomSeed(low, high); - lua_pushnumber(L, (lua_Number) low); - lua_pushnumber(L, (lua_Number) high); - return 2; -} - int w_random(lua_State *L) { return luax_getrandom(L, 1, Math::instance.random()); @@ -62,6 +47,32 @@ int w_randomNormal(lua_State *L) return 1; } +int w_setRandomSeed(lua_State *L) +{ + EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));) + return 0; +} + +int w_getRandomSeed(lua_State *L) +{ + RandomGenerator::Seed s = Math::instance.getRandomSeed(); + lua_pushnumber(L, (lua_Number) s.b32.low); + lua_pushnumber(L, (lua_Number) s.b32.high); + return 2; +} + +int w_setRandomState(lua_State *L) +{ + EXCEPT_GUARD(Math::instance.setRandomState(luax_checkstring(L, 1));) + return 0; +} + +int w_getRandomState(lua_State *L) +{ + luax_pushstring(L, Math::instance.getRandomState()); + return 1; +} + int w_newRandomGenerator(lua_State *L) { RandomGenerator::Seed s; @@ -238,6 +249,70 @@ int w_isConvex(lua_State *L) return 1; } +static int getGammaArgs(lua_State *L, float color[4]) +{ + int numcomponents = 0; + + if (lua_istable(L, 1)) + { + int n = lua_objlen(L, 1); + for (int i = 1; i <= n && i <= 4; i++) + { + lua_rawgeti(L, 1, i); + color[i - 1] = (float) luaL_checknumber(L, -1) / 255.0; + numcomponents++; + } + + lua_pop(L, numcomponents); + } + else + { + int n = lua_gettop(L); + for (int i = 1; i <= n && i <= 4; i++) + { + color[i - 1] = (float) luaL_checknumber(L, i) / 255.0; + numcomponents++; + } + } + + if (numcomponents == 0) + luaL_checknumber(L, 1); + + return numcomponents; +} + +int w_gammaToLinear(lua_State *L) +{ + float color[4]; + int numcomponents = getGammaArgs(L, color); + + for (int i = 0; i < numcomponents; i++) + { + // Alpha should always be linear. + if (i < 3) + color[i] = Math::instance.gammaToLinear(color[i]); + lua_pushnumber(L, color[i] * 255); + } + + return numcomponents; +} + +int w_linearToGamma(lua_State *L) +{ + float color[4]; + int numcomponents = getGammaArgs(L, color); + + for (int i = 0; i < numcomponents; i++) + { + // Alpha should always be linear. + if (i < 3) + color[i] = Math::instance.linearToGamma(color[i]); + lua_pushnumber(L, color[i] * 255); + } + + return numcomponents; +} + int w_noise(lua_State *L) { float w, x, y, z; @@ -277,14 +352,18 @@ int w_noise(lua_State *L) // List of functions to wrap. static const luaL_Reg functions[] = { - { "setRandomSeed", w_setRandomSeed }, - { "getRandomSeed", w_getRandomSeed }, { "random", w_random }, { "randomNormal", w_randomNormal }, + { "setRandomSeed", w_setRandomSeed }, + { "getRandomSeed", w_getRandomSeed }, + { "setRandomState", w_setRandomState }, + { "getRandomState", w_getRandomState }, { "newRandomGenerator", w_newRandomGenerator }, { "newBezierCurve", w_newBezierCurve }, { "triangulate", w_triangulate }, { "isConvex", w_isConvex }, + { "gammaToLinear", w_gammaToLinear }, + { "linearToGamma", w_linearToGamma }, { "noise", w_noise }, { 0, 0 } }; diff --git a/jni/love/src/modules/math/wrap_Math.h b/jni/love/src/modules/math/wrap_Math.h index 8693535d..d75afe4a 100644 --- a/jni/love/src/modules/math/wrap_Math.h +++ b/jni/love/src/modules/math/wrap_Math.h @@ -30,14 +30,18 @@ namespace love namespace math { -int w_setRandomSeed(lua_State *L); -int w_getRandomSeed(lua_State *L); int w_random(lua_State *L); int w_randomNormal(lua_State *L); +int w_setRandomSeed(lua_State *L); +int w_getRandomSeed(lua_State *L); +int w_setRandomState(lua_State *L); +int w_getRandomState(lua_State *L); int w_newRandomGenerator(lua_State *L); int w_newBezierCurve(lua_State *L); int w_triangulate(lua_State *L); int w_isConvex(lua_State *L); +int w_gammaToLinear(lua_State *L); +int w_linearToGamma(lua_State *L); int w_noise(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_math(lua_State *L); diff --git a/jni/love/src/modules/math/wrap_RandomGenerator.cpp b/jni/love/src/modules/math/wrap_RandomGenerator.cpp index 78cf53a0..ea029df2 100644 --- a/jni/love/src/modules/math/wrap_RandomGenerator.cpp +++ b/jni/love/src/modules/math/wrap_RandomGenerator.cpp @@ -47,16 +47,8 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx) if (!lua_isnoneornil(L, idx + 1)) { - uint32 low = checkrandomseed_part(L, idx); - uint32 high = checkrandomseed_part(L, idx + 1); - -#ifdef LOVE_BIG_ENDIAN - s.b32.a = high; - s.b32.b = low; -#else - s.b32.b = high; - s.b32.a = low; -#endif + s.b32.low = checkrandomseed_part(L, idx); + s.b32.high = checkrandomseed_part(L, idx + 1); } else s.b64 = checkrandomseed_part(L, idx); @@ -95,25 +87,6 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx) return luax_checktype(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T); } -int w_RandomGenerator_setSeed(lua_State *L) -{ - RandomGenerator *rng = luax_checkrandomgenerator(L, 1); - EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));) - return 0; -} - -int w_RandomGenerator_getSeed(lua_State *L) -{ - RandomGenerator *rng = luax_checkrandomgenerator(L, 1); - - uint32 low = 0, high = 0; - rng->getSeed(low, high); - - lua_pushnumber(L, (lua_Number) low); - lua_pushnumber(L, (lua_Number) high); - return 2; -} - int w_RandomGenerator_random(lua_State *L) { RandomGenerator *rng = luax_checkrandomgenerator(L, 1); @@ -132,12 +105,44 @@ int w_RandomGenerator_randomNormal(lua_State *L) return 1; } +int w_RandomGenerator_setSeed(lua_State *L) +{ + RandomGenerator *rng = luax_checkrandomgenerator(L, 1); + EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));) + return 0; +} + +int w_RandomGenerator_getSeed(lua_State *L) +{ + RandomGenerator *rng = luax_checkrandomgenerator(L, 1); + RandomGenerator::Seed s = rng->getSeed(); + lua_pushnumber(L, (lua_Number) s.b32.low); + lua_pushnumber(L, (lua_Number) s.b32.high); + return 2; +} + +int w_RandomGenerator_setState(lua_State *L) +{ + RandomGenerator *rng = luax_checkrandomgenerator(L, 1); + EXCEPT_GUARD(rng->setState(luax_checkstring(L, 2));) + return 0; +} + +int w_RandomGenerator_getState(lua_State *L) +{ + RandomGenerator *rng = luax_checkrandomgenerator(L, 1); + luax_pushstring(L, rng->getState()); + return 1; +} + static const luaL_Reg functions[] = { - { "setSeed", w_RandomGenerator_setSeed }, - { "getSeed", w_RandomGenerator_getSeed }, { "random", w_RandomGenerator_random }, { "randomNormal", w_RandomGenerator_randomNormal }, + { "setSeed", w_RandomGenerator_setSeed }, + { "getSeed", w_RandomGenerator_getSeed }, + { "setState", w_RandomGenerator_setState }, + { "getState", w_RandomGenerator_getState }, { 0, 0 } }; diff --git a/jni/love/src/modules/math/wrap_RandomGenerator.h b/jni/love/src/modules/math/wrap_RandomGenerator.h index 07e82894..ad5201d1 100644 --- a/jni/love/src/modules/math/wrap_RandomGenerator.h +++ b/jni/love/src/modules/math/wrap_RandomGenerator.h @@ -36,10 +36,12 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx); int luax_getrandom(lua_State *L, int startidx, double r); RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx); -int w_RandomGenerator_setSeed(lua_State *L); -int w_RandomGenerator_getSeed(lua_State *L); int w_RandomGenerator_random(lua_State *L); int w_RandomGenerator_randomNormal(lua_State *L); +int w_RandomGenerator_setSeed(lua_State *L); +int w_RandomGenerator_getSeed(lua_State *L); +int w_RandomGenerator_setState(lua_State *L); +int w_RandomGenerator_getState(lua_State *L); extern "C" int luaopen_randomgenerator(lua_State *L); } // math diff --git a/jni/love/src/modules/window/Window.cpp b/jni/love/src/modules/window/Window.cpp index 6243830a..1e3216c8 100644 --- a/jni/love/src/modules/window/Window.cpp +++ b/jni/love/src/modules/window/Window.cpp @@ -50,6 +50,7 @@ WindowSettings::WindowSettings() , centered(true) , display(0) , highdpi(false) + , sRGB(false) { } @@ -86,6 +87,7 @@ StringMap::Entry Window::settingEntri {"centered", SETTING_CENTERED}, {"display", SETTING_DISPLAY}, {"highdpi", SETTING_HIGHDPI}, + {"srgb", SETTING_SRGB}, }; StringMap Window::settings(Window::settingEntries, sizeof(Window::settingEntries)); diff --git a/jni/love/src/modules/window/Window.h b/jni/love/src/modules/window/Window.h index ad247fd0..b52900c6 100644 --- a/jni/love/src/modules/window/Window.h +++ b/jni/love/src/modules/window/Window.h @@ -57,6 +57,7 @@ public: SETTING_CENTERED, SETTING_DISPLAY, SETTING_HIGHDPI, + SETTING_SRGB, SETTING_MAX_ENUM }; @@ -157,6 +158,7 @@ struct WindowSettings bool centered; // = true int display; // = 0 bool highdpi; // false + bool sRGB; // false }; // WindowSettings diff --git a/jni/love/src/modules/window/sdl/Window.cpp b/jni/love/src/modules/window/sdl/Window.cpp index 8e2a642e..c6ca2ada 100644 --- a/jni/love/src/modules/window/sdl/Window.cpp +++ b/jni/love/src/modules/window/sdl/Window.cpp @@ -89,7 +89,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) f.minwidth = std::max(f.minwidth, 1); f.minheight = std::max(f.minheight, 1); - f.display = std::min(std::max(f.display, 0), getDisplayCount()); + f.display = std::min(std::max(f.display, 0), getDisplayCount() - 1); // Use the desktop resolution if a width or height of 0 is specified. if (width == 0 || height == 0) @@ -161,7 +161,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) if (!window) { // In Windows and Linux, some GL attributes are set on window creation. - setWindowGLAttributes(f.fsaa); + setWindowGLAttributes(f.fsaa, f.sRGB); const char *title = windowTitle.c_str(); int pos = f.centered ? centeredpos : uncenteredpos; @@ -197,7 +197,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) SDL_RaiseWindow(window); - if (!setContext(f.fsaa, f.vsync)) + if (!setContext(f.fsaa, f.vsync, f.sRGB)) { created = false; return false; @@ -216,7 +216,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) SDL_GL_GetDrawableSize(window, &width, &height); #endif - if (!gfx->setMode(width, height)) + if (!gfx->setMode(width, height, curMode.settings.sRGB)) displayError("Could not set graphics mode", "Unsupported OpenGL version?"); } @@ -237,7 +237,7 @@ bool Window::onWindowResize(int width, int height) return true; } -bool Window::setContext(int fsaa, bool vsync) +bool Window::setContext(int fsaa, bool vsync, bool sRGB) { // We would normally only need to recreate the context if FSAA changes or // SDL_GL_MakeCurrent is unsuccessful, but in Windows MakeCurrent can @@ -249,7 +249,7 @@ bool Window::setContext(int fsaa, bool vsync) } // Make sure the proper attributes are set. - setWindowGLAttributes(fsaa); + setWindowGLAttributes(fsaa, sRGB); context = SDL_GL_CreateContext(window); @@ -301,7 +301,7 @@ bool Window::setContext(int fsaa, bool vsync) return true; } -void Window::setWindowGLAttributes(int fsaa) const +void Window::setWindowGLAttributes(int fsaa, bool /* sRGB */) const { // Set GL window attributes. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); @@ -315,6 +315,15 @@ void Window::setWindowGLAttributes(int fsaa) const SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, (fsaa > 0) ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (fsaa > 0) ? fsaa : 0); + /* FIXME: Enable this code but make sure to try to re-create the window and + * context with this disabled, if creation fails with it enabled. + * We can leave this out for now because in practice the framebuffer will + * already be sRGB-capable (on desktops at least.) + #if SDL_VERSION_ATLEAST(2,0,1) + SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, sRGB ? 1 : 0); + #endif + */ + int contextprofile = 0; bool tryES2 = false; @@ -410,6 +419,8 @@ void Window::updateSettings(const WindowSettings &newsettings) else #endif SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); + + curMode.settings.sRGB = newsettings.sRGB; } void Window::displayError(const std::string &title, const std::string &text) const diff --git a/jni/love/src/modules/window/sdl/Window.h b/jni/love/src/modules/window/sdl/Window.h index 9efe3812..85144e99 100644 --- a/jni/love/src/modules/window/sdl/Window.h +++ b/jni/love/src/modules/window/sdl/Window.h @@ -90,8 +90,8 @@ public: private: - bool setContext(int fsaa, bool vsync); - void setWindowGLAttributes(int fsaa) const; + bool setContext(int fsaa, bool vsync, bool sRGB); + void setWindowGLAttributes(int fsaa, bool sRGB) const; // Update the saved window settings based on the window's actual state. void updateSettings(const WindowSettings &newsettings); diff --git a/jni/love/src/modules/window/wrap_Window.cpp b/jni/love/src/modules/window/wrap_Window.cpp index 39587511..42c14fa7 100644 --- a/jni/love/src/modules/window/wrap_Window.cpp +++ b/jni/love/src/modules/window/wrap_Window.cpp @@ -96,6 +96,7 @@ int w_setMode(lua_State *L) settings.centered = luax_boolflag(L, 3, settingName(Window::SETTING_CENTERED), true); settings.display = luax_intflag(L, 3, settingName(Window::SETTING_DISPLAY), 1); settings.highdpi = luax_boolflag(L, 3, settingName(Window::SETTING_HIGHDPI), false); + settings.sRGB = luax_boolflag(L, 3, settingName(Window::SETTING_SRGB), false); // Display index is 1-based in Lua and 0-based internally. settings.display--; @@ -151,6 +152,9 @@ int w_getMode(lua_State *L) luax_pushboolean(L, settings.highdpi); lua_setfield(L, -2, settingName(Window::SETTING_HIGHDPI)); + luax_pushboolean(L, settings.sRGB); + lua_setfield(L, -2, settingName(Window::SETTING_SRGB)); + return 3; } diff --git a/jni/love/src/scripts/boot.lua b/jni/love/src/scripts/boot.lua index ea1e6950..e223ca0b 100644 --- a/jni/love/src/scripts/boot.lua +++ b/jni/love/src/scripts/boot.lua @@ -313,6 +313,7 @@ function love.init() resizable = false, centered = true, highdpi = false, + srgb = false, }, modules = { event = true, @@ -404,6 +405,7 @@ function love.init() centered = c.window.centered, display = c.window.display, highdpi = c.window.highdpi, + srgb = c.window.srgb, }), "Could not set window mode") love.window.setTitle(c.window.title or c.title) if c.window.icon then diff --git a/jni/love/src/scripts/boot.lua.h b/jni/love/src/scripts/boot.lua.h index 0df00dee..abd2bfd6 100644 --- a/jni/love/src/scripts/boot.lua.h +++ b/jni/love/src/scripts/boot.lua.h @@ -570,6 +570,7 @@ const unsigned char boot_lua[] = 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, + 0x09, 0x09, 0x09, 0x73, 0x72, 0x67, 0x62, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x2c, 0x0a, 0x09, 0x09, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, 0x0a, @@ -705,6 +706,8 @@ const unsigned char boot_lua[] = 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x68, 0x69, 0x67, 0x68, 0x64, 0x70, 0x69, 0x2c, 0x0a, + 0x09, 0x09, 0x09, 0x73, 0x72, 0x67, 0x62, 0x20, 0x3d, 0x20, 0x63, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2c, 0x0a, 0x09, 0x09, 0x7d, 0x29, 0x2c, 0x20, 0x22, 0x43, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x73, 0x65, 0x74, 0x54,