mirror of
https://github.com/love2d/love-android.git
synced 2026-08-19 20:20:25 +02:00
Updated LÖVE source to revision cff5bc8604bd
This commit is contained in:
@@ -53,6 +53,7 @@ public:
|
|||||||
M_TIMER,
|
M_TIMER,
|
||||||
M_TOUCH,
|
M_TOUCH,
|
||||||
M_WINDOW,
|
M_WINDOW,
|
||||||
|
M_VIDEO,
|
||||||
M_MAX_ENUM
|
M_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,16 @@ public:
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T*() const
|
||||||
|
{
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
void set(T *obj)
|
void set(T *obj)
|
||||||
{
|
{
|
||||||
if (obj) obj->retain();
|
if (obj) obj->retain();
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_STREAM_H
|
||||||
|
#define LOVE_STREAM_H
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "Object.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
|
||||||
|
class Stream : public Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Stream() {}
|
||||||
|
|
||||||
|
// getData and getSize are assumed to talk about
|
||||||
|
// the buffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback, gets called when some Stream consumer exhausts the data
|
||||||
|
**/
|
||||||
|
virtual void fillBackBuffer() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the front buffer, Streams are supposed to be (at least) double-buffered
|
||||||
|
**/
|
||||||
|
virtual const void *getFrontBuffer() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of any (and in particular the front) buffer
|
||||||
|
**/
|
||||||
|
virtual size_t getSize() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swap buffers. Returns true if there is new data in the front buffer,
|
||||||
|
* false otherwise.
|
||||||
|
* NOTE: If there is no back buffer ready, this call must be ignored
|
||||||
|
**/
|
||||||
|
virtual bool swapBuffers() = 0;
|
||||||
|
}; // Stream
|
||||||
|
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_STREAM_H
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
|
|
||||||
extern StringMap<Type, TYPE_MAX_ENUM> types;
|
|
||||||
|
|
||||||
static love::Type extractudatatype(lua_State *L, int idx)
|
static love::Type extractudatatype(lua_State *L, int idx)
|
||||||
{
|
{
|
||||||
Type t = INVALID_ID;
|
Type t = INVALID_ID;
|
||||||
@@ -36,7 +34,7 @@ static love::Type extractudatatype(lua_State *L, int idx)
|
|||||||
lua_pushvalue(L, idx);
|
lua_pushvalue(L, idx);
|
||||||
int result = lua_pcall(L, 1, 1, 0);
|
int result = lua_pcall(L, 1, 1, 0);
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
types.find(lua_tostring(L, -1), t);
|
getTypeName(lua_tostring(L, -1), t);
|
||||||
if (result == 0 || result == LUA_ERRRUN)
|
if (result == 0 || result == LUA_ERRRUN)
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return t;
|
return t;
|
||||||
@@ -165,7 +163,7 @@ Variant *Variant::fromLua(lua_State *L, int n, bool allowTables)
|
|||||||
if (allowTables)
|
if (allowTables)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
std::vector<std::pair<Variant*, Variant*> > *table = new std::vector<std::pair<Variant*, Variant*> >();
|
std::vector<std::pair<Variant*, Variant*>> *table = new std::vector<std::pair<Variant*, Variant*>>();
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, n))
|
while (lua_next(L, n))
|
||||||
{
|
{
|
||||||
@@ -230,7 +228,7 @@ void Variant::toLua(lua_State *L)
|
|||||||
// I can do (at the moment).
|
// I can do (at the moment).
|
||||||
break;
|
break;
|
||||||
case TABLE:
|
case TABLE:
|
||||||
lua_newtable(L);
|
lua_createtable(L, 0, (int) data.table->size());
|
||||||
for (size_t i = 0; i < data.table->size(); ++i)
|
for (size_t i = 0; i < data.table->size(); ++i)
|
||||||
{
|
{
|
||||||
std::pair<Variant*, Variant*> &kv = data.table->at(i);
|
std::pair<Variant*, Variant*> &kv = data.table->at(i);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public:
|
|||||||
size_t len;
|
size_t len;
|
||||||
} string;
|
} string;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
std::vector<std::pair<Variant*, Variant*> > *table;
|
std::vector<std::pair<Variant*, Variant*>> *table;
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -141,6 +141,8 @@
|
|||||||
# define LOVE_ENABLE_TOUCH
|
# define LOVE_ENABLE_TOUCH
|
||||||
# define LOVE_ENABLE_TOUCH_SDL
|
# define LOVE_ENABLE_TOUCH_SDL
|
||||||
# define LOVE_ENABLE_UTF8
|
# define LOVE_ENABLE_UTF8
|
||||||
|
# define LOVE_ENABLE_VIDEO
|
||||||
|
# define LOVE_ENABLE_VIDEO_THEORA
|
||||||
# define LOVE_ENABLE_WINDOW
|
# define LOVE_ENABLE_WINDOW
|
||||||
# define LOVE_ENABLE_WINDOW_SDL
|
# define LOVE_ENABLE_WINDOW_SDL
|
||||||
# define LOVE_ENABLE_WUFF
|
# define LOVE_ENABLE_WUFF
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ int luax_require(lua_State *L, const char *name)
|
|||||||
|
|
||||||
int luax_register_module(lua_State *L, const WrappedModule &m)
|
int luax_register_module(lua_State *L, const WrappedModule &m)
|
||||||
{
|
{
|
||||||
|
love::addTypeName(m.type, m.name);
|
||||||
|
|
||||||
// Put a reference to the C++ module in Lua.
|
// Put a reference to the C++ module in Lua.
|
||||||
luax_insistregistry(L, REGISTRY_MODULES);
|
luax_insistregistry(L, REGISTRY_MODULES);
|
||||||
|
|
||||||
@@ -270,12 +272,9 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pushmetatable)
|
int luax_register_type(lua_State *L, love::Type type, const char *name, ...)
|
||||||
{
|
{
|
||||||
// Verify that this type name has a matching Type ID and type name mapping.
|
love::addTypeName(type, name);
|
||||||
const char *tname = "Invalid";
|
|
||||||
if (!love::getType(type, tname))
|
|
||||||
printf("Missing type name entry for type ID %d\n", type);
|
|
||||||
|
|
||||||
// Get the place for storing and re-using instantiated love types.
|
// Get the place for storing and re-using instantiated love types.
|
||||||
luax_getregistry(L, REGISTRY_OBJECTS);
|
luax_getregistry(L, REGISTRY_OBJECTS);
|
||||||
@@ -302,7 +301,7 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu
|
|||||||
else
|
else
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
luaL_newmetatable(L, tname);
|
luaL_newmetatable(L, name);
|
||||||
|
|
||||||
// m.__index = m
|
// m.__index = m
|
||||||
lua_pushvalue(L, -1);
|
lua_pushvalue(L, -1);
|
||||||
@@ -317,12 +316,12 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu
|
|||||||
lua_setfield(L, -2, "__eq");
|
lua_setfield(L, -2, "__eq");
|
||||||
|
|
||||||
// Add tostring function.
|
// Add tostring function.
|
||||||
lua_pushstring(L, tname);
|
lua_pushstring(L, name);
|
||||||
lua_pushcclosure(L, w__tostring, 1);
|
lua_pushcclosure(L, w__tostring, 1);
|
||||||
lua_setfield(L, -2, "__tostring");
|
lua_setfield(L, -2, "__tostring");
|
||||||
|
|
||||||
// Add type
|
// Add type
|
||||||
lua_pushstring(L, tname);
|
lua_pushstring(L, name);
|
||||||
lua_pushcclosure(L, w__type, 1);
|
lua_pushcclosure(L, w__type, 1);
|
||||||
lua_setfield(L, -2, "type");
|
lua_setfield(L, -2, "type");
|
||||||
|
|
||||||
@@ -330,16 +329,25 @@ int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f, bool pu
|
|||||||
lua_pushcfunction(L, w__typeOf);
|
lua_pushcfunction(L, w__typeOf);
|
||||||
lua_setfield(L, -2, "typeOf");
|
lua_setfield(L, -2, "typeOf");
|
||||||
|
|
||||||
if (f != nullptr)
|
va_list fs;
|
||||||
|
va_start(fs, name);
|
||||||
|
for (const luaL_Reg *f = va_arg(fs, const luaL_Reg *); f; f = va_arg(fs, const luaL_Reg *))
|
||||||
luax_setfuncs(L, f);
|
luax_setfuncs(L, f);
|
||||||
|
va_end(fs);
|
||||||
if (pushmetatable)
|
|
||||||
return 1; // leave the metatable on the stack.
|
|
||||||
|
|
||||||
lua_pop(L, 1); // Pops metatable.
|
lua_pop(L, 1); // Pops metatable.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void luax_gettypemetatable(lua_State *L, love::Type type)
|
||||||
|
{
|
||||||
|
const char *name = nullptr;
|
||||||
|
if (getTypeName(type, name))
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, name);
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
|
||||||
int luax_table_insert(lua_State *L, int tindex, int vindex, int pos)
|
int luax_table_insert(lua_State *L, int tindex, int vindex, int pos)
|
||||||
{
|
{
|
||||||
if (tindex < 0)
|
if (tindex < 0)
|
||||||
@@ -403,7 +411,7 @@ void luax_rawnewtype(lua_State *L, love::Type type, love::Object *object)
|
|||||||
u->type = type;
|
u->type = type;
|
||||||
|
|
||||||
const char *name = "Invalid";
|
const char *name = "Invalid";
|
||||||
getType(type, name);
|
getTypeName(type, name);
|
||||||
|
|
||||||
luaL_newmetatable(L, name);
|
luaL_newmetatable(L, name);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
@@ -670,7 +678,7 @@ extern "C" int luax_typerror(lua_State *L, int narg, const char *tname)
|
|||||||
// Non-love userdata might have a type metamethod which doesn't
|
// Non-love userdata might have a type metamethod which doesn't
|
||||||
// describe its type properly, so we only use it for love types.
|
// describe its type properly, so we only use it for love types.
|
||||||
love::Type t;
|
love::Type t;
|
||||||
if (!love::getType(argtname, t))
|
if (!love::getTypeName(argtname, t))
|
||||||
argtname = 0;
|
argtname = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,7 +715,7 @@ void luax_register(lua_State *L, const char *name, const luaL_Reg *l)
|
|||||||
Type luax_type(lua_State *L, int idx)
|
Type luax_type(lua_State *L, int idx)
|
||||||
{
|
{
|
||||||
Type t = INVALID_ID;
|
Type t = INVALID_ID;
|
||||||
getType(luaL_checkstring(L, idx), t);
|
getTypeName(luaL_checkstring(L, idx), t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,10 +247,15 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name);
|
|||||||
/**
|
/**
|
||||||
* Register a new type.
|
* Register a new type.
|
||||||
* @param type The type.
|
* @param type The type.
|
||||||
* @param f The list of member functions for the type.
|
* @param name The type's human-readable name
|
||||||
* @param pushmetatable Whether to push the type's metatable to the stack.
|
* @param ... The list of lists of member functions for the type. (of type luaL_Reg*)
|
||||||
**/
|
**/
|
||||||
int luax_register_type(lua_State *L, love::Type type, const luaL_Reg *f = nullptr, bool pushmetatable = false);
|
int luax_register_type(lua_State *L, love::Type type, const char *name, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes the metatable of the specified type onto the stack.
|
||||||
|
**/
|
||||||
|
void luax_gettypemetatable(lua_State *L, love::Type type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do a table.insert from C
|
* Do a table.insert from C
|
||||||
@@ -434,7 +439,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type)
|
|||||||
if (lua_type(L, idx) != LUA_TUSERDATA)
|
if (lua_type(L, idx) != LUA_TUSERDATA)
|
||||||
{
|
{
|
||||||
const char *name = "Invalid";
|
const char *name = "Invalid";
|
||||||
getType(type, name);
|
getTypeName(type, name);
|
||||||
luax_typerror(L, idx, name);
|
luax_typerror(L, idx, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +448,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type)
|
|||||||
if (!typeFlags[u->type][type])
|
if (!typeFlags[u->type][type])
|
||||||
{
|
{
|
||||||
const char *name = "Invalid";
|
const char *name = "Invalid";
|
||||||
getType(type, name);
|
getTypeName(type, name);
|
||||||
luax_typerror(L, idx, name);
|
luax_typerror(L, idx, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +459,7 @@ template <typename T>
|
|||||||
T *luax_getmodule(lua_State *L, love::Type type)
|
T *luax_getmodule(lua_State *L, love::Type type)
|
||||||
{
|
{
|
||||||
const char *name = "Invalid";
|
const char *name = "Invalid";
|
||||||
getType(type, name);
|
getTypeName(type, name);
|
||||||
|
|
||||||
luax_insistregistry(L, REGISTRY_MODULES);
|
luax_insistregistry(L, REGISTRY_MODULES);
|
||||||
lua_getfield(L, -1, name);
|
lua_getfield(L, -1, name);
|
||||||
@@ -476,7 +481,7 @@ template <typename T>
|
|||||||
T *luax_optmodule(lua_State *L, love::Type type)
|
T *luax_optmodule(lua_State *L, love::Type type)
|
||||||
{
|
{
|
||||||
const char *name = "Invalid";
|
const char *name = "Invalid";
|
||||||
getType(type, name);
|
getTypeName(type, name);
|
||||||
|
|
||||||
luax_insistregistry(L, REGISTRY_MODULES);
|
luax_insistregistry(L, REGISTRY_MODULES);
|
||||||
lua_getfield(L, -1, name);
|
lua_getfield(L, -1, name);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ static const TypeBits *createTypeFlags()
|
|||||||
b[OBJECT_ID] = one << OBJECT_ID;
|
b[OBJECT_ID] = one << OBJECT_ID;
|
||||||
b[DATA_ID] = (one << DATA_ID) | b[OBJECT_ID];
|
b[DATA_ID] = (one << DATA_ID) | b[OBJECT_ID];
|
||||||
b[MODULE_ID] = (one << MODULE_ID) | b[OBJECT_ID];
|
b[MODULE_ID] = (one << MODULE_ID) | b[OBJECT_ID];
|
||||||
|
b[STREAM_ID] = (one << STREAM_ID) | b[OBJECT_ID];
|
||||||
|
|
||||||
// Filesystem.
|
// Filesystem.
|
||||||
b[FILESYSTEM_FILE_ID] = (one << FILESYSTEM_FILE_ID) | b[OBJECT_ID];
|
b[FILESYSTEM_FILE_ID] = (one << FILESYSTEM_FILE_ID) | b[OBJECT_ID];
|
||||||
@@ -55,6 +56,7 @@ static const TypeBits *createTypeFlags()
|
|||||||
b[GRAPHICS_SHADER_ID] = (one << GRAPHICS_SHADER_ID) | b[OBJECT_ID];
|
b[GRAPHICS_SHADER_ID] = (one << GRAPHICS_SHADER_ID) | b[OBJECT_ID];
|
||||||
b[GRAPHICS_MESH_ID] = (one << GRAPHICS_MESH_ID) | b[GRAPHICS_DRAWABLE_ID];
|
b[GRAPHICS_MESH_ID] = (one << GRAPHICS_MESH_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||||
b[GRAPHICS_TEXT_ID] = (one << GRAPHICS_TEXT_ID) | b[GRAPHICS_DRAWABLE_ID];
|
b[GRAPHICS_TEXT_ID] = (one << GRAPHICS_TEXT_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||||
|
b[GRAPHICS_VIDEO_ID] = (one << GRAPHICS_VIDEO_ID) | b[GRAPHICS_DRAWABLE_ID];
|
||||||
|
|
||||||
// Image.
|
// Image.
|
||||||
b[IMAGE_IMAGE_DATA_ID] = (one << IMAGE_IMAGE_DATA_ID) | b[DATA_ID];
|
b[IMAGE_IMAGE_DATA_ID] = (one << IMAGE_IMAGE_DATA_ID) | b[DATA_ID];
|
||||||
@@ -105,6 +107,9 @@ static const TypeBits *createTypeFlags()
|
|||||||
b[THREAD_THREAD_ID] = (one << THREAD_THREAD_ID) | b[OBJECT_ID];
|
b[THREAD_THREAD_ID] = (one << THREAD_THREAD_ID) | b[OBJECT_ID];
|
||||||
b[THREAD_CHANNEL_ID] = (one << THREAD_CHANNEL_ID) | b[OBJECT_ID];
|
b[THREAD_CHANNEL_ID] = (one << THREAD_CHANNEL_ID) | b[OBJECT_ID];
|
||||||
|
|
||||||
|
// Video
|
||||||
|
b[VIDEO_VIDEO_STREAM_ID] = (one << VIDEO_VIDEO_STREAM_ID) | b[STREAM_ID];
|
||||||
|
|
||||||
// Modules.
|
// Modules.
|
||||||
b[MODULE_FILESYSTEM_ID] = (one << MODULE_FILESYSTEM_ID) | b[MODULE_ID];
|
b[MODULE_FILESYSTEM_ID] = (one << MODULE_FILESYSTEM_ID) | b[MODULE_ID];
|
||||||
b[MODULE_GRAPHICS_ID] = (one << MODULE_GRAPHICS_ID) | b[MODULE_ID];
|
b[MODULE_GRAPHICS_ID] = (one << MODULE_GRAPHICS_ID) | b[MODULE_ID];
|
||||||
@@ -116,102 +121,21 @@ static const TypeBits *createTypeFlags()
|
|||||||
|
|
||||||
const TypeBits *typeFlags = createTypeFlags();
|
const TypeBits *typeFlags = createTypeFlags();
|
||||||
|
|
||||||
StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[] =
|
static StringMap<Type, TYPE_MAX_ENUM> types(nullptr, 0);
|
||||||
|
|
||||||
|
void addTypeName(Type type, const char *name)
|
||||||
{
|
{
|
||||||
{"Invalid", INVALID_ID},
|
const char *n;
|
||||||
|
if (!types.find(type, n))
|
||||||
|
types.add(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
{"Object", OBJECT_ID},
|
bool getTypeName(const char *in, love::Type &out)
|
||||||
{"Data", DATA_ID},
|
|
||||||
{"Module", MODULE_ID},
|
|
||||||
|
|
||||||
// Filesystem
|
|
||||||
{"File", FILESYSTEM_FILE_ID},
|
|
||||||
{"DroppedFile", FILESYSTEM_DROPPED_FILE_ID},
|
|
||||||
{"FileData", FILESYSTEM_FILE_DATA_ID},
|
|
||||||
|
|
||||||
// Font
|
|
||||||
{"GlyphData", FONT_GLYPH_DATA_ID},
|
|
||||||
{"Rasterizer", FONT_RASTERIZER_ID},
|
|
||||||
|
|
||||||
// Graphics
|
|
||||||
{"Drawable", GRAPHICS_DRAWABLE_ID},
|
|
||||||
{"Texture", GRAPHICS_TEXTURE_ID},
|
|
||||||
{"Image", GRAPHICS_IMAGE_ID},
|
|
||||||
{"Quad", GRAPHICS_QUAD_ID},
|
|
||||||
{"Font", GRAPHICS_FONT_ID},
|
|
||||||
{"ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_ID},
|
|
||||||
{"SpriteBatch", GRAPHICS_SPRITE_BATCH_ID},
|
|
||||||
{"Canvas", GRAPHICS_CANVAS_ID},
|
|
||||||
{"Shader", GRAPHICS_SHADER_ID},
|
|
||||||
{"Mesh", GRAPHICS_MESH_ID},
|
|
||||||
{"Text", GRAPHICS_TEXT_ID},
|
|
||||||
|
|
||||||
// Image
|
|
||||||
{"ImageData", IMAGE_IMAGE_DATA_ID},
|
|
||||||
{"CompressedImageData", IMAGE_COMPRESSED_IMAGE_DATA_ID},
|
|
||||||
|
|
||||||
// Joystick
|
|
||||||
{"Joystick", JOYSTICK_JOYSTICK_ID},
|
|
||||||
|
|
||||||
// Math
|
|
||||||
{"RandomGenerator", MATH_RANDOM_GENERATOR_ID},
|
|
||||||
{"BezierCurve", MATH_BEZIER_CURVE_ID},
|
|
||||||
{"CompressedData", MATH_COMPRESSED_DATA_ID},
|
|
||||||
|
|
||||||
// Audio
|
|
||||||
{"Source", AUDIO_SOURCE_ID},
|
|
||||||
|
|
||||||
// Sound
|
|
||||||
{"SoundData", SOUND_SOUND_DATA_ID},
|
|
||||||
{"Decoder", SOUND_DECODER_ID},
|
|
||||||
|
|
||||||
// Mouse
|
|
||||||
{"Cursor", MOUSE_CURSOR_ID},
|
|
||||||
|
|
||||||
// Physics
|
|
||||||
{"World", PHYSICS_WORLD_ID},
|
|
||||||
{"Contact", PHYSICS_CONTACT_ID},
|
|
||||||
{"Body", PHYSICS_BODY_ID},
|
|
||||||
{"Fixture", PHYSICS_FIXTURE_ID},
|
|
||||||
{"Shape", PHYSICS_SHAPE_ID},
|
|
||||||
{"CircleShape", PHYSICS_CIRCLE_SHAPE_ID},
|
|
||||||
{"PolygonShape", PHYSICS_POLYGON_SHAPE_ID},
|
|
||||||
{"EdgeShape", PHYSICS_EDGE_SHAPE_ID},
|
|
||||||
{"ChainShape", PHYSICS_CHAIN_SHAPE_ID},
|
|
||||||
{"Joint", PHYSICS_JOINT_ID},
|
|
||||||
{"MouseJoint", PHYSICS_MOUSE_JOINT_ID},
|
|
||||||
{"DistanceJoint", PHYSICS_DISTANCE_JOINT_ID},
|
|
||||||
{"PrismaticJoint", PHYSICS_PRISMATIC_JOINT_ID},
|
|
||||||
{"RevoluteJoint", PHYSICS_REVOLUTE_JOINT_ID},
|
|
||||||
{"PulleyJoint", PHYSICS_PULLEY_JOINT_ID},
|
|
||||||
{"GearJoint", PHYSICS_GEAR_JOINT_ID},
|
|
||||||
{"FrictionJoint", PHYSICS_FRICTION_JOINT_ID},
|
|
||||||
{"WeldJoint", PHYSICS_WELD_JOINT_ID},
|
|
||||||
{"RopeJoint", PHYSICS_ROPE_JOINT_ID},
|
|
||||||
{"WheelJoint", PHYSICS_WHEEL_JOINT_ID},
|
|
||||||
{"MotorJoint", PHYSICS_MOTOR_JOINT_ID},
|
|
||||||
|
|
||||||
// Thread
|
|
||||||
{"Thread", THREAD_THREAD_ID},
|
|
||||||
{"Channel", THREAD_CHANNEL_ID},
|
|
||||||
|
|
||||||
// The modules themselves. Only add abstracted modules here.
|
|
||||||
{"filesystem", MODULE_FILESYSTEM_ID},
|
|
||||||
{"graphics", MODULE_GRAPHICS_ID},
|
|
||||||
{"image", MODULE_IMAGE_ID},
|
|
||||||
{"sound", MODULE_SOUND_ID},
|
|
||||||
};
|
|
||||||
|
|
||||||
StringMap<Type, TYPE_MAX_ENUM> types(typeEntries, sizeof(typeEntries));
|
|
||||||
|
|
||||||
static_assert((sizeof(typeEntries) / sizeof(typeEntries[0])) == TYPE_MAX_ENUM, "Type name array size doesn't match the total number of type IDs!");
|
|
||||||
|
|
||||||
bool getType(const char *in, love::Type &out)
|
|
||||||
{
|
{
|
||||||
return types.find(in, out);
|
return types.find(in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getType(love::Type in, const char *&out)
|
bool getTypeName(love::Type in, const char *&out)
|
||||||
{
|
{
|
||||||
return types.find(in, out);
|
return types.find(in, out);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ enum Type
|
|||||||
OBJECT_ID,
|
OBJECT_ID,
|
||||||
DATA_ID,
|
DATA_ID,
|
||||||
MODULE_ID,
|
MODULE_ID,
|
||||||
|
STREAM_ID,
|
||||||
|
|
||||||
// Filesystem.
|
// Filesystem.
|
||||||
FILESYSTEM_FILE_ID,
|
FILESYSTEM_FILE_ID,
|
||||||
@@ -56,6 +57,7 @@ enum Type
|
|||||||
GRAPHICS_SHADER_ID,
|
GRAPHICS_SHADER_ID,
|
||||||
GRAPHICS_MESH_ID,
|
GRAPHICS_MESH_ID,
|
||||||
GRAPHICS_TEXT_ID,
|
GRAPHICS_TEXT_ID,
|
||||||
|
GRAPHICS_VIDEO_ID,
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
IMAGE_IMAGE_DATA_ID,
|
IMAGE_IMAGE_DATA_ID,
|
||||||
@@ -106,6 +108,9 @@ enum Type
|
|||||||
THREAD_THREAD_ID,
|
THREAD_THREAD_ID,
|
||||||
THREAD_CHANNEL_ID,
|
THREAD_CHANNEL_ID,
|
||||||
|
|
||||||
|
// Video
|
||||||
|
VIDEO_VIDEO_STREAM_ID,
|
||||||
|
|
||||||
// The modules themselves. Only add abstracted modules here.
|
// The modules themselves. Only add abstracted modules here.
|
||||||
MODULE_FILESYSTEM_ID,
|
MODULE_FILESYSTEM_ID,
|
||||||
MODULE_GRAPHICS_ID,
|
MODULE_GRAPHICS_ID,
|
||||||
@@ -123,8 +128,9 @@ typedef std::bitset<TYPE_MAX_ENUM> TypeBits;
|
|||||||
**/
|
**/
|
||||||
extern const TypeBits *typeFlags;
|
extern const TypeBits *typeFlags;
|
||||||
|
|
||||||
bool getType(const char *in, Type &out);
|
void addTypeName(Type type, const char *name);
|
||||||
bool getType(Type in, const char *&out);
|
bool getTypeName(const char *in, Type &out);
|
||||||
|
bool getTypeName(Type in, const char *&out);
|
||||||
|
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ static const int VERSION_MINOR = 10;
|
|||||||
static const int VERSION_REV = 0;
|
static const int VERSION_REV = 0;
|
||||||
static const char *VERSION = LOVE_VERSION_STRING;
|
static const char *VERSION = LOVE_VERSION_STRING;
|
||||||
static const char *VERSION_COMPATIBILITY[] = { VERSION, 0 };
|
static const char *VERSION_COMPATIBILITY[] = { VERSION, 0 };
|
||||||
static const char *VERSION_CODENAME = "";
|
static const char *VERSION_CODENAME = "Super Toast";
|
||||||
|
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const luaL_Reg w_Data_functions[] =
|
|||||||
|
|
||||||
int w_Data_open(lua_State *L)
|
int w_Data_open(lua_State *L)
|
||||||
{
|
{
|
||||||
luax_register_type(L, DATA_ID, w_Data_functions);
|
luax_register_type(L, DATA_ID, "Data", w_Data_functions, nullptr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,8 @@ namespace love
|
|||||||
{
|
{
|
||||||
|
|
||||||
Data *luax_checkdata(lua_State *L, int idx);
|
Data *luax_checkdata(lua_State *L, int idx);
|
||||||
int w_Data_getString(lua_State *L);
|
|
||||||
int w_Data_getPointer(lua_State *L);
|
|
||||||
int w_Data_getSize(lua_State *L);
|
|
||||||
int w_Data_open(lua_State *L);
|
int w_Data_open(lua_State *L);
|
||||||
|
extern const luaL_Reg w_Data_functions[];
|
||||||
|
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
b2ChainShape::~b2ChainShape()
|
b2ChainShape::~b2ChainShape()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void b2ChainShape::Clear()
|
||||||
{
|
{
|
||||||
b2Free(m_vertices);
|
b2Free(m_vertices);
|
||||||
m_vertices = NULL;
|
m_vertices = NULL;
|
||||||
@@ -56,10 +61,8 @@ void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count)
|
|||||||
b2Assert(count >= 2);
|
b2Assert(count >= 2);
|
||||||
for (int32 i = 1; i < count; ++i)
|
for (int32 i = 1; i < count; ++i)
|
||||||
{
|
{
|
||||||
b2Vec2 v1 = vertices[i-1];
|
|
||||||
b2Vec2 v2 = vertices[i];
|
|
||||||
// If the code crashes here, it means your vertices are too close together.
|
// If the code crashes here, it means your vertices are too close together.
|
||||||
b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop);
|
b2Assert(b2DistanceSquared(vertices[i-1], vertices[i]) > b2_linearSlop * b2_linearSlop);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_count = count;
|
m_count = count;
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ public:
|
|||||||
/// The destructor frees the vertices using b2Free.
|
/// The destructor frees the vertices using b2Free.
|
||||||
~b2ChainShape();
|
~b2ChainShape();
|
||||||
|
|
||||||
|
/// Clear all data.
|
||||||
|
void Clear();
|
||||||
|
|
||||||
/// Create a loop. This automatically adjusts connectivity.
|
/// Create a loop. This automatically adjusts connectivity.
|
||||||
/// @param vertices an array of vertices, these are copied
|
/// @param vertices an array of vertices, these are copied
|
||||||
/// @param count the vertex count
|
/// @param count the vertex count
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
|
|||||||
bool unique = true;
|
bool unique = true;
|
||||||
for (int32 j = 0; j < tempCount; ++j)
|
for (int32 j = 0; j < tempCount; ++j)
|
||||||
{
|
{
|
||||||
if (b2DistanceSquared(v, ps[j]) < 0.5f * b2_linearSlop)
|
if (b2DistanceSquared(v, ps[j]) < ((0.5f * b2_linearSlop) * (0.5f * b2_linearSlop)))
|
||||||
{
|
{
|
||||||
unique = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
@@ -217,6 +217,14 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m < 3)
|
||||||
|
{
|
||||||
|
// Polygon is degenerate.
|
||||||
|
b2Assert(false);
|
||||||
|
SetAsBox(1.0f, 1.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_count = m;
|
m_count = m;
|
||||||
|
|
||||||
// Copy vertices.
|
// Copy vertices.
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
struct b2Color
|
struct b2Color
|
||||||
{
|
{
|
||||||
b2Color() {}
|
b2Color() {}
|
||||||
b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {}
|
b2Color(float32 r, float32 g, float32 b, float32 a = 1.0f) : r(r), g(g), b(b), a(a) {}
|
||||||
void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; }
|
void Set(float32 ri, float32 gi, float32 bi, float32 ai = 1.0f) { r = ri; g = gi; b = bi; a = ai; }
|
||||||
float32 r, g, b;
|
float32 r, g, b, a;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Implement and register this class with a b2World to provide debug drawing of physics
|
/// Implement and register this class with a b2World to provide debug drawing of physics
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <Box2D/Common/b2Settings.h>
|
#include <Box2D/Common/b2Settings.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <float.h>
|
|
||||||
|
|
||||||
/// This function is used to ensure that a floating point number is not a NaN or infinity.
|
/// This function is used to ensure that a floating point number is not a NaN or infinity.
|
||||||
inline bool b2IsValid(float32 x)
|
inline bool b2IsValid(float32 x)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "common/Exception.h"
|
#include "common/Exception.h"
|
||||||
|
|
||||||
b2Version b2_version = {2, 3, 0};
|
b2Version b2_version = {2, 3, 2};
|
||||||
|
|
||||||
// Memory allocators. Modify these to use your own allocator.
|
// Memory allocators. Modify these to use your own allocator.
|
||||||
void* b2Alloc(int32 size)
|
void* b2Alloc(int32 size)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#define B2_DEBUG_SOLVER 0
|
#define B2_DEBUG_SOLVER 0
|
||||||
|
|
||||||
|
bool g_blockSolve = true;
|
||||||
|
|
||||||
struct b2ContactPositionConstraint
|
struct b2ContactPositionConstraint
|
||||||
{
|
{
|
||||||
b2Vec2 localPoints[b2_maxManifoldPoints];
|
b2Vec2 localPoints[b2_maxManifoldPoints];
|
||||||
@@ -213,7 +215,7 @@ void b2ContactSolver::InitializeVelocityConstraints()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have two points, then prepare the block solver.
|
// If we have two points, then prepare the block solver.
|
||||||
if (vc->pointCount == 2)
|
if (vc->pointCount == 2 && g_blockSolve)
|
||||||
{
|
{
|
||||||
b2VelocityConstraintPoint* vcp1 = vc->points + 0;
|
b2VelocityConstraintPoint* vcp1 = vc->points + 0;
|
||||||
b2VelocityConstraintPoint* vcp2 = vc->points + 1;
|
b2VelocityConstraintPoint* vcp2 = vc->points + 1;
|
||||||
@@ -341,29 +343,32 @@ void b2ContactSolver::SolveVelocityConstraints()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Solve normal constraints
|
// Solve normal constraints
|
||||||
if (vc->pointCount == 1)
|
if (pointCount == 1 || g_blockSolve == false)
|
||||||
{
|
{
|
||||||
b2VelocityConstraintPoint* vcp = vc->points + 0;
|
for (int32 i = 0; i < pointCount; ++i)
|
||||||
|
{
|
||||||
|
b2VelocityConstraintPoint* vcp = vc->points + i;
|
||||||
|
|
||||||
// Relative velocity at contact
|
// Relative velocity at contact
|
||||||
b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
|
b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
|
||||||
|
|
||||||
// Compute normal impulse
|
// Compute normal impulse
|
||||||
float32 vn = b2Dot(dv, normal);
|
float32 vn = b2Dot(dv, normal);
|
||||||
float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias);
|
float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias);
|
||||||
|
|
||||||
// b2Clamp the accumulated impulse
|
// b2Clamp the accumulated impulse
|
||||||
float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
|
float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
|
||||||
lambda = newImpulse - vcp->normalImpulse;
|
lambda = newImpulse - vcp->normalImpulse;
|
||||||
vcp->normalImpulse = newImpulse;
|
vcp->normalImpulse = newImpulse;
|
||||||
|
|
||||||
// Apply contact impulse
|
// Apply contact impulse
|
||||||
b2Vec2 P = lambda * normal;
|
b2Vec2 P = lambda * normal;
|
||||||
vA -= mA * P;
|
vA -= mA * P;
|
||||||
wA -= iA * b2Cross(vcp->rA, P);
|
wA -= iA * b2Cross(vcp->rA, P);
|
||||||
|
|
||||||
vB += mB * P;
|
vB += mB * P;
|
||||||
wB += iB * b2Cross(vcp->rB, P);
|
wB += iB * b2Cross(vcp->rB, P);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -174,6 +174,9 @@ void b2PrismaticJoint::InitVelocityConstraints(const b2SolverData& data)
|
|||||||
m_s1 = b2Cross(d + rA, m_perp);
|
m_s1 = b2Cross(d + rA, m_perp);
|
||||||
m_s2 = b2Cross(rB, m_perp);
|
m_s2 = b2Cross(rB, m_perp);
|
||||||
|
|
||||||
|
float32 s1test;
|
||||||
|
s1test = b2Cross(rA, m_perp);
|
||||||
|
|
||||||
float32 k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2;
|
float32 k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2;
|
||||||
float32 k12 = iA * m_s1 + iB * m_s2;
|
float32 k12 = iA * m_s1 + iB * m_s2;
|
||||||
float32 k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2;
|
float32 k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2;
|
||||||
|
|||||||
@@ -129,6 +129,12 @@ void b2WeldJoint::InitVelocityConstraints(const b2SolverData& data)
|
|||||||
invM += m_gamma;
|
invM += m_gamma;
|
||||||
m_mass.ez.z = invM != 0.0f ? 1.0f / invM : 0.0f;
|
m_mass.ez.z = invM != 0.0f ? 1.0f / invM : 0.0f;
|
||||||
}
|
}
|
||||||
|
else if (K.ez.z == 0.0f)
|
||||||
|
{
|
||||||
|
K.GetInverse22(&m_mass);
|
||||||
|
m_gamma = 0.0f;
|
||||||
|
m_bias = 0.0f;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
K.GetSymInverse33(&m_mass);
|
K.GetSymInverse33(&m_mass);
|
||||||
@@ -271,7 +277,17 @@ bool b2WeldJoint::SolvePositionConstraints(const b2SolverData& data)
|
|||||||
|
|
||||||
b2Vec3 C(C1.x, C1.y, C2);
|
b2Vec3 C(C1.x, C1.y, C2);
|
||||||
|
|
||||||
b2Vec3 impulse = -K.Solve33(C);
|
b2Vec3 impulse;
|
||||||
|
if (K.ez.z > 0.0f)
|
||||||
|
{
|
||||||
|
impulse = -K.Solve33(C);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b2Vec2 impulse2 = -K.Solve22(C1);
|
||||||
|
impulse.Set(impulse2.x, impulse2.y, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
b2Vec2 P(impulse.x, impulse.y);
|
b2Vec2 P(impulse.x, impulse.y);
|
||||||
|
|
||||||
cA -= mA * P;
|
cA -= mA * P;
|
||||||
|
|||||||
@@ -72,9 +72,8 @@ struct b2WheelJointDef : public b2JointDef
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// A wheel joint. This joint provides two degrees of freedom: translation
|
/// A wheel joint. This joint provides two degrees of freedom: translation
|
||||||
/// along an axis fixed in bodyA and rotation in the plane. You can use a
|
/// along an axis fixed in bodyA and rotation in the plane. In other words, it is a point to
|
||||||
/// joint limit to restrict the range of motion and a joint motor to drive
|
/// line constraint with a rotational motor and a linear spring/damper.
|
||||||
/// the rotation or to model rotational friction.
|
|
||||||
/// This joint is designed for vehicle suspensions.
|
/// This joint is designed for vehicle suspensions.
|
||||||
class b2WheelJoint : public b2Joint
|
class b2WheelJoint : public b2Joint
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
b2World::b2World(const b2Vec2& gravity)
|
b2World::b2World(const b2Vec2& gravity)
|
||||||
{
|
{
|
||||||
m_destructionListener = NULL;
|
m_destructionListener = NULL;
|
||||||
m_debugDraw = NULL;
|
g_debugDraw = NULL;
|
||||||
|
|
||||||
m_bodyList = NULL;
|
m_bodyList = NULL;
|
||||||
m_jointList = NULL;
|
m_jointList = NULL;
|
||||||
@@ -101,7 +101,7 @@ void b2World::SetContactListener(b2ContactListener* listener)
|
|||||||
|
|
||||||
void b2World::SetDebugDraw(b2Draw* debugDraw)
|
void b2World::SetDebugDraw(b2Draw* debugDraw)
|
||||||
{
|
{
|
||||||
m_debugDraw = debugDraw;
|
g_debugDraw = debugDraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
b2Body* b2World::CreateBody(const b2BodyDef* def)
|
b2Body* b2World::CreateBody(const b2BodyDef* def)
|
||||||
@@ -1040,7 +1040,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
|||||||
float32 radius = circle->m_radius;
|
float32 radius = circle->m_radius;
|
||||||
b2Vec2 axis = b2Mul(xf.q, b2Vec2(1.0f, 0.0f));
|
b2Vec2 axis = b2Mul(xf.q, b2Vec2(1.0f, 0.0f));
|
||||||
|
|
||||||
m_debugDraw->DrawSolidCircle(center, radius, axis, color);
|
g_debugDraw->DrawSolidCircle(center, radius, axis, color);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1049,7 +1049,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
|||||||
b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape();
|
b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape();
|
||||||
b2Vec2 v1 = b2Mul(xf, edge->m_vertex1);
|
b2Vec2 v1 = b2Mul(xf, edge->m_vertex1);
|
||||||
b2Vec2 v2 = b2Mul(xf, edge->m_vertex2);
|
b2Vec2 v2 = b2Mul(xf, edge->m_vertex2);
|
||||||
m_debugDraw->DrawSegment(v1, v2, color);
|
g_debugDraw->DrawSegment(v1, v2, color);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1063,8 +1063,8 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
|||||||
for (int32 i = 1; i < count; ++i)
|
for (int32 i = 1; i < count; ++i)
|
||||||
{
|
{
|
||||||
b2Vec2 v2 = b2Mul(xf, vertices[i]);
|
b2Vec2 v2 = b2Mul(xf, vertices[i]);
|
||||||
m_debugDraw->DrawSegment(v1, v2, color);
|
g_debugDraw->DrawSegment(v1, v2, color);
|
||||||
m_debugDraw->DrawCircle(v1, 0.05f, color);
|
g_debugDraw->DrawCircle(v1, 0.05f, color);
|
||||||
v1 = v2;
|
v1 = v2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1082,7 +1082,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
|||||||
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
|
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color);
|
g_debugDraw->DrawSolidPolygon(vertices, vertexCount, color);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1107,7 +1107,7 @@ void b2World::DrawJoint(b2Joint* joint)
|
|||||||
switch (joint->GetType())
|
switch (joint->GetType())
|
||||||
{
|
{
|
||||||
case e_distanceJoint:
|
case e_distanceJoint:
|
||||||
m_debugDraw->DrawSegment(p1, p2, color);
|
g_debugDraw->DrawSegment(p1, p2, color);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case e_pulleyJoint:
|
case e_pulleyJoint:
|
||||||
@@ -1115,9 +1115,9 @@ void b2World::DrawJoint(b2Joint* joint)
|
|||||||
b2PulleyJoint* pulley = (b2PulleyJoint*)joint;
|
b2PulleyJoint* pulley = (b2PulleyJoint*)joint;
|
||||||
b2Vec2 s1 = pulley->GetGroundAnchorA();
|
b2Vec2 s1 = pulley->GetGroundAnchorA();
|
||||||
b2Vec2 s2 = pulley->GetGroundAnchorB();
|
b2Vec2 s2 = pulley->GetGroundAnchorB();
|
||||||
m_debugDraw->DrawSegment(s1, p1, color);
|
g_debugDraw->DrawSegment(s1, p1, color);
|
||||||
m_debugDraw->DrawSegment(s2, p2, color);
|
g_debugDraw->DrawSegment(s2, p2, color);
|
||||||
m_debugDraw->DrawSegment(s1, s2, color);
|
g_debugDraw->DrawSegment(s1, s2, color);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1126,20 +1126,20 @@ void b2World::DrawJoint(b2Joint* joint)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m_debugDraw->DrawSegment(x1, p1, color);
|
g_debugDraw->DrawSegment(x1, p1, color);
|
||||||
m_debugDraw->DrawSegment(p1, p2, color);
|
g_debugDraw->DrawSegment(p1, p2, color);
|
||||||
m_debugDraw->DrawSegment(x2, p2, color);
|
g_debugDraw->DrawSegment(x2, p2, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void b2World::DrawDebugData()
|
void b2World::DrawDebugData()
|
||||||
{
|
{
|
||||||
if (m_debugDraw == NULL)
|
if (g_debugDraw == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 flags = m_debugDraw->GetFlags();
|
uint32 flags = g_debugDraw->GetFlags();
|
||||||
|
|
||||||
if (flags & b2Draw::e_shapeBit)
|
if (flags & b2Draw::e_shapeBit)
|
||||||
{
|
{
|
||||||
@@ -1191,7 +1191,7 @@ void b2World::DrawDebugData()
|
|||||||
//b2Vec2 cA = fixtureA->GetAABB().GetCenter();
|
//b2Vec2 cA = fixtureA->GetAABB().GetCenter();
|
||||||
//b2Vec2 cB = fixtureB->GetAABB().GetCenter();
|
//b2Vec2 cB = fixtureB->GetAABB().GetCenter();
|
||||||
|
|
||||||
//m_debugDraw->DrawSegment(cA, cB, color);
|
//g_debugDraw->DrawSegment(cA, cB, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1219,7 +1219,7 @@ void b2World::DrawDebugData()
|
|||||||
vs[2].Set(aabb.upperBound.x, aabb.upperBound.y);
|
vs[2].Set(aabb.upperBound.x, aabb.upperBound.y);
|
||||||
vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y);
|
vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y);
|
||||||
|
|
||||||
m_debugDraw->DrawPolygon(vs, 4, color);
|
g_debugDraw->DrawPolygon(vs, 4, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1231,7 +1231,7 @@ void b2World::DrawDebugData()
|
|||||||
{
|
{
|
||||||
b2Transform xf = b->GetTransform();
|
b2Transform xf = b->GetTransform();
|
||||||
xf.p = b->GetWorldCenter();
|
xf.p = b->GetWorldCenter();
|
||||||
m_debugDraw->DrawTransform(xf);
|
g_debugDraw->DrawTransform(xf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ private:
|
|||||||
bool m_allowSleep;
|
bool m_allowSleep;
|
||||||
|
|
||||||
b2DestructionListener* m_destructionListener;
|
b2DestructionListener* m_destructionListener;
|
||||||
b2Draw* m_debugDraw;
|
b2Draw* g_debugDraw;
|
||||||
|
|
||||||
// This is used to compute the time step ratio to
|
// This is used to compute the time step ratio to
|
||||||
// support a variable time step.
|
// support a variable time step.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h
|
/* stb_image - v2.07 - public domain image loader - http://nothings.org/stb_image.h
|
||||||
no warranty implied; use at your own risk
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
Do this:
|
Do this:
|
||||||
@@ -25,13 +25,16 @@
|
|||||||
|
|
||||||
TGA (not sure what subset, if a subset)
|
TGA (not sure what subset, if a subset)
|
||||||
BMP non-1bpp, non-RLE
|
BMP non-1bpp, non-RLE
|
||||||
PSD (composited view only, no extra channels)
|
PSD (composited view only, no extra channels, 8/16 bit-per-channel)
|
||||||
|
|
||||||
GIF (*comp always reports as 4-channel)
|
GIF (*comp always reports as 4-channel)
|
||||||
HDR (radiance rgbE format)
|
HDR (radiance rgbE format)
|
||||||
PIC (Softimage PIC)
|
PIC (Softimage PIC)
|
||||||
PNM (PPM and PGM binary only)
|
PNM (PPM and PGM binary only)
|
||||||
|
|
||||||
|
Animated GIF still needs a proper API, but here's one way to do it:
|
||||||
|
http://gist.github.com/urraka/685d9a6340b26b830d49
|
||||||
|
|
||||||
- decode from memory or through FILE (define STBI_NO_STDIO to remove code)
|
- decode from memory or through FILE (define STBI_NO_STDIO to remove code)
|
||||||
- decode from arbitrary I/O callbacks
|
- decode from arbitrary I/O callbacks
|
||||||
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
|
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
|
||||||
@@ -143,6 +146,10 @@
|
|||||||
|
|
||||||
|
|
||||||
Latest revision history:
|
Latest revision history:
|
||||||
|
2.07 (2015-09-13) partial animated GIF support
|
||||||
|
limited 16-bit PSD support
|
||||||
|
minor bugs, code cleanup, and compiler warnings
|
||||||
|
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
|
||||||
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
|
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
|
||||||
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
||||||
2.03 (2015-04-12) additional corruption checking
|
2.03 (2015-04-12) additional corruption checking
|
||||||
@@ -175,38 +182,41 @@
|
|||||||
Tom Seddon (pic) the Horde3D community
|
Tom Seddon (pic) the Horde3D community
|
||||||
Thatcher Ulrich (psd) Janez Zemva
|
Thatcher Ulrich (psd) Janez Zemva
|
||||||
Ken Miller (pgm, ppm) Jonathan Blow
|
Ken Miller (pgm, ppm) Jonathan Blow
|
||||||
Laurent Gomila
|
urraka@github (animated gif) Laurent Gomila
|
||||||
Aruelien Pocheville
|
Aruelien Pocheville
|
||||||
Extensions, features Ryamond Barbiero
|
Ryamond Barbiero
|
||||||
Jetro Lauha (stbi_info) David Woo
|
David Woo
|
||||||
Martin "SpartanJ" Golini (stbi_info) Martin Golini
|
Extensions, features Martin Golini
|
||||||
James "moose2000" Brown (iPhone PNG) Roy Eltham
|
Jetro Lauha (stbi_info) Roy Eltham
|
||||||
Ben "Disch" Wenger (io callbacks) Luke Graham
|
Martin "SpartanJ" Golini (stbi_info) Luke Graham
|
||||||
Omar Cornut (1/2/4-bit PNG) Thomas Ruf
|
James "moose2000" Brown (iPhone PNG) Thomas Ruf
|
||||||
Nicolas Guillemot (vertical flip) John Bartholomew
|
Ben "Disch" Wenger (io callbacks) John Bartholomew
|
||||||
Ken Hamada
|
Omar Cornut (1/2/4-bit PNG) Ken Hamada
|
||||||
Optimizations & bugfixes Cort Stratton
|
Nicolas Guillemot (vertical flip) Cort Stratton
|
||||||
Fabian "ryg" Giesen Blazej Dariusz Roszkowski
|
Richard Mitton (16-bit PSD) Blazej Dariusz Roszkowski
|
||||||
Arseny Kapoulkine Thibault Reuille
|
Thibault Reuille
|
||||||
Paul Du Bois
|
Paul Du Bois
|
||||||
Guillaume George
|
Guillaume George
|
||||||
If your name should be here but Jerry Jansson
|
Jerry Jansson
|
||||||
isn't, let Sean know. Hayaki Saito
|
Hayaki Saito
|
||||||
Johan Duparc
|
Johan Duparc
|
||||||
Ronny Chevalier
|
Ronny Chevalier
|
||||||
Michal Cichon
|
Optimizations & bugfixes Michal Cichon
|
||||||
Tero Hanninen
|
Fabian "ryg" Giesen Tero Hanninen
|
||||||
Sergio Gonzalez
|
Arseny Kapoulkine Sergio Gonzalez
|
||||||
Cass Everitt
|
Cass Everitt
|
||||||
Engin Manap
|
Engin Manap
|
||||||
Martins Mozeiko
|
If your name should be here but Martins Mozeiko
|
||||||
Joseph Thomson
|
isn't, let Sean know. Joseph Thomson
|
||||||
Phil Jordan
|
Phil Jordan
|
||||||
|
Nathan Reed
|
||||||
|
Michaelangel007@github
|
||||||
|
|
||||||
License:
|
LICENSE
|
||||||
This software is in the public domain. Where that dedication is not
|
|
||||||
recognized, you are granted a perpetual, irrevocable license to copy
|
This software is in the public domain. Where that dedication is not
|
||||||
and modify this file however you want.
|
recognized, you are granted a perpetual, irrevocable license to copy,
|
||||||
|
distribute, and modify this file as you see fit.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -748,7 +758,7 @@ typedef struct
|
|||||||
stbi_uc buffer_start[128];
|
stbi_uc buffer_start[128];
|
||||||
|
|
||||||
stbi_uc *img_buffer, *img_buffer_end;
|
stbi_uc *img_buffer, *img_buffer_end;
|
||||||
stbi_uc *img_buffer_original;
|
stbi_uc *img_buffer_original, *img_buffer_original_end;
|
||||||
} stbi__context;
|
} stbi__context;
|
||||||
|
|
||||||
|
|
||||||
@@ -760,7 +770,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
|
|||||||
s->io.read = NULL;
|
s->io.read = NULL;
|
||||||
s->read_from_callbacks = 0;
|
s->read_from_callbacks = 0;
|
||||||
s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
|
s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
|
||||||
s->img_buffer_end = (stbi_uc *) buffer+len;
|
s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize a callback-based context
|
// initialize a callback-based context
|
||||||
@@ -772,6 +782,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *
|
|||||||
s->read_from_callbacks = 1;
|
s->read_from_callbacks = 1;
|
||||||
s->img_buffer_original = s->buffer_start;
|
s->img_buffer_original = s->buffer_start;
|
||||||
stbi__refill_buffer(s);
|
stbi__refill_buffer(s);
|
||||||
|
s->img_buffer_original_end = s->img_buffer_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
#ifndef STBI_NO_STDIO
|
||||||
@@ -813,6 +824,7 @@ static void stbi__rewind(stbi__context *s)
|
|||||||
// but we just rewind to the beginning of the initial buffer, because
|
// but we just rewind to the beginning of the initial buffer, because
|
||||||
// we only use it after doing 'test', which only ever looks at at most 92 bytes
|
// we only use it after doing 'test', which only ever looks at at most 92 bytes
|
||||||
s->img_buffer = s->img_buffer_original;
|
s->img_buffer = s->img_buffer_original;
|
||||||
|
s->img_buffer_end = s->img_buffer_original_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef STBI_NO_JPEG
|
#ifndef STBI_NO_JPEG
|
||||||
@@ -900,8 +912,8 @@ static void *stbi__malloc(size_t size)
|
|||||||
#define stbi__err(x,y) stbi__err(x)
|
#define stbi__err(x,y) stbi__err(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL))
|
#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
|
||||||
#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL))
|
#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
|
||||||
|
|
||||||
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
|
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
|
||||||
{
|
{
|
||||||
@@ -988,6 +1000,7 @@ static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *com
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef STBI_NO_HDR
|
||||||
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
|
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
|
||||||
{
|
{
|
||||||
if (stbi__vertically_flip_on_load && result != NULL) {
|
if (stbi__vertically_flip_on_load && result != NULL) {
|
||||||
@@ -1008,7 +1021,7 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef STBI_NO_STDIO
|
#ifndef STBI_NO_STDIO
|
||||||
|
|
||||||
@@ -1152,6 +1165,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f)
|
|||||||
stbi__start_file(&s,f);
|
stbi__start_file(&s,f);
|
||||||
return stbi__hdr_test(&s);
|
return stbi__hdr_test(&s);
|
||||||
#else
|
#else
|
||||||
|
STBI_NOTUSED(f);
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1164,6 +1178,8 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void
|
|||||||
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
|
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
|
||||||
return stbi__hdr_test(&s);
|
return stbi__hdr_test(&s);
|
||||||
#else
|
#else
|
||||||
|
STBI_NOTUSED(clbk);
|
||||||
|
STBI_NOTUSED(user);
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1278,23 +1294,29 @@ static int stbi__get16be(stbi__context *s)
|
|||||||
return (z << 8) + stbi__get8(s);
|
return (z << 8) + stbi__get8(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(STBI_NO_PNG) || !defined(STBI_NO_PSD)
|
||||||
static stbi__uint32 stbi__get32be(stbi__context *s)
|
static stbi__uint32 stbi__get32be(stbi__context *s)
|
||||||
{
|
{
|
||||||
stbi__uint32 z = stbi__get16be(s);
|
stbi__uint32 z = stbi__get16be(s);
|
||||||
return (z << 16) + stbi__get16be(s);
|
return (z << 16) + stbi__get16be(s);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(STBI_NO_BMP) || !defined(STBI_NO_TGA) || !defined(STBI_NO_GIF)
|
||||||
static int stbi__get16le(stbi__context *s)
|
static int stbi__get16le(stbi__context *s)
|
||||||
{
|
{
|
||||||
int z = stbi__get8(s);
|
int z = stbi__get8(s);
|
||||||
return z + (stbi__get8(s) << 8);
|
return z + (stbi__get8(s) << 8);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBI_NO_BMP
|
||||||
static stbi__uint32 stbi__get32le(stbi__context *s)
|
static stbi__uint32 stbi__get32le(stbi__context *s)
|
||||||
{
|
{
|
||||||
stbi__uint32 z = stbi__get16le(s);
|
stbi__uint32 z = stbi__get16le(s);
|
||||||
return z + (stbi__get16le(s) << 16);
|
return z + (stbi__get16le(s) << 16);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
|
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
|
||||||
|
|
||||||
@@ -2738,7 +2760,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
|
|||||||
if (z->img_comp[i].raw_data == NULL) {
|
if (z->img_comp[i].raw_data == NULL) {
|
||||||
for(--i; i >= 0; --i) {
|
for(--i; i >= 0; --i) {
|
||||||
STBI_FREE(z->img_comp[i].raw_data);
|
STBI_FREE(z->img_comp[i].raw_data);
|
||||||
z->img_comp[i].data = NULL;
|
z->img_comp[i].raw_data = NULL;
|
||||||
}
|
}
|
||||||
return stbi__err("outofmem", "Out of memory");
|
return stbi__err("outofmem", "Out of memory");
|
||||||
}
|
}
|
||||||
@@ -3500,10 +3522,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
|
|||||||
z->size [c] = (stbi_uc ) s;
|
z->size [c] = (stbi_uc ) s;
|
||||||
z->value[c] = (stbi__uint16) i;
|
z->value[c] = (stbi__uint16) i;
|
||||||
if (s <= STBI__ZFAST_BITS) {
|
if (s <= STBI__ZFAST_BITS) {
|
||||||
int k = stbi__bit_reverse(next_code[s],s);
|
int j = stbi__bit_reverse(next_code[s],s);
|
||||||
while (k < (1 << STBI__ZFAST_BITS)) {
|
while (j < (1 << STBI__ZFAST_BITS)) {
|
||||||
z->fast[k] = fastv;
|
z->fast[j] = fastv;
|
||||||
k += (1 << s);
|
j += (1 << s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++next_code[s];
|
++next_code[s];
|
||||||
@@ -3542,7 +3564,7 @@ static void stbi__fill_bits(stbi__zbuf *z)
|
|||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
|
STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
|
||||||
z->code_buffer |= stbi__zget8(z) << z->num_bits;
|
z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
|
||||||
z->num_bits += 8;
|
z->num_bits += 8;
|
||||||
} while (z->num_bits <= 24);
|
} while (z->num_bits <= 24);
|
||||||
}
|
}
|
||||||
@@ -4108,21 +4130,21 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
|||||||
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
||||||
}
|
}
|
||||||
if (img_n != out_n) {
|
if (img_n != out_n) {
|
||||||
|
int q;
|
||||||
// insert alpha = 255
|
// insert alpha = 255
|
||||||
stbi_uc *cur = a->out + stride*j;
|
cur = a->out + stride*j;
|
||||||
int i;
|
|
||||||
if (img_n == 1) {
|
if (img_n == 1) {
|
||||||
for (i=x-1; i >= 0; --i) {
|
for (q=x-1; q >= 0; --q) {
|
||||||
cur[i*2+1] = 255;
|
cur[q*2+1] = 255;
|
||||||
cur[i*2+0] = cur[i];
|
cur[q*2+0] = cur[q];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
STBI_ASSERT(img_n == 3);
|
STBI_ASSERT(img_n == 3);
|
||||||
for (i=x-1; i >= 0; --i) {
|
for (q=x-1; q >= 0; --q) {
|
||||||
cur[i*4+3] = 255;
|
cur[q*4+3] = 255;
|
||||||
cur[i*4+2] = cur[i*3+2];
|
cur[q*4+2] = cur[q*3+2];
|
||||||
cur[i*4+1] = cur[i*3+1];
|
cur[q*4+1] = cur[q*3+1];
|
||||||
cur[i*4+0] = cur[i*3+0];
|
cur[q*4+0] = cur[q*3+0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4577,7 +4599,7 @@ static int stbi__shiftsigned(int v, int shift, int bits)
|
|||||||
static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
|
static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
|
||||||
{
|
{
|
||||||
stbi_uc *out;
|
stbi_uc *out;
|
||||||
unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
|
unsigned int mr=0,mg=0,mb=0,ma=0, all_a=255;
|
||||||
stbi_uc pal[256][4];
|
stbi_uc pal[256][4];
|
||||||
int psize=0,i,j,compress=0,width;
|
int psize=0,i,j,compress=0,width;
|
||||||
int bpp, flip_vertically, pad, target, offset, hsz;
|
int bpp, flip_vertically, pad, target, offset, hsz;
|
||||||
@@ -4626,8 +4648,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
mg = 0xffu << 8;
|
mg = 0xffu << 8;
|
||||||
mb = 0xffu << 0;
|
mb = 0xffu << 0;
|
||||||
ma = 0xffu << 24;
|
ma = 0xffu << 24;
|
||||||
fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
|
all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
|
||||||
STBI_NOTUSED(fake_a);
|
|
||||||
} else {
|
} else {
|
||||||
mr = 31u << 10;
|
mr = 31u << 10;
|
||||||
mg = 31u << 5;
|
mg = 31u << 5;
|
||||||
@@ -4738,6 +4759,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
out[z+0] = stbi__get8(s);
|
out[z+0] = stbi__get8(s);
|
||||||
z += 3;
|
z += 3;
|
||||||
a = (easy == 2 ? stbi__get8(s) : 255);
|
a = (easy == 2 ? stbi__get8(s) : 255);
|
||||||
|
all_a |= a;
|
||||||
if (target == 4) out[z++] = a;
|
if (target == 4) out[z++] = a;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -4748,12 +4770,19 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
|
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
|
||||||
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
|
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
|
||||||
a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
|
a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
|
||||||
|
all_a |= a;
|
||||||
if (target == 4) out[z++] = STBI__BYTECAST(a);
|
if (target == 4) out[z++] = STBI__BYTECAST(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stbi__skip(s, pad);
|
stbi__skip(s, pad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if alpha channel is all 0s, replace with all 255s
|
||||||
|
if (target == 4 && all_a == 0)
|
||||||
|
for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
|
||||||
|
out[i] = 255;
|
||||||
|
|
||||||
if (flip_vertically) {
|
if (flip_vertically) {
|
||||||
stbi_uc t;
|
stbi_uc t;
|
||||||
for (j=0; j < (int) s->img_y>>1; ++j) {
|
for (j=0; j < (int) s->img_y>>1; ++j) {
|
||||||
@@ -4907,8 +4936,8 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
|
|
||||||
if ( !tga_indexed && !tga_is_RLE) {
|
if ( !tga_indexed && !tga_is_RLE) {
|
||||||
for (i=0; i < tga_height; ++i) {
|
for (i=0; i < tga_height; ++i) {
|
||||||
int y = tga_inverted ? tga_height -i - 1 : i;
|
int row = tga_inverted ? tga_height -i - 1 : i;
|
||||||
stbi_uc *tga_row = tga_data + y*tga_width*tga_comp;
|
stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
|
||||||
stbi__getn(s, tga_row, tga_width * tga_comp);
|
stbi__getn(s, tga_row, tga_width * tga_comp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -5053,6 +5082,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
int pixelCount;
|
int pixelCount;
|
||||||
int channelCount, compression;
|
int channelCount, compression;
|
||||||
int channel, i, count, len;
|
int channel, i, count, len;
|
||||||
|
int bitdepth;
|
||||||
int w,h;
|
int w,h;
|
||||||
stbi_uc *out;
|
stbi_uc *out;
|
||||||
|
|
||||||
@@ -5077,8 +5107,9 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
w = stbi__get32be(s);
|
w = stbi__get32be(s);
|
||||||
|
|
||||||
// Make sure the depth is 8 bits.
|
// Make sure the depth is 8 bits.
|
||||||
if (stbi__get16be(s) != 8)
|
bitdepth = stbi__get16be(s);
|
||||||
return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit");
|
if (bitdepth != 8 && bitdepth != 16)
|
||||||
|
return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
|
||||||
|
|
||||||
// Make sure the color mode is RGB.
|
// Make sure the color mode is RGB.
|
||||||
// Valid options are:
|
// Valid options are:
|
||||||
@@ -5139,7 +5170,8 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
p = out+channel;
|
p = out+channel;
|
||||||
if (channel >= channelCount) {
|
if (channel >= channelCount) {
|
||||||
// Fill this channel with default data.
|
// Fill this channel with default data.
|
||||||
for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4;
|
for (i = 0; i < pixelCount; i++, p += 4)
|
||||||
|
*p = (channel == 3 ? 255 : 0);
|
||||||
} else {
|
} else {
|
||||||
// Read the RLE data.
|
// Read the RLE data.
|
||||||
count = 0;
|
count = 0;
|
||||||
@@ -5185,11 +5217,17 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
p = out + channel;
|
p = out + channel;
|
||||||
if (channel > channelCount) {
|
if (channel > channelCount) {
|
||||||
// Fill this channel with default data.
|
// Fill this channel with default data.
|
||||||
for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4;
|
for (i = 0; i < pixelCount; i++, p += 4)
|
||||||
|
*p = channel == 3 ? 255 : 0;
|
||||||
} else {
|
} else {
|
||||||
// Read the data.
|
// Read the data.
|
||||||
for (i = 0; i < pixelCount; i++)
|
if (bitdepth == 16) {
|
||||||
*p = stbi__get8(s), p += 4;
|
for (i = 0; i < pixelCount; i++, p += 4)
|
||||||
|
*p = stbi__get16be(s) >> 8;
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < pixelCount; i++, p += 4)
|
||||||
|
*p = stbi__get8(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5199,7 +5237,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
if (out == NULL) return out; // stbi__convert_format frees input on failure
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comp) *comp = channelCount;
|
if (comp) *comp = 4;
|
||||||
*y = h;
|
*y = h;
|
||||||
*x = w;
|
*x = w;
|
||||||
|
|
||||||
@@ -5347,7 +5385,6 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c
|
|||||||
|
|
||||||
if (count >= 128) { // Repeated
|
if (count >= 128) { // Repeated
|
||||||
stbi_uc value[4];
|
stbi_uc value[4];
|
||||||
int i;
|
|
||||||
|
|
||||||
if (count==128)
|
if (count==128)
|
||||||
count = stbi__get16be(s);
|
count = stbi__get16be(s);
|
||||||
@@ -5435,8 +5472,8 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int w,h;
|
int w,h;
|
||||||
stbi_uc *out; // output buffer (always 4 components)
|
stbi_uc *out, *old_out; // output buffer (always 4 components)
|
||||||
int flags, bgindex, ratio, transparent, eflags;
|
int flags, bgindex, ratio, transparent, eflags, delay;
|
||||||
stbi_uc pal[256][4];
|
stbi_uc pal[256][4];
|
||||||
stbi_uc lpal[256][4];
|
stbi_uc lpal[256][4];
|
||||||
stbi__gif_lzw codes[4096];
|
stbi__gif_lzw codes[4096];
|
||||||
@@ -5554,7 +5591,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
|
|||||||
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
|
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
|
||||||
{
|
{
|
||||||
stbi_uc lzw_cs;
|
stbi_uc lzw_cs;
|
||||||
stbi__int32 len, code;
|
stbi__int32 len, init_code;
|
||||||
stbi__uint32 first;
|
stbi__uint32 first;
|
||||||
stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
|
stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
|
||||||
stbi__gif_lzw *p;
|
stbi__gif_lzw *p;
|
||||||
@@ -5567,10 +5604,10 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
|
|||||||
codemask = (1 << codesize) - 1;
|
codemask = (1 << codesize) - 1;
|
||||||
bits = 0;
|
bits = 0;
|
||||||
valid_bits = 0;
|
valid_bits = 0;
|
||||||
for (code = 0; code < clear; code++) {
|
for (init_code = 0; init_code < clear; init_code++) {
|
||||||
g->codes[code].prefix = -1;
|
g->codes[init_code].prefix = -1;
|
||||||
g->codes[code].first = (stbi_uc) code;
|
g->codes[init_code].first = (stbi_uc) init_code;
|
||||||
g->codes[code].suffix = (stbi_uc) code;
|
g->codes[init_code].suffix = (stbi_uc) init_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
// support no starting clear code
|
// support no starting clear code
|
||||||
@@ -5631,17 +5668,18 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stbi__fill_gif_background(stbi__gif *g)
|
static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1)
|
||||||
{
|
{
|
||||||
int i;
|
int x, y;
|
||||||
stbi_uc *c = g->pal[g->bgindex];
|
stbi_uc *c = g->pal[g->bgindex];
|
||||||
// @OPTIMIZE: write a dword at a time
|
for (y = y0; y < y1; y += 4 * g->w) {
|
||||||
for (i = 0; i < g->w * g->h * 4; i += 4) {
|
for (x = x0; x < x1; x += 4) {
|
||||||
stbi_uc *p = &g->out[i];
|
stbi_uc *p = &g->out[y + x];
|
||||||
p[0] = c[2];
|
p[0] = c[2];
|
||||||
p[1] = c[1];
|
p[1] = c[1];
|
||||||
p[2] = c[0];
|
p[2] = c[0];
|
||||||
p[3] = c[3];
|
p[3] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5649,27 +5687,40 @@ static void stbi__fill_gif_background(stbi__gif *g)
|
|||||||
static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp)
|
static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
stbi_uc *old_out = 0;
|
stbi_uc *prev_out = 0;
|
||||||
|
|
||||||
if (g->out == 0) {
|
if (g->out == 0 && !stbi__gif_header(s, g, comp,0))
|
||||||
if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
|
return 0; // stbi__g_failure_reason set by stbi__gif_header
|
||||||
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
|
|
||||||
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
|
prev_out = g->out;
|
||||||
stbi__fill_gif_background(g);
|
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
|
||||||
} else {
|
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
|
||||||
// animated-gif-only path
|
|
||||||
if (((g->eflags & 0x1C) >> 2) == 3) {
|
switch ((g->eflags & 0x1C) >> 2) {
|
||||||
old_out = g->out;
|
case 0: // unspecified (also always used on 1st frame)
|
||||||
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
|
stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h);
|
||||||
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
|
break;
|
||||||
memcpy(g->out, old_out, g->w*g->h*4);
|
case 1: // do not dispose
|
||||||
}
|
if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);
|
||||||
|
g->old_out = prev_out;
|
||||||
|
break;
|
||||||
|
case 2: // dispose to background
|
||||||
|
if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);
|
||||||
|
stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y);
|
||||||
|
break;
|
||||||
|
case 3: // dispose to previous
|
||||||
|
if (g->old_out) {
|
||||||
|
for (i = g->start_y; i < g->max_y; i += 4 * g->w)
|
||||||
|
memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (stbi__get8(s)) {
|
switch (stbi__get8(s)) {
|
||||||
case 0x2C: /* Image Descriptor */
|
case 0x2C: /* Image Descriptor */
|
||||||
{
|
{
|
||||||
|
int prev_trans = -1;
|
||||||
stbi__int32 x, y, w, h;
|
stbi__int32 x, y, w, h;
|
||||||
stbi_uc *o;
|
stbi_uc *o;
|
||||||
|
|
||||||
@@ -5702,10 +5753,10 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
|||||||
stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
|
stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
|
||||||
g->color_table = (stbi_uc *) g->lpal;
|
g->color_table = (stbi_uc *) g->lpal;
|
||||||
} else if (g->flags & 0x80) {
|
} else if (g->flags & 0x80) {
|
||||||
for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent
|
if (g->transparent >= 0 && (g->eflags & 0x01)) {
|
||||||
g->pal[i][3] = 255;
|
prev_trans = g->pal[g->transparent][3];
|
||||||
if (g->transparent >= 0 && (g->eflags & 0x01))
|
|
||||||
g->pal[g->transparent][3] = 0;
|
g->pal[g->transparent][3] = 0;
|
||||||
|
}
|
||||||
g->color_table = (stbi_uc *) g->pal;
|
g->color_table = (stbi_uc *) g->pal;
|
||||||
} else
|
} else
|
||||||
return stbi__errpuc("missing color table", "Corrupt GIF");
|
return stbi__errpuc("missing color table", "Corrupt GIF");
|
||||||
@@ -5713,8 +5764,9 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
|||||||
o = stbi__process_gif_raster(s, g);
|
o = stbi__process_gif_raster(s, g);
|
||||||
if (o == NULL) return NULL;
|
if (o == NULL) return NULL;
|
||||||
|
|
||||||
if (req_comp && req_comp != 4)
|
if (prev_trans != -1)
|
||||||
o = stbi__convert_format(o, 4, req_comp, g->w, g->h);
|
g->pal[g->transparent][3] = prev_trans;
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5725,7 +5777,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
|
|||||||
len = stbi__get8(s);
|
len = stbi__get8(s);
|
||||||
if (len == 4) {
|
if (len == 4) {
|
||||||
g->eflags = stbi__get8(s);
|
g->eflags = stbi__get8(s);
|
||||||
stbi__get16le(s); // delay
|
g->delay = stbi__get16le(s);
|
||||||
g->transparent = stbi__get8(s);
|
g->transparent = stbi__get8(s);
|
||||||
} else {
|
} else {
|
||||||
stbi__skip(s, len);
|
stbi__skip(s, len);
|
||||||
@@ -5757,7 +5809,11 @@ static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int
|
|||||||
if (u) {
|
if (u) {
|
||||||
*x = g.w;
|
*x = g.w;
|
||||||
*y = g.h;
|
*y = g.h;
|
||||||
|
if (req_comp && req_comp != 4)
|
||||||
|
u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
|
||||||
}
|
}
|
||||||
|
else if (g.out)
|
||||||
|
STBI_FREE(g.out);
|
||||||
|
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
@@ -6059,14 +6115,22 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
|
|||||||
int act_comp=0,num_packets=0,chained;
|
int act_comp=0,num_packets=0,chained;
|
||||||
stbi__pic_packet packets[10];
|
stbi__pic_packet packets[10];
|
||||||
|
|
||||||
stbi__skip(s, 92);
|
if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
|
||||||
|
stbi__rewind(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi__skip(s, 88);
|
||||||
|
|
||||||
*x = stbi__get16be(s);
|
*x = stbi__get16be(s);
|
||||||
*y = stbi__get16be(s);
|
*y = stbi__get16be(s);
|
||||||
if (stbi__at_eof(s)) return 0;
|
if (stbi__at_eof(s)) {
|
||||||
|
stbi__rewind( s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
|
if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
|
||||||
stbi__rewind( s );
|
stbi__rewind( s );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stbi__skip(s, 8);
|
stbi__skip(s, 8);
|
||||||
@@ -6292,6 +6356,12 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
revision history:
|
revision history:
|
||||||
|
2.07 (2015-09-13) fix compiler warnings
|
||||||
|
partial animated GIF support
|
||||||
|
limited 16-bit PSD support
|
||||||
|
#ifdef unused functions
|
||||||
|
bug with < 92 byte PIC,PNM,HDR,TGA
|
||||||
|
2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
|
||||||
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
|
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
|
||||||
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
||||||
2.03 (2015-04-12) extra corruption checking (mmozeiko)
|
2.03 (2015-04-12) extra corruption checking (mmozeiko)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ extern "C" {
|
|||||||
|
|
||||||
#ifdef LOVE_MACOSX
|
#ifdef LOVE_MACOSX
|
||||||
#include "common/macosx.h"
|
#include "common/macosx.h"
|
||||||
|
#include <unistd.h>
|
||||||
#endif // LOVE_MACOSX
|
#endif // LOVE_MACOSX
|
||||||
|
|
||||||
#ifdef LOVE_IOS
|
#ifdef LOVE_IOS
|
||||||
@@ -110,12 +111,16 @@ static void get_app_arguments(int argc, char **argv, int &new_argc, char **&new_
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LOVE_MACOSX
|
#ifdef LOVE_MACOSX
|
||||||
// Check for a drop file string.
|
// Check for a drop file string, if the app wasn't launched in a terminal.
|
||||||
std::string dropfilestr = love::macosx::checkDropEvents();
|
// Checking for the terminal is a pretty big hack, but works around an issue
|
||||||
|
// where OS X will switch Spaces if the terminal launching love is in its
|
||||||
|
// own full-screen Space.
|
||||||
|
std::string dropfilestr;
|
||||||
|
if (!isatty(STDIN_FILENO))
|
||||||
|
dropfilestr = love::macosx::checkDropEvents();
|
||||||
|
|
||||||
if (!dropfilestr.empty())
|
if (!dropfilestr.empty())
|
||||||
{
|
|
||||||
temp_argv.insert(temp_argv.begin() + 1, dropfilestr);
|
temp_argv.insert(temp_argv.begin() + 1, dropfilestr);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
TYPE_STATIC,
|
TYPE_STATIC,
|
||||||
TYPE_STREAM,
|
TYPE_STREAM,
|
||||||
TYPE_MAX_ENUM
|
TYPE_MAX_ENUM
|
||||||
}; // Type
|
};
|
||||||
|
|
||||||
enum Unit
|
enum Unit
|
||||||
{
|
{
|
||||||
@@ -71,6 +71,7 @@ public:
|
|||||||
|
|
||||||
virtual void seek(float offset, Unit unit) = 0;
|
virtual void seek(float offset, Unit unit) = 0;
|
||||||
virtual float tell(Unit unit) = 0;
|
virtual float tell(Unit unit) = 0;
|
||||||
|
virtual double getDuration(Unit unit) = 0;
|
||||||
|
|
||||||
// all float * v must be of size 3
|
// all float * v must be of size 3
|
||||||
virtual void setPosition(float *v) = 0;
|
virtual void setPosition(float *v) = 0;
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ float Source::tell(Source::Unit)
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Source::getDuration(Unit)
|
||||||
|
{
|
||||||
|
return -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void Source::setPosition(float *)
|
void Source::setPosition(float *)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public:
|
|||||||
virtual float getVolume() const;
|
virtual float getVolume() const;
|
||||||
virtual void seek(float offset, Unit unit);
|
virtual void seek(float offset, Unit unit);
|
||||||
virtual float tell(Unit unit);
|
virtual float tell(Unit unit);
|
||||||
|
virtual double getDuration(Unit unit);
|
||||||
virtual void setPosition(float *v);
|
virtual void setPosition(float *v);
|
||||||
virtual void getPosition(float *v) const;
|
virtual void getPosition(float *v) const;
|
||||||
virtual void setVelocity(float *v);
|
virtual void setVelocity(float *v);
|
||||||
|
|||||||
@@ -263,6 +263,12 @@ float Pool::tell(Source *source, void *unit)
|
|||||||
return source->tellAtomic(unit);
|
return source->tellAtomic(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Pool::getDuration(Source *source, void *unit)
|
||||||
|
{
|
||||||
|
thread::Lock lock(mutex);
|
||||||
|
return source->getDurationAtomic(unit);
|
||||||
|
}
|
||||||
|
|
||||||
ALuint Pool::findi(const Source *source) const
|
ALuint Pool::findi(const Source *source) const
|
||||||
{
|
{
|
||||||
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
|
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public:
|
|||||||
void softRewind(Source *source);
|
void softRewind(Source *source);
|
||||||
void seek(Source *source, float offset, void *unit);
|
void seek(Source *source, float offset, void *unit);
|
||||||
float tell(Source *source, void *unit);
|
float tell(Source *source, void *unit);
|
||||||
|
double getDuration(Source *source, void *unit);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ Ensure the Source is not multi-channel before calling this function.")
|
|||||||
};
|
};
|
||||||
|
|
||||||
StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
|
StaticDataBuffer::StaticDataBuffer(ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
|
||||||
|
: size(size)
|
||||||
{
|
{
|
||||||
alGenBuffers(1, &buffer);
|
alGenBuffers(1, &buffer);
|
||||||
alBufferData(buffer, format, data, size, freq);
|
alBufferData(buffer, format, data, size, freq);
|
||||||
@@ -95,6 +96,7 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
|
|||||||
, offsetSeconds(0)
|
, offsetSeconds(0)
|
||||||
, sampleRate(soundData->getSampleRate())
|
, sampleRate(soundData->getSampleRate())
|
||||||
, channels(soundData->getChannels())
|
, channels(soundData->getChannels())
|
||||||
|
, bitDepth(soundData->getBitDepth())
|
||||||
, decoder(nullptr)
|
, decoder(nullptr)
|
||||||
, toLoop(0)
|
, toLoop(0)
|
||||||
{
|
{
|
||||||
@@ -134,6 +136,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
|
|||||||
, offsetSeconds(0)
|
, offsetSeconds(0)
|
||||||
, sampleRate(decoder->getSampleRate())
|
, sampleRate(decoder->getSampleRate())
|
||||||
, channels(decoder->getChannels())
|
, channels(decoder->getChannels())
|
||||||
|
, bitDepth(decoder->getBitDepth())
|
||||||
, decoder(decoder)
|
, decoder(decoder)
|
||||||
, toLoop(0)
|
, toLoop(0)
|
||||||
{
|
{
|
||||||
@@ -169,6 +172,7 @@ Source::Source(const Source &s)
|
|||||||
, offsetSeconds(0)
|
, offsetSeconds(0)
|
||||||
, sampleRate(s.sampleRate)
|
, sampleRate(s.sampleRate)
|
||||||
, channels(s.channels)
|
, channels(s.channels)
|
||||||
|
, bitDepth(s.bitDepth)
|
||||||
, decoder(nullptr)
|
, decoder(nullptr)
|
||||||
, toLoop(0)
|
, toLoop(0)
|
||||||
{
|
{
|
||||||
@@ -308,8 +312,11 @@ bool Source::update()
|
|||||||
offsetSamples += (curOffsetSamples - newOffsetSamples);
|
offsetSamples += (curOffsetSamples - newOffsetSamples);
|
||||||
offsetSeconds += (curOffsetSecs - newOffsetSecs);
|
offsetSeconds += (curOffsetSecs - newOffsetSecs);
|
||||||
|
|
||||||
streamAtomic(buffer, decoder.get());
|
// FIXME: We should put freed buffers into a list that we later
|
||||||
alSourceQueueBuffers(source, 1, &buffer);
|
// consume here, so we can keep track of all free buffers even if we
|
||||||
|
// tried to stream data to one but the decoder didn't have data for it.
|
||||||
|
if (streamAtomic(buffer, decoder.get()) > 0)
|
||||||
|
alSourceQueueBuffers(source, 1, &buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -438,6 +445,36 @@ float Source::tell(Source::Unit unit)
|
|||||||
return pool->tell(this, &unit);
|
return pool->tell(this, &unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Source::getDurationAtomic(void *vunit)
|
||||||
|
{
|
||||||
|
Unit unit = *(Unit *) vunit;
|
||||||
|
|
||||||
|
if (type == TYPE_STREAM)
|
||||||
|
{
|
||||||
|
double seconds = decoder->getDuration();
|
||||||
|
|
||||||
|
if (unit == UNIT_SECONDS)
|
||||||
|
return seconds;
|
||||||
|
else
|
||||||
|
return seconds * decoder->getSampleRate();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ALsizei size = staticBuffer->getSize();
|
||||||
|
ALsizei samples = (size / channels) / (bitDepth / 8);
|
||||||
|
|
||||||
|
if (unit == UNIT_SAMPLES)
|
||||||
|
return (double) samples;
|
||||||
|
else
|
||||||
|
return (double) samples / (double) sampleRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double Source::getDuration(Unit unit)
|
||||||
|
{
|
||||||
|
return pool->getDuration(this, &unit);
|
||||||
|
}
|
||||||
|
|
||||||
void Source::setPosition(float *v)
|
void Source::setPosition(float *v)
|
||||||
{
|
{
|
||||||
if (channels > 1)
|
if (channels > 1)
|
||||||
@@ -575,8 +612,11 @@ bool Source::playAtomic()
|
|||||||
|
|
||||||
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
|
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
|
||||||
{
|
{
|
||||||
streamAtomic(streamBuffers[i], decoder.get());
|
if (streamAtomic(streamBuffers[i], decoder.get()) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
++usedBuffers;
|
++usedBuffers;
|
||||||
|
|
||||||
if (decoder->isFinished())
|
if (decoder->isFinished())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -736,13 +776,18 @@ ALenum Source::getFormat(int channels, int bitDepth) const
|
|||||||
int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
|
int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
|
||||||
{
|
{
|
||||||
// Get more sound data.
|
// Get more sound data.
|
||||||
int decoded = d->decode();
|
int decoded = std::max(d->decode(), 0);
|
||||||
decoded = decoded >= 0 ? decoded : 0;
|
|
||||||
|
|
||||||
int fmt = getFormat(d->getChannels(), d->getBitDepth());
|
// OpenAL implementations are allowed to ignore 0-size alBufferData calls.
|
||||||
|
if (decoded > 0)
|
||||||
|
{
|
||||||
|
int fmt = getFormat(d->getChannels(), d->getBitDepth());
|
||||||
|
|
||||||
if (fmt != 0)
|
if (fmt != 0)
|
||||||
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
|
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
|
||||||
|
else
|
||||||
|
decoded = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (decoder->isFinished() && isLooping())
|
if (decoder->isFinished() && isLooping())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,9 +65,15 @@ public:
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ALsizei getSize() const
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ALuint buffer;
|
ALuint buffer;
|
||||||
|
ALsizei size;
|
||||||
|
|
||||||
}; // StaticDataBuffer
|
}; // StaticDataBuffer
|
||||||
|
|
||||||
@@ -98,6 +104,8 @@ public:
|
|||||||
virtual void seek(float offset, Unit unit);
|
virtual void seek(float offset, Unit unit);
|
||||||
virtual float tellAtomic(void *unit) const;
|
virtual float tellAtomic(void *unit) const;
|
||||||
virtual float tell(Unit unit);
|
virtual float tell(Unit unit);
|
||||||
|
virtual double getDurationAtomic(void *unit);
|
||||||
|
virtual double getDuration(Unit unit);
|
||||||
virtual void setPosition(float *v);
|
virtual void setPosition(float *v);
|
||||||
virtual void getPosition(float *v) const;
|
virtual void getPosition(float *v) const;
|
||||||
virtual void setVelocity(float *v);
|
virtual void setVelocity(float *v);
|
||||||
@@ -180,6 +188,7 @@ private:
|
|||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int channels;
|
int channels;
|
||||||
|
int bitDepth;
|
||||||
|
|
||||||
StrongRef<love::sound::Decoder> decoder;
|
StrongRef<love::sound::Decoder> decoder;
|
||||||
|
|
||||||
|
|||||||
@@ -32,29 +32,6 @@ namespace love
|
|||||||
namespace audio
|
namespace audio
|
||||||
{
|
{
|
||||||
|
|
||||||
int w_getSourceCount(lua_State *L);
|
|
||||||
int w_newSource(lua_State *L);
|
|
||||||
int w_play(lua_State *L);
|
|
||||||
int w_stop(lua_State *L);
|
|
||||||
int w_pause(lua_State *L);
|
|
||||||
int w_resume(lua_State *L);
|
|
||||||
int w_rewind(lua_State *L);
|
|
||||||
int w_setVolume(lua_State *L);
|
|
||||||
int w_getVolume(lua_State *L);
|
|
||||||
int w_setPosition(lua_State *L);
|
|
||||||
int w_getPosition(lua_State *L);
|
|
||||||
int w_setOrientation(lua_State *L);
|
|
||||||
int w_getOrientation(lua_State *L);
|
|
||||||
int w_setVelocity(lua_State *L);
|
|
||||||
int w_getVelocity(lua_State *L);
|
|
||||||
int w_setDopplerScale(lua_State *L);
|
|
||||||
int w_getDopplerScale(lua_State *L);
|
|
||||||
int w_record(lua_State *L);
|
|
||||||
int w_getRecordedData(lua_State *L);
|
|
||||||
int w_stopRecording(lua_State *L);
|
|
||||||
int w_canRecord(lua_State *L);
|
|
||||||
int w_setDistanceModel(lua_State *L);
|
|
||||||
int w_getDistanceModel(lua_State *L);
|
|
||||||
extern "C" LOVE_EXPORT int luaopen_love_audio(lua_State *L);
|
extern "C" LOVE_EXPORT int luaopen_love_audio(lua_State *L);
|
||||||
|
|
||||||
} // audio
|
} // audio
|
||||||
|
|||||||
@@ -140,6 +140,19 @@ int w_Source_tell(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_Source_getDuration(lua_State *L)
|
||||||
|
{
|
||||||
|
Source *t = luax_checksource(L, 1);
|
||||||
|
|
||||||
|
Source::Unit u = Source::UNIT_SECONDS;
|
||||||
|
const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
|
||||||
|
if (unit && !t->getConstant(unit, u))
|
||||||
|
return luaL_error(L, "Invalid Source time unit: %s", unit);
|
||||||
|
|
||||||
|
lua_pushnumber(L, t->getDuration(u));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_Source_setPosition(lua_State *L)
|
int w_Source_setPosition(lua_State *L)
|
||||||
{
|
{
|
||||||
Source *t = luax_checksource(L, 1);
|
Source *t = luax_checksource(L, 1);
|
||||||
@@ -357,7 +370,7 @@ int w_Source_getType(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Source_functions[] =
|
||||||
{
|
{
|
||||||
{ "clone", w_Source_clone },
|
{ "clone", w_Source_clone },
|
||||||
|
|
||||||
@@ -373,6 +386,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "getVolume", w_Source_getVolume },
|
{ "getVolume", w_Source_getVolume },
|
||||||
{ "seek", w_Source_seek },
|
{ "seek", w_Source_seek },
|
||||||
{ "tell", w_Source_tell },
|
{ "tell", w_Source_tell },
|
||||||
|
{ "getDuration", w_Source_getDuration },
|
||||||
{ "setPosition", w_Source_setPosition },
|
{ "setPosition", w_Source_setPosition },
|
||||||
{ "getPosition", w_Source_getPosition },
|
{ "getPosition", w_Source_getPosition },
|
||||||
{ "setVelocity", w_Source_setVelocity },
|
{ "setVelocity", w_Source_setVelocity },
|
||||||
@@ -406,7 +420,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_source(lua_State *L)
|
extern "C" int luaopen_source(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, AUDIO_SOURCE_ID, functions);
|
return luax_register_type(L, AUDIO_SOURCE_ID, "Source", w_Source_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // audio
|
} // audio
|
||||||
|
|||||||
@@ -30,41 +30,6 @@ namespace audio
|
|||||||
{
|
{
|
||||||
|
|
||||||
Source *luax_checksource(lua_State *L, int idx);
|
Source *luax_checksource(lua_State *L, int idx);
|
||||||
int w_Source_clone(lua_State *L);
|
|
||||||
int w_Source_play(lua_State *L);
|
|
||||||
int w_Source_stop(lua_State *L);
|
|
||||||
int w_Source_pause(lua_State *L);
|
|
||||||
int w_Source_resume(lua_State *L);
|
|
||||||
int w_Source_rewind(lua_State *L);
|
|
||||||
int w_Source_setPitch(lua_State *L);
|
|
||||||
int w_Source_getPitch(lua_State *L);
|
|
||||||
int w_Source_setVolume(lua_State *L);
|
|
||||||
int w_Source_getVolume(lua_State *L);
|
|
||||||
int w_Source_seek(lua_State *L);
|
|
||||||
int w_Source_tell(lua_State *L);
|
|
||||||
int w_Source_setPosition(lua_State *L);
|
|
||||||
int w_Source_getPosition(lua_State *L);
|
|
||||||
int w_Source_setVelocity(lua_State *L);
|
|
||||||
int w_Source_getVelocity(lua_State *L);
|
|
||||||
int w_Source_setDirection(lua_State *L);
|
|
||||||
int w_Source_getDirection(lua_State *L);
|
|
||||||
int w_Source_setCone(lua_State *L);
|
|
||||||
int w_Source_getCone(lua_State *L);
|
|
||||||
int w_Source_setRelative(lua_State *L);
|
|
||||||
int w_Source_isRelative(lua_State *L);
|
|
||||||
int w_Source_setLooping(lua_State *L);
|
|
||||||
int w_Source_isLooping(lua_State *L);
|
|
||||||
int w_Source_isStopped(lua_State *L);
|
|
||||||
int w_Source_isPaused(lua_State *L);
|
|
||||||
int w_Source_isPlaying(lua_State *L);
|
|
||||||
int w_Source_setVolumeLimits(lua_State *L);
|
|
||||||
int w_Source_getVolumeLimits(lua_State *L);
|
|
||||||
int w_Source_setAttenuationDistances(lua_State *L);
|
|
||||||
int w_Source_getAttenuationDistances(lua_State *L);
|
|
||||||
int w_Source_setRolloff(lua_State *L);
|
|
||||||
int w_Source_getRolloff(lua_State *L);
|
|
||||||
int w_Source_getChannels(lua_State *L);
|
|
||||||
int w_Source_getType(lua_State *L);
|
|
||||||
extern "C" int luaopen_source(lua_State *L);
|
extern "C" int luaopen_source(lua_State *L);
|
||||||
|
|
||||||
} // audio
|
} // audio
|
||||||
|
|||||||
@@ -556,12 +556,6 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
|
|||||||
{
|
{
|
||||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||||
// Users won't expect the screensaver to activate if a game is in
|
|
||||||
// focus. Also, joystick input may not delay the screensaver timer.
|
|
||||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
|
||||||
SDL_DisableScreenSaver();
|
|
||||||
else
|
|
||||||
SDL_EnableScreenSaver();
|
|
||||||
vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED));
|
vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED));
|
||||||
msg = new Message("focus", vargs);
|
msg = new Message("focus", vargs);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ extern "C" int luaopen_love_event(lua_State *L)
|
|||||||
w.name = "event";
|
w.name = "event";
|
||||||
w.type = MODULE_ID;
|
w.type = MODULE_ID;
|
||||||
w.functions = functions;
|
w.functions = functions;
|
||||||
w.types = 0;
|
w.types = nullptr;
|
||||||
|
|
||||||
return luax_register_module(L, w);
|
return luax_register_module(L, w);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,13 +30,6 @@ namespace love
|
|||||||
namespace event
|
namespace event
|
||||||
{
|
{
|
||||||
|
|
||||||
int w_pump(lua_State *L);
|
|
||||||
int w_poll(lua_State *L);
|
|
||||||
int w_wait(lua_State *L);
|
|
||||||
int w_push(lua_State *L);
|
|
||||||
int w_clear(lua_State *L);
|
|
||||||
int w_quit(lua_State *L);
|
|
||||||
|
|
||||||
extern "C" LOVE_EXPORT int luaopen_love_event(lua_State *L);
|
extern "C" LOVE_EXPORT int luaopen_love_event(lua_State *L);
|
||||||
|
|
||||||
} // event
|
} // event
|
||||||
|
|||||||
@@ -125,12 +125,8 @@ void Filesystem::init(const char *arg0)
|
|||||||
if (!PHYSFS_init(arg0))
|
if (!PHYSFS_init(arg0))
|
||||||
throw love::Exception("%s", PHYSFS_getLastError());
|
throw love::Exception("%s", PHYSFS_getLastError());
|
||||||
|
|
||||||
PHYSFS_Version version = {};
|
// Enable symlinks by default. Also fixes an issue in PhysFS 2.1-alpha.
|
||||||
PHYSFS_getLinkedVersion(&version);
|
setSymlinksEnabled(true);
|
||||||
|
|
||||||
// FIXME: This is a workaround for a bug in PHYSFS_enumerateFiles in 2.1-alpha.
|
|
||||||
if (version.major == 2 && version.minor == 1)
|
|
||||||
PHYSFS_permitSymbolicLinks(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filesystem::setFused(bool fused)
|
void Filesystem::setFused(bool fused)
|
||||||
@@ -223,7 +219,7 @@ bool Filesystem::setSource(const char *source)
|
|||||||
std::string new_search_path = source;
|
std::string new_search_path = source;
|
||||||
|
|
||||||
#ifdef LOVE_ANDROID
|
#ifdef LOVE_ANDROID
|
||||||
if (!love::android::createStorageDirectories ())
|
if (!love::android::createStorageDirectories())
|
||||||
SDL_Log("Error creating storage directories!");
|
SDL_Log("Error creating storage directories!");
|
||||||
|
|
||||||
char* game_archive_ptr = NULL;
|
char* game_archive_ptr = NULL;
|
||||||
@@ -664,6 +660,17 @@ int64 Filesystem::getSize(const char *filename) const
|
|||||||
|
|
||||||
void Filesystem::setSymlinksEnabled(bool enable)
|
void Filesystem::setSymlinksEnabled(bool enable)
|
||||||
{
|
{
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
PHYSFS_Version version = {};
|
||||||
|
PHYSFS_getLinkedVersion(&version);
|
||||||
|
|
||||||
|
// FIXME: This is a workaround for a bug in PHYSFS_enumerateFiles in
|
||||||
|
// PhysFS 2.1-alpha.
|
||||||
|
if (version.major == 2 && version.minor == 1)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PHYSFS_permitSymbolicLinks(enable ? 1 : 0);
|
PHYSFS_permitSymbolicLinks(enable ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,31 +31,9 @@ DroppedFile *luax_checkdroppedfile(lua_State *L, int idx)
|
|||||||
return luax_checktype<DroppedFile>(L, idx, FILESYSTEM_DROPPED_FILE_ID);
|
return luax_checktype<DroppedFile>(L, idx, FILESYSTEM_DROPPED_FILE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
|
||||||
{
|
|
||||||
// Inherits from File.
|
|
||||||
{ "getSize", w_File_getSize },
|
|
||||||
{ "open", w_File_open },
|
|
||||||
{ "close", w_File_close },
|
|
||||||
{ "isOpen", w_File_isOpen },
|
|
||||||
{ "read", w_File_read },
|
|
||||||
{ "write", w_File_write },
|
|
||||||
{ "flush", w_File_flush },
|
|
||||||
{ "isEOF", w_File_isEOF },
|
|
||||||
{ "tell", w_File_tell },
|
|
||||||
{ "seek", w_File_seek },
|
|
||||||
{ "lines", w_File_lines },
|
|
||||||
{ "setBuffer", w_File_setBuffer },
|
|
||||||
{ "getBuffer", w_File_getBuffer },
|
|
||||||
{ "getMode", w_File_getMode },
|
|
||||||
{ "getFilename", w_File_getFilename },
|
|
||||||
{ "getExtension", w_File_getExtension },
|
|
||||||
{ 0, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" int luaopen_droppedfile(lua_State *L)
|
extern "C" int luaopen_droppedfile(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, FILESYSTEM_DROPPED_FILE_ID, functions);
|
return luax_register_type(L, FILESYSTEM_DROPPED_FILE_ID, "DroppedFile", w_File_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // filesystem
|
} // filesystem
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ int w_File_getExtension(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
const luaL_Reg w_File_functions[] =
|
||||||
{
|
{
|
||||||
{ "getSize", w_File_getSize },
|
{ "getSize", w_File_getSize },
|
||||||
{ "open", w_File_open },
|
{ "open", w_File_open },
|
||||||
@@ -436,7 +436,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_file(lua_State *L)
|
extern "C" int luaopen_file(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, FILESYSTEM_FILE_ID, functions);
|
return luax_register_type(L, FILESYSTEM_FILE_ID, "File", w_File_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // filesystem
|
} // filesystem
|
||||||
|
|||||||
@@ -34,25 +34,11 @@ namespace filesystem
|
|||||||
int luax_ioError(lua_State *L, const char *fmt, ...);
|
int luax_ioError(lua_State *L, const char *fmt, ...);
|
||||||
|
|
||||||
File *luax_checkfile(lua_State *L, int idx);
|
File *luax_checkfile(lua_State *L, int idx);
|
||||||
int w_File_getSize(lua_State *L);
|
|
||||||
int w_File_open(lua_State *L);
|
|
||||||
int w_File_close(lua_State *L);
|
|
||||||
int w_File_isOpen(lua_State *L);
|
|
||||||
int w_File_read(lua_State *L);
|
|
||||||
int w_File_write(lua_State *L);
|
|
||||||
int w_File_flush(lua_State *L);
|
|
||||||
int w_File_isEOF(lua_State *L);
|
|
||||||
int w_File_tell(lua_State *L);
|
|
||||||
int w_File_seek(lua_State *L);
|
|
||||||
int w_File_lines_i(lua_State *L);
|
int w_File_lines_i(lua_State *L);
|
||||||
int w_File_lines(lua_State *L);
|
|
||||||
int w_File_setBuffer(lua_State *L);
|
|
||||||
int w_File_getBuffer(lua_State *L);
|
|
||||||
int w_File_getMode(lua_State *L);
|
|
||||||
int w_File_getFilename(lua_State *L);
|
|
||||||
int w_File_getExtension(lua_State *L);
|
|
||||||
extern "C" int luaopen_file(lua_State *L);
|
extern "C" int luaopen_file(lua_State *L);
|
||||||
|
|
||||||
|
extern const luaL_Reg w_File_functions[];
|
||||||
|
|
||||||
} // filesystem
|
} // filesystem
|
||||||
} // love
|
} // love
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,6 @@ int w_FileData_getExtension(lua_State *L)
|
|||||||
|
|
||||||
static const luaL_Reg w_FileData_functions[] =
|
static const luaL_Reg w_FileData_functions[] =
|
||||||
{
|
{
|
||||||
// Data
|
|
||||||
{ "getString", w_Data_getString },
|
|
||||||
{ "getPointer", w_Data_getPointer },
|
|
||||||
{ "getSize", w_Data_getSize },
|
|
||||||
|
|
||||||
{ "getFilename", w_FileData_getFilename },
|
{ "getFilename", w_FileData_getFilename },
|
||||||
{ "getExtension", w_FileData_getExtension },
|
{ "getExtension", w_FileData_getExtension },
|
||||||
|
|
||||||
@@ -61,7 +56,7 @@ static const luaL_Reg w_FileData_functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_filedata(lua_State *L)
|
extern "C" int luaopen_filedata(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, FILESYSTEM_FILE_DATA_ID, w_FileData_functions);
|
return luax_register_type(L, FILESYSTEM_FILE_DATA_ID, "FileData", w_Data_functions, w_FileData_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // filesystem
|
} // filesystem
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ namespace filesystem
|
|||||||
{
|
{
|
||||||
|
|
||||||
FileData *luax_checkfiledata(lua_State *L, int idx);
|
FileData *luax_checkfiledata(lua_State *L, int idx);
|
||||||
int w_FileData_getFilename(lua_State *L);
|
|
||||||
int w_FileData_getExtension(lua_State *L);
|
|
||||||
extern "C" int luaopen_filedata(lua_State *L);
|
extern "C" int luaopen_filedata(lua_State *L);
|
||||||
|
|
||||||
} // filesystem
|
} // filesystem
|
||||||
|
|||||||
@@ -155,19 +155,28 @@ int w_newFile(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData *luax_getfiledata(lua_State *L, int idx)
|
File *luax_getfile(lua_State *L, int idx)
|
||||||
{
|
{
|
||||||
FileData *data = nullptr;
|
|
||||||
File *file = nullptr;
|
File *file = nullptr;
|
||||||
|
|
||||||
if (lua_isstring(L, idx))
|
if (lua_isstring(L, idx))
|
||||||
{
|
{
|
||||||
const char *filename = luaL_checkstring(L, idx);
|
const char *filename = luaL_checkstring(L, idx);
|
||||||
file = instance()->newFile(filename);
|
file = instance()->newFile(filename);
|
||||||
}
|
}
|
||||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_ID))
|
else
|
||||||
{
|
|
||||||
file = luax_checkfile(L, idx);
|
file = luax_checkfile(L, idx);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileData *luax_getfiledata(lua_State *L, int idx)
|
||||||
|
{
|
||||||
|
FileData *data = nullptr;
|
||||||
|
File *file = nullptr;
|
||||||
|
|
||||||
|
if (lua_isstring(L, idx) || luax_istype(L, idx, FILESYSTEM_FILE_ID))
|
||||||
|
{
|
||||||
|
file = luax_getfile(L, idx);
|
||||||
file->retain();
|
file->retain();
|
||||||
}
|
}
|
||||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
|
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/runtime.h"
|
#include "common/runtime.h"
|
||||||
|
#include "File.h"
|
||||||
#include "FileData.h"
|
#include "FileData.h"
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
@@ -38,45 +39,9 @@ namespace filesystem
|
|||||||
* May trigger a Lua error.
|
* May trigger a Lua error.
|
||||||
**/
|
**/
|
||||||
FileData *luax_getfiledata(lua_State *L, int idx);
|
FileData *luax_getfiledata(lua_State *L, int idx);
|
||||||
|
File *luax_getfile(lua_State *L, int idx);
|
||||||
|
|
||||||
bool hack_setupWriteDirectory();
|
bool hack_setupWriteDirectory();
|
||||||
int w_init(lua_State *L);
|
|
||||||
int w_setFused(lua_State *L);
|
|
||||||
int w_isFused(lua_State *L);
|
|
||||||
int w_setIdentity(lua_State *L);
|
|
||||||
int w_getIdentity(lua_State *L);
|
|
||||||
int w_setSource(lua_State *L);
|
|
||||||
int w_getSource(lua_State *L);
|
|
||||||
int w_mount(lua_State *L);
|
|
||||||
int w_unmount(lua_State *L);
|
|
||||||
int w_newFile(lua_State *L);
|
|
||||||
int w_newFileData(lua_State *L);
|
|
||||||
int w_getWorkingDirectory(lua_State *L);
|
|
||||||
int w_getUserDirectory(lua_State *L);
|
|
||||||
int w_getAppdataDirectory(lua_State *L);
|
|
||||||
int w_getSaveDirectory(lua_State *L);
|
|
||||||
int w_getSourceBaseDirectory(lua_State *L);
|
|
||||||
int w_getRealDirectory(lua_State *L);
|
|
||||||
int w_getExecutablePath(lua_State *L);
|
|
||||||
int w_isDirectory(lua_State *L);
|
|
||||||
int w_isFile(lua_State *L);
|
|
||||||
int w_createDirectory(lua_State *L);
|
|
||||||
int w_remove(lua_State *L);
|
|
||||||
int w_open(lua_State *L);
|
|
||||||
int w_close(lua_State *L);
|
|
||||||
int w_read(lua_State *L);
|
|
||||||
int w_write(lua_State *L);
|
|
||||||
int w_append(lua_State *L);
|
|
||||||
int w_getDirectoryItems(lua_State *L);
|
|
||||||
int w_lines(lua_State *L);
|
|
||||||
int w_load(lua_State *L);
|
|
||||||
int w_getLastModified(lua_State *L);
|
|
||||||
int w_getSize(lua_State *L);
|
|
||||||
int w_setSymlinksEnabled(lua_State *L);
|
|
||||||
int w_areSymlinksEnabled(lua_State *L);
|
|
||||||
int w_isSymlink(lua_State *L);
|
|
||||||
int w_getRequirePath(lua_State *L);
|
|
||||||
int w_setRequirePath(lua_State *L);
|
|
||||||
int loader(lua_State *L);
|
int loader(lua_State *L);
|
||||||
int extloader(lua_State *L);
|
int extloader(lua_State *L);
|
||||||
extern "C" LOVE_EXPORT int luaopen_love_filesystem(lua_State *L);
|
extern "C" LOVE_EXPORT int luaopen_love_filesystem(lua_State *L);
|
||||||
|
|||||||
@@ -116,9 +116,9 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
// We treat the luminance of the FreeType bitmap as alpha in the GlyphData.
|
// We treat the luminance of the FreeType bitmap as alpha in the GlyphData.
|
||||||
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < bitmap.rows; y++)
|
for (int y = 0; y < (int) bitmap.rows; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < bitmap.width; x++)
|
for (int x = 0; x < (int) bitmap.width; x++)
|
||||||
{
|
{
|
||||||
// Extract the 1-bit value and convert it to uint8.
|
// Extract the 1-bit value and convert it to uint8.
|
||||||
uint8 v = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0;
|
uint8 v = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0;
|
||||||
@@ -131,9 +131,9 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
}
|
}
|
||||||
else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
|
else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < bitmap.rows; y++)
|
for (int y = 0; y < (int) bitmap.rows; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < bitmap.width; x++)
|
for (int x = 0; x < (int) bitmap.width; x++)
|
||||||
{
|
{
|
||||||
dest[2 * (y * bitmap.width + x) + 0] = 255;
|
dest[2 * (y * bitmap.width + x) + 0] = 255;
|
||||||
dest[2 * (y * bitmap.width + x) + 1] = pixels[x];
|
dest[2 * (y * bitmap.width + x) + 1] = pixels[x];
|
||||||
|
|||||||
@@ -115,13 +115,8 @@ int w_GlyphData_getFormat(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
const luaL_Reg w_GlyphData_functions[] =
|
||||||
{
|
{
|
||||||
// Data
|
|
||||||
{ "getString", w_Data_getString },
|
|
||||||
{ "getPointer", w_Data_getPointer },
|
|
||||||
{ "getSize", w_Data_getSize },
|
|
||||||
|
|
||||||
{ "getWidth", w_GlyphData_getWidth },
|
{ "getWidth", w_GlyphData_getWidth },
|
||||||
{ "getHeight", w_GlyphData_getHeight },
|
{ "getHeight", w_GlyphData_getHeight },
|
||||||
{ "getDimensions", w_GlyphData_getDimensions },
|
{ "getDimensions", w_GlyphData_getDimensions },
|
||||||
@@ -136,7 +131,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_glyphdata(lua_State *L)
|
extern "C" int luaopen_glyphdata(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, FONT_GLYPH_DATA_ID, functions);
|
return luax_register_type(L, FONT_GLYPH_DATA_ID, "GlyphData", w_Data_functions, w_GlyphData_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // font
|
} // font
|
||||||
|
|||||||
@@ -33,15 +33,6 @@ namespace font
|
|||||||
{
|
{
|
||||||
|
|
||||||
GlyphData *luax_checkglyphdata(lua_State *L, int idx);
|
GlyphData *luax_checkglyphdata(lua_State *L, int idx);
|
||||||
int w_GlyphData_getWidth(lua_State *L);
|
|
||||||
int w_GlyphData_getHeight(lua_State *L);
|
|
||||||
int w_GlyphData_getDimensions(lua_State *L);
|
|
||||||
int w_GlyphData_getGlyph(lua_State *L);
|
|
||||||
int w_GlyphData_getGlyphString(lua_State *L);
|
|
||||||
int w_GlyphData_getAdvance(lua_State *L);
|
|
||||||
int w_GlyphData_getBearing(lua_State *L);
|
|
||||||
int w_GlyphData_getBoundingBox(lua_State *L);
|
|
||||||
int w_GlyphData_getFormat(lua_State *L);
|
|
||||||
extern "C" int luaopen_glyphdata(lua_State *L);
|
extern "C" int luaopen_glyphdata(lua_State *L);
|
||||||
|
|
||||||
} // font
|
} // font
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ int w_Rasterizer_hasGlyphs(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
const luaL_Reg w_Rasterizer_functions[] =
|
||||||
{
|
{
|
||||||
{ "getHeight", w_Rasterizer_getHeight },
|
{ "getHeight", w_Rasterizer_getHeight },
|
||||||
{ "getAdvance", w_Rasterizer_getAdvance },
|
{ "getAdvance", w_Rasterizer_getAdvance },
|
||||||
@@ -139,7 +139,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_rasterizer(lua_State *L)
|
extern "C" int luaopen_rasterizer(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, FONT_RASTERIZER_ID, functions);
|
return luax_register_type(L, FONT_RASTERIZER_ID, "Rasterizer", w_Rasterizer_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // font
|
} // font
|
||||||
|
|||||||
@@ -31,14 +31,6 @@ namespace font
|
|||||||
{
|
{
|
||||||
|
|
||||||
Rasterizer *luax_checkrasterizer(lua_State *L, int idx);
|
Rasterizer *luax_checkrasterizer(lua_State *L, int idx);
|
||||||
int w_Rasterizer_getHeight(lua_State *L);
|
|
||||||
int w_Rasterizer_getAdvance(lua_State *L);
|
|
||||||
int w_Rasterizer_getAscent(lua_State *L);
|
|
||||||
int w_Rasterizer_getDescent(lua_State *L);
|
|
||||||
int w_Rasterizer_getLineHeight(lua_State *L);
|
|
||||||
int w_Rasterizer_getGlyphData(lua_State *L);
|
|
||||||
int w_Rasterizer_getGlyphCount(lua_State *L);
|
|
||||||
int w_Rasterizer_hasGlyphs(lua_State *L);
|
|
||||||
extern "C" int luaopen_rasterizer(lua_State *L);
|
extern "C" int luaopen_rasterizer(lua_State *L);
|
||||||
|
|
||||||
} // font
|
} // font
|
||||||
|
|||||||
@@ -44,11 +44,26 @@ struct ColorT
|
|||||||
a = a_;
|
a = a_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator==(const ColorT<T> &other) const;
|
||||||
|
bool operator!=(const ColorT<T> &other) const;
|
||||||
|
|
||||||
ColorT<T> operator+=(const ColorT<T> &other);
|
ColorT<T> operator+=(const ColorT<T> &other);
|
||||||
ColorT<T> operator*=(T s);
|
ColorT<T> operator*=(T s);
|
||||||
ColorT<T> operator/=(T s);
|
ColorT<T> operator/=(T s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool ColorT<T>::operator==(const ColorT<T> &other) const
|
||||||
|
{
|
||||||
|
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool ColorT<T>::operator!=(const ColorT<T> &other) const
|
||||||
|
{
|
||||||
|
return !(operator==(other));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ColorT<T> ColorT<T>::operator+=(const ColorT<T> &other)
|
ColorT<T> ColorT<T>::operator+=(const ColorT<T> &other)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(
|
|||||||
|
|
||||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
|
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
|
||||||
{
|
{
|
||||||
{ "multicanvas", SUPPORT_MULTI_CANVAS },
|
|
||||||
{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
|
{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
|
||||||
|
{ "clampzero", SUPPORT_CLAMP_ZERO },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ public:
|
|||||||
|
|
||||||
enum Support
|
enum Support
|
||||||
{
|
{
|
||||||
SUPPORT_MULTI_CANVAS,
|
|
||||||
SUPPORT_MULTI_CANVAS_FORMATS,
|
SUPPORT_MULTI_CANVAS_FORMATS,
|
||||||
|
SUPPORT_CLAMP_ZERO,
|
||||||
SUPPORT_MAX_ENUM
|
SUPPORT_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,6 +178,17 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ScissorRect
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
bool operator == (const ScissorRect &rhs) const
|
||||||
|
{
|
||||||
|
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~Graphics();
|
virtual ~Graphics();
|
||||||
|
|
||||||
// Implements Module.
|
// Implements Module.
|
||||||
|
|||||||
@@ -0,0 +1,986 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
//LOVE
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "ParticleSystem.h"
|
||||||
|
|
||||||
|
#include "common/math.h"
|
||||||
|
#include "modules/math/RandomGenerator.h"
|
||||||
|
|
||||||
|
// STD
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace graphics
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
love::math::RandomGenerator rng;
|
||||||
|
|
||||||
|
float calculate_variation(float inner, float outer, float var)
|
||||||
|
{
|
||||||
|
float low = inner - (outer/2.0f)*var;
|
||||||
|
float high = inner + (outer/2.0f)*var;
|
||||||
|
float r = (float) rng.random();
|
||||||
|
return low*(1-r)+high*r;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||||
|
: pMem(nullptr)
|
||||||
|
, pFree(nullptr)
|
||||||
|
, pHead(nullptr)
|
||||||
|
, pTail(nullptr)
|
||||||
|
, texture(texture)
|
||||||
|
, active(true)
|
||||||
|
, insertMode(INSERT_MODE_TOP)
|
||||||
|
, maxParticles(0)
|
||||||
|
, activeParticles(0)
|
||||||
|
, emissionRate(0)
|
||||||
|
, emitCounter(0)
|
||||||
|
, areaSpreadDistribution(DISTRIBUTION_NONE)
|
||||||
|
, lifetime(-1)
|
||||||
|
, life(0)
|
||||||
|
, particleLifeMin(0)
|
||||||
|
, particleLifeMax(0)
|
||||||
|
, direction(0)
|
||||||
|
, spread(0)
|
||||||
|
, speedMin(0)
|
||||||
|
, speedMax(0)
|
||||||
|
, linearAccelerationMin(0, 0)
|
||||||
|
, linearAccelerationMax(0, 0)
|
||||||
|
, radialAccelerationMin(0)
|
||||||
|
, radialAccelerationMax(0)
|
||||||
|
, tangentialAccelerationMin(0)
|
||||||
|
, tangentialAccelerationMax(0)
|
||||||
|
, linearDampingMin(0.0f)
|
||||||
|
, linearDampingMax(0.0f)
|
||||||
|
, sizeVariation(0)
|
||||||
|
, rotationMin(0)
|
||||||
|
, rotationMax(0)
|
||||||
|
, spinStart(0)
|
||||||
|
, spinEnd(0)
|
||||||
|
, spinVariation(0)
|
||||||
|
, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
|
||||||
|
, defaultOffset(true)
|
||||||
|
, relativeRotation(false)
|
||||||
|
{
|
||||||
|
if (size == 0 || size > MAX_PARTICLES)
|
||||||
|
throw love::Exception("Invalid ParticleSystem size.");
|
||||||
|
|
||||||
|
sizes.push_back(1.0f);
|
||||||
|
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
|
||||||
|
setBufferSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||||
|
: pMem(nullptr)
|
||||||
|
, pFree(nullptr)
|
||||||
|
, pHead(nullptr)
|
||||||
|
, pTail(nullptr)
|
||||||
|
, texture(p.texture)
|
||||||
|
, active(p.active)
|
||||||
|
, insertMode(p.insertMode)
|
||||||
|
, maxParticles(p.maxParticles)
|
||||||
|
, activeParticles(0)
|
||||||
|
, emissionRate(p.emissionRate)
|
||||||
|
, emitCounter(0.0f)
|
||||||
|
, position(p.position)
|
||||||
|
, prevPosition(p.prevPosition)
|
||||||
|
, areaSpreadDistribution(p.areaSpreadDistribution)
|
||||||
|
, areaSpread(p.areaSpread)
|
||||||
|
, lifetime(p.lifetime)
|
||||||
|
, life(p.lifetime) // Initialize with the maximum life time.
|
||||||
|
, particleLifeMin(p.particleLifeMin)
|
||||||
|
, particleLifeMax(p.particleLifeMax)
|
||||||
|
, direction(p.direction)
|
||||||
|
, spread(p.spread)
|
||||||
|
, speedMin(p.speedMin)
|
||||||
|
, speedMax(p.speedMax)
|
||||||
|
, linearAccelerationMin(p.linearAccelerationMin)
|
||||||
|
, linearAccelerationMax(p.linearAccelerationMax)
|
||||||
|
, radialAccelerationMin(p.radialAccelerationMin)
|
||||||
|
, radialAccelerationMax(p.radialAccelerationMax)
|
||||||
|
, tangentialAccelerationMin(p.tangentialAccelerationMin)
|
||||||
|
, tangentialAccelerationMax(p.tangentialAccelerationMax)
|
||||||
|
, linearDampingMin(p.linearDampingMin)
|
||||||
|
, linearDampingMax(p.linearDampingMax)
|
||||||
|
, sizes(p.sizes)
|
||||||
|
, sizeVariation(p.sizeVariation)
|
||||||
|
, rotationMin(p.rotationMin)
|
||||||
|
, rotationMax(p.rotationMax)
|
||||||
|
, spinStart(p.spinStart)
|
||||||
|
, spinEnd(p.spinEnd)
|
||||||
|
, spinVariation(p.spinVariation)
|
||||||
|
, offset(p.offset)
|
||||||
|
, defaultOffset(p.defaultOffset)
|
||||||
|
, colors(p.colors)
|
||||||
|
, quads(p.quads)
|
||||||
|
, relativeRotation(p.relativeRotation)
|
||||||
|
{
|
||||||
|
setBufferSize(maxParticles);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem::~ParticleSystem()
|
||||||
|
{
|
||||||
|
deleteBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::resetOffset()
|
||||||
|
{
|
||||||
|
if (quads.empty())
|
||||||
|
offset = love::Vector(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Quad::Viewport v = quads[0]->getViewport();
|
||||||
|
offset = love::Vector(v.x*0.5f, v.y*0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::createBuffers(size_t size)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pFree = pMem = new Particle[size];
|
||||||
|
maxParticles = (uint32) size;
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc &)
|
||||||
|
{
|
||||||
|
deleteBuffers();
|
||||||
|
throw love::Exception("Out of memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::deleteBuffers()
|
||||||
|
{
|
||||||
|
// Clean up for great gracefulness!
|
||||||
|
delete[] pMem;
|
||||||
|
|
||||||
|
pMem = nullptr;
|
||||||
|
maxParticles = 0;
|
||||||
|
activeParticles = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setBufferSize(uint32 size)
|
||||||
|
{
|
||||||
|
if (size == 0 || size > MAX_PARTICLES)
|
||||||
|
throw love::Exception("Invalid buffer size");
|
||||||
|
deleteBuffers();
|
||||||
|
createBuffers(size);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 ParticleSystem::getBufferSize() const
|
||||||
|
{
|
||||||
|
return maxParticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::addParticle(float t)
|
||||||
|
{
|
||||||
|
if (isFull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Gets a free particle and updates the allocation pointer.
|
||||||
|
Particle *p = pFree++;
|
||||||
|
initParticle(p, t);
|
||||||
|
|
||||||
|
switch (insertMode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case INSERT_MODE_TOP:
|
||||||
|
insertTop(p);
|
||||||
|
break;
|
||||||
|
case INSERT_MODE_BOTTOM:
|
||||||
|
insertBottom(p);
|
||||||
|
break;
|
||||||
|
case INSERT_MODE_RANDOM:
|
||||||
|
insertRandom(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeParticles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::initParticle(Particle *p, float t)
|
||||||
|
{
|
||||||
|
float min,max;
|
||||||
|
|
||||||
|
// Linearly interpolate between the previous and current emitter position.
|
||||||
|
love::Vector pos = prevPosition + (position - prevPosition) * t;
|
||||||
|
|
||||||
|
min = particleLifeMin;
|
||||||
|
max = particleLifeMax;
|
||||||
|
if (min == max)
|
||||||
|
p->life = min;
|
||||||
|
else
|
||||||
|
p->life = (float) rng.random(min, max);
|
||||||
|
p->lifetime = p->life;
|
||||||
|
|
||||||
|
p->position = pos;
|
||||||
|
|
||||||
|
switch (areaSpreadDistribution)
|
||||||
|
{
|
||||||
|
case DISTRIBUTION_UNIFORM:
|
||||||
|
p->position.x += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
|
||||||
|
p->position.y += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
|
||||||
|
break;
|
||||||
|
case DISTRIBUTION_NORMAL:
|
||||||
|
p->position.x += (float) rng.randomNormal(areaSpread.getX());
|
||||||
|
p->position.y += (float) rng.randomNormal(areaSpread.getY());
|
||||||
|
break;
|
||||||
|
case DISTRIBUTION_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->origin = pos;
|
||||||
|
|
||||||
|
min = speedMin;
|
||||||
|
max = speedMax;
|
||||||
|
float speed = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
min = direction - spread/2.0f;
|
||||||
|
max = direction + spread/2.0f;
|
||||||
|
float dir = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
p->velocity = love::Vector(cosf(dir), sinf(dir)) * speed;
|
||||||
|
|
||||||
|
p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
|
||||||
|
p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
|
||||||
|
|
||||||
|
min = radialAccelerationMin;
|
||||||
|
max = radialAccelerationMax;
|
||||||
|
p->radialAcceleration = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
min = tangentialAccelerationMin;
|
||||||
|
max = tangentialAccelerationMax;
|
||||||
|
p->tangentialAcceleration = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
min = linearDampingMin;
|
||||||
|
max = linearDampingMax;
|
||||||
|
p->linearDamping = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change
|
||||||
|
p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
|
||||||
|
p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
|
||||||
|
|
||||||
|
min = rotationMin;
|
||||||
|
max = rotationMax;
|
||||||
|
p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
|
||||||
|
p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
|
||||||
|
p->rotation = (float) rng.random(min, max);
|
||||||
|
|
||||||
|
p->angle = p->rotation;
|
||||||
|
if (relativeRotation)
|
||||||
|
p->angle += atan2f(p->velocity.y, p->velocity.x);
|
||||||
|
|
||||||
|
p->color = colors[0];
|
||||||
|
|
||||||
|
p->quadIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::insertTop(Particle *p)
|
||||||
|
{
|
||||||
|
if (pHead == nullptr)
|
||||||
|
{
|
||||||
|
pHead = p;
|
||||||
|
p->prev = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pTail->next = p;
|
||||||
|
p->prev = pTail;
|
||||||
|
}
|
||||||
|
p->next = nullptr;
|
||||||
|
pTail = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::insertBottom(Particle *p)
|
||||||
|
{
|
||||||
|
if (pTail == nullptr)
|
||||||
|
{
|
||||||
|
pTail = p;
|
||||||
|
p->next = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pHead->prev = p;
|
||||||
|
p->next = pHead;
|
||||||
|
}
|
||||||
|
p->prev = nullptr;
|
||||||
|
pHead = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::insertRandom(Particle *p)
|
||||||
|
{
|
||||||
|
// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
|
||||||
|
uint64 pos = rng.rand() % ((int64) activeParticles + 1);
|
||||||
|
|
||||||
|
// Special case where the particle gets inserted before the head.
|
||||||
|
if (pos == activeParticles)
|
||||||
|
{
|
||||||
|
Particle *pA = pHead;
|
||||||
|
if (pA)
|
||||||
|
pA->prev = p;
|
||||||
|
p->prev = nullptr;
|
||||||
|
p->next = pA;
|
||||||
|
pHead = p;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts the particle after the randomly selected particle.
|
||||||
|
Particle *pA = pMem + pos;
|
||||||
|
Particle *pB = pA->next;
|
||||||
|
pA->next = p;
|
||||||
|
if (pB)
|
||||||
|
pB->prev = p;
|
||||||
|
else
|
||||||
|
pTail = p;
|
||||||
|
p->prev = pA;
|
||||||
|
p->next = pB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
|
||||||
|
{
|
||||||
|
// The linked list is updated in this function and old pointers may be
|
||||||
|
// invalidated. The returned pointer will inform the caller of the new
|
||||||
|
// pointer to the next particle.
|
||||||
|
Particle *pNext = nullptr;
|
||||||
|
|
||||||
|
// Removes the particle from the linked list.
|
||||||
|
if (p->prev)
|
||||||
|
p->prev->next = p->next;
|
||||||
|
else
|
||||||
|
pHead = p->next;
|
||||||
|
|
||||||
|
if (p->next)
|
||||||
|
{
|
||||||
|
p->next->prev = p->prev;
|
||||||
|
pNext = p->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pTail = p->prev;
|
||||||
|
|
||||||
|
// The (in memory) last particle can now be moved into the free slot.
|
||||||
|
// It will skip the moving if it happens to be the removed particle.
|
||||||
|
pFree--;
|
||||||
|
if (p != pFree)
|
||||||
|
{
|
||||||
|
*p = *pFree;
|
||||||
|
if (pNext == pFree)
|
||||||
|
pNext = p;
|
||||||
|
|
||||||
|
if (p->prev)
|
||||||
|
p->prev->next = p;
|
||||||
|
else
|
||||||
|
pHead = p;
|
||||||
|
|
||||||
|
if (p->next)
|
||||||
|
p->next->prev = p;
|
||||||
|
else
|
||||||
|
pTail = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeParticles--;
|
||||||
|
return pNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setTexture(Texture *tex)
|
||||||
|
{
|
||||||
|
texture.set(tex);
|
||||||
|
|
||||||
|
if (defaultOffset)
|
||||||
|
resetOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture *ParticleSystem::getTexture() const
|
||||||
|
{
|
||||||
|
return texture.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setInsertMode(InsertMode mode)
|
||||||
|
{
|
||||||
|
insertMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
|
||||||
|
{
|
||||||
|
return insertMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setEmissionRate(float rate)
|
||||||
|
{
|
||||||
|
if (rate < 0.0f)
|
||||||
|
throw love::Exception("Invalid emission rate");
|
||||||
|
emissionRate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getEmissionRate() const
|
||||||
|
{
|
||||||
|
return emissionRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setEmitterLifetime(float life)
|
||||||
|
{
|
||||||
|
this->life = lifetime = life;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getEmitterLifetime() const
|
||||||
|
{
|
||||||
|
return lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setParticleLifetime(float min, float max)
|
||||||
|
{
|
||||||
|
particleLifeMin = min;
|
||||||
|
if (max == 0)
|
||||||
|
particleLifeMax = min;
|
||||||
|
else
|
||||||
|
particleLifeMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getParticleLifetime(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = particleLifeMin;
|
||||||
|
max = particleLifeMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setPosition(float x, float y)
|
||||||
|
{
|
||||||
|
position = love::Vector(x, y);
|
||||||
|
prevPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
const love::Vector &ParticleSystem::getPosition() const
|
||||||
|
{
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::moveTo(float x, float y)
|
||||||
|
{
|
||||||
|
position = love::Vector(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
|
||||||
|
{
|
||||||
|
areaSpread = love::Vector(x, y);
|
||||||
|
areaSpreadDistribution = distribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
|
||||||
|
{
|
||||||
|
return areaSpreadDistribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
const love::Vector &ParticleSystem::getAreaSpreadParameters() const
|
||||||
|
{
|
||||||
|
return areaSpread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setDirection(float direction)
|
||||||
|
{
|
||||||
|
this->direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getDirection() const
|
||||||
|
{
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpread(float spread)
|
||||||
|
{
|
||||||
|
this->spread = spread;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getSpread() const
|
||||||
|
{
|
||||||
|
return spread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpeed(float speed)
|
||||||
|
{
|
||||||
|
speedMin = speedMax = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpeed(float min, float max)
|
||||||
|
{
|
||||||
|
speedMin = min;
|
||||||
|
speedMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getSpeed(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = speedMin;
|
||||||
|
max = speedMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setLinearAcceleration(float x, float y)
|
||||||
|
{
|
||||||
|
linearAccelerationMin.x = linearAccelerationMax.x = x;
|
||||||
|
linearAccelerationMin.y = linearAccelerationMax.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
|
||||||
|
{
|
||||||
|
linearAccelerationMin = love::Vector(xmin, ymin);
|
||||||
|
linearAccelerationMax = love::Vector(xmax, ymax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getLinearAcceleration(love::Vector &min, love::Vector &max) const
|
||||||
|
{
|
||||||
|
min = linearAccelerationMin;
|
||||||
|
max = linearAccelerationMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setRadialAcceleration(float acceleration)
|
||||||
|
{
|
||||||
|
radialAccelerationMin = radialAccelerationMax = acceleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setRadialAcceleration(float min, float max)
|
||||||
|
{
|
||||||
|
radialAccelerationMin = min;
|
||||||
|
radialAccelerationMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getRadialAcceleration(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = radialAccelerationMin;
|
||||||
|
max = radialAccelerationMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setTangentialAcceleration(float acceleration)
|
||||||
|
{
|
||||||
|
tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setTangentialAcceleration(float min, float max)
|
||||||
|
{
|
||||||
|
tangentialAccelerationMin = min;
|
||||||
|
tangentialAccelerationMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getTangentialAcceleration(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = tangentialAccelerationMin;
|
||||||
|
max = tangentialAccelerationMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setLinearDamping(float min, float max)
|
||||||
|
{
|
||||||
|
linearDampingMin = min;
|
||||||
|
linearDampingMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getLinearDamping(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = linearDampingMin;
|
||||||
|
max = linearDampingMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSize(float size)
|
||||||
|
{
|
||||||
|
sizes.resize(1);
|
||||||
|
sizes[0] = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSizes(const std::vector<float> &newSizes)
|
||||||
|
{
|
||||||
|
sizes = newSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<float> &ParticleSystem::getSizes() const
|
||||||
|
{
|
||||||
|
return sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSizeVariation(float variation)
|
||||||
|
{
|
||||||
|
sizeVariation = variation;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getSizeVariation() const
|
||||||
|
{
|
||||||
|
return sizeVariation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setRotation(float rotation)
|
||||||
|
{
|
||||||
|
rotationMin = rotationMax = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setRotation(float min, float max)
|
||||||
|
{
|
||||||
|
rotationMin = min;
|
||||||
|
rotationMax = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getRotation(float &min, float &max) const
|
||||||
|
{
|
||||||
|
min = rotationMin;
|
||||||
|
max = rotationMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpin(float spin)
|
||||||
|
{
|
||||||
|
spinStart = spin;
|
||||||
|
spinEnd = spin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpin(float start, float end)
|
||||||
|
{
|
||||||
|
spinStart = start;
|
||||||
|
spinEnd = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::getSpin(float &start, float &end) const
|
||||||
|
{
|
||||||
|
start = spinStart;
|
||||||
|
end = spinEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setSpinVariation(float variation)
|
||||||
|
{
|
||||||
|
spinVariation = variation;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ParticleSystem::getSpinVariation() const
|
||||||
|
{
|
||||||
|
return spinVariation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setOffset(float x, float y)
|
||||||
|
{
|
||||||
|
offset = love::Vector(x, y);
|
||||||
|
defaultOffset = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
love::Vector ParticleSystem::getOffset() const
|
||||||
|
{
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setColor(const std::vector<Colorf> &newColors)
|
||||||
|
{
|
||||||
|
colors = newColors;
|
||||||
|
|
||||||
|
for (Colorf &c : colors)
|
||||||
|
{
|
||||||
|
// We want to store the colors as [0, 1], rather than [0, 255].
|
||||||
|
c.r /= 255.0f;
|
||||||
|
c.g /= 255.0f;
|
||||||
|
c.b /= 255.0f;
|
||||||
|
c.a /= 255.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Colorf> ParticleSystem::getColor() const
|
||||||
|
{
|
||||||
|
// The particle system stores colors in the range of [0, 1]...
|
||||||
|
std::vector<Colorf> ncolors(colors);
|
||||||
|
|
||||||
|
for (Colorf &c : ncolors)
|
||||||
|
{
|
||||||
|
c.r *= 255.0f;
|
||||||
|
c.g *= 255.0f;
|
||||||
|
c.b *= 255.0f;
|
||||||
|
c.a *= 255.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ncolors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setQuads(const std::vector<Quad *> &newQuads)
|
||||||
|
{
|
||||||
|
std::vector<StrongRef<Quad>> quadlist;
|
||||||
|
quadlist.reserve(newQuads.size());
|
||||||
|
|
||||||
|
for (Quad *q : newQuads)
|
||||||
|
quadlist.push_back(q);
|
||||||
|
|
||||||
|
quads = quadlist;
|
||||||
|
|
||||||
|
if (defaultOffset)
|
||||||
|
resetOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setQuads()
|
||||||
|
{
|
||||||
|
quads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Quad *> ParticleSystem::getQuads() const
|
||||||
|
{
|
||||||
|
std::vector<Quad *> quadlist;
|
||||||
|
quadlist.reserve(quads.size());
|
||||||
|
|
||||||
|
for (const StrongRef<Quad> &q : quads)
|
||||||
|
quadlist.push_back(q.get());
|
||||||
|
|
||||||
|
return quadlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::setRelativeRotation(bool enable)
|
||||||
|
{
|
||||||
|
relativeRotation = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::hasRelativeRotation() const
|
||||||
|
{
|
||||||
|
return relativeRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 ParticleSystem::getCount() const
|
||||||
|
{
|
||||||
|
return activeParticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::start()
|
||||||
|
{
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::stop()
|
||||||
|
{
|
||||||
|
active = false;
|
||||||
|
life = lifetime;
|
||||||
|
emitCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::pause()
|
||||||
|
{
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::reset()
|
||||||
|
{
|
||||||
|
if (pMem == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pFree = pMem;
|
||||||
|
pHead = nullptr;
|
||||||
|
pTail = nullptr;
|
||||||
|
activeParticles = 0;
|
||||||
|
life = lifetime;
|
||||||
|
emitCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::emit(uint32 num)
|
||||||
|
{
|
||||||
|
if (!active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
num = std::min(num, maxParticles - activeParticles);
|
||||||
|
|
||||||
|
while (num--)
|
||||||
|
addParticle(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::isActive() const
|
||||||
|
{
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::isPaused() const
|
||||||
|
{
|
||||||
|
return !active && life < lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::isStopped() const
|
||||||
|
{
|
||||||
|
return !active && life >= lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::isEmpty() const
|
||||||
|
{
|
||||||
|
return activeParticles == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::isFull() const
|
||||||
|
{
|
||||||
|
return activeParticles == maxParticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParticleSystem::update(float dt)
|
||||||
|
{
|
||||||
|
if (pMem == nullptr || dt == 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Traverse all particles and update.
|
||||||
|
Particle *p = pHead;
|
||||||
|
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
// Decrease lifespan.
|
||||||
|
p->life -= dt;
|
||||||
|
|
||||||
|
if (p->life <= 0)
|
||||||
|
p = removeParticle(p);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Temp variables.
|
||||||
|
love::Vector radial, tangential;
|
||||||
|
love::Vector ppos = p->position;
|
||||||
|
|
||||||
|
// Get vector from particle center to particle.
|
||||||
|
radial = ppos - p->origin;
|
||||||
|
radial.normalize();
|
||||||
|
tangential = radial;
|
||||||
|
|
||||||
|
// Resize radial acceleration.
|
||||||
|
radial *= p->radialAcceleration;
|
||||||
|
|
||||||
|
// Calculate tangential acceleration.
|
||||||
|
{
|
||||||
|
float a = tangential.getX();
|
||||||
|
tangential.setX(-tangential.getY());
|
||||||
|
tangential.setY(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize tangential.
|
||||||
|
tangential *= p->tangentialAcceleration;
|
||||||
|
|
||||||
|
// Update velocity.
|
||||||
|
p->velocity += (radial + tangential + p->linearAcceleration) * dt;
|
||||||
|
|
||||||
|
// Apply damping.
|
||||||
|
p->velocity *= 1.0f / (1.0f + p->linearDamping * dt);
|
||||||
|
|
||||||
|
// Modify position.
|
||||||
|
ppos += p->velocity * dt;
|
||||||
|
|
||||||
|
p->position = ppos;
|
||||||
|
|
||||||
|
const float t = 1.0f - p->life / p->lifetime;
|
||||||
|
|
||||||
|
// Rotate.
|
||||||
|
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
|
||||||
|
|
||||||
|
p->angle = p->rotation;
|
||||||
|
|
||||||
|
if (relativeRotation)
|
||||||
|
p->angle += atan2f(p->velocity.y, p->velocity.x);
|
||||||
|
|
||||||
|
// Change size according to given intervals:
|
||||||
|
// i = 0 1 2 3 n-1
|
||||||
|
// |-------|-------|------|--- ... ---|
|
||||||
|
// t = 0 1/(n-1) 3/(n-1) 1
|
||||||
|
//
|
||||||
|
// `s' is the interpolation variable scaled to the current
|
||||||
|
// interval width, e.g. if n = 5 and t = 0.3, then the current
|
||||||
|
// indices are 1,2 and s = 0.3 - 0.25 = 0.05
|
||||||
|
float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
|
||||||
|
s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
|
||||||
|
size_t i = (size_t)s;
|
||||||
|
size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
|
||||||
|
s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
|
||||||
|
p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
|
||||||
|
|
||||||
|
// Update color according to given intervals (as above)
|
||||||
|
s = t * (float)(colors.size() - 1);
|
||||||
|
i = (size_t)s;
|
||||||
|
k = (i == colors.size() - 1) ? i : i + 1;
|
||||||
|
s -= (float)i; // 0 <= s <= 1
|
||||||
|
p->color = colors[i] * (1.0f - s) + colors[k] * s;
|
||||||
|
|
||||||
|
// Update the quad index.
|
||||||
|
k = quads.size();
|
||||||
|
if (k > 0)
|
||||||
|
{
|
||||||
|
s = t * (float) k; // [0:numquads-1] (clamped below)
|
||||||
|
i = (s > 0.0f) ? (size_t) s : 0;
|
||||||
|
p->quadIndex = (int) ((i < k) ? i : k - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next particle.
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make some more particles.
|
||||||
|
if (active)
|
||||||
|
{
|
||||||
|
float rate = 1.0f / emissionRate; // the amount of time between each particle emit
|
||||||
|
emitCounter += dt;
|
||||||
|
float total = emitCounter - rate;
|
||||||
|
while (emitCounter > rate)
|
||||||
|
{
|
||||||
|
addParticle(1.0f - (emitCounter - rate) / total);
|
||||||
|
emitCounter -= rate;
|
||||||
|
}
|
||||||
|
/*int particles = (int)(emissionRate * dt);
|
||||||
|
for (int i = 0; i != particles; i++)
|
||||||
|
add();*/
|
||||||
|
|
||||||
|
life -= dt;
|
||||||
|
if (lifetime != -1 && life < 0)
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
|
||||||
|
{
|
||||||
|
return distributions.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
|
||||||
|
{
|
||||||
|
return distributions.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::getConstant(const char *in, InsertMode &out)
|
||||||
|
{
|
||||||
|
return insertModes.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParticleSystem::getConstant(InsertMode in, const char *&out)
|
||||||
|
{
|
||||||
|
return insertModes.find(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] =
|
||||||
|
{
|
||||||
|
{ "none", DISTRIBUTION_NONE },
|
||||||
|
{ "uniform", DISTRIBUTION_UNIFORM },
|
||||||
|
{ "normal", DISTRIBUTION_NORMAL },
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
|
||||||
|
|
||||||
|
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
|
||||||
|
{
|
||||||
|
{ "top", INSERT_MODE_TOP },
|
||||||
|
{ "bottom", INSERT_MODE_BOTTOM },
|
||||||
|
{ "random", INSERT_MODE_RANDOM },
|
||||||
|
};
|
||||||
|
|
||||||
|
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
|
||||||
|
|
||||||
|
} // graphics
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,672 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef LOVE_GRAPHICS_PARTICLE_SYSTEM_H
|
||||||
|
#define LOVE_GRAPHICS_PARTICLE_SYSTEM_H
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "common/int.h"
|
||||||
|
#include "common/math.h"
|
||||||
|
#include "common/Vector.h"
|
||||||
|
#include "Drawable.h"
|
||||||
|
#include "Color.h"
|
||||||
|
#include "Quad.h"
|
||||||
|
#include "Texture.h"
|
||||||
|
|
||||||
|
// STL
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace graphics
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for creating, moving and drawing particles.
|
||||||
|
* A big thanks to bobthebloke.org
|
||||||
|
**/
|
||||||
|
class ParticleSystem : public Drawable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Type of distribution new particles are drawn from: None, uniform, normal.
|
||||||
|
*/
|
||||||
|
enum AreaSpreadDistribution
|
||||||
|
{
|
||||||
|
DISTRIBUTION_NONE,
|
||||||
|
DISTRIBUTION_UNIFORM,
|
||||||
|
DISTRIBUTION_NORMAL,
|
||||||
|
DISTRIBUTION_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insertion modes of new particles in the list: top, bottom, random.
|
||||||
|
*/
|
||||||
|
enum InsertMode
|
||||||
|
{
|
||||||
|
INSERT_MODE_TOP,
|
||||||
|
INSERT_MODE_BOTTOM,
|
||||||
|
INSERT_MODE_RANDOM,
|
||||||
|
INSERT_MODE_MAX_ENUM
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum numbers of particles in a ParticleSystem.
|
||||||
|
* This limit comes from the fact that a quad requires four vertices and the
|
||||||
|
* OpenGL API where GLsizei is a signed int.
|
||||||
|
**/
|
||||||
|
static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a particle system with the specified buffer size and texture.
|
||||||
|
**/
|
||||||
|
ParticleSystem(Texture *texture, uint32 buffer);
|
||||||
|
ParticleSystem(const ParticleSystem &p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes any allocated memory.
|
||||||
|
**/
|
||||||
|
virtual ~ParticleSystem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an identical copy of this ParticleSystem. The clone does not
|
||||||
|
* duplicate any existing particles from this ParticleSystem, just the
|
||||||
|
* settable parameters.
|
||||||
|
**/
|
||||||
|
virtual ParticleSystem *clone() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the texture used in the particle system.
|
||||||
|
* @param texture The new texture.
|
||||||
|
**/
|
||||||
|
void setTexture(Texture *texture);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the texture used when drawing the particle system.
|
||||||
|
**/
|
||||||
|
Texture *getTexture() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current buffer and allocates the appropriate amount of space for the buffer.
|
||||||
|
* @param size The new buffer size.
|
||||||
|
**/
|
||||||
|
virtual void setBufferSize(uint32 size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total amount of particles this ParticleSystem can have active
|
||||||
|
* at any given point in time.
|
||||||
|
**/
|
||||||
|
uint32 getBufferSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the insert mode for new particles.
|
||||||
|
* @param mode The new insert mode.
|
||||||
|
*/
|
||||||
|
void setInsertMode(InsertMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current insert mode.
|
||||||
|
*/
|
||||||
|
InsertMode getInsertMode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the emission rate.
|
||||||
|
* @param rate The amount of particles per second.
|
||||||
|
**/
|
||||||
|
void setEmissionRate(float rate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of particles created per second.
|
||||||
|
**/
|
||||||
|
float getEmissionRate() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the lifetime of the particle emitter (-1 means eternal)
|
||||||
|
* @param life The lifetime (in seconds).
|
||||||
|
**/
|
||||||
|
void setEmitterLifetime(float life);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lifetime of the particle emitter.
|
||||||
|
**/
|
||||||
|
float getEmitterLifetime() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the life range of the particles.
|
||||||
|
* @param min The minimum life.
|
||||||
|
* @param max The maximum life (if 0, then becomes the same as minimum life).
|
||||||
|
**/
|
||||||
|
void setParticleLifetime(float min, float max = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the lifetime of a particle.
|
||||||
|
* @param[out] min The minimum life.
|
||||||
|
* @param[out] max The maximum life.
|
||||||
|
**/
|
||||||
|
void getParticleLifetime(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the position of the center of the emitter.
|
||||||
|
* Used to move the emitter without changing the position of already existing particles.
|
||||||
|
* @param x The x-coordinate.
|
||||||
|
* @param y The y-coordinate.
|
||||||
|
**/
|
||||||
|
void setPosition(float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the emitter.
|
||||||
|
**/
|
||||||
|
const love::Vector &getPosition() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the position of the center of the emitter.
|
||||||
|
* When update is called, newly spawned particles will appear in a line
|
||||||
|
* between the old emitter position and where the emitter was moved to,
|
||||||
|
* resulting in a smoother-feeling particle system if moveTo is called
|
||||||
|
* repeatedly.
|
||||||
|
**/
|
||||||
|
void moveTo(float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the emission area spread parameters and distribution type. The interpretation of
|
||||||
|
* the parameters depends on the distribution type:
|
||||||
|
*
|
||||||
|
* * None: Parameters are ignored. No area spread.
|
||||||
|
* * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
|
||||||
|
* * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
|
||||||
|
* @param x First parameter. Interpretation depends on distribution type.
|
||||||
|
* @param y Second parameter. Interpretation depends on distribution type.
|
||||||
|
* @param distribution Distribution type
|
||||||
|
**/
|
||||||
|
void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns area spread distribution type.
|
||||||
|
**/
|
||||||
|
AreaSpreadDistribution getAreaSpreadDistribution() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns area spread parameters.
|
||||||
|
**/
|
||||||
|
const love::Vector &getAreaSpreadParameters() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the direction of the particle emitter.
|
||||||
|
* @param direction The direction (in degrees).
|
||||||
|
**/
|
||||||
|
void setDirection(float direction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the direction of the particle emitter (in radians).
|
||||||
|
**/
|
||||||
|
float getDirection() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the spread of the particle emitter.
|
||||||
|
* @param spread The spread (in radians).
|
||||||
|
**/
|
||||||
|
void setSpread(float spread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the directional spread of the emitter (in radians).
|
||||||
|
**/
|
||||||
|
float getSpread() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the speed of the particles.
|
||||||
|
* @param speed The speed.
|
||||||
|
**/
|
||||||
|
void setSpeed(float speed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the speed of the particles.
|
||||||
|
* @param min The minimum speed.
|
||||||
|
* @param max The maximum speed.
|
||||||
|
**/
|
||||||
|
void setSpeed(float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the speed of the particles.
|
||||||
|
* @param[out] min The minimum speed.
|
||||||
|
* @param[out] max The maximum speed.
|
||||||
|
**/
|
||||||
|
void getSpeed(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the linear acceleration (the acceleration along the x and y axes).
|
||||||
|
* @param x The acceleration along the x-axis.
|
||||||
|
* @param y The acceleration along the y-axis.
|
||||||
|
**/
|
||||||
|
void setLinearAcceleration(float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the linear acceleration (the acceleration along the x and y axes).
|
||||||
|
* @param xmin The minimum amount of acceleration along the x-axis.
|
||||||
|
* @param ymin The minimum amount of acceleration along the y-axis.
|
||||||
|
* @param xmax The maximum amount of acceleration along the x-axis.
|
||||||
|
* @param ymax The maximum amount of acceleration along the y-axis.
|
||||||
|
**/
|
||||||
|
void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the linear acceleration of the particles.
|
||||||
|
* @param[out] min The minimum acceleration.
|
||||||
|
* @param[out] max The maximum acceleration.
|
||||||
|
**/
|
||||||
|
void getLinearAcceleration(love::Vector &min, love::Vector &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the radial acceleration (the acceleration towards the particle emitter).
|
||||||
|
* @param acceleration The amount of acceleration.
|
||||||
|
**/
|
||||||
|
void setRadialAcceleration(float acceleration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the radial acceleration (the acceleration towards the particle emitter).
|
||||||
|
* @param min The minimum acceleration.
|
||||||
|
* @param max The maximum acceleration.
|
||||||
|
**/
|
||||||
|
void setRadialAcceleration(float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the radial acceleration.
|
||||||
|
* @param[out] min The minimum amount of radial acceleration.
|
||||||
|
* @param[out] max The maximum amount of radial acceleration.
|
||||||
|
**/
|
||||||
|
void getRadialAcceleration(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
|
||||||
|
* @param acceleration The amount of acceleration.
|
||||||
|
**/
|
||||||
|
void setTangentialAcceleration(float acceleration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
|
||||||
|
* @param min The minimum acceleration.
|
||||||
|
* @param max The maximum acceleration.
|
||||||
|
**/
|
||||||
|
void setTangentialAcceleration(float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the tangential acceleration.
|
||||||
|
* @param[out] min The minimum tangential acceleration.
|
||||||
|
* @param[out] max The maximum tangential acceleration.
|
||||||
|
**/
|
||||||
|
void getTangentialAcceleration(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the amount of linear damping. Damping reduces the velocity of
|
||||||
|
* particles over time. A value of 0 corresponds to no damping.
|
||||||
|
**/
|
||||||
|
void setLinearDamping(float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current amount of linear damping.
|
||||||
|
**/
|
||||||
|
void getLinearDamping(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the size of the sprite (1.0 being the default size).
|
||||||
|
* @param size The size of the sprite.
|
||||||
|
**/
|
||||||
|
void setSize(float size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
|
||||||
|
* @param newSizes Array of sizes
|
||||||
|
**/
|
||||||
|
void setSizes(const std::vector<float> &newSizes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sizes of the particle sprites.
|
||||||
|
**/
|
||||||
|
const std::vector<float> &getSizes() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
|
||||||
|
* @param variation The amount of variation.
|
||||||
|
**/
|
||||||
|
void setSizeVariation(float variation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of initial size variation between particles.
|
||||||
|
**/
|
||||||
|
float getSizeVariation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the amount of rotation a sprite starts out with.
|
||||||
|
* @param rotation The amount of rotation.
|
||||||
|
**/
|
||||||
|
void setRotation(float rotation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the amount of rotation a sprite starts out with (a random value between min and max).
|
||||||
|
* @param min The minimum amount of rotation.
|
||||||
|
* @param max The maximum amount of rotation.
|
||||||
|
**/
|
||||||
|
void setRotation(float min, float max);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the initial amount of rotation of a particle, in radians.
|
||||||
|
* @param[out] min The minimum initial rotation.
|
||||||
|
* @param[out] max The maximum initial rotation.
|
||||||
|
**/
|
||||||
|
void getRotation(float &min, float &max) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the spin of the sprite.
|
||||||
|
* @param spin The spin of the sprite (in degrees).
|
||||||
|
**/
|
||||||
|
void setSpin(float spin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the spin of the sprite upon particle creation and death.
|
||||||
|
* @param start The spin of the sprite upon creation (in radians / second).
|
||||||
|
* @param end The spin of the sprite upon death (in radians / second).
|
||||||
|
**/
|
||||||
|
void setSpin(float start, float end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the amount of spin of a particle during its lifetime.
|
||||||
|
* @param[out] start The initial spin, in radians / s.
|
||||||
|
* @param[out] end The final spin, in radians / s.
|
||||||
|
**/
|
||||||
|
void getSpin(float &start, float &end) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
|
||||||
|
* @param variation The variation.
|
||||||
|
**/
|
||||||
|
void setSpinVariation(float variation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of variation of the start spin of a particle.
|
||||||
|
**/
|
||||||
|
float getSpinVariation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the particles' offsets for rotation.
|
||||||
|
* @param x The x offset.
|
||||||
|
* @param y The y offset.
|
||||||
|
**/
|
||||||
|
void setOffset(float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns of the particle offset.
|
||||||
|
**/
|
||||||
|
love::Vector getOffset() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the color of the particles.
|
||||||
|
* @param newColors Array of colors
|
||||||
|
**/
|
||||||
|
void setColor(const std::vector<Colorf> &newColors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color of the particles.
|
||||||
|
**/
|
||||||
|
std::vector<Colorf> getColor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a list of Quads to use for particles over their lifetime.
|
||||||
|
**/
|
||||||
|
void setQuads(const std::vector<Quad *> &newQuads);
|
||||||
|
void setQuads();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Quads used when drawing the particles.
|
||||||
|
**/
|
||||||
|
std::vector<Quad *> getQuads() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets whether particle angles & rotations are relative to their velocities.
|
||||||
|
**/
|
||||||
|
void setRelativeRotation(bool enable);
|
||||||
|
bool hasRelativeRotation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of particles that are currently active in the system.
|
||||||
|
**/
|
||||||
|
uint32 getCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts/resumes the particle emitter.
|
||||||
|
**/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the particle emitter and resets.
|
||||||
|
**/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the particle emitter.
|
||||||
|
**/
|
||||||
|
void pause();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the particle emitter.
|
||||||
|
**/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantly emits a number of particles.
|
||||||
|
* @param num The number of particles to emit.
|
||||||
|
**/
|
||||||
|
void emit(uint32 num);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the particle emitter is active.
|
||||||
|
**/
|
||||||
|
bool isActive() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the particle emitter is paused.
|
||||||
|
**/
|
||||||
|
bool isPaused() const;
|
||||||
|
|
||||||
|
bool isStopped() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the particle system is empty of particles or not.
|
||||||
|
**/
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the amount of particles has reached the buffer limit or not.
|
||||||
|
**/
|
||||||
|
bool isFull() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the particle system.
|
||||||
|
* @param dt Time since last update.
|
||||||
|
**/
|
||||||
|
void update(float dt);
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, AreaSpreadDistribution &out);
|
||||||
|
static bool getConstant(AreaSpreadDistribution in, const char *&out);
|
||||||
|
|
||||||
|
static bool getConstant(const char *in, InsertMode &out);
|
||||||
|
static bool getConstant(InsertMode in, const char *&out);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Represents a single particle.
|
||||||
|
struct Particle
|
||||||
|
{
|
||||||
|
Particle *prev;
|
||||||
|
Particle *next;
|
||||||
|
|
||||||
|
float lifetime;
|
||||||
|
float life;
|
||||||
|
|
||||||
|
love::Vector position;
|
||||||
|
|
||||||
|
// Particles gravitate towards this point.
|
||||||
|
love::Vector origin;
|
||||||
|
|
||||||
|
love::Vector velocity;
|
||||||
|
love::Vector linearAcceleration;
|
||||||
|
float radialAcceleration;
|
||||||
|
float tangentialAcceleration;
|
||||||
|
|
||||||
|
float linearDamping;
|
||||||
|
|
||||||
|
float size;
|
||||||
|
float sizeOffset;
|
||||||
|
float sizeIntervalSize;
|
||||||
|
|
||||||
|
float rotation; // Amount of rotation applied to the final angle.
|
||||||
|
float angle;
|
||||||
|
float spinStart;
|
||||||
|
float spinEnd;
|
||||||
|
|
||||||
|
Colorf color;
|
||||||
|
|
||||||
|
int quadIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pointer to the beginning of the allocated memory.
|
||||||
|
Particle *pMem;
|
||||||
|
|
||||||
|
// Pointer to a free particle.
|
||||||
|
Particle *pFree;
|
||||||
|
|
||||||
|
// Pointer to the start of the linked list.
|
||||||
|
Particle *pHead;
|
||||||
|
|
||||||
|
// Pointer to the end of the linked list.
|
||||||
|
Particle *pTail;
|
||||||
|
|
||||||
|
// The texture to be drawn.
|
||||||
|
StrongRef<Texture> texture;
|
||||||
|
|
||||||
|
// Whether the particle emitter is active.
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
// Insert mode of new particles.
|
||||||
|
InsertMode insertMode;
|
||||||
|
|
||||||
|
// The maximum number of particles.
|
||||||
|
uint32 maxParticles;
|
||||||
|
|
||||||
|
// The number of active particles.
|
||||||
|
uint32 activeParticles;
|
||||||
|
|
||||||
|
// The emission rate (particles/sec).
|
||||||
|
float emissionRate;
|
||||||
|
|
||||||
|
// Used to determine when a particle should be emitted.
|
||||||
|
float emitCounter;
|
||||||
|
|
||||||
|
// The relative position of the particle emitter.
|
||||||
|
love::Vector position;
|
||||||
|
love::Vector prevPosition;
|
||||||
|
|
||||||
|
// Emission area spread.
|
||||||
|
AreaSpreadDistribution areaSpreadDistribution;
|
||||||
|
love::Vector areaSpread;
|
||||||
|
|
||||||
|
// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
|
||||||
|
float lifetime;
|
||||||
|
float life;
|
||||||
|
|
||||||
|
// The particle life.
|
||||||
|
float particleLifeMin;
|
||||||
|
float particleLifeMax;
|
||||||
|
|
||||||
|
// The direction (and spread) the particles will be emitted in. Measured in radians.
|
||||||
|
float direction;
|
||||||
|
float spread;
|
||||||
|
|
||||||
|
// The speed.
|
||||||
|
float speedMin;
|
||||||
|
float speedMax;
|
||||||
|
|
||||||
|
// Acceleration along the x and y axes.
|
||||||
|
love::Vector linearAccelerationMin;
|
||||||
|
love::Vector linearAccelerationMax;
|
||||||
|
|
||||||
|
// Acceleration towards the emitter's center
|
||||||
|
float radialAccelerationMin;
|
||||||
|
float radialAccelerationMax;
|
||||||
|
|
||||||
|
// Acceleration perpendicular to the particle's direction.
|
||||||
|
float tangentialAccelerationMin;
|
||||||
|
float tangentialAccelerationMax;
|
||||||
|
|
||||||
|
float linearDampingMin;
|
||||||
|
float linearDampingMax;
|
||||||
|
|
||||||
|
// Size.
|
||||||
|
std::vector<float> sizes;
|
||||||
|
float sizeVariation;
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
float rotationMin;
|
||||||
|
float rotationMax;
|
||||||
|
|
||||||
|
// Spin.
|
||||||
|
float spinStart;
|
||||||
|
float spinEnd;
|
||||||
|
float spinVariation;
|
||||||
|
|
||||||
|
// Offsets
|
||||||
|
love::Vector offset;
|
||||||
|
|
||||||
|
// Is the ParticleSystem using a default offset?
|
||||||
|
bool defaultOffset;
|
||||||
|
|
||||||
|
// Color.
|
||||||
|
std::vector<Colorf> colors;
|
||||||
|
|
||||||
|
// Quads.
|
||||||
|
std::vector<StrongRef<Quad>> quads;
|
||||||
|
|
||||||
|
bool relativeRotation;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void resetOffset();
|
||||||
|
|
||||||
|
void createBuffers(size_t size);
|
||||||
|
void deleteBuffers();
|
||||||
|
|
||||||
|
void addParticle(float t);
|
||||||
|
Particle *removeParticle(Particle *p);
|
||||||
|
|
||||||
|
// Called by addParticle.
|
||||||
|
void initParticle(Particle *p, float t);
|
||||||
|
void insertTop(Particle *p);
|
||||||
|
void insertBottom(Particle *p);
|
||||||
|
void insertRandom(Particle *p);
|
||||||
|
|
||||||
|
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
|
||||||
|
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
|
||||||
|
|
||||||
|
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
|
||||||
|
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // graphics
|
||||||
|
} // love
|
||||||
|
|
||||||
|
#endif // LOVE_GRAPHICS_PARTICLE_SYSTEM_H
|
||||||
@@ -29,7 +29,7 @@ namespace love
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
Quad::Quad(const Quad::Viewport &v, float sw, float sh)
|
Quad::Quad(const Quad::Viewport &v, double sw, double sh)
|
||||||
: sw(sw)
|
: sw(sw)
|
||||||
, sh(sh)
|
, sh(sh)
|
||||||
{
|
{
|
||||||
@@ -41,7 +41,7 @@ Quad::~Quad()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
|
void Quad::refresh(const Quad::Viewport &v, double sw, double sh)
|
||||||
{
|
{
|
||||||
viewport = v;
|
viewport = v;
|
||||||
|
|
||||||
@@ -53,20 +53,20 @@ void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
|
|||||||
vertices[0].x = 0.0f;
|
vertices[0].x = 0.0f;
|
||||||
vertices[0].y = 0.0f;
|
vertices[0].y = 0.0f;
|
||||||
vertices[1].x = 0.0f;
|
vertices[1].x = 0.0f;
|
||||||
vertices[1].y = v.h;
|
vertices[1].y = (float) v.h;
|
||||||
vertices[2].x = v.w;
|
vertices[2].x = (float) v.w;
|
||||||
vertices[2].y = 0.0f;
|
vertices[2].y = 0.0f;
|
||||||
vertices[3].x = v.w;
|
vertices[3].x = (float) v.w;
|
||||||
vertices[3].y = v.h;
|
vertices[3].y = (float) v.h;
|
||||||
|
|
||||||
vertices[0].s = v.x/sw;
|
vertices[0].s = (float) (v.x/sw);
|
||||||
vertices[0].t = v.y/sh;
|
vertices[0].t = (float) (v.y/sh);
|
||||||
vertices[1].s = v.x/sw;
|
vertices[1].s = (float) (v.x/sw);
|
||||||
vertices[1].t = (v.y+v.h)/sh;
|
vertices[1].t = (float) ((v.y+v.h)/sh);
|
||||||
vertices[2].s = (v.x+v.w)/sw;
|
vertices[2].s = (float) ((v.x+v.w)/sw);
|
||||||
vertices[2].t = v.y/sh;
|
vertices[2].t = (float) (v.y/sh);
|
||||||
vertices[3].s = (v.x+v.w)/sw;
|
vertices[3].s = (float) ((v.x+v.w)/sw);
|
||||||
vertices[3].t = (v.y+v.h)/sh;
|
vertices[3].t = (float) ((v.y+v.h)/sh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Quad::setViewport(const Quad::Viewport &v)
|
void Quad::setViewport(const Quad::Viewport &v)
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ public:
|
|||||||
|
|
||||||
struct Viewport
|
struct Viewport
|
||||||
{
|
{
|
||||||
float x, y;
|
double x, y;
|
||||||
float w, h;
|
double w, h;
|
||||||
};
|
};
|
||||||
|
|
||||||
Quad(const Viewport &v, float sw, float sh);
|
Quad(const Viewport &v, double sw, double sh);
|
||||||
virtual ~Quad();
|
virtual ~Quad();
|
||||||
|
|
||||||
void refresh(const Viewport &v, float sw, float sh);
|
void refresh(const Viewport &v, double sw, double sh);
|
||||||
void setViewport(const Viewport &v);
|
void setViewport(const Viewport &v);
|
||||||
Viewport getViewport() const;
|
Viewport getViewport() const;
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ private:
|
|||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
|
|
||||||
Viewport viewport;
|
Viewport viewport;
|
||||||
float sw;
|
double sw;
|
||||||
float sh;
|
double sh;
|
||||||
|
|
||||||
}; // Quad
|
}; // Quad
|
||||||
|
|
||||||
|
|||||||
@@ -114,18 +114,19 @@ bool Texture::getConstant(WrapMode in, const char *&out)
|
|||||||
|
|
||||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
||||||
{
|
{
|
||||||
{ "linear", Texture::FILTER_LINEAR },
|
{ "linear", FILTER_LINEAR },
|
||||||
{ "nearest", Texture::FILTER_NEAREST },
|
{ "nearest", FILTER_NEAREST },
|
||||||
{ "none", Texture::FILTER_NONE },
|
{ "none", FILTER_NONE },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
|
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
|
||||||
|
|
||||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
|
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
|
||||||
{
|
{
|
||||||
{ "clamp", Texture::WRAP_CLAMP },
|
{ "clamp", WRAP_CLAMP },
|
||||||
{ "repeat", Texture::WRAP_REPEAT },
|
{ "clampzero", WRAP_CLAMP_ZERO },
|
||||||
{ "mirroredrepeat", Texture::WRAP_MIRRORED_REPEAT },
|
{ "repeat", WRAP_REPEAT },
|
||||||
|
{ "mirroredrepeat", WRAP_MIRRORED_REPEAT },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
enum WrapMode
|
enum WrapMode
|
||||||
{
|
{
|
||||||
WRAP_CLAMP,
|
WRAP_CLAMP,
|
||||||
|
WRAP_CLAMP_ZERO,
|
||||||
WRAP_REPEAT,
|
WRAP_REPEAT,
|
||||||
WRAP_MIRRORED_REPEAT,
|
WRAP_MIRRORED_REPEAT,
|
||||||
WRAP_MAX_ENUM
|
WRAP_MAX_ENUM
|
||||||
|
|||||||
@@ -361,6 +361,14 @@ bool Canvas::setWrap(const Texture::Wrap &w)
|
|||||||
wrap.s = wrap.t = WRAP_CLAMP;
|
wrap.s = wrap.t = WRAP_CLAMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gl.isClampZeroTextureWrapSupported())
|
||||||
|
{
|
||||||
|
if (wrap.s == WRAP_CLAMP_ZERO)
|
||||||
|
wrap.s = WRAP_CLAMP;
|
||||||
|
if (wrap.t == WRAP_CLAMP_ZERO)
|
||||||
|
wrap.t = WRAP_CLAMP;
|
||||||
|
}
|
||||||
|
|
||||||
gl.bindTexture(texture);
|
gl.bindTexture(texture);
|
||||||
gl.setTextureWrap(wrap);
|
gl.setTextureWrap(wrap);
|
||||||
|
|
||||||
@@ -407,9 +415,6 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
|||||||
|
|
||||||
if (canvases.size() > 0)
|
if (canvases.size() > 0)
|
||||||
{
|
{
|
||||||
if (!isMultiCanvasSupported())
|
|
||||||
throw love::Exception("Multi-canvas rendering is not supported on this system.");
|
|
||||||
|
|
||||||
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
|
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
|
||||||
throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1);
|
throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1);
|
||||||
|
|
||||||
@@ -804,15 +809,9 @@ bool Canvas::isSupported()
|
|||||||
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
|
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::isMultiCanvasSupported()
|
|
||||||
{
|
|
||||||
// system must support at least 4 simultaneous active canvases.
|
|
||||||
return gl.getMaxRenderTargets() >= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Canvas::isMultiFormatMultiCanvasSupported()
|
bool Canvas::isMultiFormatMultiCanvasSupported()
|
||||||
{
|
{
|
||||||
return isMultiCanvasSupported() && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
|
return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::supportedFormats[] = {false};
|
bool Canvas::supportedFormats[] = {false};
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ public:
|
|||||||
FORMAT_RG8, // Two-channel (red and green) with 8 bits per channel.
|
FORMAT_RG8, // Two-channel (red and green) with 8 bits per channel.
|
||||||
FORMAT_RGBA8, // RGBA with 8 bits per channel.
|
FORMAT_RGBA8, // RGBA with 8 bits per channel.
|
||||||
FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits.
|
FORMAT_RGB10A2, // RGB with 10 bits each, and A with 2 bits.
|
||||||
FORMAT_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits.
|
FORMAT_RG11B10F, // Floating point [0, +65024]. RG with 11 FP bits each, and B with 10 FP bits.
|
||||||
FORMAT_R16F, // Floating point [-inf, +inf]. R with 16 FP bits.
|
FORMAT_R16F, // Floating point [-65504, +65504]. R with 16 FP bits.
|
||||||
FORMAT_RG16F, // Floating point [-inf, +inf]. RG with 16 FP bits per channel.
|
FORMAT_RG16F, // Floating point [-65504, +65504]. RG with 16 FP bits per channel.
|
||||||
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per channel.
|
FORMAT_RGBA16F, // Floating point [-65504, +65504]. RGBA with 16 FP bits per channel.
|
||||||
FORMAT_R32F, // Floating point [-inf, +inf]. R with 32 FP bits.
|
FORMAT_R32F, // Floating point [-65504, +65504]. R with 32 FP bits.
|
||||||
FORMAT_RG32F, // Floating point [-inf, +inf]. RG with 32 FP bits per channel.
|
FORMAT_RG32F, // Floating point [-65504, +65504]. RG with 32 FP bits per channel.
|
||||||
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per channel.
|
FORMAT_RGBA32F, // Floating point [-65504, +65504]. RGBA with 32 FP bits per channel.
|
||||||
FORMAT_SRGB, // sRGB with 8 bits per channel, plus 8 bit linear A.
|
FORMAT_SRGB, // sRGB with 8 bits per channel, plus 8 bit linear A.
|
||||||
FORMAT_MAX_ENUM
|
FORMAT_MAX_ENUM
|
||||||
};
|
};
|
||||||
@@ -118,7 +118,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool isSupported();
|
static bool isSupported();
|
||||||
static bool isMultiCanvasSupported();
|
|
||||||
static bool isMultiFormatMultiCanvasSupported();
|
static bool isMultiFormatMultiCanvasSupported();
|
||||||
static bool isFormatSupported(Format format);
|
static bool isFormatSupported(Format format);
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static inline uint16 normToUint16(double n)
|
||||||
|
{
|
||||||
|
return (uint16) (n * LOVE_UINT16_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
int Font::fontCount = 0;
|
int Font::fontCount = 0;
|
||||||
|
|
||||||
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
||||||
@@ -272,18 +277,20 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
|
|||||||
|
|
||||||
g.texture = t;
|
g.texture = t;
|
||||||
|
|
||||||
float tX = (float) textureX, tY = (float) textureY;
|
double tX = (double) textureX, tY = (double) textureY;
|
||||||
float tWidth = (float) textureWidth, tHeight = (float) textureHeight;
|
double tWidth = (double) textureWidth, tHeight = (double) textureHeight;
|
||||||
|
|
||||||
|
Color c(255, 255, 255, 255);
|
||||||
|
|
||||||
// 0----2
|
// 0----2
|
||||||
// | / |
|
// | / |
|
||||||
// | / |
|
// | / |
|
||||||
// 1----3
|
// 1----3
|
||||||
const GlyphVertex verts[4] = {
|
const GlyphVertex verts[4] = {
|
||||||
{ 0.0f, 0.0f, tX/tWidth, tY/tHeight},
|
{float(0), float(0), normToUint16((tX+0)/tWidth), normToUint16((tY+0)/tHeight), c},
|
||||||
{ 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
|
{float(0), float(h), normToUint16((tX+0)/tWidth), normToUint16((tY+h)/tHeight), c},
|
||||||
{float(w), 0.0f, (tX+w)/tWidth, tY/tHeight},
|
{float(w), float(0), normToUint16((tX+w)/tWidth), normToUint16((tY+0)/tHeight), c},
|
||||||
{float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight}
|
{float(w), float(h), normToUint16((tX+w)/tWidth), normToUint16((tY+h)/tHeight), c}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Copy vertex data to the glyph and set proper bearing.
|
// Copy vertex data to the glyph and set proper bearing.
|
||||||
@@ -341,28 +348,9 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
|
|||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getHeight() const
|
void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints)
|
||||||
{
|
{
|
||||||
return (float) height;
|
codepoints.reserve(text.size());
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
|
|
||||||
{
|
|
||||||
// Spacing counter and newline handling.
|
|
||||||
float dx = offset.x;
|
|
||||||
float dy = offset.y;
|
|
||||||
|
|
||||||
float lineheight = getBaseline();
|
|
||||||
int maxwidth = 0;
|
|
||||||
|
|
||||||
// Keeps track of when we need to switch textures in our vertex array.
|
|
||||||
std::vector<DrawCommand> drawcommands;
|
|
||||||
|
|
||||||
// Pre-allocate space for the maximum possible number of vertices.
|
|
||||||
size_t vertstartsize = vertices.size();
|
|
||||||
vertices.reserve(vertstartsize + text.length() * 4);
|
|
||||||
|
|
||||||
uint32 prevglyph = 0;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -372,80 +360,152 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
|
|||||||
while (i != end)
|
while (i != end)
|
||||||
{
|
{
|
||||||
uint32 g = *i++;
|
uint32 g = *i++;
|
||||||
|
codepoints.push_back(g);
|
||||||
if (g == '\n')
|
|
||||||
{
|
|
||||||
if (dx > maxwidth)
|
|
||||||
maxwidth = (int) dx;
|
|
||||||
|
|
||||||
// Wrap newline, but do not print it.
|
|
||||||
dy += floorf(getHeight() * getLineHeight() + 0.5f);
|
|
||||||
dx = offset.x;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 cacheid = textureCacheID;
|
|
||||||
|
|
||||||
const Glyph &glyph = findGlyph(g);
|
|
||||||
|
|
||||||
// If findGlyph invalidates the texture cache, re-start the loop.
|
|
||||||
if (cacheid != textureCacheID)
|
|
||||||
{
|
|
||||||
i = utf8::iterator<std::string::const_iterator>(text.begin(), text.begin(), text.end());
|
|
||||||
maxwidth = 0;
|
|
||||||
dx = offset.x;
|
|
||||||
dy = offset.y;
|
|
||||||
drawcommands.clear();
|
|
||||||
vertices.resize(vertstartsize);
|
|
||||||
prevglyph = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add kerning to the current horizontal offset.
|
|
||||||
dx += getKerning(prevglyph, g);
|
|
||||||
|
|
||||||
if (glyph.texture != 0)
|
|
||||||
{
|
|
||||||
// Copy the vertices and set their proper relative positions.
|
|
||||||
for (int j = 0; j < 4; j++)
|
|
||||||
{
|
|
||||||
vertices.push_back(glyph.vertices[j]);
|
|
||||||
vertices.back().x += dx;
|
|
||||||
vertices.back().y += dy + lineheight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if glyph texture has changed since the last iteration.
|
|
||||||
if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
|
|
||||||
{
|
|
||||||
// Add a new draw command if the texture has changed.
|
|
||||||
DrawCommand cmd;
|
|
||||||
cmd.startvertex = (int) vertices.size() - 4;
|
|
||||||
cmd.vertexcount = 0;
|
|
||||||
cmd.texture = glyph.texture;
|
|
||||||
drawcommands.push_back(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawcommands.back().vertexcount += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance the x position for the next glyph.
|
|
||||||
dx += glyph.spacing;
|
|
||||||
|
|
||||||
// Account for extra spacing given to space characters.
|
|
||||||
if (g == ' ' && extra_spacing != 0.0f)
|
|
||||||
dx = floorf(dx + extra_spacing);
|
|
||||||
|
|
||||||
prevglyph = g;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (utf8::exception &e)
|
catch (utf8::exception &e)
|
||||||
{
|
{
|
||||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sort draw commands by texture first, and quad position in memory second
|
void Font::getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
|
||||||
// (using the struct's < operator).
|
{
|
||||||
std::sort(drawcommands.begin(), drawcommands.end());
|
if (strs.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
codepoints.cps.reserve(strs[0].str.size());
|
||||||
|
|
||||||
|
for (const ColoredString &cstr : strs)
|
||||||
|
{
|
||||||
|
IndexedColor c = {cstr.color, (int) codepoints.cps.size()};
|
||||||
|
codepoints.colors.push_back(c);
|
||||||
|
|
||||||
|
getCodepointsFromString(cstr.str, codepoints.cps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codepoints.colors.size() == 1)
|
||||||
|
{
|
||||||
|
IndexedColor c = codepoints.colors[0];
|
||||||
|
|
||||||
|
if (c.index == 0 && c.color == Color(255, 255, 255, 255))
|
||||||
|
codepoints.colors.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Font::getHeight() const
|
||||||
|
{
|
||||||
|
return (float) height;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &codepoints, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
|
||||||
|
{
|
||||||
|
// Spacing counter and newline handling.
|
||||||
|
float dx = offset.x;
|
||||||
|
float dy = offset.y;
|
||||||
|
|
||||||
|
float lineheight = getBaseline();
|
||||||
|
int maxwidth = 0;
|
||||||
|
|
||||||
|
// Keeps track of when we need to switch textures in our vertex array.
|
||||||
|
std::vector<DrawCommand> commands;
|
||||||
|
|
||||||
|
// Pre-allocate space for the maximum possible number of vertices.
|
||||||
|
size_t vertstartsize = vertices.size();
|
||||||
|
vertices.reserve(vertstartsize + codepoints.cps.size() * 4);
|
||||||
|
|
||||||
|
uint32 prevglyph = 0;
|
||||||
|
|
||||||
|
Color curcolor(255, 255, 255, 255);
|
||||||
|
int curcolori = -1;
|
||||||
|
int ncolors = (int) codepoints.colors.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < (int) codepoints.cps.size(); i++)
|
||||||
|
{
|
||||||
|
uint32 g = codepoints.cps[i];
|
||||||
|
|
||||||
|
if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i)
|
||||||
|
curcolor = codepoints.colors[++curcolori].color;
|
||||||
|
|
||||||
|
if (g == '\n')
|
||||||
|
{
|
||||||
|
if (dx > maxwidth)
|
||||||
|
maxwidth = (int) dx;
|
||||||
|
|
||||||
|
// Wrap newline, but do not print it.
|
||||||
|
dy += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
|
dx = offset.x;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 cacheid = textureCacheID;
|
||||||
|
|
||||||
|
const Glyph &glyph = findGlyph(g);
|
||||||
|
|
||||||
|
// If findGlyph invalidates the texture cache, re-start the loop.
|
||||||
|
if (cacheid != textureCacheID)
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
maxwidth = 0;
|
||||||
|
dx = offset.x;
|
||||||
|
dy = offset.y;
|
||||||
|
commands.clear();
|
||||||
|
vertices.resize(vertstartsize);
|
||||||
|
prevglyph = 0;
|
||||||
|
curcolori = -1;
|
||||||
|
curcolor = Color(255, 255, 255, 255);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add kerning to the current horizontal offset.
|
||||||
|
dx += getKerning(prevglyph, g);
|
||||||
|
|
||||||
|
if (glyph.texture != 0)
|
||||||
|
{
|
||||||
|
// Copy the vertices and set their colors and relative positions.
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
vertices.push_back(glyph.vertices[j]);
|
||||||
|
vertices.back().x += dx;
|
||||||
|
vertices.back().y += dy + lineheight;
|
||||||
|
vertices.back().color = curcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if glyph texture has changed since the last iteration.
|
||||||
|
if (commands.empty() || commands.back().texture != glyph.texture)
|
||||||
|
{
|
||||||
|
// Add a new draw command if the texture has changed.
|
||||||
|
DrawCommand cmd;
|
||||||
|
cmd.startvertex = (int) vertices.size() - 4;
|
||||||
|
cmd.vertexcount = 0;
|
||||||
|
cmd.texture = glyph.texture;
|
||||||
|
commands.push_back(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.back().vertexcount += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance the x position for the next glyph.
|
||||||
|
dx += glyph.spacing;
|
||||||
|
|
||||||
|
// Account for extra spacing given to space characters.
|
||||||
|
if (g == ' ' && extra_spacing != 0.0f)
|
||||||
|
dx = floorf(dx + extra_spacing);
|
||||||
|
|
||||||
|
prevglyph = g;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
|
||||||
|
{
|
||||||
|
// Texture binds are expensive, so we should sort by that first.
|
||||||
|
if (a.texture != b.texture)
|
||||||
|
return a.texture < b.texture;
|
||||||
|
else
|
||||||
|
return a.startvertex < b.startvertex;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::sort(commands.begin(), commands.end(), drawsort);
|
||||||
|
|
||||||
if (dx > maxwidth)
|
if (dx > maxwidth)
|
||||||
maxwidth = (int) dx;
|
maxwidth = (int) dx;
|
||||||
@@ -456,42 +516,43 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
|
|||||||
info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
|
info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
return drawcommands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
|
std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
|
||||||
{
|
{
|
||||||
if (wrap < 0.0f)
|
ColoredCodepoints codepoints;
|
||||||
wrap = std::numeric_limits<float>::max();
|
getCodepointsFromString(text, codepoints.cps);
|
||||||
|
return generateVertices(codepoints, vertices, extra_spacing, offset, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
|
||||||
|
{
|
||||||
|
|
||||||
|
wrap = std::max(wrap, 0.0f);
|
||||||
|
|
||||||
uint32 cacheid = textureCacheID;
|
uint32 cacheid = textureCacheID;
|
||||||
|
|
||||||
std::vector<DrawCommand> drawcommands;
|
std::vector<DrawCommand> drawcommands;
|
||||||
vertices.reserve(text.length() * 4);
|
vertices.reserve(text.cps.size() * 4);
|
||||||
|
|
||||||
// wrappedlines indicates which lines were automatically wrapped. It
|
|
||||||
// has the same number of elements as lines_to_draw.
|
|
||||||
std::vector<bool> wrappedlines;
|
|
||||||
std::vector<int> widths;
|
std::vector<int> widths;
|
||||||
std::vector<std::string> lines;
|
std::vector<ColoredCodepoints> lines;
|
||||||
|
|
||||||
// We only need the list of wrapped lines in 'justify' mode.
|
getWrap(text, wrap, lines, &widths);
|
||||||
getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
|
|
||||||
|
|
||||||
float extraspacing = 0.0f;
|
|
||||||
int numspaces = 0;
|
|
||||||
int i = 0;
|
|
||||||
float y = 0.0f;
|
float y = 0.0f;
|
||||||
float maxwidth = 0;
|
float maxwidth = 0.0f;
|
||||||
|
|
||||||
for (const std::string &line : lines)
|
for (int i = 0; i < (int) lines.size(); i++)
|
||||||
{
|
{
|
||||||
extraspacing = 0.0f;
|
const auto &line = lines[i];
|
||||||
|
|
||||||
float width = (float) widths[i];
|
float width = (float) widths[i];
|
||||||
love::Vector offset(0.0f, floorf(y));
|
love::Vector offset(0.0f, floorf(y));
|
||||||
|
float extraspacing = 0.0f;
|
||||||
|
|
||||||
if (width > maxwidth)
|
maxwidth = std::max(width, maxwidth);
|
||||||
maxwidth = width;
|
|
||||||
|
|
||||||
switch (align)
|
switch (align)
|
||||||
{
|
{
|
||||||
@@ -502,22 +563,24 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string
|
|||||||
offset.x = floorf((wrap - width) / 2.0f);
|
offset.x = floorf((wrap - width) / 2.0f);
|
||||||
break;
|
break;
|
||||||
case ALIGN_JUSTIFY:
|
case ALIGN_JUSTIFY:
|
||||||
numspaces = (int) std::count(line.begin(), line.end(), ' ');
|
{
|
||||||
if (wrappedlines[i] && numspaces >= 1)
|
float numspaces = (float) std::count(line.cps.begin(), line.cps.end(), ' ');
|
||||||
extraspacing = (wrap - width) / float(numspaces);
|
if (width < wrap && numspaces >= 1)
|
||||||
|
extraspacing = (wrap - width) / numspaces;
|
||||||
else
|
else
|
||||||
extraspacing = 0.0f;
|
extraspacing = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case ALIGN_LEFT:
|
case ALIGN_LEFT:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
|
std::vector<DrawCommand> newcommands = generateVertices(line, vertices, extraspacing, offset);
|
||||||
|
|
||||||
if (!commands.empty())
|
if (!newcommands.empty())
|
||||||
{
|
{
|
||||||
auto firstcmd = commands.begin();
|
auto firstcmd = newcommands.begin();
|
||||||
|
|
||||||
// If the first draw command in the new list has the same texture
|
// 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
|
// as the last one in the existing list we're building and its
|
||||||
@@ -533,11 +596,10 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append the new draw commands to the list we're building.
|
// Append the new draw commands to the list we're building.
|
||||||
drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
|
drawcommands.insert(drawcommands.end(), firstcmd, newcommands.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
y += getHeight() * getLineHeight();
|
y += getHeight() * getLineHeight();
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info != nullptr)
|
if (info != nullptr)
|
||||||
@@ -597,30 +659,37 @@ void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands
|
|||||||
OpenGL::TempTransform transform(gl);
|
OpenGL::TempTransform transform(gl);
|
||||||
transform.get() *= t;
|
transform.get() *= t;
|
||||||
|
|
||||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
|
||||||
|
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
|
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
|
||||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
|
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GlyphVertex), &vertices[0].s);
|
||||||
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r);
|
||||||
|
|
||||||
|
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
|
||||||
|
|
||||||
drawVertices(drawcommands);
|
drawVertices(drawcommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::print(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Font::print(const std::vector<ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
std::vector<GlyphVertex> vertices;
|
ColoredCodepoints codepoints;
|
||||||
std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
|
getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
|
std::vector<GlyphVertex> vertices;
|
||||||
|
std::vector<DrawCommand> drawcommands = generateVertices(codepoints, vertices);
|
||||||
|
|
||||||
|
Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
|
|
||||||
printv(t, drawcommands, vertices);
|
printv(t, drawcommands, vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Font::printf(const std::vector<ColoredString> &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
std::vector<GlyphVertex> vertices;
|
ColoredCodepoints codepoints;
|
||||||
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
|
getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
|
std::vector<GlyphVertex> vertices;
|
||||||
|
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, wrap, align, vertices);
|
||||||
|
|
||||||
|
Matrix4 t(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
|
|
||||||
printv(t, drawcommands, vertices);
|
printv(t, drawcommands, vertices);
|
||||||
}
|
}
|
||||||
@@ -636,15 +705,16 @@ int Font::getWidth(const std::string &str)
|
|||||||
while (getline(iss, line, '\n'))
|
while (getline(iss, line, '\n'))
|
||||||
{
|
{
|
||||||
int width = 0;
|
int width = 0;
|
||||||
|
uint32 prevglyph = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
uint32 prevglyph = 0;
|
|
||||||
|
|
||||||
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
|
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
|
||||||
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
|
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
|
||||||
|
|
||||||
while (i != end)
|
while (i != end)
|
||||||
{
|
{
|
||||||
uint32 c = *i++;
|
uint32 c = *i++;
|
||||||
|
|
||||||
const Glyph &g = findGlyph(c);
|
const Glyph &g = findGlyph(c);
|
||||||
width += g.spacing + getKerning(prevglyph, c);
|
width += g.spacing + getKerning(prevglyph, c);
|
||||||
|
|
||||||
@@ -668,69 +738,180 @@ int Font::getWidth(char character)
|
|||||||
return g.spacing;
|
return g.spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *linewidths, std::vector<bool> *wrappedlines)
|
void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *linewidths)
|
||||||
{
|
{
|
||||||
const float width_space = (float) getWidth(' ');
|
// Per-line info.
|
||||||
|
float width = 0.0f;
|
||||||
|
float widthbeforelastspace = 0.0f;
|
||||||
|
float widthoftrailingspace = 0.0f;
|
||||||
|
uint32 prevglyph = 0;
|
||||||
|
|
||||||
std::istringstream iss(text);
|
int lastspaceindex = -1;
|
||||||
std::string line;
|
|
||||||
std::ostringstream string_builder;
|
|
||||||
|
|
||||||
// Split text at newlines.
|
// Keeping the indexed colors "in sync" is a bit tricky, since we split
|
||||||
while (std::getline(iss, line, '\n'))
|
// things up and we might skip some glyphs but we don't want to skip any
|
||||||
|
// color which starts at those indices.
|
||||||
|
Color curcolor(255, 255, 255, 255);
|
||||||
|
bool addcurcolor = false;
|
||||||
|
int curcolori = -1;
|
||||||
|
int endcolori = (int) codepoints.colors.size() - 1;
|
||||||
|
|
||||||
|
// A wrapped line of text.
|
||||||
|
ColoredCodepoints wline;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (i < (int) codepoints.cps.size())
|
||||||
{
|
{
|
||||||
std::vector<std::string> words;
|
uint32 c = codepoints.cps[i];
|
||||||
std::istringstream word_iss(line);
|
|
||||||
|
|
||||||
// split line into words
|
// Determine the current color before doing anything else, to make sure
|
||||||
std::copy(std::istream_iterator<std::string>(word_iss), std::istream_iterator<std::string>(), std::back_inserter(words));
|
// it's still applied to future glyphs even if this one is skipped.
|
||||||
|
if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i)
|
||||||
float width = 0.0f;
|
|
||||||
float oldwidth = 0.0f;
|
|
||||||
string_builder.str("");
|
|
||||||
|
|
||||||
// Put words back together until a wrap occurs.
|
|
||||||
for (const std::string &word : words)
|
|
||||||
{
|
{
|
||||||
float wordwidth = (float) getWidth(word);
|
curcolor = codepoints.colors[curcolori + 1].color;
|
||||||
width += wordwidth;
|
curcolori++;
|
||||||
|
addcurcolor = true;
|
||||||
// On wordwrap, push line to line buffer and clear string builder.
|
|
||||||
if (width > wrap && oldwidth > 0)
|
|
||||||
{
|
|
||||||
int realw = (int) width;
|
|
||||||
|
|
||||||
// Remove trailing space.
|
|
||||||
std::string tmp = string_builder.str();
|
|
||||||
lines.push_back(tmp.substr(0,tmp.size()-1));
|
|
||||||
string_builder.str("");
|
|
||||||
width = wordwidth;
|
|
||||||
realw -= (int) width;
|
|
||||||
|
|
||||||
if (linewidths)
|
|
||||||
linewidths->push_back(realw);
|
|
||||||
|
|
||||||
// Indicate that this line was automatically wrapped.
|
|
||||||
if (wrappedlines)
|
|
||||||
wrappedlines->push_back(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
string_builder << word << " ";
|
|
||||||
|
|
||||||
width += width_space;
|
|
||||||
oldwidth = width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push last line.
|
// Split text at newlines.
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
lines.push_back(wline);
|
||||||
|
|
||||||
|
// Ignore the width of any trailing spaces, for individual lines.
|
||||||
|
if (linewidths)
|
||||||
|
linewidths->push_back(width - widthoftrailingspace);
|
||||||
|
|
||||||
|
// Make sure the new line keeps any color that was set previously.
|
||||||
|
addcurcolor = true;
|
||||||
|
|
||||||
|
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
|
||||||
|
prevglyph = 0; // Reset kerning information.
|
||||||
|
lastspaceindex = -1;
|
||||||
|
wline.cps.clear();
|
||||||
|
wline.colors.clear();
|
||||||
|
i++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Glyph &g = findGlyph(c);
|
||||||
|
float charwidth = g.spacing + getKerning(prevglyph, c);
|
||||||
|
float newwidth = width + charwidth;
|
||||||
|
|
||||||
|
// Wrap the line if it exceeds the wrap limit. Don't wrap yet if we're
|
||||||
|
// processing a newline character, though.
|
||||||
|
if (c != ' ' && newwidth > wraplimit)
|
||||||
|
{
|
||||||
|
// If this is the first character in the line and it exceeds the
|
||||||
|
// limit, skip it completely.
|
||||||
|
if (wline.cps.empty())
|
||||||
|
i++;
|
||||||
|
else if (lastspaceindex != -1)
|
||||||
|
{
|
||||||
|
// 'Rewind' to the last seen space, if the line has one.
|
||||||
|
// FIXME: This could be more efficient...
|
||||||
|
while (!wline.cps.empty() && wline.cps.back() != ' ')
|
||||||
|
wline.cps.pop_back();
|
||||||
|
|
||||||
|
while (!wline.colors.empty() && wline.colors.back().index >= (int) wline.cps.size())
|
||||||
|
wline.colors.pop_back();
|
||||||
|
|
||||||
|
// Also 'rewind' to the color that the last character is using.
|
||||||
|
for (int colori = curcolori; colori >= 0; colori--)
|
||||||
|
{
|
||||||
|
if (codepoints.colors[colori].index <= lastspaceindex)
|
||||||
|
{
|
||||||
|
curcolor = codepoints.colors[colori].color;
|
||||||
|
curcolori = colori;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the width of trailing spaces in wrapped lines.
|
||||||
|
width = widthbeforelastspace;
|
||||||
|
|
||||||
|
i = lastspaceindex;
|
||||||
|
i++; // Start the next line after the space.
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push_back(wline);
|
||||||
|
|
||||||
|
if (linewidths)
|
||||||
|
linewidths->push_back(width);
|
||||||
|
|
||||||
|
addcurcolor = true;
|
||||||
|
|
||||||
|
prevglyph = 0;
|
||||||
|
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
|
||||||
|
wline.cps.clear();
|
||||||
|
wline.colors.clear();
|
||||||
|
lastspaceindex = -1;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevglyph != ' ' && c == ' ')
|
||||||
|
widthbeforelastspace = width;
|
||||||
|
|
||||||
|
width = newwidth;
|
||||||
|
prevglyph = c;
|
||||||
|
|
||||||
|
if (addcurcolor)
|
||||||
|
{
|
||||||
|
wline.colors.push_back({curcolor, (int) wline.cps.size()});
|
||||||
|
addcurcolor = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wline.cps.push_back(c);
|
||||||
|
|
||||||
|
// Keep track of the last seen space, so we can "rewind" to it when
|
||||||
|
// wrapping.
|
||||||
|
if (c == ' ')
|
||||||
|
{
|
||||||
|
lastspaceindex = i;
|
||||||
|
widthoftrailingspace += charwidth;
|
||||||
|
}
|
||||||
|
else if (c != '\n')
|
||||||
|
widthoftrailingspace = 0.0f;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the last line.
|
||||||
|
if (!wline.cps.empty())
|
||||||
|
{
|
||||||
|
lines.push_back(wline);
|
||||||
|
|
||||||
|
// Ignore the width of any trailing spaces, for individual lines.
|
||||||
if (linewidths)
|
if (linewidths)
|
||||||
linewidths->push_back(width);
|
linewidths->push_back(width - widthoftrailingspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string tmp = string_builder.str();
|
void Font::getWrap(const std::string &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||||
lines.push_back(tmp.substr(0,tmp.size()-1));
|
{
|
||||||
|
ColoredCodepoints codepoints;
|
||||||
|
getCodepointsFromString(text, codepoints.cps);
|
||||||
|
|
||||||
// Indicate that this line was not automatically wrapped.
|
std::vector<ColoredCodepoints> codepointlines;
|
||||||
if (wrappedlines)
|
getWrap(codepoints, wraplimit, codepointlines, linewidths);
|
||||||
wrappedlines->push_back(false);
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
for (const ColoredCodepoints &codepoints : codepointlines)
|
||||||
|
{
|
||||||
|
line.clear();
|
||||||
|
line.reserve(codepoints.cps.size());
|
||||||
|
|
||||||
|
for (uint32 codepoint : codepoints.cps)
|
||||||
|
{
|
||||||
|
char character[5] = {'\0'};
|
||||||
|
char *end = utf8::unchecked::append(codepoint, character);
|
||||||
|
line.append(character, end - character);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push_back(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,10 +1051,10 @@ bool Font::getConstant(AlignMode in, const char *&out)
|
|||||||
|
|
||||||
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
|
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
|
||||||
{
|
{
|
||||||
{ "left", Font::ALIGN_LEFT },
|
{ "left", ALIGN_LEFT },
|
||||||
{ "right", Font::ALIGN_RIGHT },
|
{ "right", ALIGN_RIGHT },
|
||||||
{ "center", Font::ALIGN_CENTER },
|
{ "center", ALIGN_CENTER },
|
||||||
{ "justify", Font::ALIGN_JUSTIFY },
|
{ "justify", ALIGN_JUSTIFY },
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
|
StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ class Font : public Object, public Volatile
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
typedef std::vector<uint32> Codepoints;
|
||||||
|
|
||||||
enum AlignMode
|
enum AlignMode
|
||||||
{
|
{
|
||||||
ALIGN_LEFT,
|
ALIGN_LEFT,
|
||||||
@@ -59,10 +61,29 @@ public:
|
|||||||
ALIGN_MAX_ENUM
|
ALIGN_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ColoredString
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
Color color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IndexedColor
|
||||||
|
{
|
||||||
|
Color color;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColoredCodepoints
|
||||||
|
{
|
||||||
|
std::vector<uint32> cps;
|
||||||
|
std::vector<IndexedColor> colors;
|
||||||
|
};
|
||||||
|
|
||||||
struct GlyphVertex
|
struct GlyphVertex
|
||||||
{
|
{
|
||||||
float x, y;
|
float x, y;
|
||||||
float s, t;
|
uint16 s, t;
|
||||||
|
Color color;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextInfo
|
struct TextInfo
|
||||||
@@ -77,27 +98,22 @@ public:
|
|||||||
GLuint texture;
|
GLuint texture;
|
||||||
int startvertex;
|
int startvertex;
|
||||||
int vertexcount;
|
int vertexcount;
|
||||||
|
|
||||||
// used when sorting with std::sort.
|
|
||||||
bool operator < (const DrawCommand &other) const
|
|
||||||
{
|
|
||||||
// Texture binds are expensive, so we should sort by that first.
|
|
||||||
if (texture != other.texture)
|
|
||||||
return texture < other.texture;
|
|
||||||
else
|
|
||||||
return startvertex < other.startvertex;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter());
|
Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter());
|
||||||
|
|
||||||
virtual ~Font();
|
virtual ~Font();
|
||||||
|
|
||||||
|
std::vector<DrawCommand> generateVertices(const ColoredCodepoints &codepoints, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr);
|
||||||
std::vector<DrawCommand> generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr);
|
std::vector<DrawCommand> generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr);
|
||||||
std::vector<DrawCommand> generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
|
|
||||||
|
std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
|
||||||
|
|
||||||
void drawVertices(const std::vector<DrawCommand> &drawcommands);
|
void drawVertices(const std::vector<DrawCommand> &drawcommands);
|
||||||
|
|
||||||
|
static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
|
||||||
|
static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the text at the designated position with rotation and scaling.
|
* Prints the text at the designated position with rotation and scaling.
|
||||||
*
|
*
|
||||||
@@ -112,9 +128,9 @@ public:
|
|||||||
* @param kx Shear along the x axis.
|
* @param kx Shear along the x axis.
|
||||||
* @param ky Shear along the y axis.
|
* @param ky Shear along the y axis.
|
||||||
**/
|
**/
|
||||||
void print(const std::string &text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
void print(const std::vector<ColoredString> &text, float x, float y, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
||||||
|
|
||||||
void printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
void printf(const std::vector<ColoredString> &text, float x, float y, float wrap, AlignMode align, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the height of the font.
|
* Returns the height of the font.
|
||||||
@@ -140,13 +156,12 @@ public:
|
|||||||
* and optionally the number of lines
|
* and optionally the number of lines
|
||||||
*
|
*
|
||||||
* @param text The input text
|
* @param text The input text
|
||||||
* @param wrap The number of pixels to wrap at
|
* @param wraplimit The number of pixels to wrap at
|
||||||
* @param max_width Optional output of the maximum width
|
* @param max_width Optional output of the maximum width
|
||||||
* @param wrapped_lines Optional output indicating which lines were
|
|
||||||
* auto-wrapped. Indices correspond to indices of the returned value.
|
|
||||||
* Returns a vector with the lines.
|
* Returns a vector with the lines.
|
||||||
**/
|
**/
|
||||||
void getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *line_widths = 0, std::vector<bool> *wrapped_lines = 0);
|
void getWrap(const std::string &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *line_widths = nullptr);
|
||||||
|
void getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *line_widths = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the line height (which should be a number to multiply the font size by,
|
* Sets the line height (which should be a number to multiply the font size by,
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ void *GLBuffer::map()
|
|||||||
|
|
||||||
void GLBuffer::unmapStatic(size_t offset, size_t size)
|
void GLBuffer::unmapStatic(size_t offset, size_t size)
|
||||||
{
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// Upload the mapped data to the buffer.
|
// Upload the mapped data to the buffer.
|
||||||
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
||||||
}
|
}
|
||||||
@@ -163,12 +166,10 @@ void GLBuffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
|
|||||||
// from the start of section a to the end of section b as modified if both
|
// from the start of section a to the end of section b as modified if both
|
||||||
// a and b are marked as modified.
|
// a and b are marked as modified.
|
||||||
|
|
||||||
size_t old_range_end = modified_offset + modified_offset;
|
size_t old_range_end = modified_offset + modified_size;
|
||||||
|
|
||||||
modified_offset = std::min(modified_offset, offset);
|
modified_offset = std::min(modified_offset, offset);
|
||||||
|
|
||||||
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
|
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
|
||||||
|
|
||||||
modified_size = new_range_end - modified_offset;
|
modified_size = new_range_end - modified_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ Graphics::~Graphics()
|
|||||||
Shader::defaultShader->release();
|
Shader::defaultShader->release();
|
||||||
Shader::defaultShader = nullptr;
|
Shader::defaultShader = nullptr;
|
||||||
}
|
}
|
||||||
|
if (Shader::defaultVideoShader)
|
||||||
|
{
|
||||||
|
Shader::defaultVideoShader->release();
|
||||||
|
Shader::defaultVideoShader = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (quadIndices)
|
if (quadIndices)
|
||||||
delete quadIndices;
|
delete quadIndices;
|
||||||
@@ -110,7 +115,7 @@ void Graphics::restoreState(const DisplayState &s)
|
|||||||
setPointSize(s.pointSize);
|
setPointSize(s.pointSize);
|
||||||
|
|
||||||
if (s.scissor)
|
if (s.scissor)
|
||||||
setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h);
|
setScissor(s.scissorRect.x, s.scissorRect.y, s.scissorRect.w, s.scissorRect.h);
|
||||||
else
|
else
|
||||||
setScissor();
|
setScissor();
|
||||||
|
|
||||||
@@ -131,11 +136,10 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
|||||||
{
|
{
|
||||||
const DisplayState &cur = states.back();
|
const DisplayState &cur = states.back();
|
||||||
|
|
||||||
if (*(uint32 *) &s.color.r != *(uint32 *) &cur.color.r)
|
if (s.color != cur.color)
|
||||||
setColor(s.color);
|
setColor(s.color);
|
||||||
|
|
||||||
if (*(uint32 *) &s.backgroundColor.r != *(uint32 *) &cur.backgroundColor.r)
|
setBackgroundColor(s.backgroundColor);
|
||||||
setBackgroundColor(s.backgroundColor);
|
|
||||||
|
|
||||||
if (s.blendMode != cur.blendMode || s.blendMultiplyAlpha != cur.blendMultiplyAlpha)
|
if (s.blendMode != cur.blendMode || s.blendMultiplyAlpha != cur.blendMultiplyAlpha)
|
||||||
setBlendMode(s.blendMode, s.blendMultiplyAlpha);
|
setBlendMode(s.blendMode, s.blendMultiplyAlpha);
|
||||||
@@ -148,10 +152,10 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
|||||||
if (s.pointSize != cur.pointSize)
|
if (s.pointSize != cur.pointSize)
|
||||||
setPointSize(s.pointSize);
|
setPointSize(s.pointSize);
|
||||||
|
|
||||||
if (s.scissor != cur.scissor || (s.scissor && !(s.scissorBox == cur.scissorBox)))
|
if (s.scissor != cur.scissor || (s.scissor && !(s.scissorRect == cur.scissorRect)))
|
||||||
{
|
{
|
||||||
if (s.scissor)
|
if (s.scissor)
|
||||||
setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h);
|
setScissor(s.scissorRect.x, s.scissorRect.y, s.scissorRect.w, s.scissorRect.h);
|
||||||
else
|
else
|
||||||
setScissor();
|
setScissor();
|
||||||
}
|
}
|
||||||
@@ -295,6 +299,10 @@ bool Graphics::setMode(int width, int height)
|
|||||||
|
|
||||||
setDebug(enabledebug);
|
setDebug(enabledebug);
|
||||||
|
|
||||||
|
// Reload all volatile objects.
|
||||||
|
if (!Volatile::loadAll())
|
||||||
|
::printf("Could not reload all volatile objects.\n");
|
||||||
|
|
||||||
// Create a quad indices object owned by love.graphics, so at least one
|
// Create a quad indices object owned by love.graphics, so at least one
|
||||||
// QuadIndices object is alive at all times while love.graphics is alive.
|
// QuadIndices object is alive at all times while love.graphics is alive.
|
||||||
// This makes sure there aren't too many expensive destruction/creations of
|
// This makes sure there aren't too many expensive destruction/creations of
|
||||||
@@ -303,10 +311,6 @@ bool Graphics::setMode(int width, int height)
|
|||||||
if (quadIndices == nullptr)
|
if (quadIndices == nullptr)
|
||||||
quadIndices = new QuadIndices(20);
|
quadIndices = new QuadIndices(20);
|
||||||
|
|
||||||
// Reload all volatile objects.
|
|
||||||
if (!Volatile::loadAll())
|
|
||||||
::printf("Could not reload all volatile objects.\n");
|
|
||||||
|
|
||||||
// Restore the graphics state.
|
// Restore the graphics state.
|
||||||
restoreState(states.back());
|
restoreState(states.back());
|
||||||
|
|
||||||
@@ -321,6 +325,13 @@ bool Graphics::setMode(int width, int height)
|
|||||||
Shader::defaultShader = newShader(Shader::defaultCode[renderer]);
|
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
|
// A shader should always be active, but the default shader shouldn't be
|
||||||
// returned by getShader(), so we don't do setShader(defaultShader).
|
// returned by getShader(), so we don't do setShader(defaultShader).
|
||||||
if (!Shader::current)
|
if (!Shader::current)
|
||||||
@@ -581,14 +592,35 @@ bool Graphics::isCreated() const
|
|||||||
|
|
||||||
void Graphics::setScissor(int x, int y, int width, int height)
|
void Graphics::setScissor(int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
OpenGL::Viewport box = {x, y, width, height};
|
ScissorRect rect = {x, y, width, height};
|
||||||
|
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
// OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
|
||||||
gl.setScissor(box);
|
gl.setScissor({rect.x, rect.y, rect.w, rect.h});
|
||||||
|
|
||||||
states.back().scissor = true;
|
states.back().scissor = true;
|
||||||
states.back().scissorBox = box;
|
states.back().scissorRect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::intersectScissor(int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
ScissorRect rect = states.back().scissorRect;
|
||||||
|
|
||||||
|
if (!states.back().scissor)
|
||||||
|
{
|
||||||
|
rect.x = 0;
|
||||||
|
rect.y = 0;
|
||||||
|
rect.w = std::numeric_limits<int>::max();
|
||||||
|
rect.h = std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
int x1 = std::max(rect.x, x);
|
||||||
|
int y1 = std::max(rect.y, y);
|
||||||
|
|
||||||
|
int x2 = std::min(rect.x + rect.w, x + width);
|
||||||
|
int y2 = std::min(rect.y + rect.h, y + height);
|
||||||
|
|
||||||
|
setScissor(x1, y1, std::max(0, x2 - x1), std::max(0, y2 - y1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setScissor()
|
void Graphics::setScissor()
|
||||||
@@ -601,10 +633,10 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const
|
|||||||
{
|
{
|
||||||
const DisplayState &state = states.back();
|
const DisplayState &state = states.back();
|
||||||
|
|
||||||
x = state.scissorBox.x;
|
x = state.scissorRect.x;
|
||||||
y = state.scissorBox.y;
|
y = state.scissorRect.y;
|
||||||
width = state.scissorBox.w;
|
width = state.scissorRect.w;
|
||||||
height = state.scissorBox.h;
|
height = state.scissorRect.h;
|
||||||
|
|
||||||
return state.scissor;
|
return state.scissor;
|
||||||
}
|
}
|
||||||
@@ -688,7 +720,7 @@ Image *Graphics::newImage(const std::vector<love::image::CompressedImageData *>
|
|||||||
return new Image(cdata, flags);
|
return new Image(cdata, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quad *Graphics::newQuad(Quad::Viewport v, float sw, float sh)
|
Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
|
||||||
{
|
{
|
||||||
return new Quad(v, sw, sh);
|
return new Quad(v, sw, sh);
|
||||||
}
|
}
|
||||||
@@ -794,11 +826,16 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, con
|
|||||||
return new Mesh(vertexformat, data, datasize, drawmode, usage);
|
return new Mesh(vertexformat, data, datasize, drawmode, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
Text *Graphics::newText(Font *font, const std::string &text)
|
Text *Graphics::newText(Font *font, const std::vector<Font::ColoredString> &text)
|
||||||
{
|
{
|
||||||
return new Text(font, text);
|
return new Text(font, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Video *Graphics::newVideo(love::video::VideoStream *stream)
|
||||||
|
{
|
||||||
|
return new Video(stream);
|
||||||
|
}
|
||||||
|
|
||||||
bool Graphics::isGammaCorrect() const
|
bool Graphics::isGammaCorrect() const
|
||||||
{
|
{
|
||||||
return love::graphics::isGammaCorrect();
|
return love::graphics::isGammaCorrect();
|
||||||
@@ -1085,7 +1122,7 @@ bool Graphics::isWireframe() const
|
|||||||
return states.back().wireframe;
|
return states.back().wireframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Graphics::print(const std::vector<Font::ColoredString> &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
@@ -1095,7 +1132,7 @@ void Graphics::print(const std::string &str, float x, float y , float angle, flo
|
|||||||
state.font->print(str, x, y, angle, sx, sy, ox, oy, kx, ky);
|
state.font->print(str, x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Graphics::printf(const std::vector<Font::ColoredString> &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
@@ -1278,21 +1315,8 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
|
|||||||
coords[2 * (i+1) + 1] = y + radius * sinf(phi);
|
coords[2 * (i+1) + 1] = y + radius * sinf(phi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
|
// NOTE: We rely on polygon() using GL_TRIANGLE_FAN, when fill mode is used.
|
||||||
if (mode == DRAW_LINE)
|
polygon(mode, coords, num_coords);
|
||||||
{
|
|
||||||
polyline(coords, num_coords); // Artifacts at sharp angles if set to looping.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OpenGL::TempDebugGroup debuggroup("Filled arc draw");
|
|
||||||
|
|
||||||
gl.prepareDraw();
|
|
||||||
gl.bindTexture(gl.getDefaultTexture());
|
|
||||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS);
|
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
|
|
||||||
gl.drawArrays(GL_TRIANGLE_FAN, 0, points + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
}
|
}
|
||||||
@@ -1482,10 +1506,10 @@ bool Graphics::isSupported(Support feature) const
|
|||||||
{
|
{
|
||||||
switch (feature)
|
switch (feature)
|
||||||
{
|
{
|
||||||
case SUPPORT_MULTI_CANVAS:
|
|
||||||
return Canvas::isMultiCanvasSupported();
|
|
||||||
case SUPPORT_MULTI_CANVAS_FORMATS:
|
case SUPPORT_MULTI_CANVAS_FORMATS:
|
||||||
return Canvas::isMultiFormatMultiCanvasSupported();
|
return Canvas::isMultiFormatMultiCanvasSupported();
|
||||||
|
case SUPPORT_CLAMP_ZERO:
|
||||||
|
return gl.isClampZeroTextureWrapSupported();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@
|
|||||||
|
|
||||||
#include "window/Window.h"
|
#include "window/Window.h"
|
||||||
|
|
||||||
|
#include "video/VideoStream.h"
|
||||||
|
|
||||||
#include "Font.h"
|
#include "Font.h"
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
#include "graphics/Quad.h"
|
#include "graphics/Quad.h"
|
||||||
@@ -47,6 +49,7 @@
|
|||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "Mesh.h"
|
#include "Mesh.h"
|
||||||
#include "Text.h"
|
#include "Text.h"
|
||||||
|
#include "Video.h"
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -126,6 +129,8 @@ public:
|
|||||||
**/
|
**/
|
||||||
void setScissor(int x, int y, int width, int height);
|
void setScissor(int x, int y, int width, int height);
|
||||||
|
|
||||||
|
void intersectScissor(int x, int y, int width, int height);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears any scissor that has been created.
|
* Clears any scissor that has been created.
|
||||||
**/
|
**/
|
||||||
@@ -160,7 +165,7 @@ public:
|
|||||||
Image *newImage(const std::vector<love::image::ImageData *> &data, const Image::Flags &flags);
|
Image *newImage(const std::vector<love::image::ImageData *> &data, const Image::Flags &flags);
|
||||||
Image *newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Flags &flags);
|
Image *newImage(const std::vector<love::image::CompressedImageData *> &cdata, const Image::Flags &flags);
|
||||||
|
|
||||||
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
Quad *newQuad(Quad::Viewport v, double sw, double sh);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Font object.
|
* Creates a Font object.
|
||||||
@@ -181,7 +186,9 @@ public:
|
|||||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||||
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, Mesh::Usage usage);
|
||||||
|
|
||||||
Text *newText(Font *font, const std::string &text = "");
|
Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||||
|
|
||||||
|
Video *newVideo(love::video::VideoStream *stream);
|
||||||
|
|
||||||
bool isGammaCorrect() const;
|
bool isGammaCorrect() const;
|
||||||
|
|
||||||
@@ -326,7 +333,7 @@ public:
|
|||||||
* @param kx Shear along the x-axis.
|
* @param kx Shear along the x-axis.
|
||||||
* @param ky Shear along the y-axis.
|
* @param ky Shear along the y-axis.
|
||||||
**/
|
**/
|
||||||
void print(const std::string &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
void print(const std::vector<Font::ColoredString> &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw formatted text on screen at the specified coordinates.
|
* Draw formatted text on screen at the specified coordinates.
|
||||||
@@ -344,7 +351,7 @@ public:
|
|||||||
* @param kx Shear along the x-axis.
|
* @param kx Shear along the x-axis.
|
||||||
* @param ky Shear along the y-axis.
|
* @param ky Shear along the y-axis.
|
||||||
**/
|
**/
|
||||||
void printf(const std::string &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
void printf(const std::vector<Font::ColoredString> &str, float x, float y, float wrap, Font::AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a point at (x,y).
|
* Draws a point at (x,y).
|
||||||
@@ -478,7 +485,7 @@ private:
|
|||||||
float pointSize = 1.0f;
|
float pointSize = 1.0f;
|
||||||
|
|
||||||
bool scissor = false;
|
bool scissor = false;
|
||||||
OpenGL::Viewport scissorBox = OpenGL::Viewport();
|
ScissorRect scissorRect = ScissorRect();
|
||||||
|
|
||||||
// Stencil.
|
// Stencil.
|
||||||
bool stencilTest = false;
|
bool stencilTest = false;
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ bool Image::loadVolatile()
|
|||||||
|
|
||||||
GLenum glerr = glGetError();
|
GLenum glerr = glGetError();
|
||||||
if (glerr != GL_NO_ERROR)
|
if (glerr != GL_NO_ERROR)
|
||||||
throw love::Exception("Cannot create image (error code 0x%x)", glerr);
|
throw love::Exception("Cannot create image (OpenGL error: %s)", OpenGL::errorString(glerr));
|
||||||
}
|
}
|
||||||
catch (love::Exception &)
|
catch (love::Exception &)
|
||||||
{
|
{
|
||||||
@@ -531,8 +531,16 @@ bool Image::setWrap(const Texture::Wrap &w)
|
|||||||
wrap.s = wrap.t = WRAP_CLAMP;
|
wrap.s = wrap.t = WRAP_CLAMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gl.isClampZeroTextureWrapSupported())
|
||||||
|
{
|
||||||
|
if (wrap.s == WRAP_CLAMP_ZERO)
|
||||||
|
wrap.s = WRAP_CLAMP;
|
||||||
|
if (wrap.t == WRAP_CLAMP_ZERO)
|
||||||
|
wrap.t = WRAP_CLAMP;
|
||||||
|
}
|
||||||
|
|
||||||
gl.bindTexture(texture);
|
gl.bindTexture(texture);
|
||||||
gl.setTextureWrap(w);
|
gl.setTextureWrap(wrap);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@@ -589,112 +597,101 @@ bool Image::isCompressed() const
|
|||||||
|
|
||||||
GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
|
GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
|
||||||
{
|
{
|
||||||
|
using image::CompressedImageData;
|
||||||
|
|
||||||
switch (cformat)
|
switch (cformat)
|
||||||
{
|
{
|
||||||
case image::CompressedImageData::FORMAT_DXT1:
|
case CompressedImageData::FORMAT_DXT1:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||||
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
case CompressedImageData::FORMAT_DXT3:
|
||||||
else
|
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||||
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
case CompressedImageData::FORMAT_DXT5:
|
||||||
case image::CompressedImageData::FORMAT_DXT3:
|
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||||
if (isSRGB)
|
case CompressedImageData::FORMAT_BC4:
|
||||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
|
|
||||||
else
|
|
||||||
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
||||||
case image::CompressedImageData::FORMAT_DXT5:
|
|
||||||
if (isSRGB)
|
|
||||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
|
||||||
else
|
|
||||||
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
||||||
case image::CompressedImageData::FORMAT_BC4:
|
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_RED_RGTC1;
|
return GL_COMPRESSED_RED_RGTC1;
|
||||||
case image::CompressedImageData::FORMAT_BC4s:
|
case CompressedImageData::FORMAT_BC4s:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_SIGNED_RED_RGTC1;
|
return GL_COMPRESSED_SIGNED_RED_RGTC1;
|
||||||
case image::CompressedImageData::FORMAT_BC5:
|
case CompressedImageData::FORMAT_BC5:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_RG_RGTC2;
|
return GL_COMPRESSED_RG_RGTC2;
|
||||||
case image::CompressedImageData::FORMAT_BC5s:
|
case CompressedImageData::FORMAT_BC5s:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
||||||
case image::CompressedImageData::FORMAT_BC6H:
|
case CompressedImageData::FORMAT_BC6H:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
|
return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
|
||||||
case image::CompressedImageData::FORMAT_BC6Hs:
|
case CompressedImageData::FORMAT_BC6Hs:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
|
return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
|
||||||
case image::CompressedImageData::FORMAT_BC7:
|
case CompressedImageData::FORMAT_BC7:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : GL_COMPRESSED_RGBA_BPTC_UNORM;
|
||||||
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
|
case CompressedImageData::FORMAT_PVR1_RGB2:
|
||||||
else
|
return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||||
return GL_COMPRESSED_RGBA_BPTC_UNORM;
|
case CompressedImageData::FORMAT_PVR1_RGB4:
|
||||||
case image::CompressedImageData::FORMAT_ETC1:
|
return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||||
|
case CompressedImageData::FORMAT_PVR1_RGBA2:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||||
|
case CompressedImageData::FORMAT_PVR1_RGBA4:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT : GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||||
|
case CompressedImageData::FORMAT_ETC1:
|
||||||
// The ETC2 format can load ETC1 textures.
|
// The ETC2 format can load ETC1 textures.
|
||||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
|
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
|
||||||
{
|
return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
|
||||||
if (isSRGB)
|
|
||||||
return GL_COMPRESSED_SRGB8_ETC2;
|
|
||||||
else
|
|
||||||
return GL_COMPRESSED_RGB8_ETC2;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_ETC1_RGB8_OES;
|
return GL_ETC1_RGB8_OES;
|
||||||
}
|
}
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGB:
|
case CompressedImageData::FORMAT_ETC2_RGB:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
|
||||||
return GL_COMPRESSED_SRGB8_ETC2;
|
case CompressedImageData::FORMAT_ETC2_RGBA:
|
||||||
else
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC;
|
||||||
return GL_COMPRESSED_RGB8_ETC2;
|
case CompressedImageData::FORMAT_ETC2_RGBA1:
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGBA:
|
return isSRGB ? GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
|
||||||
if (isSRGB)
|
case CompressedImageData::FORMAT_EAC_R:
|
||||||
return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
|
|
||||||
else
|
|
||||||
return GL_COMPRESSED_RGBA8_ETC2_EAC;
|
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGBA1:
|
|
||||||
if (isSRGB)
|
|
||||||
return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;
|
|
||||||
else
|
|
||||||
return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
|
|
||||||
case image::CompressedImageData::FORMAT_EAC_R:
|
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_R11_EAC;
|
return GL_COMPRESSED_R11_EAC;
|
||||||
case image::CompressedImageData::FORMAT_EAC_Rs:
|
case CompressedImageData::FORMAT_EAC_Rs:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_SIGNED_R11_EAC;
|
return GL_COMPRESSED_SIGNED_R11_EAC;
|
||||||
case image::CompressedImageData::FORMAT_EAC_RG:
|
case CompressedImageData::FORMAT_EAC_RG:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_RG11_EAC;
|
return GL_COMPRESSED_RG11_EAC;
|
||||||
case image::CompressedImageData::FORMAT_EAC_RGs:
|
case CompressedImageData::FORMAT_EAC_RGs:
|
||||||
isSRGB = false;
|
isSRGB = false;
|
||||||
return GL_COMPRESSED_SIGNED_RG11_EAC;
|
return GL_COMPRESSED_SIGNED_RG11_EAC;
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGB2:
|
case CompressedImageData::FORMAT_ASTC_4x4:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
|
||||||
return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
|
case CompressedImageData::FORMAT_ASTC_5x4:
|
||||||
else
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
|
||||||
return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
case CompressedImageData::FORMAT_ASTC_5x5:
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGB4:
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
|
||||||
if (isSRGB)
|
case CompressedImageData::FORMAT_ASTC_6x5:
|
||||||
return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
|
||||||
else
|
case CompressedImageData::FORMAT_ASTC_6x6:
|
||||||
return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGBA2:
|
case CompressedImageData::FORMAT_ASTC_8x5:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
|
||||||
return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
|
case CompressedImageData::FORMAT_ASTC_8x6:
|
||||||
else
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
|
||||||
return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
case CompressedImageData::FORMAT_ASTC_8x8:
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGBA4:
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
|
||||||
if (isSRGB)
|
case CompressedImageData::FORMAT_ASTC_10x5:
|
||||||
return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
|
||||||
else
|
case CompressedImageData::FORMAT_ASTC_10x6:
|
||||||
return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x8:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x10:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
|
||||||
|
case CompressedImageData::FORMAT_ASTC_12x10:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
|
||||||
|
case CompressedImageData::FORMAT_ASTC_12x12:
|
||||||
|
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
|
||||||
default:
|
default:
|
||||||
if (isSRGB)
|
return isSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||||
return GL_SRGB8_ALPHA8;
|
|
||||||
else
|
|
||||||
return GL_RGBA8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,42 +702,56 @@ bool Image::hasAnisotropicFilteringSupport()
|
|||||||
|
|
||||||
bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
|
bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
|
||||||
{
|
{
|
||||||
|
using image::CompressedImageData;
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case image::CompressedImageData::FORMAT_DXT1:
|
case CompressedImageData::FORMAT_DXT1:
|
||||||
return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
|
return GLAD_EXT_texture_compression_s3tc || GLAD_EXT_texture_compression_dxt1;
|
||||||
case image::CompressedImageData::FORMAT_DXT3:
|
case CompressedImageData::FORMAT_DXT3:
|
||||||
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3;
|
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt3;
|
||||||
case image::CompressedImageData::FORMAT_DXT5:
|
case CompressedImageData::FORMAT_DXT5:
|
||||||
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
|
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
|
||||||
case image::CompressedImageData::FORMAT_BC4:
|
case CompressedImageData::FORMAT_BC4:
|
||||||
case image::CompressedImageData::FORMAT_BC4s:
|
case CompressedImageData::FORMAT_BC4s:
|
||||||
case image::CompressedImageData::FORMAT_BC5:
|
case CompressedImageData::FORMAT_BC5:
|
||||||
case image::CompressedImageData::FORMAT_BC5s:
|
case CompressedImageData::FORMAT_BC5s:
|
||||||
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
|
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
|
||||||
case image::CompressedImageData::FORMAT_BC6H:
|
case CompressedImageData::FORMAT_BC6H:
|
||||||
case image::CompressedImageData::FORMAT_BC6Hs:
|
case CompressedImageData::FORMAT_BC6Hs:
|
||||||
case image::CompressedImageData::FORMAT_BC7:
|
case CompressedImageData::FORMAT_BC7:
|
||||||
return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc;
|
return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc;
|
||||||
case image::CompressedImageData::FORMAT_ETC1:
|
case CompressedImageData::FORMAT_PVR1_RGB2:
|
||||||
|
case CompressedImageData::FORMAT_PVR1_RGB4:
|
||||||
|
case CompressedImageData::FORMAT_PVR1_RGBA2:
|
||||||
|
case CompressedImageData::FORMAT_PVR1_RGBA4:
|
||||||
|
return sRGB ? GLAD_EXT_pvrtc_sRGB : GLAD_IMG_texture_compression_pvrtc;
|
||||||
|
case CompressedImageData::FORMAT_ETC1:
|
||||||
// ETC2 support guarantees ETC1 support as well.
|
// ETC2 support guarantees ETC1 support as well.
|
||||||
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
|
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGB:
|
case CompressedImageData::FORMAT_ETC2_RGB:
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGBA:
|
case CompressedImageData::FORMAT_ETC2_RGBA:
|
||||||
case image::CompressedImageData::FORMAT_ETC2_RGBA1:
|
case CompressedImageData::FORMAT_ETC2_RGBA1:
|
||||||
case image::CompressedImageData::FORMAT_EAC_R:
|
case CompressedImageData::FORMAT_EAC_R:
|
||||||
case image::CompressedImageData::FORMAT_EAC_Rs:
|
case CompressedImageData::FORMAT_EAC_Rs:
|
||||||
case image::CompressedImageData::FORMAT_EAC_RG:
|
case CompressedImageData::FORMAT_EAC_RG:
|
||||||
case image::CompressedImageData::FORMAT_EAC_RGs:
|
case CompressedImageData::FORMAT_EAC_RGs:
|
||||||
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility;
|
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility;
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGB2:
|
case CompressedImageData::FORMAT_ASTC_4x4:
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGB4:
|
case CompressedImageData::FORMAT_ASTC_5x4:
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGBA2:
|
case CompressedImageData::FORMAT_ASTC_5x5:
|
||||||
case image::CompressedImageData::FORMAT_PVR1_RGBA4:
|
case CompressedImageData::FORMAT_ASTC_6x5:
|
||||||
if (sRGB)
|
case CompressedImageData::FORMAT_ASTC_6x6:
|
||||||
return GLAD_EXT_pvrtc_sRGB;
|
case CompressedImageData::FORMAT_ASTC_8x5:
|
||||||
else
|
case CompressedImageData::FORMAT_ASTC_8x6:
|
||||||
return GLAD_IMG_texture_compression_pvrtc;
|
case CompressedImageData::FORMAT_ASTC_8x8:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x5:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x6:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x8:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_10x10:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_12x10:
|
||||||
|
case CompressedImageData::FORMAT_ASTC_12x12:
|
||||||
|
return /*GLAD_ES_VERSION_3_2 ||*/ GLAD_KHR_texture_compression_astc_ldr;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -763,8 +774,8 @@ bool Image::getConstant(FlagType in, const char *&out)
|
|||||||
|
|
||||||
StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM>::Entry Image::flagNameEntries[] =
|
StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM>::Entry Image::flagNameEntries[] =
|
||||||
{
|
{
|
||||||
{"mipmaps", Image::FLAG_TYPE_MIPMAPS},
|
{"mipmaps", FLAG_TYPE_MIPMAPS},
|
||||||
{"linear", Image::FLAG_TYPE_LINEAR},
|
{"linear", FLAG_TYPE_LINEAR},
|
||||||
};
|
};
|
||||||
|
|
||||||
StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM> Image::flagNames(Image::flagNameEntries, sizeof(Image::flagNameEntries));
|
StringMap<Image::FlagType, Image::FLAG_TYPE_MAX_ENUM> Image::flagNames(Image::flagNameEntries, sizeof(Image::flagNameEntries));
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size
|
|||||||
, vertexCount(0)
|
, vertexCount(0)
|
||||||
, vertexStride(0)
|
, vertexStride(0)
|
||||||
, ibo(nullptr)
|
, ibo(nullptr)
|
||||||
|
, useIndexBuffer(false)
|
||||||
, elementCount(0)
|
, elementCount(0)
|
||||||
, elementDataType(0)
|
, elementDataType(0)
|
||||||
, drawMode(drawmode)
|
, drawMode(drawmode)
|
||||||
@@ -90,6 +91,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
|
|||||||
, vertexCount((size_t) vertexcount)
|
, vertexCount((size_t) vertexcount)
|
||||||
, vertexStride(0)
|
, vertexStride(0)
|
||||||
, ibo(nullptr)
|
, ibo(nullptr)
|
||||||
|
, useIndexBuffer(false)
|
||||||
, elementCount(0)
|
, elementCount(0)
|
||||||
, elementDataType(getGLDataTypeFromMax(vertexcount))
|
, elementDataType(getGLDataTypeFromMax(vertexcount))
|
||||||
, drawMode(drawmode)
|
, drawMode(drawmode)
|
||||||
@@ -147,7 +149,7 @@ void Mesh::setupAttachedAttributes()
|
|||||||
if (attachedAttributes.find(name) != attachedAttributes.end())
|
if (attachedAttributes.find(name) != attachedAttributes.end())
|
||||||
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
|
||||||
|
|
||||||
attachedAttributes[name] = {this, i, true};
|
attachedAttributes[name] = {this, (int) i, true};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +287,17 @@ Mesh::DataType Mesh::getAttributeInfo(int attribindex, int &components) const
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Mesh::getAttributeIndex(const std::string &name) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int) vertexFormat.size(); i++)
|
||||||
|
{
|
||||||
|
if (vertexFormat[i].name == name)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
|
void Mesh::setAttributeEnabled(const std::string &name, bool enable)
|
||||||
{
|
{
|
||||||
auto it = attachedAttributes.find(name);
|
auto it = attachedAttributes.find(name);
|
||||||
@@ -326,20 +339,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
|
|||||||
oldattrib = it->second;
|
oldattrib = it->second;
|
||||||
|
|
||||||
newattrib.mesh = mesh;
|
newattrib.mesh = mesh;
|
||||||
newattrib.index = std::numeric_limits<size_t>::max();
|
|
||||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||||
|
newattrib.index = mesh->getAttributeIndex(name);
|
||||||
|
|
||||||
// Find the index of the attribute in the mesh.
|
if (newattrib.index < 0)
|
||||||
for (size_t i = 0; i < mesh->vertexFormat.size(); i++)
|
|
||||||
{
|
|
||||||
if (mesh->vertexFormat[i].name == name)
|
|
||||||
{
|
|
||||||
newattrib.index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newattrib.index == std::numeric_limits<size_t>::max())
|
|
||||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||||
|
|
||||||
if (newattrib.mesh != this)
|
if (newattrib.mesh != this)
|
||||||
@@ -357,11 +360,10 @@ void *Mesh::mapVertexData()
|
|||||||
return vbo->map();
|
return vbo->map();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::unmapVertexData()
|
void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
|
||||||
{
|
{
|
||||||
// Assume the whole buffer was modified.
|
|
||||||
GLBuffer::Bind bind(*vbo);
|
GLBuffer::Bind bind(*vbo);
|
||||||
vbo->setMappedRangeModified(0, vbo->getSize());
|
vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||||
vbo->unmap();
|
vbo->unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,6 +416,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
|||||||
if (!ibo && size > 0)
|
if (!ibo && size > 0)
|
||||||
ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage());
|
ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage());
|
||||||
|
|
||||||
|
useIndexBuffer = true;
|
||||||
elementCount = map.size();
|
elementCount = map.size();
|
||||||
|
|
||||||
if (!ibo || elementCount == 0)
|
if (!ibo || elementCount == 0)
|
||||||
@@ -437,6 +440,11 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
|||||||
elementDataType = datatype;
|
elementDataType = datatype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mesh::setVertexMap()
|
||||||
|
{
|
||||||
|
useIndexBuffer = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies index data from a mapped buffer to a vector.
|
* Copies index data from a mapped buffer to a vector.
|
||||||
**/
|
**/
|
||||||
@@ -448,14 +456,17 @@ static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32>
|
|||||||
indices.push_back((uint32) elems[i]);
|
indices.push_back((uint32) elems[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::getVertexMap(std::vector<uint32> &map) const
|
bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||||
{
|
{
|
||||||
if (!ibo || elementCount == 0)
|
if (!useIndexBuffer)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
map.clear();
|
map.clear();
|
||||||
map.reserve(elementCount);
|
map.reserve(elementCount);
|
||||||
|
|
||||||
|
if (!ibo || elementCount == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
GLBuffer::Bind ibobind(*ibo);
|
GLBuffer::Bind ibobind(*ibo);
|
||||||
|
|
||||||
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
// We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush.
|
||||||
@@ -472,6 +483,8 @@ void Mesh::getVertexMap(std::vector<uint32> &map) const
|
|||||||
copyFromIndexBuffer<uint32>(buffer, elementCount, map);
|
copyFromIndexBuffer<uint32>(buffer, elementCount, map);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Mesh::getVertexMapCount() const
|
size_t Mesh::getVertexMapCount() const
|
||||||
@@ -524,6 +537,39 @@ void Mesh::getDrawRange(int &min, int &max) const
|
|||||||
max = rangeMax;
|
max = rangeMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname)
|
||||||
|
{
|
||||||
|
const AttribFormat &format = vertexFormat[attributeindex];
|
||||||
|
|
||||||
|
GLint attriblocation = -1;
|
||||||
|
|
||||||
|
// If the attribute is one of the LOVE-defined ones, use the constant
|
||||||
|
// attribute index for it, otherwise query the index from the shader.
|
||||||
|
VertexAttribID builtinattrib;
|
||||||
|
if (Shader::getConstant(inputname.c_str(), builtinattrib))
|
||||||
|
attriblocation = (GLint) builtinattrib;
|
||||||
|
else if (Shader::current)
|
||||||
|
attriblocation = Shader::current->getAttribLocation(inputname);
|
||||||
|
|
||||||
|
// The active shader might not use this vertex attribute name.
|
||||||
|
if (attriblocation < 0)
|
||||||
|
return attriblocation;
|
||||||
|
|
||||||
|
// Needed for unmap and glVertexAttribPointer.
|
||||||
|
GLBuffer::Bind vbobind(*vbo);
|
||||||
|
|
||||||
|
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
||||||
|
vbo->unmap();
|
||||||
|
|
||||||
|
const void *gloffset = vbo->getPointer(getAttributeOffset(attributeindex));
|
||||||
|
GLenum datatype = getGLDataType(format.type);
|
||||||
|
GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
|
||||||
|
|
||||||
|
glVertexAttribPointer(attriblocation, format.components, datatype, normalized, vertexStride, gloffset);
|
||||||
|
|
||||||
|
return attriblocation;
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
OpenGL::TempDebugGroup debuggroup("Mesh draw");
|
||||||
@@ -536,43 +582,15 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
Mesh *mesh = attrib.second.mesh;
|
Mesh *mesh = attrib.second.mesh;
|
||||||
const AttribFormat &format = mesh->vertexFormat[attrib.second.index];
|
int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
|
||||||
|
|
||||||
GLint attriblocation = -1;
|
if (location >= 0)
|
||||||
|
enabledattribs |= 1u << (uint32) location;
|
||||||
// If the attribute is one of the LOVE-defined ones, use the constant
|
|
||||||
// attribute index for it, otherwise query the index from the shader.
|
|
||||||
VertexAttribID builtinattrib;
|
|
||||||
if (Shader::getConstant(format.name.c_str(), builtinattrib))
|
|
||||||
attriblocation = (GLint) builtinattrib;
|
|
||||||
else if (Shader::current)
|
|
||||||
attriblocation = Shader::current->getAttribLocation(format.name);
|
|
||||||
|
|
||||||
// The active shader might not use this vertex attribute name.
|
|
||||||
if (attriblocation < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Needed for unmap and glVertexAttribPointer.
|
|
||||||
GLBuffer::Bind vbobind(*mesh->vbo);
|
|
||||||
|
|
||||||
// Make sure the buffer isn't mapped (sends data to GPU if needed.)
|
|
||||||
mesh->vbo->unmap();
|
|
||||||
|
|
||||||
size_t offset = mesh->getAttributeOffset(attrib.second.index);
|
|
||||||
const void *gloffset = mesh->vbo->getPointer(offset);
|
|
||||||
GLenum datatype = getGLDataType(format.type);
|
|
||||||
GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
|
|
||||||
|
|
||||||
glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset);
|
|
||||||
|
|
||||||
enabledattribs |= 1 << uint32(attriblocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not supported on all platforms or GL versions, I believe.
|
||||||
if (!(enabledattribs & ATTRIBFLAG_POS))
|
if (!(enabledattribs & ATTRIBFLAG_POS))
|
||||||
{
|
|
||||||
// Not supported on all platforms or GL versions at least, I believe.
|
|
||||||
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
|
||||||
}
|
|
||||||
|
|
||||||
gl.useVertexAttribArrays(enabledattribs);
|
gl.useVertexAttribArrays(enabledattribs);
|
||||||
|
|
||||||
@@ -588,7 +606,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
|||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
|
|
||||||
if (ibo && elementCount > 0)
|
if (useIndexBuffer && ibo && elementCount > 0)
|
||||||
{
|
{
|
||||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||||
GLBuffer::Bind ibo_bind(*ibo);
|
GLBuffer::Bind ibo_bind(*ibo);
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public:
|
|||||||
**/
|
**/
|
||||||
const std::vector<AttribFormat> &getVertexFormat() const;
|
const std::vector<AttribFormat> &getVertexFormat() const;
|
||||||
DataType getAttributeInfo(int attribindex, int &components) const;
|
DataType getAttributeInfo(int attribindex, int &components) const;
|
||||||
|
int getAttributeIndex(const std::string &name) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether a specific vertex attribute is used when drawing the Mesh.
|
* Sets whether a specific vertex attribute is used when drawing the Mesh.
|
||||||
@@ -140,7 +141,7 @@ public:
|
|||||||
void attachAttribute(const std::string &name, Mesh *mesh);
|
void attachAttribute(const std::string &name, Mesh *mesh);
|
||||||
|
|
||||||
void *mapVertexData();
|
void *mapVertexData();
|
||||||
void unmapVertexData();
|
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flushes all modified data to the GPU.
|
* Flushes all modified data to the GPU.
|
||||||
@@ -154,12 +155,13 @@ public:
|
|||||||
* {0, 1, 2, 3, 4, ...}
|
* {0, 1, 2, 3, 4, ...}
|
||||||
**/
|
**/
|
||||||
void setVertexMap(const std::vector<uint32> &map);
|
void setVertexMap(const std::vector<uint32> &map);
|
||||||
|
void setVertexMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills the uint32 vector passed into the method with the previously set
|
* Fills the uint32 vector passed into the method with the previously set
|
||||||
* vertex map (index buffer) values.
|
* vertex map (index buffer) values.
|
||||||
**/
|
**/
|
||||||
void getVertexMap(std::vector<uint32> &map) const;
|
bool getVertexMap(std::vector<uint32> &map) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the total number of elements in the vertex map array.
|
* Gets the total number of elements in the vertex map array.
|
||||||
@@ -192,6 +194,8 @@ public:
|
|||||||
void setDrawRange();
|
void setDrawRange();
|
||||||
void getDrawRange(int &min, int &max) const;
|
void getDrawRange(int &min, int &max) const;
|
||||||
|
|
||||||
|
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
|
||||||
|
|
||||||
// Implements Drawable.
|
// Implements Drawable.
|
||||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
|
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
|
||||||
|
|
||||||
@@ -211,7 +215,7 @@ private:
|
|||||||
struct AttachedAttribute
|
struct AttachedAttribute
|
||||||
{
|
{
|
||||||
Mesh *mesh;
|
Mesh *mesh;
|
||||||
size_t index;
|
int index;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -242,6 +246,7 @@ private:
|
|||||||
|
|
||||||
// Element (vertex index) buffer, for the vertex map.
|
// Element (vertex index) buffer, for the vertex map.
|
||||||
GLBuffer *ibo;
|
GLBuffer *ibo;
|
||||||
|
bool useIndexBuffer;
|
||||||
size_t elementCount;
|
size_t elementCount;
|
||||||
GLenum elementDataType;
|
GLenum elementDataType;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
// C
|
// C
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
// For SDL_GL_GetProcAddress.
|
// For SDL_GL_GetProcAddress.
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
@@ -601,24 +602,32 @@ void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
|||||||
f.anisotropy = 1.0f;
|
f.anisotropy = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode)
|
||||||
|
{
|
||||||
|
switch (wmode)
|
||||||
|
{
|
||||||
|
case Texture::WRAP_CLAMP:
|
||||||
|
default:
|
||||||
|
return GL_CLAMP_TO_EDGE;
|
||||||
|
case Texture::WRAP_CLAMP_ZERO:
|
||||||
|
return GL_CLAMP_TO_BORDER;
|
||||||
|
case Texture::WRAP_REPEAT:
|
||||||
|
return GL_REPEAT;
|
||||||
|
case Texture::WRAP_MIRRORED_REPEAT:
|
||||||
|
return GL_MIRRORED_REPEAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
|
void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
|
||||||
{
|
{
|
||||||
auto glWrapMode = [](Texture::WrapMode wmode) -> GLint
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGLWrapMode(w.s));
|
||||||
{
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGLWrapMode(w.t));
|
||||||
switch (wmode)
|
}
|
||||||
{
|
|
||||||
case Texture::WRAP_CLAMP:
|
|
||||||
default:
|
|
||||||
return GL_CLAMP_TO_EDGE;
|
|
||||||
case Texture::WRAP_REPEAT:
|
|
||||||
return GL_REPEAT;
|
|
||||||
case Texture::WRAP_MIRRORED_REPEAT:
|
|
||||||
return GL_MIRRORED_REPEAT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapMode(w.s));
|
bool OpenGL::isClampZeroTextureWrapSupported() const
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapMode(w.t));
|
{
|
||||||
|
return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpenGL::getMaxTextureSize() const
|
int OpenGL::getMaxTextureSize() const
|
||||||
@@ -643,7 +652,7 @@ int OpenGL::getMaxTextureUnits() const
|
|||||||
|
|
||||||
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
|
void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
|
||||||
{
|
{
|
||||||
int64 memsize = (int64) stats.textureMemory + ((int64 )newsize - (int64) oldsize);
|
int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize);
|
||||||
stats.textureMemory = (size_t) std::max(memsize, (int64) 0);
|
stats.textureMemory = (size_t) std::max(memsize, (int64) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,6 +661,36 @@ OpenGL::Vendor OpenGL::getVendor() const
|
|||||||
return vendor;
|
return vendor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *OpenGL::errorString(GLenum errorcode)
|
||||||
|
{
|
||||||
|
switch (errorcode)
|
||||||
|
{
|
||||||
|
case GL_NO_ERROR:
|
||||||
|
return "no error";
|
||||||
|
case GL_INVALID_ENUM:
|
||||||
|
return "invalid enum";
|
||||||
|
case GL_INVALID_VALUE:
|
||||||
|
return "invalid value";
|
||||||
|
case GL_INVALID_OPERATION:
|
||||||
|
return "invalid operation";
|
||||||
|
case GL_OUT_OF_MEMORY:
|
||||||
|
return "out of memory";
|
||||||
|
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||||
|
return "invalid framebuffer operation";
|
||||||
|
case GL_CONTEXT_LOST:
|
||||||
|
return "OpenGL context has been lost";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char text[64] = {};
|
||||||
|
|
||||||
|
memset(text, 0, sizeof(text));
|
||||||
|
sprintf(text, "0x%x", errorcode);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
const char *OpenGL::debugSeverityString(GLenum severity)
|
const char *OpenGL::debugSeverityString(GLenum severity)
|
||||||
{
|
{
|
||||||
switch (severity)
|
switch (severity)
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
#if defined(DEBUG) && DEBUG == 1
|
#if defined(LOVE_IOS)
|
||||||
TempDebugGroup(const char *name)
|
TempDebugGroup(const char *name)
|
||||||
{
|
{
|
||||||
if (GLAD_EXT_debug_marker)
|
if (GLAD_EXT_debug_marker)
|
||||||
@@ -153,7 +153,7 @@ public:
|
|||||||
TempDebugGroup(const char *) {}
|
TempDebugGroup(const char *) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG) && DEBUG == 1
|
#if defined(LOVE_IOS)
|
||||||
~TempDebugGroup()
|
~TempDebugGroup()
|
||||||
{
|
{
|
||||||
if (GLAD_EXT_debug_marker)
|
if (GLAD_EXT_debug_marker)
|
||||||
@@ -312,6 +312,8 @@ public:
|
|||||||
**/
|
**/
|
||||||
void setTextureWrap(const graphics::Texture::Wrap &w);
|
void setTextureWrap(const graphics::Texture::Wrap &w);
|
||||||
|
|
||||||
|
bool isClampZeroTextureWrapSupported() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum supported width or height of a texture.
|
* Returns the maximum supported width or height of a texture.
|
||||||
**/
|
**/
|
||||||
@@ -339,6 +341,8 @@ public:
|
|||||||
**/
|
**/
|
||||||
Vendor getVendor() const;
|
Vendor getVendor() const;
|
||||||
|
|
||||||
|
static const char *errorString(GLenum errorcode);
|
||||||
|
|
||||||
// Get human-readable strings for debug info.
|
// Get human-readable strings for debug info.
|
||||||
static const char *debugSeverityString(GLenum severity);
|
static const char *debugSeverityString(GLenum severity);
|
||||||
static const char *debugSourceString(GLenum source);
|
static const char *debugSourceString(GLenum source);
|
||||||
@@ -352,6 +356,8 @@ private:
|
|||||||
void initMatrices();
|
void initMatrices();
|
||||||
void createDefaultTexture();
|
void createDefaultTexture();
|
||||||
|
|
||||||
|
GLint getGLWrapMode(Texture::WrapMode wmode);
|
||||||
|
|
||||||
bool contextInitialized;
|
bool contextInitialized;
|
||||||
|
|
||||||
float maxAnisotropy;
|
float maxAnisotropy;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,18 +22,9 @@
|
|||||||
#define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
|
#define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/int.h"
|
#include "graphics/ParticleSystem.h"
|
||||||
#include "common/math.h"
|
|
||||||
#include "common/Vector.h"
|
|
||||||
#include "graphics/Drawable.h"
|
|
||||||
#include "graphics/Color.h"
|
|
||||||
#include "graphics/Quad.h"
|
|
||||||
#include "graphics/Texture.h"
|
|
||||||
#include "GLBuffer.h"
|
#include "GLBuffer.h"
|
||||||
|
|
||||||
// STL
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -41,643 +32,28 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
class ParticleSystem : public love::graphics::ParticleSystem
|
||||||
* A class for creating, moving and drawing particles.
|
|
||||||
* A big thanks to bobthebloke.org
|
|
||||||
**/
|
|
||||||
class ParticleSystem : public Drawable
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Type of distribution new particles are drawn from: None, uniform, normal.
|
|
||||||
*/
|
|
||||||
enum AreaSpreadDistribution
|
|
||||||
{
|
|
||||||
DISTRIBUTION_NONE,
|
|
||||||
DISTRIBUTION_UNIFORM,
|
|
||||||
DISTRIBUTION_NORMAL,
|
|
||||||
DISTRIBUTION_MAX_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insertion modes of new particles in the list: top, bottom, random.
|
|
||||||
*/
|
|
||||||
enum InsertMode
|
|
||||||
{
|
|
||||||
INSERT_MODE_TOP,
|
|
||||||
INSERT_MODE_BOTTOM,
|
|
||||||
INSERT_MODE_RANDOM,
|
|
||||||
INSERT_MODE_MAX_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum numbers of particles in a ParticleSystem.
|
|
||||||
* This limit comes from the fact that a quad requires four vertices and the
|
|
||||||
* OpenGL API where GLsizei is a signed int.
|
|
||||||
**/
|
|
||||||
static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a particle system with the specified buffer size and texture.
|
|
||||||
**/
|
|
||||||
ParticleSystem(Texture *texture, uint32 buffer);
|
ParticleSystem(Texture *texture, uint32 buffer);
|
||||||
ParticleSystem(const ParticleSystem &p);
|
ParticleSystem(const ParticleSystem &p);
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes any allocated memory.
|
|
||||||
**/
|
|
||||||
virtual ~ParticleSystem();
|
virtual ~ParticleSystem();
|
||||||
|
|
||||||
/**
|
ParticleSystem *clone() override;
|
||||||
* Creates an identical copy of this ParticleSystem. The clone does not
|
void setBufferSize(uint32 size) override;
|
||||||
* duplicate any existing particles from this ParticleSystem, just the
|
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
|
||||||
* settable parameters.
|
|
||||||
**/
|
|
||||||
ParticleSystem *clone();
|
|
||||||
|
|
||||||
/**
|
private:
|
||||||
* Sets the texture used in the particle system.
|
|
||||||
* @param texture The new texture.
|
|
||||||
**/
|
|
||||||
void setTexture(Texture *texture);
|
|
||||||
|
|
||||||
/**
|
void createVertices(size_t numparticles);
|
||||||
* Returns the texture used when drawing the particle system.
|
|
||||||
**/
|
|
||||||
Texture *getTexture() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the current buffer and allocates the appropriate amount of space for the buffer.
|
|
||||||
* @param size The new buffer size.
|
|
||||||
**/
|
|
||||||
void setBufferSize(uint32 size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total amount of particles this ParticleSystem can have active
|
|
||||||
* at any given point in time.
|
|
||||||
**/
|
|
||||||
uint32 getBufferSize() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the insert mode for new particles.
|
|
||||||
* @param mode The new insert mode.
|
|
||||||
*/
|
|
||||||
void setInsertMode(InsertMode mode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current insert mode.
|
|
||||||
*/
|
|
||||||
InsertMode getInsertMode() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the emission rate.
|
|
||||||
* @param rate The amount of particles per second.
|
|
||||||
**/
|
|
||||||
void setEmissionRate(float rate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of particles created per second.
|
|
||||||
**/
|
|
||||||
float getEmissionRate() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the lifetime of the particle emitter (-1 means eternal)
|
|
||||||
* @param life The lifetime (in seconds).
|
|
||||||
**/
|
|
||||||
void setEmitterLifetime(float life);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the lifetime of the particle emitter.
|
|
||||||
**/
|
|
||||||
float getEmitterLifetime() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the life range of the particles.
|
|
||||||
* @param min The minimum life.
|
|
||||||
* @param max The maximum life (if 0, then becomes the same as minimum life).
|
|
||||||
**/
|
|
||||||
void setParticleLifetime(float min, float max = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the lifetime of a particle.
|
|
||||||
* @param[out] min The minimum life.
|
|
||||||
* @param[out] max The maximum life.
|
|
||||||
**/
|
|
||||||
void getParticleLifetime(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of the center of the emitter.
|
|
||||||
* Used to move the emitter without changing the position of already existing particles.
|
|
||||||
* @param x The x-coordinate.
|
|
||||||
* @param y The y-coordinate.
|
|
||||||
**/
|
|
||||||
void setPosition(float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the position of the emitter.
|
|
||||||
**/
|
|
||||||
const love::Vector &getPosition() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the position of the center of the emitter.
|
|
||||||
* When update is called, newly spawned particles will appear in a line
|
|
||||||
* between the old emitter position and where the emitter was moved to,
|
|
||||||
* resulting in a smoother-feeling particle system if moveTo is called
|
|
||||||
* repeatedly.
|
|
||||||
**/
|
|
||||||
void moveTo(float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the emission area spread parameters and distribution type. The interpretation of
|
|
||||||
* the parameters depends on the distribution type:
|
|
||||||
*
|
|
||||||
* * None: Parameters are ignored. No area spread.
|
|
||||||
* * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
|
|
||||||
* * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
|
|
||||||
* @param x First parameter. Interpretation depends on distribution type.
|
|
||||||
* @param y Second parameter. Interpretation depends on distribution type.
|
|
||||||
* @param distribution Distribution type
|
|
||||||
**/
|
|
||||||
void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns area spread distribution type.
|
|
||||||
**/
|
|
||||||
AreaSpreadDistribution getAreaSpreadDistribution() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns area spread parameters.
|
|
||||||
**/
|
|
||||||
const love::Vector &getAreaSpreadParameters() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the direction of the particle emitter.
|
|
||||||
* @param direction The direction (in degrees).
|
|
||||||
**/
|
|
||||||
void setDirection(float direction);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the direction of the particle emitter (in radians).
|
|
||||||
**/
|
|
||||||
float getDirection() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the spread of the particle emitter.
|
|
||||||
* @param spread The spread (in radians).
|
|
||||||
**/
|
|
||||||
void setSpread(float spread);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the directional spread of the emitter (in radians).
|
|
||||||
**/
|
|
||||||
float getSpread() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the speed of the particles.
|
|
||||||
* @param speed The speed.
|
|
||||||
**/
|
|
||||||
void setSpeed(float speed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the speed of the particles.
|
|
||||||
* @param min The minimum speed.
|
|
||||||
* @param max The maximum speed.
|
|
||||||
**/
|
|
||||||
void setSpeed(float min, float max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the speed of the particles.
|
|
||||||
* @param[out] min The minimum speed.
|
|
||||||
* @param[out] max The maximum speed.
|
|
||||||
**/
|
|
||||||
void getSpeed(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the linear acceleration (the acceleration along the x and y axes).
|
|
||||||
* @param x The acceleration along the x-axis.
|
|
||||||
* @param y The acceleration along the y-axis.
|
|
||||||
**/
|
|
||||||
void setLinearAcceleration(float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the linear acceleration (the acceleration along the x and y axes).
|
|
||||||
* @param xmin The minimum amount of acceleration along the x-axis.
|
|
||||||
* @param ymin The minimum amount of acceleration along the y-axis.
|
|
||||||
* @param xmax The maximum amount of acceleration along the x-axis.
|
|
||||||
* @param ymax The maximum amount of acceleration along the y-axis.
|
|
||||||
**/
|
|
||||||
void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the linear acceleration of the particles.
|
|
||||||
* @param[out] min The minimum acceleration.
|
|
||||||
* @param[out] max The maximum acceleration.
|
|
||||||
**/
|
|
||||||
void getLinearAcceleration(love::Vector &min, love::Vector &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the radial acceleration (the acceleration towards the particle emitter).
|
|
||||||
* @param acceleration The amount of acceleration.
|
|
||||||
**/
|
|
||||||
void setRadialAcceleration(float acceleration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the radial acceleration (the acceleration towards the particle emitter).
|
|
||||||
* @param min The minimum acceleration.
|
|
||||||
* @param max The maximum acceleration.
|
|
||||||
**/
|
|
||||||
void setRadialAcceleration(float min, float max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the radial acceleration.
|
|
||||||
* @param[out] min The minimum amount of radial acceleration.
|
|
||||||
* @param[out] max The maximum amount of radial acceleration.
|
|
||||||
**/
|
|
||||||
void getRadialAcceleration(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
|
|
||||||
* @param acceleration The amount of acceleration.
|
|
||||||
**/
|
|
||||||
void setTangentialAcceleration(float acceleration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
|
|
||||||
* @param min The minimum acceleration.
|
|
||||||
* @param max The maximum acceleration.
|
|
||||||
**/
|
|
||||||
void setTangentialAcceleration(float min, float max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the tangential acceleration.
|
|
||||||
* @param[out] min The minimum tangential acceleration.
|
|
||||||
* @param[out] max The maximum tangential acceleration.
|
|
||||||
**/
|
|
||||||
void getTangentialAcceleration(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the amount of linear damping. Damping reduces the velocity of
|
|
||||||
* particles over time. A value of 0 corresponds to no damping.
|
|
||||||
**/
|
|
||||||
void setLinearDamping(float min, float max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current amount of linear damping.
|
|
||||||
**/
|
|
||||||
void getLinearDamping(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the size of the sprite (1.0 being the default size).
|
|
||||||
* @param size The size of the sprite.
|
|
||||||
**/
|
|
||||||
void setSize(float size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
|
|
||||||
* @param newSizes Array of sizes
|
|
||||||
**/
|
|
||||||
void setSizes(const std::vector<float> &newSizes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the sizes of the particle sprites.
|
|
||||||
**/
|
|
||||||
const std::vector<float> &getSizes() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
|
|
||||||
* @param variation The amount of variation.
|
|
||||||
**/
|
|
||||||
void setSizeVariation(float variation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the amount of initial size variation between particles.
|
|
||||||
**/
|
|
||||||
float getSizeVariation() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the amount of rotation a sprite starts out with.
|
|
||||||
* @param rotation The amount of rotation.
|
|
||||||
**/
|
|
||||||
void setRotation(float rotation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the amount of rotation a sprite starts out with (a random value between min and max).
|
|
||||||
* @param min The minimum amount of rotation.
|
|
||||||
* @param max The maximum amount of rotation.
|
|
||||||
**/
|
|
||||||
void setRotation(float min, float max);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the initial amount of rotation of a particle, in radians.
|
|
||||||
* @param[out] min The minimum initial rotation.
|
|
||||||
* @param[out] max The maximum initial rotation.
|
|
||||||
**/
|
|
||||||
void getRotation(float &min, float &max) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the spin of the sprite.
|
|
||||||
* @param spin The spin of the sprite (in degrees).
|
|
||||||
**/
|
|
||||||
void setSpin(float spin);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the spin of the sprite upon particle creation and death.
|
|
||||||
* @param start The spin of the sprite upon creation (in radians / second).
|
|
||||||
* @param end The spin of the sprite upon death (in radians / second).
|
|
||||||
**/
|
|
||||||
void setSpin(float start, float end);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the amount of spin of a particle during its lifetime.
|
|
||||||
* @param[out] start The initial spin, in radians / s.
|
|
||||||
* @param[out] end The final spin, in radians / s.
|
|
||||||
**/
|
|
||||||
void getSpin(float &start, float &end) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
|
|
||||||
* @param variation The variation.
|
|
||||||
**/
|
|
||||||
void setSpinVariation(float variation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the amount of variation of the start spin of a particle.
|
|
||||||
**/
|
|
||||||
float getSpinVariation() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the particles' offsets for rotation.
|
|
||||||
* @param x The x offset.
|
|
||||||
* @param y The y offset.
|
|
||||||
**/
|
|
||||||
void setOffset(float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns of the particle offset.
|
|
||||||
**/
|
|
||||||
love::Vector getOffset() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the color of the particles.
|
|
||||||
* @param newColors Array of colors
|
|
||||||
**/
|
|
||||||
void setColor(const std::vector<Colorf> &newColors);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the color of the particles.
|
|
||||||
**/
|
|
||||||
std::vector<Colorf> getColor() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a list of Quads to use for particles over their lifetime.
|
|
||||||
**/
|
|
||||||
void setQuads(const std::vector<Quad *> &newQuads);
|
|
||||||
void setQuads();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Quads used when drawing the particles.
|
|
||||||
**/
|
|
||||||
std::vector<Quad *> getQuads() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets whether particle angles & rotations are relative to their velocities.
|
|
||||||
**/
|
|
||||||
void setRelativeRotation(bool enable);
|
|
||||||
bool hasRelativeRotation() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the amount of particles that are currently active in the system.
|
|
||||||
**/
|
|
||||||
uint32 getCount() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts/resumes the particle emitter.
|
|
||||||
**/
|
|
||||||
void start();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the particle emitter and resets.
|
|
||||||
**/
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the particle emitter.
|
|
||||||
**/
|
|
||||||
void pause();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the particle emitter.
|
|
||||||
**/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantly emits a number of particles.
|
|
||||||
* @param num The number of particles to emit.
|
|
||||||
**/
|
|
||||||
void emit(uint32 num);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the particle emitter is active.
|
|
||||||
**/
|
|
||||||
bool isActive() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the particle emitter is paused.
|
|
||||||
**/
|
|
||||||
bool isPaused() const;
|
|
||||||
|
|
||||||
bool isStopped() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the particle system is empty of particles or not.
|
|
||||||
**/
|
|
||||||
bool isEmpty() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the amount of particles has reached the buffer limit or not.
|
|
||||||
**/
|
|
||||||
bool isFull() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the particle emitter at the specified position.
|
|
||||||
* @param x The x-coordinate.
|
|
||||||
* @param y The y-coordinate.
|
|
||||||
**/
|
|
||||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the particle system.
|
|
||||||
* @param dt Time since last update.
|
|
||||||
**/
|
|
||||||
void update(float dt);
|
|
||||||
|
|
||||||
static bool getConstant(const char *in, AreaSpreadDistribution &out);
|
|
||||||
static bool getConstant(AreaSpreadDistribution in, const char *&out);
|
|
||||||
|
|
||||||
static bool getConstant(const char *in, InsertMode &out);
|
|
||||||
static bool getConstant(InsertMode in, const char *&out);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// Represents a single particle.
|
|
||||||
struct Particle
|
|
||||||
{
|
|
||||||
Particle *prev;
|
|
||||||
Particle *next;
|
|
||||||
|
|
||||||
float lifetime;
|
|
||||||
float life;
|
|
||||||
|
|
||||||
love::Vector position;
|
|
||||||
|
|
||||||
// Particles gravitate towards this point.
|
|
||||||
love::Vector origin;
|
|
||||||
|
|
||||||
love::Vector velocity;
|
|
||||||
love::Vector linearAcceleration;
|
|
||||||
float radialAcceleration;
|
|
||||||
float tangentialAcceleration;
|
|
||||||
|
|
||||||
float linearDamping;
|
|
||||||
|
|
||||||
float size;
|
|
||||||
float sizeOffset;
|
|
||||||
float sizeIntervalSize;
|
|
||||||
|
|
||||||
float rotation; // Amount of rotation applied to the final angle.
|
|
||||||
float angle;
|
|
||||||
float spinStart;
|
|
||||||
float spinEnd;
|
|
||||||
|
|
||||||
Colorf color;
|
|
||||||
|
|
||||||
int quadIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pointer to the beginning of the allocated memory.
|
|
||||||
Particle *pMem;
|
|
||||||
|
|
||||||
// Pointer to a free particle.
|
|
||||||
Particle *pFree;
|
|
||||||
|
|
||||||
// Pointer to the start of the linked list.
|
|
||||||
Particle *pHead;
|
|
||||||
|
|
||||||
// Pointer to the end of the linked list.
|
|
||||||
Particle *pTail;
|
|
||||||
|
|
||||||
// array of transformed vertex data for all particles, for drawing
|
// array of transformed vertex data for all particles, for drawing
|
||||||
Vertex *particleVerts;
|
Vertex *particleVerts;
|
||||||
|
|
||||||
// Vertex index buffer.
|
// Vertex index buffer.
|
||||||
QuadIndices quadIndices;
|
QuadIndices quadIndices;
|
||||||
|
|
||||||
// The texture to be drawn.
|
|
||||||
StrongRef<Texture> texture;
|
|
||||||
|
|
||||||
// Whether the particle emitter is active.
|
|
||||||
bool active;
|
|
||||||
|
|
||||||
// Insert mode of new particles.
|
|
||||||
InsertMode insertMode;
|
|
||||||
|
|
||||||
// The maximum number of particles.
|
|
||||||
uint32 maxParticles;
|
|
||||||
|
|
||||||
// The number of active particles.
|
|
||||||
uint32 activeParticles;
|
|
||||||
|
|
||||||
// The emission rate (particles/sec).
|
|
||||||
float emissionRate;
|
|
||||||
|
|
||||||
// Used to determine when a particle should be emitted.
|
|
||||||
float emitCounter;
|
|
||||||
|
|
||||||
// The relative position of the particle emitter.
|
|
||||||
love::Vector position;
|
|
||||||
love::Vector prevPosition;
|
|
||||||
|
|
||||||
// Emission area spread.
|
|
||||||
AreaSpreadDistribution areaSpreadDistribution;
|
|
||||||
love::Vector areaSpread;
|
|
||||||
|
|
||||||
// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
|
|
||||||
float lifetime;
|
|
||||||
float life;
|
|
||||||
|
|
||||||
// The particle life.
|
|
||||||
float particleLifeMin;
|
|
||||||
float particleLifeMax;
|
|
||||||
|
|
||||||
// The direction (and spread) the particles will be emitted in. Measured in radians.
|
|
||||||
float direction;
|
|
||||||
float spread;
|
|
||||||
|
|
||||||
// The speed.
|
|
||||||
float speedMin;
|
|
||||||
float speedMax;
|
|
||||||
|
|
||||||
// Acceleration along the x and y axes.
|
|
||||||
love::Vector linearAccelerationMin;
|
|
||||||
love::Vector linearAccelerationMax;
|
|
||||||
|
|
||||||
// Acceleration towards the emitter's center
|
|
||||||
float radialAccelerationMin;
|
|
||||||
float radialAccelerationMax;
|
|
||||||
|
|
||||||
// Acceleration perpendicular to the particle's direction.
|
|
||||||
float tangentialAccelerationMin;
|
|
||||||
float tangentialAccelerationMax;
|
|
||||||
|
|
||||||
float linearDampingMin;
|
|
||||||
float linearDampingMax;
|
|
||||||
|
|
||||||
// Size.
|
|
||||||
std::vector<float> sizes;
|
|
||||||
float sizeVariation;
|
|
||||||
|
|
||||||
// Rotation
|
|
||||||
float rotationMin;
|
|
||||||
float rotationMax;
|
|
||||||
|
|
||||||
// Spin.
|
|
||||||
float spinStart;
|
|
||||||
float spinEnd;
|
|
||||||
float spinVariation;
|
|
||||||
|
|
||||||
// Offsets
|
|
||||||
love::Vector offset;
|
|
||||||
|
|
||||||
// Is the ParticleSystem using a default offset?
|
|
||||||
bool defaultOffset;
|
|
||||||
|
|
||||||
// Color.
|
|
||||||
std::vector<Colorf> colors;
|
|
||||||
|
|
||||||
// Quads.
|
|
||||||
std::vector<StrongRef<Quad>> quads;
|
|
||||||
|
|
||||||
bool relativeRotation;
|
|
||||||
|
|
||||||
void resetOffset();
|
|
||||||
|
|
||||||
void createBuffers(size_t size);
|
|
||||||
void deleteBuffers();
|
|
||||||
|
|
||||||
void addParticle(float t);
|
|
||||||
Particle *removeParticle(Particle *p);
|
|
||||||
|
|
||||||
// Called by addParticle.
|
|
||||||
void initParticle(Particle *p, float t);
|
|
||||||
void insertTop(Particle *p);
|
|
||||||
void insertBottom(Particle *p);
|
|
||||||
void insertRandom(Particle *p);
|
|
||||||
|
|
||||||
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
|
|
||||||
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
|
|
||||||
|
|
||||||
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
|
|
||||||
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -64,8 +64,10 @@ namespace
|
|||||||
|
|
||||||
Shader *Shader::current = nullptr;
|
Shader *Shader::current = nullptr;
|
||||||
Shader *Shader::defaultShader = nullptr;
|
Shader *Shader::defaultShader = nullptr;
|
||||||
|
Shader *Shader::defaultVideoShader = nullptr;
|
||||||
|
|
||||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||||
|
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||||
|
|
||||||
std::vector<int> Shader::textureCounters;
|
std::vector<int> Shader::textureCounters;
|
||||||
|
|
||||||
@@ -77,6 +79,7 @@ Shader::Shader(const ShaderSource &source)
|
|||||||
, lastCanvas((Canvas *) -1)
|
, lastCanvas((Canvas *) -1)
|
||||||
, lastViewport()
|
, lastViewport()
|
||||||
, lastPointSize(0.0f)
|
, lastPointSize(0.0f)
|
||||||
|
, videoTextureUnits()
|
||||||
{
|
{
|
||||||
if (source.vertex.empty() && source.pixel.empty())
|
if (source.vertex.empty() && source.pixel.empty())
|
||||||
throw love::Exception("Cannot create shader: no source code!");
|
throw love::Exception("Cannot create shader: no source code!");
|
||||||
@@ -225,6 +228,9 @@ bool Shader::loadVolatile()
|
|||||||
lastProjectionMatrix.setTranslation(nan, nan);
|
lastProjectionMatrix.setTranslation(nan, nan);
|
||||||
lastTransformMatrix.setTranslation(nan, nan);
|
lastTransformMatrix.setTranslation(nan, nan);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
videoTextureUnits[i] = 0;
|
||||||
|
|
||||||
// zero out active texture list
|
// zero out active texture list
|
||||||
activeTexUnits.clear();
|
activeTexUnits.clear();
|
||||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||||
@@ -635,6 +641,57 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
|||||||
return builtinAttributes[int(attrib)] != -1;
|
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()
|
void Shader::checkSetScreenParams()
|
||||||
{
|
{
|
||||||
OpenGL::Viewport view = gl.getViewport();
|
OpenGL::Viewport view = gl.getViewport();
|
||||||
@@ -898,6 +955,9 @@ StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::built
|
|||||||
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
||||||
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
||||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_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));
|
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ public:
|
|||||||
BUILTIN_NORMAL_MATRIX,
|
BUILTIN_NORMAL_MATRIX,
|
||||||
BUILTIN_POINT_SIZE,
|
BUILTIN_POINT_SIZE,
|
||||||
BUILTIN_SCREEN_SIZE,
|
BUILTIN_SCREEN_SIZE,
|
||||||
|
BUILTIN_VIDEO_Y_CHANNEL,
|
||||||
|
BUILTIN_VIDEO_CB_CHANNEL,
|
||||||
|
BUILTIN_VIDEO_CR_CHANNEL,
|
||||||
BUILTIN_MAX_ENUM
|
BUILTIN_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,9 +92,11 @@ public:
|
|||||||
|
|
||||||
// Pointer to the default Shader.
|
// Pointer to the default Shader.
|
||||||
static Shader *defaultShader;
|
static Shader *defaultShader;
|
||||||
|
static Shader *defaultVideoShader;
|
||||||
|
|
||||||
// Default shader code (a shader is always required internally.)
|
// Default shader code (a shader is always required internally.)
|
||||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||||
|
static ShaderSource defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Shader using a list of source codes.
|
* Creates a new Shader using a list of source codes.
|
||||||
@@ -182,6 +187,7 @@ public:
|
|||||||
**/
|
**/
|
||||||
bool hasVertexAttrib(VertexAttribID attrib) const;
|
bool hasVertexAttrib(VertexAttribID attrib) const;
|
||||||
|
|
||||||
|
void setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture);
|
||||||
void checkSetScreenParams();
|
void checkSetScreenParams();
|
||||||
void checkSetPointSize(float size);
|
void checkSetPointSize(float size);
|
||||||
void checkSetBuiltinUniforms();
|
void checkSetBuiltinUniforms();
|
||||||
@@ -263,6 +269,8 @@ private:
|
|||||||
Matrix4 lastTransformMatrix;
|
Matrix4 lastTransformMatrix;
|
||||||
Matrix4 lastProjectionMatrix;
|
Matrix4 lastProjectionMatrix;
|
||||||
|
|
||||||
|
GLuint videoTextureUnits[3];
|
||||||
|
|
||||||
// Counts total number of textures bound to each texture unit in all shaders
|
// Counts total number of textures bound to each texture unit in all shaders
|
||||||
static std::vector<int> textureCounters;
|
static std::vector<int> textureCounters;
|
||||||
|
|
||||||
|
|||||||
@@ -53,23 +53,9 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage)
|
|||||||
throw love::Exception("Invalid SpriteBatch size.");
|
throw love::Exception("Invalid SpriteBatch size.");
|
||||||
|
|
||||||
GLenum gl_usage = Mesh::getGLBufferUsage(usage);
|
GLenum gl_usage = Mesh::getGLBufferUsage(usage);
|
||||||
|
size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||||
|
|
||||||
const size_t vertex_size = sizeof(Vertex) * 4 * size;
|
array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
|
|
||||||
}
|
|
||||||
catch (love::Exception &)
|
|
||||||
{
|
|
||||||
delete array_buf;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (std::bad_alloc &)
|
|
||||||
{
|
|
||||||
delete array_buf;
|
|
||||||
throw love::Exception("Out of memory.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SpriteBatch::~SpriteBatch()
|
SpriteBatch::~SpriteBatch()
|
||||||
@@ -207,6 +193,28 @@ int SpriteBatch::getBufferSize() const
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh)
|
||||||
|
{
|
||||||
|
AttachedAttribute oldattrib = {};
|
||||||
|
AttachedAttribute newattrib = {};
|
||||||
|
|
||||||
|
if (mesh->getVertexCount() < (size_t) getBufferSize() * 4)
|
||||||
|
throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", getBufferSize()*4);
|
||||||
|
|
||||||
|
auto it = attached_attributes.find(name);
|
||||||
|
if (it != attached_attributes.end())
|
||||||
|
oldattrib = it->second;
|
||||||
|
|
||||||
|
newattrib.index = mesh->getAttributeIndex(name);
|
||||||
|
|
||||||
|
if (newattrib.index < 0)
|
||||||
|
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||||
|
|
||||||
|
newattrib.mesh = mesh;
|
||||||
|
|
||||||
|
attached_attributes[name] = newattrib;
|
||||||
|
}
|
||||||
|
|
||||||
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
const size_t pos_offset = offsetof(Vertex, x);
|
const size_t pos_offset = offsetof(Vertex, x);
|
||||||
@@ -223,27 +231,47 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
|
|||||||
|
|
||||||
gl.bindTexture(*(GLuint *) texture->getHandle());
|
gl.bindTexture(*(GLuint *) texture->getHandle());
|
||||||
|
|
||||||
GLBuffer::Bind array_bind(*array_buf);
|
|
||||||
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
|
|
||||||
|
|
||||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
|
||||||
array_buf->unmap();
|
|
||||||
|
|
||||||
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
|
uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
|
||||||
|
|
||||||
// Apply per-sprite color, if a color is set.
|
|
||||||
if (color)
|
|
||||||
{
|
{
|
||||||
enabledattribs |= ATTRIBFLAG_COLOR;
|
// Scope this bind so it doesn't interfere with the
|
||||||
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
|
// Mesh::bindAttributeToShaderInput calls below.
|
||||||
|
GLBuffer::Bind array_bind(*array_buf);
|
||||||
|
|
||||||
|
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||||
|
array_buf->unmap();
|
||||||
|
|
||||||
|
// Apply per-sprite color, if a color is set.
|
||||||
|
if (color)
|
||||||
|
{
|
||||||
|
enabledattribs |= ATTRIBFLAG_COLOR;
|
||||||
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
|
||||||
|
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
|
for (const auto &it : attached_attributes)
|
||||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
|
{
|
||||||
|
Mesh *mesh = it.second.mesh.get();
|
||||||
|
|
||||||
|
// We have to do this check here as well because setBufferSize can be
|
||||||
|
// called after attachAttribute.
|
||||||
|
if (mesh->getVertexCount() < (size_t) getBufferSize() * 4)
|
||||||
|
throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
|
||||||
|
|
||||||
|
int location = mesh->bindAttributeToShaderInput(it.second.index, it.first);
|
||||||
|
|
||||||
|
if (location >= 0)
|
||||||
|
enabledattribs |= 1u << (uint32) location;
|
||||||
|
}
|
||||||
|
|
||||||
gl.useVertexAttribArrays(enabledattribs);
|
gl.useVertexAttribArrays(enabledattribs);
|
||||||
|
|
||||||
gl.prepareDraw();
|
gl.prepareDraw();
|
||||||
|
|
||||||
|
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
|
||||||
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
|
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
// C
|
// C
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
// C++
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "common/math.h"
|
#include "common/math.h"
|
||||||
#include "common/Matrix.h"
|
#include "common/Matrix.h"
|
||||||
@@ -98,11 +101,23 @@ public:
|
|||||||
**/
|
**/
|
||||||
int getBufferSize() const;
|
int getBufferSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a specific vertex attribute from a Mesh to this SpriteBatch.
|
||||||
|
* The vertex attribute will be used when drawing the SpriteBatch.
|
||||||
|
**/
|
||||||
|
void attachAttribute(const std::string &name, Mesh *mesh);
|
||||||
|
|
||||||
// Implements Drawable.
|
// Implements Drawable.
|
||||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
struct AttachedAttribute
|
||||||
|
{
|
||||||
|
StrongRef<Mesh> mesh;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
void addv(const Vertex *v, const Matrix3 &m, int index);
|
void addv(const Vertex *v, const Matrix3 &m, int index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,6 +144,8 @@ private:
|
|||||||
GLBuffer *array_buf;
|
GLBuffer *array_buf;
|
||||||
QuadIndices quad_indices;
|
QuadIndices quad_indices;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||||
|
|
||||||
}; // SpriteBatch
|
}; // SpriteBatch
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -30,10 +30,9 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
Text::Text(Font *font, const std::string &text)
|
Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
|
||||||
: font(font)
|
: font(font)
|
||||||
, vbo(nullptr)
|
, vbo(nullptr)
|
||||||
, text_info()
|
|
||||||
, vert_offset(0)
|
, vert_offset(0)
|
||||||
, texture_cache_id((uint32) -1)
|
, texture_cache_id((uint32) -1)
|
||||||
{
|
{
|
||||||
@@ -82,7 +81,7 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
|
|||||||
vbo = new_vbo;
|
vbo = new_vbo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vbo != nullptr)
|
if (vbo != nullptr && datasize > 0)
|
||||||
{
|
{
|
||||||
GLBuffer::Bind bind(*vbo);
|
GLBuffer::Bind bind(*vbo);
|
||||||
vbodata = (uint8 *) vbo->map();
|
vbodata = (uint8 *) vbo->map();
|
||||||
@@ -113,11 +112,13 @@ void Text::addTextData(const TextData &t)
|
|||||||
std::vector<Font::GlyphVertex> vertices;
|
std::vector<Font::GlyphVertex> vertices;
|
||||||
std::vector<Font::DrawCommand> new_commands;
|
std::vector<Font::DrawCommand> new_commands;
|
||||||
|
|
||||||
|
Font::TextInfo text_info;
|
||||||
|
|
||||||
// We only have formatted text if the align mode is valid.
|
// We only have formatted text if the align mode is valid.
|
||||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||||
new_commands = font->generateVertices(t.text, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
|
new_commands = font->generateVertices(t.codepoints, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
|
||||||
else
|
else
|
||||||
new_commands = font->generateVerticesFormatted(t.text, t.wrap, t.align, vertices, &text_info);
|
new_commands = font->generateVerticesFormatted(t.codepoints, t.wrap, t.align, vertices, &text_info);
|
||||||
|
|
||||||
if (t.use_matrix)
|
if (t.use_matrix)
|
||||||
t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
|
t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
|
||||||
@@ -158,27 +159,29 @@ void Text::addTextData(const TextData &t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
vert_offset = voffset + vertices.size();
|
vert_offset = voffset + vertices.size();
|
||||||
|
|
||||||
text_data.push_back(t);
|
text_data.push_back(t);
|
||||||
|
text_data.back().text_info = text_info;
|
||||||
|
|
||||||
// Font::generateVertices can invalidate the font's texture cache.
|
// Font::generateVertices can invalidate the font's texture cache.
|
||||||
if (font->getTextureCacheID() != texture_cache_id)
|
if (font->getTextureCacheID() != texture_cache_id)
|
||||||
regenerateVertices();
|
regenerateVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::set(const std::string &text)
|
void Text::set(const std::vector<Font::ColoredString> &text)
|
||||||
{
|
{
|
||||||
if (text.empty())
|
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
|
||||||
return set();
|
|
||||||
|
|
||||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, false, false, Matrix3()});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::set(const std::string &text, float wrap, Font::AlignMode align)
|
void Text::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
|
||||||
{
|
{
|
||||||
if (text.empty())
|
if (text.empty() || (text.size() == 1 && text[0].str.empty()))
|
||||||
return set();
|
return set();
|
||||||
|
|
||||||
addTextData({text, wrap, align, false, false, Matrix3()});
|
Font::ColoredCodepoints codepoints;
|
||||||
|
Font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
|
addTextData({codepoints, wrap, align, {}, false, false, Matrix3()});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::set()
|
void Text::set()
|
||||||
@@ -186,24 +189,21 @@ void Text::set()
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
int Text::add(const std::vector<Font::ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
if (text.empty())
|
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
return;
|
|
||||||
|
|
||||||
Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
|
||||||
|
|
||||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, true, true, m});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||||
{
|
{
|
||||||
if (text.empty())
|
Font::ColoredCodepoints codepoints;
|
||||||
return;
|
Font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
|
|
||||||
addTextData({text, wrap, align, true, true, m});
|
addTextData({codepoints, wrap, align, {}, true, true, m});
|
||||||
|
|
||||||
|
return (int) text_data.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::clear()
|
void Text::clear()
|
||||||
@@ -211,7 +211,6 @@ void Text::clear()
|
|||||||
text_data.clear();
|
text_data.clear();
|
||||||
draw_commands.clear();
|
draw_commands.clear();
|
||||||
texture_cache_id = font->getTextureCacheID();
|
texture_cache_id = font->getTextureCacheID();
|
||||||
text_info = {};
|
|
||||||
vert_offset = 0;
|
vert_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,12 +225,13 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
|||||||
if (font->getTextureCacheID() != texture_cache_id)
|
if (font->getTextureCacheID() != texture_cache_id)
|
||||||
regenerateVertices();
|
regenerateVertices();
|
||||||
|
|
||||||
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
||||||
const size_t tex_offset = offsetof(Font::GlyphVertex, s);
|
const size_t tex_offset = offsetof(Font::GlyphVertex, s);
|
||||||
|
const size_t color_offset = offsetof(Font::GlyphVertex, color.r);
|
||||||
const size_t stride = sizeof(Font::GlyphVertex);
|
const size_t stride = sizeof(Font::GlyphVertex);
|
||||||
|
|
||||||
OpenGL::TempTransform transform(gl);
|
OpenGL::TempTransform transform(gl);
|
||||||
transform.get() *= Matrix4(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
|
transform.get() *= Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||||
|
|
||||||
{
|
{
|
||||||
GLBuffer::Bind bind(*vbo);
|
GLBuffer::Bind bind(*vbo);
|
||||||
@@ -239,10 +239,11 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
|||||||
|
|
||||||
// Font::drawVertices expects AttribPointer calls to be done already.
|
// Font::drawVertices expects AttribPointer calls to be done already.
|
||||||
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
|
glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
|
||||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(tex_offset));
|
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset));
|
||||||
|
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
|
||||||
|
|
||||||
font->drawVertices(draw_commands);
|
font->drawVertices(draw_commands);
|
||||||
}
|
}
|
||||||
@@ -262,14 +263,26 @@ Font *Text::getFont() const
|
|||||||
return font.get();
|
return font.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Text::getWidth() const
|
int Text::getWidth(int index) const
|
||||||
{
|
{
|
||||||
return text_info.width;
|
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() const
|
int Text::getHeight(int index) const
|
||||||
{
|
{
|
||||||
return text_info.height;
|
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
|
} // opengl
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ class Text : public Drawable
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Text(Font *font, const std::string &text = "");
|
Text(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||||
virtual ~Text();
|
virtual ~Text();
|
||||||
|
|
||||||
void set(const std::string &text);
|
void set(const std::vector<Font::ColoredString> &text);
|
||||||
void set(const std::string &text, float wrap, Font::AlignMode align);
|
void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
|
||||||
void set();
|
void set();
|
||||||
|
|
||||||
void add(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
int add(const std::vector<Font::ColoredString> &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||||
void addf(const std::string &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
int addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
// Implements Drawable.
|
// Implements Drawable.
|
||||||
@@ -58,20 +58,21 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Gets the width of the currently set text.
|
* Gets the width of the currently set text.
|
||||||
**/
|
**/
|
||||||
int getWidth() const;
|
int getWidth(int index = 0) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the height of the currently set text.
|
* Gets the height of the currently set text.
|
||||||
**/
|
**/
|
||||||
int getHeight() const;
|
int getHeight(int index = 0) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct TextData
|
struct TextData
|
||||||
{
|
{
|
||||||
std::string text;
|
Font::ColoredCodepoints codepoints;
|
||||||
float wrap;
|
float wrap;
|
||||||
Font::AlignMode align;
|
Font::AlignMode align;
|
||||||
|
Font::TextInfo text_info;
|
||||||
bool use_matrix;
|
bool use_matrix;
|
||||||
bool append_vertices;
|
bool append_vertices;
|
||||||
Matrix3 matrix;
|
Matrix3 matrix;
|
||||||
@@ -87,7 +88,6 @@ private:
|
|||||||
std::vector<Font::DrawCommand> draw_commands;
|
std::vector<Font::DrawCommand> draw_commands;
|
||||||
|
|
||||||
std::vector<TextData> text_data;
|
std::vector<TextData> text_data;
|
||||||
Font::TextInfo text_info;
|
|
||||||
|
|
||||||
size_t vert_offset;
|
size_t vert_offset;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -101,17 +101,8 @@ int w_Canvas_getMSAA(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Canvas_functions[] =
|
||||||
{
|
{
|
||||||
// From wrap_Texture.
|
|
||||||
{ "getWidth", w_Texture_getWidth },
|
|
||||||
{ "getHeight", w_Texture_getHeight },
|
|
||||||
{ "getDimensions", w_Texture_getDimensions },
|
|
||||||
{ "setFilter", w_Texture_setFilter },
|
|
||||||
{ "getFilter", w_Texture_getFilter },
|
|
||||||
{ "setWrap", w_Texture_setWrap },
|
|
||||||
{ "getWrap", w_Texture_getWrap },
|
|
||||||
|
|
||||||
{ "renderTo", w_Canvas_renderTo },
|
{ "renderTo", w_Canvas_renderTo },
|
||||||
{ "newImageData", w_Canvas_newImageData },
|
{ "newImageData", w_Canvas_newImageData },
|
||||||
{ "getFormat", w_Canvas_getFormat },
|
{ "getFormat", w_Canvas_getFormat },
|
||||||
@@ -121,7 +112,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_canvas(lua_State *L)
|
extern "C" int luaopen_canvas(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_CANVAS_ID, functions);
|
return luax_register_type(L, GRAPHICS_CANVAS_ID, "Canvas", w_Texture_functions, w_Canvas_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ namespace opengl
|
|||||||
|
|
||||||
//see Canvas.h
|
//see Canvas.h
|
||||||
Canvas *luax_checkcanvas(lua_State *L, int idx);
|
Canvas *luax_checkcanvas(lua_State *L, int idx);
|
||||||
int w_Canvas_renderTo(lua_State *L);
|
|
||||||
int w_Canvas_newImageData(lua_State *L);
|
|
||||||
int w_Canvas_getFormat(lua_State *L);
|
|
||||||
int w_Canvas_getMSAA(lua_State *L);
|
|
||||||
extern "C" int luaopen_canvas(lua_State *L);
|
extern "C" int luaopen_canvas(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ int w_Font_setFallbacks(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Font_functions[] =
|
||||||
{
|
{
|
||||||
{ "getHeight", w_Font_getHeight },
|
{ "getHeight", w_Font_getHeight },
|
||||||
{ "getWidth", w_Font_getWidth },
|
{ "getWidth", w_Font_getWidth },
|
||||||
@@ -203,7 +203,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_font(lua_State *L)
|
extern "C" int luaopen_font(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_FONT_ID, functions);
|
return luax_register_type(L, GRAPHICS_FONT_ID, "Font", w_Font_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -33,18 +33,6 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Font *luax_checkfont(lua_State *L, int idx);
|
Font *luax_checkfont(lua_State *L, int idx);
|
||||||
int w_Font_getHeight(lua_State *L);
|
|
||||||
int w_Font_getWidth(lua_State *L);
|
|
||||||
int w_Font_getWrap(lua_State *L);
|
|
||||||
int w_Font_setLineHeight(lua_State *L);
|
|
||||||
int w_Font_getLineHeight(lua_State *L);
|
|
||||||
int w_Font_setFilter(lua_State *L);
|
|
||||||
int w_Font_getFilter(lua_State *L);
|
|
||||||
int w_Font_getAscent(lua_State *L);
|
|
||||||
int w_Font_getDescent(lua_State *L);
|
|
||||||
int w_Font_getBaseline(lua_State *L);
|
|
||||||
int w_Font_hasGlyphs(lua_State *L);
|
|
||||||
int w_Font_setFallbacks(lua_State *L);
|
|
||||||
extern "C" int luaopen_font(lua_State *L);
|
extern "C" int luaopen_font(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "image/Image.h"
|
#include "image/Image.h"
|
||||||
#include "font/Rasterizer.h"
|
#include "font/Rasterizer.h"
|
||||||
#include "filesystem/wrap_Filesystem.h"
|
#include "filesystem/wrap_Filesystem.h"
|
||||||
|
#include "video/VideoStream.h"
|
||||||
#include "image/wrap_Image.h"
|
#include "image/wrap_Image.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -184,6 +185,20 @@ int w_setScissor(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_intersectScissor(lua_State *L)
|
||||||
|
{
|
||||||
|
int x = (int) luaL_checknumber(L, 1);
|
||||||
|
int y = (int) luaL_checknumber(L, 2);
|
||||||
|
int w = (int) luaL_checknumber(L, 3);
|
||||||
|
int h = (int) luaL_checknumber(L, 4);
|
||||||
|
|
||||||
|
if (w < 0 || h < 0)
|
||||||
|
return luaL_error(L, "Can't set scissor with negative width and/or height.");
|
||||||
|
|
||||||
|
instance()->intersectScissor(x, y, w, h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int w_getScissor(lua_State *L)
|
int w_getScissor(lua_State *L)
|
||||||
{
|
{
|
||||||
int x, y, w, h;
|
int x, y, w, h;
|
||||||
@@ -354,13 +369,13 @@ int w_newImage(lua_State *L)
|
|||||||
int w_newQuad(lua_State *L)
|
int w_newQuad(lua_State *L)
|
||||||
{
|
{
|
||||||
Quad::Viewport v;
|
Quad::Viewport v;
|
||||||
v.x = (float) luaL_checknumber(L, 1);
|
v.x = luaL_checknumber(L, 1);
|
||||||
v.y = (float) luaL_checknumber(L, 2);
|
v.y = luaL_checknumber(L, 2);
|
||||||
v.w = (float) luaL_checknumber(L, 3);
|
v.w = luaL_checknumber(L, 3);
|
||||||
v.h = (float) luaL_checknumber(L, 4);
|
v.h = luaL_checknumber(L, 4);
|
||||||
|
|
||||||
float sw = (float) luaL_checknumber(L, 5);
|
double sw = luaL_checknumber(L, 5);
|
||||||
float sh = (float) luaL_checknumber(L, 6);
|
double sh = luaL_checknumber(L, 6);
|
||||||
|
|
||||||
Quad *quad = instance()->newQuad(v, sw, sh);
|
Quad *quad = instance()->newQuad(v, sw, sh);
|
||||||
luax_pushtype(L, GRAPHICS_QUAD_ID, quad);
|
luax_pushtype(L, GRAPHICS_QUAD_ID, quad);
|
||||||
@@ -837,7 +852,9 @@ int w_newText(lua_State *L)
|
|||||||
luax_catchexcept(L, [&](){ t = instance()->newText(font); });
|
luax_catchexcept(L, [&](){ t = instance()->newText(font); });
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string text = luax_checkstring(L, 2);
|
std::vector<Font::ColoredString> text;
|
||||||
|
luax_checkcoloredstring(L, 2, text);
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ t = instance()->newText(font, text); });
|
luax_catchexcept(L, [&](){ t = instance()->newText(font, text); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,6 +863,20 @@ int w_newText(lua_State *L)
|
|||||||
return 1;
|
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)
|
int w_setColor(lua_State *L)
|
||||||
{
|
{
|
||||||
Colorf c;
|
Colorf c;
|
||||||
@@ -1258,25 +1289,37 @@ int w_setDefaultShaderCode(lua_State *L)
|
|||||||
lua_getfield(L, 1, "opengl");
|
lua_getfield(L, 1, "opengl");
|
||||||
lua_rawgeti(L, -1, 1);
|
lua_rawgeti(L, -1, 1);
|
||||||
lua_rawgeti(L, -2, 2);
|
lua_rawgeti(L, -2, 2);
|
||||||
|
lua_rawgeti(L, -3, 3);
|
||||||
|
|
||||||
Shader::ShaderSource openglcode;
|
Shader::ShaderSource openglcode;
|
||||||
openglcode.vertex = luax_checkstring(L, -2);
|
openglcode.vertex = luax_checkstring(L, -3);
|
||||||
openglcode.pixel = luax_checkstring(L, -1);
|
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_getfield(L, 1, "opengles");
|
||||||
lua_rawgeti(L, -1, 1);
|
lua_rawgeti(L, -1, 1);
|
||||||
lua_rawgeti(L, -2, 2);
|
lua_rawgeti(L, -2, 2);
|
||||||
|
lua_rawgeti(L, -3, 3);
|
||||||
|
|
||||||
Shader::ShaderSource openglescode;
|
Shader::ShaderSource openglescode;
|
||||||
openglescode.vertex = luax_checkstring(L, -2);
|
openglescode.vertex = luax_checkstring(L, -3);
|
||||||
openglescode.pixel = luax_checkstring(L, -1);
|
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_OPENGL] = openglcode;
|
||||||
Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode;
|
Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode;
|
||||||
|
Shader::defaultVideoCode[Graphics::RENDERER_OPENGL] = openglVideocode;
|
||||||
|
Shader::defaultVideoCode[Graphics::RENDERER_OPENGLES] = openglesVideocode;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1452,7 +1495,9 @@ int w_draw(lua_State *L)
|
|||||||
|
|
||||||
int w_print(lua_State *L)
|
int w_print(lua_State *L)
|
||||||
{
|
{
|
||||||
std::string str = luax_checkstring(L, 1);
|
std::vector<Font::ColoredString> str;
|
||||||
|
luax_checkcoloredstring(L, 1, str);
|
||||||
|
|
||||||
float x = (float)luaL_optnumber(L, 2, 0.0);
|
float x = (float)luaL_optnumber(L, 2, 0.0);
|
||||||
float y = (float)luaL_optnumber(L, 3, 0.0);
|
float y = (float)luaL_optnumber(L, 3, 0.0);
|
||||||
float angle = (float)luaL_optnumber(L, 4, 0.0f);
|
float angle = (float)luaL_optnumber(L, 4, 0.0f);
|
||||||
@@ -1471,7 +1516,9 @@ int w_print(lua_State *L)
|
|||||||
|
|
||||||
int w_printf(lua_State *L)
|
int w_printf(lua_State *L)
|
||||||
{
|
{
|
||||||
std::string str = luax_checkstring(L, 1);
|
std::vector<Font::ColoredString> str;
|
||||||
|
luax_checkcoloredstring(L, 1, str);
|
||||||
|
|
||||||
float x = (float)luaL_checknumber(L, 2);
|
float x = (float)luaL_checknumber(L, 2);
|
||||||
float y = (float)luaL_checknumber(L, 3);
|
float y = (float)luaL_checknumber(L, 3);
|
||||||
float wrap = (float)luaL_checknumber(L, 4);
|
float wrap = (float)luaL_checknumber(L, 4);
|
||||||
@@ -1843,6 +1890,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "newShader", w_newShader },
|
{ "newShader", w_newShader },
|
||||||
{ "newMesh", w_newMesh },
|
{ "newMesh", w_newMesh },
|
||||||
{ "newText", w_newText },
|
{ "newText", w_newText },
|
||||||
|
{ "_newVideo", w_newVideo },
|
||||||
|
|
||||||
{ "setColor", w_setColor },
|
{ "setColor", w_setColor },
|
||||||
{ "getColor", w_getColor },
|
{ "getColor", w_getColor },
|
||||||
@@ -1899,6 +1947,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "getDimensions", w_getDimensions },
|
{ "getDimensions", w_getDimensions },
|
||||||
|
|
||||||
{ "setScissor", w_setScissor },
|
{ "setScissor", w_setScissor },
|
||||||
|
{ "intersectScissor", w_intersectScissor },
|
||||||
{ "getScissor", w_getScissor },
|
{ "getScissor", w_getScissor },
|
||||||
|
|
||||||
{ "stencil", w_stencil },
|
{ "stencil", w_stencil },
|
||||||
@@ -1925,9 +1974,16 @@ static const luaL_Reg functions[] =
|
|||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int luaopen_drawable(lua_State *L)
|
||||||
|
{
|
||||||
|
return luax_register_type(L, GRAPHICS_DRAWABLE_ID, "Drawable", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Types for this module.
|
// Types for this module.
|
||||||
static const lua_CFunction types[] =
|
static const lua_CFunction types[] =
|
||||||
{
|
{
|
||||||
|
luaopen_drawable,
|
||||||
|
luaopen_texture,
|
||||||
luaopen_font,
|
luaopen_font,
|
||||||
luaopen_image,
|
luaopen_image,
|
||||||
luaopen_quad,
|
luaopen_quad,
|
||||||
@@ -1937,6 +1993,7 @@ static const lua_CFunction types[] =
|
|||||||
luaopen_shader,
|
luaopen_shader,
|
||||||
luaopen_mesh,
|
luaopen_mesh,
|
||||||
luaopen_text,
|
luaopen_text,
|
||||||
|
luaopen_video,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "wrap_Shader.h"
|
#include "wrap_Shader.h"
|
||||||
#include "wrap_Mesh.h"
|
#include "wrap_Mesh.h"
|
||||||
#include "wrap_Text.h"
|
#include "wrap_Text.h"
|
||||||
|
#include "wrap_Video.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
@@ -41,85 +42,6 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
int w_reset(lua_State *L);
|
|
||||||
int w_clear(lua_State *L);
|
|
||||||
int w_discard(lua_State *L);
|
|
||||||
int w_present(lua_State *L);
|
|
||||||
int w_isCreated(lua_State *L);
|
|
||||||
int w_isActive(lua_State *L);
|
|
||||||
int w_isGammaCorrect(lua_State *L);
|
|
||||||
int w_getWidth(lua_State *L);
|
|
||||||
int w_getHeight(lua_State *L);
|
|
||||||
int w_getDimensions(lua_State *L);
|
|
||||||
int w_setScissor(lua_State *L);
|
|
||||||
int w_getScissor(lua_State *L);
|
|
||||||
int w_stencil(lua_State *L);
|
|
||||||
int w_setStencilTest(lua_State *L);
|
|
||||||
int w_getStencilTest(lua_State *L);
|
|
||||||
int w_newImage(lua_State *L);
|
|
||||||
int w_newQuad(lua_State *L);
|
|
||||||
int w_newFont(lua_State *L);
|
|
||||||
int w_newImageFont(lua_State *L);
|
|
||||||
int w_newSpriteBatch(lua_State *L);
|
|
||||||
int w_newParticleSystem(lua_State *L);
|
|
||||||
int w_newCanvas(lua_State *L);
|
|
||||||
int w_newShader(lua_State *L);
|
|
||||||
int w_newMesh(lua_State *L);
|
|
||||||
int w_newText(lua_State *L);
|
|
||||||
int w_setColor(lua_State *L);
|
|
||||||
int w_getColor(lua_State *L);
|
|
||||||
int w_setBackgroundColor(lua_State *L);
|
|
||||||
int w_getBackgroundColor(lua_State *L);
|
|
||||||
int w_setNewFont(lua_State *L);
|
|
||||||
int w_setFont(lua_State *L);
|
|
||||||
int w_getFont(lua_State *L);
|
|
||||||
int w_setColorMask(lua_State *L);
|
|
||||||
int w_getColorMask(lua_State *L);
|
|
||||||
int w_setBlendMode(lua_State *L);
|
|
||||||
int w_getBlendMode(lua_State *L);
|
|
||||||
int w_setDefaultFilter(lua_State *L);
|
|
||||||
int w_getDefaultFilter(lua_State *L);
|
|
||||||
int w_setDefaultMipmapFilter(lua_State *L);
|
|
||||||
int w_getDefaultMipmapFilter(lua_State *L);
|
|
||||||
int w_setLineWidth(lua_State *L);
|
|
||||||
int w_setLineStyle(lua_State *L);
|
|
||||||
int w_setLineJoin(lua_State *L);
|
|
||||||
int w_getLineWidth(lua_State *L);
|
|
||||||
int w_getLineStyle(lua_State *L);
|
|
||||||
int w_getLineJoin(lua_State *L);
|
|
||||||
int w_setPointSize(lua_State *L);
|
|
||||||
int w_getPointSize(lua_State *L);
|
|
||||||
int w_setWireframe(lua_State *L);
|
|
||||||
int w_isWireframe(lua_State *L);
|
|
||||||
int w_newScreenshot(lua_State *L);
|
|
||||||
int w_setCanvas(lua_State *L);
|
|
||||||
int w_getCanvas(lua_State *L);
|
|
||||||
int w_setShader(lua_State *L);
|
|
||||||
int w_getShader(lua_State *L);
|
|
||||||
int w_setDefaultShaderCode(lua_State *L);
|
|
||||||
int w_getSupported(lua_State *L);
|
|
||||||
int w_getCanvasFormats(lua_State *L);
|
|
||||||
int w_getCompressedImageFormats(lua_State *L);
|
|
||||||
int w_getRendererInfo(lua_State *L);
|
|
||||||
int w_getSystemLimits(lua_State *L);
|
|
||||||
int w_getStats(lua_State *L);
|
|
||||||
int w_draw(lua_State *L);
|
|
||||||
int w_print(lua_State *L);
|
|
||||||
int w_printf(lua_State *L);
|
|
||||||
int w_points(lua_State *L);
|
|
||||||
int w_line(lua_State *L);
|
|
||||||
int w_rectangle(lua_State *L);
|
|
||||||
int w_circle(lua_State *L);
|
|
||||||
int w_ellipse(lua_State *L);
|
|
||||||
int w_arc(lua_State *L);
|
|
||||||
int w_polygon(lua_State *L);
|
|
||||||
int w_push(lua_State *L);
|
|
||||||
int w_pop(lua_State *L);
|
|
||||||
int w_rotate(lua_State *L);
|
|
||||||
int w_scale(lua_State *L);
|
|
||||||
int w_translate(lua_State *L);
|
|
||||||
int w_shear(lua_State *L);
|
|
||||||
int w_origin(lua_State *L);
|
|
||||||
extern "C" LOVE_EXPORT int luaopen_love_graphics(lua_State *L);
|
extern "C" LOVE_EXPORT int luaopen_love_graphics(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -159,12 +159,34 @@ precision mediump float;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
varying mediump vec4 VaryingTexCoord;
|
varying mediump vec4 VaryingTexCoord;
|
||||||
varying lowp vec4 VaryingColor;
|
varying mediump vec4 VaryingColor;
|
||||||
|
|
||||||
#define love_Canvases gl_FragData
|
#define love_Canvases gl_FragData
|
||||||
|
|
||||||
uniform sampler2D _tex0_;]],
|
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 = [[
|
FOOTER = [[
|
||||||
void main() {
|
void main() {
|
||||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
// 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 "",
|
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||||
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
||||||
GLSL.FUNCTIONS,
|
GLSL.FUNCTIONS,
|
||||||
|
GLSL.PIXEL.FUNCTIONS,
|
||||||
lang == "glsles" and "#line 1" or "#line 0",
|
lang == "glsles" and "#line 1" or "#line 0",
|
||||||
pixelcode,
|
pixelcode,
|
||||||
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
||||||
@@ -307,23 +330,47 @@ vec4 position(mat4 transform_proj, vec4 vertpos) {
|
|||||||
return transform_proj * vertpos;
|
return transform_proj * vertpos;
|
||||||
}]],
|
}]],
|
||||||
pixel = [[
|
pixel = [[
|
||||||
vec4 effect(lowp vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||||
return Texel(tex, texcoord) * vcolor;
|
return Texel(tex, texcoord) * vcolor;
|
||||||
}]]
|
}]],
|
||||||
|
videopixel = [[
|
||||||
|
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||||
|
return VideoTexel(texcoord) * vcolor;
|
||||||
|
}]],
|
||||||
}
|
}
|
||||||
|
|
||||||
local defaults = {
|
local defaults = {
|
||||||
opengl = {
|
opengl = {
|
||||||
createVertexCode(defaultcode.vertex, "glsl"),
|
createVertexCode(defaultcode.vertex, "glsl"),
|
||||||
createPixelCode(defaultcode.pixel, false, "glsl"),
|
createPixelCode(defaultcode.pixel, false, "glsl"),
|
||||||
|
createPixelCode(defaultcode.videopixel, false, "glsl"),
|
||||||
},
|
},
|
||||||
opengles = {
|
opengles = {
|
||||||
createVertexCode(defaultcode.vertex, "glsles"),
|
createVertexCode(defaultcode.vertex, "glsles"),
|
||||||
createPixelCode(defaultcode.pixel, false, "glsles"),
|
createPixelCode(defaultcode.pixel, false, "glsles"),
|
||||||
|
createPixelCode(defaultcode.videopixel, false, "glsles"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
love.graphics._setDefaultShaderCode(defaults)
|
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.
|
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||||
--)luastring"--"
|
--)luastring"--"
|
||||||
|
|||||||
@@ -137,17 +137,8 @@ int w_Image_getFlags(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Image_functions[] =
|
||||||
{
|
{
|
||||||
// From wrap_Texture.
|
|
||||||
{ "getWidth", w_Texture_getWidth },
|
|
||||||
{ "getHeight", w_Texture_getHeight },
|
|
||||||
{ "getDimensions", w_Texture_getDimensions },
|
|
||||||
{ "setFilter", w_Texture_setFilter },
|
|
||||||
{ "getFilter", w_Texture_getFilter },
|
|
||||||
{ "setWrap", w_Texture_setWrap },
|
|
||||||
{ "getWrap", w_Texture_getWrap },
|
|
||||||
|
|
||||||
{ "setMipmapFilter", w_Image_setMipmapFilter },
|
{ "setMipmapFilter", w_Image_setMipmapFilter },
|
||||||
{ "getMipmapFilter", w_Image_getMipmapFilter },
|
{ "getMipmapFilter", w_Image_getMipmapFilter },
|
||||||
{ "isCompressed", w_Image_isCompressed },
|
{ "isCompressed", w_Image_isCompressed },
|
||||||
@@ -159,7 +150,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_image(lua_State *L)
|
extern "C" int luaopen_image(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_IMAGE_ID, functions);
|
return luax_register_type(L, GRAPHICS_IMAGE_ID, "Image", w_Texture_functions, w_Image_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -34,12 +34,6 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Image *luax_checkimage(lua_State *L, int idx);
|
Image *luax_checkimage(lua_State *L, int idx);
|
||||||
int w_Image_setMipmapFilter(lua_State *L);
|
|
||||||
int w_Image_getMipmapFilter(lua_State *L);
|
|
||||||
int w_Image_isCompressed(lua_State *L);
|
|
||||||
int w_Image_refresh(lua_State *L);
|
|
||||||
int w_Image_getData(lua_State *L);
|
|
||||||
int w_Image_getFlags(lua_State *L);
|
|
||||||
extern "C" int luaopen_image(lua_State *L);
|
extern "C" int luaopen_image(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -109,10 +109,15 @@ int w_Mesh_setVertices(lua_State *L)
|
|||||||
{
|
{
|
||||||
Mesh *t = luax_checkmesh(L, 1);
|
Mesh *t = luax_checkmesh(L, 1);
|
||||||
luaL_checktype(L, 2, LUA_TTABLE);
|
luaL_checktype(L, 2, LUA_TTABLE);
|
||||||
|
size_t vertoffset = (size_t) luaL_optnumber(L, 3, 1) - 1;
|
||||||
|
|
||||||
|
if (vertoffset >= t->getVertexCount())
|
||||||
|
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", (int) t->getVertexCount());
|
||||||
|
|
||||||
size_t nvertices = luax_objlen(L, 2);
|
size_t nvertices = luax_objlen(L, 2);
|
||||||
if (nvertices != t->getVertexCount())
|
|
||||||
return luaL_error(L, "Invalid number of vertices (expected %d, got %d)", (int) t->getVertexCount(), (int) nvertices);
|
if (vertoffset + nvertices > t->getVertexCount())
|
||||||
|
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", (int) t->getVertexCount() - (int) vertoffset, (int) nvertices);
|
||||||
|
|
||||||
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
|
||||||
|
|
||||||
@@ -120,7 +125,10 @@ int w_Mesh_setVertices(lua_State *L)
|
|||||||
for (const Mesh::AttribFormat &format : vertexformat)
|
for (const Mesh::AttribFormat &format : vertexformat)
|
||||||
ncomponents += format.components;
|
ncomponents += format.components;
|
||||||
|
|
||||||
char *data = (char *) t->mapVertexData();
|
size_t stride = t->getVertexStride();
|
||||||
|
size_t byteoffset = vertoffset * stride;
|
||||||
|
|
||||||
|
char *data = (char *) t->mapVertexData() + byteoffset;
|
||||||
|
|
||||||
for (size_t i = 0; i < nvertices; i++)
|
for (size_t i = 0; i < nvertices; i++)
|
||||||
{
|
{
|
||||||
@@ -145,7 +153,7 @@ int w_Mesh_setVertices(lua_State *L)
|
|||||||
lua_pop(L, ncomponents + 1);
|
lua_pop(L, ncomponents + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
t->unmapVertexData();
|
t->unmapVertexData(byteoffset, nvertices * stride);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,6 +339,13 @@ int w_Mesh_setVertexMap(lua_State *L)
|
|||||||
{
|
{
|
||||||
Mesh *t = luax_checkmesh(L, 1);
|
Mesh *t = luax_checkmesh(L, 1);
|
||||||
|
|
||||||
|
if (lua_isnoneornil(L, 2))
|
||||||
|
{
|
||||||
|
// Disable the vertex map / index buffer.
|
||||||
|
luax_catchexcept(L, [&](){ t->setVertexMap(); });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_table = lua_istable(L, 2);
|
bool is_table = lua_istable(L, 2);
|
||||||
int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1;
|
int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1;
|
||||||
|
|
||||||
@@ -361,7 +376,14 @@ int w_Mesh_getVertexMap(lua_State *L)
|
|||||||
Mesh *t = luax_checkmesh(L, 1);
|
Mesh *t = luax_checkmesh(L, 1);
|
||||||
|
|
||||||
std::vector<uint32> vertex_map;
|
std::vector<uint32> vertex_map;
|
||||||
luax_catchexcept(L, [&](){ t->getVertexMap(vertex_map); });
|
bool has_vertex_map = false;
|
||||||
|
luax_catchexcept(L, [&](){ has_vertex_map = t->getVertexMap(vertex_map); });
|
||||||
|
|
||||||
|
if (!has_vertex_map)
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int element_count = (int) vertex_map.size();
|
int element_count = (int) vertex_map.size();
|
||||||
|
|
||||||
@@ -468,7 +490,7 @@ int w_Mesh_getDrawRange(lua_State *L)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Mesh_functions[] =
|
||||||
{
|
{
|
||||||
{ "setVertices", w_Mesh_setVertices },
|
{ "setVertices", w_Mesh_setVertices },
|
||||||
{ "setVertex", w_Mesh_setVertex },
|
{ "setVertex", w_Mesh_setVertex },
|
||||||
@@ -494,7 +516,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_mesh(lua_State *L)
|
extern "C" int luaopen_mesh(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_MESH_ID, functions);
|
return luax_register_type(L, GRAPHICS_MESH_ID, "Mesh", w_Mesh_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -36,25 +36,6 @@ char *luax_writeAttributeData(lua_State *L, int startidx, Mesh::DataType type, i
|
|||||||
const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data);
|
const char *luax_readAttributeData(lua_State *L, Mesh::DataType type, int components, const char *data);
|
||||||
|
|
||||||
Mesh *luax_checkmesh(lua_State *L, int idx);
|
Mesh *luax_checkmesh(lua_State *L, int idx);
|
||||||
int w_Mesh_setVertices(lua_State *L);
|
|
||||||
int w_Mesh_setVertex(lua_State *L);
|
|
||||||
int w_Mesh_getVertex(lua_State *L);
|
|
||||||
int w_Mesh_setVertexAttribute(lua_State *L);
|
|
||||||
int w_Mesh_getVertexAttribute(lua_State *L);
|
|
||||||
int w_Mesh_getVertexCount(lua_State *L);
|
|
||||||
int w_Mesh_getVertexFormat(lua_State *L);
|
|
||||||
int w_Mesh_setAttributeEnabled(lua_State *L);
|
|
||||||
int w_Mesh_isAttributeEnabled(lua_State *L);
|
|
||||||
int w_Mesh_attachAttribute(lua_State *L);
|
|
||||||
int w_Mesh_flush(lua_State *L);
|
|
||||||
int w_Mesh_setVertexMap(lua_State *L);
|
|
||||||
int w_Mesh_getVertexMap(lua_State *L);
|
|
||||||
int w_Mesh_setTexture(lua_State *L);
|
|
||||||
int w_Mesh_getTexture(lua_State *L);
|
|
||||||
int w_Mesh_setDrawMode(lua_State *L);
|
|
||||||
int w_Mesh_getDrawMode(lua_State *L);
|
|
||||||
int w_Mesh_setDrawRange(lua_State *L);
|
|
||||||
int w_Mesh_getDrawRange(lua_State *L);
|
|
||||||
extern "C" int luaopen_mesh(lua_State *L);
|
extern "C" int luaopen_mesh(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -705,7 +705,7 @@ int w_ParticleSystem_update(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_ParticleSystem_functions[] =
|
||||||
{
|
{
|
||||||
{ "clone", w_ParticleSystem_clone },
|
{ "clone", w_ParticleSystem_clone },
|
||||||
{ "setTexture", w_ParticleSystem_setTexture },
|
{ "setTexture", w_ParticleSystem_setTexture },
|
||||||
@@ -772,7 +772,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_particlesystem(lua_State *L)
|
extern "C" int luaopen_particlesystem(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_PARTICLE_SYSTEM_ID, functions);
|
return luax_register_type(L, GRAPHICS_PARTICLE_SYSTEM_ID, "ParticleSystem", w_ParticleSystem_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -33,66 +33,6 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
|
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
|
||||||
int w_ParticleSystem_clone(lua_State *L);
|
|
||||||
int w_ParticleSystem_setTexture(lua_State *L);
|
|
||||||
int w_ParticleSystem_getTexture(lua_State *L);
|
|
||||||
int w_ParticleSystem_setBufferSize(lua_State *L);
|
|
||||||
int w_ParticleSystem_getBufferSize(lua_State *L);
|
|
||||||
int w_ParticleSystem_setInsertMode(lua_State *L);
|
|
||||||
int w_ParticleSystem_getInsertMode(lua_State *L);
|
|
||||||
int w_ParticleSystem_setEmissionRate(lua_State *L);
|
|
||||||
int w_ParticleSystem_getEmissionRate(lua_State *L);
|
|
||||||
int w_ParticleSystem_setEmitterLifetime(lua_State *L);
|
|
||||||
int w_ParticleSystem_getEmitterLifetime(lua_State *L);
|
|
||||||
int w_ParticleSystem_setParticleLifetime(lua_State *L);
|
|
||||||
int w_ParticleSystem_getParticleLifetime(lua_State *L);
|
|
||||||
int w_ParticleSystem_setPosition(lua_State *L);
|
|
||||||
int w_ParticleSystem_getPosition(lua_State *L);
|
|
||||||
int w_ParticleSystem_moveTo(lua_State *L);
|
|
||||||
int w_ParticleSystem_setAreaSpread(lua_State *L);
|
|
||||||
int w_ParticleSystem_getAreaSpread(lua_State *L);
|
|
||||||
int w_ParticleSystem_setDirection(lua_State *L);
|
|
||||||
int w_ParticleSystem_getDirection(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSpread(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSpread(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSpeed(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSpeed(lua_State *L);
|
|
||||||
int w_ParticleSystem_setLinearAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_getLinearAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_setRadialAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_getRadialAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_setTangentialAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_getTangentialAcceleration(lua_State *L);
|
|
||||||
int w_ParticleSystem_setLinearDamping(lua_State *L);
|
|
||||||
int w_ParticleSystem_getLinearDamping(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSizes(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSizes(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSizeVariation(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSizeVariation(lua_State *L);
|
|
||||||
int w_ParticleSystem_setRotation(lua_State *L);
|
|
||||||
int w_ParticleSystem_getRotation(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSpin(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSpin(lua_State *L);
|
|
||||||
int w_ParticleSystem_setSpinVariation(lua_State *L);
|
|
||||||
int w_ParticleSystem_getSpinVariation(lua_State *L);
|
|
||||||
int w_ParticleSystem_setColors(lua_State *L);
|
|
||||||
int w_ParticleSystem_getColors(lua_State *L);
|
|
||||||
int w_ParticleSystem_setQuads(lua_State *L);
|
|
||||||
int w_ParticleSystem_getQuads(lua_State *L);
|
|
||||||
int w_ParticleSystem_setOffset(lua_State *L);
|
|
||||||
int w_ParticleSystem_getOffset(lua_State *L);
|
|
||||||
int w_ParticleSystem_setRelativeRotation(lua_State *L);
|
|
||||||
int w_ParticleSystem_hasRelativeRotation(lua_State *L);
|
|
||||||
int w_ParticleSystem_getCount(lua_State *L);
|
|
||||||
int w_ParticleSystem_start(lua_State *L);
|
|
||||||
int w_ParticleSystem_stop(lua_State *L);
|
|
||||||
int w_ParticleSystem_pause(lua_State *L);
|
|
||||||
int w_ParticleSystem_reset(lua_State *L);
|
|
||||||
int w_ParticleSystem_emit(lua_State *L);
|
|
||||||
int w_ParticleSystem_isActive(lua_State *L);
|
|
||||||
int w_ParticleSystem_isPaused(lua_State *L);
|
|
||||||
int w_ParticleSystem_isStopped(lua_State *L);
|
|
||||||
int w_ParticleSystem_update(lua_State *L);
|
|
||||||
extern "C" int luaopen_particlesystem(lua_State *L);
|
extern "C" int luaopen_particlesystem(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
@@ -229,38 +230,86 @@ int w_Shader_sendMatrix(lua_State *L)
|
|||||||
if (!lua_istable(L, 3))
|
if (!lua_istable(L, 3))
|
||||||
return luax_typerror(L, 3, "matrix table");
|
return luax_typerror(L, 3, "matrix table");
|
||||||
|
|
||||||
lua_getfield(L, 3, "dimension");
|
int dimension = 0;
|
||||||
int dimension = (int) lua_tointeger(L, -1);
|
|
||||||
|
lua_rawgeti(L, 3, 1);
|
||||||
|
if (lua_istable(L, -1))
|
||||||
|
dimension = (int) luax_objlen(L, 3);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
if (dimension == 0)
|
||||||
|
{
|
||||||
|
lua_getfield(L, 3, "dimension");
|
||||||
|
|
||||||
|
if (!lua_isnoneornil(L, -1))
|
||||||
|
dimension = (int) lua_tointeger(L, -1);
|
||||||
|
else
|
||||||
|
dimension = (int) sqrtf((float) luax_objlen(L, 3));
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (dimension < 2 || dimension > 4)
|
if (dimension < 2 || dimension > 4)
|
||||||
return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).",
|
return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).",
|
||||||
dimension, dimension);
|
dimension, dimension);
|
||||||
|
|
||||||
float *values = new float[dimension * dimension * count];
|
float *values = new float[dimension * dimension * count];
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
lua_getfield(L, 3+i, "dimension");
|
int other_dimension = 0;
|
||||||
if (lua_tointeger(L, -1) != dimension)
|
|
||||||
|
lua_rawgeti(L, 3+i, 1);
|
||||||
|
bool table_of_tables = lua_istable(L, -1);
|
||||||
|
|
||||||
|
if (table_of_tables)
|
||||||
|
other_dimension = luax_objlen(L, -1);
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
if (!table_of_tables)
|
||||||
|
other_dimension = (int) sqrtf((float) luax_objlen(L, 3+i));
|
||||||
|
|
||||||
|
if (other_dimension != dimension)
|
||||||
{
|
{
|
||||||
// You unlock this door with the key of imagination. Beyond it is
|
// You unlock this door with the key of imagination. Beyond it is
|
||||||
// another dimension: a dimension of sound, a dimension of sight,
|
// another dimension: a dimension of sound, a dimension of sight,
|
||||||
// a dimension of mind. You're moving into a land of both shadow
|
// a dimension of mind. You're moving into a land of both shadow
|
||||||
// and substance, of things and ideas. You've just crossed over
|
// and substance, of things and ideas. You've just crossed over
|
||||||
// into... the Twilight Zone.
|
// into... the Twilight Zone.
|
||||||
int other_dimension = (int) lua_tointeger(L, -1);
|
|
||||||
delete[] values;
|
delete[] values;
|
||||||
return luaL_error(L, "Invalid matrix size at argument %d: Expected size %dx%d, got %dx%d.",
|
return luaL_error(L, "Invalid matrix size at argument %d: Expected size %dx%d, got %dx%d.",
|
||||||
3+i, dimension, dimension, other_dimension, other_dimension);
|
3+i, dimension, dimension, other_dimension, other_dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k = 1; k <= dimension*dimension; ++k)
|
if (table_of_tables)
|
||||||
{
|
{
|
||||||
lua_rawgeti(L, 3+i, k);
|
int n = 0;
|
||||||
values[i * dimension * dimension + k - 1] = (float)lua_tonumber(L, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1 + dimension);
|
for (int j = 1; j <= dimension; j++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 3+i, j);
|
||||||
|
|
||||||
|
for (int k = 1; k <= dimension; k++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, -k, k);
|
||||||
|
values[i * dimension * dimension + n] = (float) lua_tonumber(L, -1);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, dimension + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int k = 1; k <= dimension*dimension; k++)
|
||||||
|
{
|
||||||
|
lua_rawgeti(L, 3+i, k);
|
||||||
|
values[i * dimension * dimension + k - 1] = (float) lua_tonumber(L, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, dimension*dimension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
luax_catchexcept(L,
|
luax_catchexcept(L,
|
||||||
@@ -281,48 +330,6 @@ int w_Shader_sendTexture(lua_State *L)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert matrices on the stack for use with sendMatrix.
|
|
||||||
static void w_convertMatrices(lua_State *L, int idx)
|
|
||||||
{
|
|
||||||
int matrixcount = lua_gettop(L) - (idx - 1);
|
|
||||||
|
|
||||||
for (int matrix = idx; matrix < idx + matrixcount; matrix++)
|
|
||||||
{
|
|
||||||
luaL_checktype(L, matrix, LUA_TTABLE);
|
|
||||||
int dimension = (int) luax_objlen(L, matrix);
|
|
||||||
|
|
||||||
int newi = 1;
|
|
||||||
lua_createtable(L, dimension * dimension, 0);
|
|
||||||
|
|
||||||
// Collapse {{a,b,c}, {d,e,f}, ...} to {a,b,c, d,e,f, ...}
|
|
||||||
for (int i = 1; i <= (int) luax_objlen(L, matrix); i++)
|
|
||||||
{
|
|
||||||
// Push args[matrix][i] onto the stack.
|
|
||||||
lua_rawgeti(L, matrix, i);
|
|
||||||
luaL_checktype(L, -1, LUA_TTABLE);
|
|
||||||
|
|
||||||
for (int j = 1; j <= (int) luax_objlen(L, -1); j++)
|
|
||||||
{
|
|
||||||
// Push args[matrix[i][j] onto the stack.
|
|
||||||
lua_rawgeti(L, -1, j);
|
|
||||||
luaL_checktype(L, -1, LUA_TNUMBER);
|
|
||||||
|
|
||||||
// newtable[newi] = args[matrix][i][j]
|
|
||||||
lua_rawseti(L, -3, newi++);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// newtable.dimension = #args[matrix]
|
|
||||||
lua_pushinteger(L, dimension);
|
|
||||||
lua_setfield(L, -2, "dimension");
|
|
||||||
|
|
||||||
// Replace args[i] with the new table
|
|
||||||
lua_replace(L, matrix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int w_Shader_send(lua_State *L)
|
int w_Shader_send(lua_State *L)
|
||||||
{
|
{
|
||||||
int ttype = lua_type(L, 3);
|
int ttype = lua_type(L, 3);
|
||||||
@@ -352,10 +359,7 @@ int w_Shader_send(lua_State *L)
|
|||||||
if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN)
|
if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN)
|
||||||
return w_Shader_sendFloat(L);
|
return w_Shader_sendFloat(L);
|
||||||
else if (ttype == LUA_TTABLE)
|
else if (ttype == LUA_TTABLE)
|
||||||
{
|
|
||||||
w_convertMatrices(L, 3);
|
|
||||||
return w_Shader_sendMatrix(L);
|
return w_Shader_sendMatrix(L);
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -397,7 +401,7 @@ int w_Shader_getExternVariable(lua_State *L)
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
static const luaL_Reg w_Shader_functions[] =
|
||||||
{
|
{
|
||||||
{ "getWarnings", w_Shader_getWarnings },
|
{ "getWarnings", w_Shader_getWarnings },
|
||||||
{ "sendInt", w_Shader_sendInt },
|
{ "sendInt", w_Shader_sendInt },
|
||||||
@@ -413,7 +417,7 @@ static const luaL_Reg functions[] =
|
|||||||
|
|
||||||
extern "C" int luaopen_shader(lua_State *L)
|
extern "C" int luaopen_shader(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_SHADER_ID, functions);
|
return luax_register_type(L, GRAPHICS_SHADER_ID, "Shader", w_Shader_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -33,14 +33,6 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Shader *luax_checkshader(lua_State *L, int idx);
|
Shader *luax_checkshader(lua_State *L, int idx);
|
||||||
int w_Shader_getWarnings(lua_State *L);
|
|
||||||
int w_Shader_sendInt(lua_State *L);
|
|
||||||
int w_Shader_sendFloat(lua_State *L);
|
|
||||||
int w_Shader_sendColor(lua_State *L);
|
|
||||||
int w_Shader_sendMatrix(lua_State *L);
|
|
||||||
int w_Shader_sendTexture(lua_State *L);
|
|
||||||
int w_Shader_send(lua_State *L);
|
|
||||||
int w_Shader_getExternVariable(lua_State *L);
|
|
||||||
extern "C" int luaopen_shader(lua_State *L);
|
extern "C" int luaopen_shader(lua_State *L);
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
@@ -203,7 +203,17 @@ int w_SpriteBatch_getBufferSize(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const luaL_Reg functions[] =
|
int w_SpriteBatch_attachAttribute(lua_State *L)
|
||||||
|
{
|
||||||
|
SpriteBatch *t = luax_checkspritebatch(L, 1);
|
||||||
|
const char *name = luaL_checkstring(L, 2);
|
||||||
|
Mesh *m = luax_checktype<Mesh>(L, 3, GRAPHICS_MESH_ID);
|
||||||
|
|
||||||
|
luax_catchexcept(L, [&](){ t->attachAttribute(name, m); });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg w_SpriteBatch_functions[] =
|
||||||
{
|
{
|
||||||
{ "add", w_SpriteBatch_add },
|
{ "add", w_SpriteBatch_add },
|
||||||
{ "set", w_SpriteBatch_set },
|
{ "set", w_SpriteBatch_set },
|
||||||
@@ -216,12 +226,13 @@ static const luaL_Reg functions[] =
|
|||||||
{ "getCount", w_SpriteBatch_getCount },
|
{ "getCount", w_SpriteBatch_getCount },
|
||||||
{ "setBufferSize", w_SpriteBatch_setBufferSize },
|
{ "setBufferSize", w_SpriteBatch_setBufferSize },
|
||||||
{ "getBufferSize", w_SpriteBatch_getBufferSize },
|
{ "getBufferSize", w_SpriteBatch_getBufferSize },
|
||||||
|
{ "attachAttribute", w_SpriteBatch_attachAttribute },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" int luaopen_spritebatch(lua_State *L)
|
extern "C" int luaopen_spritebatch(lua_State *L)
|
||||||
{
|
{
|
||||||
return luax_register_type(L, GRAPHICS_SPRITE_BATCH_ID, functions);
|
return luax_register_type(L, GRAPHICS_SPRITE_BATCH_ID, "SpriteBatch", w_SpriteBatch_functions, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // opengl
|
} // opengl
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user