mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Add initial video playback support for Ogg Theora videos (resolves issue #66.)
The basic APIs are:
video = love.graphics.newVideo("myvideo.ogv")
love.graphics.draw(video, ...) -- Video objects are Drawables.
video:play(), video:pause()
video:getDuration(), video:tell(), video:rewind(), video:seek(seconds)
video:getSource()
video:getWidth(), video:getHeight(), video:setFilter(min, mag)
More advanced APIs include video:setSource(source), video:getStream(), and videostream:setSync.
To use a custom pixel shader when drawing a Video, call the new TexelVideo(texcoords) function instead of Texel(texture, texcoords) in order to get the pixel colors of a video frame.
This commit is contained in:
@@ -86,6 +86,11 @@ Graphics::~Graphics()
|
||||
Shader::defaultShader->release();
|
||||
Shader::defaultShader = nullptr;
|
||||
}
|
||||
if (Shader::defaultVideoShader)
|
||||
{
|
||||
Shader::defaultVideoShader->release();
|
||||
Shader::defaultVideoShader = nullptr;
|
||||
}
|
||||
|
||||
if (quadIndices)
|
||||
delete quadIndices;
|
||||
@@ -320,6 +325,13 @@ bool Graphics::setMode(int width, int height)
|
||||
Shader::defaultShader = newShader(Shader::defaultCode[renderer]);
|
||||
}
|
||||
|
||||
// and a default video shader.
|
||||
if (!Shader::defaultVideoShader)
|
||||
{
|
||||
Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
Shader::defaultVideoShader = newShader(Shader::defaultVideoCode[renderer]);
|
||||
}
|
||||
|
||||
// A shader should always be active, but the default shader shouldn't be
|
||||
// returned by getShader(), so we don't do setShader(defaultShader).
|
||||
if (!Shader::current)
|
||||
@@ -819,6 +831,11 @@ Text *Graphics::newText(Font *font, const std::vector<Font::ColoredString> &text
|
||||
return new Text(font, text);
|
||||
}
|
||||
|
||||
Video *Graphics::newVideo(love::video::VideoStream *stream)
|
||||
{
|
||||
return new Video(stream);
|
||||
}
|
||||
|
||||
bool Graphics::isGammaCorrect() const
|
||||
{
|
||||
return love::graphics::isGammaCorrect();
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#include "window/Window.h"
|
||||
|
||||
#include "video/VideoStream.h"
|
||||
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
#include "graphics/Quad.h"
|
||||
@@ -47,6 +49,7 @@
|
||||
#include "Shader.h"
|
||||
#include "Mesh.h"
|
||||
#include "Text.h"
|
||||
#include "Video.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -185,6 +188,8 @@ public:
|
||||
|
||||
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
|
||||
Video *newVideo(love::video::VideoStream *stream);
|
||||
|
||||
bool isGammaCorrect() const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,8 +64,10 @@ namespace
|
||||
|
||||
Shader *Shader::current = nullptr;
|
||||
Shader *Shader::defaultShader = nullptr;
|
||||
Shader *Shader::defaultVideoShader = nullptr;
|
||||
|
||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
std::vector<int> Shader::textureCounters;
|
||||
|
||||
@@ -77,6 +79,7 @@ Shader::Shader(const ShaderSource &source)
|
||||
, lastCanvas((Canvas *) -1)
|
||||
, lastViewport()
|
||||
, lastPointSize(0.0f)
|
||||
, videoTextureUnits()
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
@@ -225,6 +228,9 @@ bool Shader::loadVolatile()
|
||||
lastProjectionMatrix.setTranslation(nan, nan);
|
||||
lastTransformMatrix.setTranslation(nan, nan);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
videoTextureUnits[i] = 0;
|
||||
|
||||
// zero out active texture list
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||
@@ -635,6 +641,57 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
||||
return builtinAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
// Set up the texture units that will be used by the shader to sample from
|
||||
// the textures, if they haven't been set up yet.
|
||||
if (videoTextureUnits[0] == 0)
|
||||
{
|
||||
const GLint locs[3] = {
|
||||
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CR_CHANNEL]
|
||||
};
|
||||
|
||||
const char *names[3] = {nullptr, nullptr, nullptr};
|
||||
builtinNames.find(BUILTIN_VIDEO_Y_CHANNEL, names[0]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CB_CHANNEL, names[1]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CR_CHANNEL, names[2]);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (locs[i] >= 0 && names[i] != nullptr)
|
||||
{
|
||||
videoTextureUnits[i] = getTextureUnit(names[i]);
|
||||
|
||||
// Increment global shader texture id counter for this texture
|
||||
// unit, if we haven't already.
|
||||
if (activeTexUnits[videoTextureUnits[i] - 1] == 0)
|
||||
++textureCounters[videoTextureUnits[i] - 1];
|
||||
|
||||
glUniform1i(locs[i], videoTextureUnits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GLuint textures[3] = {ytexture, cbtexture, crtexture};
|
||||
|
||||
// Bind the textures to their respective texture units.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (videoTextureUnits[i] != 0)
|
||||
{
|
||||
// Store texture id so it can be re-bound later.
|
||||
activeTexUnits[videoTextureUnits[i] - 1] = textures[i];
|
||||
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
gl.setTextureUnit(0);
|
||||
}
|
||||
|
||||
void Shader::checkSetScreenParams()
|
||||
{
|
||||
OpenGL::Viewport view = gl.getViewport();
|
||||
@@ -898,6 +955,9 @@ StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::built
|
||||
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
||||
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
|
||||
{"love_VideoYChannel", Shader::BUILTIN_VIDEO_Y_CHANNEL},
|
||||
{"love_VideoCbChannel", Shader::BUILTIN_VIDEO_CB_CHANNEL},
|
||||
{"love_VideoCrChannel", Shader::BUILTIN_VIDEO_CR_CHANNEL},
|
||||
};
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||
|
||||
@@ -64,6 +64,9 @@ public:
|
||||
BUILTIN_NORMAL_MATRIX,
|
||||
BUILTIN_POINT_SIZE,
|
||||
BUILTIN_SCREEN_SIZE,
|
||||
BUILTIN_VIDEO_Y_CHANNEL,
|
||||
BUILTIN_VIDEO_CB_CHANNEL,
|
||||
BUILTIN_VIDEO_CR_CHANNEL,
|
||||
BUILTIN_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -89,9 +92,11 @@ public:
|
||||
|
||||
// Pointer to the default Shader.
|
||||
static Shader *defaultShader;
|
||||
static Shader *defaultVideoShader;
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
static ShaderSource defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
/**
|
||||
* Creates a new Shader using a list of source codes.
|
||||
@@ -182,6 +187,7 @@ public:
|
||||
**/
|
||||
bool hasVertexAttrib(VertexAttribID attrib) const;
|
||||
|
||||
void setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture);
|
||||
void checkSetScreenParams();
|
||||
void checkSetPointSize(float size);
|
||||
void checkSetBuiltinUniforms();
|
||||
@@ -263,6 +269,8 @@ private:
|
||||
Matrix4 lastTransformMatrix;
|
||||
Matrix4 lastProjectionMatrix;
|
||||
|
||||
GLuint videoTextureUnits[3];
|
||||
|
||||
// Counts total number of textures bound to each texture unit in all shaders
|
||||
static std::vector<int> textureCounters;
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 "Video.h"
|
||||
|
||||
// LOVE
|
||||
#include "Shader.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Video::Video(love::video::VideoStream *stream)
|
||||
: stream(stream)
|
||||
, filter(Texture::getDefaultFilter())
|
||||
{
|
||||
filter.mipmap = Texture::FILTER_NONE;
|
||||
|
||||
stream->fillBackBuffer();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
vertices[i].r = vertices[i].g = vertices[i].b = vertices[i].a = 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) stream->getHeight();
|
||||
vertices[2].x = (float) stream->getWidth();
|
||||
vertices[2].y = 0.0f;
|
||||
vertices[3].x = (float) stream->getWidth();
|
||||
vertices[3].y = (float) stream->getHeight();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
Video::~Video()
|
||||
{
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
bool Video::loadVolatile()
|
||||
{
|
||||
glGenTextures(3, &textures[0]);
|
||||
|
||||
// Create the textures using the initial frame data.
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
gl.bindTexture(textures[0]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->yw, frame->yh,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
|
||||
|
||||
gl.bindTexture(textures[1]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
|
||||
|
||||
gl.bindTexture(textures[2]);
|
||||
gl.setTextureFilter(filter);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->cw, frame->ch,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Video::unloadVolatile()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.deleteTexture(textures[i]);
|
||||
textures[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
love::video::VideoStream *Video::getStream()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Video::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
update();
|
||||
|
||||
Shader *shader = Shader::current;
|
||||
bool defaultShader = (shader == Shader::defaultShader);
|
||||
if (defaultShader)
|
||||
{
|
||||
// If we're still using the default shader, substitute the video version
|
||||
Shader::defaultVideoShader->attach();
|
||||
shader = Shader::defaultVideoShader;
|
||||
}
|
||||
|
||||
shader->setVideoTextures(textures[0], textures[1], textures[2]);
|
||||
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
||||
|
||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].x);
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &vertices[0].s);
|
||||
|
||||
gl.prepareDraw();
|
||||
gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// If we were using the default shader, reattach it
|
||||
if (defaultShader)
|
||||
Shader::defaultShader->attach();
|
||||
}
|
||||
|
||||
void Video::update()
|
||||
{
|
||||
bool bufferschanged = stream->swapBuffers();
|
||||
stream->fillBackBuffer();
|
||||
|
||||
if (bufferschanged)
|
||||
{
|
||||
auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
|
||||
|
||||
gl.bindTexture(textures[0]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->yw, frame->yh,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->yplane);
|
||||
|
||||
gl.bindTexture(textures[1]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->cbplane);
|
||||
|
||||
gl.bindTexture(textures[2]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->cw, frame->ch,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->crplane);
|
||||
}
|
||||
}
|
||||
|
||||
love::audio::Source *Video::getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
void Video::setSource(love::audio::Source *source)
|
||||
{
|
||||
this->source = source;
|
||||
}
|
||||
|
||||
int Video::getWidth() const
|
||||
{
|
||||
return stream->getWidth();
|
||||
}
|
||||
|
||||
int Video::getHeight() const
|
||||
{
|
||||
return stream->getHeight();
|
||||
}
|
||||
|
||||
void Video::setFilter(const Texture::Filter &f)
|
||||
{
|
||||
if (!Texture::validateFilter(f, false))
|
||||
throw love::Exception("Invalid texture filter.");
|
||||
|
||||
filter = f;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
gl.bindTexture(textures[i]);
|
||||
gl.setTextureFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
const Texture::Filter &Video::getFilter() const
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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 "common/math.h"
|
||||
#include "graphics/Drawable.h"
|
||||
#include "graphics/Volatile.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "audio/Source.h"
|
||||
|
||||
#include "OpenGL.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
class Video : public Drawable, public Volatile
|
||||
{
|
||||
public:
|
||||
|
||||
Video(love::video::VideoStream *stream);
|
||||
~Video();
|
||||
|
||||
// Volatile
|
||||
bool loadVolatile();
|
||||
void unloadVolatile();
|
||||
|
||||
love::video::VideoStream *getStream();
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
love::audio::Source *getSource();
|
||||
void setSource(love::audio::Source *source);
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
void setFilter(const Texture::Filter &f);
|
||||
const Texture::Filter &getFilter() const;
|
||||
|
||||
private:
|
||||
|
||||
void update();
|
||||
|
||||
StrongRef<love::video::VideoStream> stream;
|
||||
StrongRef<love::audio::Source> source;
|
||||
|
||||
GLuint textures[3];
|
||||
|
||||
Vertex vertices[4];
|
||||
|
||||
Texture::Filter filter;
|
||||
|
||||
}; // Video
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "image/Image.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "image/wrap_Image.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -862,6 +863,20 @@ int w_newText(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newVideo(lua_State *L)
|
||||
{
|
||||
if (!luax_istype(L, 1, VIDEO_VIDEO_STREAM_ID))
|
||||
luax_convobj(L, 1, "video", "newVideoStream");
|
||||
|
||||
auto stream = luax_checktype<love::video::VideoStream>(L, 1, VIDEO_VIDEO_STREAM_ID);
|
||||
Video *video = nullptr;
|
||||
|
||||
luax_catchexcept(L, [&]() { video = instance()->newVideo(stream); });
|
||||
luax_pushtype(L, GRAPHICS_VIDEO_ID, video);
|
||||
video->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setColor(lua_State *L)
|
||||
{
|
||||
Colorf c;
|
||||
@@ -1274,25 +1289,37 @@ int w_setDefaultShaderCode(lua_State *L)
|
||||
lua_getfield(L, 1, "opengl");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglcode;
|
||||
openglcode.vertex = luax_checkstring(L, -2);
|
||||
openglcode.pixel = luax_checkstring(L, -1);
|
||||
openglcode.vertex = luax_checkstring(L, -3);
|
||||
openglcode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglVideocode;
|
||||
openglVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
lua_getfield(L, 1, "opengles");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglescode;
|
||||
openglescode.vertex = luax_checkstring(L, -2);
|
||||
openglescode.pixel = luax_checkstring(L, -1);
|
||||
openglescode.vertex = luax_checkstring(L, -3);
|
||||
openglescode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglesVideocode;
|
||||
openglesVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglesVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGL] = openglcode;
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGL] = openglVideocode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGLES] = openglesVideocode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1863,6 +1890,7 @@ static const luaL_Reg functions[] =
|
||||
{ "newShader", w_newShader },
|
||||
{ "newMesh", w_newMesh },
|
||||
{ "newText", w_newText },
|
||||
{ "_newVideo", w_newVideo },
|
||||
|
||||
{ "setColor", w_setColor },
|
||||
{ "getColor", w_getColor },
|
||||
@@ -1965,6 +1993,7 @@ static const lua_CFunction types[] =
|
||||
luaopen_shader,
|
||||
luaopen_mesh,
|
||||
luaopen_text,
|
||||
luaopen_video,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
|
||||
@@ -165,6 +165,28 @@ varying mediump vec4 VaryingColor;
|
||||
|
||||
uniform sampler2D _tex0_;]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords)
|
||||
{
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
|
||||
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
|
||||
|
||||
vec4 color;
|
||||
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
|
||||
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
|
||||
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
|
||||
color.a = 1.0;
|
||||
|
||||
return gammaCorrectColor(color);
|
||||
}]],
|
||||
|
||||
FOOTER = [[
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
@@ -209,6 +231,7 @@ local function createPixelCode(pixelcode, is_multicanvas, lang)
|
||||
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
||||
GLSL.FUNCTIONS,
|
||||
GLSL.PIXEL.FUNCTIONS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
pixelcode,
|
||||
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
||||
@@ -309,21 +332,45 @@ vec4 position(mat4 transform_proj, vec4 vertpos) {
|
||||
pixel = [[
|
||||
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return Texel(tex, texcoord) * vcolor;
|
||||
}]]
|
||||
}]],
|
||||
videopixel = [[
|
||||
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return VideoTexel(texcoord) * vcolor;
|
||||
}]],
|
||||
}
|
||||
|
||||
local defaults = {
|
||||
opengl = {
|
||||
createVertexCode(defaultcode.vertex, "glsl"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsl"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsl"),
|
||||
},
|
||||
opengles = {
|
||||
createVertexCode(defaultcode.vertex, "glsles"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsles"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsles"),
|
||||
},
|
||||
}
|
||||
|
||||
love.graphics._setDefaultShaderCode(defaults)
|
||||
|
||||
function love.graphics.newVideo(file, loadaudio)
|
||||
local video = love.graphics._newVideo(file)
|
||||
local source, success
|
||||
|
||||
if loadaudio ~= false then
|
||||
success, source = pcall(love.audio.newSource, video:getStream():getFilename())
|
||||
end
|
||||
if success then
|
||||
video:setSource(source)
|
||||
elseif loadaudio == true then
|
||||
error("Video had no audio track", 2)
|
||||
else
|
||||
video:getStream():setSync(love.video.newRemote())
|
||||
end
|
||||
|
||||
return video
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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, GRAPHICS_VIDEO_ID);
|
||||
}
|
||||
|
||||
int w_Video_getStream(lua_State *L)
|
||||
{
|
||||
Video *video = luax_checkvideo(L, 1);
|
||||
luax_pushtype(L, VIDEO_VIDEO_STREAM_ID, 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, AUDIO_SOURCE_ID, 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, AUDIO_SOURCE_ID);
|
||||
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_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 },
|
||||
{ "setFilter", w_Video_setFilter },
|
||||
{ "getFilter", w_Video_getFilter },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
int luaopen_video(lua_State *L)
|
||||
{
|
||||
int ret = luax_register_type(L, GRAPHICS_VIDEO_ID, "Video", functions, nullptr);
|
||||
|
||||
luaL_loadbuffer(L, video_lua, sizeof(video_lua), "Video.lua");
|
||||
luax_gettypemetatable(L, GRAPHICS_VIDEO_ID);
|
||||
lua_call(L, 1, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 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
|
||||
@@ -0,0 +1,69 @@
|
||||
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-2015 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:loadRemote()
|
||||
local stream = self:getStream()
|
||||
local remote = love.video.newRemote()
|
||||
stream:setSync(remote)
|
||||
return remote
|
||||
end
|
||||
|
||||
function Video:setSource(source)
|
||||
self:_setSource(source)
|
||||
if source then
|
||||
self:getStream():setSync(source)
|
||||
else
|
||||
self:getStream():setSync(love.video.newRemote())
|
||||
end
|
||||
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