mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Merged default into minor
--HG-- branch : minor
This commit is contained in:
+1
-1
@@ -38,7 +38,7 @@ public:
|
||||
/**
|
||||
* Destructor.
|
||||
**/
|
||||
virtual ~Data() {};
|
||||
virtual ~Data() {}
|
||||
|
||||
/**
|
||||
* Gets a pointer to the data. This pointer will obviously not
|
||||
|
||||
@@ -38,8 +38,12 @@ void Memoizer::remove(void *key)
|
||||
|
||||
void *Memoizer::find(void *key)
|
||||
{
|
||||
if (objectMap.count(key)) return objectMap[key];
|
||||
return NULL;
|
||||
auto it = objectMap.find(key);
|
||||
|
||||
if (it != objectMap.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // love
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace love
|
||||
const char REFERENCE_TABLE_NAME[] = "love-references";
|
||||
|
||||
Reference::Reference()
|
||||
: L(0)
|
||||
: L(nullptr)
|
||||
, idx(LUA_REFNIL)
|
||||
{
|
||||
}
|
||||
@@ -64,21 +64,31 @@ void Reference::unref()
|
||||
}
|
||||
}
|
||||
|
||||
void Reference::push()
|
||||
void Reference::push(lua_State *newL)
|
||||
{
|
||||
if (idx != LUA_REFNIL)
|
||||
{
|
||||
luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
|
||||
lua_rawgeti(L, -1, idx);
|
||||
lua_remove(L, -2);
|
||||
luax_insist(newL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
|
||||
lua_rawgeti(newL, -1, idx);
|
||||
lua_remove(newL, -2);
|
||||
}
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_pushnil(newL);
|
||||
}
|
||||
|
||||
lua_State *Reference::getL()
|
||||
void Reference::push()
|
||||
{
|
||||
push(L);
|
||||
}
|
||||
|
||||
lua_State *Reference::getL() const
|
||||
{
|
||||
return L;
|
||||
}
|
||||
|
||||
void Reference::setL(lua_State *newL)
|
||||
{
|
||||
L = newL;
|
||||
}
|
||||
|
||||
} // love
|
||||
|
||||
+17
-1
@@ -63,6 +63,14 @@ public:
|
||||
**/
|
||||
void unref();
|
||||
|
||||
/**
|
||||
* Pushes the referred value onto the stack of a different coroutine
|
||||
* in the same main Lua state.
|
||||
* THIS SHOULD NOT BE USED FOR DIFFERENT LUA STATES (created with
|
||||
* luaL_newstate)! Only with different coroutines!
|
||||
**/
|
||||
void push(lua_State *newL);
|
||||
|
||||
/**
|
||||
* Pushes the referred value onto the stack.
|
||||
**/
|
||||
@@ -72,7 +80,15 @@ public:
|
||||
* Gets the Lua state associated with this
|
||||
* reference.
|
||||
**/
|
||||
lua_State *getL();
|
||||
lua_State *getL() const;
|
||||
|
||||
/**
|
||||
* Associates a new Lua state with this reference.
|
||||
* THIS IS DANGEROUS! It is only designed to be
|
||||
* used with different coroutines from the same
|
||||
* main Lua state!
|
||||
**/
|
||||
void setL(lua_State *newL);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
namespace love
|
||||
{
|
||||
// Implementation in header.
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,4 +38,4 @@ char *b64_decode(const char *src, int slen, int &size);
|
||||
|
||||
} // love
|
||||
|
||||
#endif // LOVE_B64_H
|
||||
#endif // LOVE_B64_H
|
||||
|
||||
@@ -464,7 +464,7 @@ void luax_pushtype(lua_State *L, const char *name, bits flags, love::Object *dat
|
||||
|
||||
bool luax_istype(lua_State *L, int idx, love::bits type)
|
||||
{
|
||||
if (lua_isuserdata(L, idx) == 0)
|
||||
if (lua_type(L, idx) != LUA_TUSERDATA)
|
||||
return false;
|
||||
|
||||
return ((((Proxy *)lua_touserdata(L, idx))->flags & type) == type);
|
||||
|
||||
@@ -401,7 +401,7 @@ extern "C" { // Also called from luasocket
|
||||
template <typename T>
|
||||
T *luax_checktype(lua_State *L, int idx, const char *name, love::bits type)
|
||||
{
|
||||
if (lua_isuserdata(L, idx) == 0)
|
||||
if (lua_type(L, idx) != LUA_TUSERDATA)
|
||||
luax_typerror(L, idx, name);
|
||||
|
||||
Proxy *u = (Proxy *)lua_touserdata(L, idx);
|
||||
|
||||
+1
-1
@@ -45,4 +45,4 @@ void replace_char(std::string &str, char find, char replace);
|
||||
|
||||
} // love
|
||||
|
||||
#endif // LOVE_WINDOWS
|
||||
#endif // LOVE_WINDOWS
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace love
|
||||
// Version stuff.
|
||||
const int VERSION_MAJOR = 0;
|
||||
const int VERSION_MINOR = 9;
|
||||
const int VERSION_REV = 0;
|
||||
const char *VERSION = "0.9.0";
|
||||
const char *VERSION_COMPATIBILITY[] = { VERSION, 0 };
|
||||
const int VERSION_REV = 1;
|
||||
const char *VERSION = "0.9.1";
|
||||
const char *VERSION_COMPATIBILITY[] = { VERSION, "0.9.0", 0 };
|
||||
const char *VERSION_CODENAME = "Baby Inspector";
|
||||
|
||||
} // love
|
||||
|
||||
@@ -237,36 +237,37 @@ size_t Parser::getMipmapCount() const
|
||||
|
||||
size_t Parser::parseImageSize(Format fmt, int width, int height) const
|
||||
{
|
||||
size_t size = 0;
|
||||
size_t numBlocksWide = 0;
|
||||
size_t numBlocksHigh = 0;
|
||||
size_t numBytesPerBlock = 0;
|
||||
|
||||
switch (fmt)
|
||||
{
|
||||
case FORMAT_DXT1:
|
||||
case FORMAT_BC4:
|
||||
case FORMAT_BC4s:
|
||||
numBytesPerBlock = 8;
|
||||
break;
|
||||
case FORMAT_DXT3:
|
||||
case FORMAT_DXT5:
|
||||
case FORMAT_BC5s:
|
||||
case FORMAT_BC5:
|
||||
case FORMAT_BC6H:
|
||||
case FORMAT_BC7:
|
||||
case FORMAT_BC7srgb:
|
||||
{
|
||||
int numBlocksWide = 0;
|
||||
if (width > 0)
|
||||
numBlocksWide = std::max(1, (width + 3) / 4);
|
||||
|
||||
int numBlocksHigh = 0;
|
||||
if (height > 0)
|
||||
numBlocksHigh = std::max(1, (height + 3) / 4);
|
||||
|
||||
int numBytesPerBlock = (fmt == FORMAT_DXT1 ? 8 : 16);
|
||||
|
||||
size = numBlocksWide * numBytesPerBlock * numBlocksHigh;
|
||||
}
|
||||
numBytesPerBlock = 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
if (width > 0)
|
||||
numBlocksWide = std::max(1, (width + 3) / 4);
|
||||
|
||||
if (height > 0)
|
||||
numBlocksHigh = std::max(1, (height + 3) / 4);
|
||||
|
||||
return numBlocksWide * numBytesPerBlock * numBlocksHigh;
|
||||
}
|
||||
|
||||
bool Parser::parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips)
|
||||
|
||||
@@ -60,7 +60,7 @@ struct Image
|
||||
const uint8_t *data;
|
||||
|
||||
Image() : width(0), height(0), dataSize(0), data(0)
|
||||
{};
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -716,10 +716,6 @@ static const struct luaL_Reg enet_peer_funcs [] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const struct luaL_Reg enet_event_funcs [] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int luaopen_enet(lua_State *l) {
|
||||
enet_initialize();
|
||||
atexit(enet_deinitialize);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef LUA_WRAP_H
|
||||
#define LUA_WRAP_H
|
||||
|
||||
#define LUA_COMPAT_ALL
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
@@ -10,3 +13,5 @@
|
||||
|
||||
extern int luax_typerror(lua_State *L, int narg, const char *type);
|
||||
#endif
|
||||
|
||||
#endif // LUA_WRAP_H
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
/**
|
||||
* Destructor.
|
||||
**/
|
||||
virtual ~Audio() {};
|
||||
virtual ~Audio() {}
|
||||
|
||||
virtual Source *newSource(love::sound::Decoder *decoder) = 0;
|
||||
virtual Source *newSource(love::sound::SoundData *soundData) = 0;
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
* Play the specified Source.
|
||||
* @param source The Source to play.
|
||||
**/
|
||||
virtual void play(Source *source) = 0;
|
||||
virtual bool play(Source *source) = 0;
|
||||
|
||||
/**
|
||||
* Stops playback on the specified source.
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
|
||||
virtual Source *clone() = 0;
|
||||
|
||||
virtual void play() = 0;
|
||||
virtual bool play() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void pause() = 0;
|
||||
virtual void resume() = 0;
|
||||
|
||||
@@ -61,12 +61,9 @@ int Audio::getMaxSources() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Audio::play(love::audio::Source *)
|
||||
{
|
||||
}
|
||||
|
||||
void Audio::play()
|
||||
bool Audio::play(love::audio::Source *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Audio::stop(love::audio::Source *)
|
||||
|
||||
@@ -48,8 +48,7 @@ public:
|
||||
love::audio::Source *newSource(love::sound::SoundData *soundData);
|
||||
int getSourceCount() const;
|
||||
int getMaxSources() const;
|
||||
void play(love::audio::Source *source);
|
||||
void play();
|
||||
bool play(love::audio::Source *source);
|
||||
void stop(love::audio::Source *source);
|
||||
void stop();
|
||||
void pause(love::audio::Source *source);
|
||||
|
||||
@@ -42,8 +42,9 @@ love::audio::Source *Source::clone()
|
||||
return this;
|
||||
}
|
||||
|
||||
void Source::play()
|
||||
bool Source::play()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Source::stop()
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
virtual ~Source();
|
||||
|
||||
virtual love::audio::Source *clone();
|
||||
virtual void play();
|
||||
virtual bool play();
|
||||
virtual void stop();
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
|
||||
@@ -69,22 +69,25 @@ void Audio::PoolThread::setFinish()
|
||||
}
|
||||
|
||||
Audio::Audio()
|
||||
: distanceModel(DISTANCE_INVERSE_CLAMPED)
|
||||
: device(nullptr)
|
||||
, capture(nullptr)
|
||||
, context(nullptr)
|
||||
, pool(nullptr)
|
||||
, poolThread(nullptr)
|
||||
, distanceModel(DISTANCE_INVERSE_CLAMPED)
|
||||
{
|
||||
// Passing zero for default device.
|
||||
device = alcOpenDevice(0);
|
||||
// Passing null for default device.
|
||||
device = alcOpenDevice(nullptr);
|
||||
|
||||
if (device == 0)
|
||||
if (device == nullptr)
|
||||
throw love::Exception("Could not open device.");
|
||||
|
||||
context = alcCreateContext(device, 0);
|
||||
context = alcCreateContext(device, nullptr);
|
||||
|
||||
if (context == 0)
|
||||
if (context == nullptr)
|
||||
throw love::Exception("Could not create context.");
|
||||
|
||||
alcMakeContextCurrent(context);
|
||||
|
||||
if (alcGetError(device) != ALC_NO_ERROR)
|
||||
if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
|
||||
throw love::Exception("Could not make context current.");
|
||||
|
||||
/*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
|
||||
@@ -108,7 +111,18 @@ Audio::Audio()
|
||||
}*/
|
||||
|
||||
// pool must be allocated after AL context.
|
||||
pool = new Pool();
|
||||
try
|
||||
{
|
||||
pool = new Pool();
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
//if (capture) alcCaptureCloseDevice(capture);
|
||||
alcCloseDevice(device);
|
||||
throw;
|
||||
}
|
||||
|
||||
poolThread = new PoolThread(pool);
|
||||
poolThread->start();
|
||||
@@ -122,7 +136,7 @@ Audio::~Audio()
|
||||
delete poolThread;
|
||||
delete pool;
|
||||
|
||||
alcMakeContextCurrent(0);
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
//if (capture) alcCaptureCloseDevice(capture);
|
||||
alcCloseDevice(device);
|
||||
@@ -154,9 +168,9 @@ int Audio::getMaxSources() const
|
||||
return pool->getMaxSources();
|
||||
}
|
||||
|
||||
void Audio::play(love::audio::Source *source)
|
||||
bool Audio::play(love::audio::Source *source)
|
||||
{
|
||||
source->play();
|
||||
return source->play();
|
||||
}
|
||||
|
||||
void Audio::stop(love::audio::Source *source)
|
||||
@@ -281,7 +295,7 @@ bool Audio::canRecord()
|
||||
|
||||
Audio::DistanceModel Audio::getDistanceModel() const
|
||||
{
|
||||
return this->distanceModel;
|
||||
return distanceModel;
|
||||
}
|
||||
|
||||
void Audio::setDistanceModel(DistanceModel distanceModel)
|
||||
|
||||
@@ -67,8 +67,7 @@ public:
|
||||
love::audio::Source *newSource(love::sound::SoundData *soundData);
|
||||
int getSourceCount() const;
|
||||
int getMaxSources() const;
|
||||
void play(love::audio::Source *source);
|
||||
void play();
|
||||
bool play(love::audio::Source *source);
|
||||
void stop(love::audio::Source *source);
|
||||
void stop();
|
||||
void pause(love::audio::Source *source);
|
||||
|
||||
@@ -30,23 +30,45 @@ namespace openal
|
||||
{
|
||||
|
||||
Pool::Pool()
|
||||
: sources()
|
||||
, totalSources(0)
|
||||
, mutex(nullptr)
|
||||
{
|
||||
// Clear errors.
|
||||
alGetError();
|
||||
|
||||
// Generate sources.
|
||||
alGenSources(NUM_SOURCES, sources);
|
||||
for (int i = 0; i < MAX_SOURCES; i++)
|
||||
{
|
||||
alGenSources(1, &sources[i]);
|
||||
|
||||
// We might hit an implementation-dependent limit on the total number
|
||||
// of sources before reaching MAX_SOURCES.
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
break;
|
||||
|
||||
totalSources++;
|
||||
}
|
||||
|
||||
if (totalSources < 4)
|
||||
throw love::Exception("Could not generate sources.");
|
||||
|
||||
// Create the mutex.
|
||||
mutex = thread::newMutex();
|
||||
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
throw love::Exception("Could not generate sources.");
|
||||
ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels");
|
||||
|
||||
// Make all sources available initially.
|
||||
for (int i = 0; i < NUM_SOURCES; i++)
|
||||
for (int i = 0; i < totalSources; i++)
|
||||
{
|
||||
#ifdef AL_DIRECT_CHANNELS_SOFT
|
||||
// Bypassing virtualization of speakers for multi-channel sources in OpenAL Soft.
|
||||
alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
|
||||
#ifdef AL_SOFT_direct_channels
|
||||
if (hasext)
|
||||
{
|
||||
// Bypass virtualization of speakers for multi-channel sources in OpenAL Soft.
|
||||
alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
available.push(sources[i]);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +80,7 @@ Pool::~Pool()
|
||||
delete mutex;
|
||||
|
||||
// Free all sources.
|
||||
alDeleteSources(NUM_SOURCES, sources);
|
||||
alDeleteSources(totalSources, sources);
|
||||
}
|
||||
|
||||
bool Pool::isAvailable() const
|
||||
@@ -113,7 +135,7 @@ int Pool::getSourceCount() const
|
||||
|
||||
int Pool::getMaxSources() const
|
||||
{
|
||||
return NUM_SOURCES;
|
||||
return totalSources;
|
||||
}
|
||||
|
||||
bool Pool::play(Source *source, ALuint &out)
|
||||
@@ -141,9 +163,7 @@ bool Pool::play(Source *source, ALuint &out)
|
||||
|
||||
source->retain();
|
||||
|
||||
source->playAtomic();
|
||||
|
||||
ok = true;
|
||||
ok = source->playAtomic();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#ifdef LOVE_MACOSX_USE_FRAMEWORKS
|
||||
#include <OpenAL-Soft/alc.h>
|
||||
#include <OpenAL-Soft/al.h>
|
||||
#include <OpenAL-Soft/alext.h>
|
||||
#else
|
||||
#include <AL/alc.h>
|
||||
#include <AL/al.h>
|
||||
@@ -99,11 +100,15 @@ private:
|
||||
|
||||
bool findSource(Source *source, ALuint &out);
|
||||
bool removeSource(Source *source);
|
||||
// Number of OpenAL sources.
|
||||
static const int NUM_SOURCES = 64;
|
||||
|
||||
// Maximum possible number of OpenAL sources the pool attempts to generate.
|
||||
static const int MAX_SOURCES = 64;
|
||||
|
||||
// OpenAL sources
|
||||
ALuint sources[NUM_SOURCES];
|
||||
ALuint sources[MAX_SOURCES];
|
||||
|
||||
// Total number of created sources in the pool.
|
||||
int totalSources;
|
||||
|
||||
// A queue of available sources.
|
||||
std::queue<ALuint> available;
|
||||
|
||||
@@ -165,15 +165,16 @@ love::audio::Source *Source::clone()
|
||||
return new Source(*this);
|
||||
}
|
||||
|
||||
void Source::play()
|
||||
bool Source::play()
|
||||
{
|
||||
if (valid && paused)
|
||||
{
|
||||
pool->resume(this);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
valid = pool->play(this, source);
|
||||
return valid;
|
||||
}
|
||||
|
||||
void Source::stop()
|
||||
@@ -512,7 +513,7 @@ bool Source::isLooping() const
|
||||
return looping;
|
||||
}
|
||||
|
||||
void Source::playAtomic()
|
||||
bool Source::playAtomic()
|
||||
{
|
||||
if (type == TYPE_STATIC)
|
||||
{
|
||||
@@ -539,10 +540,19 @@ void Source::playAtomic()
|
||||
// of the new one.
|
||||
reset();
|
||||
|
||||
// Clear errors.
|
||||
alGetError();
|
||||
|
||||
alSourcePlay(source);
|
||||
|
||||
// alSourcePlay may fail if the system has reached its limit of simultaneous
|
||||
// playing sources.
|
||||
bool success = alGetError() == AL_NO_ERROR;
|
||||
|
||||
valid = true; //if it fails it will be set to false again
|
||||
//but this prevents a horrible, horrible bug
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Source::stopAtomic()
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
virtual ~Source();
|
||||
|
||||
virtual love::audio::Source *clone();
|
||||
virtual void play();
|
||||
virtual bool play();
|
||||
virtual void stop();
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
virtual float getMaxDistance() const;
|
||||
virtual int getChannels() const;
|
||||
|
||||
void playAtomic();
|
||||
bool playAtomic();
|
||||
void stopAtomic();
|
||||
void pauseAtomic();
|
||||
void resumeAtomic();
|
||||
|
||||
@@ -75,8 +75,8 @@ int w_newSource(lua_State *L)
|
||||
int w_play(lua_State *L)
|
||||
{
|
||||
Source *s = luax_checksource(L, 1);
|
||||
instance->play(s);
|
||||
return 0;
|
||||
luax_pushboolean(L, instance->play(s));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_stop(lua_State *L)
|
||||
|
||||
@@ -44,8 +44,8 @@ int w_Source_clone(lua_State *L)
|
||||
int w_Source_play(lua_State *L)
|
||||
{
|
||||
Source *t = luax_checksource(L, 1);
|
||||
t->play();
|
||||
return 0;
|
||||
luax_pushboolean(L, t->play());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Source_stop(lua_State *L)
|
||||
|
||||
@@ -37,6 +37,24 @@ namespace event
|
||||
namespace sdl
|
||||
{
|
||||
|
||||
// SDL reports mouse coordinates in the window coordinate system in OS X, but
|
||||
// we want them in pixel coordinates (may be different with high-DPI enabled.)
|
||||
static void windowToPixelCoords(int *x, int *y)
|
||||
{
|
||||
double scale = 1.0;
|
||||
|
||||
window::Window *window = (window::Window *) Module::findInstance("love.window.");
|
||||
if (window != nullptr)
|
||||
scale = window->getPixelScale();
|
||||
|
||||
if (x != nullptr)
|
||||
*x = int(double(*x) * scale);
|
||||
|
||||
if (y != nullptr)
|
||||
*y = int(double(*y) * scale);
|
||||
}
|
||||
|
||||
|
||||
const char *Event::getName() const
|
||||
{
|
||||
return "love.event.sdl";
|
||||
@@ -162,8 +180,11 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (buttons.find(e.button.button, button) && mouse::Mouse::getConstant(button, txt))
|
||||
{
|
||||
arg1 = new Variant((double) e.button.x);
|
||||
arg2 = new Variant((double) e.button.y);
|
||||
int x = e.button.x;
|
||||
int y = e.button.y;
|
||||
windowToPixelCoords(&x, &y);
|
||||
arg1 = new Variant((double) x);
|
||||
arg2 = new Variant((double) y);
|
||||
arg3 = new Variant(txt, strlen(txt));
|
||||
msg = new Message((e.type == SDL_MOUSEBUTTONDOWN) ?
|
||||
"mousepressed" : "mousereleased",
|
||||
@@ -182,6 +203,7 @@ Message *Event::convert(const SDL_Event &e) const
|
||||
|
||||
int mx, my;
|
||||
SDL_GetMouseState(&mx, &my);
|
||||
windowToPixelCoords(&mx, &my);
|
||||
|
||||
arg1 = new Variant((double) mx);
|
||||
arg2 = new Variant((double) my);
|
||||
@@ -366,7 +388,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
|
||||
Message *Event::convertWindowEvent(const SDL_Event &e) const
|
||||
{
|
||||
Message *msg = 0;
|
||||
Variant *arg1, *arg2;
|
||||
Variant *arg1, *arg2, *arg3, *arg4;
|
||||
window::Window *win = 0;
|
||||
|
||||
if (e.type != SDL_WINDOWEVENT)
|
||||
@@ -402,17 +424,31 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
|
||||
win = (window::Window *) Module::findInstance("love.window.");
|
||||
if (win)
|
||||
{
|
||||
int px_w = e.window.data1;
|
||||
int px_h = e.window.data2;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2,0,1)
|
||||
SDL_Window *sdlwin = SDL_GetWindowFromID(e.window.windowID);
|
||||
if (sdlwin)
|
||||
SDL_GL_GetDrawableSize(sdlwin, &px_w, &px_h);
|
||||
#endif
|
||||
|
||||
win->onWindowResize(e.window.data1, e.window.data2);
|
||||
|
||||
graphics::Graphics *gfx = (graphics::Graphics *) Module::findInstance("love.graphics.");
|
||||
if (gfx)
|
||||
gfx->setViewportSize(e.window.data1, e.window.data2);
|
||||
gfx->setViewportSize(px_w, px_h);
|
||||
|
||||
arg1 = new Variant((double) px_w);
|
||||
arg2 = new Variant((double) px_h);
|
||||
arg3 = new Variant((double) e.window.data1);
|
||||
arg4 = new Variant((double) e.window.data2);
|
||||
msg = new Message("resize", arg1, arg2, arg3, arg4);
|
||||
arg1->release();
|
||||
arg2->release();
|
||||
arg3->release();
|
||||
arg4->release();
|
||||
}
|
||||
arg1 = new Variant((double) e.window.data1);
|
||||
arg2 = new Variant((double) e.window.data2);
|
||||
msg = new Message("resize", arg1, arg2);
|
||||
arg1->release();
|
||||
arg2->release();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -608,6 +644,17 @@ std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::createKeyMap()
|
||||
k[SDLK_AUDIOPLAY] = Keyboard::KEY_AUDIOPLAY;
|
||||
k[SDLK_AUDIOMUTE] = Keyboard::KEY_AUDIOMUTE;
|
||||
k[SDLK_MEDIASELECT] = Keyboard::KEY_MEDIASELECT;
|
||||
k[SDLK_WWW] = Keyboard::KEY_WWW;
|
||||
k[SDLK_MAIL] = Keyboard::KEY_MAIL;
|
||||
k[SDLK_CALCULATOR] = Keyboard::KEY_CALCULATOR;
|
||||
k[SDLK_COMPUTER] = Keyboard::KEY_COMPUTER;
|
||||
k[SDLK_AC_SEARCH] = Keyboard::KEY_APP_SEARCH;
|
||||
k[SDLK_AC_HOME] = Keyboard::KEY_APP_HOME;
|
||||
k[SDLK_AC_BACK] = Keyboard::KEY_APP_BACK;
|
||||
k[SDLK_AC_FORWARD] = Keyboard::KEY_APP_FORWARD;
|
||||
k[SDLK_AC_STOP] = Keyboard::KEY_APP_STOP;
|
||||
k[SDLK_AC_REFRESH] = Keyboard::KEY_APP_REFRESH;
|
||||
k[SDLK_AC_BOOKMARKS] = Keyboard::KEY_APP_BOOKMARKS;
|
||||
|
||||
k[SDLK_BRIGHTNESSDOWN] = Keyboard::KEY_BRIGHTNESSDOWN;
|
||||
k[SDLK_BRIGHTNESSUP] = Keyboard::KEY_BRIGHTNESSUP;
|
||||
|
||||
@@ -54,4 +54,4 @@ public:
|
||||
} // font
|
||||
} // love
|
||||
|
||||
#endif // LOVE_FONT_FONT_H
|
||||
#endif // LOVE_FONT_FONT_H
|
||||
|
||||
@@ -46,7 +46,6 @@ struct GlyphMetrics
|
||||
int advance;
|
||||
int bearingX;
|
||||
int bearingY;
|
||||
int spacing;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,4 +79,4 @@ private:
|
||||
} // font
|
||||
} // love
|
||||
|
||||
#endif // LOVE_FONT_IMAGE_RASTERIZER_H
|
||||
#endif // LOVE_FONT_IMAGE_RASTERIZER_H
|
||||
|
||||
@@ -96,4 +96,4 @@ bool Rasterizer::hasGlyphs(const std::string &text) const
|
||||
}
|
||||
|
||||
} // font
|
||||
} // love
|
||||
} // love
|
||||
|
||||
@@ -114,4 +114,4 @@ protected:
|
||||
} // font
|
||||
} // love
|
||||
|
||||
#endif // LOVE_FONT_RASTERIZER_H
|
||||
#endif // LOVE_FONT_RASTERIZER_H
|
||||
|
||||
@@ -146,4 +146,4 @@ bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const
|
||||
|
||||
} // freetype
|
||||
} // font
|
||||
} // love
|
||||
} // love
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
* @param kx Shear along the x-axis.
|
||||
* @param ky Shear along the y-axis.
|
||||
**/
|
||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
|
||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0;
|
||||
};
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -99,6 +99,16 @@ bool Graphics::getConstant(RendererInfo in, const char *&out)
|
||||
return rendererInfo.find(in, out);
|
||||
}
|
||||
|
||||
bool Graphics::getConstant(const char *in, SystemLimit &out)
|
||||
{
|
||||
return systemLimits.find(in, out);
|
||||
}
|
||||
|
||||
bool Graphics::getConstant(SystemLimit in, const char *&out)
|
||||
{
|
||||
return systemLimits.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
|
||||
{
|
||||
{ "line", Graphics::DRAW_LINE },
|
||||
@@ -157,6 +167,8 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
|
||||
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
||||
{ "dxt", Graphics::SUPPORT_DXT },
|
||||
{ "bc5", Graphics::SUPPORT_BC5 },
|
||||
{ "instancing", Graphics::SUPPORT_INSTANCING },
|
||||
{ "srgb", Graphics::SUPPORT_SRGB },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
@@ -171,5 +183,15 @@ StringMap<Graphics::RendererInfo, Graphics::RENDERER_INFO_MAX_ENUM>::Entry Graph
|
||||
|
||||
StringMap<Graphics::RendererInfo, Graphics::RENDERER_INFO_MAX_ENUM> Graphics::rendererInfo(Graphics::rendererInfoEntries, sizeof(Graphics::rendererInfoEntries));
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::systemLimitEntries[] =
|
||||
{
|
||||
{"pointsize", Graphics::LIMIT_POINT_SIZE},
|
||||
{"texturesize", Graphics::LIMIT_TEXTURE_SIZE},
|
||||
{"multicanvas", Graphics::LIMIT_MULTI_CANVAS},
|
||||
{"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA},
|
||||
};
|
||||
|
||||
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -87,6 +87,8 @@ public:
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_DXT,
|
||||
SUPPORT_BC5,
|
||||
SUPPORT_INSTANCING,
|
||||
SUPPORT_SRGB,
|
||||
SUPPORT_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -99,6 +101,15 @@ public:
|
||||
RENDERER_INFO_MAX_ENUM
|
||||
};
|
||||
|
||||
enum SystemLimit
|
||||
{
|
||||
LIMIT_POINT_SIZE,
|
||||
LIMIT_TEXTURE_SIZE,
|
||||
LIMIT_MULTI_CANVAS,
|
||||
LIMIT_CANVAS_FSAA,
|
||||
LIMIT_MAX_ENUM
|
||||
};
|
||||
|
||||
virtual ~Graphics();
|
||||
|
||||
/**
|
||||
@@ -111,7 +122,7 @@ public:
|
||||
* @param width The viewport width.
|
||||
* @param height The viewport height.
|
||||
**/
|
||||
virtual bool setMode(int width, int height) = 0;
|
||||
virtual bool setMode(int width, int height, bool &sRGB) = 0;
|
||||
|
||||
/**
|
||||
* Un-sets the current graphics display mode (uninitializing objects if
|
||||
@@ -140,6 +151,9 @@ public:
|
||||
static bool getConstant(const char *in, RendererInfo &out);
|
||||
static bool getConstant(RendererInfo in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, SystemLimit &out);
|
||||
static bool getConstant(SystemLimit in, const char *&out);
|
||||
|
||||
private:
|
||||
|
||||
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
|
||||
@@ -163,6 +177,9 @@ private:
|
||||
static StringMap<RendererInfo, RENDERER_INFO_MAX_ENUM>::Entry rendererInfoEntries[];
|
||||
static StringMap<RendererInfo, RENDERER_INFO_MAX_ENUM> rendererInfo;
|
||||
|
||||
static StringMap<SystemLimit, LIMIT_MAX_ENUM>::Entry systemLimitEntries[];
|
||||
static StringMap<SystemLimit, LIMIT_MAX_ENUM> systemLimits;
|
||||
|
||||
}; // Graphics
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -109,6 +109,16 @@ bool Texture::getConstant(WrapMode in, const char *&out)
|
||||
return wrapModes.find(in, out);
|
||||
}
|
||||
|
||||
bool Texture::getConstant(const char *in, Format &out)
|
||||
{
|
||||
return formats.find(in, out);
|
||||
}
|
||||
|
||||
bool Texture::getConstant(Format in, const char *&out)
|
||||
{
|
||||
return formats.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
|
||||
{
|
||||
{ "linear", Texture::FILTER_LINEAR },
|
||||
@@ -125,6 +135,15 @@ StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEnt
|
||||
|
||||
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
|
||||
|
||||
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM>::Entry Texture::formatEntries[] =
|
||||
{
|
||||
{"normal", Texture::FORMAT_NORMAL},
|
||||
{"hdr", Texture::FORMAT_HDR},
|
||||
{"srgb", Texture::FORMAT_SRGB},
|
||||
};
|
||||
|
||||
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM> Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries));
|
||||
|
||||
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -55,6 +55,14 @@ public:
|
||||
FILTER_MAX_ENUM
|
||||
};
|
||||
|
||||
enum Format
|
||||
{
|
||||
FORMAT_NORMAL,
|
||||
FORMAT_HDR,
|
||||
FORMAT_SRGB,
|
||||
FORMAT_MAX_ENUM
|
||||
};
|
||||
|
||||
struct Filter
|
||||
{
|
||||
Filter();
|
||||
@@ -88,7 +96,7 @@ public:
|
||||
* @param kx Shear along the x-axis.
|
||||
* @param ky Shear along the y-axis.
|
||||
**/
|
||||
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
|
||||
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0;
|
||||
|
||||
virtual int getWidth() const;
|
||||
virtual int getHeight() const;
|
||||
@@ -111,6 +119,9 @@ public:
|
||||
static bool getConstant(const char *in, WrapMode &out);
|
||||
static bool getConstant(WrapMode in, const char *&out);
|
||||
|
||||
static bool getConstant(const char *in, Format &out);
|
||||
static bool getConstant(Format in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
int width;
|
||||
@@ -132,6 +143,9 @@ private:
|
||||
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
|
||||
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
|
||||
|
||||
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
|
||||
static StringMap<Format, FORMAT_MAX_ENUM> formats;
|
||||
|
||||
}; // Texture
|
||||
|
||||
} // graphics
|
||||
|
||||
@@ -55,10 +55,25 @@ struct FramebufferStrategy
|
||||
/**
|
||||
* @param[in] width Width of the stencil buffer
|
||||
* @param[in] height Height of the stencil buffer
|
||||
* @param[in] samples Number of samples to use
|
||||
* @param[out] stencil Name for stencil buffer
|
||||
* @return Whether the stencil buffer was successfully created
|
||||
**/
|
||||
virtual bool createStencil(int, int, GLuint &)
|
||||
virtual bool createStencil(int, int, int, GLuint &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Create a MSAA renderbuffer and attach it to the active FBO.
|
||||
/**
|
||||
* @param[in] width Width of the MSAA buffer
|
||||
* @param[in] height Height of the MSAA buffer
|
||||
* @param[inout] samples Number of samples to use
|
||||
* @param[in] internalformat The internal format to use for the buffer
|
||||
* @param[out] buffer Name for the MSAA buffer
|
||||
* @return Whether the MSAA buffer was successfully created and attached
|
||||
**/
|
||||
virtual bool createMSAABuffer(int, int, int &, GLenum, GLuint &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -68,7 +83,7 @@ struct FramebufferStrategy
|
||||
* @param[in] framebuffer Framebuffer name
|
||||
* @param[in] depth_stencil Name for packed depth and stencil buffer
|
||||
*/
|
||||
virtual void deleteFBO(GLuint, GLuint) {}
|
||||
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
|
||||
virtual void bindFBO(GLuint) {}
|
||||
|
||||
/// attach additional canvases to the active framebuffer for rendering
|
||||
@@ -93,8 +108,11 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, texture, 0);
|
||||
if (texture != 0)
|
||||
{
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, texture, 0);
|
||||
}
|
||||
|
||||
// check status
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
@@ -104,27 +122,62 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
||||
{
|
||||
// create combined depth/stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glGenRenderbuffers(1, &stencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
|
||||
|
||||
if (samples > 1)
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_STENCIL, width, height);
|
||||
else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, stencil);
|
||||
GL_RENDERBUFFER, stencil);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
stencil = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil)
|
||||
virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
|
||||
{
|
||||
glGenRenderbuffers(1, &buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat,
|
||||
width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER, buffer);
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteRenderbuffers(1, &buffer);
|
||||
buffer = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
|
||||
{
|
||||
if (depth_stencil != 0)
|
||||
glDeleteRenderbuffers(1, &depth_stencil);
|
||||
if (msaa_buffer != 0)
|
||||
glDeleteRenderbuffers(1, &msaa_buffer);
|
||||
if (framebuffer != 0)
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
}
|
||||
@@ -189,27 +242,71 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
||||
{
|
||||
// create combined depth/stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glDeleteRenderbuffersEXT(1, &stencil);
|
||||
glGenRenderbuffersEXT(1, &stencil);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
|
||||
width, height);
|
||||
|
||||
if (samples > 1)
|
||||
{
|
||||
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
||||
GL_DEPTH_STENCIL, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
|
||||
width, height);
|
||||
}
|
||||
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
||||
GL_RENDERBUFFER_EXT, stencil);
|
||||
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
|
||||
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteRenderbuffersEXT(1, &stencil);
|
||||
stencil = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil)
|
||||
virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
|
||||
{
|
||||
if (!GLEE_EXT_framebuffer_multisample)
|
||||
return false;
|
||||
|
||||
glGenRenderbuffersEXT(1, &buffer);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER, buffer);
|
||||
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
||||
internalformat, width, height);
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER, buffer);
|
||||
glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
||||
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER, 0);
|
||||
|
||||
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteRenderbuffersEXT(1, &buffer);
|
||||
buffer = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
|
||||
{
|
||||
if (depth_stencil != 0)
|
||||
glDeleteRenderbuffersEXT(1, &depth_stencil);
|
||||
if (msaa_buffer != 0)
|
||||
glDeleteRenderbuffersEXT(1, &msaa_buffer);
|
||||
if (framebuffer != 0)
|
||||
glDeleteFramebuffersEXT(1, &framebuffer);
|
||||
}
|
||||
@@ -254,33 +351,50 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
|
||||
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
||||
{
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
||||
{
|
||||
// create stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glDeleteRenderbuffersEXT(1, &stencil);
|
||||
glGenRenderbuffersEXT(1, &stencil);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
|
||||
width, height);
|
||||
|
||||
if (samples > 1)
|
||||
{
|
||||
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
||||
GL_STENCIL_INDEX, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
|
||||
width, height);
|
||||
}
|
||||
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
||||
GL_RENDERBUFFER_EXT, stencil);
|
||||
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
|
||||
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glDeleteRenderbuffersEXT(1, &stencil);
|
||||
stencil = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isSupported()
|
||||
{
|
||||
GLuint fb = 0, stencil = 0;
|
||||
GLenum status = createFBO(fb, 0);
|
||||
deleteFBO(fb, stencil);
|
||||
deleteFBO(fb, stencil, 0);
|
||||
return status == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
};
|
||||
|
||||
FramebufferStrategy *strategy = NULL;
|
||||
FramebufferStrategy *strategy = nullptr;
|
||||
|
||||
FramebufferStrategy strategyNone;
|
||||
|
||||
@@ -290,8 +404,9 @@ FramebufferStrategyPackedEXT strategyPackedEXT;
|
||||
|
||||
FramebufferStrategyEXT strategyEXT;
|
||||
|
||||
Canvas *Canvas::current = NULL;
|
||||
Canvas *Canvas::current = nullptr;
|
||||
OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
|
||||
bool Canvas::screenHasSRGB = false;
|
||||
|
||||
static void getStrategy()
|
||||
{
|
||||
@@ -308,14 +423,15 @@ static void getStrategy()
|
||||
}
|
||||
}
|
||||
|
||||
static int maxFBOColorAttachments = 0;
|
||||
static int maxDrawBuffers = 0;
|
||||
|
||||
Canvas::Canvas(int width, int height, TextureType texture_type)
|
||||
Canvas::Canvas(int width, int height, Texture::Format format, int fsaa)
|
||||
: fbo(0)
|
||||
, resolve_fbo(0)
|
||||
, texture(0)
|
||||
, fsaa_buffer(0)
|
||||
, depth_stencil(0)
|
||||
, texture_type(texture_type)
|
||||
, format(format)
|
||||
, fsaa_samples(fsaa)
|
||||
, fsaa_dirty(false)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
@@ -357,9 +473,50 @@ Canvas::~Canvas()
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
bool Canvas::createFSAAFBO(GLenum internalformat)
|
||||
{
|
||||
// Create our FBO without a texture.
|
||||
status = strategy->createFBO(fbo, 0);
|
||||
|
||||
GLuint previous = 0;
|
||||
if (current != this)
|
||||
{
|
||||
if (current != nullptr)
|
||||
previous = current->fbo;
|
||||
|
||||
strategy->bindFBO(fbo);
|
||||
}
|
||||
|
||||
// Create and attach the MSAA buffer for our FBO.
|
||||
if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer))
|
||||
status = GL_FRAMEBUFFER_COMPLETE;
|
||||
else
|
||||
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
||||
|
||||
// Create the FBO used for the MSAA resolve, and attach the texture.
|
||||
if (status == GL_FRAMEBUFFER_COMPLETE)
|
||||
status = strategy->createFBO(resolve_fbo, texture);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
// Clean up.
|
||||
strategy->deleteFBO(fbo, 0, fsaa_buffer);
|
||||
strategy->deleteFBO(resolve_fbo, 0, 0);
|
||||
fbo = fsaa_buffer = resolve_fbo = 0;
|
||||
fsaa_samples = 0;
|
||||
}
|
||||
|
||||
if (current != this)
|
||||
strategy->bindFBO(previous);
|
||||
|
||||
return status == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
|
||||
bool Canvas::loadVolatile()
|
||||
{
|
||||
fbo = depth_stencil = texture = 0;
|
||||
resolve_fbo = fsaa_buffer = 0;
|
||||
status = GL_FRAMEBUFFER_COMPLETE;
|
||||
|
||||
// glTexImage2D is guaranteed to error in this case.
|
||||
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
|
||||
@@ -376,13 +533,17 @@ bool Canvas::loadVolatile()
|
||||
|
||||
GLint internalformat;
|
||||
GLenum textype;
|
||||
switch (texture_type)
|
||||
switch (format)
|
||||
{
|
||||
case TYPE_HDR:
|
||||
case Texture::FORMAT_HDR:
|
||||
internalformat = GL_RGBA16F;
|
||||
textype = GL_FLOAT;
|
||||
break;
|
||||
case TYPE_NORMAL:
|
||||
case Texture::FORMAT_SRGB:
|
||||
internalformat = GL_SRGB8_ALPHA8;
|
||||
textype = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case Texture::FORMAT_NORMAL:
|
||||
default:
|
||||
internalformat = GL_RGBA8;
|
||||
textype = GL_UNSIGNED_BYTE;
|
||||
@@ -406,21 +567,44 @@ bool Canvas::loadVolatile()
|
||||
return false;
|
||||
}
|
||||
|
||||
status = strategy->createFBO(fbo, texture);
|
||||
int max_samples = 0;
|
||||
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
|
||||
|| GLEE_EXT_framebuffer_multisample)
|
||||
{
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
|
||||
}
|
||||
|
||||
if (fsaa_samples > max_samples)
|
||||
fsaa_samples = max_samples;
|
||||
|
||||
// Try to create a FSAA FBO if requested.
|
||||
bool fsaasuccess = false;
|
||||
if (fsaa_samples > 1)
|
||||
fsaasuccess = createFSAAFBO(internalformat);
|
||||
|
||||
// On failure (or no requested FSAA), fall back to a regular FBO.
|
||||
if (!fsaasuccess)
|
||||
status = strategy->createFBO(fbo, texture);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
return false;
|
||||
|
||||
clear(Color(0, 0, 0, 0));
|
||||
|
||||
fsaa_dirty = (fsaa_buffer != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Canvas::unloadVolatile()
|
||||
{
|
||||
strategy->deleteFBO(fbo, depth_stencil);
|
||||
strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer);
|
||||
strategy->deleteFBO(resolve_fbo, 0, 0);
|
||||
|
||||
gl.deleteTexture(texture);
|
||||
|
||||
fbo = depth_stencil = texture = 0;
|
||||
resolve_fbo = fsaa_buffer = 0;
|
||||
|
||||
for (size_t i = 0; i < attachedCanvases.size(); i++)
|
||||
attachedCanvases[i]->release();
|
||||
@@ -428,13 +612,12 @@ void Canvas::unloadVolatile()
|
||||
attachedCanvases.clear();
|
||||
}
|
||||
|
||||
void Canvas::drawv(const Matrix &t, const Vertex *v) const
|
||||
void Canvas::drawv(const Matrix &t, const Vertex *v)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
glMultMatrixf((const GLfloat *)t.getElements());
|
||||
|
||||
gl.bindTexture(texture);
|
||||
predraw();
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
@@ -447,11 +630,13 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) const
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
postdraw();
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
static Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
@@ -459,7 +644,7 @@ void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, f
|
||||
drawv(t, vertices);
|
||||
}
|
||||
|
||||
void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
static Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
@@ -487,8 +672,12 @@ GLuint Canvas::getGLTexture() const
|
||||
return texture;
|
||||
}
|
||||
|
||||
void Canvas::predraw() const
|
||||
void Canvas::predraw()
|
||||
{
|
||||
// We need to make sure the texture is up-to-date by resolving the MSAA
|
||||
// buffer (which we render to when the canvas is active) to it.
|
||||
resolveMSAA();
|
||||
|
||||
gl.bindTexture(texture);
|
||||
}
|
||||
|
||||
@@ -498,12 +687,19 @@ void Canvas::setupGrab()
|
||||
if (current == this)
|
||||
return;
|
||||
|
||||
// cleanup after previous fbo
|
||||
if (current != NULL)
|
||||
current->stopGrab();
|
||||
// cleanup after previous Canvas
|
||||
if (current != nullptr)
|
||||
{
|
||||
systemViewport = current->systemViewport;
|
||||
current->stopGrab(true);
|
||||
}
|
||||
else
|
||||
systemViewport = gl.getViewport();
|
||||
|
||||
// indicate we are using this Canvas.
|
||||
current = this;
|
||||
|
||||
// bind the framebuffer object.
|
||||
systemViewport = gl.getViewport();
|
||||
strategy->bindFBO(fbo);
|
||||
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
|
||||
|
||||
@@ -518,8 +714,14 @@ void Canvas::setupGrab()
|
||||
// Switch back to modelview matrix
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
// indicate we are using this fbo
|
||||
current = this;
|
||||
// Make sure the correct sRGB setting is used when drawing to the canvas.
|
||||
if (format == FORMAT_SRGB)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
else if (screenHasSRGB)
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
if (fsaa_buffer != 0)
|
||||
fsaa_dirty = true;
|
||||
}
|
||||
|
||||
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
||||
@@ -533,8 +735,11 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
||||
if (!isMultiCanvasSupported())
|
||||
throw love::Exception("Multi-canvas rendering is not supported on this system.");
|
||||
|
||||
if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments))
|
||||
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
|
||||
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
|
||||
|
||||
if (fsaa_samples != 0)
|
||||
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < canvases.size(); i++)
|
||||
@@ -542,8 +747,11 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
||||
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
|
||||
throw love::Exception("All canvas arguments must have the same dimensions.");
|
||||
|
||||
if (canvases[i]->getTextureType() != texture_type)
|
||||
throw love::Exception("All canvas arguments must have the same texture type.");
|
||||
if (canvases[i]->getTextureFormat() != format)
|
||||
throw love::Exception("All canvas arguments must have the same texture format.");
|
||||
|
||||
if (canvases[i]->getFSAA() != 0)
|
||||
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
|
||||
|
||||
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
|
||||
canvaseschanged = true;
|
||||
@@ -584,21 +792,33 @@ void Canvas::startGrab()
|
||||
attachedCanvases.clear();
|
||||
}
|
||||
|
||||
void Canvas::stopGrab()
|
||||
void Canvas::stopGrab(bool switchingToOtherCanvas)
|
||||
{
|
||||
// i am not grabbing. leave me alone
|
||||
if (current != this)
|
||||
return;
|
||||
|
||||
// bind default
|
||||
strategy->bindFBO(0);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
current = nullptr;
|
||||
if (switchingToOtherCanvas)
|
||||
{
|
||||
if (format == FORMAT_SRGB)
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
else
|
||||
{
|
||||
// bind system framebuffer.
|
||||
strategy->bindFBO(0);
|
||||
current = nullptr;
|
||||
gl.setViewport(systemViewport);
|
||||
|
||||
gl.setViewport(systemViewport);
|
||||
if (format == FORMAT_SRGB && !screenHasSRGB)
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
else if (format != FORMAT_SRGB && screenHasSRGB)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::clear(Color c)
|
||||
@@ -651,6 +871,9 @@ void Canvas::clear(Color c)
|
||||
|
||||
if (current != this)
|
||||
strategy->bindFBO(previous);
|
||||
|
||||
if (fsaa_buffer != 0)
|
||||
fsaa_dirty = true;
|
||||
}
|
||||
|
||||
bool Canvas::checkCreateStencil()
|
||||
@@ -662,7 +885,7 @@ bool Canvas::checkCreateStencil()
|
||||
if (current != this)
|
||||
strategy->bindFBO(fbo);
|
||||
|
||||
bool success = strategy->createStencil(width, height, depth_stencil);
|
||||
bool success = strategy->createStencil(width, height, fsaa_samples, depth_stencil);
|
||||
|
||||
if (current && current != this)
|
||||
strategy->bindFBO(current->fbo);
|
||||
@@ -674,12 +897,22 @@ bool Canvas::checkCreateStencil()
|
||||
|
||||
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
|
||||
{
|
||||
resolveMSAA();
|
||||
|
||||
int row = 4 * width;
|
||||
int size = row * height;
|
||||
GLubyte *pixels = new GLubyte[size];
|
||||
|
||||
strategy->bindFBO(fbo);
|
||||
// Our texture is attached to 'resolve_fbo' when we use MSAA.
|
||||
if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
|
||||
else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
|
||||
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
|
||||
else
|
||||
strategy->bindFBO(fbo);
|
||||
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
if (current)
|
||||
strategy->bindFBO(current->fbo);
|
||||
else
|
||||
@@ -692,10 +925,17 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
|
||||
|
||||
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
|
||||
{
|
||||
if (current != this)
|
||||
resolveMSAA();
|
||||
|
||||
// Our texture is attached to 'resolve_fbo' when we use MSAA.
|
||||
if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
|
||||
else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
|
||||
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
|
||||
else if (current != this)
|
||||
strategy->bindFBO(fbo);
|
||||
|
||||
glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
|
||||
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
|
||||
|
||||
if (current && current != this)
|
||||
strategy->bindFBO(current->fbo);
|
||||
@@ -703,6 +943,44 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
|
||||
strategy->bindFBO(0);
|
||||
}
|
||||
|
||||
bool Canvas::resolveMSAA()
|
||||
{
|
||||
if (resolve_fbo == 0 || fsaa_buffer == 0)
|
||||
return false;
|
||||
|
||||
if (!fsaa_dirty)
|
||||
return true;
|
||||
|
||||
GLuint previous = 0;
|
||||
if (current != nullptr)
|
||||
previous = current->fbo;
|
||||
|
||||
// Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
|
||||
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
else if (GLEE_EXT_framebuffer_multisample && GLEE_EXT_framebuffer_blit)
|
||||
{
|
||||
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo);
|
||||
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
||||
glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
strategy->bindFBO(previous);
|
||||
|
||||
if (current != this)
|
||||
fsaa_dirty = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Canvas::isSupported()
|
||||
{
|
||||
getStrategy();
|
||||
@@ -714,19 +992,22 @@ bool Canvas::isHDRSupported()
|
||||
return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
|
||||
}
|
||||
|
||||
bool Canvas::isMultiCanvasSupported()
|
||||
bool Canvas::isSRGBSupported()
|
||||
{
|
||||
if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers)))
|
||||
if (GLEE_VERSION_3_0)
|
||||
return true;
|
||||
|
||||
if (!isSupported())
|
||||
return false;
|
||||
|
||||
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
|
||||
{
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
||||
}
|
||||
return (GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB)
|
||||
&& GLEE_EXT_texture_sRGB;
|
||||
}
|
||||
|
||||
// system must support at least 4 simultanious active canvases
|
||||
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
|
||||
bool Canvas::isMultiCanvasSupported()
|
||||
{
|
||||
// system must support at least 4 simultanious active canvases.
|
||||
return gl.getMaxRenderTargets() >= 4;
|
||||
}
|
||||
|
||||
void Canvas::bindDefaultCanvas()
|
||||
@@ -735,23 +1016,6 @@ void Canvas::bindDefaultCanvas()
|
||||
current->stopGrab();
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(const char *in, Canvas::TextureType &out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(Canvas::TextureType in, const char *&out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM>::Entry Canvas::textureTypeEntries[] =
|
||||
{
|
||||
{"normal", Canvas::TYPE_NORMAL},
|
||||
{"hdr", Canvas::TYPE_HDR},
|
||||
};
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM> Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries));
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -39,14 +39,7 @@ class Canvas : public Texture
|
||||
{
|
||||
public:
|
||||
|
||||
enum TextureType
|
||||
{
|
||||
TYPE_NORMAL,
|
||||
TYPE_HDR,
|
||||
TYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
|
||||
Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
|
||||
virtual ~Canvas();
|
||||
|
||||
// Implements Volatile.
|
||||
@@ -54,14 +47,14 @@ public:
|
||||
virtual void unloadVolatile();
|
||||
|
||||
// Implements Drawable.
|
||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
// Implements Texture.
|
||||
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
virtual void setFilter(const Texture::Filter &f);
|
||||
virtual void setWrap(const Texture::Wrap &w);
|
||||
virtual GLuint getGLTexture() const;
|
||||
virtual void predraw() const;
|
||||
virtual void predraw();
|
||||
|
||||
/**
|
||||
* @param canvases A list of other canvases to temporarily attach to this one,
|
||||
@@ -69,7 +62,7 @@ public:
|
||||
**/
|
||||
void startGrab(const std::vector<Canvas *> &canvases);
|
||||
void startGrab();
|
||||
void stopGrab();
|
||||
void stopGrab(bool switchingToOtherCanvas = false);
|
||||
|
||||
void clear(Color c);
|
||||
|
||||
@@ -92,42 +85,56 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
inline TextureType getTextureType() const
|
||||
inline Texture::Format getTextureFormat() const
|
||||
{
|
||||
return texture_type;
|
||||
return format;
|
||||
}
|
||||
|
||||
inline int getFSAA() const
|
||||
{
|
||||
return fsaa_samples;
|
||||
}
|
||||
|
||||
bool resolveMSAA();
|
||||
|
||||
static bool isSupported();
|
||||
static bool isHDRSupported();
|
||||
static bool isSRGBSupported();
|
||||
static bool isMultiCanvasSupported();
|
||||
|
||||
static bool getConstant(const char *in, TextureType &out);
|
||||
static bool getConstant(TextureType in, const char *&out);
|
||||
|
||||
static Canvas *current;
|
||||
static void bindDefaultCanvas();
|
||||
|
||||
// The viewport dimensions of the system (default) framebuffer.
|
||||
static OpenGL::Viewport systemViewport;
|
||||
|
||||
// Whether the main screen should have linear -> sRGB conversions enabled.
|
||||
static bool screenHasSRGB;
|
||||
|
||||
private:
|
||||
|
||||
bool createFSAAFBO(GLenum internalformat);
|
||||
|
||||
GLuint fbo;
|
||||
GLuint resolve_fbo;
|
||||
|
||||
GLuint texture;
|
||||
GLuint fsaa_buffer;
|
||||
GLuint depth_stencil;
|
||||
|
||||
TextureType texture_type;
|
||||
Format format;
|
||||
|
||||
GLenum status;
|
||||
|
||||
std::vector<Canvas *> attachedCanvases;
|
||||
|
||||
void setupGrab();
|
||||
void drawv(const Matrix &t, const Vertex *v) const;
|
||||
int fsaa_samples;
|
||||
bool fsaa_dirty;
|
||||
|
||||
static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
|
||||
static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;
|
||||
};
|
||||
void setupGrab();
|
||||
void drawv(const Matrix &t, const Vertex *v);
|
||||
|
||||
}; // Canvas
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
|
||||
@@ -46,6 +46,7 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
||||
, lineHeight(1)
|
||||
, mSpacing(1)
|
||||
, filter(filter)
|
||||
, useSpacesAsTab(false)
|
||||
{
|
||||
this->filter.mipmap = Texture::FILTER_NONE;
|
||||
|
||||
@@ -67,13 +68,16 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
|
||||
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
|
||||
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
|
||||
|
||||
love::font::GlyphData *gd = 0;
|
||||
love::font::GlyphData *gd = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
gd = r->getGlyphData(32);
|
||||
gd = r->getGlyphData(32); // Space character.
|
||||
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
|
||||
|
||||
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
|
||||
useSpacesAsTab = true;
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
catch (love::Exception &)
|
||||
@@ -172,7 +176,26 @@ void Font::createTexture()
|
||||
|
||||
Font::Glyph *Font::addGlyph(uint32 glyph)
|
||||
{
|
||||
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
|
||||
love::font::GlyphData *gd = nullptr;
|
||||
|
||||
// Use spaces for the tab 'glyph'.
|
||||
if (glyph == 9 && useSpacesAsTab)
|
||||
{
|
||||
love::font::GlyphData *spacegd = rasterizer->getGlyphData(32);
|
||||
|
||||
love::font::GlyphMetrics gm = {};
|
||||
gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
|
||||
gm.bearingX = spacegd->getBearingX();
|
||||
gm.bearingY = spacegd->getBearingY();
|
||||
love::font::GlyphData::Format f = spacegd->getFormat();
|
||||
|
||||
spacegd->release();
|
||||
|
||||
gd = new love::font::GlyphData(glyph, gm, f);
|
||||
}
|
||||
else
|
||||
gd = rasterizer->getGlyphData(glyph);
|
||||
|
||||
int w = gd->getWidth();
|
||||
int h = gd->getHeight();
|
||||
|
||||
@@ -186,7 +209,15 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
|
||||
if (textureY + h + TEXTURE_PADDING > textureHeight)
|
||||
{
|
||||
// totally out of space - new texture!
|
||||
createTexture();
|
||||
try
|
||||
{
|
||||
createTexture();
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
gd->release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Glyph *g = new Glyph;
|
||||
|
||||
@@ -175,7 +175,7 @@ private:
|
||||
return texture < other.texture;
|
||||
else
|
||||
return startvertex < other.startvertex;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
bool initializeTexture(GLenum format);
|
||||
@@ -202,14 +202,19 @@ private:
|
||||
FontType type;
|
||||
Texture::Filter filter;
|
||||
|
||||
int textureX, textureY;
|
||||
int rowHeight;
|
||||
|
||||
bool useSpacesAsTab;
|
||||
|
||||
static const int NUM_TEXTURE_SIZES = 7;
|
||||
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
|
||||
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
|
||||
|
||||
static const int TEXTURE_PADDING = 1;
|
||||
|
||||
int textureX, textureY;
|
||||
int rowHeight;
|
||||
// This will be used if the Rasterizer doesn't have a tab character itself.
|
||||
static const int SPACES_PER_TAB = 4;
|
||||
|
||||
}; // Font
|
||||
|
||||
|
||||
@@ -53,12 +53,18 @@ Graphics::Graphics()
|
||||
, width(0)
|
||||
, height(0)
|
||||
, created(false)
|
||||
, activeStencil(false)
|
||||
, savedState()
|
||||
{
|
||||
currentWindow = love::window::sdl::Window::createSingleton();
|
||||
|
||||
int w, h;
|
||||
love::window::WindowSettings wsettings;
|
||||
|
||||
currentWindow->getWindow(w, h, wsettings);
|
||||
|
||||
if (currentWindow->isCreated())
|
||||
setMode(currentWindow->getWidth(), currentWindow->getHeight());
|
||||
setMode(w, h, wsettings.sRGB);
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
@@ -95,6 +101,8 @@ DisplayState Graphics::saveState()
|
||||
for (int i = 0; i < 4; i++)
|
||||
s.colorMask[i] = colorMask[i];
|
||||
|
||||
wireframe = isWireframe();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -111,6 +119,7 @@ void Graphics::restoreState(const DisplayState &s)
|
||||
else
|
||||
setScissor();
|
||||
setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]);
|
||||
setWireframe(s.wireframe);
|
||||
}
|
||||
|
||||
void Graphics::setViewportSize(int width, int height)
|
||||
@@ -147,7 +156,7 @@ void Graphics::setViewportSize(int width, int height)
|
||||
c->startGrab(c->getAttachedCanvases());
|
||||
}
|
||||
|
||||
bool Graphics::setMode(int width, int height)
|
||||
bool Graphics::setMode(int width, int height, bool &sRGB)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
@@ -166,9 +175,6 @@ bool Graphics::setMode(int width, int height)
|
||||
// Enable blending
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
// "Normal" blending
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Enable all color component writes.
|
||||
setColorMask(true, true, true, true);
|
||||
|
||||
@@ -207,6 +213,31 @@ bool Graphics::setMode(int width, int height)
|
||||
glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit);
|
||||
matrixLimit -= 5;
|
||||
|
||||
// Set whether drawing converts input from linear -> sRGB colorspace.
|
||||
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB)
|
||||
{
|
||||
if (sRGB)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
else
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
else
|
||||
sRGB = false;
|
||||
|
||||
Canvas::screenHasSRGB = sRGB;
|
||||
|
||||
bool enabledebug = false;
|
||||
|
||||
if (GLEE_VERSION_3_0)
|
||||
{
|
||||
// Enable OpenGL's debug output if a debug context has been created.
|
||||
GLint flags = 0;
|
||||
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
enabledebug = (flags & GL_CONTEXT_FLAG_DEBUG_BIT) != 0;
|
||||
}
|
||||
|
||||
setDebug(enabledebug);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -223,6 +254,61 @@ void Graphics::unSetMode()
|
||||
gl.deInitContext();
|
||||
}
|
||||
|
||||
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, GLvoid* /*usr*/)
|
||||
{
|
||||
// Human-readable strings for the debug info.
|
||||
const char *sourceStr = OpenGL::debugSourceString(source);
|
||||
const char *typeStr = OpenGL::debugTypeString(type);
|
||||
const char *severityStr = OpenGL::debugSeverityString(severity);
|
||||
|
||||
const char *fmt = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n";
|
||||
printf(fmt, msg, sourceStr, typeStr, severityStr, id);
|
||||
}
|
||||
|
||||
void Graphics::setDebug(bool enable)
|
||||
{
|
||||
// Make sure debug output is supported. The AMD ext. is a bit different
|
||||
// so we don't make use of it, since AMD drivers now support KHR_debug.
|
||||
if (!(GLEE_VERSION_4_3 || GLEE_KHR_debug || GLEE_ARB_debug_output))
|
||||
return;
|
||||
|
||||
// Ugly hack to reduce code duplication.
|
||||
if (GLEE_ARB_debug_output && !(GLEE_VERSION_4_3 || GLEE_KHR_debug))
|
||||
{
|
||||
glDebugMessageCallback = (GLEEPFNGLDEBUGMESSAGECALLBACKPROC) glDebugMessageCallbackARB;
|
||||
glDebugMessageControl = (GLEEPFNGLDEBUGMESSAGECONTROLPROC) glDebugMessageControlARB;
|
||||
}
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
// Disable the debug callback function.
|
||||
glDebugMessageCallback(nullptr, nullptr);
|
||||
|
||||
// We can disable debug output entirely with KHR_debug.
|
||||
if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
|
||||
glDisable(GL_DEBUG_OUTPUT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want asynchronous debug output.
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
|
||||
glDebugMessageCallback(debugCB, nullptr);
|
||||
|
||||
// Initially, enable everything.
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
|
||||
|
||||
// Disable messages about deprecated OpenGL functionality.
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
|
||||
|
||||
if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
|
||||
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
|
||||
}
|
||||
|
||||
void Graphics::reset()
|
||||
{
|
||||
DisplayState s;
|
||||
@@ -295,6 +381,8 @@ void Graphics::defineStencil()
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 1, 1);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
|
||||
activeStencil = true;
|
||||
}
|
||||
|
||||
void Graphics::useStencil(bool invert)
|
||||
@@ -306,19 +394,18 @@ void Graphics::useStencil(bool invert)
|
||||
|
||||
void Graphics::discardStencil()
|
||||
{
|
||||
if (!activeStencil)
|
||||
return;
|
||||
|
||||
setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
activeStencil = false;
|
||||
}
|
||||
|
||||
int Graphics::getMaxTextureSize() const
|
||||
{
|
||||
return gl.getMaxTextureSize();
|
||||
}
|
||||
|
||||
Image *Graphics::newImage(love::image::ImageData *data)
|
||||
Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)
|
||||
{
|
||||
// Create the image.
|
||||
Image *image = new Image(data);
|
||||
Image *image = new Image(data, format);
|
||||
|
||||
if (!isCreated())
|
||||
return image;
|
||||
@@ -342,10 +429,10 @@ Image *Graphics::newImage(love::image::ImageData *data)
|
||||
return image;
|
||||
}
|
||||
|
||||
Image *Graphics::newImage(love::image::CompressedData *cdata)
|
||||
Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format)
|
||||
{
|
||||
// Create the image.
|
||||
Image *image = new Image(cdata);
|
||||
Image *image = new Image(cdata, format);
|
||||
|
||||
if (!isCreated())
|
||||
return image;
|
||||
@@ -389,11 +476,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
|
||||
return new ParticleSystem(texture, size);
|
||||
}
|
||||
|
||||
Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type)
|
||||
Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa)
|
||||
{
|
||||
if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported())
|
||||
if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported())
|
||||
throw Exception("HDR Canvases are not supported by your OpenGL implementation");
|
||||
|
||||
if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported())
|
||||
throw Exception("sRGB Canvases are not supported by your OpenGL implementation");
|
||||
|
||||
if (width > gl.getMaxTextureSize())
|
||||
throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
|
||||
else if (height > gl.getMaxTextureSize())
|
||||
@@ -402,7 +492,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t
|
||||
while (GL_NO_ERROR != glGetError())
|
||||
/* clear opengl error flag */;
|
||||
|
||||
Canvas *canvas = new Canvas(width, height, texture_type);
|
||||
Canvas *canvas = new Canvas(width, height, format, fsaa);
|
||||
GLenum err = canvas->getStatus();
|
||||
|
||||
// everything ok, return canvas (early out)
|
||||
@@ -457,6 +547,11 @@ Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode
|
||||
return new Mesh(vertices, mode);
|
||||
}
|
||||
|
||||
Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode)
|
||||
{
|
||||
return new Mesh(vertexcount, mode);
|
||||
}
|
||||
|
||||
void Graphics::setColor(const Color &c)
|
||||
{
|
||||
gl.setColor(c);
|
||||
@@ -509,22 +604,16 @@ const bool *Graphics::getColorMask() const
|
||||
|
||||
void Graphics::setBlendMode(Graphics::BlendMode mode)
|
||||
{
|
||||
const int gl_1_4 = GLEE_VERSION_1_4;
|
||||
|
||||
GLenum func = GL_FUNC_ADD;
|
||||
GLenum src_rgb = GL_ONE;
|
||||
GLenum src_a = GL_ONE;
|
||||
GLenum dst_rgb = GL_ZERO;
|
||||
GLenum dst_a = GL_ZERO;
|
||||
OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case BLEND_ALPHA:
|
||||
if (gl_1_4 || GLEE_EXT_blend_func_separate)
|
||||
if (GLEE_VERSION_1_4 || GLEE_EXT_blend_func_separate)
|
||||
{
|
||||
src_rgb = GL_SRC_ALPHA;
|
||||
src_a = GL_ONE;
|
||||
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
|
||||
state.srcRGB = GL_SRC_ALPHA;
|
||||
state.srcA = GL_ONE;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -532,98 +621,56 @@ void Graphics::setBlendMode(Graphics::BlendMode mode)
|
||||
// This will most likely only be used for the Microsoft software renderer and
|
||||
// since it's still stuck with OpenGL 1.1, the only expected difference is a
|
||||
// different alpha value when reading back the default framebuffer (newScreenshot).
|
||||
src_rgb = src_a = GL_SRC_ALPHA;
|
||||
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
|
||||
state.srcRGB = state.srcA = GL_SRC_ALPHA;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
break;
|
||||
case BLEND_MULTIPLICATIVE:
|
||||
src_rgb = src_a = GL_DST_COLOR;
|
||||
dst_rgb = dst_a = GL_ZERO;
|
||||
state.srcRGB = state.srcA = GL_DST_COLOR;
|
||||
state.dstRGB = state.dstA = GL_ZERO;
|
||||
break;
|
||||
case BLEND_PREMULTIPLIED:
|
||||
src_rgb = src_a = GL_ONE;
|
||||
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
|
||||
state.srcRGB = state.srcA = GL_ONE;
|
||||
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
case BLEND_SUBTRACTIVE:
|
||||
func = GL_FUNC_REVERSE_SUBTRACT;
|
||||
state.func = GL_FUNC_REVERSE_SUBTRACT;
|
||||
case BLEND_ADDITIVE:
|
||||
src_rgb = src_a = GL_SRC_ALPHA;
|
||||
dst_rgb = dst_a = GL_ONE;
|
||||
state.srcRGB = state.srcA = GL_SRC_ALPHA;
|
||||
state.dstRGB = state.dstA = GL_ONE;
|
||||
break;
|
||||
case BLEND_REPLACE:
|
||||
default:
|
||||
src_rgb = src_a = GL_ONE;
|
||||
dst_rgb = dst_a = GL_ZERO;
|
||||
state.srcRGB = state.srcA = GL_ONE;
|
||||
state.dstRGB = state.dstA = GL_ZERO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (gl_1_4 || GLEE_ARB_imaging)
|
||||
glBlendEquation(func);
|
||||
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
|
||||
glBlendEquationEXT(func);
|
||||
else
|
||||
{
|
||||
if (func == GL_FUNC_REVERSE_SUBTRACT)
|
||||
throw Exception("This graphics card does not support the subtractive blend mode!");
|
||||
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
|
||||
}
|
||||
|
||||
if (src_rgb == src_a && dst_rgb == dst_a)
|
||||
glBlendFunc(src_rgb, dst_rgb);
|
||||
else
|
||||
{
|
||||
if (gl_1_4)
|
||||
glBlendFuncSeparate(src_rgb, dst_rgb, src_a, dst_a);
|
||||
else if (GLEE_EXT_blend_func_separate)
|
||||
glBlendFuncSeparateEXT(src_rgb, dst_rgb, src_a, dst_a);
|
||||
else
|
||||
throw Exception("This graphics card does not support separated rgb and alpha blend functions!");
|
||||
}
|
||||
gl.setBlendState(state);
|
||||
}
|
||||
|
||||
Graphics::BlendMode Graphics::getBlendMode() const
|
||||
{
|
||||
const int gl_1_4 = GLEE_VERSION_1_4;
|
||||
OpenGL::BlendState state = gl.getBlendState();
|
||||
|
||||
GLint src_rgb, src_a, dst_rgb, dst_a;
|
||||
GLint equation = GL_FUNC_ADD;
|
||||
|
||||
if (gl_1_4 || GLEE_EXT_blend_func_separate)
|
||||
{
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_a);
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_a);
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetIntegerv(GL_BLEND_SRC, &src_rgb);
|
||||
glGetIntegerv(GL_BLEND_DST, &dst_rgb);
|
||||
src_a = src_rgb;
|
||||
dst_a = dst_rgb;
|
||||
}
|
||||
|
||||
if (gl_1_4 || GLEE_ARB_imaging || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract))
|
||||
glGetIntegerv(GL_BLEND_EQUATION, &equation);
|
||||
|
||||
if (equation == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
|
||||
if (state.func == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
|
||||
return BLEND_SUBTRACTIVE;
|
||||
// Everything else has equation == GL_FUNC_ADD.
|
||||
else if (src_rgb == src_a && dst_rgb == dst_a)
|
||||
else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA)
|
||||
{
|
||||
if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE)
|
||||
if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE)
|
||||
return BLEND_ADDITIVE;
|
||||
else if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
|
||||
else if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA)
|
||||
return BLEND_ALPHA; // alpha blend mode fallback for very old OpenGL versions.
|
||||
else if (src_rgb == GL_DST_COLOR && dst_rgb == GL_ZERO)
|
||||
else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO)
|
||||
return BLEND_MULTIPLICATIVE;
|
||||
else if (src_rgb == GL_ONE && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
|
||||
else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA)
|
||||
return BLEND_PREMULTIPLIED;
|
||||
else if (src_rgb == GL_ONE && dst_rgb == GL_ZERO)
|
||||
else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO)
|
||||
return BLEND_REPLACE;
|
||||
}
|
||||
else if (src_rgb == GL_SRC_ALPHA && src_a == GL_ONE &&
|
||||
dst_rgb == GL_ONE_MINUS_SRC_ALPHA && dst_a == GL_ONE_MINUS_SRC_ALPHA)
|
||||
else if (state.srcRGB == GL_SRC_ALPHA && state.srcA == GL_ONE &&
|
||||
state.dstRGB == GL_ONE_MINUS_SRC_ALPHA && state.dstA == GL_ONE_MINUS_SRC_ALPHA)
|
||||
return BLEND_ALPHA;
|
||||
|
||||
throw Exception("Unknown blend mode");
|
||||
@@ -693,11 +740,15 @@ float Graphics::getPointSize() const
|
||||
return (float)size;
|
||||
}
|
||||
|
||||
int Graphics::getMaxPointSize() const
|
||||
void Graphics::setWireframe(bool enable)
|
||||
{
|
||||
GLint max;
|
||||
glGetIntegerv(GL_POINT_SIZE_MAX, &max);
|
||||
return (int)max;
|
||||
wireframe = enable;
|
||||
glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
|
||||
}
|
||||
|
||||
bool Graphics::isWireframe() const
|
||||
{
|
||||
return 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)
|
||||
@@ -1007,6 +1058,41 @@ std::string Graphics::getRendererInfo(Graphics::RendererInfo infotype) const
|
||||
return std::string(infostr);
|
||||
}
|
||||
|
||||
double Graphics::getSystemLimit(SystemLimit limittype) const
|
||||
{
|
||||
double limit = 0.0;
|
||||
|
||||
switch (limittype)
|
||||
{
|
||||
case Graphics::LIMIT_POINT_SIZE:
|
||||
{
|
||||
GLfloat limits[2];
|
||||
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits);
|
||||
limit = limits[1];
|
||||
}
|
||||
break;
|
||||
case Graphics::LIMIT_TEXTURE_SIZE:
|
||||
limit = (double) gl.getMaxTextureSize();
|
||||
break;
|
||||
case Graphics::LIMIT_MULTI_CANVAS:
|
||||
limit = (double) gl.getMaxRenderTargets();
|
||||
break;
|
||||
case Graphics::LIMIT_CANVAS_FSAA:
|
||||
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
|
||||
|| GLEE_EXT_framebuffer_multisample)
|
||||
{
|
||||
GLint intlimit = 0;
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &intlimit);
|
||||
limit = (double) intlimit;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
void Graphics::push()
|
||||
{
|
||||
if (userMatrices == matrixLimit)
|
||||
|
||||
@@ -80,6 +80,8 @@ struct DisplayState
|
||||
// Color mask.
|
||||
bool colorMask[4];
|
||||
|
||||
bool wireframe;
|
||||
|
||||
// Default values.
|
||||
DisplayState()
|
||||
{
|
||||
@@ -91,6 +93,7 @@ struct DisplayState
|
||||
pointSize = 1.0f;
|
||||
scissor = false;
|
||||
colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true;
|
||||
wireframe = false;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -110,9 +113,11 @@ public:
|
||||
void restoreState(const DisplayState &s);
|
||||
|
||||
virtual void setViewportSize(int width, int height);
|
||||
virtual bool setMode(int width, int height);
|
||||
virtual bool setMode(int width, int height, bool &sRGB);
|
||||
virtual void unSetMode();
|
||||
|
||||
void setDebug(bool enable);
|
||||
|
||||
/**
|
||||
* Resets the current color, background color,
|
||||
* line style, and so forth. (This will be called
|
||||
@@ -184,16 +189,11 @@ public:
|
||||
*/
|
||||
void discardStencil();
|
||||
|
||||
/**
|
||||
* Gets the maximum supported width or height of Textures on this system.
|
||||
**/
|
||||
int getMaxTextureSize() const;
|
||||
|
||||
/**
|
||||
* Creates an Image object with padding and/or optimization.
|
||||
**/
|
||||
Image *newImage(love::image::ImageData *data);
|
||||
Image *newImage(love::image::CompressedData *cdata);
|
||||
Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
|
||||
Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
|
||||
|
||||
Quad *newQuad(Quad::Viewport v, float sw, float sh);
|
||||
|
||||
@@ -206,11 +206,12 @@ public:
|
||||
|
||||
ParticleSystem *newParticleSystem(Texture *texture, int size);
|
||||
|
||||
Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
|
||||
Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
|
||||
|
||||
Shader *newShader(const Shader::ShaderSources &sources);
|
||||
|
||||
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
|
||||
Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
|
||||
|
||||
/**
|
||||
* Sets the foreground color.
|
||||
@@ -324,10 +325,17 @@ public:
|
||||
float getPointSize() const;
|
||||
|
||||
/**
|
||||
* Gets the maximum point size supported.
|
||||
* This may vary from computer to computer.
|
||||
* Sets whether graphics will be drawn as wireframe lines instead of filled
|
||||
* triangles (has no effect for drawn points.)
|
||||
* This should only be used as a debugging tool. The wireframe lines do not
|
||||
* behave the same as regular love.graphics lines.
|
||||
**/
|
||||
int getMaxPointSize() const;
|
||||
void setWireframe(bool enable);
|
||||
|
||||
/**
|
||||
* Gets whether wireframe drawing mode is enabled.
|
||||
**/
|
||||
bool isWireframe() const;
|
||||
|
||||
/**
|
||||
* Draws text at the specified coordinates, with rotation and
|
||||
@@ -430,6 +438,11 @@ public:
|
||||
**/
|
||||
std::string getRendererInfo(Graphics::RendererInfo infotype) const;
|
||||
|
||||
/**
|
||||
* Gets the system-dependent numeric limit for the specified parameter.
|
||||
**/
|
||||
double getSystemLimit(SystemLimit limittype) const;
|
||||
|
||||
void push();
|
||||
void pop();
|
||||
void rotate(float r);
|
||||
@@ -450,11 +463,14 @@ private:
|
||||
GLint matrixLimit;
|
||||
GLint userMatrices;
|
||||
bool colorMask[4];
|
||||
bool wireframe;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
bool created;
|
||||
|
||||
bool activeStencil;
|
||||
|
||||
DisplayState savedState;
|
||||
|
||||
}; // Graphics
|
||||
|
||||
@@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f;
|
||||
Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
|
||||
float Image::defaultMipmapSharpness = 0.0f;
|
||||
|
||||
Image::Image(love::image::ImageData *data)
|
||||
Image::Image(love::image::ImageData *data, Texture::Format format)
|
||||
: data(data)
|
||||
, cdata(nullptr)
|
||||
, paddedWidth(width)
|
||||
@@ -45,6 +45,7 @@ Image::Image(love::image::ImageData *data)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, mipmapsCreated(false)
|
||||
, compressed(false)
|
||||
, format(format)
|
||||
, usingDefaultTexture(false)
|
||||
{
|
||||
width = data->getWidth();
|
||||
@@ -54,7 +55,7 @@ Image::Image(love::image::ImageData *data)
|
||||
preload();
|
||||
}
|
||||
|
||||
Image::Image(love::image::CompressedData *cdata)
|
||||
Image::Image(love::image::CompressedData *cdata, Texture::Format format)
|
||||
: data(nullptr)
|
||||
, cdata(cdata)
|
||||
, paddedWidth(width)
|
||||
@@ -63,6 +64,7 @@ Image::Image(love::image::CompressedData *cdata)
|
||||
, mipmapSharpness(defaultMipmapSharpness)
|
||||
, mipmapsCreated(false)
|
||||
, compressed(true)
|
||||
, format(format)
|
||||
, usingDefaultTexture(false)
|
||||
{
|
||||
width = cdata->getWidth(0);
|
||||
@@ -91,7 +93,7 @@ love::image::CompressedData *Image::getCompressedData() const
|
||||
return cdata;
|
||||
}
|
||||
|
||||
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
@@ -99,7 +101,7 @@ void Image::draw(float x, float y, float angle, float sx, float sy, float ox, fl
|
||||
drawv(t, vertices);
|
||||
}
|
||||
|
||||
void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
@@ -107,7 +109,7 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy,
|
||||
drawv(t, quad->getVertices());
|
||||
}
|
||||
|
||||
void Image::predraw() const
|
||||
void Image::predraw()
|
||||
{
|
||||
bind();
|
||||
|
||||
@@ -121,7 +123,7 @@ void Image::predraw() const
|
||||
}
|
||||
}
|
||||
|
||||
void Image::postdraw() const
|
||||
void Image::postdraw()
|
||||
{
|
||||
if (width != paddedWidth || height != paddedHeight)
|
||||
{
|
||||
@@ -328,6 +330,9 @@ void Image::unload()
|
||||
|
||||
bool Image::loadVolatile()
|
||||
{
|
||||
if (format == FORMAT_SRGB && !hasSRGBSupport())
|
||||
throw love::Exception("sRGB images are not supported on this system.");
|
||||
|
||||
if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
|
||||
{
|
||||
const char *str;
|
||||
@@ -400,9 +405,10 @@ void Image::uploadTexturePadded()
|
||||
}
|
||||
else if (data)
|
||||
{
|
||||
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
iformat,
|
||||
(GLsizei)paddedWidth,
|
||||
(GLsizei)paddedHeight,
|
||||
0,
|
||||
@@ -437,9 +443,10 @@ void Image::uploadTexture()
|
||||
}
|
||||
else if (data)
|
||||
{
|
||||
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
iformat,
|
||||
(GLsizei)width,
|
||||
(GLsizei)height,
|
||||
0,
|
||||
@@ -497,6 +504,11 @@ bool Image::refresh()
|
||||
return true;
|
||||
}
|
||||
|
||||
Texture::Format Image::getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
void Image::uploadDefaultTexture()
|
||||
{
|
||||
usingDefaultTexture = true;
|
||||
@@ -511,7 +523,7 @@ void Image::uploadDefaultTexture()
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
|
||||
}
|
||||
|
||||
void Image::drawv(const Matrix &t, const Vertex *v) const
|
||||
void Image::drawv(const Matrix &t, const Vertex *v)
|
||||
{
|
||||
predraw();
|
||||
|
||||
@@ -561,16 +573,27 @@ bool Image::isCompressed() const
|
||||
return compressed;
|
||||
}
|
||||
|
||||
GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
|
||||
GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
|
||||
{
|
||||
switch (format)
|
||||
bool srgb = format == FORMAT_SRGB;
|
||||
|
||||
switch (cformat)
|
||||
{
|
||||
case image::CompressedData::FORMAT_DXT1:
|
||||
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
if (srgb)
|
||||
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
case image::CompressedData::FORMAT_DXT3:
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
if (srgb)
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
case image::CompressedData::FORMAT_DXT5:
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
if (srgb)
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
else
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
case image::CompressedData::FORMAT_BC4:
|
||||
return GL_COMPRESSED_RED_RGTC1;
|
||||
case image::CompressedData::FORMAT_BC4s:
|
||||
@@ -580,7 +603,10 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
|
||||
case image::CompressedData::FORMAT_BC5s:
|
||||
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
||||
default:
|
||||
return GL_RGBA8;
|
||||
if (srgb)
|
||||
return GL_SRGB8_ALPHA8;
|
||||
else
|
||||
return GL_RGBA8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,6 +658,11 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Image::hasSRGBSupport()
|
||||
{
|
||||
return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB;
|
||||
}
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
@@ -56,14 +56,14 @@ public:
|
||||
*
|
||||
* @param data The data from which to load the image.
|
||||
**/
|
||||
Image(love::image::ImageData *data);
|
||||
Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
|
||||
|
||||
/**
|
||||
* Creates a new Image with compressed image data.
|
||||
*
|
||||
* @param cdata The compressed data from which to load the image.
|
||||
**/
|
||||
Image(love::image::CompressedData *cdata);
|
||||
Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
|
||||
|
||||
/**
|
||||
* Destructor. Deletes the hardware texture and other resources.
|
||||
@@ -76,20 +76,20 @@ public:
|
||||
/**
|
||||
* @copydoc Drawable::draw()
|
||||
**/
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
/**
|
||||
* @copydoc Texture::drawq()
|
||||
**/
|
||||
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
/**
|
||||
* Call before using this Image's texture to draw. Binds the texture,
|
||||
* globally scales texture coordinates if the Image has NPOT dimensions and
|
||||
* NPOT isn't supported, etc.
|
||||
**/
|
||||
virtual void predraw() const;
|
||||
virtual void postdraw() const;
|
||||
virtual void predraw();
|
||||
virtual void postdraw();
|
||||
|
||||
virtual GLuint getGLTexture() const;
|
||||
|
||||
@@ -120,6 +120,8 @@ public:
|
||||
**/
|
||||
bool refresh();
|
||||
|
||||
Texture::Format getFormat() const;
|
||||
|
||||
static void setDefaultMipmapSharpness(float sharpness);
|
||||
static float getDefaultMipmapSharpness();
|
||||
static void setDefaultMipmapFilter(FilterMode f);
|
||||
@@ -133,11 +135,13 @@ public:
|
||||
static bool hasCompressedTextureSupport();
|
||||
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
|
||||
|
||||
static bool hasSRGBSupport();
|
||||
|
||||
private:
|
||||
|
||||
void uploadDefaultTexture();
|
||||
|
||||
void drawv(const Matrix &t, const Vertex *v) const;
|
||||
void drawv(const Matrix &t, const Vertex *v);
|
||||
|
||||
// The ImageData from which the texture is created. May be null if
|
||||
// Compressed image data was used to create the texture.
|
||||
@@ -162,6 +166,9 @@ private:
|
||||
// Whether this Image is using a compressed texture.
|
||||
bool compressed;
|
||||
|
||||
// The format to interpret the texture's data as.
|
||||
Texture::Format format;
|
||||
|
||||
// True if the image wasn't able to be properly created and it had to fall
|
||||
// back to a default texture.
|
||||
bool usingDefaultTexture;
|
||||
@@ -180,7 +187,7 @@ private:
|
||||
static FilterMode defaultMipmapFilter;
|
||||
static float defaultMipmapSharpness;
|
||||
|
||||
GLenum getCompressedFormat(image::CompressedData::Format format) const;
|
||||
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
|
||||
|
||||
}; // Image
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#include "common/Matrix.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
@@ -35,14 +38,46 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
|
||||
, vertex_count(0)
|
||||
, ibo(nullptr)
|
||||
, element_count(0)
|
||||
, element_data_type(getGLDataTypeFromMax(verts.size()))
|
||||
, instance_count(1)
|
||||
, draw_mode(mode)
|
||||
, range_min(-1)
|
||||
, range_max(-1)
|
||||
, texture(nullptr)
|
||||
, colors_enabled(false)
|
||||
, wireframe(false)
|
||||
{
|
||||
setVertices(verts);
|
||||
}
|
||||
|
||||
Mesh::Mesh(int vertexcount, Mesh::DrawMode mode)
|
||||
: vbo(nullptr)
|
||||
, vertex_count(0)
|
||||
, ibo(nullptr)
|
||||
, element_count(0)
|
||||
, element_data_type(getGLDataTypeFromMax(vertexcount))
|
||||
, draw_mode(mode)
|
||||
, range_min(-1)
|
||||
, range_max(-1)
|
||||
, texture(nullptr)
|
||||
, colors_enabled(false)
|
||||
{
|
||||
if (vertexcount < 1)
|
||||
throw love::Exception("Invalid number of vertices.");
|
||||
|
||||
std::vector<Vertex> verts(vertexcount);
|
||||
|
||||
// Default-initialized vertices should have a white opaque color.
|
||||
for (size_t i = 0; i < verts.size(); i++)
|
||||
{
|
||||
verts[i].r = 255;
|
||||
verts[i].g = 255;
|
||||
verts[i].b = 255;
|
||||
verts[i].a = 255;
|
||||
}
|
||||
|
||||
setVertices(verts);
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
delete vbo;
|
||||
@@ -51,8 +86,8 @@ Mesh::~Mesh()
|
||||
|
||||
void Mesh::setVertices(const std::vector<Vertex> &verts)
|
||||
{
|
||||
if (verts.size() < 3)
|
||||
throw love::Exception("At least 3 vertices are required.");
|
||||
if (verts.size() == 0)
|
||||
throw love::Exception("At least one vertex is required.");
|
||||
|
||||
size_t size = sizeof(Vertex) * verts.size();
|
||||
|
||||
@@ -118,15 +153,42 @@ size_t Mesh::getVertexCount() const
|
||||
return vertex_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies index data from a vector to a mapped index buffer.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::Mapper &buffermap, size_t maxval)
|
||||
{
|
||||
T *elems = (T *) buffermap.get();
|
||||
|
||||
for (size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
if (indices[i] >= maxval)
|
||||
throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
|
||||
|
||||
elems[i] = (T) indices[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
{
|
||||
for (size_t i = 0; i < map.size(); i++)
|
||||
{
|
||||
if (map[i] >= vertex_count)
|
||||
throw love::Exception("Invalid vertex map value: %d", map[i] + 1);
|
||||
}
|
||||
GLenum datatype = getGLDataTypeFromMax(vertex_count);
|
||||
|
||||
size_t size = sizeof(uint32) * map.size();
|
||||
// Calculate the size in bytes of the index buffer data.
|
||||
size_t size = map.size();
|
||||
switch (datatype)
|
||||
{
|
||||
case GL_UNSIGNED_BYTE:
|
||||
size *= sizeof(uint8);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
size *= sizeof(uint16);
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
default:
|
||||
size *= sizeof(uint32);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ibo && size > ibo->getSize())
|
||||
{
|
||||
@@ -142,27 +204,68 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
|
||||
|
||||
element_count = map.size();
|
||||
|
||||
if (ibo && element_count > 0)
|
||||
{
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
VertexBuffer::Mapper ibo_map(*ibo);
|
||||
if (!ibo || element_count == 0)
|
||||
return;
|
||||
|
||||
// Fill the buffer.
|
||||
memcpy(ibo_map.get(), &map[0], size);
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
VertexBuffer::Mapper ibo_map(*ibo);
|
||||
|
||||
// Fill the buffer with the index values from the vector.
|
||||
switch (datatype)
|
||||
{
|
||||
case GL_UNSIGNED_BYTE:
|
||||
copyToIndexBuffer<uint8>(map, ibo_map, vertex_count);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
copyToIndexBuffer<uint16>(map, ibo_map, vertex_count);
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
default:
|
||||
copyToIndexBuffer<uint32>(map, ibo_map, vertex_count);
|
||||
break;
|
||||
}
|
||||
|
||||
element_data_type = datatype;
|
||||
}
|
||||
|
||||
const uint32 *Mesh::getVertexMap() const
|
||||
/**
|
||||
* Copies index data from a mapped buffer to a vector.
|
||||
**/
|
||||
template <typename T>
|
||||
static void copyFromIndexBuffer(void *buffer, std::vector<uint32> &indices, size_t maxval)
|
||||
{
|
||||
if (ibo && element_count > 0)
|
||||
T *elems = (T *) buffer;
|
||||
for (size_t i = 0; i < maxval; i++)
|
||||
indices.push_back((uint32) elems[i]);
|
||||
}
|
||||
|
||||
void Mesh::getVertexMap(std::vector<uint32> &map) const
|
||||
{
|
||||
if (!ibo || element_count == 0)
|
||||
return;
|
||||
|
||||
map.clear();
|
||||
map.reserve(element_count);
|
||||
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
|
||||
void *buffer = ibo->map();
|
||||
|
||||
// Fill the vector from the buffer.
|
||||
switch (element_data_type)
|
||||
{
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
|
||||
return (uint32 *) ibo->map();
|
||||
case GL_UNSIGNED_BYTE:
|
||||
copyFromIndexBuffer<uint8>(buffer, map, vertex_count);
|
||||
break;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
copyFromIndexBuffer<uint16>(buffer, map, vertex_count);
|
||||
break;
|
||||
case GL_UNSIGNED_INT:
|
||||
default:
|
||||
copyFromIndexBuffer<uint32>(buffer, map, vertex_count);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Mesh::getVertexMapCount() const
|
||||
@@ -170,6 +273,16 @@ size_t Mesh::getVertexMapCount() const
|
||||
return element_count;
|
||||
}
|
||||
|
||||
void Mesh::setInstanceCount(int count)
|
||||
{
|
||||
instance_count = std::max(count, 1);
|
||||
}
|
||||
|
||||
int Mesh::getInstanceCount() const
|
||||
{
|
||||
return instance_count;
|
||||
}
|
||||
|
||||
void Mesh::setTexture(Texture *tex)
|
||||
{
|
||||
tex->retain();
|
||||
@@ -203,6 +316,26 @@ Mesh::DrawMode Mesh::getDrawMode() const
|
||||
return draw_mode;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange(int min, int max)
|
||||
{
|
||||
if (min < 0 || max < 0 || min > max)
|
||||
throw love::Exception("Invalid draw range.");
|
||||
|
||||
range_min = min;
|
||||
range_max = max;
|
||||
}
|
||||
|
||||
void Mesh::setDrawRange()
|
||||
{
|
||||
range_min = range_max = -1;
|
||||
}
|
||||
|
||||
void Mesh::getDrawRange(int &min, int &max) const
|
||||
{
|
||||
min = range_min;
|
||||
max = range_max;
|
||||
}
|
||||
|
||||
void Mesh::setVertexColors(bool enable)
|
||||
{
|
||||
colors_enabled = enable;
|
||||
@@ -213,17 +346,7 @@ bool Mesh::hasVertexColors() const
|
||||
return colors_enabled;
|
||||
}
|
||||
|
||||
void Mesh::setWireframe(bool enable)
|
||||
{
|
||||
wireframe = enable;
|
||||
}
|
||||
|
||||
bool Mesh::isWireframe() const
|
||||
{
|
||||
return wireframe;
|
||||
}
|
||||
|
||||
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
const size_t pos_offset = offsetof(Vertex, x);
|
||||
const size_t tex_offset = offsetof(Vertex, s);
|
||||
@@ -261,31 +384,50 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
|
||||
}
|
||||
|
||||
if (wireframe)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
|
||||
GLenum mode = getGLDrawMode(draw_mode);
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
if (ibo && element_count > 0)
|
||||
{
|
||||
// Use the custom vertex map (index buffer) to draw the vertices.
|
||||
VertexBuffer::Bind ibo_bind(*ibo);
|
||||
|
||||
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
|
||||
ibo->unmap();
|
||||
|
||||
// Use the custom vertex map to draw the vertices.
|
||||
glDrawElements(mode, element_count, GL_UNSIGNED_INT, ibo->getPointer(0));
|
||||
int max = element_count - 1;
|
||||
if (range_max >= 0)
|
||||
max = std::min(std::max(range_max, 0), (int) element_count - 1);
|
||||
|
||||
int min = 0;
|
||||
if (range_min >= 0)
|
||||
min = std::min(std::max(range_min, 0), max);
|
||||
|
||||
const void *indices = ibo->getPointer(min * sizeof(uint32));
|
||||
GLenum type = element_data_type;
|
||||
|
||||
if (instance_count > 1)
|
||||
gl.drawElementsInstanced(mode, max - min + 1, type, indices, instance_count);
|
||||
else
|
||||
glDrawElements(mode, max - min + 1, type, indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal non-indexed drawing (no custom vertex map.)
|
||||
glDrawArrays(mode, 0, vertex_count);
|
||||
}
|
||||
int max = vertex_count - 1;
|
||||
if (range_max >= 0)
|
||||
max = std::min(std::max(range_max, 0), (int) vertex_count - 1);
|
||||
|
||||
if (wireframe)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
int min = 0;
|
||||
if (range_min >= 0)
|
||||
min = std::min(std::max(range_min, 0), max);
|
||||
|
||||
// Normal non-indexed drawing (no custom vertex map.)
|
||||
if (instance_count > 1)
|
||||
gl.drawArraysInstanced(mode, min, max - min + 1, instance_count);
|
||||
else
|
||||
glDrawArrays(mode, min, max - min + 1);
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
@@ -303,7 +445,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
|
||||
texture->postdraw();
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
|
||||
GLenum Mesh::getGLDrawMode(DrawMode mode) const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
@@ -322,6 +464,16 @@ GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
|
||||
return GL_TRIANGLES;
|
||||
}
|
||||
|
||||
GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
|
||||
{
|
||||
if (maxvalue > LOVE_UINT16_MAX)
|
||||
return GL_UNSIGNED_INT;
|
||||
else if (maxvalue > LOVE_UINT8_MAX)
|
||||
return GL_UNSIGNED_SHORT;
|
||||
else
|
||||
return GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
|
||||
{
|
||||
return drawModes.find(in, out);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define LOVE_GRAPHICS_OPENGL_MESH_H
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "common/int.h"
|
||||
#include "common/math.h"
|
||||
#include "common/StringMap.h"
|
||||
@@ -64,6 +65,16 @@ public:
|
||||
* @param mode The draw mode to use when drawing the Mesh.
|
||||
**/
|
||||
Mesh(const std::vector<Vertex> &verts, DrawMode mode = DRAW_MODE_FAN);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Creates a Mesh with a certain number of default-initialized (hidden)
|
||||
* vertices.
|
||||
* @param vertexcount The number of vertices to use in the Mesh.
|
||||
* @param mode The draw mode to use when drawing the Mesh.
|
||||
**/
|
||||
Mesh(int vertexcount, DrawMode mode = DRAW_MODE_FAN);
|
||||
|
||||
virtual ~Mesh();
|
||||
|
||||
/**
|
||||
@@ -98,17 +109,25 @@ public:
|
||||
void setVertexMap(const std::vector<uint32> &map);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the vertex map array. The pointer is only valid until
|
||||
* the next function call in the graphics module.
|
||||
* May return null if the vertex map is empty.
|
||||
* Fills the uint32 vector passed into the method with the previously set
|
||||
* vertex map (index buffer) values.
|
||||
**/
|
||||
const uint32 *getVertexMap() const;
|
||||
void getVertexMap(std::vector<uint32> &map) const;
|
||||
|
||||
/**
|
||||
* Gets the total number of elements in the vertex map array.
|
||||
**/
|
||||
size_t getVertexMapCount() const;
|
||||
|
||||
/**
|
||||
* Sets the number of instances of this Mesh to draw (uses hardware
|
||||
* instancing when possible.)
|
||||
* A custom vertex shader is necessary in order to introduce differences
|
||||
* in each instance.
|
||||
**/
|
||||
void setInstanceCount(int count);
|
||||
int getInstanceCount() const;
|
||||
|
||||
/**
|
||||
* Sets the texture used when drawing the Mesh.
|
||||
**/
|
||||
@@ -131,6 +150,10 @@ public:
|
||||
void setDrawMode(DrawMode mode);
|
||||
DrawMode getDrawMode() const;
|
||||
|
||||
void setDrawRange(int min, int max);
|
||||
void setDrawRange();
|
||||
void getDrawRange(int &min, int &max) const;
|
||||
|
||||
/**
|
||||
* Sets whether per-vertex colors are enabled. If this is disabled, the
|
||||
* global color (love.graphics.setColor) will be used for the entire Mesh.
|
||||
@@ -138,17 +161,8 @@ public:
|
||||
void setVertexColors(bool enable);
|
||||
bool hasVertexColors() const;
|
||||
|
||||
/**
|
||||
* Sets whether the Mesh will be drawn as wireframe lines instead of filled
|
||||
* triangles (has no effect for DRAW_MODE_POINTS.)
|
||||
* This should only be used as a debugging tool. The wireframe lines do not
|
||||
* behave the same as regular love.graphics lines.
|
||||
**/
|
||||
void setWireframe(bool enable);
|
||||
bool isWireframe() const;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
static bool getConstant(const char *in, DrawMode &out);
|
||||
static bool getConstant(DrawMode in, const char *&out);
|
||||
@@ -156,6 +170,7 @@ public:
|
||||
private:
|
||||
|
||||
GLenum getGLDrawMode(DrawMode mode) const;
|
||||
GLenum getGLDataTypeFromMax(size_t maxvalue) const;
|
||||
|
||||
// Vertex buffer.
|
||||
VertexBuffer *vbo;
|
||||
@@ -164,16 +179,20 @@ private:
|
||||
// Element (vertex index) buffer, for the vertex map.
|
||||
VertexBuffer *ibo;
|
||||
size_t element_count;
|
||||
GLenum element_data_type;
|
||||
|
||||
int instance_count;
|
||||
|
||||
DrawMode draw_mode;
|
||||
|
||||
int range_min;
|
||||
int range_max;
|
||||
|
||||
Texture *texture;
|
||||
|
||||
// Whether the per-vertex colors are used when drawing.
|
||||
bool colors_enabled;
|
||||
|
||||
bool wireframe;
|
||||
|
||||
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
|
||||
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ OpenGL::OpenGL()
|
||||
: contextInitialized(false)
|
||||
, maxAnisotropy(1.0f)
|
||||
, maxTextureSize(0)
|
||||
, maxRenderTargets(0)
|
||||
, vendor(VENDOR_UNKNOWN)
|
||||
, state()
|
||||
{
|
||||
@@ -110,9 +111,14 @@ void OpenGL::initContext()
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
|
||||
}
|
||||
|
||||
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
|
||||
setBlendState(blend);
|
||||
|
||||
initMaxValues();
|
||||
createDefaultTexture();
|
||||
|
||||
state.lastPseudoInstanceID = -1;
|
||||
|
||||
contextInitialized = true;
|
||||
}
|
||||
|
||||
@@ -188,6 +194,19 @@ void OpenGL::initMaxValues()
|
||||
maxAnisotropy = 1.0f;
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
|
||||
if (Canvas::isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers))
|
||||
{
|
||||
int maxattachments = 0;
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
|
||||
|
||||
int maxdrawbuffers = 0;
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
|
||||
|
||||
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
|
||||
}
|
||||
else
|
||||
maxRenderTargets = 0;
|
||||
}
|
||||
|
||||
void OpenGL::createDefaultTexture()
|
||||
@@ -214,10 +233,81 @@ void OpenGL::createDefaultTexture()
|
||||
|
||||
void OpenGL::prepareDraw()
|
||||
{
|
||||
// Make sure the active shader has the correct values for the built-in
|
||||
// screen params uniform.
|
||||
if (Shader::current)
|
||||
Shader::current->checkSetScreenParams();
|
||||
Shader *shader = Shader::current;
|
||||
if (shader != nullptr)
|
||||
{
|
||||
// Make sure the active shader has the correct values for its
|
||||
// love-provided uniforms.
|
||||
shader->checkSetScreenParams();
|
||||
|
||||
// Make sure the Instance ID variable is up-to-date when
|
||||
// pseudo-instancing is used.
|
||||
if (state.lastPseudoInstanceID != 0 && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID))
|
||||
{
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f);
|
||||
state.lastPseudoInstanceID = 0;
|
||||
}
|
||||
|
||||
// We need to make sure antialiased Canvases are properly resolved
|
||||
// before sampling from their textures in a shader.
|
||||
// This is kind of a big hack. :(
|
||||
const std::map<std::string, Object *> &r = shader->getBoundRetainables();
|
||||
for (auto it = r.begin(); it != r.end(); ++it)
|
||||
{
|
||||
// Even bigger hack! D:
|
||||
Canvas *canvas = dynamic_cast<Canvas *>(it->second);
|
||||
if (canvas != nullptr)
|
||||
canvas->resolveMSAA();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
|
||||
{
|
||||
Shader *shader = Shader::current;
|
||||
|
||||
if (GLEE_ARB_draw_instanced)
|
||||
glDrawArraysInstancedARB(mode, first, count, primcount);
|
||||
else
|
||||
{
|
||||
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
|
||||
|
||||
// Pseudo-instancing fallback.
|
||||
for (int i = 0; i < primcount; i++)
|
||||
{
|
||||
if (shaderHasID)
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
|
||||
|
||||
glDrawArrays(mode, first, count);
|
||||
}
|
||||
|
||||
if (shaderHasID)
|
||||
state.lastPseudoInstanceID = primcount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
|
||||
{
|
||||
Shader *shader = Shader::current;
|
||||
|
||||
if (GLEE_ARB_draw_instanced)
|
||||
glDrawElementsInstancedARB(mode, count, type, indices, primcount);
|
||||
else
|
||||
{
|
||||
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
|
||||
|
||||
// Pseudo-instancing fallback.
|
||||
for (int i = 0; i < primcount; i++)
|
||||
{
|
||||
if (shaderHasID)
|
||||
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
|
||||
|
||||
glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
if (shaderHasID)
|
||||
state.lastPseudoInstanceID = primcount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGL::setColor(const Color &c)
|
||||
@@ -277,6 +367,39 @@ OpenGL::Viewport OpenGL::getScissor() const
|
||||
return state.scissor;
|
||||
}
|
||||
|
||||
void OpenGL::setBlendState(const BlendState &blend)
|
||||
{
|
||||
if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
|
||||
glBlendEquation(blend.func);
|
||||
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
|
||||
glBlendEquationEXT(blend.func);
|
||||
else
|
||||
{
|
||||
if (blend.func == GL_FUNC_REVERSE_SUBTRACT)
|
||||
throw love::Exception("This graphics card does not support the subtractive blend mode!");
|
||||
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
|
||||
}
|
||||
|
||||
if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA)
|
||||
glBlendFunc(blend.srcRGB, blend.dstRGB);
|
||||
else
|
||||
{
|
||||
if (GLEE_VERSION_1_4)
|
||||
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
else if (GLEE_EXT_blend_func_separate)
|
||||
glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
|
||||
else
|
||||
throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!");
|
||||
}
|
||||
|
||||
state.blend = blend;
|
||||
}
|
||||
|
||||
OpenGL::BlendState OpenGL::getBlendState() const
|
||||
{
|
||||
return state.blend;
|
||||
}
|
||||
|
||||
void OpenGL::setTextureUnit(int textureunit)
|
||||
{
|
||||
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
|
||||
@@ -504,11 +627,77 @@ int OpenGL::getMaxTextureSize() const
|
||||
return maxTextureSize;
|
||||
}
|
||||
|
||||
int OpenGL::getMaxRenderTargets() const
|
||||
{
|
||||
return maxRenderTargets;
|
||||
}
|
||||
|
||||
OpenGL::Vendor OpenGL::getVendor() const
|
||||
{
|
||||
return vendor;
|
||||
}
|
||||
|
||||
const char *OpenGL::debugSeverityString(GLenum severity)
|
||||
{
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
return "high";
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
return "medium";
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
return "low";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *OpenGL::debugSourceString(GLenum source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
return "API";
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
return "window";
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
return "shader";
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
return "external";
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
return "LOVE";
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
return "other";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *OpenGL::debugTypeString(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
return "error";
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
return "deprecated behavior";
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
return "undefined behavior";
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
return "performance";
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
return "portability";
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
return "other";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
// OpenGL class instance singleton.
|
||||
OpenGL gl;
|
||||
|
||||
|
||||
@@ -63,6 +63,17 @@ public:
|
||||
VENDOR_UNKNOWN
|
||||
};
|
||||
|
||||
// Vertex attributes used in shaders by LOVE. The values map to OpenGL
|
||||
// generic vertex attribute indices, when applicable.
|
||||
// LOVE uses the old hard-coded attribute APIs for positions, colors, etc.
|
||||
// (for now.)
|
||||
enum VertexAttrib
|
||||
{
|
||||
// Instance ID when pseudo-instancing is used.
|
||||
ATTRIB_PSEUDO_INSTANCE_ID = 1,
|
||||
ATTRIB_MAX_ENUM
|
||||
};
|
||||
|
||||
// A rectangle representing an OpenGL viewport or a scissor box.
|
||||
struct Viewport
|
||||
{
|
||||
@@ -76,6 +87,18 @@ public:
|
||||
Viewport(int _x, int _y, int _w, int _h)
|
||||
: x(_x), y(_y), w(_w), h(_h)
|
||||
{}
|
||||
|
||||
bool operator == (const Viewport &rhs) const
|
||||
{
|
||||
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
|
||||
}
|
||||
};
|
||||
|
||||
struct BlendState
|
||||
{
|
||||
GLenum srcRGB, srcA;
|
||||
GLenum dstRGB, dstA;
|
||||
GLenum func;
|
||||
};
|
||||
|
||||
OpenGL();
|
||||
@@ -99,6 +122,16 @@ public:
|
||||
**/
|
||||
void prepareDraw();
|
||||
|
||||
/**
|
||||
* glDrawArraysInstanced with a pseudo-instancing fallback.
|
||||
**/
|
||||
void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
|
||||
|
||||
/**
|
||||
* glDrawElementsInstanced with a pseudo-instancing fallback.
|
||||
**/
|
||||
void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
|
||||
|
||||
/**
|
||||
* Sets the current constant color.
|
||||
**/
|
||||
@@ -141,6 +174,17 @@ public:
|
||||
**/
|
||||
Viewport getScissor() const;
|
||||
|
||||
/**
|
||||
* Sets blending functionality.
|
||||
* Note: This does not globally enable or disable blending.
|
||||
**/
|
||||
void setBlendState(const BlendState &blend);
|
||||
|
||||
/**
|
||||
* Gets the currently set blending functionality.
|
||||
**/
|
||||
BlendState getBlendState() const;
|
||||
|
||||
/**
|
||||
* Helper for setting the active texture unit.
|
||||
*
|
||||
@@ -194,11 +238,21 @@ public:
|
||||
**/
|
||||
int getMaxTextureSize() const;
|
||||
|
||||
/**
|
||||
* Returns the maximum supported number of simultaneous render targets.
|
||||
**/
|
||||
int getMaxRenderTargets() const;
|
||||
|
||||
/**
|
||||
* Get the GPU vendor of this OpenGL context.
|
||||
**/
|
||||
Vendor getVendor() const;
|
||||
|
||||
// Get human-readable strings for debug info.
|
||||
static const char *debugSeverityString(GLenum severity);
|
||||
static const char *debugSourceString(GLenum source);
|
||||
static const char *debugTypeString(GLenum type);
|
||||
|
||||
private:
|
||||
|
||||
void initVendor();
|
||||
@@ -210,6 +264,7 @@ private:
|
||||
|
||||
float maxAnisotropy;
|
||||
int maxTextureSize;
|
||||
int maxRenderTargets;
|
||||
|
||||
Vendor vendor;
|
||||
|
||||
@@ -230,6 +285,11 @@ private:
|
||||
Viewport viewport;
|
||||
Viewport scissor;
|
||||
|
||||
BlendState blend;
|
||||
|
||||
// The last ID value used for pseudo-instancing.
|
||||
int lastPseudoInstanceID;
|
||||
|
||||
} state;
|
||||
|
||||
}; // OpenGL
|
||||
|
||||
@@ -94,6 +94,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
|
||||
, spinVariation(0)
|
||||
, offsetX(float(texture->getWidth())*0.5f)
|
||||
, offsetY(float(texture->getHeight())*0.5f)
|
||||
, relativeRotation(false)
|
||||
{
|
||||
if (size == 0 || size > MAX_PARTICLES)
|
||||
throw love::Exception("Invalid ParticleSystem size.");
|
||||
@@ -118,6 +119,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||
, emissionRate(p.emissionRate)
|
||||
, emitCounter(0.0f)
|
||||
, position(p.position)
|
||||
, prevPosition(p.prevPosition)
|
||||
, areaSpreadDistribution(p.areaSpreadDistribution)
|
||||
, areaSpread(p.areaSpread)
|
||||
, lifetime(p.lifetime)
|
||||
@@ -144,6 +146,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
|
||||
, offsetX(p.offsetX)
|
||||
, offsetY(p.offsetY)
|
||||
, colors(p.colors)
|
||||
, relativeRotation(p.relativeRotation)
|
||||
{
|
||||
setBufferSize(maxParticles);
|
||||
|
||||
@@ -168,7 +171,7 @@ void ParticleSystem::createBuffers(size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
pFree = pMem = new particle[size];
|
||||
pFree = pMem = new Particle[size];
|
||||
particleVerts = new love::Vertex[size * 4];
|
||||
maxParticles = (uint32) size;
|
||||
}
|
||||
@@ -205,14 +208,14 @@ uint32 ParticleSystem::getBufferSize() const
|
||||
return maxParticles;
|
||||
}
|
||||
|
||||
void ParticleSystem::addParticle()
|
||||
void ParticleSystem::addParticle(float t)
|
||||
{
|
||||
if (isFull())
|
||||
return;
|
||||
|
||||
// Gets a free particle and updates the allocation pointer.
|
||||
particle *p = pFree++;
|
||||
initParticle(p);
|
||||
Particle *p = pFree++;
|
||||
initParticle(p, t);
|
||||
|
||||
switch (insertMode)
|
||||
{
|
||||
@@ -231,10 +234,13 @@ void ParticleSystem::addParticle()
|
||||
activeParticles++;
|
||||
}
|
||||
|
||||
void ParticleSystem::initParticle(particle *p)
|
||||
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)
|
||||
@@ -243,8 +249,8 @@ void ParticleSystem::initParticle(particle *p)
|
||||
p->life = (float) rng.random(min, max);
|
||||
p->lifetime = p->life;
|
||||
|
||||
p->position[0] = position.getX();
|
||||
p->position[1] = position.getY();
|
||||
p->position[0] = pos.x;
|
||||
p->position[1] = pos.y;
|
||||
|
||||
switch (areaSpreadDistribution)
|
||||
{
|
||||
@@ -265,7 +271,7 @@ void ParticleSystem::initParticle(particle *p)
|
||||
max = direction + spread/2.0f;
|
||||
p->direction = (float) rng.random(min, max);
|
||||
|
||||
p->origin = position;
|
||||
p->origin = pos;
|
||||
|
||||
min = speedMin;
|
||||
max = speedMax;
|
||||
@@ -294,10 +300,14 @@ void ParticleSystem::initParticle(particle *p)
|
||||
p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
|
||||
p->rotation = (float) rng.random(min, max);
|
||||
|
||||
p->angle = p->rotation;
|
||||
if (relativeRotation)
|
||||
p->angle += atan2f(p->speed.y, p->speed.x);
|
||||
|
||||
p->color = colors[0];
|
||||
}
|
||||
|
||||
void ParticleSystem::insertTop(particle *p)
|
||||
void ParticleSystem::insertTop(Particle *p)
|
||||
{
|
||||
if (pHead == nullptr)
|
||||
{
|
||||
@@ -313,7 +323,7 @@ void ParticleSystem::insertTop(particle *p)
|
||||
pTail = p;
|
||||
}
|
||||
|
||||
void ParticleSystem::insertBottom(particle *p)
|
||||
void ParticleSystem::insertBottom(Particle *p)
|
||||
{
|
||||
if (pTail == nullptr)
|
||||
{
|
||||
@@ -329,7 +339,7 @@ void ParticleSystem::insertBottom(particle *p)
|
||||
pHead = p;
|
||||
}
|
||||
|
||||
void ParticleSystem::insertRandom(particle *p)
|
||||
void ParticleSystem::insertRandom(Particle *p)
|
||||
{
|
||||
// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
|
||||
uint64 pos = rng.rand() % ((int64) activeParticles + 1);
|
||||
@@ -337,7 +347,7 @@ void ParticleSystem::insertRandom(particle *p)
|
||||
// Special case where the particle gets inserted before the head.
|
||||
if (pos == activeParticles)
|
||||
{
|
||||
particle *pA = pHead;
|
||||
Particle *pA = pHead;
|
||||
if (pA)
|
||||
pA->prev = p;
|
||||
p->prev = nullptr;
|
||||
@@ -347,8 +357,8 @@ void ParticleSystem::insertRandom(particle *p)
|
||||
}
|
||||
|
||||
// Inserts the particle after the randomly selected particle.
|
||||
particle *pA = pMem + pos;
|
||||
particle *pB = pA->next;
|
||||
Particle *pA = pMem + pos;
|
||||
Particle *pB = pA->next;
|
||||
pA->next = p;
|
||||
if (pB)
|
||||
pB->prev = p;
|
||||
@@ -358,12 +368,12 @@ void ParticleSystem::insertRandom(particle *p)
|
||||
p->next = pB;
|
||||
}
|
||||
|
||||
ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
|
||||
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;
|
||||
Particle *pNext = nullptr;
|
||||
|
||||
// Removes the particle from the linked list.
|
||||
if (p->prev)
|
||||
@@ -428,14 +438,14 @@ ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
|
||||
return insertMode;
|
||||
}
|
||||
|
||||
void ParticleSystem::setEmissionRate(int rate)
|
||||
void ParticleSystem::setEmissionRate(float rate)
|
||||
{
|
||||
if (rate < 0)
|
||||
if (rate < 0.0f)
|
||||
throw love::Exception("Invalid emission rate");
|
||||
emissionRate = rate;
|
||||
}
|
||||
|
||||
int ParticleSystem::getEmissionRate() const
|
||||
float ParticleSystem::getEmissionRate() const
|
||||
{
|
||||
return emissionRate;
|
||||
}
|
||||
@@ -470,6 +480,7 @@ void ParticleSystem::getParticleLifetime(float *min, float *max) const
|
||||
void ParticleSystem::setPosition(float x, float y)
|
||||
{
|
||||
position = love::Vector(x, y);
|
||||
prevPosition = position;
|
||||
}
|
||||
|
||||
const love::Vector &ParticleSystem::getPosition() const
|
||||
@@ -477,6 +488,11 @@ 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);
|
||||
@@ -705,6 +721,16 @@ std::vector<Color> ParticleSystem::getColor() const
|
||||
return ncolors;
|
||||
}
|
||||
|
||||
void ParticleSystem::setRelativeRotation(bool enable)
|
||||
{
|
||||
relativeRotation = enable;
|
||||
}
|
||||
|
||||
bool ParticleSystem::hasRelativeRotation() const
|
||||
{
|
||||
return relativeRotation;
|
||||
}
|
||||
|
||||
uint32 ParticleSystem::getCount() const
|
||||
{
|
||||
return activeParticles;
|
||||
@@ -748,7 +774,7 @@ void ParticleSystem::emit(uint32 num)
|
||||
num = std::min(num, maxParticles - activeParticles);
|
||||
|
||||
while(num--)
|
||||
addParticle();
|
||||
addParticle(1.0f);
|
||||
}
|
||||
|
||||
bool ParticleSystem::isActive() const
|
||||
@@ -776,7 +802,7 @@ bool ParticleSystem::isFull() const
|
||||
return activeParticles == maxParticles;
|
||||
}
|
||||
|
||||
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
uint32 pCount = getCount();
|
||||
if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr)
|
||||
@@ -792,13 +818,13 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
|
||||
|
||||
const Vertex *textureVerts = texture->getVertices();
|
||||
Vertex *pVerts = particleVerts;
|
||||
particle *p = pHead;
|
||||
Particle *p = pHead;
|
||||
|
||||
// set the vertex data for each particle (transformation, texcoords, color)
|
||||
while (p)
|
||||
{
|
||||
// particle vertices are image vertices transformed by particle information
|
||||
t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
|
||||
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
|
||||
t.transform(pVerts, textureVerts, 4);
|
||||
|
||||
// set the texture coordinate and color data for particle vertices
|
||||
@@ -847,27 +873,8 @@ void ParticleSystem::update(float dt)
|
||||
if (pMem == nullptr || dt == 0.0f)
|
||||
return;
|
||||
|
||||
// Make some more particles.
|
||||
if (active)
|
||||
{
|
||||
float rate = 1.0f / emissionRate; // the amount of time between each particle emit
|
||||
emitCounter += dt;
|
||||
while (emitCounter > rate)
|
||||
{
|
||||
addParticle();
|
||||
emitCounter -= rate;
|
||||
}
|
||||
/*int particles = (int)(emissionRate * dt);
|
||||
for (int i = 0; i != particles; i++)
|
||||
add();*/
|
||||
|
||||
life -= dt;
|
||||
if (lifetime != -1 && life < 0)
|
||||
stop();
|
||||
}
|
||||
|
||||
// Traverse all particles and update.
|
||||
particle *p = pHead;
|
||||
Particle *p = pHead;
|
||||
|
||||
while (p)
|
||||
{
|
||||
@@ -912,7 +919,12 @@ void ParticleSystem::update(float dt)
|
||||
const float t = 1.0f - p->life / p->lifetime;
|
||||
|
||||
// Rotate.
|
||||
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
|
||||
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
|
||||
|
||||
p->angle = p->rotation;
|
||||
|
||||
if (relativeRotation)
|
||||
p->angle += atan2f(p->speed.y, p->speed.x);
|
||||
|
||||
// Change size according to given intervals:
|
||||
// i = 0 1 2 3 n-1
|
||||
@@ -940,6 +952,28 @@ void ParticleSystem::update(float dt)
|
||||
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)
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
INSERT_MODE_TOP,
|
||||
INSERT_MODE_BOTTOM,
|
||||
INSERT_MODE_RANDOM,
|
||||
INSERT_MODE_MAX_ENUM,
|
||||
INSERT_MODE_MAX_ENUM
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,12 +131,12 @@ public:
|
||||
* Sets the emission rate.
|
||||
* @param rate The amount of particles per second.
|
||||
**/
|
||||
void setEmissionRate(int rate);
|
||||
void setEmissionRate(float rate);
|
||||
|
||||
/**
|
||||
* Returns the number of particles created per second.
|
||||
**/
|
||||
int getEmissionRate() const;
|
||||
float getEmissionRate() const;
|
||||
|
||||
/**
|
||||
* Sets the lifetime of the particle emitter (-1 means eternal)
|
||||
@@ -176,6 +176,15 @@ public:
|
||||
**/
|
||||
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:
|
||||
@@ -412,6 +421,12 @@ public:
|
||||
**/
|
||||
std::vector<Color> getColor() 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.
|
||||
**/
|
||||
@@ -470,7 +485,7 @@ public:
|
||||
* @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) const;
|
||||
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.
|
||||
@@ -485,11 +500,12 @@ public:
|
||||
static bool getConstant(InsertMode in, const char *&out);
|
||||
|
||||
protected:
|
||||
|
||||
// Represents a single particle.
|
||||
struct particle
|
||||
struct Particle
|
||||
{
|
||||
particle *prev;
|
||||
particle *next;
|
||||
Particle *prev;
|
||||
Particle *next;
|
||||
|
||||
float lifetime;
|
||||
float life;
|
||||
@@ -509,7 +525,8 @@ protected:
|
||||
float sizeOffset;
|
||||
float sizeIntervalSize;
|
||||
|
||||
float rotation;
|
||||
float rotation; // Amount of rotation applied to the final angle.
|
||||
float angle;
|
||||
float spinStart;
|
||||
float spinEnd;
|
||||
|
||||
@@ -517,16 +534,16 @@ protected:
|
||||
};
|
||||
|
||||
// Pointer to the beginning of the allocated memory.
|
||||
particle *pMem;
|
||||
Particle *pMem;
|
||||
|
||||
// Pointer to a free particle.
|
||||
particle *pFree;
|
||||
Particle *pFree;
|
||||
|
||||
// Pointer to the start of the linked list.
|
||||
particle *pHead;
|
||||
Particle *pHead;
|
||||
|
||||
// Pointer to the end of the linked list.
|
||||
particle *pTail;
|
||||
Particle *pTail;
|
||||
|
||||
// array of transformed vertex data for all particles, for drawing
|
||||
Vertex *particleVerts;
|
||||
@@ -547,13 +564,14 @@ protected:
|
||||
uint32 activeParticles;
|
||||
|
||||
// The emission rate (particles/sec).
|
||||
int emissionRate;
|
||||
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;
|
||||
@@ -607,17 +625,19 @@ protected:
|
||||
// Color.
|
||||
std::vector<Colorf> colors;
|
||||
|
||||
bool relativeRotation;
|
||||
|
||||
void createBuffers(size_t size);
|
||||
void deleteBuffers();
|
||||
|
||||
void addParticle();
|
||||
particle *removeParticle(particle *p);
|
||||
void addParticle(float t);
|
||||
Particle *removeParticle(Particle *p);
|
||||
|
||||
// Called by addParticle.
|
||||
void initParticle(particle *p);
|
||||
void insertTop(particle *p);
|
||||
void insertBottom(particle *p);
|
||||
void insertRandom(particle *p);
|
||||
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;
|
||||
|
||||
@@ -70,7 +70,9 @@ Shader::Shader(const ShaderSources &sources)
|
||||
: shaderSources(sources)
|
||||
, program(0)
|
||||
, builtinUniforms()
|
||||
, vertexAttributes()
|
||||
, lastCanvas((Canvas *) -1)
|
||||
, lastViewport()
|
||||
{
|
||||
if (shaderSources.empty())
|
||||
throw love::Exception("Cannot create shader: no source code!");
|
||||
@@ -181,6 +183,14 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
|
||||
for (it = shaderids.begin(); it != shaderids.end(); ++it)
|
||||
glAttachShader(program, *it);
|
||||
|
||||
// Bind generic vertex attribute indices to names in the shader.
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find((OpenGL::VertexAttrib) i, name))
|
||||
glBindAttribLocation(program, i, (const GLchar *) name);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
// flag shaders for auto-deletion when the program object is deleted.
|
||||
@@ -273,6 +283,15 @@ bool Shader::loadVolatile()
|
||||
// Retreive all active uniform variables in this shader from OpenGL.
|
||||
mapActiveUniforms();
|
||||
|
||||
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
if (attribNames.find(OpenGL::VertexAttrib(i), name))
|
||||
vertexAttributes[i] = glGetAttribLocation(program, name);
|
||||
else
|
||||
vertexAttributes[i] = -1;
|
||||
}
|
||||
|
||||
if (current == this)
|
||||
{
|
||||
// make sure glUseProgram gets called.
|
||||
@@ -633,6 +652,11 @@ int Shader::getTextureUnit(const std::string &name)
|
||||
return texunit;
|
||||
}
|
||||
|
||||
bool Shader::hasVertexAttrib(OpenGL::VertexAttrib attrib) const
|
||||
{
|
||||
return vertexAttributes[int(attrib)] != -1;
|
||||
}
|
||||
|
||||
bool Shader::hasBuiltinExtern(BuiltinExtern builtin) const
|
||||
{
|
||||
return builtinUniforms[int(builtin)] != -1;
|
||||
@@ -670,30 +694,42 @@ bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *ve
|
||||
|
||||
void Shader::checkSetScreenParams()
|
||||
{
|
||||
if (lastCanvas == Canvas::current)
|
||||
OpenGL::Viewport view = gl.getViewport();
|
||||
|
||||
if (view == lastViewport && lastCanvas == Canvas::current)
|
||||
return;
|
||||
|
||||
// In the shader, we do pixcoord.y = gl_FragCoord.y * params[0] + params[1].
|
||||
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
|
||||
// This lets us flip pixcoord.y when needed, to be consistent (Canvases
|
||||
// have flipped y-values for pixel coordinates.)
|
||||
GLfloat params[] = {0.0f, 0.0f};
|
||||
GLfloat params[] = {
|
||||
(GLfloat) view.w, (GLfloat) view.h,
|
||||
0.0f, 0.0f,
|
||||
};
|
||||
|
||||
if (Canvas::current != nullptr)
|
||||
{
|
||||
// gl_FragCoord.y is flipped in Canvases, so we un-flip:
|
||||
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
|
||||
params[0] = -1.0f;
|
||||
params[1] = (float) Canvas::current->getHeight();
|
||||
params[2] = -1.0f;
|
||||
params[3] = (GLfloat) view.h;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
|
||||
params[0] = 1.0f;
|
||||
params[1] = 0.0f;
|
||||
params[2] = 1.0f;
|
||||
params[3] = 0.0f;
|
||||
}
|
||||
|
||||
sendBuiltinFloat(BUILTIN_SCREEN_PARAMS, 2, params, 1);
|
||||
sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1);
|
||||
|
||||
lastCanvas = Canvas::current;
|
||||
lastViewport = view;
|
||||
}
|
||||
|
||||
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
|
||||
{
|
||||
return boundRetainables;
|
||||
}
|
||||
|
||||
std::string Shader::getGLSLVersion()
|
||||
@@ -730,9 +766,16 @@ StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntr
|
||||
|
||||
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
|
||||
{
|
||||
{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
|
||||
};
|
||||
|
||||
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
|
||||
|
||||
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
|
||||
{
|
||||
{"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS},
|
||||
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
|
||||
};
|
||||
|
||||
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
// Built-in extern (uniform) variables.
|
||||
enum BuiltinExtern
|
||||
{
|
||||
BUILTIN_SCREEN_PARAMS,
|
||||
BUILTIN_SCREEN_SIZE,
|
||||
BUILTIN_MAX_ENUM
|
||||
};
|
||||
|
||||
@@ -138,10 +138,13 @@ public:
|
||||
/**
|
||||
* Internal use only.
|
||||
**/
|
||||
bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
|
||||
bool hasBuiltinExtern(BuiltinExtern builtin) const;
|
||||
bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
|
||||
void checkSetScreenParams();
|
||||
|
||||
const std::map<std::string, Object *> &getBoundRetainables() const;
|
||||
|
||||
static std::string getGLSLVersion();
|
||||
static bool isSupported();
|
||||
|
||||
@@ -198,6 +201,9 @@ private:
|
||||
// Location values for any built-in uniform variables.
|
||||
GLint builtinUniforms[BUILTIN_MAX_ENUM];
|
||||
|
||||
// Location values for any generic vertex attribute variables.
|
||||
GLint vertexAttributes[OpenGL::ATTRIB_MAX_ENUM];
|
||||
|
||||
// Uniform location buffer map
|
||||
std::map<std::string, Uniform> uniforms;
|
||||
|
||||
@@ -210,6 +216,7 @@ private:
|
||||
|
||||
// Pointer to the active Canvas when the screen params were last checked.
|
||||
Canvas *lastCanvas;
|
||||
OpenGL::Viewport lastViewport;
|
||||
|
||||
// Max GPU texture units available for sent images
|
||||
static GLint maxTexUnits;
|
||||
@@ -220,6 +227,10 @@ private:
|
||||
static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
|
||||
static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
|
||||
|
||||
// Names for the generic vertex attributes used by love.
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
|
||||
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
|
||||
|
||||
// Names for the built-in uniform variables.
|
||||
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
|
||||
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
|
||||
|
||||
@@ -105,6 +105,8 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl
|
||||
if ((index == -1 && next >= size) || index < -1 || index >= size)
|
||||
return -1;
|
||||
|
||||
Vertex sprite[4];
|
||||
|
||||
// Needed for colors.
|
||||
memcpy(sprite, texture->getVertices(), sizeof(Vertex) * 4);
|
||||
|
||||
@@ -131,6 +133,8 @@ int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy,
|
||||
if ((index == -1 && next >= size) || index < -1 || index >= next)
|
||||
return -1;
|
||||
|
||||
Vertex sprite[4];
|
||||
|
||||
// Needed for colors.
|
||||
memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4);
|
||||
|
||||
@@ -263,7 +267,7 @@ int SpriteBatch::getBufferSize() const
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
|
||||
{
|
||||
const size_t vertex_offset = offsetof(Vertex, x);
|
||||
const size_t texel_offset = offsetof(Vertex, s);
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
int getBufferSize() const;
|
||||
|
||||
// Implements Drawable.
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
|
||||
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
|
||||
|
||||
static bool getConstant(const char *in, UsageHint &out);
|
||||
static bool getConstant(UsageHint in, const char *&out);
|
||||
@@ -135,8 +135,6 @@ private:
|
||||
// The next free element.
|
||||
int next;
|
||||
|
||||
Vertex sprite[4];
|
||||
|
||||
// Current color. This color, if present, will be applied to the next
|
||||
// added sprite.
|
||||
Color *color;
|
||||
|
||||
@@ -48,12 +48,12 @@ public:
|
||||
* Any setup the texture might need to do before drawing, e.g. binding
|
||||
* the OpenGL texture for use.
|
||||
**/
|
||||
virtual void predraw() const {}
|
||||
virtual void predraw() {}
|
||||
|
||||
/**
|
||||
* Any cleanup the texture might need to do directly after drawing.
|
||||
**/
|
||||
virtual void postdraw() const {}
|
||||
virtual void postdraw() {}
|
||||
|
||||
|
||||
}; // Texture
|
||||
|
||||
@@ -34,22 +34,21 @@ Canvas *luax_checkcanvas(lua_State *L, int idx)
|
||||
|
||||
int w_Canvas_renderTo(lua_State *L)
|
||||
{
|
||||
// As startGrab() clears the framebuffer, better not allow
|
||||
// grabbing inside another grabbing
|
||||
if (Canvas::current != NULL)
|
||||
{
|
||||
Canvas::bindDefaultCanvas();
|
||||
return luaL_error(L, "Current render target not the default canvas!");
|
||||
}
|
||||
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
|
||||
// Save the current Canvas so we can restore it when we're done.
|
||||
Canvas *oldcanvas = Canvas::current;
|
||||
|
||||
EXCEPT_GUARD(canvas->startGrab();)
|
||||
|
||||
lua_settop(L, 2); // make sure the function is on top of the stack
|
||||
lua_call(L, 0, 0);
|
||||
canvas->stopGrab();
|
||||
|
||||
if (oldcanvas != nullptr)
|
||||
oldcanvas->startGrab(oldcanvas->getAttachedCanvases());
|
||||
else
|
||||
Canvas::bindDefaultCanvas();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -111,16 +110,25 @@ int w_Canvas_clear(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Canvas_getType(lua_State *L)
|
||||
int w_Canvas_getFormat(lua_State *L)
|
||||
{
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
Canvas::TextureType type = canvas->getTextureType();
|
||||
Texture::Format format = canvas->getTextureFormat();
|
||||
const char *str;
|
||||
Canvas::getConstant(type, str);
|
||||
if (!Texture::getConstant(format, str))
|
||||
return luaL_error(L, "Unknown texture format.");
|
||||
|
||||
lua_pushstring(L, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Canvas_getFSAA(lua_State *L)
|
||||
{
|
||||
Canvas *canvas = luax_checkcanvas(L, 1);
|
||||
lua_pushinteger(L, canvas->getFSAA());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
// From wrap_Texture.
|
||||
@@ -136,7 +144,11 @@ static const luaL_Reg functions[] =
|
||||
{ "getImageData", w_Canvas_getImageData },
|
||||
{ "getPixel", w_Canvas_getPixel },
|
||||
{ "clear", w_Canvas_clear },
|
||||
{ "getType", w_Canvas_getType },
|
||||
{ "getFormat", w_Canvas_getFormat },
|
||||
{ "getFSAA", w_Canvas_getFSAA },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "getType", w_Canvas_getFormat },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ int w_Canvas_renderTo(lua_State *L);
|
||||
int w_Canvas_getImageData(lua_State *L);
|
||||
int w_Canvas_getPixel(lua_State * L);
|
||||
int w_Canvas_clear(lua_State *L);
|
||||
int w_Canvas_getType(lua_State *L);
|
||||
int w_Canvas_getFormat(lua_State *L);
|
||||
int w_Canvas_getFSAA(lua_State *L);
|
||||
extern "C" int luaopen_canvas(lua_State *L);
|
||||
|
||||
} // opengl
|
||||
|
||||
@@ -146,14 +146,23 @@ int w_setInvertedStencil(lua_State *L)
|
||||
|
||||
int w_getMaxTextureSize(lua_State *L)
|
||||
{
|
||||
lua_pushinteger(L, instance->getMaxTextureSize());
|
||||
lua_pushinteger(L, instance->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newImage(lua_State *L)
|
||||
{
|
||||
love::image::ImageData *data = 0;
|
||||
love::image::CompressedData *cdata = 0;
|
||||
love::image::ImageData *data = nullptr;
|
||||
love::image::CompressedData *cdata = nullptr;
|
||||
|
||||
Texture::Format format = Texture::FORMAT_NORMAL;
|
||||
const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
|
||||
|
||||
if (fstr != nullptr && !Texture::getConstant(fstr, format))
|
||||
return luaL_error(L, "Invalid texture format: %s", fstr);
|
||||
|
||||
if (format == Texture::FORMAT_HDR) // For now...
|
||||
return luaL_error(L, "HDR images are not supported.");
|
||||
|
||||
// Convert to FileData, if necessary.
|
||||
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
|
||||
@@ -185,15 +194,15 @@ int w_newImage(lua_State *L)
|
||||
return luaL_error(L, "Error creating image.");
|
||||
|
||||
// Create the image.
|
||||
Image *image = 0;
|
||||
Image *image = nullptr;
|
||||
EXCEPT_GUARD(
|
||||
if (cdata)
|
||||
image = instance->newImage(cdata);
|
||||
image = instance->newImage(cdata, format);
|
||||
else if (data)
|
||||
image = instance->newImage(data);
|
||||
image = instance->newImage(data, format);
|
||||
)
|
||||
|
||||
if (image == 0)
|
||||
if (image == nullptr)
|
||||
return luaL_error(L, "Could not load image.");
|
||||
|
||||
// Push the type.
|
||||
@@ -323,15 +332,16 @@ int w_newCanvas(lua_State *L)
|
||||
int width = luaL_optint(L, 1, instance->getWidth());
|
||||
int height = luaL_optint(L, 2, instance->getHeight());
|
||||
const char *str = luaL_optstring(L, 3, "normal");
|
||||
int fsaa = luaL_optint(L, 4, 0);
|
||||
|
||||
Canvas::TextureType texture_type;
|
||||
if (!Canvas::getConstant(str, texture_type))
|
||||
return luaL_error(L, "Invalid canvas type: %s", str);
|
||||
Texture::Format format;
|
||||
if (!Texture::getConstant(str, format))
|
||||
return luaL_error(L, "Invalid texture format: %s", str);
|
||||
|
||||
Canvas *canvas = 0;
|
||||
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type);)
|
||||
Canvas *canvas = nullptr;
|
||||
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);)
|
||||
|
||||
if (canvas == 0)
|
||||
if (canvas == nullptr)
|
||||
return luaL_error(L, "Canvas not created, but no error thrown. I don't even...");
|
||||
|
||||
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas);
|
||||
@@ -440,8 +450,10 @@ int w_newShader(lua_State *L)
|
||||
|
||||
int w_newMesh(lua_State *L)
|
||||
{
|
||||
// Check first argument: mandatory table of vertices.
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
// Check first argument: table of vertices or number of vertices.
|
||||
int ttype = lua_type(L, 1);
|
||||
if (ttype != LUA_TTABLE && ttype != LUA_TNUMBER)
|
||||
luaL_argerror(L, 1, "table or number expected");
|
||||
|
||||
// Second argument: optional texture.
|
||||
Texture *tex = nullptr;
|
||||
@@ -456,52 +468,60 @@ int w_newMesh(lua_State *L)
|
||||
if (str && !Mesh::getConstant(str, mode))
|
||||
return luaL_error(L, "Invalid mesh draw mode: %s", str);
|
||||
|
||||
size_t vertex_count = lua_objlen(L, 1);
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(vertex_count);
|
||||
|
||||
bool use_colors = false;
|
||||
|
||||
// Get the vertices from the table.
|
||||
for (size_t i = 1; i <= vertex_count; i++)
|
||||
{
|
||||
lua_rawgeti(L, 1, i);
|
||||
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
return luax_typerror(L, 1, "table of tables");
|
||||
|
||||
for (int j = 1; j <= 8; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
Vertex v;
|
||||
|
||||
v.x = (float) luaL_checknumber(L, -8);
|
||||
v.y = (float) luaL_checknumber(L, -7);
|
||||
|
||||
v.s = (float) luaL_checknumber(L, -6);
|
||||
v.t = (float) luaL_checknumber(L, -5);
|
||||
|
||||
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
|
||||
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
|
||||
v.b = (unsigned char) luaL_optinteger(L, -2, 255);
|
||||
v.a = (unsigned char) luaL_optinteger(L, -1, 255);
|
||||
|
||||
// Enable per-vertex coloring if any color is not the default.
|
||||
if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
|
||||
use_colors = true;
|
||||
|
||||
lua_pop(L, 9);
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
Mesh *t = nullptr;
|
||||
EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
|
||||
|
||||
if (ttype == LUA_TTABLE)
|
||||
{
|
||||
size_t vertex_count = lua_objlen(L, 1);
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(vertex_count);
|
||||
|
||||
bool use_colors = false;
|
||||
|
||||
// Get the vertices from the table.
|
||||
for (size_t i = 1; i <= vertex_count; i++)
|
||||
{
|
||||
lua_rawgeti(L, 1, i);
|
||||
|
||||
if (lua_type(L, -1) != LUA_TTABLE)
|
||||
return luax_typerror(L, 1, "table of tables");
|
||||
|
||||
for (int j = 1; j <= 8; j++)
|
||||
lua_rawgeti(L, -j, j);
|
||||
|
||||
Vertex v;
|
||||
|
||||
v.x = (float) luaL_checknumber(L, -8);
|
||||
v.y = (float) luaL_checknumber(L, -7);
|
||||
|
||||
v.s = (float) luaL_optnumber(L, -6, 0.0);
|
||||
v.t = (float) luaL_optnumber(L, -5, 0.0);
|
||||
|
||||
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
|
||||
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
|
||||
v.b = (unsigned char) luaL_optinteger(L, -2, 255);
|
||||
v.a = (unsigned char) luaL_optinteger(L, -1, 255);
|
||||
|
||||
// Enable per-vertex coloring if any color is not the default.
|
||||
if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
|
||||
use_colors = true;
|
||||
|
||||
lua_pop(L, 9);
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
|
||||
t->setVertexColors(use_colors);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = luaL_checkint(L, 1);
|
||||
EXCEPT_GUARD(t = instance->newMesh(count, mode);)
|
||||
}
|
||||
|
||||
if (tex)
|
||||
t->setTexture(tex);
|
||||
|
||||
t->setVertexColors(use_colors);
|
||||
|
||||
luax_pushtype(L, "Mesh", GRAPHICS_MESH_T, t);
|
||||
return 1;
|
||||
}
|
||||
@@ -798,7 +818,19 @@ int w_getPointSize(lua_State *L)
|
||||
|
||||
int w_getMaxPointSize(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, instance->getMaxPointSize());
|
||||
lua_pushnumber(L, instance->getSystemLimit(Graphics::LIMIT_POINT_SIZE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setWireframe(lua_State *L)
|
||||
{
|
||||
instance->setWireframe(luax_toboolean(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_isWireframe(lua_State *L)
|
||||
{
|
||||
luax_pushboolean(L, instance->isWireframe());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -962,6 +994,14 @@ int w_isSupported(lua_State *L)
|
||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_INSTANCING:
|
||||
if (!GLEE_ARB_draw_instanced)
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_SRGB:
|
||||
if (!Canvas::isSRGBSupported())
|
||||
supported = false;
|
||||
break;
|
||||
default:
|
||||
supported = false;
|
||||
}
|
||||
@@ -989,6 +1029,18 @@ int w_getRendererInfo(lua_State *L)
|
||||
return 4;
|
||||
}
|
||||
|
||||
int w_getSystemLimit(lua_State *L)
|
||||
{
|
||||
const char *limitstr = luaL_checkstring(L, 1);
|
||||
Graphics::SystemLimit limittype;
|
||||
|
||||
if (!Graphics::getConstant(limitstr, limittype))
|
||||
return luaL_error(L, "Invalid system limit type: %s", limitstr);
|
||||
|
||||
lua_pushnumber(L, instance->getSystemLimit(limittype));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_draw(lua_State *L)
|
||||
{
|
||||
Drawable *drawable = nullptr;
|
||||
@@ -1100,8 +1152,9 @@ int w_line(lua_State *L)
|
||||
args = lua_objlen(L, 1);
|
||||
is_table = true;
|
||||
}
|
||||
|
||||
if (args % 2 != 0)
|
||||
return luaL_error(L, "Number of vertices must be a multiple of two");
|
||||
return luaL_error(L, "Number of vertex components must be a multiple of two");
|
||||
else if (args < 4)
|
||||
return luaL_error(L, "Need at least two vertices to draw a line");
|
||||
|
||||
@@ -1202,7 +1255,7 @@ int w_polygon(lua_State *L)
|
||||
}
|
||||
|
||||
if (args % 2 != 0)
|
||||
return luaL_error(L, "Number of vertices must be a multiple of two");
|
||||
return luaL_error(L, "Number of vertex components must be a multiple of two");
|
||||
else if (args < 6)
|
||||
return luaL_error(L, "Need at least three vertices to draw a polygon");
|
||||
|
||||
@@ -1325,6 +1378,8 @@ static const luaL_Reg functions[] =
|
||||
{ "getPointSize", w_getPointSize },
|
||||
{ "getMaxPointSize", w_getMaxPointSize },
|
||||
{ "getMaxTextureSize", w_getMaxTextureSize },
|
||||
{ "setWireframe", w_setWireframe },
|
||||
{ "isWireframe", w_isWireframe },
|
||||
{ "newScreenshot", w_newScreenshot },
|
||||
{ "setCanvas", w_setCanvas },
|
||||
{ "getCanvas", w_getCanvas },
|
||||
@@ -1334,6 +1389,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
{ "isSupported", w_isSupported },
|
||||
{ "getRendererInfo", w_getRendererInfo },
|
||||
{ "getSystemLimit", w_getSystemLimit },
|
||||
|
||||
{ "draw", w_draw },
|
||||
|
||||
@@ -1369,6 +1425,7 @@ static const luaL_Reg functions[] =
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "getMaxImageSize", w_getMaxTextureSize },
|
||||
{ "getMaxPointSize", w_getMaxPointSize },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -83,6 +83,8 @@ int w_getLineJoin(lua_State *L);
|
||||
int w_setPointSize(lua_State *L);
|
||||
int w_getPointSize(lua_State *L);
|
||||
int w_getMaxPointSize(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);
|
||||
@@ -90,6 +92,7 @@ int w_setShader(lua_State *L);
|
||||
int w_getShader(lua_State *L);
|
||||
int w_isSupported(lua_State *L);
|
||||
int w_getRendererInfo(lua_State *L);
|
||||
int w_getSystemLimit(lua_State *L);
|
||||
int w_draw(lua_State *L);
|
||||
int w_print(lua_State *L);
|
||||
int w_printf(lua_State *L);
|
||||
|
||||
@@ -53,8 +53,8 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
|
||||
v.x = luaL_checknumber(L, -8);
|
||||
v.y = luaL_checknumber(L, -7);
|
||||
v.s = luaL_checknumber(L, -6);
|
||||
v.t = luaL_checknumber(L, -5);
|
||||
v.s = luaL_optnumber(L, -6, 0.0);
|
||||
v.t = luaL_optnumber(L, -5, 0.0);
|
||||
v.r = luaL_optinteger(L, -4, 255);
|
||||
v.g = luaL_optinteger(L, -3, 255);
|
||||
v.b = luaL_optinteger(L, -2, 255);
|
||||
@@ -66,8 +66,8 @@ int w_Mesh_setVertex(lua_State *L)
|
||||
{
|
||||
v.x = luaL_checknumber(L, 3);
|
||||
v.y = luaL_checknumber(L, 4);
|
||||
v.s = luaL_checknumber(L, 5);
|
||||
v.t = luaL_checknumber(L, 6);
|
||||
v.s = luaL_optnumber(L, 5, 0.0);
|
||||
v.t = luaL_optnumber(L, 6, 0.0);
|
||||
v.r = luaL_optinteger(L, 7, 255);
|
||||
v.g = luaL_optinteger(L, 8, 255);
|
||||
v.b = luaL_optinteger(L, 9, 255);
|
||||
@@ -122,8 +122,8 @@ int w_Mesh_setVertices(lua_State *L)
|
||||
v.x = (float) luaL_checknumber(L, -8);
|
||||
v.y = (float) luaL_checknumber(L, -7);
|
||||
|
||||
v.s = (float) luaL_checknumber(L, -6);
|
||||
v.t = (float) luaL_checknumber(L, -5);
|
||||
v.s = (float) luaL_optnumber(L, -6, 0.0);
|
||||
v.t = (float) luaL_optnumber(L, -5, 0.0);
|
||||
|
||||
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
|
||||
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
|
||||
@@ -220,14 +220,15 @@ int w_Mesh_setVertexMap(lua_State *L)
|
||||
int w_Mesh_getVertexMap(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
const uint32 *vertex_map = 0;
|
||||
|
||||
EXCEPT_GUARD(vertex_map = t->getVertexMap();)
|
||||
size_t elements = t->getVertexMapCount();
|
||||
std::vector<uint32> vertex_map;
|
||||
EXCEPT_GUARD(t->getVertexMap(vertex_map);)
|
||||
|
||||
lua_createtable(L, elements, 0);
|
||||
size_t element_count = vertex_map.size();
|
||||
|
||||
for (size_t i = 0; i < elements; i++)
|
||||
lua_createtable(L, element_count, 0);
|
||||
|
||||
for (size_t i = 0; i < element_count; i++)
|
||||
{
|
||||
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
@@ -236,6 +237,20 @@ int w_Mesh_getVertexMap(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setInstanceCount(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
t->setInstanceCount(luaL_checkint(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_getInstanceCount(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
lua_pushinteger(L, t->getInstanceCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setTexture(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -301,6 +316,38 @@ int w_Mesh_getDrawMode(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setDrawRange(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
|
||||
if (lua_isnoneornil(L, 2))
|
||||
t->setDrawRange();
|
||||
else
|
||||
{
|
||||
int rangemin = luaL_checkint(L, 2) - 1;
|
||||
int rangemax = luaL_checkint(L, 3) - 1;
|
||||
EXCEPT_GUARD(t->setDrawRange(rangemin, rangemax);)
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_getDrawRange(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
|
||||
int rangemin = -1;
|
||||
int rangemax = -1;
|
||||
t->getDrawRange(rangemin, rangemax);
|
||||
|
||||
if (rangemin < 0 || rangemax < 0)
|
||||
return 0;
|
||||
|
||||
lua_pushinteger(L, rangemin + 1);
|
||||
lua_pushinteger(L, rangemax + 1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_Mesh_setVertexColors(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
@@ -315,20 +362,6 @@ int w_Mesh_hasVertexColors(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_Mesh_setWireframe(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
t->setWireframe(luax_toboolean(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Mesh_isWireframe(lua_State *L)
|
||||
{
|
||||
Mesh *t = luax_checkmesh(L, 1);
|
||||
luax_pushboolean(L, t->isWireframe());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "setVertex", w_Mesh_setVertex },
|
||||
@@ -338,14 +371,16 @@ static const luaL_Reg functions[] =
|
||||
{ "getVertexCount", w_Mesh_getVertexCount },
|
||||
{ "setVertexMap", w_Mesh_setVertexMap },
|
||||
{ "getVertexMap", w_Mesh_getVertexMap },
|
||||
{ "setInstanceCount", w_Mesh_setInstanceCount },
|
||||
{ "getInstanceCount", w_Mesh_getInstanceCount },
|
||||
{ "setTexture", w_Mesh_setTexture },
|
||||
{ "getTexture", w_Mesh_getTexture },
|
||||
{ "setDrawMode", w_Mesh_setDrawMode },
|
||||
{ "getDrawMode", w_Mesh_getDrawMode },
|
||||
{ "setDrawRange", w_Mesh_setDrawRange },
|
||||
{ "getDrawRange", w_Mesh_getDrawRange },
|
||||
{ "setVertexColors", w_Mesh_setVertexColors },
|
||||
{ "hasVertexColors", w_Mesh_hasVertexColors },
|
||||
{ "setWireframe", w_Mesh_setWireframe },
|
||||
{ "isWireframe", w_Mesh_isWireframe },
|
||||
|
||||
// Deprecated since 0.9.1.
|
||||
{ "setImage", w_Mesh_setTexture },
|
||||
|
||||
@@ -41,14 +41,16 @@ int w_Mesh_getVertices(lua_State *L);
|
||||
int w_Mesh_getVertexCount(lua_State *L);
|
||||
int w_Mesh_setVertexMap(lua_State *L);
|
||||
int w_Mesh_getVertexMap(lua_State *L);
|
||||
int w_Mesh_setInstanceCount(lua_State *L);
|
||||
int w_Mesh_getInstanceCount(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);
|
||||
int w_Mesh_setVertexColors(lua_State *L);
|
||||
int w_Mesh_hasVertexColors(lua_State *L);
|
||||
int w_Mesh_setWireframe(lua_State *L);
|
||||
int w_Mesh_isWireframe(lua_State *L);
|
||||
|
||||
extern "C" int luaopen_mesh(lua_State *L);
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ int w_ParticleSystem_getInsertMode(lua_State *L)
|
||||
int w_ParticleSystem_setEmissionRate(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
int arg1 = luaL_checkint(L, 2);
|
||||
float arg1 = (float) luaL_checknumber(L, 2);
|
||||
EXCEPT_GUARD(t->setEmissionRate(arg1);)
|
||||
return 0;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ int w_ParticleSystem_setEmissionRate(lua_State *L)
|
||||
int w_ParticleSystem_getEmissionRate(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
lua_pushinteger(L, t->getEmissionRate());
|
||||
lua_pushnumber(L, t->getEmissionRate());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -191,6 +191,15 @@ int w_ParticleSystem_getPosition(lua_State *L)
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_ParticleSystem_moveTo(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
float arg1 = (float)luaL_checknumber(L, 2);
|
||||
float arg2 = (float)luaL_checknumber(L, 3);
|
||||
t->moveTo(arg1, arg2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_ParticleSystem_setAreaSpread(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
@@ -560,6 +569,20 @@ int w_ParticleSystem_getColors(lua_State *L)
|
||||
return colors.size();
|
||||
}
|
||||
|
||||
int w_ParticleSystem_setRelativeRotation(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
t->setRelativeRotation(luax_toboolean(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_ParticleSystem_hasRelativeRotation(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
luax_pushboolean(L, t->hasRelativeRotation());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_ParticleSystem_getCount(lua_State *L)
|
||||
{
|
||||
ParticleSystem *t = luax_checkparticlesystem(L, 1);
|
||||
@@ -649,6 +672,7 @@ static const luaL_Reg functions[] =
|
||||
{ "getParticleLifetime", w_ParticleSystem_getParticleLifetime },
|
||||
{ "setPosition", w_ParticleSystem_setPosition },
|
||||
{ "getPosition", w_ParticleSystem_getPosition },
|
||||
{ "moveTo", w_ParticleSystem_moveTo },
|
||||
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
|
||||
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
|
||||
{ "setDirection", w_ParticleSystem_setDirection },
|
||||
@@ -677,6 +701,8 @@ static const luaL_Reg functions[] =
|
||||
{ "getColors", w_ParticleSystem_getColors },
|
||||
{ "setOffset", w_ParticleSystem_setOffset },
|
||||
{ "getOffset", w_ParticleSystem_getOffset },
|
||||
{ "setRelativeRotation", w_ParticleSystem_setRelativeRotation },
|
||||
{ "hasRelativeRotation", w_ParticleSystem_hasRelativeRotation },
|
||||
{ "getCount", w_ParticleSystem_getCount },
|
||||
{ "start", w_ParticleSystem_start },
|
||||
{ "stop", w_ParticleSystem_stop },
|
||||
|
||||
@@ -48,6 +48,7 @@ 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);
|
||||
@@ -76,6 +77,8 @@ int w_ParticleSystem_setColors(lua_State *L);
|
||||
int w_ParticleSystem_getColors(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);
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
/**
|
||||
* Destructor.
|
||||
**/
|
||||
virtual ~Image() {};
|
||||
virtual ~Image() {}
|
||||
|
||||
/**
|
||||
* Creates new ImageData from FileData.
|
||||
@@ -93,4 +93,4 @@ public:
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_IMAGE_H
|
||||
#endif // LOVE_IMAGE_IMAGE_H
|
||||
|
||||
@@ -23,15 +23,10 @@
|
||||
// LOVE
|
||||
#include "common/Exception.h"
|
||||
#include "common/math.h"
|
||||
#include "thread/threads.h"
|
||||
|
||||
// DevIL
|
||||
#include <IL/il.h>
|
||||
|
||||
using love::thread::Lock;
|
||||
|
||||
static Mutex *devilMutex = 0;
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -44,21 +39,22 @@ static inline void ilxClearErrors()
|
||||
while (ilGetError() != IL_NO_ERROR);
|
||||
}
|
||||
|
||||
void DevilHandler::init()
|
||||
DevilHandler::DevilHandler()
|
||||
: mutex(nullptr)
|
||||
{
|
||||
// There should only ever be one DevilHandler object (owned by the Image
|
||||
// module), so we can use the global initialization function here.
|
||||
ilInit();
|
||||
ilEnable(IL_ORIGIN_SET);
|
||||
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
|
||||
}
|
||||
|
||||
void DevilHandler::quit()
|
||||
DevilHandler::~DevilHandler()
|
||||
{
|
||||
ilShutDown();
|
||||
if (devilMutex)
|
||||
{
|
||||
delete devilMutex;
|
||||
devilMutex = 0;
|
||||
}
|
||||
|
||||
if (mutex)
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
bool DevilHandler::canDecode(love::filesystem::FileData * /*data*/)
|
||||
@@ -85,10 +81,10 @@ bool DevilHandler::canEncode(ImageData::Format format)
|
||||
|
||||
DevilHandler::DecodedImage DevilHandler::decode(love::filesystem::FileData *data)
|
||||
{
|
||||
if (!devilMutex)
|
||||
devilMutex = thread::newMutex();
|
||||
if (!mutex)
|
||||
mutex = love::thread::newMutex();
|
||||
|
||||
Lock lock(devilMutex);
|
||||
love::thread::Lock lock(mutex);
|
||||
|
||||
ILuint image = ilGenImage();
|
||||
ilBindImage(image);
|
||||
@@ -140,10 +136,10 @@ DevilHandler::DecodedImage DevilHandler::decode(love::filesystem::FileData *data
|
||||
|
||||
DevilHandler::EncodedImage DevilHandler::encode(const DecodedImage &img, ImageData::Format format)
|
||||
{
|
||||
if (!devilMutex)
|
||||
devilMutex = thread::newMutex();
|
||||
if (!mutex)
|
||||
mutex = love::thread::newMutex();
|
||||
|
||||
Lock lock(devilMutex);
|
||||
love::thread::Lock lock(mutex);
|
||||
|
||||
ILuint tempimage = ilGenImage();
|
||||
ilBindImage(tempimage);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// LOVE
|
||||
#include "filesystem/FileData.h"
|
||||
#include "FormatHandler.h"
|
||||
#include "thread/threads.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -39,16 +40,20 @@ class DevilHandler : public FormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
static void init();
|
||||
static void quit();
|
||||
|
||||
// Implements FormatHandler.
|
||||
|
||||
static bool canDecode(love::filesystem::FileData *data);
|
||||
static bool canEncode(ImageData::Format format);
|
||||
DevilHandler();
|
||||
virtual ~DevilHandler();
|
||||
|
||||
static DecodedImage decode(love::filesystem::FileData *data);
|
||||
static EncodedImage encode(const DecodedImage &img, ImageData::Format format);
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
virtual bool canEncode(ImageData::Format format);
|
||||
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
|
||||
|
||||
private:
|
||||
|
||||
Mutex *mutex;
|
||||
|
||||
}; // DevilHandler
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 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 "FormatHandler.h"
|
||||
#include "common/Exception.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
FormatHandler::FormatHandler()
|
||||
{
|
||||
}
|
||||
|
||||
FormatHandler::~FormatHandler()
|
||||
{
|
||||
}
|
||||
|
||||
bool FormatHandler::canDecode(love::filesystem::FileData* /*data*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FormatHandler::canEncode(ImageData::Format /*format*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FormatHandler::DecodedImage FormatHandler::decode(love::filesystem::FileData* /*data*/)
|
||||
{
|
||||
throw love::Exception("Image decoding is not implemented for this format backend.");
|
||||
}
|
||||
|
||||
FormatHandler::EncodedImage FormatHandler::encode(const DecodedImage& /*img*/, ImageData::Format /*format*/)
|
||||
{
|
||||
throw love::Exception("Image encoding is not implemented for this format backend.");
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
@@ -24,6 +24,7 @@
|
||||
// LOVE
|
||||
#include "image/ImageData.h"
|
||||
#include "filesystem/FileData.h"
|
||||
#include "common/Object.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -34,8 +35,9 @@ namespace magpie
|
||||
|
||||
/**
|
||||
* Base class for all ImageData encoder/decoder library interfaces.
|
||||
* We inherit from love::Object to take advantage of reference counting...
|
||||
**/
|
||||
class FormatHandler
|
||||
class FormatHandler : public love::Object
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -56,26 +58,32 @@ public:
|
||||
EncodedImage() : size(0), data(0) {}
|
||||
};
|
||||
|
||||
// Lets pretend we have virtual static methods...
|
||||
/**
|
||||
* The default constructor is called when the Image module is initialized.
|
||||
**/
|
||||
FormatHandler();
|
||||
|
||||
/**
|
||||
* Determines whether a particular FileData can be decoded by this handler.
|
||||
* @param data The data to decode.
|
||||
* The destructor is called when the Image module is uninitialized.
|
||||
**/
|
||||
// virtual static bool canDecode(love::filesystem::FileData *data) = 0;
|
||||
virtual ~FormatHandler();
|
||||
|
||||
/**
|
||||
* Determines whether this handler can encode to a particular format.
|
||||
* @param format The format to encode to.
|
||||
* Whether this format handler can decode a particular FileData.
|
||||
**/
|
||||
// virtual static bool canEncode(ImageData::Format format) = 0;
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
|
||||
/**
|
||||
* Whether this format handler can encode to a particular format.
|
||||
**/
|
||||
virtual bool canEncode(ImageData::Format format);
|
||||
|
||||
/**
|
||||
* Decodes an image from its encoded form into raw pixel data.
|
||||
* @param data The encoded data to decode.
|
||||
* @return The decoded pixel data.
|
||||
**/
|
||||
// virtual static DecodedImage decode(love::filesystem::FileData *data) = 0;
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
|
||||
/**
|
||||
* Encodes an image from raw pixel data into a particular format.
|
||||
@@ -83,7 +91,7 @@ public:
|
||||
* @param format The format to encode to.
|
||||
* @return The encoded image data.
|
||||
**/
|
||||
// virtual static EncodedImage encode(const DecodedImage &img, ImageData::Format format) = 0;
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
|
||||
|
||||
}; // FormatHandler
|
||||
|
||||
|
||||
@@ -34,12 +34,15 @@ namespace magpie
|
||||
|
||||
Image::Image()
|
||||
{
|
||||
DevilHandler::init();
|
||||
formatHandlers.push_back(new DevilHandler);
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
DevilHandler::quit();
|
||||
// ImageData objects reference the FormatHandlers in our list, so we should
|
||||
// release them instead of deleting them completely here.
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
(*it)->release();
|
||||
}
|
||||
|
||||
const char *Image::getName() const
|
||||
@@ -49,17 +52,17 @@ const char *Image::getName() const
|
||||
|
||||
love::image::ImageData *Image::newImageData(love::filesystem::FileData *data)
|
||||
{
|
||||
return new ImageData(data);
|
||||
return new ImageData(formatHandlers, data);
|
||||
}
|
||||
|
||||
love::image::ImageData *Image::newImageData(int width, int height)
|
||||
{
|
||||
return new ImageData(width, height);
|
||||
return new ImageData(formatHandlers, width, height);
|
||||
}
|
||||
|
||||
love::image::ImageData *Image::newImageData(int width, int height, void *data, bool own)
|
||||
{
|
||||
return new ImageData(width, height, data, own);
|
||||
return new ImageData(formatHandlers, width, height, data, own);
|
||||
}
|
||||
|
||||
love::image::CompressedData *Image::newCompressedData(love::filesystem::FileData *data)
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
// LOVE
|
||||
#include "image/Image.h"
|
||||
#include "FormatHandler.h"
|
||||
|
||||
// C++
|
||||
#include <list>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -54,6 +58,11 @@ public:
|
||||
|
||||
bool isCompressed(love::filesystem::FileData *data);
|
||||
|
||||
private:
|
||||
|
||||
// Image format handlers we can use for decoding and encoding ImageData.
|
||||
std::list<FormatHandler *> formatHandlers;
|
||||
|
||||
}; // Image
|
||||
|
||||
} // magpie
|
||||
|
||||
@@ -18,11 +18,9 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "ImageData.h"
|
||||
|
||||
#include "FormatHandler.h"
|
||||
#include "DevilHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -30,23 +28,36 @@ namespace image
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
ImageData::ImageData(love::filesystem::FileData *data)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data)
|
||||
: formatHandlers(formats)
|
||||
{
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
(*it)->retain();
|
||||
|
||||
decode(data);
|
||||
}
|
||||
|
||||
ImageData::ImageData(int width, int height)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
|
||||
: formatHandlers(formats)
|
||||
{
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
(*it)->retain();
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
create(width, height);
|
||||
|
||||
// Set to black/transparency.
|
||||
memset(data, 0, width*height*sizeof(pixel));
|
||||
}
|
||||
|
||||
ImageData::ImageData(int width, int height, void *data, bool own)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own)
|
||||
: formatHandlers(formats)
|
||||
{
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
(*it)->retain();
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
||||
@@ -59,6 +70,9 @@ ImageData::ImageData(int width, int height, void *data, bool own)
|
||||
ImageData::~ImageData()
|
||||
{
|
||||
delete[] data;
|
||||
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
(*it)->release();
|
||||
}
|
||||
|
||||
void ImageData::create(int width, int height, void *data)
|
||||
@@ -80,16 +94,23 @@ void ImageData::decode(love::filesystem::FileData *data)
|
||||
{
|
||||
FormatHandler::DecodedImage decodedimage;
|
||||
|
||||
if (DevilHandler::canDecode(data))
|
||||
decodedimage = DevilHandler::decode(data);
|
||||
else
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
{
|
||||
if ((*it)->canDecode(data))
|
||||
{
|
||||
decodedimage = (*it)->decode(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (decodedimage.data == nullptr)
|
||||
throw love::Exception("Could not decode image: unrecognized format.");
|
||||
|
||||
// The decoder *must* output a 32 bits-per-pixel image.
|
||||
if (decodedimage.size != decodedimage.width*decodedimage.height*sizeof(pixel))
|
||||
{
|
||||
delete[] decodedimage.data;
|
||||
throw love::Exception("Coult not convert image!");
|
||||
throw love::Exception("Could not convert image!");
|
||||
}
|
||||
|
||||
if (this->data)
|
||||
@@ -114,15 +135,21 @@ void ImageData::encode(love::filesystem::File *f, ImageData::Format format)
|
||||
rawimage.size = width*height*sizeof(pixel);
|
||||
rawimage.data = data;
|
||||
|
||||
if (DevilHandler::canEncode(format))
|
||||
encodedimage = DevilHandler::encode(rawimage, format);
|
||||
else
|
||||
for (auto it = formatHandlers.begin(); it != formatHandlers.end(); ++it)
|
||||
{
|
||||
if ((*it)->canEncode(format))
|
||||
{
|
||||
encodedimage = (*it)->encode(rawimage, format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (encodedimage.data == nullptr)
|
||||
throw love::Exception("Image format has no suitable encoder.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
f->open(love::filesystem::File::WRITE);
|
||||
f->write(encodedimage.data, encodedimage.size);
|
||||
f->close();
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
#define LOVE_IMAGE_MAGPIE_IMAGE_DATA_H
|
||||
|
||||
// LOVE
|
||||
#include "FormatHandler.h"
|
||||
#include "filesystem/File.h"
|
||||
#include "image/ImageData.h"
|
||||
|
||||
// C++
|
||||
#include <list>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -36,9 +40,9 @@ class ImageData : public love::image::ImageData
|
||||
{
|
||||
public:
|
||||
|
||||
ImageData(love::filesystem::FileData *data);
|
||||
ImageData(int width, int height);
|
||||
ImageData(int width, int height, void *data, bool own);
|
||||
ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data);
|
||||
ImageData(std::list<FormatHandler *> formats, int width, int height);
|
||||
ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own);
|
||||
virtual ~ImageData();
|
||||
|
||||
// Implements image::ImageData.
|
||||
@@ -52,6 +56,9 @@ private:
|
||||
// Decode and load an encoded format.
|
||||
void decode(love::filesystem::FileData *data);
|
||||
|
||||
// Image format handlers we can use for decoding and encoding.
|
||||
std::list<FormatHandler *> formatHandlers;
|
||||
|
||||
}; // ImageData
|
||||
|
||||
} // magpie
|
||||
|
||||
@@ -220,6 +220,17 @@ StringMap<Keyboard::Key, Keyboard::KEY_MAX_ENUM>::Entry Keyboard::keyEntries[] =
|
||||
{"audioplay", Keyboard::KEY_AUDIOPLAY},
|
||||
{"audiomute", Keyboard::KEY_AUDIOMUTE},
|
||||
{"mediaselect", Keyboard::KEY_MEDIASELECT},
|
||||
{"www", Keyboard::KEY_WWW},
|
||||
{"mail", Keyboard::KEY_MAIL},
|
||||
{"calculator", Keyboard::KEY_CALCULATOR},
|
||||
{"computer", Keyboard::KEY_COMPUTER},
|
||||
{"appsearch", Keyboard::KEY_APP_SEARCH},
|
||||
{"apphome", Keyboard::KEY_APP_HOME},
|
||||
{"appback", Keyboard::KEY_APP_BACK},
|
||||
{"appforward", Keyboard::KEY_APP_FORWARD},
|
||||
{"appstop", Keyboard::KEY_APP_STOP},
|
||||
{"apprefresh", Keyboard::KEY_APP_REFRESH},
|
||||
{"appbookmarks", Keyboard::KEY_APP_BOOKMARKS},
|
||||
|
||||
{"brightnessdown", Keyboard::KEY_BRIGHTNESSDOWN},
|
||||
{"brightnessup", Keyboard::KEY_BRIGHTNESSUP},
|
||||
|
||||
@@ -220,6 +220,17 @@ public:
|
||||
KEY_AUDIOPLAY,
|
||||
KEY_AUDIOMUTE,
|
||||
KEY_MEDIASELECT,
|
||||
KEY_WWW,
|
||||
KEY_MAIL,
|
||||
KEY_CALCULATOR,
|
||||
KEY_COMPUTER,
|
||||
KEY_APP_SEARCH,
|
||||
KEY_APP_HOME,
|
||||
KEY_APP_BACK,
|
||||
KEY_APP_FORWARD,
|
||||
KEY_APP_STOP,
|
||||
KEY_APP_REFRESH,
|
||||
KEY_APP_BOOKMARKS,
|
||||
|
||||
KEY_BRIGHTNESSDOWN,
|
||||
KEY_BRIGHTNESSUP,
|
||||
|
||||
@@ -264,6 +264,17 @@ std::map<Keyboard::Key, SDL_Keycode> Keyboard::createKeyMap()
|
||||
k[Keyboard::KEY_AUDIOPLAY] = SDLK_AUDIOPLAY;
|
||||
k[Keyboard::KEY_AUDIOMUTE] = SDLK_AUDIOMUTE;
|
||||
k[Keyboard::KEY_MEDIASELECT] = SDLK_MEDIASELECT;
|
||||
k[Keyboard::KEY_WWW] = SDLK_WWW;
|
||||
k[Keyboard::KEY_MAIL] = SDLK_MAIL;
|
||||
k[Keyboard::KEY_CALCULATOR] = SDLK_CALCULATOR;
|
||||
k[Keyboard::KEY_COMPUTER] = SDLK_COMPUTER;
|
||||
k[Keyboard::KEY_APP_SEARCH] = SDLK_AC_SEARCH;
|
||||
k[Keyboard::KEY_APP_HOME] = SDLK_AC_HOME;
|
||||
k[Keyboard::KEY_APP_BACK] = SDLK_AC_BACK;
|
||||
k[Keyboard::KEY_APP_FORWARD] = SDLK_AC_FORWARD;
|
||||
k[Keyboard::KEY_APP_STOP] = SDLK_AC_STOP;
|
||||
k[Keyboard::KEY_APP_REFRESH] = SDLK_AC_REFRESH;
|
||||
k[Keyboard::KEY_APP_BOOKMARKS] = SDLK_AC_BOOKMARKS;
|
||||
|
||||
k[Keyboard::KEY_BRIGHTNESSDOWN] = SDLK_BRIGHTNESSDOWN;
|
||||
k[Keyboard::KEY_BRIGHTNESSUP] = SDLK_BRIGHTNESSUP;
|
||||
|
||||
@@ -170,6 +170,15 @@ const char *love_codename()
|
||||
return love::VERSION_CODENAME;
|
||||
}
|
||||
|
||||
static int w_love_getVersion(lua_State *L)
|
||||
{
|
||||
lua_pushinteger(L, love::VERSION_MAJOR);
|
||||
lua_pushinteger(L, love::VERSION_MINOR);
|
||||
lua_pushinteger(L, love::VERSION_REV);
|
||||
lua_pushstring(L, love::VERSION_CODENAME);
|
||||
return 4;
|
||||
}
|
||||
|
||||
int luaopen_love(lua_State * L)
|
||||
{
|
||||
love::luax_insistglobal(L, "love");
|
||||
@@ -203,6 +212,9 @@ int luaopen_love(lua_State * L)
|
||||
|
||||
lua_setfield(L, -2, "_version_compat");
|
||||
|
||||
lua_pushcfunction(L, w_love_getVersion);
|
||||
lua_setfield(L, -2, "getVersion");
|
||||
|
||||
#ifdef LOVE_WINDOWS
|
||||
lua_pushstring(L, "Windows");
|
||||
#elif defined(LOVE_MACOSX)
|
||||
|
||||
@@ -193,5 +193,35 @@ bool Math::isConvex(const std::vector<Vertex> &polygon)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
|
||||
**/
|
||||
float Math::gammaToLinear(float c) const
|
||||
{
|
||||
if (c > 1.0)
|
||||
return 1.0;
|
||||
else if (c < 0.0)
|
||||
return 0.0;
|
||||
else if (c <= 0.04045)
|
||||
return c / 12.92;
|
||||
else
|
||||
return powf((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29
|
||||
**/
|
||||
float Math::linearToGamma(float c) const
|
||||
{
|
||||
if (c > 1.0)
|
||||
return 1.0;
|
||||
else if (c < 0.0)
|
||||
return 0.0;
|
||||
else if (c < 0.0031308)
|
||||
return c * 12.92;
|
||||
else
|
||||
return 1.055 * powf(c, 0.41666) - 0.055;
|
||||
}
|
||||
|
||||
} // math
|
||||
} // love
|
||||
|
||||
@@ -53,26 +53,6 @@ public:
|
||||
virtual ~Math()
|
||||
{}
|
||||
|
||||
inline void setRandomSeed(RandomGenerator::Seed seed)
|
||||
{
|
||||
rng.setSeed(seed);
|
||||
}
|
||||
|
||||
inline void setRandomSeed(uint32 low, uint32 high)
|
||||
{
|
||||
rng.setSeed(low, high);
|
||||
}
|
||||
|
||||
inline RandomGenerator::Seed getRandomSeed() const
|
||||
{
|
||||
return rng.getSeed();
|
||||
}
|
||||
|
||||
inline void getRandomSeed(uint32 &low, uint32 &high) const
|
||||
{
|
||||
rng.getSeed(low, high);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc RandomGenerator::random()
|
||||
**/
|
||||
@@ -105,6 +85,26 @@ public:
|
||||
return rng.randomNormal(stddev);
|
||||
}
|
||||
|
||||
inline void setRandomSeed(RandomGenerator::Seed seed)
|
||||
{
|
||||
rng.setSeed(seed);
|
||||
}
|
||||
|
||||
inline RandomGenerator::Seed getRandomSeed() const
|
||||
{
|
||||
return rng.getSeed();
|
||||
}
|
||||
|
||||
inline void setRandomState(const std::string &statestr)
|
||||
{
|
||||
rng.setState(statestr);
|
||||
}
|
||||
|
||||
inline std::string getRandomState() const
|
||||
{
|
||||
return rng.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new random number generator.
|
||||
**/
|
||||
@@ -136,6 +136,16 @@ public:
|
||||
**/
|
||||
bool isConvex(const std::vector<Vertex> &polygon);
|
||||
|
||||
/**
|
||||
* Converts a value from the sRGB (gamma) colorspace to linear RGB.
|
||||
**/
|
||||
float gammaToLinear(float c) const;
|
||||
|
||||
/**
|
||||
* Converts a value from linear RGB to the sRGB (gamma) colorspace.
|
||||
**/
|
||||
float linearToGamma(float c) const;
|
||||
|
||||
/**
|
||||
* Calculate Simplex noise for the specified coordinate(s).
|
||||
*
|
||||
|
||||
@@ -20,8 +20,13 @@
|
||||
|
||||
#include "RandomGenerator.h"
|
||||
|
||||
// STL
|
||||
// C++
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// C
|
||||
#include <cstdlib>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -36,25 +41,10 @@ RandomGenerator::RandomGenerator()
|
||||
{
|
||||
// because it is too big for some compilers to handle ... if you know what
|
||||
// i mean
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
seed.b32.a = 0x0139408D;
|
||||
seed.b32.b = 0xCBBF7A44;
|
||||
#else
|
||||
seed.b32.b = 0x0139408D;
|
||||
seed.b32.a = 0xCBBF7A44;
|
||||
#endif
|
||||
|
||||
rng_state = seed;
|
||||
}
|
||||
|
||||
void RandomGenerator::setSeed(RandomGenerator::Seed newseed)
|
||||
{
|
||||
// 0 xor 0 is still 0, so Xorshift can't generate new numbers.
|
||||
if (newseed.b64 == 0)
|
||||
throw love::Exception("Invalid random seed.");
|
||||
|
||||
seed = newseed;
|
||||
rng_state = seed;
|
||||
Seed newseed;
|
||||
newseed.b32.low = 0xCBBF7A44;
|
||||
newseed.b32.high = 0x0139408D;
|
||||
setSeed(newseed);
|
||||
}
|
||||
|
||||
uint64 RandomGenerator::rand()
|
||||
@@ -83,5 +73,71 @@ double RandomGenerator::randomNormal(double stddev)
|
||||
return r * sin(phi) * stddev;
|
||||
}
|
||||
|
||||
void RandomGenerator::setSeed(RandomGenerator::Seed newseed)
|
||||
{
|
||||
// 0 xor 0 is still 0, so Xorshift can't generate new numbers.
|
||||
if (newseed.b64 == 0)
|
||||
throw love::Exception("Invalid random seed.");
|
||||
|
||||
seed = newseed;
|
||||
rng_state = seed;
|
||||
}
|
||||
|
||||
RandomGenerator::Seed RandomGenerator::getSeed() const
|
||||
{
|
||||
return seed;
|
||||
}
|
||||
|
||||
void RandomGenerator::setState(const std::string &statestr)
|
||||
{
|
||||
// For this implementation we'll accept a hex string representing the
|
||||
// 64-bit state integer xorshift uses.
|
||||
|
||||
Seed state = {};
|
||||
|
||||
// Hex string must start with 0x.
|
||||
if (statestr.find("0x") != 0 || statestr.size() < 3)
|
||||
throw love::Exception("Invalid random state.");
|
||||
|
||||
// standardized strtoull (or 64 bit integer support for stringstream)
|
||||
// requires C++11's standard library, which we can't use yet.
|
||||
// I use strtol like this not because it's the best solution, but because
|
||||
// it's "good enough".
|
||||
|
||||
// Convert the hex string to the state integer character-by-character.
|
||||
for (size_t i = 2; i < statestr.size(); i++)
|
||||
{
|
||||
char hex[2] = {statestr[i], 0};
|
||||
char *end = nullptr;
|
||||
|
||||
// Convert the current hex character to a number.
|
||||
int nibble = strtol(hex, &end, 16);
|
||||
|
||||
// Check if strtol failed to convert it.
|
||||
if (end != nullptr && *end != 0)
|
||||
throw love::Exception("Invalid random state.");
|
||||
|
||||
state.b64 = (state.b64 << 4) + nibble;
|
||||
}
|
||||
|
||||
rng_state = state;
|
||||
}
|
||||
|
||||
std::string RandomGenerator::getState() const
|
||||
{
|
||||
// For this implementation we'll return a hex string representing the 64-bit
|
||||
// state integer xorshift uses.
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "0x";
|
||||
|
||||
// Again with the stringstream not dealing with 64 bit integers...
|
||||
ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.high;
|
||||
ss << std::setfill('0') << std::setw(8) << std::hex << rng_state.b32.low;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // math
|
||||
} // love
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
#include "common/int.h"
|
||||
#include "common/Object.h"
|
||||
|
||||
// STL
|
||||
// C++
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -45,54 +46,19 @@ public:
|
||||
uint64 b64;
|
||||
struct
|
||||
{
|
||||
uint32 a;
|
||||
uint32 b;
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
uint32 high;
|
||||
uint32 low;
|
||||
#else
|
||||
uint32 low;
|
||||
uint32 high;
|
||||
#endif
|
||||
} b32;
|
||||
};
|
||||
|
||||
RandomGenerator();
|
||||
virtual ~RandomGenerator() {}
|
||||
|
||||
/**
|
||||
* Set pseudo-random seed.
|
||||
* It's up to the implementation how to use this.
|
||||
**/
|
||||
void setSeed(Seed seed);
|
||||
|
||||
/**
|
||||
* Separately set the low and high bits of the pseudo-random seed.
|
||||
**/
|
||||
inline void setSeed(uint32 low, uint32 high)
|
||||
{
|
||||
Seed newseed;
|
||||
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
newseed.b32.a = high;
|
||||
newseed.b32.b = low;
|
||||
#else
|
||||
newseed.b32.b = high;
|
||||
newseed.b32.a = low;
|
||||
#endif
|
||||
|
||||
setSeed(newseed);
|
||||
}
|
||||
|
||||
inline Seed getSeed() const
|
||||
{
|
||||
return seed;
|
||||
}
|
||||
|
||||
inline void getSeed(uint32 &low, uint32 &high) const
|
||||
{
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
high = seed.b32.a;
|
||||
low = seed.b32.b;
|
||||
#else
|
||||
high = seed.b32.b;
|
||||
low = seed.b32.a;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Return uniformly distributed pseudo random integer.
|
||||
*
|
||||
@@ -138,6 +104,28 @@ public:
|
||||
**/
|
||||
double randomNormal(double stddev);
|
||||
|
||||
/**
|
||||
* Set pseudo-random seed.
|
||||
* It's up to the implementation how to use this.
|
||||
**/
|
||||
void setSeed(Seed seed);
|
||||
|
||||
/**
|
||||
* Get the previously set pseudo-random seed.
|
||||
**/
|
||||
Seed getSeed() const;
|
||||
|
||||
/**
|
||||
* Set the internal implementation-dependent state value based on a string.
|
||||
**/
|
||||
void setState(const std::string &statestr);
|
||||
|
||||
/**
|
||||
* Get a string representation of the implementation-dependent internal
|
||||
* state value.
|
||||
**/
|
||||
std::string getState() const;
|
||||
|
||||
private:
|
||||
|
||||
Seed seed;
|
||||
|
||||
@@ -32,21 +32,6 @@ namespace love
|
||||
namespace math
|
||||
{
|
||||
|
||||
int w_setRandomSeed(lua_State *L)
|
||||
{
|
||||
EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_getRandomSeed(lua_State *L)
|
||||
{
|
||||
uint32 low = 0, high = 0;
|
||||
Math::instance.getRandomSeed(low, high);
|
||||
lua_pushnumber(L, (lua_Number) low);
|
||||
lua_pushnumber(L, (lua_Number) high);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_random(lua_State *L)
|
||||
{
|
||||
return luax_getrandom(L, 1, Math::instance.random());
|
||||
@@ -62,6 +47,32 @@ int w_randomNormal(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setRandomSeed(lua_State *L)
|
||||
{
|
||||
EXCEPT_GUARD(Math::instance.setRandomSeed(luax_checkrandomseed(L, 1));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_getRandomSeed(lua_State *L)
|
||||
{
|
||||
RandomGenerator::Seed s = Math::instance.getRandomSeed();
|
||||
lua_pushnumber(L, (lua_Number) s.b32.low);
|
||||
lua_pushnumber(L, (lua_Number) s.b32.high);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_setRandomState(lua_State *L)
|
||||
{
|
||||
EXCEPT_GUARD(Math::instance.setRandomState(luax_checkstring(L, 1));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_getRandomState(lua_State *L)
|
||||
{
|
||||
luax_pushstring(L, Math::instance.getRandomState());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_newRandomGenerator(lua_State *L)
|
||||
{
|
||||
RandomGenerator::Seed s;
|
||||
@@ -238,6 +249,70 @@ int w_isConvex(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getGammaArgs(lua_State *L, float color[4])
|
||||
{
|
||||
int numcomponents = 0;
|
||||
|
||||
if (lua_istable(L, 1))
|
||||
{
|
||||
int n = lua_objlen(L, 1);
|
||||
for (int i = 1; i <= n && i <= 4; i++)
|
||||
{
|
||||
lua_rawgeti(L, 1, i);
|
||||
color[i - 1] = (float) luaL_checknumber(L, -1) / 255.0;
|
||||
numcomponents++;
|
||||
}
|
||||
|
||||
lua_pop(L, numcomponents);
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = lua_gettop(L);
|
||||
for (int i = 1; i <= n && i <= 4; i++)
|
||||
{
|
||||
color[i - 1] = (float) luaL_checknumber(L, i) / 255.0;
|
||||
numcomponents++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numcomponents == 0)
|
||||
luaL_checknumber(L, 1);
|
||||
|
||||
return numcomponents;
|
||||
}
|
||||
|
||||
int w_gammaToLinear(lua_State *L)
|
||||
{
|
||||
float color[4];
|
||||
int numcomponents = getGammaArgs(L, color);
|
||||
|
||||
for (int i = 0; i < numcomponents; i++)
|
||||
{
|
||||
// Alpha should always be linear.
|
||||
if (i < 3)
|
||||
color[i] = Math::instance.gammaToLinear(color[i]);
|
||||
lua_pushnumber(L, color[i] * 255);
|
||||
}
|
||||
|
||||
return numcomponents;
|
||||
}
|
||||
|
||||
int w_linearToGamma(lua_State *L)
|
||||
{
|
||||
float color[4];
|
||||
int numcomponents = getGammaArgs(L, color);
|
||||
|
||||
for (int i = 0; i < numcomponents; i++)
|
||||
{
|
||||
// Alpha should always be linear.
|
||||
if (i < 3)
|
||||
color[i] = Math::instance.linearToGamma(color[i]);
|
||||
lua_pushnumber(L, color[i] * 255);
|
||||
}
|
||||
|
||||
return numcomponents;
|
||||
}
|
||||
|
||||
int w_noise(lua_State *L)
|
||||
{
|
||||
float w, x, y, z;
|
||||
@@ -277,14 +352,18 @@ int w_noise(lua_State *L)
|
||||
// List of functions to wrap.
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "setRandomSeed", w_setRandomSeed },
|
||||
{ "getRandomSeed", w_getRandomSeed },
|
||||
{ "random", w_random },
|
||||
{ "randomNormal", w_randomNormal },
|
||||
{ "setRandomSeed", w_setRandomSeed },
|
||||
{ "getRandomSeed", w_getRandomSeed },
|
||||
{ "setRandomState", w_setRandomState },
|
||||
{ "getRandomState", w_getRandomState },
|
||||
{ "newRandomGenerator", w_newRandomGenerator },
|
||||
{ "newBezierCurve", w_newBezierCurve },
|
||||
{ "triangulate", w_triangulate },
|
||||
{ "isConvex", w_isConvex },
|
||||
{ "gammaToLinear", w_gammaToLinear },
|
||||
{ "linearToGamma", w_linearToGamma },
|
||||
{ "noise", w_noise },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@@ -30,14 +30,18 @@ namespace love
|
||||
namespace math
|
||||
{
|
||||
|
||||
int w_setRandomSeed(lua_State *L);
|
||||
int w_getRandomSeed(lua_State *L);
|
||||
int w_random(lua_State *L);
|
||||
int w_randomNormal(lua_State *L);
|
||||
int w_setRandomSeed(lua_State *L);
|
||||
int w_getRandomSeed(lua_State *L);
|
||||
int w_setRandomState(lua_State *L);
|
||||
int w_getRandomState(lua_State *L);
|
||||
int w_newRandomGenerator(lua_State *L);
|
||||
int w_newBezierCurve(lua_State *L);
|
||||
int w_triangulate(lua_State *L);
|
||||
int w_isConvex(lua_State *L);
|
||||
int w_gammaToLinear(lua_State *L);
|
||||
int w_linearToGamma(lua_State *L);
|
||||
int w_noise(lua_State *L);
|
||||
extern "C" LOVE_EXPORT int luaopen_love_math(lua_State *L);
|
||||
|
||||
|
||||
@@ -47,16 +47,8 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx)
|
||||
|
||||
if (!lua_isnoneornil(L, idx + 1))
|
||||
{
|
||||
uint32 low = checkrandomseed_part<uint32>(L, idx);
|
||||
uint32 high = checkrandomseed_part<uint32>(L, idx + 1);
|
||||
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
s.b32.a = high;
|
||||
s.b32.b = low;
|
||||
#else
|
||||
s.b32.b = high;
|
||||
s.b32.a = low;
|
||||
#endif
|
||||
s.b32.low = checkrandomseed_part<uint32>(L, idx);
|
||||
s.b32.high = checkrandomseed_part<uint32>(L, idx + 1);
|
||||
}
|
||||
else
|
||||
s.b64 = checkrandomseed_part<uint64>(L, idx);
|
||||
@@ -95,25 +87,6 @@ RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
|
||||
return luax_checktype<RandomGenerator>(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T);
|
||||
}
|
||||
|
||||
int w_RandomGenerator_setSeed(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_getSeed(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
|
||||
uint32 low = 0, high = 0;
|
||||
rng->getSeed(low, high);
|
||||
|
||||
lua_pushnumber(L, (lua_Number) low);
|
||||
lua_pushnumber(L, (lua_Number) high);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_random(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
@@ -132,12 +105,44 @@ int w_RandomGenerator_randomNormal(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_setSeed(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
EXCEPT_GUARD(rng->setSeed(luax_checkrandomseed(L, 2));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_getSeed(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
RandomGenerator::Seed s = rng->getSeed();
|
||||
lua_pushnumber(L, (lua_Number) s.b32.low);
|
||||
lua_pushnumber(L, (lua_Number) s.b32.high);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_setState(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
EXCEPT_GUARD(rng->setState(luax_checkstring(L, 2));)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_RandomGenerator_getState(lua_State *L)
|
||||
{
|
||||
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
|
||||
luax_pushstring(L, rng->getState());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "setSeed", w_RandomGenerator_setSeed },
|
||||
{ "getSeed", w_RandomGenerator_getSeed },
|
||||
{ "random", w_RandomGenerator_random },
|
||||
{ "randomNormal", w_RandomGenerator_randomNormal },
|
||||
{ "setSeed", w_RandomGenerator_setSeed },
|
||||
{ "getSeed", w_RandomGenerator_getSeed },
|
||||
{ "setState", w_RandomGenerator_setState },
|
||||
{ "getState", w_RandomGenerator_getState },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -36,10 +36,12 @@ RandomGenerator::Seed luax_checkrandomseed(lua_State *L, int idx);
|
||||
int luax_getrandom(lua_State *L, int startidx, double r);
|
||||
|
||||
RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
|
||||
int w_RandomGenerator_setSeed(lua_State *L);
|
||||
int w_RandomGenerator_getSeed(lua_State *L);
|
||||
int w_RandomGenerator_random(lua_State *L);
|
||||
int w_RandomGenerator_randomNormal(lua_State *L);
|
||||
int w_RandomGenerator_setSeed(lua_State *L);
|
||||
int w_RandomGenerator_getSeed(lua_State *L);
|
||||
int w_RandomGenerator_setState(lua_State *L);
|
||||
int w_RandomGenerator_getState(lua_State *L);
|
||||
extern "C" int luaopen_randomgenerator(lua_State *L);
|
||||
|
||||
} // math
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
BUTTON_MAX_ENUM
|
||||
};
|
||||
|
||||
virtual ~Mouse() {};
|
||||
virtual ~Mouse() {}
|
||||
|
||||
virtual Cursor *newCursor(love::image::ImageData *data, int hotx, int hoty) = 0;
|
||||
virtual Cursor *getSystemCursor(Cursor::SystemCursor cursortype) = 0;
|
||||
|
||||
@@ -32,6 +32,39 @@ namespace mouse
|
||||
namespace sdl
|
||||
{
|
||||
|
||||
// SDL reports mouse coordinates in the window coordinate system in OS X, but
|
||||
// we want them in pixel coordinates (may be different with high-DPI enabled.)
|
||||
static void windowToPixelCoords(int *x, int *y)
|
||||
{
|
||||
double scale = 1.0;
|
||||
|
||||
love::window::Window *window = love::window::sdl::Window::getSingleton();
|
||||
if (window != nullptr)
|
||||
scale = window->getPixelScale();
|
||||
|
||||
if (x != nullptr)
|
||||
*x = int(double(*x) * scale);
|
||||
|
||||
if (y != nullptr)
|
||||
*y = int(double(*y) * scale);
|
||||
}
|
||||
|
||||
// And vice versa for setting mouse coordinates.
|
||||
static void pixelToWindowCoords(int *x, int *y)
|
||||
{
|
||||
double scale = 1.0;
|
||||
|
||||
love::window::Window *window = love::window::sdl::Window::getSingleton();
|
||||
if (window != nullptr)
|
||||
scale = window->getPixelScale();
|
||||
|
||||
if (x != nullptr)
|
||||
*x = int(double(*x) / scale);
|
||||
|
||||
if (y != nullptr)
|
||||
*y = int(double(*y) / scale);
|
||||
}
|
||||
|
||||
const char *Mouse::getName() const
|
||||
{
|
||||
return "love.mouse.sdl";
|
||||
@@ -98,30 +131,40 @@ love::mouse::Cursor *Mouse::getCursor() const
|
||||
int Mouse::getX() const
|
||||
{
|
||||
int x;
|
||||
SDL_GetMouseState(&x, 0);
|
||||
SDL_GetMouseState(&x, nullptr);
|
||||
windowToPixelCoords(&x, nullptr);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
int Mouse::getY() const
|
||||
{
|
||||
int y;
|
||||
SDL_GetMouseState(0, &y);
|
||||
SDL_GetMouseState(nullptr, &y);
|
||||
windowToPixelCoords(nullptr, &y);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
void Mouse::getPosition(int &x, int &y) const
|
||||
{
|
||||
SDL_GetMouseState(&x, &y);
|
||||
int mx, my;
|
||||
SDL_GetMouseState(&mx, &my);
|
||||
windowToPixelCoords(&mx, &my);
|
||||
|
||||
x = mx;
|
||||
y = my;
|
||||
}
|
||||
|
||||
void Mouse::setPosition(int x, int y)
|
||||
{
|
||||
love::window::Window *window = love::window::sdl::Window::getSingleton();
|
||||
|
||||
SDL_Window *handle = NULL;
|
||||
SDL_Window *handle = nullptr;
|
||||
if (window)
|
||||
handle = (SDL_Window *) window->getHandle();
|
||||
|
||||
pixelToWindowCoords(&x, &y);
|
||||
SDL_WarpMouseInWindow(handle, x, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ int w_Cursor_getType(lua_State *L)
|
||||
|
||||
lua_pushstring(L, typestr);
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
@@ -65,4 +65,4 @@ extern "C" int luaopen_cursor(lua_State *L)
|
||||
}
|
||||
|
||||
} // mouse
|
||||
} // love
|
||||
} // love
|
||||
|
||||
@@ -37,10 +37,14 @@ namespace box2d
|
||||
|
||||
Body::Body(World *world, b2Vec2 p, Body::Type type)
|
||||
: world(world)
|
||||
, udata(nullptr)
|
||||
{
|
||||
udata = new bodyudata();
|
||||
udata->ref = nullptr;
|
||||
world->retain();
|
||||
b2BodyDef def;
|
||||
def.position = Physics::scaleDown(p);
|
||||
def.userData = (void *) udata;
|
||||
body = world->world->CreateBody(&def);
|
||||
// Box2D body holds a reference to the love Body.
|
||||
this->retain();
|
||||
@@ -50,7 +54,9 @@ Body::Body(World *world, b2Vec2 p, Body::Type type)
|
||||
|
||||
Body::Body(b2Body *b)
|
||||
: body(b)
|
||||
, udata(nullptr)
|
||||
{
|
||||
udata = (bodyudata *) b->GetUserData();
|
||||
world = (World *)Memoizer::find(b->GetWorld());
|
||||
world->retain();
|
||||
// Box2D body holds a reference to the love Body.
|
||||
@@ -60,8 +66,10 @@ Body::Body(b2Body *b)
|
||||
|
||||
Body::~Body()
|
||||
{
|
||||
if (udata != nullptr)
|
||||
delete udata->ref;
|
||||
delete udata;
|
||||
world->release();
|
||||
body = 0;
|
||||
}
|
||||
|
||||
float Body::getX()
|
||||
@@ -469,6 +477,40 @@ void Body::destroy()
|
||||
this->release();
|
||||
}
|
||||
|
||||
int Body::setUserData(lua_State *L)
|
||||
{
|
||||
love::luax_assert_argc(L, 1, 1);
|
||||
|
||||
if (udata == nullptr)
|
||||
{
|
||||
udata = new bodyudata();
|
||||
body->SetUserData((void *) udata);
|
||||
}
|
||||
|
||||
if (udata->ref != nullptr)
|
||||
{
|
||||
// We set the Reference's lua_State to this one before deleting it, so
|
||||
// it unrefs using the current lua_State's stack. This is necessary
|
||||
// if setUserData is called in a coroutine.
|
||||
udata->ref->setL(L);
|
||||
delete udata->ref;
|
||||
}
|
||||
|
||||
udata->ref = new Reference(L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Body::getUserData(lua_State *L)
|
||||
{
|
||||
if (udata != nullptr && udata->ref != nullptr)
|
||||
udata->ref->push(L);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // box2d
|
||||
} // physics
|
||||
} // love
|
||||
|
||||
@@ -41,6 +41,16 @@ class World;
|
||||
class Shape;
|
||||
class Fixture;
|
||||
|
||||
/**
|
||||
* This struct is stored in a void pointer in the Box2D Body class. For now, all
|
||||
* we need is a Lua reference to arbitrary data, but we might need more later.
|
||||
**/
|
||||
struct bodyudata
|
||||
{
|
||||
// Reference to arbitrary data.
|
||||
Reference *ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Body is an entity which has position and orientation
|
||||
* in world space. A Body does have collision geometry
|
||||
@@ -388,6 +398,18 @@ public:
|
||||
**/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* This function stores an in-C reference to
|
||||
* arbitrary Lua data in the Box2D Body object.
|
||||
**/
|
||||
int setUserData(lua_State *L);
|
||||
|
||||
/**
|
||||
* Gets the data set with setData. If no
|
||||
* data is set, nil is returned.
|
||||
**/
|
||||
int getUserData(lua_State *L);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -408,7 +430,10 @@ private:
|
||||
// This ensures that a World only can be destroyed
|
||||
// once all bodies have been destroyed too.
|
||||
World *world;
|
||||
};
|
||||
|
||||
bodyudata *udata;
|
||||
|
||||
}; // Body
|
||||
|
||||
} // box2d
|
||||
} // physics
|
||||
|
||||
@@ -39,10 +39,10 @@ namespace box2d
|
||||
|
||||
Fixture::Fixture(Body *body, Shape *shape, float density)
|
||||
: body(body)
|
||||
, fixture(NULL)
|
||||
, fixture(nullptr)
|
||||
{
|
||||
data = new fixtureudata();
|
||||
data->ref = 0;
|
||||
data->ref = nullptr;
|
||||
b2FixtureDef def;
|
||||
def.shape = shape->shape;
|
||||
def.userData = (void *)data;
|
||||
@@ -65,13 +65,10 @@ Fixture::Fixture(b2Fixture *f)
|
||||
|
||||
Fixture::~Fixture()
|
||||
{
|
||||
if (data->ref != 0)
|
||||
if (data != nullptr)
|
||||
delete data->ref;
|
||||
|
||||
delete data;
|
||||
data = NULL;
|
||||
|
||||
fixture = NULL;
|
||||
}
|
||||
|
||||
Shape::Type Fixture::getType() const
|
||||
@@ -127,14 +124,14 @@ Body *Fixture::getBody() const
|
||||
Shape *Fixture::getShape() const
|
||||
{
|
||||
if (!fixture->GetShape())
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
return new Shape(fixture->GetShape(), false);
|
||||
}
|
||||
|
||||
bool Fixture::isValid() const
|
||||
{
|
||||
return fixture != 0;
|
||||
return fixture != nullptr;
|
||||
}
|
||||
|
||||
void Fixture::setFilterData(int *v)
|
||||
@@ -230,21 +227,24 @@ int Fixture::setUserData(lua_State *L)
|
||||
{
|
||||
love::luax_assert_argc(L, 1, 1);
|
||||
|
||||
if (data->ref != 0)
|
||||
if (data->ref != nullptr)
|
||||
{
|
||||
// We set the Reference's lua_State to this one before deleting it, so
|
||||
// it unrefs using the current lua_State's stack. This is necessary
|
||||
// if setUserData is called in a coroutine.
|
||||
data->ref->setL(L);
|
||||
delete data->ref;
|
||||
data->ref = 0;
|
||||
}
|
||||
|
||||
data->ref = new Reference(L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fixture::getUserData(lua_State *L)
|
||||
{
|
||||
love::luax_assert_argc(L, 0, 0);
|
||||
if (data->ref != 0)
|
||||
data->ref->push();
|
||||
if (data->ref != nullptr)
|
||||
data->ref->push(L);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
@@ -311,10 +311,10 @@ void Fixture::destroy(bool implicit)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!implicit && fixture != 0)
|
||||
if (!implicit && fixture != nullptr)
|
||||
body->body->DestroyFixture(fixture);
|
||||
Memoizer::remove(fixture);
|
||||
fixture = NULL;
|
||||
fixture = nullptr;
|
||||
|
||||
// Box2D fixture destroyed. Release its reference to the love Fixture.
|
||||
this->release();
|
||||
|
||||
@@ -123,9 +123,6 @@ public:
|
||||
/**
|
||||
* This function stores an in-C reference to
|
||||
* arbitrary Lua data in the Box2D Fixture object.
|
||||
*
|
||||
* The data set here will be passed to the collision
|
||||
* handler when collisions occur.
|
||||
**/
|
||||
int setUserData(lua_State *L);
|
||||
|
||||
|
||||
@@ -93,8 +93,9 @@ EdgeShape *Physics::newEdgeShape(float x1, float y1, float x2, float y2)
|
||||
int Physics::newPolygonShape(lua_State *L)
|
||||
{
|
||||
int argc = lua_gettop(L);
|
||||
if (argc%2 != 0)
|
||||
return luaL_error(L, "Number of vertices must be a multiple of two.");
|
||||
if (argc % 2 != 0)
|
||||
return luaL_error(L, "Number of vertex components must be a multiple of two.");
|
||||
|
||||
// 3 to 8 (b2_maxPolygonVertices) vertices
|
||||
int vcount = argc / 2;
|
||||
if (vcount < 3)
|
||||
|
||||
@@ -538,6 +538,20 @@ int w_Body_destroy(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w_Body_setUserData(lua_State *L)
|
||||
{
|
||||
Body *t = luax_checkbody(L, 1);
|
||||
lua_remove(L, 1);
|
||||
return t->setUserData(L);
|
||||
}
|
||||
|
||||
int w_Body_getUserData(lua_State *L)
|
||||
{
|
||||
Body *t = luax_checkbody(L, 1);
|
||||
lua_remove(L, 1);
|
||||
return t->getUserData(L);
|
||||
}
|
||||
|
||||
static const luaL_Reg functions[] =
|
||||
{
|
||||
{ "getX", w_Body_getX },
|
||||
@@ -592,6 +606,8 @@ static const luaL_Reg functions[] =
|
||||
{ "isFixedRotation", w_Body_isFixedRotation },
|
||||
{ "getFixtureList", w_Body_getFixtureList },
|
||||
{ "destroy", w_Body_destroy },
|
||||
{ "setUserData", w_Body_setUserData },
|
||||
{ "getUserData", w_Body_getUserData },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ int w_Body_setFixedRotation(lua_State *L);
|
||||
int w_Body_isFixedRotation(lua_State *L);
|
||||
int w_Body_getFixtureList(lua_State *L);
|
||||
int w_Body_destroy(lua_State *L);
|
||||
int w_Body_setUserData(lua_State *L);
|
||||
int w_Body_getUserData(lua_State *L);
|
||||
extern "C" int luaopen_body(lua_State *L);
|
||||
|
||||
} // box2d
|
||||
|
||||
@@ -140,4 +140,4 @@ extern "C" int luaopen_motorjoint(lua_State *L)
|
||||
|
||||
} // box2d
|
||||
} // phyics
|
||||
} // love
|
||||
} // love
|
||||
|
||||
@@ -143,21 +143,27 @@ int w_World_getBodyList(lua_State *L)
|
||||
{
|
||||
World *t = luax_checkworld(L, 1);
|
||||
lua_remove(L, 1);
|
||||
return t->getBodyList(L);
|
||||
int ret = 0;
|
||||
EXCEPT_GUARD(ret = t->getBodyList(L);)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int w_World_getJointList(lua_State *L)
|
||||
{
|
||||
World *t = luax_checkworld(L, 1);
|
||||
lua_remove(L, 1);
|
||||
return t->getJointList(L);
|
||||
int ret = 0;
|
||||
EXCEPT_GUARD(ret = t->getJointList(L);)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int w_World_getContactList(lua_State *L)
|
||||
{
|
||||
World *t = luax_checkworld(L, 1);
|
||||
lua_remove(L, 1);
|
||||
return t->getContactList(L);
|
||||
int ret = 0;
|
||||
EXCEPT_GUARD(ret = t->getContactList(L);)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int w_World_queryBoundingBox(lua_State *L)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user