mirror of
https://github.com/love2d/love-android.git
synced 2026-08-19 12:14:49 +02:00
Updated LÖVE source to revision cff5bc8604bd
This commit is contained in:
@@ -53,6 +53,7 @@ public:
|
||||
M_TIMER,
|
||||
M_TOUCH,
|
||||
M_WINDOW,
|
||||
M_VIDEO,
|
||||
M_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -117,6 +117,16 @@ public:
|
||||
return object;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return object != nullptr;
|
||||
}
|
||||
|
||||
operator T*() const
|
||||
{
|
||||
return object;
|
||||
}
|
||||
|
||||
void set(T *obj)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
extern StringMap<Type, TYPE_MAX_ENUM> types;
|
||||
|
||||
static love::Type extractudatatype(lua_State *L, int idx)
|
||||
{
|
||||
Type t = INVALID_ID;
|
||||
@@ -36,7 +34,7 @@ static love::Type extractudatatype(lua_State *L, int idx)
|
||||
lua_pushvalue(L, idx);
|
||||
int result = lua_pcall(L, 1, 1, 0);
|
||||
if (result == 0)
|
||||
types.find(lua_tostring(L, -1), t);
|
||||
getTypeName(lua_tostring(L, -1), t);
|
||||
if (result == 0 || result == LUA_ERRRUN)
|
||||
lua_pop(L, 1);
|
||||
return t;
|
||||
@@ -165,7 +163,7 @@ Variant *Variant::fromLua(lua_State *L, int n, bool allowTables)
|
||||
if (allowTables)
|
||||
{
|
||||
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);
|
||||
while (lua_next(L, n))
|
||||
{
|
||||
@@ -230,7 +228,7 @@ void Variant::toLua(lua_State *L)
|
||||
// I can do (at the moment).
|
||||
break;
|
||||
case TABLE:
|
||||
lua_newtable(L);
|
||||
lua_createtable(L, 0, (int) data.table->size());
|
||||
for (size_t i = 0; i < data.table->size(); ++i)
|
||||
{
|
||||
std::pair<Variant*, Variant*> &kv = data.table->at(i);
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
size_t len;
|
||||
} string;
|
||||
void *userdata;
|
||||
std::vector<std::pair<Variant*, Variant*> > *table;
|
||||
std::vector<std::pair<Variant*, Variant*>> *table;
|
||||
} data;
|
||||
|
||||
private:
|
||||
|
||||
@@ -141,6 +141,8 @@
|
||||
# define LOVE_ENABLE_TOUCH
|
||||
# define LOVE_ENABLE_TOUCH_SDL
|
||||
# define LOVE_ENABLE_UTF8
|
||||
# define LOVE_ENABLE_VIDEO
|
||||
# define LOVE_ENABLE_VIDEO_THEORA
|
||||
# define LOVE_ENABLE_WINDOW
|
||||
# define LOVE_ENABLE_WINDOW_SDL
|
||||
# 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)
|
||||
{
|
||||
love::addTypeName(m.type, m.name);
|
||||
|
||||
// Put a reference to the C++ module in Lua.
|
||||
luax_insistregistry(L, REGISTRY_MODULES);
|
||||
|
||||
@@ -270,12 +272,9 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name)
|
||||
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.
|
||||
const char *tname = "Invalid";
|
||||
if (!love::getType(type, tname))
|
||||
printf("Missing type name entry for type ID %d\n", type);
|
||||
love::addTypeName(type, name);
|
||||
|
||||
// Get the place for storing and re-using instantiated love types.
|
||||
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
|
||||
lua_pop(L, 1);
|
||||
|
||||
luaL_newmetatable(L, tname);
|
||||
luaL_newmetatable(L, name);
|
||||
|
||||
// m.__index = m
|
||||
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");
|
||||
|
||||
// Add tostring function.
|
||||
lua_pushstring(L, tname);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushcclosure(L, w__tostring, 1);
|
||||
lua_setfield(L, -2, "__tostring");
|
||||
|
||||
// Add type
|
||||
lua_pushstring(L, tname);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushcclosure(L, w__type, 1);
|
||||
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_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);
|
||||
|
||||
if (pushmetatable)
|
||||
return 1; // leave the metatable on the stack.
|
||||
va_end(fs);
|
||||
|
||||
lua_pop(L, 1); // Pops metatable.
|
||||
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)
|
||||
{
|
||||
if (tindex < 0)
|
||||
@@ -403,7 +411,7 @@ void luax_rawnewtype(lua_State *L, love::Type type, love::Object *object)
|
||||
u->type = type;
|
||||
|
||||
const char *name = "Invalid";
|
||||
getType(type, name);
|
||||
getTypeName(type, name);
|
||||
|
||||
luaL_newmetatable(L, name);
|
||||
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
|
||||
// describe its type properly, so we only use it for love types.
|
||||
love::Type t;
|
||||
if (!love::getType(argtname, t))
|
||||
if (!love::getTypeName(argtname, t))
|
||||
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 t = INVALID_ID;
|
||||
getType(luaL_checkstring(L, idx), t);
|
||||
getTypeName(luaL_checkstring(L, idx), t);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,10 +247,15 @@ int luax_preload(lua_State *L, lua_CFunction f, const char *name);
|
||||
/**
|
||||
* Register a new type.
|
||||
* @param type The type.
|
||||
* @param f The list of member functions for the type.
|
||||
* @param pushmetatable Whether to push the type's metatable to the stack.
|
||||
* @param name The type's human-readable name
|
||||
* @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
|
||||
@@ -434,7 +439,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type)
|
||||
if (lua_type(L, idx) != LUA_TUSERDATA)
|
||||
{
|
||||
const char *name = "Invalid";
|
||||
getType(type, name);
|
||||
getTypeName(type, 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])
|
||||
{
|
||||
const char *name = "Invalid";
|
||||
getType(type, name);
|
||||
getTypeName(type, name);
|
||||
luax_typerror(L, idx, name);
|
||||
}
|
||||
|
||||
@@ -454,7 +459,7 @@ template <typename T>
|
||||
T *luax_getmodule(lua_State *L, love::Type type)
|
||||
{
|
||||
const char *name = "Invalid";
|
||||
getType(type, name);
|
||||
getTypeName(type, name);
|
||||
|
||||
luax_insistregistry(L, REGISTRY_MODULES);
|
||||
lua_getfield(L, -1, name);
|
||||
@@ -476,7 +481,7 @@ template <typename T>
|
||||
T *luax_optmodule(lua_State *L, love::Type type)
|
||||
{
|
||||
const char *name = "Invalid";
|
||||
getType(type, name);
|
||||
getTypeName(type, name);
|
||||
|
||||
luax_insistregistry(L, REGISTRY_MODULES);
|
||||
lua_getfield(L, -1, name);
|
||||
|
||||
@@ -34,6 +34,7 @@ static const TypeBits *createTypeFlags()
|
||||
b[OBJECT_ID] = one << OBJECT_ID;
|
||||
b[DATA_ID] = (one << DATA_ID) | b[OBJECT_ID];
|
||||
b[MODULE_ID] = (one << MODULE_ID) | b[OBJECT_ID];
|
||||
b[STREAM_ID] = (one << STREAM_ID) | b[OBJECT_ID];
|
||||
|
||||
// Filesystem.
|
||||
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_MESH_ID] = (one << GRAPHICS_MESH_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.
|
||||
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_CHANNEL_ID] = (one << THREAD_CHANNEL_ID) | b[OBJECT_ID];
|
||||
|
||||
// Video
|
||||
b[VIDEO_VIDEO_STREAM_ID] = (one << VIDEO_VIDEO_STREAM_ID) | b[STREAM_ID];
|
||||
|
||||
// Modules.
|
||||
b[MODULE_FILESYSTEM_ID] = (one << MODULE_FILESYSTEM_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();
|
||||
|
||||
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},
|
||||
{"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)
|
||||
bool getTypeName(const char *in, love::Type &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);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ enum Type
|
||||
OBJECT_ID,
|
||||
DATA_ID,
|
||||
MODULE_ID,
|
||||
STREAM_ID,
|
||||
|
||||
// Filesystem.
|
||||
FILESYSTEM_FILE_ID,
|
||||
@@ -56,6 +57,7 @@ enum Type
|
||||
GRAPHICS_SHADER_ID,
|
||||
GRAPHICS_MESH_ID,
|
||||
GRAPHICS_TEXT_ID,
|
||||
GRAPHICS_VIDEO_ID,
|
||||
|
||||
// Image
|
||||
IMAGE_IMAGE_DATA_ID,
|
||||
@@ -106,6 +108,9 @@ enum Type
|
||||
THREAD_THREAD_ID,
|
||||
THREAD_CHANNEL_ID,
|
||||
|
||||
// Video
|
||||
VIDEO_VIDEO_STREAM_ID,
|
||||
|
||||
// The modules themselves. Only add abstracted modules here.
|
||||
MODULE_FILESYSTEM_ID,
|
||||
MODULE_GRAPHICS_ID,
|
||||
@@ -123,8 +128,9 @@ typedef std::bitset<TYPE_MAX_ENUM> TypeBits;
|
||||
**/
|
||||
extern const TypeBits *typeFlags;
|
||||
|
||||
bool getType(const char *in, Type &out);
|
||||
bool getType(Type in, const char *&out);
|
||||
void addTypeName(Type type, const char *name);
|
||||
bool getTypeName(const char *in, Type &out);
|
||||
bool getTypeName(Type in, const char *&out);
|
||||
|
||||
} // love
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ static const int VERSION_MINOR = 10;
|
||||
static const int VERSION_REV = 0;
|
||||
static const char *VERSION = LOVE_VERSION_STRING;
|
||||
static const char *VERSION_COMPATIBILITY[] = { VERSION, 0 };
|
||||
static const char *VERSION_CODENAME = "";
|
||||
static const char *VERSION_CODENAME = "Super Toast";
|
||||
|
||||
} // love
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ const luaL_Reg w_Data_functions[] =
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@ namespace love
|
||||
{
|
||||
|
||||
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);
|
||||
extern const luaL_Reg w_Data_functions[];
|
||||
|
||||
} // love
|
||||
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
#include <string.h>
|
||||
|
||||
b2ChainShape::~b2ChainShape()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void b2ChainShape::Clear()
|
||||
{
|
||||
b2Free(m_vertices);
|
||||
m_vertices = NULL;
|
||||
@@ -56,10 +61,8 @@ void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count)
|
||||
b2Assert(count >= 2);
|
||||
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.
|
||||
b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop);
|
||||
b2Assert(b2DistanceSquared(vertices[i-1], vertices[i]) > b2_linearSlop * b2_linearSlop);
|
||||
}
|
||||
|
||||
m_count = count;
|
||||
|
||||
@@ -37,6 +37,9 @@ public:
|
||||
/// The destructor frees the vertices using b2Free.
|
||||
~b2ChainShape();
|
||||
|
||||
/// Clear all data.
|
||||
void Clear();
|
||||
|
||||
/// Create a loop. This automatically adjusts connectivity.
|
||||
/// @param vertices an array of vertices, these are copied
|
||||
/// @param count the vertex count
|
||||
|
||||
@@ -138,7 +138,7 @@ void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
|
||||
bool unique = true;
|
||||
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;
|
||||
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;
|
||||
|
||||
// Copy vertices.
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
struct b2Color
|
||||
{
|
||||
b2Color() {}
|
||||
b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {}
|
||||
void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; }
|
||||
float32 r, g, 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, float32 ai = 1.0f) { r = ri; g = gi; b = bi; a = ai; }
|
||||
float32 r, g, b, a;
|
||||
};
|
||||
|
||||
/// Implement and register this class with a b2World to provide debug drawing of physics
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <Box2D/Common/b2Settings.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
/// This function is used to ensure that a floating point number is not a NaN or infinity.
|
||||
inline bool b2IsValid(float32 x)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#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.
|
||||
void* b2Alloc(int32 size)
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#define B2_DEBUG_SOLVER 0
|
||||
|
||||
bool g_blockSolve = true;
|
||||
|
||||
struct b2ContactPositionConstraint
|
||||
{
|
||||
b2Vec2 localPoints[b2_maxManifoldPoints];
|
||||
@@ -213,7 +215,7 @@ void b2ContactSolver::InitializeVelocityConstraints()
|
||||
}
|
||||
|
||||
// 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* vcp2 = vc->points + 1;
|
||||
@@ -341,29 +343,32 @@ void b2ContactSolver::SolveVelocityConstraints()
|
||||
}
|
||||
|
||||
// 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
|
||||
b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
|
||||
// Relative velocity at contact
|
||||
b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
|
||||
|
||||
// Compute normal impulse
|
||||
float32 vn = b2Dot(dv, normal);
|
||||
float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias);
|
||||
// Compute normal impulse
|
||||
float32 vn = b2Dot(dv, normal);
|
||||
float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias);
|
||||
|
||||
// b2Clamp the accumulated impulse
|
||||
float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
|
||||
lambda = newImpulse - vcp->normalImpulse;
|
||||
vcp->normalImpulse = newImpulse;
|
||||
// b2Clamp the accumulated impulse
|
||||
float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
|
||||
lambda = newImpulse - vcp->normalImpulse;
|
||||
vcp->normalImpulse = newImpulse;
|
||||
|
||||
// Apply contact impulse
|
||||
b2Vec2 P = lambda * normal;
|
||||
vA -= mA * P;
|
||||
wA -= iA * b2Cross(vcp->rA, P);
|
||||
// Apply contact impulse
|
||||
b2Vec2 P = lambda * normal;
|
||||
vA -= mA * P;
|
||||
wA -= iA * b2Cross(vcp->rA, P);
|
||||
|
||||
vB += mB * P;
|
||||
wB += iB * b2Cross(vcp->rB, P);
|
||||
vB += mB * P;
|
||||
wB += iB * b2Cross(vcp->rB, P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -174,6 +174,9 @@ void b2PrismaticJoint::InitVelocityConstraints(const b2SolverData& data)
|
||||
m_s1 = b2Cross(d + rA, 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 k12 = iA * m_s1 + iB * m_s2;
|
||||
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;
|
||||
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
|
||||
{
|
||||
K.GetSymInverse33(&m_mass);
|
||||
@@ -271,7 +277,17 @@ bool b2WeldJoint::SolvePositionConstraints(const b2SolverData& data)
|
||||
|
||||
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);
|
||||
|
||||
cA -= mA * P;
|
||||
|
||||
@@ -72,9 +72,8 @@ struct b2WheelJointDef : public b2JointDef
|
||||
};
|
||||
|
||||
/// 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
|
||||
/// joint limit to restrict the range of motion and a joint motor to drive
|
||||
/// the rotation or to model rotational friction.
|
||||
/// along an axis fixed in bodyA and rotation in the plane. In other words, it is a point to
|
||||
/// line constraint with a rotational motor and a linear spring/damper.
|
||||
/// This joint is designed for vehicle suspensions.
|
||||
class b2WheelJoint : public b2Joint
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
b2World::b2World(const b2Vec2& gravity)
|
||||
{
|
||||
m_destructionListener = NULL;
|
||||
m_debugDraw = NULL;
|
||||
g_debugDraw = NULL;
|
||||
|
||||
m_bodyList = NULL;
|
||||
m_jointList = NULL;
|
||||
@@ -101,7 +101,7 @@ void b2World::SetContactListener(b2ContactListener* listener)
|
||||
|
||||
void b2World::SetDebugDraw(b2Draw* debugDraw)
|
||||
{
|
||||
m_debugDraw = debugDraw;
|
||||
g_debugDraw = debugDraw;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -1049,7 +1049,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
||||
b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape();
|
||||
b2Vec2 v1 = b2Mul(xf, edge->m_vertex1);
|
||||
b2Vec2 v2 = b2Mul(xf, edge->m_vertex2);
|
||||
m_debugDraw->DrawSegment(v1, v2, color);
|
||||
g_debugDraw->DrawSegment(v1, v2, color);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1063,8 +1063,8 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
||||
for (int32 i = 1; i < count; ++i)
|
||||
{
|
||||
b2Vec2 v2 = b2Mul(xf, vertices[i]);
|
||||
m_debugDraw->DrawSegment(v1, v2, color);
|
||||
m_debugDraw->DrawCircle(v1, 0.05f, color);
|
||||
g_debugDraw->DrawSegment(v1, v2, color);
|
||||
g_debugDraw->DrawCircle(v1, 0.05f, color);
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
@@ -1082,7 +1082,7 @@ void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color
|
||||
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
|
||||
}
|
||||
|
||||
m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color);
|
||||
g_debugDraw->DrawSolidPolygon(vertices, vertexCount, color);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1107,7 +1107,7 @@ void b2World::DrawJoint(b2Joint* joint)
|
||||
switch (joint->GetType())
|
||||
{
|
||||
case e_distanceJoint:
|
||||
m_debugDraw->DrawSegment(p1, p2, color);
|
||||
g_debugDraw->DrawSegment(p1, p2, color);
|
||||
break;
|
||||
|
||||
case e_pulleyJoint:
|
||||
@@ -1115,9 +1115,9 @@ void b2World::DrawJoint(b2Joint* joint)
|
||||
b2PulleyJoint* pulley = (b2PulleyJoint*)joint;
|
||||
b2Vec2 s1 = pulley->GetGroundAnchorA();
|
||||
b2Vec2 s2 = pulley->GetGroundAnchorB();
|
||||
m_debugDraw->DrawSegment(s1, p1, color);
|
||||
m_debugDraw->DrawSegment(s2, p2, color);
|
||||
m_debugDraw->DrawSegment(s1, s2, color);
|
||||
g_debugDraw->DrawSegment(s1, p1, color);
|
||||
g_debugDraw->DrawSegment(s2, p2, color);
|
||||
g_debugDraw->DrawSegment(s1, s2, color);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1126,20 +1126,20 @@ void b2World::DrawJoint(b2Joint* joint)
|
||||
break;
|
||||
|
||||
default:
|
||||
m_debugDraw->DrawSegment(x1, p1, color);
|
||||
m_debugDraw->DrawSegment(p1, p2, color);
|
||||
m_debugDraw->DrawSegment(x2, p2, color);
|
||||
g_debugDraw->DrawSegment(x1, p1, color);
|
||||
g_debugDraw->DrawSegment(p1, p2, color);
|
||||
g_debugDraw->DrawSegment(x2, p2, color);
|
||||
}
|
||||
}
|
||||
|
||||
void b2World::DrawDebugData()
|
||||
{
|
||||
if (m_debugDraw == NULL)
|
||||
if (g_debugDraw == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 flags = m_debugDraw->GetFlags();
|
||||
uint32 flags = g_debugDraw->GetFlags();
|
||||
|
||||
if (flags & b2Draw::e_shapeBit)
|
||||
{
|
||||
@@ -1191,7 +1191,7 @@ void b2World::DrawDebugData()
|
||||
//b2Vec2 cA = fixtureA->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[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();
|
||||
xf.p = b->GetWorldCenter();
|
||||
m_debugDraw->DrawTransform(xf);
|
||||
g_debugDraw->DrawTransform(xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ private:
|
||||
bool m_allowSleep;
|
||||
|
||||
b2DestructionListener* m_destructionListener;
|
||||
b2Draw* m_debugDraw;
|
||||
b2Draw* g_debugDraw;
|
||||
|
||||
// This is used to compute the time step ratio to
|
||||
// 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
|
||||
|
||||
Do this:
|
||||
@@ -25,13 +25,16 @@
|
||||
|
||||
TGA (not sure what subset, if a subset)
|
||||
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)
|
||||
HDR (radiance rgbE format)
|
||||
PIC (Softimage PIC)
|
||||
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 arbitrary I/O callbacks
|
||||
- SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
|
||||
@@ -143,6 +146,10 @@
|
||||
|
||||
|
||||
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.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
||||
2.03 (2015-04-12) additional corruption checking
|
||||
@@ -175,38 +182,41 @@
|
||||
Tom Seddon (pic) the Horde3D community
|
||||
Thatcher Ulrich (psd) Janez Zemva
|
||||
Ken Miller (pgm, ppm) Jonathan Blow
|
||||
Laurent Gomila
|
||||
urraka@github (animated gif) Laurent Gomila
|
||||
Aruelien Pocheville
|
||||
Extensions, features Ryamond Barbiero
|
||||
Jetro Lauha (stbi_info) David Woo
|
||||
Martin "SpartanJ" Golini (stbi_info) Martin Golini
|
||||
James "moose2000" Brown (iPhone PNG) Roy Eltham
|
||||
Ben "Disch" Wenger (io callbacks) Luke Graham
|
||||
Omar Cornut (1/2/4-bit PNG) Thomas Ruf
|
||||
Nicolas Guillemot (vertical flip) John Bartholomew
|
||||
Ken Hamada
|
||||
Optimizations & bugfixes Cort Stratton
|
||||
Fabian "ryg" Giesen Blazej Dariusz Roszkowski
|
||||
Arseny Kapoulkine Thibault Reuille
|
||||
Ryamond Barbiero
|
||||
David Woo
|
||||
Extensions, features Martin Golini
|
||||
Jetro Lauha (stbi_info) Roy Eltham
|
||||
Martin "SpartanJ" Golini (stbi_info) Luke Graham
|
||||
James "moose2000" Brown (iPhone PNG) Thomas Ruf
|
||||
Ben "Disch" Wenger (io callbacks) John Bartholomew
|
||||
Omar Cornut (1/2/4-bit PNG) Ken Hamada
|
||||
Nicolas Guillemot (vertical flip) Cort Stratton
|
||||
Richard Mitton (16-bit PSD) Blazej Dariusz Roszkowski
|
||||
Thibault Reuille
|
||||
Paul Du Bois
|
||||
Guillaume George
|
||||
If your name should be here but Jerry Jansson
|
||||
isn't, let Sean know. Hayaki Saito
|
||||
Jerry Jansson
|
||||
Hayaki Saito
|
||||
Johan Duparc
|
||||
Ronny Chevalier
|
||||
Michal Cichon
|
||||
Tero Hanninen
|
||||
Sergio Gonzalez
|
||||
Optimizations & bugfixes Michal Cichon
|
||||
Fabian "ryg" Giesen Tero Hanninen
|
||||
Arseny Kapoulkine Sergio Gonzalez
|
||||
Cass Everitt
|
||||
Engin Manap
|
||||
Martins Mozeiko
|
||||
Joseph Thomson
|
||||
If your name should be here but Martins Mozeiko
|
||||
isn't, let Sean know. Joseph Thomson
|
||||
Phil Jordan
|
||||
Nathan Reed
|
||||
Michaelangel007@github
|
||||
|
||||
License:
|
||||
This software is in the public domain. Where that dedication is not
|
||||
recognized, you are granted a perpetual, irrevocable license to copy
|
||||
and modify this file however you want.
|
||||
LICENSE
|
||||
|
||||
This software is in the public domain. Where that dedication is not
|
||||
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 *img_buffer, *img_buffer_end;
|
||||
stbi_uc *img_buffer_original;
|
||||
stbi_uc *img_buffer_original, *img_buffer_original_end;
|
||||
} 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->read_from_callbacks = 0;
|
||||
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
|
||||
@@ -772,6 +782,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *
|
||||
s->read_from_callbacks = 1;
|
||||
s->img_buffer_original = s->buffer_start;
|
||||
stbi__refill_buffer(s);
|
||||
s->img_buffer_original_end = s->img_buffer_end;
|
||||
}
|
||||
|
||||
#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
|
||||
// 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_end = s->img_buffer_original_end;
|
||||
}
|
||||
|
||||
#ifndef STBI_NO_JPEG
|
||||
@@ -900,8 +912,8 @@ static void *stbi__malloc(size_t size)
|
||||
#define stbi__err(x,y) stbi__err(x)
|
||||
#endif
|
||||
|
||||
#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL))
|
||||
#define stbi__errpuc(x,y) ((unsigned char *) (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 *)(size_t) (stbi__err(x,y)?NULL:NULL))
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef STBI_NO_HDR
|
||||
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -1152,6 +1165,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f)
|
||||
stbi__start_file(&s,f);
|
||||
return stbi__hdr_test(&s);
|
||||
#else
|
||||
STBI_NOTUSED(f);
|
||||
return 0;
|
||||
#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);
|
||||
return stbi__hdr_test(&s);
|
||||
#else
|
||||
STBI_NOTUSED(clbk);
|
||||
STBI_NOTUSED(user);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -1278,23 +1294,29 @@ static int stbi__get16be(stbi__context *s)
|
||||
return (z << 8) + stbi__get8(s);
|
||||
}
|
||||
|
||||
#if !defined(STBI_NO_PNG) || !defined(STBI_NO_PSD)
|
||||
static stbi__uint32 stbi__get32be(stbi__context *s)
|
||||
{
|
||||
stbi__uint32 z = 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)
|
||||
{
|
||||
int z = stbi__get8(s);
|
||||
return z + (stbi__get8(s) << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef STBI_NO_BMP
|
||||
static stbi__uint32 stbi__get32le(stbi__context *s)
|
||||
{
|
||||
stbi__uint32 z = stbi__get16le(s);
|
||||
return z + (stbi__get16le(s) << 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#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) {
|
||||
for(--i; i >= 0; --i) {
|
||||
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");
|
||||
}
|
||||
@@ -3500,10 +3522,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
|
||||
z->size [c] = (stbi_uc ) s;
|
||||
z->value[c] = (stbi__uint16) i;
|
||||
if (s <= STBI__ZFAST_BITS) {
|
||||
int k = stbi__bit_reverse(next_code[s],s);
|
||||
while (k < (1 << STBI__ZFAST_BITS)) {
|
||||
z->fast[k] = fastv;
|
||||
k += (1 << s);
|
||||
int j = stbi__bit_reverse(next_code[s],s);
|
||||
while (j < (1 << STBI__ZFAST_BITS)) {
|
||||
z->fast[j] = fastv;
|
||||
j += (1 << s);
|
||||
}
|
||||
}
|
||||
++next_code[s];
|
||||
@@ -3542,7 +3564,7 @@ static void stbi__fill_bits(stbi__zbuf *z)
|
||||
{
|
||||
do {
|
||||
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;
|
||||
} 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 (img_n != out_n) {
|
||||
int q;
|
||||
// insert alpha = 255
|
||||
stbi_uc *cur = a->out + stride*j;
|
||||
int i;
|
||||
cur = a->out + stride*j;
|
||||
if (img_n == 1) {
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
cur[i*2+1] = 255;
|
||||
cur[i*2+0] = cur[i];
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*2+1] = 255;
|
||||
cur[q*2+0] = cur[q];
|
||||
}
|
||||
} else {
|
||||
STBI_ASSERT(img_n == 3);
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
cur[i*4+3] = 255;
|
||||
cur[i*4+2] = cur[i*3+2];
|
||||
cur[i*4+1] = cur[i*3+1];
|
||||
cur[i*4+0] = cur[i*3+0];
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*4+3] = 255;
|
||||
cur[q*4+2] = cur[q*3+2];
|
||||
cur[q*4+1] = cur[q*3+1];
|
||||
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)
|
||||
{
|
||||
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];
|
||||
int psize=0,i,j,compress=0,width;
|
||||
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;
|
||||
mb = 0xffu << 0;
|
||||
ma = 0xffu << 24;
|
||||
fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
|
||||
STBI_NOTUSED(fake_a);
|
||||
all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
|
||||
} else {
|
||||
mr = 31u << 10;
|
||||
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);
|
||||
z += 3;
|
||||
a = (easy == 2 ? stbi__get8(s) : 255);
|
||||
all_a |= a;
|
||||
if (target == 4) out[z++] = a;
|
||||
}
|
||||
} 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 & mb, bshift, bcount));
|
||||
a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
|
||||
all_a |= a;
|
||||
if (target == 4) out[z++] = STBI__BYTECAST(a);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
stbi_uc t;
|
||||
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) {
|
||||
for (i=0; i < tga_height; ++i) {
|
||||
int y = tga_inverted ? tga_height -i - 1 : i;
|
||||
stbi_uc *tga_row = tga_data + y*tga_width*tga_comp;
|
||||
int row = tga_inverted ? tga_height -i - 1 : i;
|
||||
stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
|
||||
stbi__getn(s, tga_row, tga_width * tga_comp);
|
||||
}
|
||||
} else {
|
||||
@@ -5053,6 +5082,7 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
|
||||
int pixelCount;
|
||||
int channelCount, compression;
|
||||
int channel, i, count, len;
|
||||
int bitdepth;
|
||||
int w,h;
|
||||
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);
|
||||
|
||||
// Make sure the depth is 8 bits.
|
||||
if (stbi__get16be(s) != 8)
|
||||
return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit");
|
||||
bitdepth = stbi__get16be(s);
|
||||
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.
|
||||
// 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;
|
||||
if (channel >= channelCount) {
|
||||
// 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 {
|
||||
// Read the RLE data.
|
||||
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;
|
||||
if (channel > channelCount) {
|
||||
// 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 {
|
||||
// Read the data.
|
||||
for (i = 0; i < pixelCount; i++)
|
||||
*p = stbi__get8(s), p += 4;
|
||||
if (bitdepth == 16) {
|
||||
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 (comp) *comp = channelCount;
|
||||
if (comp) *comp = 4;
|
||||
*y = h;
|
||||
*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
|
||||
stbi_uc value[4];
|
||||
int i;
|
||||
|
||||
if (count==128)
|
||||
count = stbi__get16be(s);
|
||||
@@ -5435,8 +5472,8 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
int w,h;
|
||||
stbi_uc *out; // output buffer (always 4 components)
|
||||
int flags, bgindex, ratio, transparent, eflags;
|
||||
stbi_uc *out, *old_out; // output buffer (always 4 components)
|
||||
int flags, bgindex, ratio, transparent, eflags, delay;
|
||||
stbi_uc pal[256][4];
|
||||
stbi_uc lpal[256][4];
|
||||
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)
|
||||
{
|
||||
stbi_uc lzw_cs;
|
||||
stbi__int32 len, code;
|
||||
stbi__int32 len, init_code;
|
||||
stbi__uint32 first;
|
||||
stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
|
||||
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;
|
||||
bits = 0;
|
||||
valid_bits = 0;
|
||||
for (code = 0; code < clear; code++) {
|
||||
g->codes[code].prefix = -1;
|
||||
g->codes[code].first = (stbi_uc) code;
|
||||
g->codes[code].suffix = (stbi_uc) code;
|
||||
for (init_code = 0; init_code < clear; init_code++) {
|
||||
g->codes[init_code].prefix = -1;
|
||||
g->codes[init_code].first = (stbi_uc) init_code;
|
||||
g->codes[init_code].suffix = (stbi_uc) init_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];
|
||||
// @OPTIMIZE: write a dword at a time
|
||||
for (i = 0; i < g->w * g->h * 4; i += 4) {
|
||||
stbi_uc *p = &g->out[i];
|
||||
p[0] = c[2];
|
||||
p[1] = c[1];
|
||||
p[2] = c[0];
|
||||
p[3] = c[3];
|
||||
for (y = y0; y < y1; y += 4 * g->w) {
|
||||
for (x = x0; x < x1; x += 4) {
|
||||
stbi_uc *p = &g->out[y + x];
|
||||
p[0] = c[2];
|
||||
p[1] = c[1];
|
||||
p[2] = c[0];
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
stbi_uc *old_out = 0;
|
||||
stbi_uc *prev_out = 0;
|
||||
|
||||
if (g->out == 0) {
|
||||
if (!stbi__gif_header(s, g, comp,0)) 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");
|
||||
stbi__fill_gif_background(g);
|
||||
} else {
|
||||
// animated-gif-only path
|
||||
if (((g->eflags & 0x1C) >> 2) == 3) {
|
||||
old_out = g->out;
|
||||
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
|
||||
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
|
||||
memcpy(g->out, old_out, g->w*g->h*4);
|
||||
}
|
||||
if (g->out == 0 && !stbi__gif_header(s, g, comp,0))
|
||||
return 0; // stbi__g_failure_reason set by stbi__gif_header
|
||||
|
||||
prev_out = g->out;
|
||||
g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);
|
||||
if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");
|
||||
|
||||
switch ((g->eflags & 0x1C) >> 2) {
|
||||
case 0: // unspecified (also always used on 1st frame)
|
||||
stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h);
|
||||
break;
|
||||
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 (;;) {
|
||||
switch (stbi__get8(s)) {
|
||||
case 0x2C: /* Image Descriptor */
|
||||
{
|
||||
int prev_trans = -1;
|
||||
stbi__int32 x, y, w, h;
|
||||
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);
|
||||
g->color_table = (stbi_uc *) g->lpal;
|
||||
} else if (g->flags & 0x80) {
|
||||
for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent
|
||||
g->pal[i][3] = 255;
|
||||
if (g->transparent >= 0 && (g->eflags & 0x01))
|
||||
if (g->transparent >= 0 && (g->eflags & 0x01)) {
|
||||
prev_trans = g->pal[g->transparent][3];
|
||||
g->pal[g->transparent][3] = 0;
|
||||
}
|
||||
g->color_table = (stbi_uc *) g->pal;
|
||||
} else
|
||||
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);
|
||||
if (o == NULL) return NULL;
|
||||
|
||||
if (req_comp && req_comp != 4)
|
||||
o = stbi__convert_format(o, 4, req_comp, g->w, g->h);
|
||||
if (prev_trans != -1)
|
||||
g->pal[g->transparent][3] = prev_trans;
|
||||
|
||||
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);
|
||||
if (len == 4) {
|
||||
g->eflags = stbi__get8(s);
|
||||
stbi__get16le(s); // delay
|
||||
g->delay = stbi__get16le(s);
|
||||
g->transparent = stbi__get8(s);
|
||||
} else {
|
||||
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) {
|
||||
*x = g.w;
|
||||
*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;
|
||||
}
|
||||
@@ -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;
|
||||
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);
|
||||
*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)) {
|
||||
stbi__rewind( s );
|
||||
return 0;
|
||||
stbi__rewind( s );
|
||||
return 0;
|
||||
}
|
||||
|
||||
stbi__skip(s, 8);
|
||||
@@ -6292,6 +6356,12 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
|
||||
|
||||
/*
|
||||
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.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
|
||||
2.03 (2015-04-12) extra corruption checking (mmozeiko)
|
||||
|
||||
@@ -37,6 +37,7 @@ extern "C" {
|
||||
|
||||
#ifdef LOVE_MACOSX
|
||||
#include "common/macosx.h"
|
||||
#include <unistd.h>
|
||||
#endif // LOVE_MACOSX
|
||||
|
||||
#ifdef LOVE_IOS
|
||||
@@ -110,12 +111,16 @@ static void get_app_arguments(int argc, char **argv, int &new_argc, char **&new_
|
||||
}
|
||||
|
||||
#ifdef LOVE_MACOSX
|
||||
// Check for a drop file string.
|
||||
std::string dropfilestr = love::macosx::checkDropEvents();
|
||||
// Check for a drop file string, if the app wasn't launched in a terminal.
|
||||
// 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())
|
||||
{
|
||||
temp_argv.insert(temp_argv.begin() + 1, dropfilestr);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
TYPE_STATIC,
|
||||
TYPE_STREAM,
|
||||
TYPE_MAX_ENUM
|
||||
}; // Type
|
||||
};
|
||||
|
||||
enum Unit
|
||||
{
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
|
||||
virtual void seek(float offset, Unit unit) = 0;
|
||||
virtual float tell(Unit unit) = 0;
|
||||
virtual double getDuration(Unit unit) = 0;
|
||||
|
||||
// all float * v must be of size 3
|
||||
virtual void setPosition(float *v) = 0;
|
||||
|
||||
@@ -112,6 +112,11 @@ float Source::tell(Source::Unit)
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
double Source::getDuration(Unit)
|
||||
{
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
void Source::setPosition(float *)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
virtual float getVolume() const;
|
||||
virtual void seek(float offset, Unit unit);
|
||||
virtual float tell(Unit unit);
|
||||
virtual double getDuration(Unit unit);
|
||||
virtual void setPosition(float *v);
|
||||
virtual void getPosition(float *v) const;
|
||||
virtual void setVelocity(float *v);
|
||||
|
||||
@@ -263,6 +263,12 @@ float Pool::tell(Source *source, void *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
|
||||
{
|
||||
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
void softRewind(Source *source);
|
||||
void seek(Source *source, float offset, void *unit);
|
||||
float tell(Source *source, void *unit);
|
||||
double getDuration(Source *source, void *unit);
|
||||
|
||||
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)
|
||||
: size(size)
|
||||
{
|
||||
alGenBuffers(1, &buffer);
|
||||
alBufferData(buffer, format, data, size, freq);
|
||||
@@ -95,6 +96,7 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(soundData->getSampleRate())
|
||||
, channels(soundData->getChannels())
|
||||
, bitDepth(soundData->getBitDepth())
|
||||
, decoder(nullptr)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -134,6 +136,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(decoder->getSampleRate())
|
||||
, channels(decoder->getChannels())
|
||||
, bitDepth(decoder->getBitDepth())
|
||||
, decoder(decoder)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -169,6 +172,7 @@ Source::Source(const Source &s)
|
||||
, offsetSeconds(0)
|
||||
, sampleRate(s.sampleRate)
|
||||
, channels(s.channels)
|
||||
, bitDepth(s.bitDepth)
|
||||
, decoder(nullptr)
|
||||
, toLoop(0)
|
||||
{
|
||||
@@ -308,8 +312,11 @@ bool Source::update()
|
||||
offsetSamples += (curOffsetSamples - newOffsetSamples);
|
||||
offsetSeconds += (curOffsetSecs - newOffsetSecs);
|
||||
|
||||
streamAtomic(buffer, decoder.get());
|
||||
alSourceQueueBuffers(source, 1, &buffer);
|
||||
// FIXME: We should put freed buffers into a list that we later
|
||||
// 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;
|
||||
@@ -438,6 +445,36 @@ float Source::tell(Source::Unit 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)
|
||||
{
|
||||
if (channels > 1)
|
||||
@@ -575,8 +612,11 @@ bool Source::playAtomic()
|
||||
|
||||
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
|
||||
{
|
||||
streamAtomic(streamBuffers[i], decoder.get());
|
||||
if (streamAtomic(streamBuffers[i], decoder.get()) == 0)
|
||||
break;
|
||||
|
||||
++usedBuffers;
|
||||
|
||||
if (decoder->isFinished())
|
||||
break;
|
||||
}
|
||||
@@ -736,13 +776,18 @@ ALenum Source::getFormat(int channels, int bitDepth) const
|
||||
int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
|
||||
{
|
||||
// Get more sound data.
|
||||
int decoded = d->decode();
|
||||
decoded = decoded >= 0 ? decoded : 0;
|
||||
int decoded = std::max(d->decode(), 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)
|
||||
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
|
||||
if (fmt != 0)
|
||||
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
|
||||
else
|
||||
decoded = 0;
|
||||
}
|
||||
|
||||
if (decoder->isFinished() && isLooping())
|
||||
{
|
||||
|
||||
@@ -65,9 +65,15 @@ public:
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline ALsizei getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ALuint buffer;
|
||||
ALsizei size;
|
||||
|
||||
}; // StaticDataBuffer
|
||||
|
||||
@@ -98,6 +104,8 @@ public:
|
||||
virtual void seek(float offset, Unit unit);
|
||||
virtual float tellAtomic(void *unit) const;
|
||||
virtual float tell(Unit unit);
|
||||
virtual double getDurationAtomic(void *unit);
|
||||
virtual double getDuration(Unit unit);
|
||||
virtual void setPosition(float *v);
|
||||
virtual void getPosition(float *v) const;
|
||||
virtual void setVelocity(float *v);
|
||||
@@ -180,6 +188,7 @@ private:
|
||||
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitDepth;
|
||||
|
||||
StrongRef<love::sound::Decoder> decoder;
|
||||
|
||||
|
||||
@@ -32,29 +32,6 @@ namespace love
|
||||
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);
|
||||
|
||||
} // audio
|
||||
|
||||
@@ -140,6 +140,19 @@ int w_Source_tell(lua_State *L)
|
||||
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)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
@@ -357,7 +370,7 @@ int w_Source_getType(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
static const luaL_Reg w_Source_functions[] =
|
||||
{
|
||||
{ "clone", w_Source_clone },
|
||||
|
||||
@@ -373,6 +386,7 @@ static const luaL_Reg functions[] =
|
||||
{ "getVolume", w_Source_getVolume },
|
||||
{ "seek", w_Source_seek },
|
||||
{ "tell", w_Source_tell },
|
||||
{ "getDuration", w_Source_getDuration },
|
||||
{ "setPosition", w_Source_setPosition },
|
||||
{ "getPosition", w_Source_getPosition },
|
||||
{ "setVelocity", w_Source_setVelocity },
|
||||
@@ -406,7 +420,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -30,41 +30,6 @@ namespace audio
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // audio
|
||||
|
||||
@@ -556,12 +556,6 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
|
||||
{
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
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));
|
||||
msg = new Message("focus", vargs);
|
||||
break;
|
||||
|
||||
@@ -141,7 +141,7 @@ extern "C" int luaopen_love_event(lua_State *L)
|
||||
w.name = "event";
|
||||
w.type = MODULE_ID;
|
||||
w.functions = functions;
|
||||
w.types = 0;
|
||||
w.types = nullptr;
|
||||
|
||||
return luax_register_module(L, w);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,6 @@ namespace love
|
||||
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);
|
||||
|
||||
} // event
|
||||
|
||||
@@ -125,12 +125,8 @@ void Filesystem::init(const char *arg0)
|
||||
if (!PHYSFS_init(arg0))
|
||||
throw love::Exception("%s", PHYSFS_getLastError());
|
||||
|
||||
PHYSFS_Version version = {};
|
||||
PHYSFS_getLinkedVersion(&version);
|
||||
|
||||
// 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);
|
||||
// Enable symlinks by default. Also fixes an issue in PhysFS 2.1-alpha.
|
||||
setSymlinksEnabled(true);
|
||||
}
|
||||
|
||||
void Filesystem::setFused(bool fused)
|
||||
@@ -223,7 +219,7 @@ bool Filesystem::setSource(const char *source)
|
||||
std::string new_search_path = source;
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
if (!love::android::createStorageDirectories ())
|
||||
if (!love::android::createStorageDirectories())
|
||||
SDL_Log("Error creating storage directories!");
|
||||
|
||||
char* game_archive_ptr = NULL;
|
||||
@@ -664,6 +660,17 @@ int64 Filesystem::getSize(const char *filename) const
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,31 +31,9 @@ DroppedFile *luax_checkdroppedfile(lua_State *L, int idx)
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -413,7 +413,7 @@ int w_File_getExtension(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
const luaL_Reg w_File_functions[] =
|
||||
{
|
||||
{ "getSize", w_File_getSize },
|
||||
{ "open", w_File_open },
|
||||
@@ -436,7 +436,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -34,25 +34,11 @@ namespace filesystem
|
||||
int luax_ioError(lua_State *L, const char *fmt, ...);
|
||||
|
||||
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(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 const luaL_Reg w_File_functions[];
|
||||
|
||||
} // filesystem
|
||||
} // love
|
||||
|
||||
|
||||
@@ -48,11 +48,6 @@ int w_FileData_getExtension(lua_State *L)
|
||||
|
||||
static const luaL_Reg w_FileData_functions[] =
|
||||
{
|
||||
// Data
|
||||
{ "getString", w_Data_getString },
|
||||
{ "getPointer", w_Data_getPointer },
|
||||
{ "getSize", w_Data_getSize },
|
||||
|
||||
{ "getFilename", w_FileData_getFilename },
|
||||
{ "getExtension", w_FileData_getExtension },
|
||||
|
||||
@@ -61,7 +56,7 @@ static const luaL_Reg w_FileData_functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace filesystem
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // filesystem
|
||||
|
||||
@@ -155,19 +155,28 @@ int w_newFile(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileData *luax_getfiledata(lua_State *L, int idx)
|
||||
File *luax_getfile(lua_State *L, int idx)
|
||||
{
|
||||
FileData *data = nullptr;
|
||||
File *file = nullptr;
|
||||
|
||||
if (lua_isstring(L, idx))
|
||||
{
|
||||
const char *filename = luaL_checkstring(L, idx);
|
||||
file = instance()->newFile(filename);
|
||||
}
|
||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_ID))
|
||||
{
|
||||
else
|
||||
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();
|
||||
}
|
||||
else if (luax_istype(L, idx, FILESYSTEM_FILE_DATA_ID))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
// LOVE
|
||||
#include "common/runtime.h"
|
||||
#include "File.h"
|
||||
#include "FileData.h"
|
||||
|
||||
namespace love
|
||||
@@ -38,45 +39,9 @@ namespace filesystem
|
||||
* May trigger a Lua error.
|
||||
**/
|
||||
FileData *luax_getfiledata(lua_State *L, int idx);
|
||||
File *luax_getfile(lua_State *L, int idx);
|
||||
|
||||
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 extloader(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.
|
||||
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.
|
||||
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)
|
||||
{
|
||||
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) + 1] = pixels[x];
|
||||
|
||||
@@ -115,13 +115,8 @@ int w_GlyphData_getFormat(lua_State *L)
|
||||
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 },
|
||||
{ "getHeight", w_GlyphData_getHeight },
|
||||
{ "getDimensions", w_GlyphData_getDimensions },
|
||||
@@ -136,7 +131,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -33,15 +33,6 @@ namespace font
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // font
|
||||
|
||||
@@ -124,7 +124,7 @@ int w_Rasterizer_hasGlyphs(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
const luaL_Reg w_Rasterizer_functions[] =
|
||||
{
|
||||
{ "getHeight", w_Rasterizer_getHeight },
|
||||
{ "getAdvance", w_Rasterizer_getAdvance },
|
||||
@@ -139,7 +139,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -31,14 +31,6 @@ namespace font
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // font
|
||||
|
||||
@@ -44,11 +44,26 @@ struct ColorT
|
||||
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*=(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>
|
||||
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[] =
|
||||
{
|
||||
{ "multicanvas", SUPPORT_MULTI_CANVAS },
|
||||
{ "multicanvasformats", SUPPORT_MULTI_CANVAS_FORMATS },
|
||||
{ "clampzero", SUPPORT_CLAMP_ZERO },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
|
||||
@@ -98,8 +98,8 @@ public:
|
||||
|
||||
enum Support
|
||||
{
|
||||
SUPPORT_MULTI_CANVAS,
|
||||
SUPPORT_MULTI_CANVAS_FORMATS,
|
||||
SUPPORT_CLAMP_ZERO,
|
||||
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();
|
||||
|
||||
// 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
|
||||
{
|
||||
|
||||
Quad::Quad(const Quad::Viewport &v, float sw, float sh)
|
||||
Quad::Quad(const Quad::Viewport &v, double sw, double sh)
|
||||
: sw(sw)
|
||||
, 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;
|
||||
|
||||
@@ -53,20 +53,20 @@ void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
|
||||
vertices[0].x = 0.0f;
|
||||
vertices[0].y = 0.0f;
|
||||
vertices[1].x = 0.0f;
|
||||
vertices[1].y = v.h;
|
||||
vertices[2].x = v.w;
|
||||
vertices[1].y = (float) v.h;
|
||||
vertices[2].x = (float) v.w;
|
||||
vertices[2].y = 0.0f;
|
||||
vertices[3].x = v.w;
|
||||
vertices[3].y = v.h;
|
||||
vertices[3].x = (float) v.w;
|
||||
vertices[3].y = (float) v.h;
|
||||
|
||||
vertices[0].s = v.x/sw;
|
||||
vertices[0].t = v.y/sh;
|
||||
vertices[1].s = v.x/sw;
|
||||
vertices[1].t = (v.y+v.h)/sh;
|
||||
vertices[2].s = (v.x+v.w)/sw;
|
||||
vertices[2].t = v.y/sh;
|
||||
vertices[3].s = (v.x+v.w)/sw;
|
||||
vertices[3].t = (v.y+v.h)/sh;
|
||||
vertices[0].s = (float) (v.x/sw);
|
||||
vertices[0].t = (float) (v.y/sh);
|
||||
vertices[1].s = (float) (v.x/sw);
|
||||
vertices[1].t = (float) ((v.y+v.h)/sh);
|
||||
vertices[2].s = (float) ((v.x+v.w)/sw);
|
||||
vertices[2].t = (float) (v.y/sh);
|
||||
vertices[3].s = (float) ((v.x+v.w)/sw);
|
||||
vertices[3].t = (float) ((v.y+v.h)/sh);
|
||||
}
|
||||
|
||||
void Quad::setViewport(const Quad::Viewport &v)
|
||||
|
||||
@@ -36,14 +36,14 @@ public:
|
||||
|
||||
struct Viewport
|
||||
{
|
||||
float x, y;
|
||||
float w, h;
|
||||
double x, y;
|
||||
double w, h;
|
||||
};
|
||||
|
||||
Quad(const Viewport &v, float sw, float sh);
|
||||
Quad(const Viewport &v, double sw, double sh);
|
||||
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);
|
||||
Viewport getViewport() const;
|
||||
|
||||
@@ -54,8 +54,8 @@ private:
|
||||
Vertex vertices[4];
|
||||
|
||||
Viewport viewport;
|
||||
float sw;
|
||||
float sh;
|
||||
double sw;
|
||||
double sh;
|
||||
|
||||
}; // Quad
|
||||
|
||||
|
||||
@@ -114,18 +114,19 @@ bool Texture::getConstant(WrapMode in, const char *&out)
|
||||
|
||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
||||
{
|
||||
{ "linear", Texture::FILTER_LINEAR },
|
||||
{ "nearest", Texture::FILTER_NEAREST },
|
||||
{ "none", Texture::FILTER_NONE },
|
||||
{ "linear", FILTER_LINEAR },
|
||||
{ "nearest", FILTER_NEAREST },
|
||||
{ "none", FILTER_NONE },
|
||||
};
|
||||
|
||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
|
||||
|
||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
|
||||
{
|
||||
{ "clamp", Texture::WRAP_CLAMP },
|
||||
{ "repeat", Texture::WRAP_REPEAT },
|
||||
{ "mirroredrepeat", Texture::WRAP_MIRRORED_REPEAT },
|
||||
{ "clamp", WRAP_CLAMP },
|
||||
{ "clampzero", WRAP_CLAMP_ZERO },
|
||||
{ "repeat", WRAP_REPEAT },
|
||||
{ "mirroredrepeat", WRAP_MIRRORED_REPEAT },
|
||||
};
|
||||
|
||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
enum WrapMode
|
||||
{
|
||||
WRAP_CLAMP,
|
||||
WRAP_CLAMP_ZERO,
|
||||
WRAP_REPEAT,
|
||||
WRAP_MIRRORED_REPEAT,
|
||||
WRAP_MAX_ENUM
|
||||
|
||||
@@ -361,6 +361,14 @@ bool Canvas::setWrap(const Texture::Wrap &w)
|
||||
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.setTextureWrap(wrap);
|
||||
|
||||
@@ -407,9 +415,6 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
||||
|
||||
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())
|
||||
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;
|
||||
}
|
||||
|
||||
bool Canvas::isMultiCanvasSupported()
|
||||
{
|
||||
// system must support at least 4 simultaneous active canvases.
|
||||
return gl.getMaxRenderTargets() >= 4;
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
@@ -55,13 +55,13 @@ public:
|
||||
FORMAT_RG8, // Two-channel (red and green) 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_RG11B10F, // Floating point [0, +inf]. RG with 11 FP bits each, and B with 10 FP bits.
|
||||
FORMAT_R16F, // Floating point [-inf, +inf]. R with 16 FP bits.
|
||||
FORMAT_RG16F, // Floating point [-inf, +inf]. RG with 16 FP bits per channel.
|
||||
FORMAT_RGBA16F, // Floating point [-inf, +inf]. RGBA with 16 FP bits per channel.
|
||||
FORMAT_R32F, // Floating point [-inf, +inf]. R with 32 FP bits.
|
||||
FORMAT_RG32F, // Floating point [-inf, +inf]. RG with 32 FP bits per channel.
|
||||
FORMAT_RGBA32F, // Floating point [-inf, +inf]. RGBA with 32 FP bits per channel.
|
||||
FORMAT_RG11B10F, // Floating point [0, +65024]. RG with 11 FP bits each, and B with 10 FP bits.
|
||||
FORMAT_R16F, // Floating point [-65504, +65504]. R with 16 FP bits.
|
||||
FORMAT_RG16F, // Floating point [-65504, +65504]. RG with 16 FP bits per channel.
|
||||
FORMAT_RGBA16F, // Floating point [-65504, +65504]. RGBA with 16 FP bits per channel.
|
||||
FORMAT_R32F, // Floating point [-65504, +65504]. R with 32 FP bits.
|
||||
FORMAT_RG32F, // Floating point [-65504, +65504]. RG 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_MAX_ENUM
|
||||
};
|
||||
@@ -118,7 +118,6 @@ public:
|
||||
}
|
||||
|
||||
static bool isSupported();
|
||||
static bool isMultiCanvasSupported();
|
||||
static bool isMultiFormatMultiCanvasSupported();
|
||||
static bool isFormatSupported(Format format);
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
static inline uint16 normToUint16(double n)
|
||||
{
|
||||
return (uint16) (n * LOVE_UINT16_MAX);
|
||||
}
|
||||
|
||||
int Font::fontCount = 0;
|
||||
|
||||
Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
||||
@@ -272,18 +277,20 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
|
||||
|
||||
g.texture = t;
|
||||
|
||||
float tX = (float) textureX, tY = (float) textureY;
|
||||
float tWidth = (float) textureWidth, tHeight = (float) textureHeight;
|
||||
double tX = (double) textureX, tY = (double) textureY;
|
||||
double tWidth = (double) textureWidth, tHeight = (double) textureHeight;
|
||||
|
||||
Color c(255, 255, 255, 255);
|
||||
|
||||
// 0----2
|
||||
// | / |
|
||||
// | / |
|
||||
// 1----3
|
||||
const GlyphVertex verts[4] = {
|
||||
{ 0.0f, 0.0f, tX/tWidth, tY/tHeight},
|
||||
{ 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
|
||||
{float(w), 0.0f, (tX+w)/tWidth, tY/tHeight},
|
||||
{float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight}
|
||||
{float(0), float(0), normToUint16((tX+0)/tWidth), normToUint16((tY+0)/tHeight), c},
|
||||
{float(0), float(h), normToUint16((tX+0)/tWidth), normToUint16((tY+h)/tHeight), c},
|
||||
{float(w), float(0), normToUint16((tX+w)/tWidth), normToUint16((tY+0)/tHeight), c},
|
||||
{float(w), float(h), normToUint16((tX+w)/tWidth), normToUint16((tY+h)/tHeight), c}
|
||||
};
|
||||
|
||||
// Copy vertex data to the glyph and set proper bearing.
|
||||
@@ -341,28 +348,9 @@ float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||
return k;
|
||||
}
|
||||
|
||||
float Font::getHeight() const
|
||||
void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints)
|
||||
{
|
||||
return (float) height;
|
||||
}
|
||||
|
||||
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;
|
||||
codepoints.reserve(text.size());
|
||||
|
||||
try
|
||||
{
|
||||
@@ -372,80 +360,152 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
|
||||
while (i != end)
|
||||
{
|
||||
uint32 g = *i++;
|
||||
|
||||
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;
|
||||
codepoints.push_back(g);
|
||||
}
|
||||
}
|
||||
catch (utf8::exception &e)
|
||||
{
|
||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Sort draw commands by texture first, and quad position in memory second
|
||||
// (using the struct's < operator).
|
||||
std::sort(drawcommands.begin(), drawcommands.end());
|
||||
void Font::getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
|
||||
{
|
||||
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)
|
||||
maxwidth = (int) dx;
|
||||
@@ -455,43 +515,44 @@ std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, s
|
||||
info->width = maxwidth - offset.x;;
|
||||
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)
|
||||
wrap = std::numeric_limits<float>::max();
|
||||
ColoredCodepoints codepoints;
|
||||
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;
|
||||
|
||||
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<std::string> lines;
|
||||
std::vector<ColoredCodepoints> lines;
|
||||
|
||||
// We only need the list of wrapped lines in 'justify' mode.
|
||||
getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
|
||||
getWrap(text, wrap, lines, &widths);
|
||||
|
||||
float extraspacing = 0.0f;
|
||||
int numspaces = 0;
|
||||
int i = 0;
|
||||
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];
|
||||
love::Vector offset(0.0f, floorf(y));
|
||||
float extraspacing = 0.0f;
|
||||
|
||||
if (width > maxwidth)
|
||||
maxwidth = width;
|
||||
maxwidth = std::max(width, maxwidth);
|
||||
|
||||
switch (align)
|
||||
{
|
||||
@@ -502,22 +563,24 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string
|
||||
offset.x = floorf((wrap - width) / 2.0f);
|
||||
break;
|
||||
case ALIGN_JUSTIFY:
|
||||
numspaces = (int) std::count(line.begin(), line.end(), ' ');
|
||||
if (wrappedlines[i] && numspaces >= 1)
|
||||
extraspacing = (wrap - width) / float(numspaces);
|
||||
{
|
||||
float numspaces = (float) std::count(line.cps.begin(), line.cps.end(), ' ');
|
||||
if (width < wrap && numspaces >= 1)
|
||||
extraspacing = (wrap - width) / numspaces;
|
||||
else
|
||||
extraspacing = 0.0f;
|
||||
break;
|
||||
}
|
||||
case ALIGN_LEFT:
|
||||
default:
|
||||
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
|
||||
// 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.
|
||||
drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
|
||||
drawcommands.insert(drawcommands.end(), firstcmd, newcommands.end());
|
||||
}
|
||||
|
||||
y += getHeight() * getLineHeight();
|
||||
i++;
|
||||
}
|
||||
|
||||
if (info != nullptr)
|
||||
@@ -597,30 +659,37 @@ void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands
|
||||
OpenGL::TempTransform transform(gl);
|
||||
transform.get() *= t;
|
||||
|
||||
gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
|
||||
ColoredCodepoints codepoints;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
|
||||
ColoredCodepoints codepoints;
|
||||
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);
|
||||
}
|
||||
@@ -636,15 +705,16 @@ int Font::getWidth(const std::string &str)
|
||||
while (getline(iss, line, '\n'))
|
||||
{
|
||||
int width = 0;
|
||||
uint32 prevglyph = 0;
|
||||
try
|
||||
{
|
||||
uint32 prevglyph = 0;
|
||||
|
||||
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());
|
||||
|
||||
while (i != end)
|
||||
{
|
||||
uint32 c = *i++;
|
||||
|
||||
const Glyph &g = findGlyph(c);
|
||||
width += g.spacing + getKerning(prevglyph, c);
|
||||
|
||||
@@ -668,69 +738,180 @@ int Font::getWidth(char character)
|
||||
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);
|
||||
std::string line;
|
||||
std::ostringstream string_builder;
|
||||
int lastspaceindex = -1;
|
||||
|
||||
// Split text at newlines.
|
||||
while (std::getline(iss, line, '\n'))
|
||||
// Keeping the indexed colors "in sync" is a bit tricky, since we split
|
||||
// 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;
|
||||
std::istringstream word_iss(line);
|
||||
uint32 c = codepoints.cps[i];
|
||||
|
||||
// split line into words
|
||||
std::copy(std::istream_iterator<std::string>(word_iss), std::istream_iterator<std::string>(), std::back_inserter(words));
|
||||
|
||||
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)
|
||||
// Determine the current color before doing anything else, to make sure
|
||||
// it's still applied to future glyphs even if this one is skipped.
|
||||
if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i)
|
||||
{
|
||||
float wordwidth = (float) getWidth(word);
|
||||
width += wordwidth;
|
||||
|
||||
// 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;
|
||||
curcolor = codepoints.colors[curcolori + 1].color;
|
||||
curcolori++;
|
||||
addcurcolor = true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
linewidths->push_back(width);
|
||||
linewidths->push_back(width - widthoftrailingspace);
|
||||
}
|
||||
}
|
||||
|
||||
std::string tmp = string_builder.str();
|
||||
lines.push_back(tmp.substr(0,tmp.size()-1));
|
||||
void Font::getWrap(const std::string &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||
{
|
||||
ColoredCodepoints codepoints;
|
||||
getCodepointsFromString(text, codepoints.cps);
|
||||
|
||||
// Indicate that this line was not automatically wrapped.
|
||||
if (wrappedlines)
|
||||
wrappedlines->push_back(false);
|
||||
std::vector<ColoredCodepoints> codepointlines;
|
||||
getWrap(codepoints, wraplimit, codepointlines, linewidths);
|
||||
|
||||
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[] =
|
||||
{
|
||||
{ "left", Font::ALIGN_LEFT },
|
||||
{ "right", Font::ALIGN_RIGHT },
|
||||
{ "center", Font::ALIGN_CENTER },
|
||||
{ "justify", Font::ALIGN_JUSTIFY },
|
||||
{ "left", ALIGN_LEFT },
|
||||
{ "right", ALIGN_RIGHT },
|
||||
{ "center", ALIGN_CENTER },
|
||||
{ "justify", ALIGN_JUSTIFY },
|
||||
};
|
||||
|
||||
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:
|
||||
|
||||
typedef std::vector<uint32> Codepoints;
|
||||
|
||||
enum AlignMode
|
||||
{
|
||||
ALIGN_LEFT,
|
||||
@@ -59,10 +61,29 @@ public:
|
||||
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
|
||||
{
|
||||
float x, y;
|
||||
float s, t;
|
||||
float x, y;
|
||||
uint16 s, t;
|
||||
Color color;
|
||||
};
|
||||
|
||||
struct TextInfo
|
||||
@@ -77,27 +98,22 @@ public:
|
||||
GLuint texture;
|
||||
int startvertex;
|
||||
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());
|
||||
|
||||
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> 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);
|
||||
|
||||
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.
|
||||
*
|
||||
@@ -112,9 +128,9 @@ public:
|
||||
* @param kx Shear along the x 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.
|
||||
@@ -140,13 +156,12 @@ public:
|
||||
* and optionally the number of lines
|
||||
*
|
||||
* @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 wrapped_lines Optional output indicating which lines were
|
||||
* auto-wrapped. Indices correspond to indices of the returned value.
|
||||
* 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,
|
||||
|
||||
@@ -90,6 +90,9 @@ void *GLBuffer::map()
|
||||
|
||||
void GLBuffer::unmapStatic(size_t offset, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
// Upload the mapped data to the buffer.
|
||||
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
|
||||
// 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);
|
||||
|
||||
size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
|
||||
|
||||
modified_size = new_range_end - modified_offset;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,11 @@ Graphics::~Graphics()
|
||||
Shader::defaultShader->release();
|
||||
Shader::defaultShader = nullptr;
|
||||
}
|
||||
if (Shader::defaultVideoShader)
|
||||
{
|
||||
Shader::defaultVideoShader->release();
|
||||
Shader::defaultVideoShader = nullptr;
|
||||
}
|
||||
|
||||
if (quadIndices)
|
||||
delete quadIndices;
|
||||
@@ -110,7 +115,7 @@ void Graphics::restoreState(const DisplayState &s)
|
||||
setPointSize(s.pointSize);
|
||||
|
||||
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
|
||||
setScissor();
|
||||
|
||||
@@ -131,11 +136,10 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
||||
{
|
||||
const DisplayState &cur = states.back();
|
||||
|
||||
if (*(uint32 *) &s.color.r != *(uint32 *) &cur.color.r)
|
||||
if (s.color != cur.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)
|
||||
setBlendMode(s.blendMode, s.blendMultiplyAlpha);
|
||||
@@ -148,10 +152,10 @@ void Graphics::restoreStateChecked(const DisplayState &s)
|
||||
if (s.pointSize != cur.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)
|
||||
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
|
||||
setScissor();
|
||||
}
|
||||
@@ -295,6 +299,10 @@ bool Graphics::setMode(int width, int height)
|
||||
|
||||
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
|
||||
// QuadIndices object is alive at all times while love.graphics is alive.
|
||||
// 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)
|
||||
quadIndices = new QuadIndices(20);
|
||||
|
||||
// Reload all volatile objects.
|
||||
if (!Volatile::loadAll())
|
||||
::printf("Could not reload all volatile objects.\n");
|
||||
|
||||
// Restore the graphics state.
|
||||
restoreState(states.back());
|
||||
|
||||
@@ -321,6 +325,13 @@ bool Graphics::setMode(int width, int height)
|
||||
Shader::defaultShader = newShader(Shader::defaultCode[renderer]);
|
||||
}
|
||||
|
||||
// and a default video shader.
|
||||
if (!Shader::defaultVideoShader)
|
||||
{
|
||||
Renderer renderer = GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
|
||||
Shader::defaultVideoShader = newShader(Shader::defaultVideoCode[renderer]);
|
||||
}
|
||||
|
||||
// A shader should always be active, but the default shader shouldn't be
|
||||
// returned by getShader(), so we don't do setShader(defaultShader).
|
||||
if (!Shader::current)
|
||||
@@ -581,14 +592,35 @@ bool Graphics::isCreated() const
|
||||
|
||||
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);
|
||||
// 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().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()
|
||||
@@ -601,10 +633,10 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const
|
||||
{
|
||||
const DisplayState &state = states.back();
|
||||
|
||||
x = state.scissorBox.x;
|
||||
y = state.scissorBox.y;
|
||||
width = state.scissorBox.w;
|
||||
height = state.scissorBox.h;
|
||||
x = state.scissorRect.x;
|
||||
y = state.scissorRect.y;
|
||||
width = state.scissorRect.w;
|
||||
height = state.scissorRect.h;
|
||||
|
||||
return state.scissor;
|
||||
}
|
||||
@@ -688,7 +720,7 @@ Image *Graphics::newImage(const std::vector<love::image::CompressedImageData *>
|
||||
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);
|
||||
}
|
||||
@@ -794,11 +826,16 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, con
|
||||
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);
|
||||
}
|
||||
|
||||
Video *Graphics::newVideo(love::video::VideoStream *stream)
|
||||
{
|
||||
return new Video(stream);
|
||||
}
|
||||
|
||||
bool Graphics::isGammaCorrect() const
|
||||
{
|
||||
return love::graphics::isGammaCorrect();
|
||||
@@ -1085,7 +1122,7 @@ bool Graphics::isWireframe() const
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
|
||||
if (mode == DRAW_LINE)
|
||||
{
|
||||
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);
|
||||
}
|
||||
// NOTE: We rely on polygon() using GL_TRIANGLE_FAN, when fill mode is used.
|
||||
polygon(mode, coords, num_coords);
|
||||
|
||||
delete[] coords;
|
||||
}
|
||||
@@ -1482,10 +1506,10 @@ bool Graphics::isSupported(Support feature) const
|
||||
{
|
||||
switch (feature)
|
||||
{
|
||||
case SUPPORT_MULTI_CANVAS:
|
||||
return Canvas::isMultiCanvasSupported();
|
||||
case SUPPORT_MULTI_CANVAS_FORMATS:
|
||||
return Canvas::isMultiFormatMultiCanvasSupported();
|
||||
case SUPPORT_CLAMP_ZERO:
|
||||
return gl.isClampZeroTextureWrapSupported();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
#include "window/Window.h"
|
||||
|
||||
#include "video/VideoStream.h"
|
||||
|
||||
#include "Font.h"
|
||||
#include "Image.h"
|
||||
#include "graphics/Quad.h"
|
||||
@@ -47,6 +49,7 @@
|
||||
#include "Shader.h"
|
||||
#include "Mesh.h"
|
||||
#include "Text.h"
|
||||
#include "Video.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -126,6 +129,8 @@ public:
|
||||
**/
|
||||
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.
|
||||
**/
|
||||
@@ -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::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.
|
||||
@@ -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, 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;
|
||||
|
||||
@@ -326,7 +333,7 @@ public:
|
||||
* @param kx Shear along the x-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.
|
||||
@@ -344,7 +351,7 @@ public:
|
||||
* @param kx Shear along the x-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).
|
||||
@@ -478,7 +485,7 @@ private:
|
||||
float pointSize = 1.0f;
|
||||
|
||||
bool scissor = false;
|
||||
OpenGL::Viewport scissorBox = OpenGL::Viewport();
|
||||
ScissorRect scissorRect = ScissorRect();
|
||||
|
||||
// Stencil.
|
||||
bool stencilTest = false;
|
||||
|
||||
@@ -356,7 +356,7 @@ bool Image::loadVolatile()
|
||||
|
||||
GLenum glerr = glGetError();
|
||||
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 &)
|
||||
{
|
||||
@@ -531,8 +531,16 @@ bool Image::setWrap(const Texture::Wrap &w)
|
||||
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.setTextureWrap(w);
|
||||
gl.setTextureWrap(wrap);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -589,112 +597,101 @@ bool Image::isCompressed() const
|
||||
|
||||
GLenum Image::getCompressedFormat(image::CompressedImageData::Format cformat, bool &isSRGB) const
|
||||
{
|
||||
using image::CompressedImageData;
|
||||
|
||||
switch (cformat)
|
||||
{
|
||||
case image::CompressedImageData::FORMAT_DXT1:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
case image::CompressedImageData::FORMAT_DXT3:
|
||||
if (isSRGB)
|
||||
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:
|
||||
case CompressedImageData::FORMAT_DXT1:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB_S3TC_DXT1_EXT : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
case CompressedImageData::FORMAT_DXT3:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
case CompressedImageData::FORMAT_DXT5:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
case CompressedImageData::FORMAT_BC4:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_RED_RGTC1;
|
||||
case image::CompressedImageData::FORMAT_BC4s:
|
||||
case CompressedImageData::FORMAT_BC4s:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_SIGNED_RED_RGTC1;
|
||||
case image::CompressedImageData::FORMAT_BC5:
|
||||
case CompressedImageData::FORMAT_BC5:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_RG_RGTC2;
|
||||
case image::CompressedImageData::FORMAT_BC5s:
|
||||
case CompressedImageData::FORMAT_BC5s:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
||||
case image::CompressedImageData::FORMAT_BC6H:
|
||||
case CompressedImageData::FORMAT_BC6H:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
|
||||
case image::CompressedImageData::FORMAT_BC6Hs:
|
||||
case CompressedImageData::FORMAT_BC6Hs:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
|
||||
case image::CompressedImageData::FORMAT_BC7:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
|
||||
else
|
||||
return GL_COMPRESSED_RGBA_BPTC_UNORM;
|
||||
case image::CompressedImageData::FORMAT_ETC1:
|
||||
case CompressedImageData::FORMAT_BC7:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM : GL_COMPRESSED_RGBA_BPTC_UNORM;
|
||||
case CompressedImageData::FORMAT_PVR1_RGB2:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||
case CompressedImageData::FORMAT_PVR1_RGB4:
|
||||
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.
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
|
||||
{
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB8_ETC2;
|
||||
else
|
||||
return GL_COMPRESSED_RGB8_ETC2;
|
||||
}
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
|
||||
else
|
||||
{
|
||||
isSRGB = false;
|
||||
return GL_ETC1_RGB8_OES;
|
||||
}
|
||||
case image::CompressedImageData::FORMAT_ETC2_RGB:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB8_ETC2;
|
||||
else
|
||||
return GL_COMPRESSED_RGB8_ETC2;
|
||||
case image::CompressedImageData::FORMAT_ETC2_RGBA:
|
||||
if (isSRGB)
|
||||
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:
|
||||
case CompressedImageData::FORMAT_ETC2_RGB:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ETC2 : GL_COMPRESSED_RGB8_ETC2;
|
||||
case CompressedImageData::FORMAT_ETC2_RGBA:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : GL_COMPRESSED_RGBA8_ETC2_EAC;
|
||||
case CompressedImageData::FORMAT_ETC2_RGBA1:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
|
||||
case CompressedImageData::FORMAT_EAC_R:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_R11_EAC;
|
||||
case image::CompressedImageData::FORMAT_EAC_Rs:
|
||||
case CompressedImageData::FORMAT_EAC_Rs:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_SIGNED_R11_EAC;
|
||||
case image::CompressedImageData::FORMAT_EAC_RG:
|
||||
case CompressedImageData::FORMAT_EAC_RG:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_RG11_EAC;
|
||||
case image::CompressedImageData::FORMAT_EAC_RGs:
|
||||
case CompressedImageData::FORMAT_EAC_RGs:
|
||||
isSRGB = false;
|
||||
return GL_COMPRESSED_SIGNED_RG11_EAC;
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGB2:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGB4:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGBA2:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGBA4:
|
||||
if (isSRGB)
|
||||
return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||
case CompressedImageData::FORMAT_ASTC_4x4:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_5x4:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_5x5:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_6x5:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_6x6:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_8x5:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_8x6:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_8x8:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_10x5:
|
||||
return isSRGB ? GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
|
||||
case CompressedImageData::FORMAT_ASTC_10x6:
|
||||
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:
|
||||
if (isSRGB)
|
||||
return GL_SRGB8_ALPHA8;
|
||||
else
|
||||
return GL_RGBA8;
|
||||
return isSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,42 +702,56 @@ bool Image::hasAnisotropicFilteringSupport()
|
||||
|
||||
bool Image::hasCompressedTextureSupport(image::CompressedImageData::Format format, bool sRGB)
|
||||
{
|
||||
using image::CompressedImageData;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case image::CompressedImageData::FORMAT_DXT1:
|
||||
case CompressedImageData::FORMAT_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;
|
||||
case image::CompressedImageData::FORMAT_DXT5:
|
||||
case CompressedImageData::FORMAT_DXT5:
|
||||
return GLAD_EXT_texture_compression_s3tc || GLAD_ANGLE_texture_compression_dxt5;
|
||||
case image::CompressedImageData::FORMAT_BC4:
|
||||
case image::CompressedImageData::FORMAT_BC4s:
|
||||
case image::CompressedImageData::FORMAT_BC5:
|
||||
case image::CompressedImageData::FORMAT_BC5s:
|
||||
case CompressedImageData::FORMAT_BC4:
|
||||
case CompressedImageData::FORMAT_BC4s:
|
||||
case CompressedImageData::FORMAT_BC5:
|
||||
case CompressedImageData::FORMAT_BC5s:
|
||||
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
|
||||
case image::CompressedImageData::FORMAT_BC6H:
|
||||
case image::CompressedImageData::FORMAT_BC6Hs:
|
||||
case image::CompressedImageData::FORMAT_BC7:
|
||||
case CompressedImageData::FORMAT_BC6H:
|
||||
case CompressedImageData::FORMAT_BC6Hs:
|
||||
case CompressedImageData::FORMAT_BC7:
|
||||
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.
|
||||
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 image::CompressedImageData::FORMAT_ETC2_RGBA:
|
||||
case image::CompressedImageData::FORMAT_ETC2_RGBA1:
|
||||
case image::CompressedImageData::FORMAT_EAC_R:
|
||||
case image::CompressedImageData::FORMAT_EAC_Rs:
|
||||
case image::CompressedImageData::FORMAT_EAC_RG:
|
||||
case image::CompressedImageData::FORMAT_EAC_RGs:
|
||||
case CompressedImageData::FORMAT_ETC2_RGB:
|
||||
case CompressedImageData::FORMAT_ETC2_RGBA:
|
||||
case CompressedImageData::FORMAT_ETC2_RGBA1:
|
||||
case CompressedImageData::FORMAT_EAC_R:
|
||||
case CompressedImageData::FORMAT_EAC_Rs:
|
||||
case CompressedImageData::FORMAT_EAC_RG:
|
||||
case CompressedImageData::FORMAT_EAC_RGs:
|
||||
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility;
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGB2:
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGB4:
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGBA2:
|
||||
case image::CompressedImageData::FORMAT_PVR1_RGBA4:
|
||||
if (sRGB)
|
||||
return GLAD_EXT_pvrtc_sRGB;
|
||||
else
|
||||
return GLAD_IMG_texture_compression_pvrtc;
|
||||
case CompressedImageData::FORMAT_ASTC_4x4:
|
||||
case CompressedImageData::FORMAT_ASTC_5x4:
|
||||
case CompressedImageData::FORMAT_ASTC_5x5:
|
||||
case CompressedImageData::FORMAT_ASTC_6x5:
|
||||
case CompressedImageData::FORMAT_ASTC_6x6:
|
||||
case CompressedImageData::FORMAT_ASTC_8x5:
|
||||
case CompressedImageData::FORMAT_ASTC_8x6:
|
||||
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:
|
||||
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[] =
|
||||
{
|
||||
{"mipmaps", Image::FLAG_TYPE_MIPMAPS},
|
||||
{"linear", Image::FLAG_TYPE_LINEAR},
|
||||
{"mipmaps", FLAG_TYPE_MIPMAPS},
|
||||
{"linear", FLAG_TYPE_LINEAR},
|
||||
};
|
||||
|
||||
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)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(0)
|
||||
, drawMode(drawmode)
|
||||
@@ -90,6 +91,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
|
||||
, vertexCount((size_t) vertexcount)
|
||||
, vertexStride(0)
|
||||
, ibo(nullptr)
|
||||
, useIndexBuffer(false)
|
||||
, elementCount(0)
|
||||
, elementDataType(getGLDataTypeFromMax(vertexcount))
|
||||
, drawMode(drawmode)
|
||||
@@ -147,7 +149,7 @@ void Mesh::setupAttachedAttributes()
|
||||
if (attachedAttributes.find(name) != attachedAttributes.end())
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto it = attachedAttributes.find(name);
|
||||
@@ -326,20 +339,10 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh)
|
||||
oldattrib = it->second;
|
||||
|
||||
newattrib.mesh = mesh;
|
||||
newattrib.index = std::numeric_limits<size_t>::max();
|
||||
newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
|
||||
newattrib.index = mesh->getAttributeIndex(name);
|
||||
|
||||
// Find the index of the attribute in the mesh.
|
||||
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())
|
||||
if (newattrib.index < 0)
|
||||
throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str());
|
||||
|
||||
if (newattrib.mesh != this)
|
||||
@@ -357,11 +360,10 @@ void *Mesh::mapVertexData()
|
||||
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);
|
||||
vbo->setMappedRangeModified(0, vbo->getSize());
|
||||
vbo->setMappedRangeModified(modifiedoffset, modifiedsize);
|
||||
vbo->unmap();
|
||||
}
|
||||
|
||||
@@ -414,6 +416,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
if (!ibo && size > 0)
|
||||
ibo = new GLBuffer(size, nullptr, GL_ELEMENT_ARRAY_BUFFER, vbo->getUsage());
|
||||
|
||||
useIndexBuffer = true;
|
||||
elementCount = map.size();
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
@@ -437,6 +440,11 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
elementDataType = datatype;
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap()
|
||||
{
|
||||
useIndexBuffer = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
|
||||
void Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
bool Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
{
|
||||
if (!ibo || elementCount == 0)
|
||||
return;
|
||||
if (!useIndexBuffer)
|
||||
return false;
|
||||
|
||||
map.clear();
|
||||
map.reserve(elementCount);
|
||||
|
||||
if (!ibo || elementCount == 0)
|
||||
return true;
|
||||
|
||||
GLBuffer::Bind ibobind(*ibo);
|
||||
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
@@ -524,6 +537,39 @@ void Mesh::getDrawRange(int &min, int &max) const
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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 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);
|
||||
if (location >= 0)
|
||||
enabledattribs |= 1u << (uint32) location;
|
||||
}
|
||||
|
||||
// Not supported on all platforms or GL versions, I believe.
|
||||
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.");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (ibo && elementCount > 0)
|
||||
if (useIndexBuffer && ibo && elementCount > 0)
|
||||
{
|
||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||
GLBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
@@ -126,6 +126,7 @@ public:
|
||||
**/
|
||||
const std::vector<AttribFormat> &getVertexFormat() 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.
|
||||
@@ -140,7 +141,7 @@ public:
|
||||
void attachAttribute(const std::string &name, Mesh *mesh);
|
||||
|
||||
void *mapVertexData();
|
||||
void unmapVertexData();
|
||||
void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1);
|
||||
|
||||
/**
|
||||
* Flushes all modified data to the GPU.
|
||||
@@ -154,12 +155,13 @@ public:
|
||||
* {0, 1, 2, 3, 4, ...}
|
||||
**/
|
||||
void setVertexMap(const std::vector<uint32> &map);
|
||||
void setVertexMap();
|
||||
|
||||
/**
|
||||
* Fills the uint32 vector passed into the method with the previously set
|
||||
* 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.
|
||||
@@ -192,6 +194,8 @@ public:
|
||||
void setDrawRange();
|
||||
void getDrawRange(int &min, int &max) const;
|
||||
|
||||
int bindAttributeToShaderInput(int attributeindex, const std::string &inputname);
|
||||
|
||||
// Implements Drawable.
|
||||
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
|
||||
{
|
||||
Mesh *mesh;
|
||||
size_t index;
|
||||
int index;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
@@ -242,6 +246,7 @@ private:
|
||||
|
||||
// Element (vertex index) buffer, for the vertex map.
|
||||
GLBuffer *ibo;
|
||||
bool useIndexBuffer;
|
||||
size_t elementCount;
|
||||
GLenum elementDataType;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
// C
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
// For SDL_GL_GetProcAddress.
|
||||
#include <SDL_video.h>
|
||||
@@ -601,24 +602,32 @@ void OpenGL::setTextureFilter(graphics::Texture::Filter &f)
|
||||
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)
|
||||
{
|
||||
auto glWrapMode = [](Texture::WrapMode wmode) -> GLint
|
||||
{
|
||||
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, getGLWrapMode(w.s));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGLWrapMode(w.t));
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapMode(w.s));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapMode(w.t));
|
||||
bool OpenGL::isClampZeroTextureWrapSupported() const
|
||||
{
|
||||
return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxTextureSize() const
|
||||
@@ -643,7 +652,7 @@ int OpenGL::getMaxTextureUnits() const
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -652,6 +661,36 @@ OpenGL::Vendor OpenGL::getVendor() const
|
||||
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)
|
||||
{
|
||||
switch (severity)
|
||||
|
||||
@@ -143,7 +143,7 @@ public:
|
||||
{
|
||||
public:
|
||||
|
||||
#if defined(DEBUG) && DEBUG == 1
|
||||
#if defined(LOVE_IOS)
|
||||
TempDebugGroup(const char *name)
|
||||
{
|
||||
if (GLAD_EXT_debug_marker)
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
TempDebugGroup(const char *) {}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) && DEBUG == 1
|
||||
#if defined(LOVE_IOS)
|
||||
~TempDebugGroup()
|
||||
{
|
||||
if (GLAD_EXT_debug_marker)
|
||||
@@ -312,6 +312,8 @@ public:
|
||||
**/
|
||||
void setTextureWrap(const graphics::Texture::Wrap &w);
|
||||
|
||||
bool isClampZeroTextureWrapSupported() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported width or height of a texture.
|
||||
**/
|
||||
@@ -339,6 +341,8 @@ public:
|
||||
**/
|
||||
Vendor getVendor() const;
|
||||
|
||||
static const char *errorString(GLenum errorcode);
|
||||
|
||||
// Get human-readable strings for debug info.
|
||||
static const char *debugSeverityString(GLenum severity);
|
||||
static const char *debugSourceString(GLenum source);
|
||||
@@ -352,6 +356,8 @@ private:
|
||||
void initMatrices();
|
||||
void createDefaultTexture();
|
||||
|
||||
GLint getGLWrapMode(Texture::WrapMode wmode);
|
||||
|
||||
bool contextInitialized;
|
||||
|
||||
float maxAnisotropy;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,18 +22,9 @@
|
||||
#define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
|
||||
|
||||
// LOVE
|
||||
#include "common/int.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 "graphics/ParticleSystem.h"
|
||||
#include "GLBuffer.h"
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -41,643 +32,28 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
/**
|
||||
* A class for creating, moving and drawing particles.
|
||||
* A big thanks to bobthebloke.org
|
||||
**/
|
||||
class ParticleSystem : public Drawable
|
||||
class ParticleSystem : public love::graphics::ParticleSystem
|
||||
{
|
||||
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.
|
||||
**/
|
||||
ParticleSystem *clone();
|
||||
ParticleSystem *clone() override;
|
||||
void setBufferSize(uint32 size) override;
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) override;
|
||||
|
||||
/**
|
||||
* Sets the texture used in the particle system.
|
||||
* @param texture The new texture.
|
||||
**/
|
||||
void setTexture(Texture *texture);
|
||||
private:
|
||||
|
||||
/**
|
||||
* 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;
|
||||
void createVertices(size_t numparticles);
|
||||
|
||||
// array of transformed vertex data for all particles, for drawing
|
||||
Vertex *particleVerts;
|
||||
|
||||
// Vertex index buffer.
|
||||
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
|
||||
|
||||
@@ -64,8 +64,10 @@ namespace
|
||||
|
||||
Shader *Shader::current = nullptr;
|
||||
Shader *Shader::defaultShader = nullptr;
|
||||
Shader *Shader::defaultVideoShader = nullptr;
|
||||
|
||||
Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
std::vector<int> Shader::textureCounters;
|
||||
|
||||
@@ -77,6 +79,7 @@ Shader::Shader(const ShaderSource &source)
|
||||
, lastCanvas((Canvas *) -1)
|
||||
, lastViewport()
|
||||
, lastPointSize(0.0f)
|
||||
, videoTextureUnits()
|
||||
{
|
||||
if (source.vertex.empty() && source.pixel.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
@@ -225,6 +228,9 @@ bool Shader::loadVolatile()
|
||||
lastProjectionMatrix.setTranslation(nan, nan);
|
||||
lastTransformMatrix.setTranslation(nan, nan);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
videoTextureUnits[i] = 0;
|
||||
|
||||
// zero out active texture list
|
||||
activeTexUnits.clear();
|
||||
activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0);
|
||||
@@ -635,6 +641,57 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
|
||||
return builtinAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture)
|
||||
{
|
||||
TemporaryAttacher attacher(this);
|
||||
|
||||
// Set up the texture units that will be used by the shader to sample from
|
||||
// the textures, if they haven't been set up yet.
|
||||
if (videoTextureUnits[0] == 0)
|
||||
{
|
||||
const GLint locs[3] = {
|
||||
builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL],
|
||||
builtinUniforms[BUILTIN_VIDEO_CR_CHANNEL]
|
||||
};
|
||||
|
||||
const char *names[3] = {nullptr, nullptr, nullptr};
|
||||
builtinNames.find(BUILTIN_VIDEO_Y_CHANNEL, names[0]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CB_CHANNEL, names[1]);
|
||||
builtinNames.find(BUILTIN_VIDEO_CR_CHANNEL, names[2]);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (locs[i] >= 0 && names[i] != nullptr)
|
||||
{
|
||||
videoTextureUnits[i] = getTextureUnit(names[i]);
|
||||
|
||||
// Increment global shader texture id counter for this texture
|
||||
// unit, if we haven't already.
|
||||
if (activeTexUnits[videoTextureUnits[i] - 1] == 0)
|
||||
++textureCounters[videoTextureUnits[i] - 1];
|
||||
|
||||
glUniform1i(locs[i], videoTextureUnits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const GLuint textures[3] = {ytexture, cbtexture, crtexture};
|
||||
|
||||
// Bind the textures to their respective texture units.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (videoTextureUnits[i] != 0)
|
||||
{
|
||||
// Store texture id so it can be re-bound later.
|
||||
activeTexUnits[videoTextureUnits[i] - 1] = textures[i];
|
||||
gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
gl.setTextureUnit(0);
|
||||
}
|
||||
|
||||
void Shader::checkSetScreenParams()
|
||||
{
|
||||
OpenGL::Viewport view = gl.getViewport();
|
||||
@@ -898,6 +955,9 @@ StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM>::Entry Shader::built
|
||||
{"NormalMatrix", Shader::BUILTIN_NORMAL_MATRIX},
|
||||
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
|
||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
|
||||
{"love_VideoYChannel", Shader::BUILTIN_VIDEO_Y_CHANNEL},
|
||||
{"love_VideoCbChannel", Shader::BUILTIN_VIDEO_CB_CHANNEL},
|
||||
{"love_VideoCrChannel", Shader::BUILTIN_VIDEO_CR_CHANNEL},
|
||||
};
|
||||
|
||||
StringMap<Shader::BuiltinUniform, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||
|
||||
@@ -64,6 +64,9 @@ public:
|
||||
BUILTIN_NORMAL_MATRIX,
|
||||
BUILTIN_POINT_SIZE,
|
||||
BUILTIN_SCREEN_SIZE,
|
||||
BUILTIN_VIDEO_Y_CHANNEL,
|
||||
BUILTIN_VIDEO_CB_CHANNEL,
|
||||
BUILTIN_VIDEO_CR_CHANNEL,
|
||||
BUILTIN_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -89,9 +92,11 @@ public:
|
||||
|
||||
// Pointer to the default Shader.
|
||||
static Shader *defaultShader;
|
||||
static Shader *defaultVideoShader;
|
||||
|
||||
// Default shader code (a shader is always required internally.)
|
||||
static ShaderSource defaultCode[Graphics::RENDERER_MAX_ENUM];
|
||||
static ShaderSource defaultVideoCode[Graphics::RENDERER_MAX_ENUM];
|
||||
|
||||
/**
|
||||
* Creates a new Shader using a list of source codes.
|
||||
@@ -182,6 +187,7 @@ public:
|
||||
**/
|
||||
bool hasVertexAttrib(VertexAttribID attrib) const;
|
||||
|
||||
void setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture);
|
||||
void checkSetScreenParams();
|
||||
void checkSetPointSize(float size);
|
||||
void checkSetBuiltinUniforms();
|
||||
@@ -263,6 +269,8 @@ private:
|
||||
Matrix4 lastTransformMatrix;
|
||||
Matrix4 lastProjectionMatrix;
|
||||
|
||||
GLuint videoTextureUnits[3];
|
||||
|
||||
// Counts total number of textures bound to each texture unit in all shaders
|
||||
static std::vector<int> textureCounters;
|
||||
|
||||
|
||||
@@ -53,23 +53,9 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, Mesh::Usage usage)
|
||||
throw love::Exception("Invalid SpriteBatch size.");
|
||||
|
||||
GLenum gl_usage = Mesh::getGLBufferUsage(usage);
|
||||
size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||
|
||||
const size_t vertex_size = sizeof(Vertex) * 4 * size;
|
||||
|
||||
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.");
|
||||
}
|
||||
array_buf = new GLBuffer(vertex_size, nullptr, GL_ARRAY_BUFFER, gl_usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
|
||||
}
|
||||
|
||||
SpriteBatch::~SpriteBatch()
|
||||
@@ -207,6 +193,28 @@ int SpriteBatch::getBufferSize() const
|
||||
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)
|
||||
{
|
||||
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());
|
||||
|
||||
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;
|
||||
|
||||
// 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));
|
||||
// Scope this bind so it doesn't interfere with the
|
||||
// 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));
|
||||
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
|
||||
for (const auto &it : attached_attributes)
|
||||
{
|
||||
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.prepareDraw();
|
||||
|
||||
GLBuffer::Bind element_bind(*quad_indices.getBuffer());
|
||||
gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
// C
|
||||
#include <cstring>
|
||||
|
||||
// C++
|
||||
#include <unordered_map>
|
||||
|
||||
// LOVE
|
||||
#include "common/math.h"
|
||||
#include "common/Matrix.h"
|
||||
@@ -98,11 +101,23 @@ public:
|
||||
**/
|
||||
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.
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
private:
|
||||
|
||||
struct AttachedAttribute
|
||||
{
|
||||
StrongRef<Mesh> mesh;
|
||||
int index;
|
||||
};
|
||||
|
||||
void addv(const Vertex *v, const Matrix3 &m, int index);
|
||||
|
||||
/**
|
||||
@@ -129,6 +144,8 @@ private:
|
||||
GLBuffer *array_buf;
|
||||
QuadIndices quad_indices;
|
||||
|
||||
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
|
||||
|
||||
}; // SpriteBatch
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -30,10 +30,9 @@ namespace graphics
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
Text::Text(Font *font, const std::string &text)
|
||||
Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
|
||||
: font(font)
|
||||
, vbo(nullptr)
|
||||
, text_info()
|
||||
, vert_offset(0)
|
||||
, texture_cache_id((uint32) -1)
|
||||
{
|
||||
@@ -82,7 +81,7 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
|
||||
vbo = new_vbo;
|
||||
}
|
||||
|
||||
if (vbo != nullptr)
|
||||
if (vbo != nullptr && datasize > 0)
|
||||
{
|
||||
GLBuffer::Bind bind(*vbo);
|
||||
vbodata = (uint8 *) vbo->map();
|
||||
@@ -113,11 +112,13 @@ void Text::addTextData(const TextData &t)
|
||||
std::vector<Font::GlyphVertex> vertices;
|
||||
std::vector<Font::DrawCommand> new_commands;
|
||||
|
||||
Font::TextInfo text_info;
|
||||
|
||||
// We only have formatted text if the align mode is valid.
|
||||
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
|
||||
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)
|
||||
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();
|
||||
|
||||
text_data.push_back(t);
|
||||
text_data.back().text_info = text_info;
|
||||
|
||||
// Font::generateVertices can invalidate the font's texture cache.
|
||||
if (font->getTextureCacheID() != texture_cache_id)
|
||||
regenerateVertices();
|
||||
}
|
||||
|
||||
void Text::set(const std::string &text)
|
||||
void Text::set(const std::vector<Font::ColoredString> &text)
|
||||
{
|
||||
if (text.empty())
|
||||
return set();
|
||||
|
||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, false, false, Matrix3()});
|
||||
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
addTextData({text, wrap, align, false, false, Matrix3()});
|
||||
Font::ColoredCodepoints codepoints;
|
||||
Font::getCodepointsFromString(text, codepoints);
|
||||
|
||||
addTextData({codepoints, wrap, align, {}, false, false, Matrix3()});
|
||||
}
|
||||
|
||||
void Text::set()
|
||||
@@ -186,24 +189,21 @@ void Text::set()
|
||||
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;
|
||||
|
||||
Matrix3 m(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
addTextData({text, -1.0f, Font::ALIGN_MAX_ENUM, true, true, m});
|
||||
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
}
|
||||
|
||||
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())
|
||||
return;
|
||||
Font::ColoredCodepoints codepoints;
|
||||
Font::getCodepointsFromString(text, codepoints);
|
||||
|
||||
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()
|
||||
@@ -211,7 +211,6 @@ void Text::clear()
|
||||
text_data.clear();
|
||||
draw_commands.clear();
|
||||
texture_cache_id = font->getTextureCacheID();
|
||||
text_info = {};
|
||||
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)
|
||||
regenerateVertices();
|
||||
|
||||
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
||||
const size_t tex_offset = offsetof(Font::GlyphVertex, s);
|
||||
const size_t pos_offset = offsetof(Font::GlyphVertex, x);
|
||||
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);
|
||||
|
||||
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);
|
||||
@@ -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.
|
||||
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);
|
||||
}
|
||||
@@ -262,14 +263,26 @@ Font *Text::getFont() const
|
||||
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
|
||||
|
||||
@@ -38,15 +38,15 @@ class Text : public Drawable
|
||||
{
|
||||
public:
|
||||
|
||||
Text(Font *font, const std::string &text = "");
|
||||
Text(Font *font, const std::vector<Font::ColoredString> &text = {});
|
||||
virtual ~Text();
|
||||
|
||||
void set(const std::string &text);
|
||||
void set(const std::string &text, float wrap, Font::AlignMode align);
|
||||
void set(const std::vector<Font::ColoredString> &text);
|
||||
void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
|
||||
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);
|
||||
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 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);
|
||||
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();
|
||||
|
||||
// Implements Drawable.
|
||||
@@ -58,20 +58,21 @@ public:
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
int getHeight() const;
|
||||
int getHeight(int index = 0) const;
|
||||
|
||||
private:
|
||||
|
||||
struct TextData
|
||||
{
|
||||
std::string text;
|
||||
Font::ColoredCodepoints codepoints;
|
||||
float wrap;
|
||||
Font::AlignMode align;
|
||||
Font::TextInfo text_info;
|
||||
bool use_matrix;
|
||||
bool append_vertices;
|
||||
Matrix3 matrix;
|
||||
@@ -87,7 +88,6 @@ private:
|
||||
std::vector<Font::DrawCommand> draw_commands;
|
||||
|
||||
std::vector<TextData> text_data;
|
||||
Font::TextInfo text_info;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 },
|
||||
{ "newImageData", w_Canvas_newImageData },
|
||||
{ "getFormat", w_Canvas_getFormat },
|
||||
@@ -121,7 +112,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -35,10 +35,6 @@ namespace opengl
|
||||
|
||||
//see Canvas.h
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -184,7 +184,7 @@ int w_Font_setFallbacks(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
static const luaL_Reg w_Font_functions[] =
|
||||
{
|
||||
{ "getHeight", w_Font_getHeight },
|
||||
{ "getWidth", w_Font_getWidth },
|
||||
@@ -203,7 +203,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -33,18 +33,6 @@ namespace opengl
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "image/Image.h"
|
||||
#include "font/Rasterizer.h"
|
||||
#include "filesystem/wrap_Filesystem.h"
|
||||
#include "video/VideoStream.h"
|
||||
#include "image/wrap_Image.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -184,6 +185,20 @@ int w_setScissor(lua_State *L)
|
||||
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 x, y, w, h;
|
||||
@@ -354,13 +369,13 @@ int w_newImage(lua_State *L)
|
||||
int w_newQuad(lua_State *L)
|
||||
{
|
||||
Quad::Viewport v;
|
||||
v.x = (float) luaL_checknumber(L, 1);
|
||||
v.y = (float) luaL_checknumber(L, 2);
|
||||
v.w = (float) luaL_checknumber(L, 3);
|
||||
v.h = (float) luaL_checknumber(L, 4);
|
||||
v.x = luaL_checknumber(L, 1);
|
||||
v.y = luaL_checknumber(L, 2);
|
||||
v.w = luaL_checknumber(L, 3);
|
||||
v.h = luaL_checknumber(L, 4);
|
||||
|
||||
float sw = (float) luaL_checknumber(L, 5);
|
||||
float sh = (float) luaL_checknumber(L, 6);
|
||||
double sw = luaL_checknumber(L, 5);
|
||||
double sh = luaL_checknumber(L, 6);
|
||||
|
||||
Quad *quad = instance()->newQuad(v, sw, sh);
|
||||
luax_pushtype(L, GRAPHICS_QUAD_ID, quad);
|
||||
@@ -837,7 +852,9 @@ int w_newText(lua_State *L)
|
||||
luax_catchexcept(L, [&](){ t = instance()->newText(font); });
|
||||
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); });
|
||||
}
|
||||
|
||||
@@ -846,6 +863,20 @@ int w_newText(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newVideo(lua_State *L)
|
||||
{
|
||||
if (!luax_istype(L, 1, VIDEO_VIDEO_STREAM_ID))
|
||||
luax_convobj(L, 1, "video", "newVideoStream");
|
||||
|
||||
auto stream = luax_checktype<love::video::VideoStream>(L, 1, VIDEO_VIDEO_STREAM_ID);
|
||||
Video *video = nullptr;
|
||||
|
||||
luax_catchexcept(L, [&]() { video = instance()->newVideo(stream); });
|
||||
luax_pushtype(L, GRAPHICS_VIDEO_ID, video);
|
||||
video->release();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setColor(lua_State *L)
|
||||
{
|
||||
Colorf c;
|
||||
@@ -1258,25 +1289,37 @@ int w_setDefaultShaderCode(lua_State *L)
|
||||
lua_getfield(L, 1, "opengl");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglcode;
|
||||
openglcode.vertex = luax_checkstring(L, -2);
|
||||
openglcode.pixel = luax_checkstring(L, -1);
|
||||
openglcode.vertex = luax_checkstring(L, -3);
|
||||
openglcode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglVideocode;
|
||||
openglVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
lua_getfield(L, 1, "opengles");
|
||||
lua_rawgeti(L, -1, 1);
|
||||
lua_rawgeti(L, -2, 2);
|
||||
lua_rawgeti(L, -3, 3);
|
||||
|
||||
Shader::ShaderSource openglescode;
|
||||
openglescode.vertex = luax_checkstring(L, -2);
|
||||
openglescode.pixel = luax_checkstring(L, -1);
|
||||
openglescode.vertex = luax_checkstring(L, -3);
|
||||
openglescode.pixel = luax_checkstring(L, -2);
|
||||
|
||||
lua_pop(L, 3);
|
||||
Shader::ShaderSource openglesVideocode;
|
||||
openglesVideocode.vertex = luax_checkstring(L, -3);
|
||||
openglesVideocode.pixel = luax_checkstring(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGL] = openglcode;
|
||||
Shader::defaultCode[Graphics::RENDERER_OPENGLES] = openglescode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGL] = openglVideocode;
|
||||
Shader::defaultVideoCode[Graphics::RENDERER_OPENGLES] = openglesVideocode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1452,7 +1495,9 @@ int w_draw(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 y = (float)luaL_optnumber(L, 3, 0.0);
|
||||
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)
|
||||
{
|
||||
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 y = (float)luaL_checknumber(L, 3);
|
||||
float wrap = (float)luaL_checknumber(L, 4);
|
||||
@@ -1843,6 +1890,7 @@ static const luaL_Reg functions[] =
|
||||
{ "newShader", w_newShader },
|
||||
{ "newMesh", w_newMesh },
|
||||
{ "newText", w_newText },
|
||||
{ "_newVideo", w_newVideo },
|
||||
|
||||
{ "setColor", w_setColor },
|
||||
{ "getColor", w_getColor },
|
||||
@@ -1899,6 +1947,7 @@ static const luaL_Reg functions[] =
|
||||
{ "getDimensions", w_getDimensions },
|
||||
|
||||
{ "setScissor", w_setScissor },
|
||||
{ "intersectScissor", w_intersectScissor },
|
||||
{ "getScissor", w_getScissor },
|
||||
|
||||
{ "stencil", w_stencil },
|
||||
@@ -1925,9 +1974,16 @@ static const luaL_Reg functions[] =
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static int luaopen_drawable(lua_State *L)
|
||||
{
|
||||
return luax_register_type(L, GRAPHICS_DRAWABLE_ID, "Drawable", nullptr);
|
||||
}
|
||||
|
||||
// Types for this module.
|
||||
static const lua_CFunction types[] =
|
||||
{
|
||||
luaopen_drawable,
|
||||
luaopen_texture,
|
||||
luaopen_font,
|
||||
luaopen_image,
|
||||
luaopen_quad,
|
||||
@@ -1937,6 +1993,7 @@ static const lua_CFunction types[] =
|
||||
luaopen_shader,
|
||||
luaopen_mesh,
|
||||
luaopen_text,
|
||||
luaopen_video,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "wrap_Shader.h"
|
||||
#include "wrap_Mesh.h"
|
||||
#include "wrap_Text.h"
|
||||
#include "wrap_Video.h"
|
||||
#include "Graphics.h"
|
||||
|
||||
namespace love
|
||||
@@ -41,85 +42,6 @@ namespace graphics
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -159,12 +159,34 @@ precision mediump float;
|
||||
#endif
|
||||
|
||||
varying mediump vec4 VaryingTexCoord;
|
||||
varying lowp vec4 VaryingColor;
|
||||
varying mediump vec4 VaryingColor;
|
||||
|
||||
#define love_Canvases gl_FragData
|
||||
|
||||
uniform sampler2D _tex0_;]],
|
||||
|
||||
FUNCTIONS = [[
|
||||
uniform sampler2D love_VideoYChannel;
|
||||
uniform sampler2D love_VideoCbChannel;
|
||||
uniform sampler2D love_VideoCrChannel;
|
||||
|
||||
vec4 VideoTexel(vec2 texcoords)
|
||||
{
|
||||
vec3 yuv;
|
||||
yuv[0] = Texel(love_VideoYChannel, texcoords).r;
|
||||
yuv[1] = Texel(love_VideoCbChannel, texcoords).r;
|
||||
yuv[2] = Texel(love_VideoCrChannel, texcoords).r;
|
||||
yuv += vec3(-0.0627451017, -0.501960814, -0.501960814);
|
||||
|
||||
vec4 color;
|
||||
color.r = dot(yuv, vec3(1.164, 0.000, 1.596));
|
||||
color.g = dot(yuv, vec3(1.164, -0.391, -0.813));
|
||||
color.b = dot(yuv, vec3(1.164, 2.018, 0.000));
|
||||
color.a = 1.0;
|
||||
|
||||
return gammaCorrectColor(color);
|
||||
}]],
|
||||
|
||||
FOOTER = [[
|
||||
void main() {
|
||||
// fix crashing issue in OSX when _tex0_ is unused within effect()
|
||||
@@ -209,6 +231,7 @@ local function createPixelCode(pixelcode, is_multicanvas, lang)
|
||||
love.graphics.isGammaCorrect() and "#define LOVE_GAMMA_CORRECT 1" or "",
|
||||
GLSL.PIXEL.HEADER, GLSL.UNIFORMS,
|
||||
GLSL.FUNCTIONS,
|
||||
GLSL.PIXEL.FUNCTIONS,
|
||||
lang == "glsles" and "#line 1" or "#line 0",
|
||||
pixelcode,
|
||||
is_multicanvas and GLSL.PIXEL.FOOTER_MULTI_CANVAS or GLSL.PIXEL.FOOTER,
|
||||
@@ -307,23 +330,47 @@ vec4 position(mat4 transform_proj, vec4 vertpos) {
|
||||
return transform_proj * vertpos;
|
||||
}]],
|
||||
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;
|
||||
}]]
|
||||
}]],
|
||||
videopixel = [[
|
||||
vec4 effect(mediump vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
|
||||
return VideoTexel(texcoord) * vcolor;
|
||||
}]],
|
||||
}
|
||||
|
||||
local defaults = {
|
||||
opengl = {
|
||||
createVertexCode(defaultcode.vertex, "glsl"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsl"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsl"),
|
||||
},
|
||||
opengles = {
|
||||
createVertexCode(defaultcode.vertex, "glsles"),
|
||||
createPixelCode(defaultcode.pixel, false, "glsles"),
|
||||
createPixelCode(defaultcode.videopixel, false, "glsles"),
|
||||
},
|
||||
}
|
||||
|
||||
love.graphics._setDefaultShaderCode(defaults)
|
||||
|
||||
function love.graphics.newVideo(file, loadaudio)
|
||||
local video = love.graphics._newVideo(file)
|
||||
local source, success
|
||||
|
||||
if loadaudio ~= false then
|
||||
success, source = pcall(love.audio.newSource, video:getStream():getFilename())
|
||||
end
|
||||
if success then
|
||||
video:setSource(source)
|
||||
elseif loadaudio == true then
|
||||
error("Video had no audio track", 2)
|
||||
else
|
||||
video:getStream():setSync(love.video.newRemote())
|
||||
end
|
||||
|
||||
return video
|
||||
end
|
||||
|
||||
-- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string.
|
||||
--)luastring"--"
|
||||
|
||||
@@ -137,17 +137,8 @@ int w_Image_getFlags(lua_State *L)
|
||||
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 },
|
||||
{ "getMipmapFilter", w_Image_getMipmapFilter },
|
||||
{ "isCompressed", w_Image_isCompressed },
|
||||
@@ -159,7 +150,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -34,12 +34,6 @@ namespace opengl
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -109,10 +109,15 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
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);
|
||||
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();
|
||||
|
||||
@@ -120,7 +125,10 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
for (const Mesh::AttribFormat &format : vertexformat)
|
||||
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++)
|
||||
{
|
||||
@@ -145,7 +153,7 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
lua_pop(L, ncomponents + 1);
|
||||
}
|
||||
|
||||
t->unmapVertexData();
|
||||
t->unmapVertexData(byteoffset, nvertices * stride);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -331,6 +339,13 @@ int w_Mesh_setVertexMap(lua_State *L)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
@@ -468,7 +490,7 @@ int w_Mesh_getDrawRange(lua_State *L)
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
static const luaL_Reg w_Mesh_functions[] =
|
||||
{
|
||||
{ "setVertices", w_Mesh_setVertices },
|
||||
{ "setVertex", w_Mesh_setVertex },
|
||||
@@ -494,7 +516,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -705,7 +705,7 @@ int w_ParticleSystem_update(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
static const luaL_Reg w_ParticleSystem_functions[] =
|
||||
{
|
||||
{ "clone", w_ParticleSystem_clone },
|
||||
{ "setTexture", w_ParticleSystem_setTexture },
|
||||
@@ -772,7 +772,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -33,66 +33,6 @@ namespace opengl
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -229,38 +230,86 @@ int w_Shader_sendMatrix(lua_State *L)
|
||||
if (!lua_istable(L, 3))
|
||||
return luax_typerror(L, 3, "matrix table");
|
||||
|
||||
lua_getfield(L, 3, "dimension");
|
||||
int dimension = (int) lua_tointeger(L, -1);
|
||||
int dimension = 0;
|
||||
|
||||
lua_rawgeti(L, 3, 1);
|
||||
if (lua_istable(L, -1))
|
||||
dimension = (int) luax_objlen(L, 3);
|
||||
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)
|
||||
return luaL_error(L, "Invalid matrix size: %dx%d (only 2x2, 3x3 and 4x4 matrices are supported).",
|
||||
dimension, dimension);
|
||||
|
||||
float *values = new float[dimension * dimension * count];
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
lua_getfield(L, 3+i, "dimension");
|
||||
if (lua_tointeger(L, -1) != dimension)
|
||||
int other_dimension = 0;
|
||||
|
||||
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
|
||||
// another dimension: a dimension of sound, a dimension of sight,
|
||||
// a dimension of mind. You're moving into a land of both shadow
|
||||
// and substance, of things and ideas. You've just crossed over
|
||||
// into... the Twilight Zone.
|
||||
int other_dimension = (int) lua_tointeger(L, -1);
|
||||
delete[] values;
|
||||
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);
|
||||
}
|
||||
|
||||
for (int k = 1; k <= dimension*dimension; ++k)
|
||||
if (table_of_tables)
|
||||
{
|
||||
lua_rawgeti(L, 3+i, k);
|
||||
values[i * dimension * dimension + k - 1] = (float)lua_tonumber(L, -1);
|
||||
}
|
||||
int n = 0;
|
||||
|
||||
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,
|
||||
@@ -281,48 +330,6 @@ int w_Shader_sendTexture(lua_State *L)
|
||||
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 ttype = lua_type(L, 3);
|
||||
@@ -352,10 +359,7 @@ int w_Shader_send(lua_State *L)
|
||||
if (ttype == LUA_TNUMBER || ttype == LUA_TBOOLEAN)
|
||||
return w_Shader_sendFloat(L);
|
||||
else if (ttype == LUA_TTABLE)
|
||||
{
|
||||
w_convertMatrices(L, 3);
|
||||
return w_Shader_sendMatrix(L);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
@@ -397,7 +401,7 @@ int w_Shader_getExternVariable(lua_State *L)
|
||||
return 3;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
static const luaL_Reg w_Shader_functions[] =
|
||||
{
|
||||
{ "getWarnings", w_Shader_getWarnings },
|
||||
{ "sendInt", w_Shader_sendInt },
|
||||
@@ -413,7 +417,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
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
|
||||
|
||||
@@ -33,14 +33,6 @@ namespace opengl
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -203,7 +203,17 @@ int w_SpriteBatch_getBufferSize(lua_State *L)
|
||||
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 },
|
||||
{ "set", w_SpriteBatch_set },
|
||||
@@ -216,12 +226,13 @@ static const luaL_Reg functions[] =
|
||||
{ "getCount", w_SpriteBatch_getCount },
|
||||
{ "setBufferSize", w_SpriteBatch_setBufferSize },
|
||||
{ "getBufferSize", w_SpriteBatch_getBufferSize },
|
||||
{ "attachAttribute", w_SpriteBatch_attachAttribute },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user