mirror of
https://github.com/love2d/love.git
synced 2026-08-17 19:23:38 +02:00
Move most of Text and Video love.graphics classes out of the opengl subfolder.
--HG-- branch : minor
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
#include "math/MathModule.h"
|
||||
#include "window/Window.h"
|
||||
#include "Buffer.h"
|
||||
#include "Video.h"
|
||||
#include "Text.h"
|
||||
|
||||
#include "libraries/xxHash/xxhash.h"
|
||||
|
||||
@@ -185,12 +187,12 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, con
|
||||
return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
|
||||
}
|
||||
|
||||
Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
|
||||
love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
|
||||
{
|
||||
return new Text(this, font, text);
|
||||
}
|
||||
|
||||
Video *Graphics::newVideo(love::video::VideoStream *stream, float pixeldensity)
|
||||
love::graphics::Video *Graphics::newVideo(love::video::VideoStream *stream, float pixeldensity)
|
||||
{
|
||||
return new Video(stream, pixeldensity);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,12 @@
|
||||
#include "image/Image.h"
|
||||
#include "image/ImageData.h"
|
||||
|
||||
#include "video/VideoStream.h"
|
||||
|
||||
#include "Image.h"
|
||||
#include "SpriteBatch.h"
|
||||
#include "ParticleSystem.h"
|
||||
#include "Canvas.h"
|
||||
#include "Shader.h"
|
||||
#include "Mesh.h"
|
||||
#include "Text.h"
|
||||
#include "Video.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -86,9 +82,9 @@ public:
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage);
|
||||
|
||||
Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
love::graphics::Text *newText(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {}) override;
|
||||
|
||||
Video *newVideo(love::video::VideoStream *stream, float pixeldensity);
|
||||
love::graphics::Video *newVideo(love::video::VideoStream *stream, float pixeldensity) override;
|
||||
|
||||
void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
|
||||
bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;
|
||||
|
||||
@@ -335,14 +335,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
GLuint vbo;
|
||||
size_t gpuReadOffset;
|
||||
GLenum glMode;
|
||||
uint8 *data;
|
||||
|
||||
|
||||
BufferSync sync;
|
||||
|
||||
|
||||
}; // StreamBufferPersistentMapSync
|
||||
|
||||
love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size)
|
||||
|
||||
@@ -19,12 +19,9 @@
|
||||
**/
|
||||
|
||||
#include "Text.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "OpenGL.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -32,174 +29,13 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
love::Type Text::type("Text", &Drawable::type);
|
||||
|
||||
Text::Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text)
|
||||
: font(font)
|
||||
, vbo(nullptr)
|
||||
, quadIndices(gfx, 20)
|
||||
, vert_offset(0)
|
||||
, texture_cache_id((uint32) -1)
|
||||
: love::graphics::Text(gfx, font, text)
|
||||
{
|
||||
set(text);
|
||||
}
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
delete vbo;
|
||||
}
|
||||
|
||||
void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
|
||||
{
|
||||
size_t offset = vertoffset * sizeof(Font::GlyphVertex);
|
||||
size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
|
||||
uint8 *vbodata = nullptr;
|
||||
|
||||
// If we haven't created a VBO or the vertices are too big, make a new one.
|
||||
if (datasize > 0 && (!vbo || (offset + datasize) > vbo->getSize()))
|
||||
{
|
||||
// Make it bigger than necessary to reduce potential future allocations.
|
||||
size_t newsize = size_t((offset + datasize) * 1.5);
|
||||
|
||||
if (vbo != nullptr)
|
||||
newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
|
||||
|
||||
auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
|
||||
Buffer *new_vbo = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0);
|
||||
|
||||
if (vbo != nullptr)
|
||||
vbo->copyTo(0, vbo->getSize(), new_vbo, 0);
|
||||
|
||||
delete vbo;
|
||||
vbo = new_vbo;
|
||||
}
|
||||
|
||||
if (vbo != nullptr && datasize > 0)
|
||||
{
|
||||
vbodata = (uint8 *) vbo->map();
|
||||
memcpy(vbodata + offset, &vertices[0], datasize);
|
||||
// We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
|
||||
}
|
||||
}
|
||||
|
||||
void Text::regenerateVertices()
|
||||
{
|
||||
// If the font's texture cache was invalidated then we need to recreate the
|
||||
// text's vertices, since glyph texcoords might have changed.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
{
|
||||
std::vector<TextData> textdata = text_data;
|
||||
|
||||
clear();
|
||||
|
||||
for (const TextData &t : textdata)
|
||||
addTextData(t);
|
||||
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::addTextData(const TextData &t)
|
||||
{
|
||||
std::vector<Font::GlyphVertex> vertices;
|
||||
std::vector<Font::DrawCommand> new_commands;
|
||||
|
||||
Font::TextInfo text_info;
|
||||
|
||||
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// We only have formatted text if the align mode is valid.
|
||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||
new_commands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
|
||||
else
|
||||
new_commands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &text_info);
|
||||
|
||||
if (t.use_matrix)
|
||||
t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
|
||||
|
||||
size_t voffset = vert_offset;
|
||||
|
||||
if (!t.append_vertices)
|
||||
{
|
||||
voffset = 0;
|
||||
draw_commands.clear();
|
||||
text_data.clear();
|
||||
}
|
||||
|
||||
uploadVertices(vertices, voffset);
|
||||
|
||||
if (!new_commands.empty())
|
||||
{
|
||||
// The start vertex should be adjusted to account for the vertex offset.
|
||||
for (Font::DrawCommand &cmd : new_commands)
|
||||
cmd.startvertex += (int) voffset;
|
||||
|
||||
auto firstcmd = new_commands.begin();
|
||||
|
||||
// If the first draw command in the new list has the same texture as the
|
||||
// last one in the existing list we're building and its vertices are
|
||||
// in-order, we can combine them (saving a draw call.)
|
||||
if (!draw_commands.empty())
|
||||
{
|
||||
auto prevcmd = draw_commands.back();
|
||||
if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
|
||||
{
|
||||
draw_commands.back().vertexcount += firstcmd->vertexcount;
|
||||
++firstcmd;
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new draw commands to the list we're building.
|
||||
draw_commands.insert(draw_commands.end(), firstcmd, new_commands.end());
|
||||
}
|
||||
|
||||
vert_offset = voffset + vertices.size();
|
||||
|
||||
text_data.push_back(t);
|
||||
text_data.back().text_info = text_info;
|
||||
|
||||
// Font::generateVertices can invalidate the font's texture cache.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
void Text::set(const std::vector<Font::ColoredString> &text)
|
||||
{
|
||||
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
|
||||
}
|
||||
|
||||
void Text::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
|
||||
{
|
||||
if (text.empty() || (text.size() == 1 && text[0].str.empty()))
|
||||
return clear();
|
||||
|
||||
Font::ColoredCodepoints codepoints;
|
||||
Font::getCodepointsFromString(text, codepoints);
|
||||
|
||||
addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
|
||||
}
|
||||
|
||||
int Text::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
|
||||
{
|
||||
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
|
||||
}
|
||||
|
||||
int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
|
||||
{
|
||||
Font::ColoredCodepoints codepoints;
|
||||
Font::getCodepointsFromString(text, codepoints);
|
||||
|
||||
addTextData({codepoints, wrap, align, {}, true, true, m});
|
||||
|
||||
return (int) text_data.size() - 1;
|
||||
}
|
||||
|
||||
void Text::clear()
|
||||
{
|
||||
text_data.clear();
|
||||
draw_commands.clear();
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
vert_offset = 0;
|
||||
}
|
||||
|
||||
void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
@@ -260,43 +96,6 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setFont(love::graphics::Font *f)
|
||||
{
|
||||
font.set(f);
|
||||
|
||||
// Invalidate the texture cache ID since the font is different. We also have
|
||||
// to re-upload all the vertices based on the new font's textures.
|
||||
texture_cache_id = (uint32) -1;
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
love::graphics::Font *Text::getFont() const
|
||||
{
|
||||
return font.get();
|
||||
}
|
||||
|
||||
int Text::getWidth(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.width;
|
||||
}
|
||||
|
||||
int Text::getHeight(int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
index = std::max((int) text_data.size() - 1, 0);
|
||||
|
||||
if (index >= (int) text_data.size())
|
||||
return 0;
|
||||
|
||||
return text_data[index].text_info.height;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -18,92 +18,30 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_TEXT_H
|
||||
#define LOVE_GRAPHICS_OPENGL_TEXT_H
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Font.h"
|
||||
#include "graphics/Buffer.h"
|
||||
#include "graphics/Text.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
class Graphics;
|
||||
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Text : public Drawable
|
||||
class Text final : public love::graphics::Text
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
virtual ~Text();
|
||||
|
||||
void set(const std::vector<Font::ColoredString> &text);
|
||||
void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
|
||||
|
||||
int add(const std::vector<Font::ColoredString> &text, const Matrix4 &m);
|
||||
int addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m);
|
||||
|
||||
void clear();
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
void setFont(love::graphics::Font *f);
|
||||
love::graphics::Font *getFont() const;
|
||||
|
||||
/**
|
||||
* Gets the width of the currently set text.
|
||||
**/
|
||||
int getWidth(int index = 0) const;
|
||||
|
||||
/**
|
||||
* Gets the height of the currently set text.
|
||||
**/
|
||||
int getHeight(int index = 0) const;
|
||||
|
||||
private:
|
||||
|
||||
struct TextData
|
||||
{
|
||||
Font::ColoredCodepoints codepoints;
|
||||
float wrap;
|
||||
Font::AlignMode align;
|
||||
Font::TextInfo text_info;
|
||||
bool use_matrix;
|
||||
bool append_vertices;
|
||||
Matrix4 matrix;
|
||||
};
|
||||
|
||||
void uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset);
|
||||
void regenerateVertices();
|
||||
void addTextData(const TextData &s);
|
||||
|
||||
StrongRef<love::graphics::Font> font;
|
||||
Buffer *vbo;
|
||||
QuadIndices quadIndices;
|
||||
|
||||
std::vector<Font::DrawCommand> draw_commands;
|
||||
|
||||
std::vector<TextData> text_data;
|
||||
|
||||
size_t vert_offset;
|
||||
|
||||
// Used so we know when the font's texture cache is invalidated.
|
||||
uint32 texture_cache_id;
|
||||
void draw(love::graphics::Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
}; // Text
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_TEXT_H
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
|
||||
#include "Video.h"
|
||||
|
||||
// LOVE
|
||||
#include "Shader.h"
|
||||
#include "graphics/Graphics.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -31,43 +27,9 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
love::Type Video::type("Video", &Drawable::type);
|
||||
|
||||
Video::Video(love::video::VideoStream *stream, float pixeldensity)
|
||||
: stream(stream)
|
||||
, width(stream->getWidth() / pixeldensity)
|
||||
, height(stream->getHeight() / pixeldensity)
|
||||
, filter(Texture::defaultFilter)
|
||||
: love::graphics::Video(stream, pixeldensity)
|
||||
{
|
||||
filter.mipmap = Texture::FILTER_NONE;
|
||||
|
||||
stream->fillBackBuffer();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
vertices[i].color = Color(255, 255, 255, 255);
|
||||
|
||||
// Vertices are ordered for use with triangle strips:
|
||||
// 0---2
|
||||
// | / |
|
||||
// 1---3
|
||||
vertices[0].x = 0.0f;
|
||||
vertices[0].y = 0.0f;
|
||||
vertices[1].x = 0.0f;
|
||||
vertices[1].y = (float) height;
|
||||
vertices[2].x = (float) width;
|
||||
vertices[2].y = 0.0f;
|
||||
vertices[3].x = (float) width;
|
||||
vertices[3].y = (float) height;
|
||||
|
||||
vertices[0].s = 0.0f;
|
||||
vertices[0].t = 0.0f;
|
||||
vertices[1].s = 0.0f;
|
||||
vertices[1].t = 1.0f;
|
||||
vertices[2].s = 1.0f;
|
||||
vertices[2].t = 0.0f;
|
||||
vertices[3].s = 1.0f;
|
||||
vertices[3].t = 1.0f;
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
@@ -78,8 +40,12 @@ Video::~Video()
|
||||
|
||||
bool Video::loadVolatile()
|
||||
{
|
||||
GLuint textures[3];
|
||||
glGenTextures(3, textures);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
textureHandles[i] = textures[i];
|
||||
|
||||
// Create the textures using the initial frame data.
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
@@ -111,113 +77,27 @@ void Video::unloadVolatile()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.deleteTexture(textures[i]);
|
||||
textures[i] = 0;
|
||||
gl.deleteTexture((GLuint) textureHandles[i]);
|
||||
textureHandles[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
love::video::VideoStream *Video::getStream()
|
||||
void Video::uploadFrame(const love::video::VideoStream::Frame *frame)
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
||||
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
||||
|
||||
void Video::draw(Graphics *gfx, const Matrix4 &m)
|
||||
{
|
||||
update();
|
||||
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
bool srgb = false;
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(PIXELFORMAT_R8, false, srgb);
|
||||
|
||||
love::graphics::Shader *shader = Shader::current;
|
||||
bool usingdefaultshader = (shader == Shader::defaultShader);
|
||||
if (usingdefaultshader)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// If we're using the default shader, substitute the video version.
|
||||
Shader::defaultVideoShader->attach();
|
||||
shader = Shader::defaultVideoShader;
|
||||
gl.bindTextureToUnit((GLuint) textureHandles[i], 0, false);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[i], heights[i],
|
||||
fmt.externalformat, fmt.type, data[i]);
|
||||
}
|
||||
|
||||
shader->setVideoTextures(textures[0], textures[1], textures[2]);
|
||||
|
||||
Graphics::StreamDrawRequest req;
|
||||
req.formats[0] = vertex::CommonFormat::XYf_STf_RGBAub;
|
||||
req.indexMode = vertex::TriangleIndexMode::QUADS;
|
||||
req.vertexCount = 4;
|
||||
|
||||
Graphics::StreamVertexData data = gfx->requestStreamDraw(req);
|
||||
Vertex *verts = (Vertex *) data.stream[0];
|
||||
|
||||
Matrix4 t(gfx->getTransform(), m);
|
||||
t.transform(verts, vertices, 4);
|
||||
|
||||
Color c = toColor(gfx->getColor());
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
verts[i].s = vertices[i].s;
|
||||
verts[i].t = vertices[i].t;
|
||||
verts[i].color = c;
|
||||
}
|
||||
|
||||
gfx->flushStreamDraws();
|
||||
|
||||
if (usingdefaultshader)
|
||||
Shader::defaultShader->attach();
|
||||
}
|
||||
|
||||
void Video::update()
|
||||
{
|
||||
bool bufferschanged = stream->swapBuffers();
|
||||
stream->fillBackBuffer();
|
||||
|
||||
if (bufferschanged)
|
||||
{
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
int widths[3] = {frame->yw, frame->cw, frame->cw};
|
||||
int heights[3] = {frame->yh, frame->ch, frame->ch};
|
||||
|
||||
const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
|
||||
|
||||
bool srgb = false;
|
||||
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(PIXELFORMAT_R8, false, srgb);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.bindTextureToUnit(textures[i], 0, false);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[i], heights[i],
|
||||
fmt.externalformat, fmt.type, data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
love::audio::Source *Video::getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
void Video::setSource(love::audio::Source *source)
|
||||
{
|
||||
this->source = source;
|
||||
}
|
||||
|
||||
int Video::getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
int Video::getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
int Video::getPixelWidth() const
|
||||
{
|
||||
return stream->getWidth();
|
||||
}
|
||||
|
||||
int Video::getPixelHeight() const
|
||||
{
|
||||
return stream->getHeight();
|
||||
}
|
||||
|
||||
void Video::setFilter(const Texture::Filter &f)
|
||||
@@ -229,16 +109,11 @@ void Video::setFilter(const Texture::Filter &f)
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.bindTextureToUnit(textures[i], 0, false);
|
||||
gl.bindTextureToUnit((GLuint) textureHandles[i], 0, false);
|
||||
gl.setTextureFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
const Texture::Filter &Video::getFilter() const
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -21,12 +21,8 @@
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "common/math.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Video.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "audio/Source.h"
|
||||
|
||||
#include "OpenGL.h"
|
||||
|
||||
namespace love
|
||||
@@ -36,49 +32,22 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Video : public Drawable, public Volatile
|
||||
class Video : public love::graphics::Video, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
static love::Type type;
|
||||
|
||||
Video(love::video::VideoStream *stream, float pixeldensity = 1.0f);
|
||||
~Video();
|
||||
virtual ~Video();
|
||||
|
||||
// Volatile
|
||||
bool loadVolatile() override;
|
||||
void unloadVolatile() override;
|
||||
|
||||
// Drawable
|
||||
void draw(Graphics *gfx, const Matrix4 &m) override;
|
||||
|
||||
love::video::VideoStream *getStream();
|
||||
|
||||
love::audio::Source *getSource();
|
||||
void setSource(love::audio::Source *source);
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
int getPixelWidth() const;
|
||||
int getPixelHeight() const;
|
||||
|
||||
void setFilter(const Texture::Filter &f);
|
||||
const Texture::Filter &getFilter() const;
|
||||
void setFilter(const Texture::Filter &f) override;
|
||||
|
||||
private:
|
||||
|
||||
void update();
|
||||
|
||||
StrongRef<love::video::VideoStream> stream;
|
||||
StrongRef<love::audio::Source> source;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
GLuint textures[3];
|
||||
|
||||
Vertex vertices[4];
|
||||
void uploadFrame(const love::video::VideoStream::Frame *frame) override;
|
||||
|
||||
Texture::Filter filter;
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
#include "graphics/wrap_Canvas.h"
|
||||
#include "graphics/wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "graphics/wrap_Text.h"
|
||||
#include "graphics/wrap_Video.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "wrap_Text.h"
|
||||
#include "graphics/wrap_Font.h"
|
||||
#include "math/wrap_Transform.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Text *luax_checktext(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Text>(L, idx);
|
||||
}
|
||||
|
||||
int w_Text_set(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
if (lua_isnoneornil(L, 3))
|
||||
{
|
||||
// Single argument: unformatted text.
|
||||
std::vector<Font::ColoredString> newtext;
|
||||
luax_checkcoloredstring(L, 2, newtext);
|
||||
luax_catchexcept(L, [&](){ t->set(newtext); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multiple arguments: formatted text.
|
||||
float wraplimit = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
std::vector<Font::ColoredString> newtext;
|
||||
luax_checkcoloredstring(L, 2, newtext);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_setf(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
float wraplimit = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
std::vector<Font::ColoredString> newtext;
|
||||
luax_checkcoloredstring(L, 2, newtext);
|
||||
|
||||
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_add(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
int index = 0;
|
||||
|
||||
std::vector<Font::ColoredString> text;
|
||||
luax_checkcoloredstring(L, 2, text);
|
||||
|
||||
if (luax_istype(L, 3, math::Transform::type))
|
||||
{
|
||||
math::Transform *tf = luax_totype<math::Transform>(L, 3);
|
||||
luax_catchexcept(L, [&](){ index = t->add(text, tf->getMatrix()); });
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = (float) luaL_optnumber(L, 3, 0.0);
|
||||
float y = (float) luaL_optnumber(L, 4, 0.0);
|
||||
float a = (float) luaL_optnumber(L, 5, 0.0);
|
||||
float sx = (float) luaL_optnumber(L, 6, 1.0);
|
||||
float sy = (float) luaL_optnumber(L, 7, sx);
|
||||
float ox = (float) luaL_optnumber(L, 8, 0.0);
|
||||
float oy = (float) luaL_optnumber(L, 9, 0.0);
|
||||
float kx = (float) luaL_optnumber(L, 10, 0.0);
|
||||
float ky = (float) luaL_optnumber(L, 11, 0.0);
|
||||
|
||||
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
luax_catchexcept(L, [&](){ index = t->add(text, m); });
|
||||
}
|
||||
|
||||
lua_pushnumber(L, index + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_addf(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
|
||||
int index = 0;
|
||||
|
||||
std::vector<Font::ColoredString> text;
|
||||
luax_checkcoloredstring(L, 2, text);
|
||||
|
||||
float wrap = (float) luaL_checknumber(L, 3);
|
||||
|
||||
Font::AlignMode align = Font::ALIGN_MAX_ENUM;
|
||||
const char *alignstr = luaL_checkstring(L, 4);
|
||||
|
||||
if (!Font::getConstant(alignstr, align))
|
||||
return luaL_error(L, "Invalid align mode: %s", alignstr);
|
||||
|
||||
if (luax_istype(L, 5, math::Transform::type))
|
||||
{
|
||||
math::Transform *tf = luax_totype<math::Transform>(L, 5);
|
||||
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, tf->getMatrix()); });
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = (float) luaL_optnumber(L, 5, 0.0);
|
||||
float y = (float) luaL_optnumber(L, 6, 0.0);
|
||||
float a = (float) luaL_optnumber(L, 7, 0.0);
|
||||
float sx = (float) luaL_optnumber(L, 8, 1.0);
|
||||
float sy = (float) luaL_optnumber(L, 9, sx);
|
||||
float ox = (float) luaL_optnumber(L, 10, 0.0);
|
||||
float oy = (float) luaL_optnumber(L, 11, 0.0);
|
||||
float kx = (float) luaL_optnumber(L, 12, 0.0);
|
||||
float ky = (float) luaL_optnumber(L, 13, 0.0);
|
||||
|
||||
Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky);
|
||||
luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); });
|
||||
}
|
||||
|
||||
lua_pushnumber(L, index + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_clear(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
luax_catchexcept(L, [&](){ t->clear(); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_setFont(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
Font *f = luax_checktype<Font>(L, 2);
|
||||
luax_catchexcept(L, [&](){ t->setFont(f); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Text_getFont(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
Font *f = t->getFont();
|
||||
luax_pushtype(L, f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_getWidth(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
int index = (int) luaL_optnumber(L, 2, 0) - 1;
|
||||
lua_pushnumber(L, t->getWidth(index));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_getHeight(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
int index = (int) luaL_optnumber(L, 2, 0) - 1;
|
||||
lua_pushnumber(L, t->getHeight(index));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Text_getDimensions(lua_State *L)
|
||||
{
|
||||
Text *t = luax_checktext(L, 1);
|
||||
int index = (int) luaL_optnumber(L, 2, 0) - 1;
|
||||
lua_pushnumber(L, t->getWidth(index));
|
||||
lua_pushnumber(L, t->getHeight(index));
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const luaL_Reg w_Text_functions[] =
|
||||
{
|
||||
{ "set", w_Text_set },
|
||||
{ "setf", w_Text_setf },
|
||||
{ "add", w_Text_add },
|
||||
{ "addf", w_Text_addf },
|
||||
{ "clear", w_Text_clear },
|
||||
{ "setFont", w_Text_setFont },
|
||||
{ "getFont", w_Text_getFont },
|
||||
{ "getWidth", w_Text_getWidth },
|
||||
{ "getHeight", w_Text_getHeight },
|
||||
{ "getDimensions", w_Text_getDimensions },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
extern "C" int luaopen_text(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, &Text::type, w_Text_functions, nullptr);
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
#define LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
|
||||
#include "Text.h"
|
||||
#include "common/runtime.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Text *luax_checktext(lua_State *L, int idx);
|
||||
extern "C" int luaopen_text(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif // LOVE_GRAPHICS_OPENGL_WRAP_TEXT_H
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "wrap_Video.h"
|
||||
|
||||
// Shove the wrap_Video.lua code directly into a raw string literal.
|
||||
static const char video_lua[] =
|
||||
#include "wrap_Video.lua"
|
||||
;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Video *luax_checkvideo(lua_State *L, int idx)
|
||||
{
|
||||
return luax_checktype<Video>(L, idx);
|
||||
}
|
||||
|
||||
int w_Video_getStream(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
luax_pushtype(L, video->getStream());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getSource(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
auto source = video->getSource();
|
||||
if (source)
|
||||
luax_pushtype(L, video->getSource());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_setSource(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
video->setSource(nullptr);
|
||||
else
|
||||
{
|
||||
auto source = luax_checktype<love::audio::Source>(L, 2);
|
||||
video->setSource(source);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Video_getWidth(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getWidth());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getHeight(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getHeight());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getDimensions(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getWidth());
|
||||
lua_pushnumber(L, video->getHeight());
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_Video_getPixelWidth(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getPixelWidth());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getPixelHeight(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getPixelHeight());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Video_getPixelDimensions(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
lua_pushnumber(L, video->getPixelWidth());
|
||||
lua_pushnumber(L, video->getPixelHeight());
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_Video_setFilter(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
Texture::Filter f = video->getFilter();
|
||||
|
||||
const char *minstr = luaL_checkstring(L, 2);
|
||||
const char *magstr = luaL_optstring(L, 3, minstr);
|
||||
|
||||
if (!Texture::getConstant(minstr, f.min))
|
||||
return luaL_error(L, "Invalid filter mode: %s", minstr);
|
||||
if (!Texture::getConstant(magstr, f.mag))
|
||||
return luaL_error(L, "Invalid filter mode: %s", magstr);
|
||||
|
||||
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
|
||||
|
||||
luax_catchexcept(L, [&](){ video->setFilter(f); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Video_getFilter(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
const Texture::Filter f = video->getFilter();
|
||||
|
||||
const char *minstr = nullptr;
|
||||
const char *magstr = nullptr;
|
||||
|
||||
if (!Texture::getConstant(f.min, minstr))
|
||||
return luaL_error(L, "Unknown filter mode.");
|
||||
if (!Texture::getConstant(f.mag, magstr))
|
||||
return luaL_error(L, "Unknown filter mode.");
|
||||
|
||||
lua_pushstring(L, minstr);
|
||||
lua_pushstring(L, magstr);
|
||||
lua_pushnumber(L, f.anisotropy);
|
||||
return 3;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "getStream", w_Video_getStream },
|
||||
{ "getSource", w_Video_getSource },
|
||||
{ "_setSource", w_Video_setSource },
|
||||
{ "getWidth", w_Video_getWidth },
|
||||
{ "getHeight", w_Video_getHeight },
|
||||
{ "getDimensions", w_Video_getDimensions },
|
||||
{ "getPixelWidth", w_Video_getPixelWidth },
|
||||
{ "getPixelHeight", w_Video_getPixelHeight },
|
||||
{ "getPixelDimensions", w_Video_getPixelDimensions },
|
||||
{ "setFilter", w_Video_setFilter },
|
||||
{ "getFilter", w_Video_getFilter },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
int luaopen_video(lua_State *L)
|
||||
{
|
||||
int ret = luax_register_type(L, &Video::type, functions, nullptr);
|
||||
|
||||
luaL_loadbuffer(L, video_lua, sizeof(video_lua), "Video.lua");
|
||||
luax_gettypemetatable(L, Video::type);
|
||||
lua_call(L, 1, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2017 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
// LOVE
|
||||
#include "Video.h"
|
||||
#include "common/runtime.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
int luaopen_video(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -1,58 +0,0 @@
|
||||
R"luastring"--(
|
||||
-- DO NOT REMOVE THE ABOVE LINE. It is used to load this file as a C++ string.
|
||||
-- There is a matching delimiter at the bottom of the file.
|
||||
|
||||
--[[
|
||||
Copyright (c) 2006-2017 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
--]]
|
||||
|
||||
local Video_mt = ...
|
||||
local Video = Video_mt.__index
|
||||
|
||||
function Video:setSource(source)
|
||||
self:_setSource(source)
|
||||
self:getStream():setSync(source)
|
||||
end
|
||||
|
||||
function Video:play()
|
||||
return self:getStream():play()
|
||||
end
|
||||
|
||||
function Video:pause()
|
||||
return self:getStream():pause()
|
||||
end
|
||||
|
||||
function Video:seek(offset)
|
||||
return self:getStream():seek(offset)
|
||||
end
|
||||
|
||||
function Video:rewind()
|
||||
return self:getStream():rewind()
|
||||
end
|
||||
|
||||
function Video:tell()
|
||||
return self:getStream():tell()
|
||||
end
|
||||
|
||||
function Video:isPlaying()
|
||||
return self:getStream():isPlaying()
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
Reference in New Issue
Block a user