mirror of
https://github.com/love2d/love.git
synced 2026-08-14 01:20:56 +02:00
OpenGL 2.1 is now required to use love.graphics (resolves issue #779.)
Removed love.graphics.isSupported("canvas", "shader", "npot", "subtractive", and "mipmap"), since those features are all guaranteed by the minimum system requirements.
--HG--
branch : minor
This commit is contained in:
@@ -158,13 +158,8 @@ StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
|
||||
{
|
||||
{ "canvas", Graphics::SUPPORT_CANVAS },
|
||||
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
|
||||
{ "multicanvas", Graphics::SUPPORT_MULTI_CANVAS },
|
||||
{ "shader", Graphics::SUPPORT_SHADER },
|
||||
{ "npot", Graphics::SUPPORT_NPOT },
|
||||
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
|
||||
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
||||
{ "dxt", Graphics::SUPPORT_DXT },
|
||||
{ "bc5", Graphics::SUPPORT_BC5 },
|
||||
{ "instancing", Graphics::SUPPORT_INSTANCING },
|
||||
|
||||
@@ -78,13 +78,8 @@ public:
|
||||
|
||||
enum Support
|
||||
{
|
||||
SUPPORT_CANVAS = 1,
|
||||
SUPPORT_HDR_CANVAS,
|
||||
SUPPORT_HDR_CANVAS = 1,
|
||||
SUPPORT_MULTI_CANVAS,
|
||||
SUPPORT_SHADER,
|
||||
SUPPORT_NPOT,
|
||||
SUPPORT_SUBTRACTIVE,
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_DXT,
|
||||
SUPPORT_BC5,
|
||||
SUPPORT_INSTANCING,
|
||||
|
||||
@@ -213,10 +213,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
}
|
||||
|
||||
// set up multiple render targets
|
||||
if (GLEE_VERSION_2_0)
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
else if (GLEE_ARB_draw_buffers)
|
||||
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -342,10 +339,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
}
|
||||
|
||||
// set up multiple render targets
|
||||
if (GLEE_VERSION_2_0)
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
else if (GLEE_ARB_draw_buffers)
|
||||
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ Graphics::Graphics()
|
||||
, created(false)
|
||||
, activeStencil(false)
|
||||
, savedState()
|
||||
, displayedMinReqWarning(false)
|
||||
{
|
||||
currentWindow = love::window::sdl::Window::createSingleton();
|
||||
|
||||
@@ -161,16 +162,38 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
// Okay, setup OpenGL.
|
||||
gl.initContext();
|
||||
|
||||
// Does the system meet LOVE's minimum requirements for graphics?
|
||||
if (!(GLEE_VERSION_2_0 && Shader::isSupported() && Canvas::isSupported())
|
||||
&& !displayedMinReqWarning)
|
||||
{
|
||||
love::window::MessageBoxType type = love::window::MESSAGEBOX_ERROR;
|
||||
|
||||
const char *title = "Minimum system requirements not met!";
|
||||
|
||||
std::string message;
|
||||
message += "Detected OpenGL version: ";
|
||||
message += (const char *) glGetString(GL_VERSION);
|
||||
message += "\nRequired OpenGL version: 2.1."; // -ish
|
||||
message += "\nThe program may crash or have graphical issues.";
|
||||
|
||||
::printf("%s\n%s\n", title, message.c_str());
|
||||
currentWindow->showMessageBox(type, title, message.c_str());
|
||||
|
||||
// We should only show the message once, instead of after every setMode.
|
||||
displayedMinReqWarning = true;
|
||||
}
|
||||
|
||||
// Okay, setup OpenGL.
|
||||
gl.setupContext();
|
||||
|
||||
created = true;
|
||||
|
||||
setViewportSize(width, height);
|
||||
|
||||
// Make sure antialiasing works when set elsewhere
|
||||
if (GLEE_VERSION_1_3 || GLEE_ARB_multisample)
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
// Enable blending
|
||||
glEnable(GL_BLEND);
|
||||
@@ -184,8 +207,7 @@ bool Graphics::setMode(int width, int height, bool &sRGB)
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
|
||||
// Auto-generated mipmaps should be the best quality possible
|
||||
if (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap)
|
||||
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
||||
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
||||
|
||||
// Enable textures
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
@@ -609,21 +631,9 @@ void Graphics::setBlendMode(Graphics::BlendMode mode)
|
||||
switch (mode)
|
||||
{
|
||||
case BLEND_ALPHA:
|
||||
if (GLEE_VERSION_1_4 || GLEE_EXT_blend_func_separate)
|
||||
{
|
||||
state.srcRGB = GL_SRC_ALPHA;
|
||||
state.srcA = GL_ONE;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback for OpenGL implementations without support for separate blend functions.
|
||||
// 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).
|
||||
state.srcRGB = state.srcA = GL_SRC_ALPHA;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
state.srcRGB = GL_SRC_ALPHA;
|
||||
state.srcA = GL_ONE;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
case BLEND_MULTIPLICATIVE:
|
||||
state.srcRGB = state.srcA = GL_DST_COLOR;
|
||||
@@ -986,8 +996,10 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
|
||||
{
|
||||
delete[] pixels;
|
||||
delete[] screenshot;
|
||||
|
||||
if (curcanvas)
|
||||
curcanvas->startGrab(curcanvas->getAttachedCanvases());
|
||||
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
@@ -1019,8 +1031,10 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool
|
||||
catch (love::Exception &)
|
||||
{
|
||||
delete[] screenshot;
|
||||
|
||||
if (curcanvas)
|
||||
curcanvas->startGrab(curcanvas->getAttachedCanvases());
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
@@ -473,6 +473,8 @@ private:
|
||||
|
||||
DisplayState savedState;
|
||||
|
||||
bool displayedMinReqWarning;
|
||||
|
||||
}; // Graphics
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -39,8 +39,6 @@ float Image::defaultMipmapSharpness = 0.0f;
|
||||
Image::Image(love::image::ImageData *data, Texture::Format format)
|
||||
: data(data)
|
||||
, cdata(nullptr)
|
||||
, paddedWidth(width)
|
||||
, paddedHeight(height)
|
||||
, texture(0)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, mipmapsCreated(false)
|
||||
@@ -58,8 +56,6 @@ Image::Image(love::image::ImageData *data, Texture::Format format)
|
||||
Image::Image(love::image::CompressedData *cdata, Texture::Format format)
|
||||
: data(nullptr)
|
||||
, cdata(cdata)
|
||||
, paddedWidth(width)
|
||||
, paddedHeight(height)
|
||||
, texture(0)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, mipmapsCreated(false)
|
||||
@@ -112,25 +108,10 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy,
|
||||
void Image::predraw()
|
||||
{
|
||||
bind();
|
||||
|
||||
if (width != paddedWidth || height != paddedHeight)
|
||||
{
|
||||
// NPOT image padded to POT size, so the texcoords should be scaled.
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glPushMatrix();
|
||||
glScalef(float(width) / float(paddedWidth), float(height) / float(paddedHeight), 0.0f);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::postdraw()
|
||||
{
|
||||
if (width != paddedWidth || height != paddedHeight)
|
||||
{
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint Image::getGLTexture() const
|
||||
@@ -148,27 +129,18 @@ void Image::uploadCompressedMipmaps()
|
||||
int count = cdata->getMipmapCount();
|
||||
|
||||
// We have to inform OpenGL if the image doesn't have all mipmap levels.
|
||||
if (GLEE_VERSION_1_2 || GLEE_SGIS_texture_lod)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
|
||||
}
|
||||
else if (cdata->getWidth(count-1) > 1 || cdata->getHeight(count-1) > 1)
|
||||
{
|
||||
// Telling OpenGL to ignore certain levels isn't always supported.
|
||||
throw love::Exception("Cannot load mipmaps: "
|
||||
"compressed image does not have all required levels.");
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
|
||||
|
||||
for (int i = 1; i < count; i++)
|
||||
{
|
||||
glCompressedTexImage2DARB(GL_TEXTURE_2D,
|
||||
i,
|
||||
getCompressedFormat(cdata->getFormat()),
|
||||
cdata->getWidth(i),
|
||||
cdata->getHeight(i),
|
||||
0,
|
||||
GLsizei(cdata->getSize(i)),
|
||||
cdata->getData(i));
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D,
|
||||
i,
|
||||
getCompressedFormat(cdata->getFormat()),
|
||||
cdata->getWidth(i),
|
||||
cdata->getHeight(i),
|
||||
0,
|
||||
GLsizei(cdata->getSize(i)),
|
||||
cdata->getData(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +150,6 @@ void Image::createMipmaps()
|
||||
if (!data || isCompressed())
|
||||
return;
|
||||
|
||||
if (!hasMipmapSupport())
|
||||
throw love::Exception("Mipmap filtering is not supported on this system.");
|
||||
|
||||
// Some old drivers claim support for NPOT textures, but fail when creating
|
||||
// mipmaps. We can't detect which systems will do this, so we fail gracefully
|
||||
// for all NPOT images.
|
||||
@@ -196,7 +165,7 @@ void Image::createMipmaps()
|
||||
// Prevent other threads from changing the ImageData while we upload it.
|
||||
love::thread::Lock lock(data->getMutex());
|
||||
|
||||
if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
|
||||
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
|
||||
{
|
||||
if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
|
||||
{
|
||||
@@ -266,18 +235,13 @@ void Image::setWrap(const Texture::Wrap &w)
|
||||
|
||||
void Image::setMipmapSharpness(float sharpness)
|
||||
{
|
||||
if (hasMipmapSharpnessSupport())
|
||||
{
|
||||
// LOD bias has the range (-maxbias, maxbias)
|
||||
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
|
||||
// LOD bias has the range (-maxbias, maxbias)
|
||||
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
|
||||
|
||||
bind();
|
||||
bind();
|
||||
|
||||
// negative bias is sharper
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||
}
|
||||
else
|
||||
mipmapSharpness = 0.0f;
|
||||
// negative bias is sharper
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
|
||||
}
|
||||
|
||||
float Image::getMipmapSharpness() const
|
||||
@@ -345,28 +309,18 @@ bool Image::loadVolatile()
|
||||
throw love::Exception("cannot create image: format is not supported on this system.");
|
||||
}
|
||||
|
||||
if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
|
||||
if (maxMipmapSharpness == 0.0f)
|
||||
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
|
||||
|
||||
glGenTextures(1, &texture);
|
||||
gl.bindTexture(texture);
|
||||
|
||||
filter.anisotropy = gl.setTextureFilter(filter);
|
||||
gl.setTextureFilter(filter);
|
||||
gl.setTextureWrap(wrap);
|
||||
setMipmapSharpness(mipmapSharpness);
|
||||
|
||||
paddedWidth = width;
|
||||
paddedHeight = height;
|
||||
|
||||
if (!hasNpot())
|
||||
{
|
||||
// NPOT textures will be padded to POT dimensions if necessary.
|
||||
paddedWidth = next_p2(width);
|
||||
paddedHeight = next_p2(height);
|
||||
}
|
||||
|
||||
// Use a default texture if the size is too big for the system.
|
||||
if (paddedWidth > gl.getMaxTextureSize() || paddedHeight > gl.getMaxTextureSize())
|
||||
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
|
||||
{
|
||||
uploadDefaultTexture();
|
||||
return true;
|
||||
@@ -379,10 +333,7 @@ bool Image::loadVolatile()
|
||||
|
||||
while (glGetError() != GL_NO_ERROR); // Clear errors.
|
||||
|
||||
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
|
||||
uploadTexture();
|
||||
else
|
||||
uploadTexturePadded();
|
||||
uploadTexture();
|
||||
|
||||
GLenum glerr = glGetError();
|
||||
if (glerr != GL_NO_ERROR)
|
||||
@@ -395,51 +346,19 @@ bool Image::loadVolatile()
|
||||
return true;
|
||||
}
|
||||
|
||||
void Image::uploadTexturePadded()
|
||||
{
|
||||
if (isCompressed() && cdata)
|
||||
{
|
||||
// Padded textures don't really work if they're compressed...
|
||||
throw love::Exception("Cannot create image: "
|
||||
"compressed NPOT images are not supported on this system.");
|
||||
}
|
||||
else if (data)
|
||||
{
|
||||
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
iformat,
|
||||
(GLsizei)paddedWidth,
|
||||
(GLsizei)paddedHeight,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
0);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
0, 0,
|
||||
(GLsizei)width,
|
||||
(GLsizei)height,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
data->getData());
|
||||
}
|
||||
}
|
||||
|
||||
void Image::uploadTexture()
|
||||
{
|
||||
if (isCompressed() && cdata)
|
||||
{
|
||||
GLenum format = getCompressedFormat(cdata->getFormat());
|
||||
glCompressedTexImage2DARB(GL_TEXTURE_2D,
|
||||
0,
|
||||
format,
|
||||
cdata->getWidth(0),
|
||||
cdata->getHeight(0),
|
||||
0,
|
||||
GLsizei(cdata->getSize(0)),
|
||||
cdata->getData(0));
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
format,
|
||||
cdata->getWidth(0),
|
||||
cdata->getHeight(0),
|
||||
0,
|
||||
GLsizei(cdata->getSize(0)),
|
||||
cdata->getData(0));
|
||||
}
|
||||
else if (data)
|
||||
{
|
||||
@@ -488,10 +407,7 @@ bool Image::refresh()
|
||||
|
||||
while (glGetError() != GL_NO_ERROR); // Clear errors.
|
||||
|
||||
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
|
||||
uploadTexture();
|
||||
else
|
||||
uploadTexturePadded();
|
||||
uploadTexture();
|
||||
|
||||
if (glGetError() != GL_NO_ERROR)
|
||||
uploadDefaultTexture();
|
||||
@@ -610,36 +526,13 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
|
||||
}
|
||||
}
|
||||
|
||||
bool Image::hasNpot()
|
||||
{
|
||||
return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two;
|
||||
}
|
||||
|
||||
bool Image::hasAnisotropicFilteringSupport()
|
||||
{
|
||||
return GLEE_EXT_texture_filter_anisotropic;
|
||||
}
|
||||
|
||||
bool Image::hasMipmapSupport()
|
||||
{
|
||||
return GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap;
|
||||
}
|
||||
|
||||
bool Image::hasMipmapSharpnessSupport()
|
||||
{
|
||||
return GLEE_VERSION_1_4;
|
||||
}
|
||||
|
||||
bool Image::hasCompressedTextureSupport()
|
||||
{
|
||||
return GLEE_VERSION_1_3 || GLEE_ARB_texture_compression;
|
||||
}
|
||||
|
||||
bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
|
||||
{
|
||||
if (!hasCompressedTextureSupport())
|
||||
return false;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case image::CompressedData::FORMAT_DXT1:
|
||||
|
||||
@@ -127,14 +127,8 @@ public:
|
||||
static void setDefaultMipmapFilter(FilterMode f);
|
||||
static FilterMode getDefaultMipmapFilter();
|
||||
|
||||
static bool hasNpot();
|
||||
static bool hasAnisotropicFilteringSupport();
|
||||
static bool hasMipmapSupport();
|
||||
static bool hasMipmapSharpnessSupport();
|
||||
|
||||
static bool hasCompressedTextureSupport();
|
||||
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
|
||||
|
||||
static bool hasSRGBSupport();
|
||||
|
||||
private:
|
||||
@@ -151,9 +145,6 @@ private:
|
||||
// null if raw ImageData was used to create the texture.
|
||||
love::image::CompressedData *cdata;
|
||||
|
||||
// Real dimensions of the texture, if it was auto-padded to POT size.
|
||||
int paddedWidth, paddedHeight;
|
||||
|
||||
// OpenGL texture identifier.
|
||||
GLuint texture;
|
||||
|
||||
@@ -175,7 +166,6 @@ private:
|
||||
|
||||
void preload();
|
||||
|
||||
void uploadTexturePadded();
|
||||
void uploadTexture();
|
||||
|
||||
void uploadCompressedMipmaps();
|
||||
|
||||
@@ -57,6 +57,14 @@ void OpenGL::initContext()
|
||||
initOpenGLFunctions();
|
||||
initVendor();
|
||||
|
||||
contextInitialized = true;
|
||||
}
|
||||
|
||||
void OpenGL::setupContext()
|
||||
{
|
||||
if (!contextInitialized)
|
||||
return;
|
||||
|
||||
// Store the current color so we don't have to get it through GL later.
|
||||
GLfloat glcolor[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, glcolor);
|
||||
@@ -80,37 +88,28 @@ void OpenGL::initContext()
|
||||
glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
|
||||
state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
|
||||
|
||||
// Initialize multiple texture unit support for shaders, if available.
|
||||
// Initialize multiple texture unit support for shaders.
|
||||
state.textureUnits.clear();
|
||||
if (Shader::isSupported())
|
||||
|
||||
GLint maxtextureunits;
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
|
||||
|
||||
state.textureUnits.resize(maxtextureunits, 0);
|
||||
|
||||
GLenum curgltextureunit;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
|
||||
|
||||
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
|
||||
|
||||
// Retrieve currently bound textures for each texture unit.
|
||||
for (size_t i = 0; i < state.textureUnits.size(); i++)
|
||||
{
|
||||
GLint maxtextureunits;
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
|
||||
|
||||
state.textureUnits.resize(maxtextureunits, 0);
|
||||
|
||||
GLenum curgltextureunit;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
|
||||
|
||||
state.curTextureUnit = (int) curgltextureunit - GL_TEXTURE0;
|
||||
|
||||
// Retrieve currently bound textures for each texture unit.
|
||||
for (size_t i = 0; i < state.textureUnits.size(); i++)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
|
||||
}
|
||||
|
||||
glActiveTexture(curgltextureunit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multitexturing not supported, so we only have 1 texture unit.
|
||||
state.textureUnits.resize(1, 0);
|
||||
state.curTextureUnit = 0;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
|
||||
}
|
||||
|
||||
glActiveTexture(curgltextureunit);
|
||||
|
||||
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
|
||||
setBlendState(blend);
|
||||
|
||||
@@ -118,8 +117,6 @@ void OpenGL::initContext()
|
||||
createDefaultTexture();
|
||||
|
||||
state.lastPseudoInstanceID = -1;
|
||||
|
||||
contextInitialized = true;
|
||||
}
|
||||
|
||||
void OpenGL::deInitContext()
|
||||
@@ -158,31 +155,6 @@ void OpenGL::initVendor()
|
||||
|
||||
void OpenGL::initOpenGLFunctions()
|
||||
{
|
||||
// The functionality of the core and ARB VBOs are identical, so we can
|
||||
// assign the pointers of the core functions to the names of the ARB
|
||||
// functions, if the latter isn't supported but the former is.
|
||||
if (GLEE_VERSION_1_5 && !GLEE_ARB_vertex_buffer_object)
|
||||
{
|
||||
glBindBufferARB = (GLEEPFNGLBINDBUFFERARBPROC) glBindBuffer;
|
||||
glBufferDataARB = (GLEEPFNGLBUFFERDATAARBPROC) glBufferData;
|
||||
glBufferSubDataARB = (GLEEPFNGLBUFFERSUBDATAARBPROC) glBufferSubData;
|
||||
glDeleteBuffersARB = (GLEEPFNGLDELETEBUFFERSARBPROC) glDeleteBuffers;
|
||||
glGenBuffersARB = (GLEEPFNGLGENBUFFERSARBPROC) glGenBuffers;
|
||||
glGetBufferParameterivARB = (GLEEPFNGLGETBUFFERPARAMETERIVARBPROC) glGetBufferParameteriv;
|
||||
glGetBufferPointervARB = (GLEEPFNGLGETBUFFERPOINTERVARBPROC) glGetBufferPointerv;
|
||||
glGetBufferSubDataARB = (GLEEPFNGLGETBUFFERSUBDATAARBPROC) glGetBufferSubData;
|
||||
glIsBufferARB = (GLEEPFNGLISBUFFERARBPROC) glIsBuffer;
|
||||
glMapBufferARB = (GLEEPFNGLMAPBUFFERARBPROC) glMapBuffer;
|
||||
glUnmapBufferARB = (GLEEPFNGLUNMAPBUFFERARBPROC) glUnmapBuffer;
|
||||
}
|
||||
|
||||
// Same deal for compressed textures.
|
||||
if (GLEE_VERSION_1_3 && !GLEE_ARB_texture_compression)
|
||||
{
|
||||
glCompressedTexImage2DARB = (GLEEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC) glCompressedTexImage2D;
|
||||
glCompressedTexSubImage2DARB = (GLEEPFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) glCompressedTexSubImage2D;
|
||||
glGetCompressedTexImageARB = (GLEEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC) glGetCompressedTexImage;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::initMaxValues()
|
||||
@@ -195,18 +167,13 @@ void OpenGL::initMaxValues()
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
|
||||
if (Canvas::isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers))
|
||||
{
|
||||
int maxattachments = 0;
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
|
||||
int maxattachments = 0;
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
|
||||
|
||||
int maxdrawbuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
|
||||
int maxdrawbuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
|
||||
|
||||
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
|
||||
}
|
||||
else
|
||||
maxRenderTargets = 0;
|
||||
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
|
||||
}
|
||||
|
||||
void OpenGL::createDefaultTexture()
|
||||
@@ -369,28 +336,8 @@ OpenGL::Viewport OpenGL::getScissor() const
|
||||
|
||||
void OpenGL::setBlendState(const BlendState &blend)
|
||||
{
|
||||
if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
|
||||
glBlendEquation(blend.func);
|
||||
else if (GLEE_EXT_blend_minmax && GLEE_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 (GLEE_VERSION_1_4)
|
||||
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
else if (GLEE_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!");
|
||||
}
|
||||
glBlendEquation(blend.func);
|
||||
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
|
||||
state.blend = blend;
|
||||
}
|
||||
@@ -406,12 +353,7 @@ void OpenGL::setTextureUnit(int textureunit)
|
||||
throw love::Exception("Invalid texture unit index (%d).", textureunit);
|
||||
|
||||
if (textureunit != state.curTextureUnit)
|
||||
{
|
||||
if (state.textureUnits.size() > 1)
|
||||
glActiveTexture(GL_TEXTURE0 + textureunit);
|
||||
else
|
||||
throw love::Exception("Multitexturing is not supported.");
|
||||
}
|
||||
glActiveTexture(GL_TEXTURE0 + textureunit);
|
||||
|
||||
state.curTextureUnit = textureunit;
|
||||
}
|
||||
@@ -457,7 +399,7 @@ void OpenGL::deleteTexture(GLuint texture)
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
{
|
||||
GLint gmin, gmag;
|
||||
|
||||
@@ -502,8 +444,8 @@ float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
|
||||
}
|
||||
|
||||
return f.anisotropy;
|
||||
else
|
||||
f.anisotropy = 1.0f;
|
||||
}
|
||||
|
||||
graphics::Texture::Filter OpenGL::getTextureFilter()
|
||||
|
||||
@@ -104,11 +104,16 @@ public:
|
||||
OpenGL();
|
||||
|
||||
/**
|
||||
* Initializes some required context state based on current and default
|
||||
* OpenGL state. Call this directly after creating an OpenGL context!
|
||||
* Initializes the active OpenGL context.
|
||||
**/
|
||||
void initContext();
|
||||
|
||||
/**
|
||||
* Sets up some required context state based on current and default OpenGL
|
||||
* state. Call this directly after initializing an OpenGL context!
|
||||
**/
|
||||
void setupContext();
|
||||
|
||||
/**
|
||||
* Marks current context state as invalid and deletes OpenGL objects owned
|
||||
* by this class instance. Call this directly before potentially deleting
|
||||
@@ -214,9 +219,10 @@ public:
|
||||
|
||||
/**
|
||||
* Sets the texture filter mode for the currently bound texture.
|
||||
* Returns the actual amount of anisotropic filtering set.
|
||||
* The anisotropy parameter of the argument is set to the actual amount of
|
||||
* anisotropy that was used.
|
||||
**/
|
||||
float setTextureFilter(graphics::Texture::Filter &f);
|
||||
void setTextureFilter(graphics::Texture::Filter &f);
|
||||
|
||||
/**
|
||||
* Returns the texture filter mode for the currently bound texture.
|
||||
|
||||
@@ -734,11 +734,7 @@ const std::map<std::string, Object *> &Shader::getBoundRetainables() const
|
||||
|
||||
std::string Shader::getGLSLVersion()
|
||||
{
|
||||
const char *tmp = nullptr;
|
||||
|
||||
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
|
||||
if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
|
||||
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
|
||||
if (tmp == nullptr)
|
||||
return "0.0";
|
||||
@@ -755,7 +751,7 @@ std::string Shader::getGLSLVersion()
|
||||
|
||||
bool Shader::isSupported()
|
||||
{
|
||||
return GLEE_VERSION_2_0 && getGLSLVersion() >= "1.2";
|
||||
return getGLSLVersion() >= "1.2";
|
||||
}
|
||||
|
||||
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntries[] =
|
||||
|
||||
@@ -44,16 +44,7 @@ namespace opengl
|
||||
|
||||
VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to create a VBO.
|
||||
return new VBO(size, target, usage, backing);
|
||||
}
|
||||
catch(const love::Exception &)
|
||||
{
|
||||
// VBO not supported ... create regular array.
|
||||
return new VertexArray(size, target, usage, backing);
|
||||
}
|
||||
return new VertexBuffer(size, target, usage, backing);
|
||||
}
|
||||
|
||||
VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
||||
@@ -63,68 +54,10 @@ VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacki
|
||||
, target(target)
|
||||
, usage(usage)
|
||||
, backing(backing)
|
||||
{
|
||||
}
|
||||
|
||||
VertexBuffer::~VertexBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
// VertexArray
|
||||
|
||||
VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
||||
: VertexBuffer(size, target, usage, backing)
|
||||
, buf(new char[size])
|
||||
{
|
||||
}
|
||||
|
||||
VertexArray::~VertexArray()
|
||||
{
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
void *VertexArray::map()
|
||||
{
|
||||
is_mapped = true;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void VertexArray::unmap()
|
||||
{
|
||||
is_mapped = false;
|
||||
}
|
||||
|
||||
void VertexArray::bind()
|
||||
{
|
||||
is_bound = true;
|
||||
}
|
||||
|
||||
void VertexArray::unbind()
|
||||
{
|
||||
is_bound = false;
|
||||
}
|
||||
|
||||
void VertexArray::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
memcpy(buf + offset, data, size);
|
||||
}
|
||||
|
||||
const void *VertexArray::getPointer(size_t offset) const
|
||||
{
|
||||
return buf + offset;
|
||||
}
|
||||
|
||||
// VBO
|
||||
|
||||
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
||||
: VertexBuffer(size, target, usage, backing)
|
||||
, vbo(0)
|
||||
, memory_map(0)
|
||||
, is_dirty(true)
|
||||
{
|
||||
if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
|
||||
throw love::Exception("Not supported");
|
||||
|
||||
if (getMemoryBacking() == BACKING_FULL)
|
||||
memory_map = malloc(getSize());
|
||||
|
||||
@@ -137,7 +70,7 @@ VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
||||
}
|
||||
}
|
||||
|
||||
VBO::~VBO()
|
||||
VertexBuffer::~VertexBuffer()
|
||||
{
|
||||
if (vbo != 0)
|
||||
unload(false);
|
||||
@@ -146,7 +79,7 @@ VBO::~VBO()
|
||||
free(memory_map);
|
||||
}
|
||||
|
||||
void *VBO::map()
|
||||
void *VertexBuffer::map()
|
||||
{
|
||||
if (is_mapped)
|
||||
return memory_map;
|
||||
@@ -160,7 +93,7 @@ void *VBO::map()
|
||||
|
||||
if (is_dirty)
|
||||
{
|
||||
glGetBufferSubDataARB(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
||||
glGetBufferSubData(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
||||
is_dirty = false;
|
||||
}
|
||||
|
||||
@@ -169,7 +102,7 @@ void *VBO::map()
|
||||
return memory_map;
|
||||
}
|
||||
|
||||
void VBO::unmap()
|
||||
void VertexBuffer::unmap()
|
||||
{
|
||||
if (!is_mapped)
|
||||
return;
|
||||
@@ -178,36 +111,36 @@ void VBO::unmap()
|
||||
// bound here.
|
||||
if (!is_bound)
|
||||
{
|
||||
glBindBufferARB(getTarget(), vbo);
|
||||
glBindBuffer(getTarget(), vbo);
|
||||
is_bound = true;
|
||||
}
|
||||
|
||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
||||
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||
|
||||
is_mapped = false;
|
||||
}
|
||||
|
||||
void VBO::bind()
|
||||
void VertexBuffer::bind()
|
||||
{
|
||||
if (!is_mapped)
|
||||
{
|
||||
glBindBufferARB(getTarget(), vbo);
|
||||
glBindBuffer(getTarget(), vbo);
|
||||
is_bound = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VBO::unbind()
|
||||
void VertexBuffer::unbind()
|
||||
{
|
||||
if (is_bound)
|
||||
glBindBufferARB(getTarget(), 0);
|
||||
glBindBuffer(getTarget(), 0);
|
||||
|
||||
is_bound = false;
|
||||
}
|
||||
|
||||
void VBO::fill(size_t offset, size_t size, const void *data)
|
||||
void VertexBuffer::fill(size_t offset, size_t size, const void *data)
|
||||
{
|
||||
if (is_mapped || getMemoryBacking() == BACKING_FULL)
|
||||
memcpy(static_cast<char *>(memory_map) + offset, data, size);
|
||||
@@ -217,7 +150,7 @@ void VBO::fill(size_t offset, size_t size, const void *data)
|
||||
// Not all systems have access to some faster paths...
|
||||
if (GLEE_APPLE_flush_buffer_range)
|
||||
{
|
||||
void *mapdata = glMapBufferARB(getTarget(), GL_WRITE_ONLY);
|
||||
void *mapdata = glMapBuffer(getTarget(), GL_WRITE_ONLY);
|
||||
|
||||
if (mapdata)
|
||||
{
|
||||
@@ -228,12 +161,12 @@ void VBO::fill(size_t offset, size_t size, const void *data)
|
||||
glFlushMappedBufferRangeAPPLE(getTarget(), (GLintptr) offset, (GLsizei) size);
|
||||
}
|
||||
|
||||
glUnmapBufferARB(getTarget());
|
||||
glUnmapBuffer(getTarget());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to a possibly slower SubData (more chance of syncing.)
|
||||
glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
|
||||
}
|
||||
|
||||
if (getMemoryBacking() != BACKING_FULL)
|
||||
@@ -241,24 +174,24 @@ void VBO::fill(size_t offset, size_t size, const void *data)
|
||||
}
|
||||
}
|
||||
|
||||
const void *VBO::getPointer(size_t offset) const
|
||||
const void *VertexBuffer::getPointer(size_t offset) const
|
||||
{
|
||||
return BUFFER_OFFSET(offset);
|
||||
}
|
||||
|
||||
bool VBO::loadVolatile()
|
||||
bool VertexBuffer::loadVolatile()
|
||||
{
|
||||
return load(true);
|
||||
}
|
||||
|
||||
void VBO::unloadVolatile()
|
||||
void VertexBuffer::unloadVolatile()
|
||||
{
|
||||
unload(true);
|
||||
}
|
||||
|
||||
bool VBO::load(bool restore)
|
||||
bool VertexBuffer::load(bool restore)
|
||||
{
|
||||
glGenBuffersARB(1, &vbo);
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
VertexBuffer::Bind bind(*this);
|
||||
|
||||
@@ -275,13 +208,13 @@ bool VBO::load(bool restore)
|
||||
glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
|
||||
|
||||
// Note that if 'src' is '0', no data will be copied.
|
||||
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
|
||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
|
||||
GLenum err = glGetError();
|
||||
|
||||
return (GL_NO_ERROR == err);
|
||||
}
|
||||
|
||||
void VBO::unload(bool save)
|
||||
void VertexBuffer::unload(bool save)
|
||||
{
|
||||
// Save data before unloading, if we need to.
|
||||
if (save && getMemoryBacking() == BACKING_PARTIAL)
|
||||
@@ -294,7 +227,7 @@ void VBO::unload(bool save)
|
||||
is_mapped = mapped;
|
||||
}
|
||||
|
||||
glDeleteBuffersARB(1, &vbo);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,15 +36,12 @@ namespace opengl
|
||||
{
|
||||
|
||||
/**
|
||||
* VertexBuffer is an abstraction over VBOs (Vertex Buffer Objects), which
|
||||
* falls back to regular vertex arrays if VBOs are not supported.
|
||||
*
|
||||
* This allows code to take advantage of VBOs where available, but still
|
||||
* work on older systems where it's *not* available. Everyone's happy.
|
||||
* VertexBuffer is a thin abstraction over VBOs (Vertex Buffer Objects) and
|
||||
* other OpenGL Buffer Objects.
|
||||
*
|
||||
* The class is (for now) meant for internal use.
|
||||
*/
|
||||
class VertexBuffer
|
||||
class VertexBuffer : public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -61,11 +58,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new VertexBuffer (either a plain vertex array, or a VBO),
|
||||
* based on what's supported on the system.
|
||||
*
|
||||
* If VBOs are not supported, a plain vertex array will automatically
|
||||
* be created and returned instead.
|
||||
* Create a new VertexBuffer.
|
||||
*
|
||||
* @param size The size of the VertexBuffer (in bytes).
|
||||
* @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
|
||||
@@ -86,7 +79,7 @@ public:
|
||||
VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
|
||||
|
||||
/**
|
||||
* Destructor. Does nothing, but must be declared virtual.
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~VertexBuffer();
|
||||
|
||||
@@ -145,7 +138,7 @@ public:
|
||||
*
|
||||
* @return A pointer to memory which represents the buffer.
|
||||
*/
|
||||
virtual void *map() = 0;
|
||||
virtual void *map();
|
||||
|
||||
/**
|
||||
* Unmap a previously mapped VertexBuffer. The buffer must be unmapped
|
||||
@@ -153,18 +146,18 @@ public:
|
||||
*
|
||||
* The VertexBuffer must be bound to use this function.
|
||||
*/
|
||||
virtual void unmap() = 0;
|
||||
virtual void unmap();
|
||||
|
||||
/**
|
||||
* Bind the VertexBuffer to its specified target.
|
||||
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
|
||||
*/
|
||||
virtual void bind() = 0;
|
||||
virtual void bind();
|
||||
|
||||
/**
|
||||
* Unbind a prevously bound VertexBuffer.
|
||||
*/
|
||||
virtual void unbind() = 0;
|
||||
virtual void unbind();
|
||||
|
||||
/**
|
||||
* Fill a portion of the buffer with data.
|
||||
@@ -175,7 +168,7 @@ public:
|
||||
* @param size The size of the incoming data.
|
||||
* @param data Pointer to memory to copy data from.
|
||||
*/
|
||||
virtual void fill(size_t offset, size_t size, const void *data) = 0;
|
||||
virtual void fill(size_t offset, size_t size, const void *data);
|
||||
|
||||
/**
|
||||
* Get a pointer which represents the specified byte offset.
|
||||
@@ -183,7 +176,11 @@ public:
|
||||
* @param offset The byte offset. (0 is first byte).
|
||||
* @return A pointer which represents the offset.
|
||||
*/
|
||||
virtual const void *getPointer(size_t offset) const = 0;
|
||||
virtual const void *getPointer(size_t offset) const;
|
||||
|
||||
// Implements Volatile.
|
||||
virtual bool loadVolatile();
|
||||
virtual void unloadVolatile();
|
||||
|
||||
/**
|
||||
* This helper class can bind a VertexArray temporarily, and
|
||||
@@ -214,11 +211,13 @@ public:
|
||||
|
||||
// VertexBuffer to work on.
|
||||
VertexBuffer &buf;
|
||||
};
|
||||
|
||||
}; // Bind
|
||||
|
||||
class Mapper
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Memory-maps a VertexBuffer.
|
||||
*/
|
||||
@@ -245,98 +244,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
VertexBuffer &buf;
|
||||
void *elems;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// Whether the buffer is currently bound.
|
||||
bool is_bound;
|
||||
|
||||
// Whether the buffer is currently mapped to main memory.
|
||||
bool is_mapped;
|
||||
|
||||
private:
|
||||
|
||||
// The size of the buffer, in bytes.
|
||||
size_t size;
|
||||
|
||||
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
|
||||
GLenum target;
|
||||
|
||||
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
|
||||
GLenum usage;
|
||||
|
||||
//
|
||||
MemoryBacking backing;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of VertexBuffer which uses plain arrays to store the data.
|
||||
*
|
||||
* This implementation should be supported everywhere, and acts as a fallback
|
||||
* on systems which do not support VBOs.
|
||||
*/
|
||||
class VertexArray : public VertexBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
|
||||
*/
|
||||
VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
|
||||
|
||||
/**
|
||||
* Frees the data we've allocated.
|
||||
*/
|
||||
virtual ~VertexArray();
|
||||
|
||||
// Implements VertexBuffer.
|
||||
virtual void *map();
|
||||
virtual void unmap();
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
virtual void fill(size_t offset, size_t size, const void *data);
|
||||
virtual const void *getPointer(size_t offset) const ;
|
||||
|
||||
private:
|
||||
// Holds the data.
|
||||
char *buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* Vertex Buffer Object (VBO) implementation of VertexBuffer.
|
||||
*
|
||||
* This will be used on all systems that support it. It's in general
|
||||
* faster than vertex arrays, but especially in use-cases where there
|
||||
* is no need to update the data every frame.
|
||||
**/
|
||||
class VBO : public VertexBuffer, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
|
||||
**/
|
||||
VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
|
||||
|
||||
/**
|
||||
* Deletes the VBOs from OpenGL.
|
||||
**/
|
||||
virtual ~VBO();
|
||||
|
||||
// Implements VertexBuffer.
|
||||
virtual void *map();
|
||||
virtual void unmap();
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
virtual void fill(size_t offset, size_t size, const void *data);
|
||||
virtual const void *getPointer(size_t offset) const ;
|
||||
|
||||
// Implements Volatile.
|
||||
bool loadVolatile();
|
||||
void unloadVolatile();
|
||||
}; // Mapper
|
||||
|
||||
private:
|
||||
|
||||
@@ -355,6 +267,25 @@ private:
|
||||
*/
|
||||
void unload(bool save);
|
||||
|
||||
|
||||
// Whether the buffer is currently bound.
|
||||
bool is_bound;
|
||||
|
||||
// Whether the buffer is currently mapped to main memory.
|
||||
bool is_mapped;
|
||||
|
||||
// The size of the buffer, in bytes.
|
||||
size_t size;
|
||||
|
||||
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
|
||||
GLenum target;
|
||||
|
||||
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
|
||||
GLenum usage;
|
||||
|
||||
//
|
||||
MemoryBacking backing;
|
||||
|
||||
// The VBO identifier. Assigned by OpenGL.
|
||||
GLuint vbo;
|
||||
|
||||
@@ -365,7 +296,9 @@ private:
|
||||
// Set if the buffer was modified while operating on gpu memory
|
||||
// and needs to be synchronized.
|
||||
bool is_dirty;
|
||||
};
|
||||
|
||||
}; // VertexBuffer
|
||||
|
||||
|
||||
/**
|
||||
* VertexIndex manages one shared VertexBuffer that stores the indices for an
|
||||
|
||||
@@ -344,9 +344,6 @@ int w_newCanvas(lua_State *L)
|
||||
|
||||
int w_newShader(lua_State *L)
|
||||
{
|
||||
if (!Shader::isSupported())
|
||||
return luaL_error(L, "Sorry, your graphics card does not support shaders.");
|
||||
|
||||
// clamp stack to 2 elements
|
||||
lua_settop(L, 2);
|
||||
|
||||
@@ -946,10 +943,6 @@ int w_isSupported(lua_State *L)
|
||||
|
||||
switch (support)
|
||||
{
|
||||
case Graphics::SUPPORT_CANVAS:
|
||||
if (!Canvas::isSupported())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_HDR_CANVAS:
|
||||
if (!Canvas::isHDRSupported())
|
||||
supported = false;
|
||||
@@ -958,22 +951,6 @@ int w_isSupported(lua_State *L)
|
||||
if (!Canvas::isMultiCanvasSupported())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_SHADER:
|
||||
if (!Shader::isSupported())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_NPOT:
|
||||
if (!Image::hasNpot())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_SUBTRACTIVE:
|
||||
if (!((GLEE_VERSION_1_4 || GLEE_ARB_imaging) || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)))
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_MIPMAP:
|
||||
if (!Image::hasMipmapSupport())
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_DXT:
|
||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_DXT5))
|
||||
supported = false;
|
||||
|
||||
@@ -74,6 +74,16 @@ bool Window::getConstant(Window::Setting in, const char *&out)
|
||||
return settings.find(in, out);
|
||||
}
|
||||
|
||||
bool Window::getConstant(const char *in, MessageBoxType &out)
|
||||
{
|
||||
return messageBoxTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Window::getConstant(MessageBoxType in, const char *&out)
|
||||
{
|
||||
return messageBoxTypes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Window::Setting, Window::SETTING_MAX_ENUM>::Entry Window::settingEntries[] =
|
||||
{
|
||||
{"fullscreen", SETTING_FULLSCREEN},
|
||||
@@ -100,5 +110,14 @@ StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM>::Entry Windo
|
||||
|
||||
StringMap<Window::FullscreenType, Window::FULLSCREEN_TYPE_MAX_ENUM> Window::fullscreenTypes(Window::fullscreenTypeEntries, sizeof(Window::fullscreenTypeEntries));
|
||||
|
||||
StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM>::Entry Window::messageBoxTypeEntries[] =
|
||||
{
|
||||
{"error", MESSAGEBOX_ERROR},
|
||||
{"warning", MESSAGEBOX_WARNING},
|
||||
{"info", MESSAGEBOX_INFO},
|
||||
};
|
||||
|
||||
StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM> Window::messageBoxTypes(Window::messageBoxTypeEntries, sizeof(Window::messageBoxTypeEntries));
|
||||
|
||||
} // window
|
||||
} // love
|
||||
|
||||
@@ -39,6 +39,14 @@ namespace window
|
||||
// whole thing here because it uses the Window::Type enum.
|
||||
struct WindowSettings;
|
||||
|
||||
enum MessageBoxType
|
||||
{
|
||||
MESSAGEBOX_ERROR,
|
||||
MESSAGEBOX_WARNING,
|
||||
MESSAGEBOX_INFO,
|
||||
MESSAGEBOX_MAX_ENUM
|
||||
};
|
||||
|
||||
class Window : public Module
|
||||
{
|
||||
public:
|
||||
@@ -119,6 +127,8 @@ public:
|
||||
|
||||
virtual const void *getHandle() const = 0;
|
||||
|
||||
virtual void showMessageBox(MessageBoxType type, const char *title, const char *message) = 0;
|
||||
|
||||
//virtual static Window *createSingleton() = 0;
|
||||
//virtual static Window *getSingleton() = 0;
|
||||
// No virtual statics, of course, but you are supposed to implement these statics.
|
||||
@@ -129,6 +139,9 @@ public:
|
||||
static bool getConstant(const char *in, FullscreenType &out);
|
||||
static bool getConstant(FullscreenType in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, MessageBoxType &out);
|
||||
static bool getConstant(MessageBoxType in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
static Window *singleton;
|
||||
@@ -141,6 +154,9 @@ private:
|
||||
static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM>::Entry fullscreenTypeEntries[];
|
||||
static StringMap<FullscreenType, FULLSCREEN_TYPE_MAX_ENUM> fullscreenTypes;
|
||||
|
||||
static StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM>::Entry messageBoxTypeEntries[];
|
||||
static StringMap<MessageBoxType, MESSAGEBOX_MAX_ENUM> messageBoxTypes;
|
||||
|
||||
}; // Window
|
||||
|
||||
struct WindowSettings
|
||||
|
||||
@@ -654,6 +654,27 @@ const void *Window::getHandle() const
|
||||
return window;
|
||||
}
|
||||
|
||||
void Window::showMessageBox(MessageBoxType type, const char *title, const char *message)
|
||||
{
|
||||
SDL_MessageBoxFlags sdlflags;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MESSAGEBOX_ERROR:
|
||||
sdlflags = SDL_MESSAGEBOX_ERROR;
|
||||
break;
|
||||
case MESSAGEBOX_WARNING:
|
||||
sdlflags = SDL_MESSAGEBOX_WARNING;
|
||||
break;
|
||||
case MESSAGEBOX_INFO:
|
||||
default:
|
||||
sdlflags = SDL_MESSAGEBOX_INFORMATION;
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_ShowSimpleMessageBox(sdlflags, title, message, window);
|
||||
}
|
||||
|
||||
love::window::Window *Window::createSingleton()
|
||||
{
|
||||
if (!singleton)
|
||||
|
||||
@@ -83,6 +83,8 @@ public:
|
||||
|
||||
const void *getHandle() const;
|
||||
|
||||
void showMessageBox(MessageBoxType type, const char *title, const char *message);
|
||||
|
||||
static love::window::Window *createSingleton();
|
||||
static love::window::Window *getSingleton();
|
||||
|
||||
|
||||
+2
-14
@@ -1331,13 +1331,7 @@ function love.nogame()
|
||||
local create_rain
|
||||
|
||||
function love.load()
|
||||
-- Subtractive blending isn't supported on some ancient systems, so
|
||||
-- we should make sure it still looks decent in that case.
|
||||
if love.graphics.isSupported("subtractive") then
|
||||
love.graphics.setBackgroundColor(137, 194, 218)
|
||||
else
|
||||
love.graphics.setBackgroundColor(11, 88, 123)
|
||||
end
|
||||
love.graphics.setBackgroundColor(137, 194, 218)
|
||||
|
||||
local win_w = love.graphics.getWidth()
|
||||
local win_h = love.graphics.getHeight()
|
||||
@@ -1429,18 +1423,12 @@ function love.nogame()
|
||||
end
|
||||
|
||||
local function draw_grid()
|
||||
local blendmode = "subtractive"
|
||||
if not love.graphics.isSupported("subtractive") then
|
||||
-- We also change the background color in this case, so it looks OK.
|
||||
blendmode = "additive"
|
||||
end
|
||||
|
||||
local y = rain.spacing_y * rain.t
|
||||
|
||||
local small_y = -rain.spacing_y + y / 2
|
||||
local big_y = -rain.spacing_y + y
|
||||
|
||||
love.graphics.setBlendMode(blendmode)
|
||||
love.graphics.setBlendMode("subtractive")
|
||||
love.graphics.setColor(255, 255, 255, 128)
|
||||
love.graphics.draw(rain.batch, -rain.spacing_x, small_y, 0, 0.5, 0.5)
|
||||
|
||||
|
||||
+5
-33
@@ -4850,25 +4850,9 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x0a,
|
||||
0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x6c, 0x6f, 0x61,
|
||||
0x64, 0x28, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x62,
|
||||
0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x61, 0x6e, 0x63, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x0a,
|
||||
0x09, 0x09, 0x2d, 0x2d, 0x20, 0x77, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6d, 0x61, 0x6b,
|
||||
0x65, 0x20, 0x73, 0x75, 0x72, 0x65, 0x20, 0x69, 0x74, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x6c, 0x6f,
|
||||
0x6f, 0x6b, 0x73, 0x20, 0x64, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74,
|
||||
0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x0a,
|
||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
|
||||
0x2e, 0x69, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x28, 0x22, 0x73, 0x75, 0x62, 0x74,
|
||||
0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
|
||||
0x31, 0x33, 0x37, 0x2c, 0x20, 0x31, 0x39, 0x34, 0x2c, 0x20, 0x32, 0x31, 0x38, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73,
|
||||
0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
|
||||
0x31, 0x31, 0x2c, 0x20, 0x38, 0x38, 0x2c, 0x20, 0x31, 0x32, 0x33, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
|
||||
0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31,
|
||||
0x33, 0x37, 0x2c, 0x20, 0x31, 0x39, 0x34, 0x2c, 0x20, 0x32, 0x31, 0x38, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77, 0x69, 0x6e, 0x5f, 0x77, 0x20, 0x3d, 0x20, 0x6c, 0x6f,
|
||||
0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57, 0x69, 0x64,
|
||||
0x74, 0x68, 0x28, 0x29, 0x0a,
|
||||
@@ -5024,18 +5008,6 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x72,
|
||||
0x61, 0x77, 0x5f, 0x67, 0x72, 0x69, 0x64, 0x28, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f, 0x64, 0x65, 0x20,
|
||||
0x3d, 0x20, 0x22, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x0a,
|
||||
0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70,
|
||||
0x68, 0x69, 0x63, 0x73, 0x2e, 0x69, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x28, 0x22,
|
||||
0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x63, 0x68, 0x61, 0x6e,
|
||||
0x67, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20,
|
||||
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x61, 0x73, 0x65,
|
||||
0x2c, 0x20, 0x73, 0x6f, 0x20, 0x69, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x4f, 0x4b, 0x2e, 0x0a,
|
||||
0x09, 0x09, 0x09, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x22, 0x61, 0x64,
|
||||
0x64, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x0a,
|
||||
0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x72, 0x61, 0x69, 0x6e, 0x2e, 0x73,
|
||||
0x70, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x79, 0x20, 0x2a, 0x20, 0x72, 0x61, 0x69, 0x6e, 0x2e, 0x74, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x5f, 0x79, 0x20, 0x3d, 0x20,
|
||||
@@ -5044,8 +5016,8 @@ const unsigned char boot_lua[] =
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x62, 0x69, 0x67, 0x5f, 0x79, 0x20, 0x3d, 0x20, 0x2d, 0x72,
|
||||
0x61, 0x69, 0x6e, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x79, 0x20, 0x2b, 0x20, 0x79, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
|
||||
0x74, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x6d, 0x6f,
|
||||
0x64, 0x65, 0x29, 0x0a,
|
||||
0x74, 0x42, 0x6c, 0x65, 0x6e, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x28, 0x22, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61,
|
||||
0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x29, 0x0a,
|
||||
0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65,
|
||||
0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32,
|
||||
0x35, 0x35, 0x2c, 0x20, 0x31, 0x32, 0x38, 0x29, 0x0a,
|
||||
|
||||
Reference in New Issue
Block a user