imported Löve GLES branch (changeset 1ba9037e558b)

This commit is contained in:
Martin Felis
2013-12-05 18:05:13 +01:00
parent 41b2db04a7
commit 2644d1ee18
615 changed files with 150300 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2006-2013 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 Audio 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 Audio distribution.
**/
#include "Audio.h"
namespace love
{
namespace audio
{
StringMap<Audio::DistanceModel, Audio::DISTANCE_MAX_ENUM>::Entry Audio::distanceModelEntries[] =
{
{"none", Audio::DISTANCE_NONE},
{"inverse", Audio::DISTANCE_INVERSE},
{"inverse clamped", Audio::DISTANCE_INVERSE_CLAMPED},
{"linear", Audio::DISTANCE_LINEAR},
{"linear clamped", Audio::DISTANCE_LINEAR_CLAMPED},
{"exponent", Audio::DISTANCE_EXPONENT},
{"exponent clamped", Audio::DISTANCE_EXPONENT_CLAMPED}
};
StringMap<Audio::DistanceModel, Audio::DISTANCE_MAX_ENUM> Audio::distanceModels(Audio::distanceModelEntries, sizeof(Audio::distanceModelEntries));
bool Audio::getConstant(const char *in, DistanceModel &out)
{
return distanceModels.find(in, out);
}
bool Audio::getConstant(DistanceModel in, const char *&out)
{
return distanceModels.find(in, out);
}
} // audio
} // love
+239
View File
@@ -0,0 +1,239 @@
/**
* Copyright (c) 2006-2013 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 = 0; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_AUDIO_H
#define LOVE_AUDIO_AUDIO_H
#include "common/Module.h"
#include "common/StringMap.h"
#include "Source.h"
namespace love
{
namespace sound
{
class Decoder;
class SoundData;
} // sound
namespace audio
{
/**
* The Audio module is responsible for playing back raw sound samples.
**/
class Audio : public Module
{
public:
/**
* Attenuation by distance.
*/
enum DistanceModel
{
DISTANCE_NONE = 1,
DISTANCE_INVERSE,
DISTANCE_INVERSE_CLAMPED,
DISTANCE_LINEAR,
DISTANCE_LINEAR_CLAMPED,
DISTANCE_EXPONENT,
DISTANCE_EXPONENT_CLAMPED,
DISTANCE_MAX_ENUM
};
static bool getConstant(const char *in, DistanceModel &out);
static bool getConstant(DistanceModel in, const char *&out);
/**
* Destructor.
**/
virtual ~Audio() {};
virtual Source *newSource(love::sound::Decoder *decoder) = 0;
virtual Source *newSource(love::sound::SoundData *soundData) = 0;
/**
* Gets the current number of simultaneous playing sources.
* @return The current number of simultaneous playing sources.
**/
virtual int getSourceCount() const = 0;
/**
* Gets the maximum supported number of simultaneous playing sources.
* @return The maximum supported number of simultaneous playing sources.
**/
virtual int getMaxSources() const = 0;
/**
* Play the specified Source.
* @param source The Source to play.
**/
virtual void play(Source *source) = 0;
/**
* Stops playback on the specified source.
* @param source The source on which to stop the playback.
**/
virtual void stop(Source *source) = 0;
/**
* Stops all playing audio.
**/
virtual void stop() = 0;
/**
* Pauses playback on the specified source.
* @param source The source on which to pause the playback.
**/
virtual void pause(Source *source) = 0;
/**
* Pauses all audio.
**/
virtual void pause() = 0;
/**
* Resumes playback on the specified source.
* @param source The source on which to resume the playback.
**/
virtual void resume(Source *source) = 0;
/**
* Resumes all audio.
**/
virtual void resume() = 0;
/**
* Rewinds the specified source. Whatever is playing on this
* source gets rewound to the start.
* @param source The source to rewind.
**/
virtual void rewind(Source *source) = 0;
/**
* Rewinds all playing audio.
**/
virtual void rewind() = 0;
/**
* Sets the master volume, where 0.0f is min (off) and 1.0f is max.
* @param volume The new master volume.
**/
virtual void setVolume(float volume) = 0;
/**
* Gets the master volume.
* @return The current master volume.
**/
virtual float getVolume() const = 0;
/**
* Gets the position of the listener.
* @param v A float array of size 3 containing (x,y,z) in that order.
**/
virtual void getPosition(float *v) const = 0;
/**
* Sets the position of the listener.
* @param v A float array of size 3 containing [x,y,z] in that order.
**/
virtual void setPosition(float *v) = 0;
/**
* Gets the orientation of the listener.
* @param v A float array of size 6 containing [x,y,z] for the forward
* vector, followed by [x,y,z] for the up vector.
**/
virtual void getOrientation(float *v) const = 0;
/**
* Sets the orientation of the listener.
* @param v A float array of size 6 containing [x,y,z] for the forward
* vector, followed by [x,y,z] for the up vector.
**/
virtual void setOrientation(float *v) = 0;
/**
* Gets the velocity of the listener.
* @param v A float array of size 3 containing [x,y,z] in that order.
**/
virtual void getVelocity(float *v) const = 0;
/**
* Sets the velocity of the listener.
* @param v A float array of size 3 containing [x,y,z] in that order.
**/
virtual void setVelocity(float *v) = 0;
/**
* Begins recording audio input from the microphone.
**/
virtual void record() = 0;
/**
* Gets a section of recorded audio.
* Per OpenAL, the measurement begins from the start of the
* audio data in memory, which is after the last time this function
* was called. If this function has not been called yet this recording
* session, it just grabs from the beginning.
* @return All the recorded SoundData thus far.
**/
virtual love::sound::SoundData *getRecordedData() = 0;
/**
* Stops recording and, if passed true, returns all the recorded audio
* not already gotten by getRecordedData.
* @param returnData Whether to return recorded audio.
* @return if returnData, all the recorded audio yet to be gotten,
* otherwise NULL.
**/
virtual love::sound::SoundData *stopRecording(bool returnData) = 0;
/**
* Checks whether LOVE is able to record audio input.
* @return hasMic Whether LOVE has a microphone enabled.
**/
virtual bool canRecord() = 0;
/**
* Gets the distance model used for attenuation.
* @return Distance model.
*/
virtual DistanceModel getDistanceModel() const = 0;
/**
* Sets the distance model used for attenuation.
* @param distanceModel Distance model.
*/
virtual void setDistanceModel(DistanceModel distanceModel) = 0;
private:
static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];
static StringMap<DistanceModel, DISTANCE_MAX_ENUM> distanceModels;
}; // Audio
} // audio
} // love
#endif // LOVE_AUDIO_AUDIO_H
+74
View File
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Source.h"
namespace love
{
namespace audio
{
Source::Source(Type type)
: type(type)
{
}
Source::~Source()
{
}
bool Source::getConstant(const char *in, Type &out)
{
return types.find(in, out);
}
bool Source::getConstant(Type in, const char *&out)
{
return types.find(in, out);
}
bool Source::getConstant(const char *in, Unit &out)
{
return units.find(in, out);
}
bool Source::getConstant(Unit in, const char *&out)
{
return units.find(in, out);
}
StringMap<Source::Type, Source::TYPE_MAX_ENUM>::Entry Source::typeEntries[] =
{
{"static", Source::TYPE_STATIC},
{"stream", Source::TYPE_STREAM},
};
StringMap<Source::Type, Source::TYPE_MAX_ENUM> Source::types(Source::typeEntries, sizeof(Source::typeEntries));
StringMap<Source::Unit, Source::UNIT_MAX_ENUM>::Entry Source::unitEntries[] =
{
{"seconds", Source::UNIT_SECONDS},
{"samples", Source::UNIT_SAMPLES},
};
StringMap<Source::Unit, Source::UNIT_MAX_ENUM> Source::units(Source::unitEntries, sizeof(Source::unitEntries));
} // audio
} // love
+127
View File
@@ -0,0 +1,127 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_SOURCE_H
#define LOVE_AUDIO_SOURCE_H
// LOVE
#include "common/Object.h"
#include "common/StringMap.h"
namespace love
{
namespace audio
{
class Source : public Object
{
public:
enum Type
{
TYPE_STATIC = 1,
TYPE_STREAM,
TYPE_MAX_ENUM
}; // Type
enum Unit
{
UNIT_SECONDS = 1,
UNIT_SAMPLES,
UNIT_MAX_ENUM
};
Source(Type type);
virtual ~Source();
virtual Source *copy() = 0;
virtual void play() = 0;
virtual void stop() = 0;
virtual void pause() = 0;
virtual void resume() = 0;
virtual void rewind() = 0;
virtual bool isStopped() const = 0;
virtual bool isPaused() const = 0;
virtual bool isFinished() const = 0;
virtual bool update() = 0;
virtual void setPitch(float pitch) = 0;
virtual float getPitch() const = 0;
virtual void setVolume(float volume) = 0;
virtual float getVolume() const = 0;
virtual void seek(float offset, Unit unit) = 0;
virtual float tell(Unit unit) = 0;
// all float * v must be of size 3
virtual void setPosition(float *v) = 0;
virtual void getPosition(float *v) const = 0;
virtual void setVelocity(float *v) = 0;
virtual void getVelocity(float *v) const = 0;
virtual void setDirection(float *v) = 0;
virtual void getDirection(float *v) const = 0;
virtual void setCone(float innerAngle, float outerAngle, float outerVolume) = 0;
virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const = 0;
virtual void setRelative(bool enable) = 0;
virtual bool isRelative() const = 0;
virtual void setLooping(bool looping) = 0;
virtual bool isLooping() const = 0;
virtual bool isStatic() const = 0;
virtual void setMinVolume(float volume) = 0;
virtual float getMinVolume() const = 0;
virtual void setMaxVolume(float volume) = 0;
virtual float getMaxVolume() const = 0;
virtual void setReferenceDistance(float distance) = 0;
virtual float getReferenceDistance() const = 0;
virtual void setRolloffFactor(float factor) = 0;
virtual float getRolloffFactor() const = 0;
virtual void setMaxDistance(float distance) = 0;
virtual float getMaxDistance() const = 0;
virtual int getChannels() const = 0;
static bool getConstant(const char *in, Type &out);
static bool getConstant(Type in, const char *&out);
static bool getConstant(const char *in, Unit &out);
static bool getConstant(Unit in, const char *&out);
protected:
Type type;
private:
static StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[];
static StringMap<Type, TYPE_MAX_ENUM> types;
static StringMap<Unit, UNIT_MAX_ENUM>::Entry unitEntries[];
static StringMap<Unit, UNIT_MAX_ENUM> units;
}; // Source
} // audio
} // love
#endif // LOVE_AUDIO_SOURCE_H
+169
View File
@@ -0,0 +1,169 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Audio.h"
namespace love
{
namespace audio
{
namespace null
{
Audio::Audio()
: distanceModel(DISTANCE_NONE)
{
}
Audio::~Audio()
{
}
const char *Audio::getName() const
{
return "love.audio.null";
}
love::audio::Source *Audio::newSource(love::sound::Decoder *)
{
return new Source();
}
love::audio::Source *Audio::newSource(love::sound::SoundData *)
{
return new Source();
}
int Audio::getSourceCount() const
{
return 0;
}
int Audio::getMaxSources() const
{
return 0;
}
void Audio::play(love::audio::Source *)
{
}
void Audio::play()
{
}
void Audio::stop(love::audio::Source *)
{
}
void Audio::stop()
{
}
void Audio::pause(love::audio::Source *)
{
}
void Audio::pause()
{
}
void Audio::resume(love::audio::Source *)
{
}
void Audio::resume()
{
}
void Audio::rewind(love::audio::Source *)
{
}
void Audio::rewind()
{
}
void Audio::setVolume(float volume)
{
this->volume = volume;
}
float Audio::getVolume() const
{
return volume;
}
void Audio::getPosition(float *) const
{
}
void Audio::setPosition(float *)
{
}
void Audio::getOrientation(float *) const
{
}
void Audio::setOrientation(float *)
{
}
void Audio::getVelocity(float *) const
{
}
void Audio::setVelocity(float *)
{
}
void Audio::record()
{
}
love::sound::SoundData *Audio::getRecordedData()
{
return NULL;
}
love::sound::SoundData *Audio::stopRecording(bool)
{
return NULL;
}
bool Audio::canRecord()
{
return false;
}
Audio::DistanceModel Audio::getDistanceModel() const
{
return this->distanceModel;
}
void Audio::setDistanceModel(DistanceModel distanceModel)
{
this->distanceModel = distanceModel;
}
} // null
} // audio
} // love
+89
View File
@@ -0,0 +1,89 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_NULL_AUDIO_H
#define LOVE_AUDIO_NULL_AUDIO_H
// LOVE
#include "audio/Audio.h"
#include "Source.h"
namespace love
{
namespace audio
{
namespace null
{
class Audio : public love::audio::Audio
{
public:
Audio();
virtual ~Audio();
// Implements Module.
const char *getName() const;
// Implements Audio.
love::audio::Source *newSource(love::sound::Decoder *decoder);
love::audio::Source *newSource(love::sound::SoundData *soundData);
int getSourceCount() const;
int getMaxSources() const;
void play(love::audio::Source *source);
void play();
void stop(love::audio::Source *source);
void stop();
void pause(love::audio::Source *source);
void pause();
void resume(love::audio::Source *source);
void resume();
void rewind(love::audio::Source *source);
void rewind();
void setVolume(float volume);
float getVolume() const;
void getPosition(float *v) const;
void setPosition(float *v);
void getOrientation(float *v) const;
void setOrientation(float *v);
void getVelocity(float *v) const;
void setVelocity(float *v);
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
bool canRecord();
DistanceModel getDistanceModel() const;
void setDistanceModel(DistanceModel distanceModel);
private:
float volume;
DistanceModel distanceModel;
}; // Audio
} // null
} // audio
} // love
#endif // LOVE_AUDIO_NULL_AUDIO_H
+234
View File
@@ -0,0 +1,234 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Source.h"
namespace love
{
namespace audio
{
namespace null
{
Source::Source()
: love::audio::Source(Source::TYPE_STATIC)
{
}
Source::~Source()
{
}
love::audio::Source *Source::copy()
{
this->retain();
return this;
}
void Source::play()
{
}
void Source::stop()
{
}
void Source::pause()
{
}
void Source::resume()
{
}
void Source::rewind()
{
}
bool Source::isStopped() const
{
return true;
}
bool Source::isPaused() const
{
return false;
}
bool Source::isFinished() const
{
return true;
}
bool Source::update()
{
return false;
}
void Source::setPitch(float pitch)
{
this->pitch = pitch;
}
float Source::getPitch() const
{
return pitch;
}
void Source::setVolume(float volume)
{
this->volume = volume;
}
float Source::getVolume() const
{
return volume;
}
void Source::seek(float, Source::Unit)
{
}
float Source::tell(Source::Unit)
{
return 0.0f;
}
void Source::setPosition(float *)
{
}
void Source::getPosition(float *) const
{
}
void Source::setVelocity(float *)
{
}
void Source::getVelocity(float *) const
{
}
void Source::setDirection(float *)
{
}
void Source::getDirection(float *) const
{
}
void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
{
coneInnerAngle = innerAngle;
coneOuterAngle = outerAngle;
coneOuterVolume = outerVolume;
}
void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
{
innerAngle = coneInnerAngle;
outerAngle = coneOuterAngle;
outerVolume = coneOuterVolume;
}
void Source::setRelative(bool enable)
{
relative = enable;
}
bool Source::isRelative() const
{
return relative;
}
void Source::setLooping(bool looping)
{
this->looping = looping;
}
bool Source::isLooping() const
{
return looping;
}
bool Source::isStatic() const
{
return (type == TYPE_STATIC);
}
void Source::setMinVolume(float volume)
{
this->minVolume = volume;
}
float Source::getMinVolume() const
{
return this->minVolume;
}
void Source::setMaxVolume(float volume)
{
this->maxVolume = volume;
}
float Source::getMaxVolume() const
{
return this->maxVolume;
}
void Source::setReferenceDistance(float distance)
{
this->referenceDistance = distance;
}
float Source::getReferenceDistance() const
{
return this->referenceDistance;
}
void Source::setRolloffFactor(float factor)
{
this->rolloffFactor = factor;
}
float Source::getRolloffFactor() const
{
return this->rolloffFactor;
}
void Source::setMaxDistance(float distance)
{
this->maxDistance = distance;
}
float Source::getMaxDistance() const
{
return this->maxDistance;
}
int Source::getChannels() const
{
return 2;
}
} // null
} // audio
} // love
+103
View File
@@ -0,0 +1,103 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_NULL_SOURCE_H
#define LOVE_AUDIO_NULL_SOURCE_H
// LOVE
#include "common/Object.h"
#include "audio/Source.h"
namespace love
{
namespace audio
{
namespace null
{
class Source : public love::audio::Source
{
public:
Source();
virtual ~Source();
virtual love::audio::Source *copy();
virtual void play();
virtual void stop();
virtual void pause();
virtual void resume();
virtual void rewind();
virtual bool isStopped() const;
virtual bool isPaused() const;
virtual bool isFinished() const;
virtual bool update();
virtual void setPitch(float pitch);
virtual float getPitch() const;
virtual void setVolume(float volume);
virtual float getVolume() const;
virtual void seek(float offset, Unit unit);
virtual float tell(Unit unit);
virtual void setPosition(float *v);
virtual void getPosition(float *v) const;
virtual void setVelocity(float *v);
virtual void getVelocity(float *v) const;
virtual void setDirection(float *v);
virtual void getDirection(float *v) const;
virtual void setCone(float innerAngle, float outerAngle, float outerVolume);
virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const;
virtual void setRelative(bool enable);
virtual bool isRelative() const;
void setLooping(bool looping);
bool isLooping() const;
bool isStatic() const;
virtual void setMinVolume(float volume);
virtual float getMinVolume() const;
virtual void setMaxVolume(float volume);
virtual float getMaxVolume() const;
virtual void setReferenceDistance(float distance);
virtual float getReferenceDistance() const;
virtual void setRolloffFactor(float factor);
virtual float getRolloffFactor() const;
virtual void setMaxDistance(float distance);
virtual float getMaxDistance() const;
virtual int getChannels() const;
private:
float pitch;
float volume;
float coneInnerAngle;
float coneOuterAngle;
float coneOuterVolume;
bool relative;
bool looping;
float minVolume;
float maxVolume;
float referenceDistance;
float rolloffFactor;
float maxDistance;
}; // Source
} // null
} // audio
} // love
#endif // LOVE_AUDIO_NULL_SOURCE_H
+328
View File
@@ -0,0 +1,328 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Audio.h"
#include "common/delay.h"
#include "sound/Decoder.h"
#include <cstdlib>
namespace love
{
namespace audio
{
namespace openal
{
Audio::PoolThread::PoolThread(Pool *pool)
: pool(pool)
, finish(false)
{
mutex = thread::newMutex();
}
Audio::PoolThread::~PoolThread()
{
delete mutex;
}
void Audio::PoolThread::threadFunction()
{
while (true)
{
{
thread::Lock lock(mutex);
if (finish)
{
return;
}
}
pool->update();
delay(5);
}
}
void Audio::PoolThread::setFinish()
{
thread::Lock lock(mutex);
finish = true;
}
Audio::Audio()
: distanceModel(DISTANCE_INVERSE_CLAMPED)
{
// Passing zero for default device.
device = alcOpenDevice(0);
if (device == 0)
throw love::Exception("Could not open device.");
context = alcCreateContext(device, 0);
if (context == 0)
throw love::Exception("Could not create context.");
alcMakeContextCurrent(context);
if (alcGetError(device) != ALC_NO_ERROR)
throw love::Exception("Could not make context current.");
/*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
const ALCchar * devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
while (*devices)
{
std::string device(devices);
devices += device.size() + 1;
if (device.find("Mic") != std::string::npos || device.find("mic") != std::string::npos)
{
captureName = device;
}
}
capture = alcCaptureOpenDevice(captureName.c_str(), 8000, AL_FORMAT_MONO16, 262144); // about 32 seconds
if (!capture)
{
// We're not going to prevent LOVE from running without a microphone, but we should warn, at least
std::cerr << "Warning, couldn't open capture device! No audio input!" << std::endl;
}*/
// pool must be allocated after AL context.
pool = new Pool();
poolThread = new PoolThread(pool);
poolThread->start();
}
Audio::~Audio()
{
poolThread->setFinish();
poolThread->wait();
delete poolThread;
delete pool;
alcMakeContextCurrent(0);
alcDestroyContext(context);
//if (capture) alcCaptureCloseDevice(capture);
alcCloseDevice(device);
}
const char *Audio::getName() const
{
return "love.audio.openal";
}
love::audio::Source *Audio::newSource(love::sound::Decoder *decoder)
{
return new Source(pool, decoder);
}
love::audio::Source *Audio::newSource(love::sound::SoundData *soundData)
{
return new Source(pool, soundData);
}
int Audio::getSourceCount() const
{
return pool->getSourceCount();
}
int Audio::getMaxSources() const
{
return pool->getMaxSources();
}
void Audio::play(love::audio::Source *source)
{
source->play();
}
void Audio::stop(love::audio::Source *source)
{
source->stop();
}
void Audio::stop()
{
pool->stop();
}
void Audio::pause(love::audio::Source *source)
{
source->pause();
}
void Audio::pause()
{
pool->pause();
}
void Audio::resume(love::audio::Source *source)
{
source->resume();
}
void Audio::resume()
{
pool->resume();
}
void Audio::rewind(love::audio::Source *source)
{
source->rewind();
}
void Audio::rewind()
{
pool->rewind();
}
void Audio::setVolume(float volume)
{
alListenerf(AL_GAIN, volume);
}
float Audio::getVolume() const
{
ALfloat volume;
alGetListenerf(AL_GAIN, &volume);
return volume;
}
void Audio::getPosition(float *v) const
{
alGetListenerfv(AL_POSITION, v);
}
void Audio::setPosition(float *v)
{
alListenerfv(AL_POSITION, v);
}
void Audio::getOrientation(float *v) const
{
alGetListenerfv(AL_ORIENTATION, v);
}
void Audio::setOrientation(float *v)
{
alListenerfv(AL_ORIENTATION, v);
}
void Audio::getVelocity(float *v) const
{
alGetListenerfv(AL_VELOCITY, v);
}
void Audio::setVelocity(float *v)
{
alListenerfv(AL_VELOCITY, v);
}
void Audio::record()
{
if (!canRecord()) return;
alcCaptureStart(capture);
}
love::sound::SoundData *Audio::getRecordedData()
{
if (!canRecord())
return NULL;
int samplerate = 8000;
ALCint samples;
alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 4, &samples);
void *data = malloc(samples * (2/sizeof(char)));
alcCaptureSamples(capture, data, samples);
love::sound::SoundData *sd = new love::sound::SoundData(data, samples, samplerate, 16, 1);
free(data);
return sd;
}
love::sound::SoundData *Audio::stopRecording(bool returnData)
{
if (!canRecord())
return NULL;
love::sound::SoundData *sd = NULL;
if (returnData)
{
sd = getRecordedData();
}
alcCaptureStop(capture);
return sd;
}
bool Audio::canRecord()
{
return (capture != NULL);
}
Audio::DistanceModel Audio::getDistanceModel() const
{
return this->distanceModel;
}
void Audio::setDistanceModel(DistanceModel distanceModel)
{
this->distanceModel = distanceModel;
switch (distanceModel)
{
case DISTANCE_NONE:
alDistanceModel(AL_NONE);
break;
case DISTANCE_INVERSE:
alDistanceModel(AL_INVERSE_DISTANCE);
break;
case DISTANCE_INVERSE_CLAMPED:
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
break;
case DISTANCE_LINEAR:
alDistanceModel(AL_LINEAR_DISTANCE);
break;
case DISTANCE_LINEAR_CLAMPED:
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
break;
case DISTANCE_EXPONENT:
alDistanceModel(AL_EXPONENT_DISTANCE);
break;
case DISTANCE_EXPONENT_CLAMPED:
alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
break;
default:
break;
}
}
} // openal
} // audio
} // love
+142
View File
@@ -0,0 +1,142 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_OPENAL_AUDIO_H
#define LOVE_AUDIO_OPENAL_AUDIO_H
// STD
#include <queue>
#include <map>
#include <iostream>
#include <cmath>
// LOVE
#include "audio/Audio.h"
#include "common/config.h"
#include "sound/SoundData.h"
#include "Source.h"
#include "Pool.h"
#include "thread/threads.h"
// OpenAL
#ifdef LOVE_MACOSX
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#else
#include <AL/alc.h>
#include <AL/al.h>
#endif
namespace love
{
namespace audio
{
namespace openal
{
class Audio : public love::audio::Audio
{
public:
Audio();
~Audio();
// Implements Module.
const char *getName() const;
// Implements Audio.
love::audio::Source *newSource(love::sound::Decoder *decoder);
love::audio::Source *newSource(love::sound::SoundData *soundData);
int getSourceCount() const;
int getMaxSources() const;
void play(love::audio::Source *source);
void play();
void stop(love::audio::Source *source);
void stop();
void pause(love::audio::Source *source);
void pause();
void resume(love::audio::Source *source);
void resume();
void rewind(love::audio::Source *source);
void rewind();
void setVolume(float volume);
float getVolume() const;
void getPosition(float *v) const;
void setPosition(float *v);
void getOrientation(float *v) const;
void setOrientation(float *v);
void getVelocity(float *v) const;
void setVelocity(float *v);
void record();
love::sound::SoundData *getRecordedData();
love::sound::SoundData *stopRecording(bool returnData);
bool canRecord();
DistanceModel getDistanceModel() const;
void setDistanceModel(DistanceModel distanceModel);
private:
// The OpenAL device.
ALCdevice *device;
// The OpenAL capture device (microphone).
ALCdevice *capture;
// The OpenAL context.
ALCcontext *context;
// The Pool.
Pool *pool;
class PoolThread: public thread::Threadable
{
protected:
Pool *pool;
// Set this to true when the thread should finish.
// Main thread will write to this value, and PoolThread
// will read from it.
volatile bool finish;
// finish lock
thread::Mutex *mutex;
public:
PoolThread(Pool *pool);
~PoolThread();
void setFinish();
void threadFunction();
};
PoolThread *poolThread;
DistanceModel distanceModel;
}; // Audio
} // openal
} // audio
} // love
#endif // LOVE_AUDIO_OPENAL_AUDIO_H
+293
View File
@@ -0,0 +1,293 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Pool.h"
#include "Source.h"
namespace love
{
namespace audio
{
namespace openal
{
Pool::Pool()
{
// Generate sources.
alGenSources(NUM_SOURCES, sources);
// Create the mutex.
mutex = thread::newMutex();
if (alGetError() != AL_NO_ERROR)
throw love::Exception("Could not generate sources.");
// Make all sources available initially.
for (int i = 0; i < NUM_SOURCES; 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);
#endif
available.push(sources[i]);
}
}
Pool::~Pool()
{
stop();
delete mutex;
// Free all sources.
alDeleteSources(NUM_SOURCES, sources);
}
bool Pool::isAvailable() const
{
bool has = false;
{
thread::Lock lock(mutex);
has = !available.empty();
}
return has;
}
bool Pool::isPlaying(Source *s)
{
bool p = false;
{
thread::Lock lock(mutex);
for (auto i = playing.begin(); i != playing.end(); i++)
{
if (i->first == s)
p = true;
}
}
return p;
}
void Pool::update()
{
thread::Lock lock(mutex);
std::map<Source *, ALuint>::iterator i = playing.begin();
while (i != playing.end())
{
if (!i->first->update())
{
i->first->stopAtomic();
i->first->rewindAtomic();
i->first->release();
available.push(i->second);
playing.erase(i++);
}
else
i++;
}
}
int Pool::getSourceCount() const
{
return playing.size();
}
int Pool::getMaxSources() const
{
return NUM_SOURCES;
}
bool Pool::play(Source *source, ALuint &out)
{
thread::Lock lock(mutex);
bool ok = true;
out = 0;
bool alreadyPlaying = findSource(source, out);
if (!alreadyPlaying)
{
// Try to play.
if (!available.empty())
{
// Get the first available source.
out = available.front();
// Remove it.
available.pop();
// Insert into map of playing sources.
playing.insert(std::pair<Source *, ALuint>(source, out));
source->retain();
source->playAtomic();
ok = true;
}
else
{
ok = false;
}
}
else
{
ok = true;
}
return ok;
}
void Pool::stop()
{
thread::Lock lock(mutex);
for (auto i = playing.begin(); i != playing.end(); i++)
{
i->first->stopAtomic();
i->first->release();
available.push(i->second);
}
playing.clear();
}
void Pool::stop(Source *source)
{
thread::Lock lock(mutex);
removeSource(source);
}
void Pool::pause()
{
thread::Lock lock(mutex);
for (auto i = playing.begin(); i != playing.end(); i++)
i->first->pauseAtomic();
}
void Pool::pause(Source *source)
{
thread::Lock lock(mutex);
ALuint out;
if (findSource(source, out))
source->pauseAtomic();
}
void Pool::resume()
{
thread::Lock lock(mutex);
for (auto i = playing.begin(); i != playing.end(); i++)
i->first->resumeAtomic();
}
void Pool::resume(Source *source)
{
thread::Lock lock(mutex);
ALuint out;
if (findSource(source, out))
source->resumeAtomic();
}
void Pool::rewind()
{
thread::Lock lock(mutex);
for (auto i = playing.begin(); i != playing.end(); i++)
i->first->rewindAtomic();
}
// For those times we don't need it backed.
void Pool::softRewind(Source *source)
{
thread::Lock lock(mutex);
source->rewindAtomic();
}
void Pool::rewind(Source *source)
{
thread::Lock lock(mutex);
source->rewindAtomic();
}
void Pool::release(Source *source)
{
ALuint s = findi(source);
if (s != 0)
{
available.push(s);
playing.erase(source);
}
}
void Pool::seek(Source *source, float offset, void *unit)
{
thread::Lock lock(mutex);
return source->seekAtomic(offset, unit);
}
float Pool::tell(Source *source, void *unit)
{
thread::Lock lock(mutex);
return source->tellAtomic(unit);
}
ALuint Pool::findi(const Source *source) const
{
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
if (i != playing.end())
return i->second;
return 0;
}
bool Pool::findSource(Source *source, ALuint &out)
{
std::map<Source *, ALuint>::const_iterator i = playing.find((Source *)source);
bool found = i != playing.end();
if (found)
out = i->second;
return found;
}
bool Pool::removeSource(Source *source)
{
std::map<Source *, ALuint>::iterator i = playing.find((Source *)source);
if (i != playing.end())
{
source->stopAtomic();
available.push(i->second);
playing.erase(i++);
source->release();
return true;
}
return false;
}
} // openal
} // audio
} // love
+124
View File
@@ -0,0 +1,124 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_OPENAL_POOL_H
#define LOVE_AUDIO_OPENAL_POOL_H
// STD
#include <queue>
#include <map>
#include <iostream>
#include <cmath>
// LOVE
#include "common/config.h"
#include "common/Exception.h"
#include "thread/threads.h"
// OpenAL
#ifdef LOVE_MACOSX
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#else
#include <AL/alc.h>
#include <AL/al.h>
#include <AL/alext.h>
#endif
namespace love
{
namespace audio
{
namespace openal
{
class Source;
class Pool
{
public:
Pool();
~Pool();
/**
* Checks whether an OpenAL source is available.
* @return True if at least one is available, false otherwise.
**/
bool isAvailable() const;
/**
* Checks whether a Source is currently in the playing list.
**/
bool isPlaying(Source *s);
void update();
int getSourceCount() const;
int getMaxSources() const;
bool play(Source *source, ALuint &out);
void stop();
void stop(Source *source);
void pause();
void pause(Source *source);
void resume();
void resume(Source *source);
void rewind();
void rewind(Source *source);
void softRewind(Source *source);
void seek(Source *source, float offset, void *unit);
float tell(Source *source, void *unit);
private:
/**
* Makes the specified OpenAL source available for use.
* @param source The OpenAL source.
**/
void release(Source *source);
ALuint findi(const Source *source) const;
bool findSource(Source *source, ALuint &out);
bool removeSource(Source *source);
// Number of OpenAL sources.
static const int NUM_SOURCES = 64;
// OpenAL sources
ALuint sources[NUM_SOURCES];
// A queue of available sources.
std::queue<ALuint> available;
// A map of playing sources.
std::map<Source *, ALuint> playing;
// Only one thread can access this object at the same time. This mutex will
// make sure of that.
thread::Mutex *mutex;
}; // Pool
} // openal
} // audio
} // love
#endif // LOVE_AUDIO_OPENAL_POOL_H
@@ -0,0 +1,766 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Source.h"
#include "Pool.h"
#include "common/math.h"
// STD
#include <iostream>
#include <float.h>
namespace love
{
namespace audio
{
namespace openal
{
Source::Source(Pool *pool, love::sound::SoundData *soundData)
: love::audio::Source(Source::TYPE_STATIC)
, pool(pool)
, valid(false)
, pitch(1.0f)
, volume(1.0f)
, relative(false)
, looping(false)
, paused(false)
, minVolume(0.0f)
, maxVolume(1.0f)
, referenceDistance(1.0f)
, rolloffFactor(1.0f)
, maxDistance(FLT_MAX)
, cone()
, offsetSamples(0)
, offsetSeconds(0)
, channels(soundData->getChannels())
, decoder(0)
, toLoop(0)
{
alGenBuffers(1, buffers);
ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
alBufferData(buffers[0], fmt, soundData->getData(), soundData->getSize(), soundData->getSampleRate());
static float z[3] = {0, 0, 0};
setFloatv(position, z);
setFloatv(velocity, z);
setFloatv(direction, z);
}
Source::Source(Pool *pool, love::sound::Decoder *decoder)
: love::audio::Source(Source::TYPE_STREAM)
, pool(pool)
, valid(false)
, pitch(1.0f)
, volume(1.0f)
, relative(false)
, looping(false)
, paused(false)
, minVolume(0.0f)
, maxVolume(1.0f)
, referenceDistance(1.0f)
, rolloffFactor(1.0f)
, maxDistance(FLT_MAX)
, cone()
, offsetSamples(0)
, offsetSeconds(0)
, channels(decoder->getChannels())
, decoder(decoder)
, toLoop(0)
{
decoder->retain();
alGenBuffers(MAX_BUFFERS, buffers);
static float z[3] = {0, 0, 0};
setFloatv(position, z);
setFloatv(velocity, z);
setFloatv(direction, z);
}
Source::~Source()
{
if (valid)
pool->stop(this);
alDeleteBuffers((type == TYPE_STATIC) ? 1 : MAX_BUFFERS, buffers);
if (decoder)
decoder->release();
}
love::audio::Source *Source::copy()
{
return 0;
}
void Source::play()
{
if (valid && paused)
{
pool->resume(this);
return;
}
valid = pool->play(this, source);
}
void Source::stop()
{
if (!isStopped())
{
pool->stop(this);
pool->softRewind(this);
}
}
void Source::pause()
{
pool->pause(this);
}
void Source::resume()
{
pool->resume(this);
}
void Source::rewind()
{
pool->rewind(this);
}
bool Source::isStopped() const
{
if (valid)
{
ALenum state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_STOPPED);
}
return true;
}
bool Source::isPaused() const
{
if (valid)
{
ALenum state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_PAUSED);
}
return false;
}
bool Source::isFinished() const
{
return type == TYPE_STATIC ? isStopped() : isStopped() && !isLooping() && decoder->isFinished();
}
bool Source::update()
{
if (!valid)
return false;
if (type == TYPE_STATIC)
{
// Looping mode could have changed.
alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE);
return !isStopped();
}
else if (type == TYPE_STREAM && (isLooping() || !isFinished()))
{
// Number of processed buffers.
ALint processed = 0;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
while (processed--)
{
ALuint buffer;
float curOffsetSamples, curOffsetSecs;
alGetSourcef(source, AL_SAMPLE_OFFSET, &curOffsetSamples);
ALint b;
alGetSourcei(source, AL_BUFFER, &b);
int freq;
alGetBufferi(b, AL_FREQUENCY, &freq);
curOffsetSecs = curOffsetSamples / freq;
// Get a free buffer.
alSourceUnqueueBuffers(source, 1, &buffer);
float newOffsetSamples, newOffsetSecs;
alGetSourcef(source, AL_SAMPLE_OFFSET, &newOffsetSamples);
newOffsetSecs = newOffsetSamples / freq;
offsetSamples += (curOffsetSamples - newOffsetSamples);
offsetSeconds += (curOffsetSecs - newOffsetSecs);
streamAtomic(buffer, decoder);
alSourceQueueBuffers(source, 1, &buffer);
}
return true;
}
return false;
}
void Source::setPitch(float pitch)
{
if (valid)
alSourcef(source, AL_PITCH, pitch);
this->pitch = pitch;
}
float Source::getPitch() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_PITCH, &f);
return f;
}
// In case the Source isn't playing.
return pitch;
}
void Source::setVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_GAIN, volume);
}
this->volume = volume;
}
float Source::getVolume() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_GAIN, &f);
return f;
}
// In case the Source isn't playing.
return volume;
}
void Source::seekAtomic(float offset, void *unit)
{
if (valid)
{
switch (*((Source::Unit *) unit))
{
case Source::UNIT_SAMPLES:
if (type == TYPE_STREAM)
{
offsetSamples = offset;
ALint buffer;
alGetSourcei(source, AL_BUFFER, &buffer);
int freq;
alGetBufferi(buffer, AL_FREQUENCY, &freq);
offset /= freq;
offsetSeconds = offset;
decoder->seek(offset);
}
else
{
alSourcef(source, AL_SAMPLE_OFFSET, offset);
}
break;
case Source::UNIT_SECONDS:
default:
if (type == TYPE_STREAM)
{
offsetSeconds = offset;
decoder->seek(offset);
ALint buffer;
alGetSourcei(source, AL_BUFFER, &buffer);
int freq;
alGetBufferi(buffer, AL_FREQUENCY, &freq);
offsetSamples = offset*freq;
}
else
{
alSourcef(source, AL_SEC_OFFSET, offset);
}
break;
}
if (type == TYPE_STREAM)
{
bool waspaused = paused;
// Because we still have old data
// from before the seek in the buffers
// let's empty them.
stopAtomic();
playAtomic();
if (waspaused)
pauseAtomic();
}
}
}
void Source::seek(float offset, Source::Unit unit)
{
return pool->seek(this, offset, &unit);
}
float Source::tellAtomic(void *unit) const
{
if (valid)
{
float offset;
switch (*((Source::Unit *) unit))
{
case Source::UNIT_SAMPLES:
alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
if (type == TYPE_STREAM) offset += offsetSamples;
break;
case Source::UNIT_SECONDS:
default:
alGetSourcef(source, AL_SAMPLE_OFFSET, &offset);
ALint buffer;
alGetSourcei(source, AL_BUFFER, &buffer);
int freq;
alGetBufferi(buffer, AL_FREQUENCY, &freq);
offset /= freq;
if (type == TYPE_STREAM) offset += offsetSeconds;
break;
}
return offset;
}
return 0.0f;
}
float Source::tell(Source::Unit unit)
{
return pool->tell(this, &unit);
}
void Source::setPosition(float *v)
{
if (valid)
alSourcefv(source, AL_POSITION, v);
setFloatv(position, v);
}
void Source::getPosition(float *v) const
{
if (valid)
alGetSourcefv(source, AL_POSITION, v);
else
setFloatv(v, position);
}
void Source::setVelocity(float *v)
{
if (valid)
alSourcefv(source, AL_VELOCITY, v);
setFloatv(velocity, v);
}
void Source::getVelocity(float *v) const
{
if (valid)
alGetSourcefv(source, AL_VELOCITY, v);
else
setFloatv(v, velocity);
}
void Source::setDirection(float *v)
{
if (valid)
alSourcefv(source, AL_DIRECTION, v);
else
setFloatv(direction, v);
}
void Source::getDirection(float *v) const
{
if (valid)
alGetSourcefv(source, AL_DIRECTION, v);
else
setFloatv(v, direction);
}
void Source::setCone(float innerAngle, float outerAngle, float outerVolume)
{
cone.innerAngle = LOVE_TODEG(innerAngle);
cone.outerAngle = LOVE_TODEG(outerAngle);
cone.outerVolume = outerVolume;
if (valid)
{
alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
}
}
void Source::getCone(float &innerAngle, float &outerAngle, float &outerVolume) const
{
innerAngle = LOVE_TORAD(cone.innerAngle);
outerAngle = LOVE_TORAD(cone.outerAngle);
outerVolume = cone.outerVolume;
}
void Source::setRelative(bool enable)
{
if (valid)
alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
relative = enable;
}
bool Source::isRelative() const
{
return relative;
}
void Source::setLooping(bool looping)
{
if (valid && type == TYPE_STATIC)
alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
this->looping = looping;
}
bool Source::isLooping() const
{
return looping;
}
void Source::playAtomic()
{
if (type == TYPE_STATIC)
{
alSourcei(source, AL_BUFFER, buffers[0]);
}
else if (type == TYPE_STREAM)
{
int usedBuffers = 0;
for (unsigned int i = 0; i < MAX_BUFFERS; i++)
{
streamAtomic(buffers[i], decoder);
++usedBuffers;
if (decoder->isFinished())
break;
}
if (usedBuffers > 0)
alSourceQueueBuffers(source, usedBuffers, buffers);
}
// This Source may now be associated with an OpenAL source that still has
// the properties of another love Source. Let's reset it to the settings
// of the new one.
reset();
alSourcePlay(source);
valid = true; //if it fails it will be set to false again
//but this prevents a horrible, horrible bug
}
void Source::stopAtomic()
{
if (valid)
{
if (type == TYPE_STATIC)
{
alSourceStop(source);
}
else if (type == TYPE_STREAM)
{
alSourceStop(source);
int queued = 0;
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
while (queued--)
{
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
}
}
alSourcei(source, AL_BUFFER, AL_NONE);
}
toLoop = 0;
valid = false;
}
void Source::pauseAtomic()
{
if (valid)
{
alSourcePause(source);
paused = true;
}
}
void Source::resumeAtomic()
{
if (valid && paused)
{
alSourcePlay(source);
paused = false;
}
}
void Source::rewindAtomic()
{
if (valid && type == TYPE_STATIC)
{
alSourceRewind(source);
if (!paused)
alSourcePlay(source);
}
else if (valid && type == TYPE_STREAM)
{
bool waspaused = paused;
decoder->rewind();
// Because we still have old data
// from before the seek in the buffers
// let's empty them.
stopAtomic();
playAtomic();
if (waspaused)
pauseAtomic();
offsetSamples = 0;
offsetSeconds = 0;
}
else if (type == TYPE_STREAM)
{
decoder->rewind();
offsetSamples = 0;
offsetSeconds = 0;
}
}
void Source::reset()
{
alSourcefv(source, AL_POSITION, position);
alSourcefv(source, AL_VELOCITY, velocity);
alSourcefv(source, AL_DIRECTION, direction);
alSourcef(source, AL_PITCH, pitch);
alSourcef(source, AL_GAIN, volume);
alSourcef(source, AL_MIN_GAIN, minVolume);
alSourcef(source, AL_MAX_GAIN, maxVolume);
alSourcef(source, AL_REFERENCE_DISTANCE, referenceDistance);
alSourcef(source, AL_ROLLOFF_FACTOR, rolloffFactor);
alSourcef(source, AL_MAX_DISTANCE, maxDistance);
alSourcei(source, AL_LOOPING, isStatic() && isLooping() ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
alSourcei(source, AL_CONE_INNER_ANGLE, cone.innerAngle);
alSourcei(source, AL_CONE_OUTER_ANGLE, cone.outerAngle);
alSourcef(source, AL_CONE_OUTER_GAIN, cone.outerVolume);
}
void Source::setFloatv(float *dst, const float *src) const
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
ALenum Source::getFormat(int channels, int bitDepth) const
{
if (channels == 1 && bitDepth == 8)
return AL_FORMAT_MONO8;
else if (channels == 1 && bitDepth == 16)
return AL_FORMAT_MONO16;
else if (channels == 2 && bitDepth == 8)
return AL_FORMAT_STEREO8;
else if (channels == 2 && bitDepth == 16)
return AL_FORMAT_STEREO16;
else
return 0;
}
int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
{
// Get more sound data.
int decoded = d->decode();
int fmt = getFormat(d->getChannels(), d->getBitDepth());
if (fmt != 0)
alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
if (decoder->isFinished() && isLooping())
{
int queued, processed;
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
if (queued > processed)
toLoop = queued-processed;
else
toLoop = MAX_BUFFERS-processed;
d->rewind();
}
if (toLoop > 0)
{
if (--toLoop == 0)
{
offsetSamples = 0;
offsetSeconds = 0;
}
}
return decoded;
}
bool Source::isStatic() const
{
return (type == TYPE_STATIC);
}
void Source::setMinVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_MIN_GAIN, volume);
}
this->minVolume = volume;
}
float Source::getMinVolume() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_MIN_GAIN, &f);
return f;
}
// In case the Source isn't playing.
return this->minVolume;
}
void Source::setMaxVolume(float volume)
{
if (valid)
{
alSourcef(source, AL_MAX_GAIN, volume);
}
this->maxVolume = volume;
}
float Source::getMaxVolume() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_MAX_GAIN, &f);
return f;
}
// In case the Source isn't playing.
return this->maxVolume;
}
void Source::setReferenceDistance(float distance)
{
if (valid)
{
alSourcef(source, AL_REFERENCE_DISTANCE, distance);
}
this->referenceDistance = distance;
}
float Source::getReferenceDistance() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_REFERENCE_DISTANCE, &f);
return f;
}
// In case the Source isn't playing.
return this->referenceDistance;
}
void Source::setRolloffFactor(float factor)
{
if (valid)
{
alSourcef(source, AL_ROLLOFF_FACTOR, factor);
}
this->rolloffFactor = factor;
}
float Source::getRolloffFactor() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_ROLLOFF_FACTOR, &f);
return f;
}
// In case the Source isn't playing.
return this->rolloffFactor;
}
void Source::setMaxDistance(float distance)
{
if (valid)
{
alSourcef(source, AL_MAX_DISTANCE, distance);
}
this->maxDistance = distance;
}
float Source::getMaxDistance() const
{
if (valid)
{
ALfloat f;
alGetSourcef(source, AL_MAX_DISTANCE, &f);
return f;
}
// In case the Source isn't playing.
return this->maxDistance;
}
int Source::getChannels() const
{
return channels;
}
} // openal
} // audio
} // love
+171
View File
@@ -0,0 +1,171 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_OPENAL_SOURCE_H
#define LOVE_AUDIO_OPENAL_SOURCE_H
// LOVE
#include "common/config.h"
#include "common/Object.h"
#include "audio/Source.h"
#include "sound/SoundData.h"
#include "sound/Decoder.h"
// OpenAL
#ifdef LOVE_MACOSX
#include <OpenAL-Soft/alc.h>
#include <OpenAL-Soft/al.h>
#else
#include <AL/alc.h>
#include <AL/al.h>
#endif
namespace love
{
namespace audio
{
namespace openal
{
class Audio;
class Pool;
class Source : public love::audio::Source
{
public:
Source(Pool *pool, love::sound::SoundData *soundData);
Source(Pool *pool, love::sound::Decoder *decoder);
virtual ~Source();
virtual love::audio::Source *copy();
virtual void play();
virtual void stop();
virtual void pause();
virtual void resume();
virtual void rewind();
virtual bool isStopped() const;
virtual bool isPaused() const;
virtual bool isFinished() const;
virtual bool update();
virtual void setPitch(float pitch);
virtual float getPitch() const;
virtual void setVolume(float volume);
virtual float getVolume() const;
virtual void seekAtomic(float offset, void *unit);
virtual void seek(float offset, Unit unit);
virtual float tellAtomic(void *unit) const;
virtual float tell(Unit unit);
virtual void setPosition(float *v);
virtual void getPosition(float *v) const;
virtual void setVelocity(float *v);
virtual void getVelocity(float *v) const;
virtual void setDirection(float *v);
virtual void getDirection(float *v) const;
virtual void setCone(float innerAngle, float outerAngle, float outerVolume);
virtual void getCone(float &innerAngle, float &outerAngle, float &outerVolume) const;
virtual void setRelative(bool enable);
virtual bool isRelative() const;
void setLooping(bool looping);
bool isLooping() const;
bool isStatic() const;
virtual void setMinVolume(float volume);
virtual float getMinVolume() const;
virtual void setMaxVolume(float volume);
virtual float getMaxVolume() const;
virtual void setReferenceDistance(float distance);
virtual float getReferenceDistance() const;
virtual void setRolloffFactor(float factor);
virtual float getRolloffFactor() const;
virtual void setMaxDistance(float distance);
virtual float getMaxDistance() const;
virtual int getChannels() const;
void playAtomic();
void stopAtomic();
void pauseAtomic();
void resumeAtomic();
void rewindAtomic();
private:
void reset();
void setFloatv(float *dst, const float *src) const;
/**
* Gets the OpenAL format identifier based on number of
* channels and bits.
* @param channels Either 1 (mono) or 2 (stereo).
* @param bitDepth Either 8-bit samples, or 16-bit samples.
* @return One of AL_FORMAT_*, or 0 if unsupported format.
**/
ALenum getFormat(int channels, int bitDepth) const;
int streamAtomic(ALuint buffer, love::sound::Decoder *d);
Pool *pool;
ALuint source;
bool valid;
static const unsigned int MAX_BUFFERS = 32;
ALuint buffers[MAX_BUFFERS];
float pitch;
float volume;
float position[3];
float velocity[3];
float direction[3];
bool relative;
bool looping;
bool paused;
float minVolume;
float maxVolume;
float referenceDistance;
float rolloffFactor;
float maxDistance;
struct Cone
{
int innerAngle; // degrees
int outerAngle; // degrees
float outerVolume;
Cone()
: innerAngle(360)
, outerAngle(360)
, outerVolume(0.0f)
{}
} cone;
float offsetSamples;
float offsetSeconds;
int channels;
love::sound::Decoder *decoder;
unsigned int toLoop;
}; // Source
} // openal
} // audio
} // love
#endif // LOVE_AUDIO_OPENAL_SOURCE_H
+351
View File
@@ -0,0 +1,351 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Audio.h"
#include "openal/Audio.h"
#include "null/Audio.h"
#include "common/runtime.h"
namespace love
{
namespace audio
{
static Audio *instance = 0;
int w_getSourceCount(lua_State *L)
{
lua_pushinteger(L, instance->getSourceCount());
return 1;
}
int w_newSource(lua_State *L)
{
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
if (luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
luax_convobj(L, 1, "sound", "newDecoder");
Source::Type stype = Source::TYPE_STREAM;
const char *stypestr = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
if (stypestr && !Source::getConstant(stypestr, stype))
return luaL_error(L, "Invalid source type: %s", stypestr);
if (stype == Source::TYPE_STATIC && luax_istype(L, 1, SOUND_DECODER_T))
luax_convobj(L, 1, "sound", "newSoundData");
Source *t = 0;
if (luax_istype(L, 1, SOUND_SOUND_DATA_T))
t = instance->newSource(luax_totype<love::sound::SoundData>(L, 1, "SoundData", SOUND_SOUND_DATA_T));
else if (luax_istype(L, 1, SOUND_DECODER_T))
t = instance->newSource(luax_totype<love::sound::Decoder>(L, 1, "Decoder", SOUND_DECODER_T));
if (t)
{
luax_pushtype(L, "Source", AUDIO_SOURCE_T, t);
return 1;
}
else
return luax_typerror(L, 1, "Decoder or SoundData");
}
int w_play(lua_State *L)
{
Source *s = luax_checksource(L, 1);
instance->play(s);
return 0;
}
int w_stop(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->stop();
}
else
{
Source *s = luax_checksource(L, 1);
s->stop();
}
return 0;
}
int w_pause(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->pause();
}
else
{
Source *s = luax_checksource(L, 1);
s->pause();
}
return 0;
}
int w_resume(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->resume();
}
else
{
Source *s = luax_checksource(L, 1);
s->resume();
}
return 0;
}
int w_rewind(lua_State *L)
{
if (lua_gettop(L) == 0)
{
instance->rewind();
}
else
{
Source *s = luax_checksource(L, 1);
s->rewind();
}
return 0;
}
int w_setVolume(lua_State *L)
{
float v = (float)luaL_checknumber(L, 1);
instance->setVolume(v);
return 0;
}
int w_getVolume(lua_State *L)
{
lua_pushnumber(L, instance->getVolume());
return 1;
}
int w_setPosition(lua_State *L)
{
float v[3];
v[0] = (float)luaL_checknumber(L, 1);
v[1] = (float)luaL_checknumber(L, 2);
v[2] = (float)luaL_optnumber(L, 3, 0);
instance->setPosition(v);
return 0;
}
int w_getPosition(lua_State *L)
{
float v[3];
instance->getPosition(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int w_setOrientation(lua_State *L)
{
float v[6];
v[0] = (float)luaL_checknumber(L, 1);
v[1] = (float)luaL_checknumber(L, 2);
v[2] = (float)luaL_checknumber(L, 3);
v[3] = (float)luaL_checknumber(L, 4);
v[4] = (float)luaL_checknumber(L, 5);
v[5] = (float)luaL_checknumber(L, 6);
instance->setOrientation(v);
return 0;
}
int w_getOrientation(lua_State *L)
{
float v[6];
instance->getOrientation(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
lua_pushnumber(L, v[3]);
lua_pushnumber(L, v[4]);
lua_pushnumber(L, v[5]);
return 6;
}
int w_setVelocity(lua_State *L)
{
float v[3];
v[0] = (float)luaL_checknumber(L, 1);
v[1] = (float)luaL_checknumber(L, 2);
v[2] = (float)luaL_optnumber(L, 3, 0);
instance->setVelocity(v);
return 0;
}
int w_getVelocity(lua_State *L)
{
float v[3];
instance->getVelocity(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int w_record(lua_State *)
{
instance->record();
return 0;
}
int w_getRecordedData(lua_State *L)
{
love::sound::SoundData *sd = instance->getRecordedData();
if (!sd)
lua_pushnil(L);
else
luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
return 1;
}
int w_stopRecording(lua_State *L)
{
if (luax_optboolean(L, 1, true))
{
love::sound::SoundData *sd = instance->stopRecording(true);
if (!sd) lua_pushnil(L);
else luax_pushtype(L, "SoundData", SOUND_SOUND_DATA_T, sd);
return 1;
}
instance->stopRecording(false);
return 0;
}
int w_canRecord(lua_State *L)
{
luax_pushboolean(L, instance->canRecord());
return 1;
}
int w_setDistanceModel(lua_State *L)
{
const char *modelStr = luaL_checkstring(L, 1);
Audio::DistanceModel distanceModel;
if (!Audio::getConstant(modelStr, distanceModel))
return luaL_error(L, "Invalid distance model: %s", modelStr);
instance->setDistanceModel(distanceModel);
return 0;
}
int w_getDistanceModel(lua_State *L)
{
Audio::DistanceModel distanceModel = instance->getDistanceModel();
const char *modelStr;
if (!Audio::getConstant(distanceModel, modelStr))
return 0;
lua_pushstring(L, modelStr);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "getSourceCount", w_getSourceCount },
{ "newSource", w_newSource },
{ "play", w_play },
{ "stop", w_stop },
{ "pause", w_pause },
{ "resume", w_resume },
{ "rewind", w_rewind },
{ "setVolume", w_setVolume },
{ "getVolume", w_getVolume },
{ "setPosition", w_setPosition },
{ "getPosition", w_getPosition },
{ "setOrientation", w_setOrientation },
{ "getOrientation", w_getOrientation },
{ "setVelocity", w_setVelocity },
{ "getVelocity", w_getVelocity },
/*{ "record", w_record },
{ "getRecordedData", w_getRecordedData },
{ "stopRecording", w_stopRecording },*/
{ "setDistanceModel", w_setDistanceModel },
{ "getDistanceModel", w_getDistanceModel },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_source,
0
};
extern "C" int luaopen_love_audio(lua_State *L)
{
if (instance == 0)
{
// Try OpenAL first.
try
{
instance = new love::audio::openal::Audio();
}
catch(love::Exception &e)
{
std::cout << e.what() << std::endl;
}
}
else
instance->retain();
if (instance == 0)
{
// Fall back to nullaudio.
try
{
instance = new love::audio::null::Audio();
}
catch(love::Exception &e)
{
std::cout << e.what() << std::endl;
}
}
if (instance == 0)
return luaL_error(L, "Could not open any audio module.");
WrappedModule w;
w.module = instance;
w.name = "audio";
w.flags = MODULE_T;
w.functions = functions;
w.types = types;
int n = luax_register_module(L, w);
return n;
}
} // audio
} // love
+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_WRAP_AUDIO_H
#define LOVE_AUDIO_WRAP_AUDIO_H
// LOVE
#include "common/config.h"
#include "common/runtime.h"
#include "Audio.h"
#include "wrap_Source.h"
namespace love
{
namespace audio
{
int w_getSourceCount(lua_State *L);
int w_newSource(lua_State *L);
int w_play(lua_State *L);
int w_stop(lua_State *L);
int w_pause(lua_State *L);
int w_resume(lua_State *L);
int w_rewind(lua_State *L);
int w_setVolume(lua_State *L);
int w_getVolume(lua_State *L);
int w_setPosition(lua_State *L);
int w_getPosition(lua_State *L);
int w_setOrientation(lua_State *L);
int w_getOrientation(lua_State *L);
int w_setVelocity(lua_State *L);
int w_getVelocity(lua_State *L);
int w_record(lua_State *L);
int w_getRecordedData(lua_State *L);
int w_stopRecording(lua_State *L);
int w_canRecord(lua_State *L);
int w_setDistanceModel(lua_State *L);
int w_getDistanceModel(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_audio(lua_State *L);
} // audio
} // love
#endif // LOVE_AUDIO_WRAP_AUDIO_H
+385
View File
@@ -0,0 +1,385 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Source.h"
namespace love
{
namespace audio
{
Source *luax_checksource(lua_State *L, int idx)
{
return luax_checktype<Source>(L, idx, "Source", AUDIO_SOURCE_T);
}
int w_Source_play(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->play();
return 0;
}
int w_Source_stop(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->stop();
return 0;
}
int w_Source_pause(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->pause();
return 0;
}
int w_Source_resume(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->resume();
return 0;
}
int w_Source_rewind(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->rewind();
return 0;
}
int w_Source_setPitch(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float p = (float)luaL_checknumber(L, 2);
t->setPitch(p);
return 0;
}
int w_Source_getPitch(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getPitch());
return 1;
}
int w_Source_setVolume(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float p = (float)luaL_checknumber(L, 2);
t->setVolume(p);
return 0;
}
int w_Source_getVolume(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getVolume());
return 1;
}
int w_Source_seek(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float offset = (float)luaL_checknumber(L, 2);
if (offset < 0)
return luaL_argerror(L, 2, "can't seek to a negative position");
Source::Unit u = Source::UNIT_SECONDS;
const char *unit = lua_isnoneornil(L, 3) ? 0 : lua_tostring(L, 3);
if (unit && !t->getConstant(unit, u))
return luaL_error(L, "Invalid Source time unit: %s", unit);
t->seek(offset, u);
return 0;
}
int w_Source_tell(lua_State *L)
{
Source *t = luax_checksource(L, 1);
Source::Unit u = Source::UNIT_SECONDS;
const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
if (unit && !t->getConstant(unit, u))
return luaL_error(L, "Invalid Source time unit: %s", unit);
lua_pushnumber(L, t->tell(u));
return 1;
}
int w_Source_setPosition(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setPosition(v);
return 0;
}
int w_Source_getPosition(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getPosition(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int w_Source_setVelocity(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setVelocity(v);
return 0;
}
int w_Source_getVelocity(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getVelocity(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int w_Source_setDirection(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
v[0] = (float)luaL_checknumber(L, 2);
v[1] = (float)luaL_checknumber(L, 3);
v[2] = (float)luaL_optnumber(L, 4, 0);
t->setDirection(v);
return 0;
}
int w_Source_getDirection(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float v[3];
t->getDirection(v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int w_Source_setCone(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float innerAngle = (float) luaL_checknumber(L, 2);
float outerAngle = (float) luaL_checknumber(L, 3);
float outerVolume = (float) luaL_optnumber(L, 4, 0.0);
t->setCone(innerAngle, outerAngle, outerVolume);
return 0;
}
int w_Source_getCone(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float innerAngle, outerAngle, outerVolume;
t->getCone(innerAngle, outerAngle, outerVolume);
lua_pushnumber(L, innerAngle);
lua_pushnumber(L, outerAngle);
lua_pushnumber(L, outerVolume);
return 3;
}
int w_Source_setRelative(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->setRelative(luax_toboolean(L, 2));
return 0;
}
int w_Source_isRelative(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isRelative());
return 1;
}
int w_Source_setLooping(lua_State *L)
{
Source *t = luax_checksource(L, 1);
t->setLooping(luax_toboolean(L, 2));
return 0;
}
int w_Source_isLooping(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isLooping());
return 1;
}
int w_Source_isStopped(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isStopped());
return 1;
}
int w_Source_isPaused(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isPaused());
return 1;
}
int w_Source_isPlaying(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, !t->isStopped() && !t->isPaused());
return 1;
}
int w_Source_isStatic(lua_State *L)
{
Source *t = luax_checksource(L, 1);
luax_pushboolean(L, t->isStatic());
return 1;
}
int w_Source_setVolumeLimits(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float vmin = (float)luaL_checknumber(L, 2);
float vmax = (float)luaL_checknumber(L, 3);
if (vmin < .0f || vmin > 1.f || vmax < .0f || vmax > 1.f)
return luaL_error(L, "Invalid volume limits: [%f:%f]. Must be in [0:1]", vmin, vmax);
t->setMinVolume(vmin);
t->setMaxVolume(vmax);
return 0;
}
int w_Source_getVolumeLimits(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getMinVolume());
lua_pushnumber(L, t->getMaxVolume());
return 2;
}
int w_Source_setAttenuationDistances(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float dref = (float)luaL_checknumber(L, 2);
float dmax = (float)luaL_checknumber(L, 3);
if (dref < .0f || dmax < .0f)
return luaL_error(L, "Invalid distances: %f, %f. Must be > 0", dref, dmax);
t->setReferenceDistance(dref);
t->setMaxDistance(dmax);
return 0;
}
int w_Source_getAttenuationDistances(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getReferenceDistance());
lua_pushnumber(L, t->getMaxDistance());
return 2;
}
int w_Source_setRolloff(lua_State *L)
{
Source *t = luax_checksource(L, 1);
float rolloff = (float)luaL_checknumber(L, 2);
if (rolloff < .0f)
return luaL_error(L, "Invalid rolloff: %f. Must be > 0.", rolloff);
t->setRolloffFactor(rolloff);
return 0;
}
int w_Source_getRolloff(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushnumber(L, t->getRolloffFactor());
return 1;
}
int w_Source_getChannels(lua_State *L)
{
Source *t = luax_checksource(L, 1);
lua_pushinteger(L, t->getChannels());
return 1;
}
static const luaL_Reg functions[] =
{
{ "play", w_Source_play },
{ "stop", w_Source_stop },
{ "pause", w_Source_pause },
{ "resume", w_Source_resume },
{ "rewind", w_Source_rewind },
{ "setPitch", w_Source_setPitch },
{ "getPitch", w_Source_getPitch },
{ "setVolume", w_Source_setVolume },
{ "getVolume", w_Source_getVolume },
{ "seek", w_Source_seek },
{ "tell", w_Source_tell },
{ "setPosition", w_Source_setPosition },
{ "getPosition", w_Source_getPosition },
{ "setVelocity", w_Source_setVelocity },
{ "getVelocity", w_Source_getVelocity },
{ "setDirection", w_Source_setDirection },
{ "getDirection", w_Source_getDirection },
{ "setCone", w_Source_setCone },
{ "getCone", w_Source_getCone },
{ "setRelative", w_Source_setRelative },
{ "isRelative", w_Source_isRelative },
{ "setLooping", w_Source_setLooping },
{ "isLooping", w_Source_isLooping },
{ "isStopped", w_Source_isStopped },
{ "isPaused", w_Source_isPaused },
{ "isPlaying", w_Source_isPlaying },
{ "isStatic", w_Source_isStatic },
{ "setVolumeLimits", w_Source_setVolumeLimits },
{ "getVolumeLimits", w_Source_getVolumeLimits },
{ "setAttenuationDistances", w_Source_setAttenuationDistances },
{ "getAttenuationDistances", w_Source_getAttenuationDistances },
{ "setRolloff", w_Source_setRolloff},
{ "getRolloff", w_Source_getRolloff},
{ "getChannels", w_Source_getChannels },
{ 0, 0 }
};
extern "C" int luaopen_source(lua_State *L)
{
return luax_register_type(L, "Source", functions);
}
} // audio
} // love
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_AUDIO_WRAP_SOURCE_H
#define LOVE_AUDIO_WRAP_SOURCE_H
#include "common/runtime.h"
#include "Source.h"
namespace love
{
namespace audio
{
Source *luax_checksource(lua_State *L, int idx);
int w_Source_play(lua_State *L);
int w_Source_stop(lua_State *L);
int w_Source_pause(lua_State *L);
int w_Source_resume(lua_State *L);
int w_Source_rewind(lua_State *L);
int w_Source_setPitch(lua_State *L);
int w_Source_getPitch(lua_State *L);
int w_Source_setVolume(lua_State *L);
int w_Source_getVolume(lua_State *L);
int w_Source_seek(lua_State *L);
int w_Source_tell(lua_State *L);
int w_Source_setPosition(lua_State *L);
int w_Source_getPosition(lua_State *L);
int w_Source_setVelocity(lua_State *L);
int w_Source_getVelocity(lua_State *L);
int w_Source_setDirection(lua_State *L);
int w_Source_getDirection(lua_State *L);
int w_Source_setCone(lua_State *L);
int w_Source_getCone(lua_State *L);
int w_Source_setRelative(lua_State *L);
int w_Source_isRelative(lua_State *L);
int w_Source_setLooping(lua_State *L);
int w_Source_isLooping(lua_State *L);
int w_Source_isStopped(lua_State *L);
int w_Source_isPaused(lua_State *L);
int w_Source_isPlaying(lua_State *L);
int w_Source_isStatic(lua_State *L);
int w_Source_setVolumeLimits(lua_State *L);
int w_Source_getVolumeLimits(lua_State *L);
int w_Source_setAttenuationDistances(lua_State *L);
int w_Source_getAttenuationDistances(lua_State *L);
int w_Source_setRolloff(lua_State *L);
int w_Source_getRolloff(lua_State *L);
int w_Source_getChannels(lua_State *L);
extern "C" int luaopen_source(lua_State *L);
} // audio
} // love
#endif // LOVE_AUDIO_WRAP_SOURCE_H
+122
View File
@@ -0,0 +1,122 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Event.h"
using love::thread::Mutex;
using love::thread::Lock;
namespace love
{
namespace event
{
Message::Message(const std::string &name, Variant *a, Variant *b, Variant *c, Variant *d)
: name(name)
, nargs(0)
{
args[0] = a;
args[1] = b;
args[2] = c;
args[3] = d;
for (int i = 0; i < 4; i++)
{
if (!args[i])
break;
args[i]->retain();
nargs++;
}
}
Message::~Message()
{
for (int i = 0; i < nargs; i++)
args[i]->release();
}
int Message::toLua(lua_State *L)
{
luax_pushstring(L, name);
for (int i = 0; i < nargs; i++)
args[i]->toLua(L);
return nargs+1;
}
Message *Message::fromLua(lua_State *L, int n)
{
std::string name = luax_checkstring(L, n);
n++;
Message *m = new Message(name);
for (int i = 0; i < 4; i++)
{
if (lua_isnoneornil(L, n+i))
break;
m->args[i] = Variant::fromLua(L, n+i);
if (!m->args[i])
{
delete m;
luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
return NULL;
}
m->nargs++;
}
return m;
}
Event::Event()
{
mutex = thread::newMutex();
}
Event::~Event()
{
delete mutex;
}
void Event::push(Message *msg)
{
Lock lock(mutex);
msg->retain();
queue.push(msg);
}
bool Event::poll(Message *&msg)
{
Lock lock(mutex);
if (queue.empty())
return false;
msg = queue.front();
queue.pop();
return true;
}
void Event::clear()
{
Lock lock(mutex);
while (!queue.empty())
{
// std::queue::pop will remove the first (front) element.
queue.front()->release();
queue.pop();
}
}
} // event
} // love
+76
View File
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_EVENT_EVENT_H
#define LOVE_EVENT_EVENT_H
// LOVE
#include "common/Module.h"
#include "common/StringMap.h"
#include "common/Variant.h"
#include "keyboard/Keyboard.h"
#include "mouse/Mouse.h"
#include "joystick/Joystick.h"
#include "thread/threads.h"
// STL
#include <queue>
namespace love
{
namespace event
{
class Message : public Object
{
private:
std::string name;
Variant *args[4];
int nargs;
public:
Message(const std::string &name, Variant *a = NULL, Variant *b = NULL, Variant *c = NULL, Variant *d = NULL);
~Message();
int toLua(lua_State *L);
static Message *fromLua(lua_State *L, int n);
};
class Event : public Module
{
public:
Event();
virtual ~Event();
void push(Message *msg);
bool poll(Message *&msg);
virtual void clear();
virtual void pump() = 0;
protected:
thread::Mutex *mutex;
std::queue<Message *> queue;
}; // Event
} // event
} // love
#endif // LOVE_EVENT_EVENT_H
+639
View File
@@ -0,0 +1,639 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Event.h"
#include "keyboard/Keyboard.h"
#include "mouse/Mouse.h"
#include "joystick/JoystickModule.h"
#include "joystick/sdl/Joystick.h"
#include "graphics/Graphics.h"
#include "window/Window.h"
#include "common/Exception.h"
#include <cmath>
namespace love
{
namespace event
{
namespace sdl
{
const char *Event::getName() const
{
return "love.event.sdl";
}
Event::Event()
{
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0)
throw love::Exception("%s", SDL_GetError());
}
Event::~Event()
{
SDL_QuitSubSystem(SDL_INIT_EVENTS);
}
void Event::pump()
{
SDL_PumpEvents();
static SDL_Event e;
Message *msg;
while (SDL_PollEvent(&e))
{
msg = convert(e);
if (msg)
{
push(msg);
msg->release();
}
}
}
Message *Event::wait()
{
static SDL_Event e;
bool ok = (SDL_WaitEvent(&e) == 1);
if (!ok)
return NULL;
return convert(e);
}
void Event::clear()
{
static SDL_Event e;
while (SDL_PollEvent(&e))
{
// Do nothing with 'e' ...
}
love::event::Event::clear();
}
Message *Event::convert(const SDL_Event &e) const
{
Message *msg = NULL;
love::keyboard::Keyboard *kb = 0;
love::keyboard::Keyboard::Key key;
love::mouse::Mouse::Button button;
Variant *arg1, *arg2, *arg3;
const char *txt;
std::map<SDL_Keycode, love::keyboard::Keyboard::Key>::const_iterator keyit;
switch (e.type)
{
case SDL_KEYDOWN:
if (e.key.repeat)
{
kb = (love::keyboard::Keyboard *) Module::findInstance("love.keyboard.");
if (kb && !kb->hasKeyRepeat())
break;
}
keyit = keys.find(e.key.keysym.sym);
if (keyit != keys.end())
key = keyit->second;
else
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::keyboard::Keyboard::getConstant(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant(e.key.repeat != 0);
msg = new Message("keypressed", arg1, arg2);
arg1->release();
arg2->release();
break;
case SDL_KEYUP:
keyit = keys.find(e.key.keysym.sym);
if (keyit != keys.end())
key = keyit->second;
else
key = love::keyboard::Keyboard::KEY_UNKNOWN;
if (!love::keyboard::Keyboard::getConstant(key, txt))
txt = "unknown";
arg1 = new Variant(txt, strlen(txt));
msg = new Message("keyreleased", arg1);
arg1->release();
break;
case SDL_TEXTINPUT:
txt = e.text.text;
arg1 = new Variant(txt, strlen(txt));
msg = new Message("textinput", arg1);
arg1->release();
break;
case SDL_TEXTEDITING:
txt = e.edit.text;
arg1 = new Variant(txt, strlen(txt));
arg2 = new Variant((double) e.edit.start);
arg3 = new Variant((double) e.edit.length);
msg = new Message("textedit", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
break;
case SDL_MOUSEBUTTONDOWN:
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);
arg3 = new Variant(txt, strlen(txt));
msg = new Message((e.type == SDL_MOUSEBUTTONDOWN) ?
"mousepressed" : "mousereleased",
arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
}
break;
case SDL_MOUSEWHEEL:
if (e.wheel.y != 0)
{
button = (e.wheel.y > 0) ? mouse::Mouse::BUTTON_WHEELUP : mouse::Mouse::BUTTON_WHEELDOWN;
if (!love::mouse::Mouse::getConstant(button, txt))
break;
int mx, my;
SDL_GetMouseState(&mx, &my);
arg1 = new Variant((double) mx);
arg2 = new Variant((double) my);
arg3 = new Variant(txt, strlen(txt));
msg = new Message("mousepressed", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
}
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
case SDL_JOYAXISMOTION:
case SDL_JOYBALLMOTION:
case SDL_JOYHATMOTION:
case SDL_JOYDEVICEADDED:
case SDL_JOYDEVICEREMOVED:
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERAXISMOTION:
msg = convertJoystickEvent(e);
break;
case SDL_WINDOWEVENT:
msg = convertWindowEvent(e);
break;
case SDL_DROPFILE:
SDL_free(e.drop.file);
break;
case SDL_QUIT:
msg = new Message("quit");
break;
default:
break;
}
return msg;
}
Message *Event::convertJoystickEvent(const SDL_Event &e) const
{
joystick::JoystickModule *joymodule = (joystick::JoystickModule *) Module::findInstance("love.joystick.");
if (!joymodule)
return 0;
Message *msg = 0;
Proxy proxy;
love::joystick::Joystick::Hat hat;
love::joystick::Joystick::GamepadButton padbutton;
love::joystick::Joystick::GamepadAxis padaxis;
Variant *arg1, *arg2, *arg3;
const char *txt;
switch (e.type)
{
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jbutton.which);
if (!proxy.data)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jbutton.button+1));
msg = new Message((e.type == SDL_JOYBUTTONDOWN) ?
"joystickpressed" : "joystickreleased",
arg1, arg2);
arg1->release();
arg2->release();
break;
case SDL_JOYAXISMOTION:
{
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jaxis.which);
if (!proxy.data)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jaxis.axis+1));
float value = e.jaxis.value / 32768.0f;
if (fabsf(value) < 0.001f) value = 0.0f;
if (value < -0.99f) value = -1.0f;
if (value > 0.99f) value = 1.0f;
arg3 = new Variant((double) value);
msg = new Message("joystickaxis", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
}
break;
case SDL_JOYHATMOTION:
if (!joystick::sdl::Joystick::getConstant(e.jhat.value, hat) || !joystick::Joystick::getConstant(hat, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.jhat.which);
if (!proxy.data)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant((double)(e.jhat.hat+1));
arg3 = new Variant(txt, strlen(txt));
msg = new Message("joystickhat", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
if (!joystick::sdl::Joystick::getConstant((SDL_GameControllerButton) e.cbutton.button, padbutton))
break;
if (!joystick::Joystick::getConstant(padbutton, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.cbutton.which);
if (!proxy.data)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant(txt, strlen(txt));
msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ?
"gamepadpressed" : "gamepadreleased", arg1, arg2);
arg1->release();
arg2->release();
break;
case SDL_CONTROLLERAXISMOTION:
if (joystick::sdl::Joystick::getConstant((SDL_GameControllerAxis) e.caxis.axis, padaxis))
{
if (!joystick::Joystick::getConstant(padaxis, txt))
break;
proxy.flags = JOYSTICK_JOYSTICK_T;
proxy.data = joymodule->getJoystickFromID(e.caxis.which);
if (!proxy.data)
break;
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
arg2 = new Variant(txt, strlen(txt));
float value = e.jaxis.value / 32768.0f;
if (fabsf(value) < 0.001f) value = 0.0f;
if (value < -0.99f) value = -1.0f;
if (value > 0.99f) value = 1.0f;
arg3 = new Variant((double) value);
msg = new Message("gamepadaxis", arg1, arg2, arg3);
arg1->release();
arg2->release();
arg3->release();
}
break;
case SDL_JOYDEVICEADDED:
// jdevice.which is the joystick device index.
proxy.data = joymodule->addJoystick(e.jdevice.which);
proxy.flags = JOYSTICK_JOYSTICK_T;
if (proxy.data)
{
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
msg = new Message("joystickadded", arg1);
arg1->release();
}
break;
case SDL_JOYDEVICEREMOVED:
// jdevice.which is the joystick instance ID now.
proxy.data = joymodule->getJoystickFromID(e.jdevice.which);
proxy.flags = JOYSTICK_JOYSTICK_T;
if (proxy.data)
{
joymodule->removeJoystick((joystick::Joystick *) proxy.data);
arg1 = new Variant(JOYSTICK_JOYSTICK_ID, (void *) &proxy);
msg = new Message("joystickremoved", arg1);
arg1->release();
}
break;
default:
break;
}
return msg;
}
Message *Event::convertWindowEvent(const SDL_Event &e) const
{
Message *msg = 0;
Variant *arg1, *arg2;
window::Window *win = 0;
if (e.type != SDL_WINDOWEVENT)
return 0;
switch (e.window.event)
{
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_FOCUS_LOST:
// Users won't expect the screensaver to activate if a game is in
// focus. Also, joystick input may not delay the screensaver timer.
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
SDL_DisableScreenSaver();
else
SDL_EnableScreenSaver();
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
msg = new Message("focus", arg1);
arg1->release();
break;
case SDL_WINDOWEVENT_ENTER:
case SDL_WINDOWEVENT_LEAVE:
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_ENTER);
msg = new Message("mousefocus", arg1);
arg1->release();
break;
case SDL_WINDOWEVENT_SHOWN:
case SDL_WINDOWEVENT_HIDDEN:
arg1 = new Variant(e.window.event == SDL_WINDOWEVENT_SHOWN);
msg = new Message("visible", arg1);
arg1->release();
break;
case SDL_WINDOWEVENT_RESIZED:
win = (window::Window *) Module::findInstance("love.window.");
if (win)
{
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);
}
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;
}
return msg;
}
std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::createKeyMap()
{
using love::keyboard::Keyboard;
std::map<SDL_Keycode, Keyboard::Key> k;
k[SDLK_UNKNOWN] = Keyboard::KEY_UNKNOWN;
k[SDLK_RETURN] = Keyboard::KEY_RETURN;
k[SDLK_ESCAPE] = Keyboard::KEY_ESCAPE;
k[SDLK_BACKSPACE] = Keyboard::KEY_BACKSPACE;
k[SDLK_TAB] = Keyboard::KEY_TAB;
k[SDLK_SPACE] = Keyboard::KEY_SPACE;
k[SDLK_EXCLAIM] = Keyboard::KEY_EXCLAIM;
k[SDLK_QUOTEDBL] = Keyboard::KEY_QUOTEDBL;
k[SDLK_HASH] = Keyboard::KEY_HASH;
k[SDLK_DOLLAR] = Keyboard::KEY_DOLLAR;
k[SDLK_AMPERSAND] = Keyboard::KEY_AMPERSAND;
k[SDLK_QUOTE] = Keyboard::KEY_QUOTE;
k[SDLK_LEFTPAREN] = Keyboard::KEY_LEFTPAREN;
k[SDLK_RIGHTPAREN] = Keyboard::KEY_RIGHTPAREN;
k[SDLK_ASTERISK] = Keyboard::KEY_ASTERISK;
k[SDLK_PLUS] = Keyboard::KEY_PLUS;
k[SDLK_COMMA] = Keyboard::KEY_COMMA;
k[SDLK_MINUS] = Keyboard::KEY_MINUS;
k[SDLK_PERIOD] = Keyboard::KEY_PERIOD;
k[SDLK_SLASH] = Keyboard::KEY_SLASH;
k[SDLK_0] = Keyboard::KEY_0;
k[SDLK_1] = Keyboard::KEY_1;
k[SDLK_2] = Keyboard::KEY_2;
k[SDLK_3] = Keyboard::KEY_3;
k[SDLK_4] = Keyboard::KEY_4;
k[SDLK_5] = Keyboard::KEY_5;
k[SDLK_6] = Keyboard::KEY_6;
k[SDLK_7] = Keyboard::KEY_7;
k[SDLK_8] = Keyboard::KEY_8;
k[SDLK_9] = Keyboard::KEY_9;
k[SDLK_COLON] = Keyboard::KEY_COLON;
k[SDLK_SEMICOLON] = Keyboard::KEY_SEMICOLON;
k[SDLK_LESS] = Keyboard::KEY_LESS;
k[SDLK_EQUALS] = Keyboard::KEY_EQUALS;
k[SDLK_GREATER] = Keyboard::KEY_GREATER;
k[SDLK_QUESTION] = Keyboard::KEY_QUESTION;
k[SDLK_AT] = Keyboard::KEY_AT;
k[SDLK_LEFTBRACKET] = Keyboard::KEY_LEFTBRACKET;
k[SDLK_BACKSLASH] = Keyboard::KEY_BACKSLASH;
k[SDLK_RIGHTBRACKET] = Keyboard::KEY_RIGHTBRACKET;
k[SDLK_CARET] = Keyboard::KEY_CARET;
k[SDLK_UNDERSCORE] = Keyboard::KEY_UNDERSCORE;
k[SDLK_BACKQUOTE] = Keyboard::KEY_BACKQUOTE;
k[SDLK_a] = Keyboard::KEY_A;
k[SDLK_b] = Keyboard::KEY_B;
k[SDLK_c] = Keyboard::KEY_C;
k[SDLK_d] = Keyboard::KEY_D;
k[SDLK_e] = Keyboard::KEY_E;
k[SDLK_f] = Keyboard::KEY_F;
k[SDLK_g] = Keyboard::KEY_G;
k[SDLK_h] = Keyboard::KEY_H;
k[SDLK_i] = Keyboard::KEY_I;
k[SDLK_j] = Keyboard::KEY_J;
k[SDLK_k] = Keyboard::KEY_K;
k[SDLK_l] = Keyboard::KEY_L;
k[SDLK_m] = Keyboard::KEY_M;
k[SDLK_n] = Keyboard::KEY_N;
k[SDLK_o] = Keyboard::KEY_O;
k[SDLK_p] = Keyboard::KEY_P;
k[SDLK_q] = Keyboard::KEY_Q;
k[SDLK_r] = Keyboard::KEY_R;
k[SDLK_s] = Keyboard::KEY_S;
k[SDLK_t] = Keyboard::KEY_T;
k[SDLK_u] = Keyboard::KEY_U;
k[SDLK_v] = Keyboard::KEY_V;
k[SDLK_w] = Keyboard::KEY_W;
k[SDLK_x] = Keyboard::KEY_X;
k[SDLK_y] = Keyboard::KEY_Y;
k[SDLK_z] = Keyboard::KEY_Z;
k[SDLK_CAPSLOCK] = Keyboard::KEY_CAPSLOCK;
k[SDLK_F1] = Keyboard::KEY_F1;
k[SDLK_F2] = Keyboard::KEY_F2;
k[SDLK_F3] = Keyboard::KEY_F3;
k[SDLK_F4] = Keyboard::KEY_F4;
k[SDLK_F5] = Keyboard::KEY_F5;
k[SDLK_F6] = Keyboard::KEY_F6;
k[SDLK_F7] = Keyboard::KEY_F7;
k[SDLK_F8] = Keyboard::KEY_F8;
k[SDLK_F9] = Keyboard::KEY_F9;
k[SDLK_F10] = Keyboard::KEY_F10;
k[SDLK_F11] = Keyboard::KEY_F11;
k[SDLK_F12] = Keyboard::KEY_F12;
k[SDLK_PRINTSCREEN] = Keyboard::KEY_PRINTSCREEN;
k[SDLK_SCROLLLOCK] = Keyboard::KEY_SCROLLLOCK;
k[SDLK_PAUSE] = Keyboard::KEY_PAUSE;
k[SDLK_INSERT] = Keyboard::KEY_INSERT;
k[SDLK_HOME] = Keyboard::KEY_HOME;
k[SDLK_PAGEUP] = Keyboard::KEY_PAGEUP;
k[SDLK_DELETE] = Keyboard::KEY_DELETE;
k[SDLK_END] = Keyboard::KEY_END;
k[SDLK_PAGEDOWN] = Keyboard::KEY_PAGEDOWN;
k[SDLK_RIGHT] = Keyboard::KEY_RIGHT;
k[SDLK_LEFT] = Keyboard::KEY_LEFT;
k[SDLK_DOWN] = Keyboard::KEY_DOWN;
k[SDLK_UP] = Keyboard::KEY_UP;
k[SDLK_NUMLOCKCLEAR] = Keyboard::KEY_NUMLOCKCLEAR;
k[SDLK_KP_DIVIDE] = Keyboard::KEY_KP_DIVIDE;
k[SDLK_KP_MULTIPLY] = Keyboard::KEY_KP_MULTIPLY;
k[SDLK_KP_MINUS] = Keyboard::KEY_KP_MINUS;
k[SDLK_KP_PLUS] = Keyboard::KEY_KP_PLUS;
k[SDLK_KP_ENTER] = Keyboard::KEY_KP_ENTER;
k[SDLK_KP_0] = Keyboard::KEY_KP_0;
k[SDLK_KP_1] = Keyboard::KEY_KP_1;
k[SDLK_KP_2] = Keyboard::KEY_KP_2;
k[SDLK_KP_3] = Keyboard::KEY_KP_3;
k[SDLK_KP_4] = Keyboard::KEY_KP_4;
k[SDLK_KP_5] = Keyboard::KEY_KP_5;
k[SDLK_KP_6] = Keyboard::KEY_KP_6;
k[SDLK_KP_7] = Keyboard::KEY_KP_7;
k[SDLK_KP_8] = Keyboard::KEY_KP_8;
k[SDLK_KP_9] = Keyboard::KEY_KP_9;
k[SDLK_KP_PERIOD] = Keyboard::KEY_KP_PERIOD;
k[SDLK_KP_COMMA] = Keyboard::KEY_KP_COMMA;
k[SDLK_KP_EQUALS] = Keyboard::KEY_KP_EQUALS;
k[SDLK_APPLICATION] = Keyboard::KEY_APPLICATION;
k[SDLK_POWER] = Keyboard::KEY_POWER;
k[SDLK_F13] = Keyboard::KEY_F13;
k[SDLK_F14] = Keyboard::KEY_F14;
k[SDLK_F15] = Keyboard::KEY_F15;
k[SDLK_F16] = Keyboard::KEY_F16;
k[SDLK_F17] = Keyboard::KEY_F17;
k[SDLK_F18] = Keyboard::KEY_F18;
k[SDLK_F19] = Keyboard::KEY_F19;
k[SDLK_F20] = Keyboard::KEY_F20;
k[SDLK_F21] = Keyboard::KEY_F21;
k[SDLK_F22] = Keyboard::KEY_F22;
k[SDLK_F23] = Keyboard::KEY_F23;
k[SDLK_F24] = Keyboard::KEY_F24;
k[SDLK_EXECUTE] = Keyboard::KEY_EXECUTE;
k[SDLK_HELP] = Keyboard::KEY_HELP;
k[SDLK_MENU] = Keyboard::KEY_MENU;
k[SDLK_SELECT] = Keyboard::KEY_SELECT;
k[SDLK_STOP] = Keyboard::KEY_STOP;
k[SDLK_AGAIN] = Keyboard::KEY_AGAIN;
k[SDLK_UNDO] = Keyboard::KEY_UNDO;
k[SDLK_CUT] = Keyboard::KEY_CUT;
k[SDLK_COPY] = Keyboard::KEY_COPY;
k[SDLK_PASTE] = Keyboard::KEY_PASTE;
k[SDLK_FIND] = Keyboard::KEY_FIND;
k[SDLK_MUTE] = Keyboard::KEY_MUTE;
k[SDLK_VOLUMEUP] = Keyboard::KEY_VOLUMEUP;
k[SDLK_VOLUMEDOWN] = Keyboard::KEY_VOLUMEDOWN;
k[SDLK_ALTERASE] = Keyboard::KEY_ALTERASE;
k[SDLK_SYSREQ] = Keyboard::KEY_SYSREQ;
k[SDLK_CANCEL] = Keyboard::KEY_CANCEL;
k[SDLK_CLEAR] = Keyboard::KEY_CLEAR;
k[SDLK_PRIOR] = Keyboard::KEY_PRIOR;
k[SDLK_RETURN2] = Keyboard::KEY_RETURN2;
k[SDLK_SEPARATOR] = Keyboard::KEY_SEPARATOR;
k[SDLK_OUT] = Keyboard::KEY_OUT;
k[SDLK_OPER] = Keyboard::KEY_OPER;
k[SDLK_CLEARAGAIN] = Keyboard::KEY_CLEARAGAIN;
k[SDLK_THOUSANDSSEPARATOR] = Keyboard::KEY_THOUSANDSSEPARATOR;
k[SDLK_DECIMALSEPARATOR] = Keyboard::KEY_DECIMALSEPARATOR;
k[SDLK_CURRENCYUNIT] = Keyboard::KEY_CURRENCYUNIT;
k[SDLK_CURRENCYSUBUNIT] = Keyboard::KEY_CURRENCYSUBUNIT;
k[SDLK_LCTRL] = Keyboard::KEY_LCTRL;
k[SDLK_LSHIFT] = Keyboard::KEY_LSHIFT;
k[SDLK_LALT] = Keyboard::KEY_LALT;
k[SDLK_LGUI] = Keyboard::KEY_LGUI;
k[SDLK_RCTRL] = Keyboard::KEY_RCTRL;
k[SDLK_RSHIFT] = Keyboard::KEY_RSHIFT;
k[SDLK_RALT] = Keyboard::KEY_RALT;
k[SDLK_RGUI] = Keyboard::KEY_RGUI;
k[SDLK_MODE] = Keyboard::KEY_MODE;
k[SDLK_AUDIONEXT] = Keyboard::KEY_AUDIONEXT;
k[SDLK_AUDIOPREV] = Keyboard::KEY_AUDIOPREV;
k[SDLK_AUDIOSTOP] = Keyboard::KEY_AUDIOSTOP;
k[SDLK_AUDIOPLAY] = Keyboard::KEY_AUDIOPLAY;
k[SDLK_AUDIOMUTE] = Keyboard::KEY_AUDIOMUTE;
k[SDLK_MEDIASELECT] = Keyboard::KEY_MEDIASELECT;
k[SDLK_BRIGHTNESSDOWN] = Keyboard::KEY_BRIGHTNESSDOWN;
k[SDLK_BRIGHTNESSUP] = Keyboard::KEY_BRIGHTNESSUP;
k[SDLK_DISPLAYSWITCH] = Keyboard::KEY_DISPLAYSWITCH;
k[SDLK_KBDILLUMTOGGLE] = Keyboard::KEY_KBDILLUMTOGGLE;
k[SDLK_KBDILLUMDOWN] = Keyboard::KEY_KBDILLUMDOWN;
k[SDLK_KBDILLUMUP] = Keyboard::KEY_KBDILLUMUP;
k[SDLK_EJECT] = Keyboard::KEY_EJECT;
k[SDLK_SLEEP] = Keyboard::KEY_SLEEP;
return k;
}
std::map<SDL_Keycode, love::keyboard::Keyboard::Key> Event::keys = Event::createKeyMap();
EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry Event::buttonEntries[] =
{
{ love::mouse::Mouse::BUTTON_LEFT, SDL_BUTTON_LEFT},
{ love::mouse::Mouse::BUTTON_MIDDLE, SDL_BUTTON_MIDDLE},
{ love::mouse::Mouse::BUTTON_RIGHT, SDL_BUTTON_RIGHT},
{ love::mouse::Mouse::BUTTON_X1, SDL_BUTTON_X1},
{ love::mouse::Mouse::BUTTON_X2, SDL_BUTTON_X2},
};
EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM> Event::buttons(Event::buttonEntries, sizeof(Event::buttonEntries));
} // sdl
} // event
} // love
+89
View File
@@ -0,0 +1,89 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_EVENT_SDL_EVENT_H
#define LOVE_EVENT_SDL_EVENT_H
// LOVE
#include "event/Event.h"
#include "common/runtime.h"
#include "common/EnumMap.h"
// SDL
#include <SDL.h>
// STL
#include <map>
namespace love
{
namespace event
{
namespace sdl
{
class Event : public love::event::Event
{
public:
// Implements Module.
const char *getName() const;
Event();
virtual ~Event();
/**
* Pumps the event queue. This function gathers all the pending input information
* from devices and places it on the event queue. Normally not needed if you poll
* for events.
**/
void pump();
/**
* Waits for the next event (indefinitely). Useful for creating games where
* the screen and game state only needs updating when the user interacts with
* the window.
**/
Message *wait();
/**
* Clears the event queue.
*/
void clear();
private:
Message *convert(const SDL_Event &e) const;
Message *convertJoystickEvent(const SDL_Event &e) const;
Message *convertWindowEvent(const SDL_Event &e) const;
static std::map<SDL_Keycode, love::keyboard::Keyboard::Key> createKeyMap();
static std::map<SDL_Keycode, love::keyboard::Keyboard::Key> keys;
static EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM>::Entry buttonEntries[];
static EnumMap<love::mouse::Mouse::Button, Uint8, love::mouse::Mouse::BUTTON_MAX_ENUM> buttons;
}; // System
} // sdl
} // event
} // love
#endif // LOVE_EVENT_SDL_EVENT_H
@@ -0,0 +1,143 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Event.h"
// LOVE
#include "common/runtime.h"
// sdlevent
#include "Event.h"
namespace love
{
namespace event
{
namespace sdl
{
static Event *instance = 0;
static int poll_i(lua_State *L)
{
Message *m;
while (instance->poll(m))
{
int args = m->toLua(L);
m->release();
return args;
}
// No pending events.
return 0;
}
int w_pump(lua_State *)
{
instance->pump();
return 0;
}
int w_poll(lua_State *L)
{
lua_pushcclosure(L, &poll_i, 0);
return 1;
}
int w_wait(lua_State *L)
{
Message *m;
if ((m = instance->wait()))
{
int args = m->toLua(L);
m->release();
return args;
}
return 0;
}
int w_push(lua_State *L)
{
Message *m;
bool success = (m = Message::fromLua(L, 1)) != NULL;
luax_pushboolean(L, success);
if (!success)
return 1;
instance->push(m);
m->release();
return 1;
}
int w_clear(lua_State *)
{
instance->clear();
return 0;
}
int w_quit(lua_State *L)
{
Message *m = new Message("quit");
instance->push(m);
m->release();
luax_pushboolean(L, true);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "pump", w_pump },
{ "poll", w_poll },
{ "wait", w_wait },
{ "push", w_push },
{ "clear", w_clear },
{ "quit", w_quit },
{ 0, 0 }
};
extern "C" int luaopen_love_event(lua_State *L)
{
if (instance == 0)
{
EXCEPT_GUARD(instance = new Event();)
}
else
instance->retain();
WrappedModule w;
w.module = instance;
w.name = "event";
w.flags = MODULE_T;
w.functions = functions;
w.types = 0;
return luax_register_module(L, w);
}
} // sdl
} // event
} // love
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_EVENT_SDL_WRAP_EVENT_H
#define LOVE_EVENT_SDL_WRAP_EVENT_H
// LOVE
#include "common/config.h"
#include "Event.h"
namespace love
{
namespace event
{
namespace sdl
{
int w_pump(lua_State *L);
int w_poll(lua_State *L);
int w_wait(lua_State *L);
int w_push(lua_State *L);
int w_clear(lua_State *L);
int w_quit(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_event(lua_State *L);
} // sdl
} // event
} // love
#endif // LOVE_EVENT_SDL_WRAP_EVENT_H
+72
View File
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "File.h"
namespace love
{
namespace filesystem
{
File::~File()
{
}
bool File::getConstant(const char *in, Mode &out)
{
return modes.find(in, out);
}
bool File::getConstant(Mode in, const char *&out)
{
return modes.find(in, out);
}
bool File::getConstant(const char *in, BufferMode &out)
{
return bufferModes.find(in, out);
}
bool File::getConstant(BufferMode in, const char *&out)
{
return bufferModes.find(in, out);
}
StringMap<File::Mode, File::MODE_MAX_ENUM>::Entry File::modeEntries[] =
{
{"c", File::CLOSED},
{"r", File::READ},
{"w", File::WRITE},
{"a", File::APPEND},
};
StringMap<File::Mode, File::MODE_MAX_ENUM> File::modes(File::modeEntries, sizeof(File::modeEntries));
StringMap<File::BufferMode, File::BUFFER_MAX_ENUM>::Entry File::bufferModeEntries[] =
{
{"none", File::BUFFER_NONE},
{"line", File::BUFFER_LINE},
{"full", File::BUFFER_FULL},
};
StringMap<File::BufferMode, File::BUFFER_MAX_ENUM> File::bufferModes(File::bufferModeEntries, sizeof(File::bufferModeEntries));
} // filesystem
} // love
+222
View File
@@ -0,0 +1,222 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_FILE_H
#define LOVE_FILESYSTEM_FILE_H
// STD
#include <string>
// LOVE
#include "common/Data.h"
#include "common/Object.h"
#include "common/StringMap.h"
#include "common/int.h"
#include "FileData.h"
namespace love
{
namespace filesystem
{
/**
* A File interface, providing generic means of reading from and
* writing to files.
**/
class File : public Object
{
public:
/**
* File open mode.
**/
enum Mode
{
CLOSED,
READ,
WRITE,
APPEND,
MODE_MAX_ENUM
};
enum BufferMode
{
BUFFER_NONE,
BUFFER_LINE,
BUFFER_FULL,
BUFFER_MAX_ENUM
};
/**
* Used to indicate ALL data in a file.
**/
static const int64 ALL = -1;
/**
* Destructor.
**/
virtual ~File();
/**
* Opens the file in a certain mode.
*
* @param mode READ, WRITE, APPEND.
* @return True if successful, false otherwise.
**/
virtual bool open(Mode mode) = 0;
/**
* Closes the file.
*
* @return True if successful, false otherwise.
**/
virtual bool close() = 0;
/**
* Gets whether the file is open.
**/
virtual bool isOpen() const = 0;
/**
* Gets the size of the file.
*
* @return The size of the file.
**/
virtual int64 getSize() = 0;
/**
* Reads data from the file and allocates a Data object.
*
* @param size The number of bytes to attempt reading, or -1 for EOF.
* @return A newly allocated Data object.
**/
virtual FileData *read(int64 size = ALL) = 0;
/**
* Reads data into the destination buffer.
*
* @param dst The destination buffer.
* @param size The number of bytes to attempt reading.
* @return The number of bytes actually read.
**/
virtual int64 read(void *dst, int64 size) = 0;
/**
* Writes data into the File.
*
* @param data The source buffer.
* @param size The size of the buffer.
* @return True of success, false otherwise.
**/
virtual bool write(const void *data, int64 size) = 0;
/**
* Writes a Data object into the File.
*
* @param data The data object to write into the file.
* @param size The number of bytes to attempt writing, or -1 for everything.
* @return True of success, false otherwise.
**/
virtual bool write(const Data *data, int64 size = ALL) = 0;
/**
* Flushes the currently buffered file data to disk. Only applicable in
* write mode.
**/
virtual bool flush() = 0;
/**
* Checks whether we are currently at end-of-file.
*
* @return True if EOF, false otherwise.
**/
virtual bool eof() = 0;
/**
* Gets the current position in the File.
*
* @return The current byte position in the File.
**/
virtual int64 tell() = 0;
/**
* Seeks to a certain position in the File.
*
* @param pos The byte position in the file.
* @return True on success, false otherwise.
**/
virtual bool seek(uint64 pos) = 0;
/**
* Sets the buffering mode for the file. When buffering is enabled, the file
* will not write to disk (or will pre-load data if in read mode) until the
* buffer's capacity is reached.
* In the BUFFER_LINE mode, the file will also write to disk if a newline is
* written.
*
* @param bufmode The buffer mode.
* @param size The size in bytes of the buffer.
**/
virtual bool setBuffer(BufferMode bufmode, int64 size) = 0;
/**
* @param[out] size The size in bytes of the buffer.
* @return The current buffer mode.
**/
virtual BufferMode getBuffer(int64 &size) const = 0;
/**
* Gets the current mode of the File.
* @return The current mode of the File; CLOSED, READ, WRITE or APPEND.
**/
virtual Mode getMode() const = 0;
/**
* Gets the filename for this File, or empty string if none.
* @return The filename for this File.
**/
virtual std::string getFilename() const = 0;
/**
* Gets the file extension for this File, or empty string if none.
* @return The file extension for this File (without the dot).
**/
virtual std::string getExtension() const = 0;
static bool getConstant(const char *in, Mode &out);
static bool getConstant(Mode in, const char *&out);
static bool getConstant(const char *in, BufferMode &out);
static bool getConstant(BufferMode in, const char *&out);
private:
static StringMap<Mode, MODE_MAX_ENUM>::Entry modeEntries[];
static StringMap<Mode, MODE_MAX_ENUM> modes;
static StringMap<BufferMode, BUFFER_MAX_ENUM>::Entry bufferModeEntries[];
static StringMap<BufferMode, BUFFER_MAX_ENUM> bufferModes;
}; // File
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_FILE_H
@@ -0,0 +1,91 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "FileData.h"
// STD
#include <iostream>
#include <climits>
namespace love
{
namespace filesystem
{
FileData::FileData(uint64 size, const std::string &filename)
: data(new char[(size_t) size])
, size(size)
, filename(filename)
{
if (filename.rfind('.') != std::string::npos)
extension = filename.substr(filename.rfind('.')+1);
}
FileData::~FileData()
{
delete [] data;
}
void *FileData::getData() const
{
return (void *)data;
}
// TODO: Enable this
/*uint64 FileData::getSize() const
{
return size;
}*/
int FileData::getSize() const
{
return size > INT_MAX ? INT_MAX : (int) size;
}
const std::string &FileData::getFilename() const
{
return filename;
}
const std::string &FileData::getExtension() const
{
return extension;
}
bool FileData::getConstant(const char *in, Decoder &out)
{
return decoders.find(in, out);
}
bool FileData::getConstant(Decoder in, const char *&out)
{
return decoders.find(in, out);
}
StringMap<FileData::Decoder, FileData::DECODE_MAX_ENUM>::Entry FileData::decoderEntries[] =
{
{"file", FileData::FILE},
{"base64", FileData::BASE64},
};
StringMap<FileData::Decoder, FileData::DECODE_MAX_ENUM> FileData::decoders(FileData::decoderEntries, sizeof(FileData::decoderEntries));
} // filesystem
} // love
@@ -0,0 +1,84 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_FILE_DATA_H
#define LOVE_FILESYSTEM_FILE_DATA_H
// LOVE
#include <string>
#include "common/Data.h"
#include "common/StringMap.h"
#include "common/int.h"
namespace love
{
namespace filesystem
{
class FileData : public Data
{
public:
enum Decoder
{
FILE,
BASE64,
DECODE_MAX_ENUM
}; // Decoder
FileData(uint64 size, const std::string &filename);
virtual ~FileData();
// Implements Data.
void *getData() const;
//TODO: Enable this
//uint64 getSize() const;
int getSize() const;
const std::string &getFilename() const;
const std::string &getExtension() const;
static bool getConstant(const char *in, Decoder &out);
static bool getConstant(Decoder in, const char *&out);
private:
// The actual data.
char *data;
// Size of the data.
uint64 size;
// The filename used for error purposes.
std::string filename;
// The extension (without dot). Used to identify file type.
std::string extension;
static StringMap<Decoder, DECODE_MAX_ENUM>::Entry decoderEntries[];
static StringMap<Decoder, DECODE_MAX_ENUM> decoders;
}; // FileData
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_FILE_DATA_H
@@ -0,0 +1,342 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "File.h"
// STD
#include <cstring>
// LOVE
#include "Filesystem.h"
#include "filesystem/FileData.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
extern bool hack_setupWriteDirectory();
File::File(const std::string &filename)
: filename(filename)
, file(0)
, mode(CLOSED)
, bufferMode(BUFFER_NONE)
, bufferSize(0)
{
}
File::~File()
{
if (mode != CLOSED)
close();
}
bool File::open(Mode mode)
{
if (mode == CLOSED)
return true;
// File must exist if read mode.
if ((mode == READ) && !PHYSFS_exists(filename.c_str()))
throw love::Exception("Could not open file %s. Does not exist.", filename.c_str());
// Check whether the write directory is set.
if ((mode == APPEND || mode == WRITE) && (PHYSFS_getWriteDir() == 0) && !hack_setupWriteDirectory())
throw love::Exception("Could not set write directory.");
// File already open?
if (file != 0)
return false;
this->mode = mode;
switch (mode)
{
case READ:
file = PHYSFS_openRead(filename.c_str());
break;
case APPEND:
file = PHYSFS_openAppend(filename.c_str());
break;
case WRITE:
file = PHYSFS_openWrite(filename.c_str());
break;
default:
break;
}
if (file != 0 && !setBuffer(bufferMode, bufferSize))
{
// Revert to buffer defaults if we don't successfully set the buffer.
bufferMode = BUFFER_NONE;
bufferSize = 0;
}
return (file != 0);
}
bool File::close()
{
if (!PHYSFS_close(file))
return false;
mode = CLOSED;
file = 0;
return true;
}
bool File::isOpen() const
{
return mode != CLOSED && file != 0;
}
int64 File::getSize()
{
// If the file is closed, open it to
// check the size.
if (file == 0)
{
open(READ);
int64 size = (int64)PHYSFS_fileLength(file);
close();
return size;
}
return (int64)PHYSFS_fileLength(file);
}
FileData *File::read(int64 size)
{
bool isOpen = (file != 0);
if (!isOpen && !open(READ))
throw love::Exception("Could not read file %s.", filename.c_str());
int64 max = getSize();
int64 cur = tell();
size = (size == ALL) ? max : size;
if (size < 0)
throw love::Exception("Invalid read size.");
// Clamping because the file offset may be in a weird position.
if (cur < 0)
cur = 0;
else if (cur > max)
cur = max;
if (cur + size > max)
size = max - cur;
FileData *fileData = new FileData(size, getFilename());
int64 bytesRead = read(fileData->getData(), size);
if (bytesRead < 0 || (bytesRead == 0 && bytesRead != size))
{
delete fileData;
throw love::Exception("Could not read from file.");
}
if (bytesRead < size)
{
FileData *tmpFileData = new FileData(bytesRead, getFilename());
memcpy(tmpFileData->getData(), fileData->getData(), (size_t) bytesRead);
delete fileData;
fileData = tmpFileData;
}
if (!isOpen)
close();
return fileData;
}
int64 File::read(void *dst, int64 size)
{
if (!file || mode != READ)
throw love::Exception("File is not opened for reading.");
int64 max = (int64)PHYSFS_fileLength(file);
size = (size == ALL) ? max : size;
size = (size > max) ? max : size;
// Sadly, we'll have to clamp to 32 bits here
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
if (size < 0)
throw love::Exception("Invalid read size.");
int64 read = (int64)PHYSFS_read(file, dst, 1, (PHYSFS_uint32) size);
return read;
}
bool File::write(const void *data, int64 size)
{
if (!file || (mode != WRITE && mode != APPEND))
throw love::Exception("File is not opened for writing.");
// Another clamp, for the time being.
size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size;
if (size < 0)
throw love::Exception("Invalid write size.");
// Try to write.
int64 written = static_cast<int64>(PHYSFS_write(file, data, 1, (PHYSFS_uint32) size));
// Check that correct amount of data was written.
if (written != size)
return false;
// Manually flush the buffer in BUFFER_LINE mode if we find a newline.
if (bufferMode == BUFFER_LINE && bufferSize > size)
{
if (memchr(data, '\n', (size_t) size) != NULL)
flush();
}
return true;
}
bool File::write(const Data *data, int64 size)
{
return write(data->getData(), (size == ALL) ? data->getSize() : size);
}
bool File::flush()
{
if (!file || (mode != WRITE && mode != APPEND))
throw love::Exception("File is not opened for writing.");
return PHYSFS_flush(file) != 0;
}
#ifdef LOVE_WINDOWS
// MSVC doesn't like the 'this' keyword
// well, we'll use 'that'.
// It zigs, we zag.
inline bool test_eof(File *that, PHYSFS_File *)
{
int64 pos = that->tell();
int64 size = that->getSize();
return pos == -1 || size == -1 || pos >= size;
}
#else
inline bool test_eof(File *, PHYSFS_File *file)
{
return PHYSFS_eof(file);
}
#endif
bool File::eof()
{
if (file == 0 || test_eof(this, file))
return true;
return false;
}
int64 File::tell()
{
if (file == 0)
return -1;
return (int64) PHYSFS_tell(file);
}
bool File::seek(uint64 pos)
{
if (file == 0)
return false;
if (!PHYSFS_seek(file, (PHYSFS_uint64) pos))
return false;
return true;
}
bool File::setBuffer(BufferMode bufmode, int64 size)
{
// No negativity allowed!
if (size < 0)
return false;
// If the file isn't open, we'll make sure the buffer values are set in
// File::open.
if (file == 0 || mode == CLOSED)
{
bufferMode = bufmode;
bufferSize = size;
return true;
}
int ret = 1;
switch (bufmode)
{
case BUFFER_NONE:
default:
ret = PHYSFS_setBuffer(file, 0);
size = 0;
break;
case BUFFER_LINE:
case BUFFER_FULL:
ret = PHYSFS_setBuffer(file, size);
break;
}
if (ret == 0)
return false;
bufferMode = bufmode;
bufferSize = size;
return true;
}
File::BufferMode File::getBuffer(int64 &size) const
{
size = bufferSize;
return bufferMode;
}
std::string File::getFilename() const
{
return filename;
}
std::string File::getExtension() const
{
std::string::size_type idx = filename.rfind('.');
if (idx != std::string::npos)
return filename.substr(idx+1);
else
return std::string();
}
filesystem::File::Mode File::getMode() const
{
return mode;
}
} // physfs
} // filesystem
} // love
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_FILE_H
#define LOVE_FILESYSTEM_PHYSFS_FILE_H
// LOVE
#include "filesystem/File.h"
// PhysFS
#ifdef LOVE_MACOSX // wacky Mac behavior means different #include syntax!
#include <physfs/physfs.h>
#else
#include <physfs.h>
#endif
// STD
#include <string>
namespace love
{
namespace filesystem
{
namespace physfs
{
class File : public love::filesystem::File
{
public:
/**
* Constructs an File with the given ilename.
* @param filename The relative filepath of the file to load.
**/
File(const std::string &filename);
virtual ~File();
// Implements love::filesystem::File.
bool open(Mode mode);
bool close();
bool isOpen() const;
int64 getSize();
FileData *read(int64 size = ALL);
int64 read(void *dst, int64 size);
bool write(const void *data, int64 size);
bool write(const Data *data, int64 size = ALL);
bool flush();
bool eof();
int64 tell();
bool seek(uint64 pos);
bool setBuffer(BufferMode bufmode, int64 size);
BufferMode getBuffer(int64 &size) const;
Mode getMode() const;
std::string getFilename() const;
std::string getExtension() const;
private:
// filename
std::string filename;
// PHYSFS File handle.
PHYSFS_File *file;
// The current mode of the file.
Mode mode;
BufferMode bufferMode;
int64 bufferSize;
}; // File
} // physfs
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_PHYSFS_FILE_H
@@ -0,0 +1,609 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include <iostream>
#include "common/utf8.h"
#include "common/b64.h"
#include "Filesystem.h"
namespace
{
size_t getDriveDelim(const std::string &input)
{
for (size_t i = 0; i < input.size(); ++i)
if (input[i] == '/' || input[i] == '\\')
return i;
// Something's horribly wrong
return 0;
}
std::string getDriveRoot(const std::string &input)
{
return input.substr(0, getDriveDelim(input)+1);
}
std::string skipDriveRoot(const std::string &input)
{
return input.substr(getDriveDelim(input)+1);
}
}
namespace love
{
namespace filesystem
{
namespace physfs
{
Filesystem::Filesystem()
: initialized(false)
, fused(false)
, fusedSet(false)
{
}
Filesystem::~Filesystem()
{
if (initialized)
PHYSFS_deinit();
}
const char *Filesystem::getName() const
{
return "love.filesystem.physfs";
}
void Filesystem::init(const char *arg0)
{
if (!PHYSFS_init(arg0))
throw Exception(PHYSFS_getLastError());
initialized = true;
}
void Filesystem::setFused(bool fused)
{
if (fusedSet)
return;
this->fused = fused;
fusedSet = true;
}
bool Filesystem::isFused() const
{
if (!fusedSet)
return false;
return fused;
}
bool Filesystem::setIdentity(const char *ident, bool appendToPath)
{
if (!initialized)
return false;
std::string old_save_path = save_path_full;
// Store the save directory.
save_identity = std::string(ident);
// Generate the relative path to the game save folder.
save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity;
// Generate the full path to the game save folder.
save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR);
if (fused)
save_path_full += std::string(LOVE_APPDATA_PREFIX) + save_identity;
else
save_path_full += save_path_relative;
// We now have something like:
// save_identity: game
// save_path_relative: ./LOVE/game
// save_path_full: C:\Documents and Settings\user\Application Data/LOVE/game
// We don't want old read-only save paths to accumulate when we set a new
// identity.
if (!old_save_path.empty())
PHYSFS_removeFromSearchPath(old_save_path.c_str());
// Try to add the save directory to the search path.
// (No error on fail, it means that the path doesn't exist).
PHYSFS_addToSearchPath(save_path_full.c_str(), appendToPath);
return true;
}
const char *Filesystem::getIdentity() const
{
return save_identity.c_str();
}
bool Filesystem::setSource(const char *source)
{
if (!initialized)
return false;
// Check whether directory is already set.
if (!game_source.empty())
return false;
// Add the directory.
if (!PHYSFS_addToSearchPath(source, 1))
return false;
// Save the game source.
game_source = std::string(source);
return true;
}
const char *Filesystem::getSource() const
{
return game_source.c_str();
}
bool Filesystem::setupWriteDirectory()
{
if (!initialized)
return false;
// These must all be set.
if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty())
return false;
// Set the appdata folder as writable directory.
// (We must create the save folder before mounting it).
if (!PHYSFS_setWriteDir(getDriveRoot(save_path_full).c_str()))
return false;
// Create the save folder. (We're now "at" %APPDATA%).
if (!createDirectory(skipDriveRoot(save_path_full).c_str()))
{
// Clear the write directory in case of error.
PHYSFS_setWriteDir(0);
return false;
}
// Set the final write directory.
if (!PHYSFS_setWriteDir(save_path_full.c_str()))
return false;
// Add the directory. (Will not be readded if already present).
if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0))
{
PHYSFS_setWriteDir(0); // Clear the write directory in case of error.
return false;
}
return true;
}
bool Filesystem::mount(const char *archive, const char *mountpoint, bool appendToPath)
{
if (!initialized || !archive)
return false;
std::string realPath;
std::string sourceBase = getSourceBaseDirectory();
if (isFused() && sourceBase.compare(archive) == 0)
{
// Special case: if the game is fused and the archive is the source's
// base directory, mount it even though it's outside of the save dir.
realPath = sourceBase;
}
else
{
// Not allowed for safety reasons.
if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
return false;
const char *realDir = PHYSFS_getRealDir(archive);
if (!realDir)
return false;
realPath = realDir;
// Always disallow mounting of files inside the game source, since it
// won't work anyway if the game source is a zipped .love file.
if (realPath.find(game_source) == 0)
return false;
realPath += LOVE_PATH_SEPARATOR;
realPath += archive;
}
if (realPath.length() == 0)
return false;
return PHYSFS_mount(realPath.c_str(), mountpoint, appendToPath);
}
bool Filesystem::unmount(const char *archive)
{
if (!initialized || !archive)
return false;
std::string realPath;
std::string sourceBase = getSourceBaseDirectory();
if (isFused() && sourceBase.compare(archive) == 0)
{
// Special case: if the game is fused and the archive is the source's
// base directory, unmount it even though it's outside of the save dir.
realPath = sourceBase;
}
else
{
// Not allowed for safety reasons.
if (strlen(archive) == 0 || strstr(archive, "..") || strcmp(archive, "/") == 0)
return false;
const char *realDir = PHYSFS_getRealDir(archive);
if (!realDir)
return false;
realPath = realDir;
realPath += LOVE_PATH_SEPARATOR;
realPath += archive;
}
const char *mountPoint = PHYSFS_getMountPoint(realPath.c_str());
if (!mountPoint)
return false;
return PHYSFS_removeFromSearchPath(realPath.c_str());
}
File *Filesystem::newFile(const char *filename) const
{
return new File(filename);
}
FileData *Filesystem::newFileData(void *data, unsigned int size, const char *filename) const
{
FileData *fd = new FileData(size, std::string(filename));
// Copy the data into FileData.
memcpy(fd->getData(), data, size);
return fd;
}
FileData *Filesystem::newFileData(const char *b64, const char *filename) const
{
int size = strlen(b64);
int outsize = 0;
char *dst = b64_decode(b64, size, outsize);
FileData *fd = new FileData(outsize, std::string(filename));
// Copy the data into FileData.
memcpy(fd->getData(), dst, outsize);
delete [] dst;
return fd;
}
const char *Filesystem::getWorkingDirectory()
{
if (cwd.empty())
{
#ifdef LOVE_WINDOWS
WCHAR w_cwd[LOVE_MAX_PATH];
_wgetcwd(w_cwd, LOVE_MAX_PATH);
cwd = to_utf8(w_cwd);
replace_char(cwd, '\\', '/');
#else
char *cwd_char = new char[LOVE_MAX_PATH];
if (getcwd(cwd_char, LOVE_MAX_PATH))
cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty
delete [] cwd_char;
#endif
}
return cwd.c_str();
}
const char *Filesystem::getUserDirectory()
{
return PHYSFS_getUserDir();
}
const char *Filesystem::getAppdataDirectory()
{
#ifdef LOVE_WINDOWS
if (appdata.empty())
{
wchar_t *w_appdata = _wgetenv(L"APPDATA");
appdata = to_utf8(w_appdata);
replace_char(appdata, '\\', '/');
}
return appdata.c_str();
#elif defined(LOVE_MACOSX)
if (appdata.empty())
{
std::string udir = getUserDirectory();
udir.append("/Library/Application Support");
appdata = udir;
}
return appdata.c_str();
#elif defined(LOVE_LINUX)
if (appdata.empty())
{
char *xdgdatahome = getenv("XDG_DATA_HOME");
if (!xdgdatahome)
appdata = std::string(getUserDirectory()) + "/.local/share/";
else
appdata = xdgdatahome;
}
return appdata.c_str();
#else
return getUserDirectory();
#endif
}
const char *Filesystem::getSaveDirectory()
{
return save_path_full.c_str();
}
std::string Filesystem::getSourceBaseDirectory() const
{
size_t source_len = game_source.length();
if (source_len == 0)
return "";
// FIXME: This doesn't take into account parent and current directory
// symbols (i.e. '..' and '.')
#ifdef LOVE_WINDOWS
// In windows, delimiters can be either '/' or '\'.
size_t base_end_pos = game_source.find_last_of("/\\", source_len - 2);
#else
size_t base_end_pos = game_source.find_last_of('/', source_len - 2);
#endif
if (base_end_pos == std::string::npos)
return "";
// If the source is in the unix root (aka '/'), we want to keep the '/'.
if (base_end_pos == 0)
base_end_pos = 1;
return game_source.substr(0, base_end_pos);
}
bool Filesystem::exists(const char *file) const
{
return PHYSFS_exists(file);
}
bool Filesystem::isDirectory(const char *file) const
{
return PHYSFS_isDirectory(file);
}
bool Filesystem::isFile(const char *file) const
{
return exists(file) && !isDirectory(file);
}
bool Filesystem::createDirectory(const char *dir)
{
if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory())
return false;
if (!PHYSFS_mkdir(dir))
return false;
return true;
}
bool Filesystem::remove(const char *file)
{
if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory())
return false;
if (!PHYSFS_delete(file))
return false;
return true;
}
Data *Filesystem::read(const char *filename, int64 size) const
{
File file(filename);
file.open(File::READ);
// close() is called in the File destructor.
return file.read(size);
}
void Filesystem::write(const char *filename, const void *data, int64 size) const
{
File file(filename);
file.open(File::WRITE);
// close() is called in the File destructor.
if (!file.write(data, size))
throw love::Exception("Data could not be written.");
}
void Filesystem::append(const char *filename, const void *data, int64 size) const
{
File file(filename);
file.open(File::APPEND);
// close() is called in the File destructor.
if (!file.write(data, size))
throw love::Exception("Data could not be written.");
}
int Filesystem::getDirectoryItems(lua_State *L)
{
const char *dir = luaL_checkstring(L, 1);
char **rc = PHYSFS_enumerateFiles(dir);
int index = 1;
lua_newtable(L);
for (char **i = rc; *i != 0; i++)
{
lua_pushstring(L, *i);
lua_rawseti(L, -2, index);
index++;
}
PHYSFS_freeList(rc);
return 1;
}
int Filesystem::lines_i(lua_State *L)
{
const int bufsize = 1024;
char buf[bufsize];
int linesize = 0;
bool newline = false;
File *file = luax_checktype<File>(L, lua_upvalueindex(1), "File", FILESYSTEM_FILE_T);
// Only accept read mode at this point.
if (file->getMode() != File::READ)
return luaL_error(L, "File needs to stay in read mode.");
int64 pos = file->tell();
int64 userpos = -1;
if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0)
{
// User may have changed the file position.
userpos = pos;
pos = (int64) lua_tonumber(L, lua_upvalueindex(2));
if (userpos != pos)
file->seek(pos);
}
while (!newline && !file->eof())
{
// This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize.
int read = (int) file->read(buf, bufsize);
if (read < 0)
return luaL_error(L, "Could not read from file.");
linesize += read;
for (int i = 0; i < read; i++)
{
if (buf[i] == '\n')
{
linesize -= read - i;
newline = true;
break;
}
}
}
if (newline || (file->eof() && linesize > 0))
{
if (linesize < bufsize)
{
// We have the line in the buffer on the stack. No 'new' and 'read' needed.
lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize);
if (userpos < 0)
file->seek(pos + linesize + 1);
}
else
{
char *str = 0;
try
{
str = new char[linesize + 1];
}
catch(std::bad_alloc &)
{
// Can't lua_error (longjmp) in exception handlers.
}
if (!str)
return luaL_error(L, "Out of memory.");
file->seek(pos);
// Read the \n anyway and save us a call to seek.
if (file->read(str, linesize + 1) == -1)
{
delete [] str;
return luaL_error(L, "Could not read from file.");
}
lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize);
delete [] str;
}
if (userpos >= 0)
{
// Save new position in upvalue.
lua_pushnumber(L, (lua_Number)(pos + linesize + 1));
lua_replace(L, lua_upvalueindex(2));
file->seek(userpos);
}
return 1;
}
// EOF reached.
if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3)))
file->seek(userpos);
else
file->close();
return 0;
}
int64 Filesystem::getLastModified(const char *filename) const
{
PHYSFS_sint64 time = PHYSFS_getLastModTime(filename);
if (time == -1)
throw love::Exception("Could not determine file modification date.");
return time;
}
int64 Filesystem::getSize(const char *filename) const
{
File file(filename);
int64 size = file.getSize();
return size;
}
} // physfs
} // filesystem
} // love
@@ -0,0 +1,315 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_FILESYSTEM_H
#define LOVE_FILESYSTEM_PHYSFS_FILESYSTEM_H
// STD
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
// LOVE
#include "common/Module.h"
#include "common/config.h"
#include "common/int.h"
#include "filesystem/FileData.h"
#include "File.h"
// For great CWD. (Current Working Directory)
// Using this instead of boost::filesystem which totally
// cramped our style.
#ifdef LOVE_WINDOWS
# include <windows.h>
# include <direct.h>
#else
# include <sys/param.h>
# include <unistd.h>
#endif
// In Windows, we would like to use "LOVE" as the
// application folder, but in Linux, we like .love.
#define LOVE_APPDATA_PREFIX ""
#ifdef LOVE_WINDOWS
# define LOVE_APPDATA_FOLDER "LOVE"
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH _MAX_PATH
#else
# ifdef LOVE_MACOSX
# define LOVE_APPDATA_FOLDER "LOVE"
# elif defined(LOVE_LINUX)
# define LOVE_APPDATA_FOLDER "love"
# else
# define LOVE_APPDATA_PREFIX "."
# define LOVE_APPDATA_FOLDER "love"
# endif
# define LOVE_PATH_SEPARATOR "/"
# define LOVE_MAX_PATH MAXPATHLEN
#endif
namespace love
{
namespace filesystem
{
namespace physfs
{
class Filesystem : public Module
{
public:
Filesystem();
virtual ~Filesystem();
const char *getName() const;
void init(const char *arg0);
void setFused(bool fused);
bool isFused() const;
/**
* This sets up the save directory. If the
* it is already set up, nothing happens.
* @return True on success, false otherwise.
**/
bool setupWriteDirectory();
/**
* Sets the name of the save folder.
* @param ident The name of the game. Will be used to
* to create the folder in the LOVE data folder.
**/
bool setIdentity(const char *ident, bool appendToPath = false);
const char *getIdentity() const;
/**
* Sets the path to the game source.
* This can only be set once.
* @param source Path to a directory or a .love-file.
**/
bool setSource(const char *source);
/**
* Gets the path to the game source.
* Returns a 0-length string if the source has not been set.
**/
const char *getSource() const;
bool mount(const char *archive, const char *mountpoint, bool appendToPath = false);
bool unmount(const char *archive);
/**
* Creates a new file.
**/
File *newFile(const char *filename) const;
/**
* Creates a new FileData object. Data will be copied.
* @param data Pointer to the data.
* @param size The size of the data.
* @param filename The full filename used to file type identification.
**/
FileData *newFileData(void *data, unsigned int size, const char *filename) const;
/**
* Creates a new FileData object from base64 data.
* @param b64 The base64 data.
**/
FileData *newFileData(const char *b64, const char *filename) const;
/**
* Gets the current working directory.
**/
const char *getWorkingDirectory();
/**
* Gets the user home directory.
**/
const char *getUserDirectory();
/**
* Gets the APPDATA directory. On Windows, this is the folder
* in the %APPDATA% enviroment variable. On Linux, this is the
* user home folder.
**/
const char *getAppdataDirectory();
/**
* Gets the full path of the save folder.
**/
const char *getSaveDirectory();
/**
* Gets the full path to the directory containing the game source.
* For example if the game source is C:\Games\mygame.love, this will return
* C:\Games.
**/
std::string getSourceBaseDirectory() const;
/**
* Checks whether a file exists in the current search path
* or not.
* @param file The filename to check.
**/
bool exists(const char *file) const;
/**
* Checks if an existing file really is a directory.
* @param file The filename to check.
**/
bool isDirectory(const char *file) const;
/**
* Checks if an existing file really is a file,
* and not a directory.
* @param file The filename to check.
**/
bool isFile(const char *file) const;
/**
* Creates a directory. Write dir must be set.
* @param dir The directory to create.
**/
bool createDirectory(const char *dir);
/**
* Removes a file (or directory).
* @param file The file or directory to remove.
**/
bool remove(const char *file);
/**
* Opens a file for reading or writing. (Depends
* on the mode chosen at the time of creation).
* @param file The file to open.
* @param mode The mode to open the file in.
**/
bool open(File *file, File::Mode mode);
/**
* Closes a file.
* @param file The file to close.
**/
bool close(File *file);
/**
* Reads data from a file.
* @param filename The name of the file to read from.
* @param size The size in bytes of the data to read.
**/
Data *read(const char *filename, int64 size = File::ALL) const;
/**
* Write data to a file.
* @param filename The name of the file to write to.
* @param data The data to write.
* @param size The size in bytes of the data to write.
**/
void write(const char *filename, const void *data, int64 size) const;
/**
* Append data to a file, creating it if it doesn't exist.
* @param filename The name of the file to write to.
* @param data The data to append.
* @param size The size in bytes of the data to append.
**/
void append(const char *filename, const void *data, int64 size) const;
/**
* Check if end-of-file is reached.
* @return True if EOF, false otherwise.
**/
bool eof(File *file);
/**
* Gets the current position in a file.
* @param file An open File.
**/
int tell(File *file);
/**
* Seek to a position within a file.
* @param pos The position to seek to.
**/
bool seek(File *file, uint64 pos);
/**
* This "native" method returns a table of all
* files in a given directory.
**/
int getDirectoryItems(lua_State *L);
/**
* Gets the last modification time of a file, in seconds
* since the Unix epoch.
* @param filename The name of the file.
**/
int64 getLastModified(const char *filename) const;
/**
* Gets the size of a file in bytes.
* @param filename The name of the file.
**/
int64 getSize(const char *filename) const;
/**
* Text file line-reading iterator function used and
* pushed on the Lua stack by love.filesystem.lines
* and File:lines.
**/
static int lines_i(lua_State *L);
private:
// Contains the current working directory (UTF8).
std::string cwd;
// %APPDATA% on Windows.
std::string appdata;
// This name will be used to create the folder
// in the appdata/userdata folder.
std::string save_identity;
// Full and relative paths of the game save folder.
// (Relative to the %APPDATA% folder, meaning that the
// relative string will look something like: ./LOVE/game)
std::string save_path_relative, save_path_full;
// The full path to the source of the game.
std::string game_source;
// Workaround for machines without PhysFS 2.0
bool initialized;
// Allow saving outside of the LOVE_APPDATA_FOLDER
// for release 'builds'
bool fused;
bool fusedSet;
}; // Filesystem
} // physfs
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_PHYSFS_FILESYSTEM_H
@@ -0,0 +1,328 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_File.h"
#include "common/Data.h"
#include "common/Exception.h"
#include "common/int.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
int luax_ioError(lua_State *L, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
lua_pushnil(L);
lua_pushvfstring(L, fmt, args);
va_end(args);
return 2;
}
File *luax_checkfile(lua_State *L, int idx)
{
return luax_checktype<File>(L, idx, "File", FILESYSTEM_FILE_T);
}
int w_File_getSize(lua_State *L)
{
File *t = luax_checkfile(L, 1);
int64 size = -1;
try
{
size = t->getSize();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
// Push nil on failure or if size does not fit into a double precision floating-point number.
if (size == -1)
return luax_ioError(L, "Could not determine file size.");
else if (size >= 0x20000000000000LL)
return luax_ioError(L, "Size is too large.");
lua_pushnumber(L, (lua_Number) size);
return 1;
}
int w_File_open(lua_State *L)
{
File *file = luax_checkfile(L, 1);
const char *str = luaL_checkstring(L, 2);
File::Mode mode;
if (!File::getConstant(str, mode))
return luaL_error(L, "Incorrect file open mode: %s", str);
try
{
luax_pushboolean(L, file->open(mode));
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
return 1;
}
int w_File_close(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->close());
return 1;
}
int w_File_isOpen(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->isOpen());
return 1;
}
int w_File_read(lua_State *L)
{
File *file = luax_checkfile(L, 1);
Data *d = 0;
int64 size = (int64)luaL_optnumber(L, 2, File::ALL);
try
{
d = file->read(size);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
lua_pushlstring(L, (const char *) d->getData(), d->getSize());
lua_pushnumber(L, d->getSize());
d->release();
return 2;
}
int w_File_write(lua_State *L)
{
File *file = luax_checkfile(L, 1);
bool result = false;
if (lua_isstring(L, 2))
{
try
{
size_t datasize = 0;
const char *data = lua_tolstring(L, 2, &datasize);
if (!lua_isnoneornil(L, 3))
datasize = luaL_checkinteger(L, 3);
result = file->write(data, datasize);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
}
else if (luax_istype(L, 2, DATA_T))
{
try
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
result = file->write(data, luaL_optinteger(L, 3, data->getSize()));
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
}
else
{
return luaL_argerror(L, 2, "string or data expected");
}
luax_pushboolean(L, result);
return 1;
}
int w_File_flush(lua_State *L)
{
File *file = luax_checkfile(L, 1);
bool success = false;
try
{
success = file->flush();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, success);
return 1;
}
int w_File_eof(lua_State *L)
{
File *file = luax_checkfile(L, 1);
luax_pushboolean(L, file->eof());
return 1;
}
int w_File_tell(lua_State *L)
{
File *file = luax_checkfile(L, 1);
int64 pos = file->tell();
// Push nil on failure or if pos does not fit into a double precision floating-point number.
if (pos == -1)
return luax_ioError(L, "Invalid position.");
else if (pos >= 0x20000000000000LL)
return luax_ioError(L, "Number is too large.");
else
lua_pushnumber(L, (lua_Number)pos);
return 1;
}
int w_File_seek(lua_State *L)
{
File *file = luax_checkfile(L, 1);
lua_Number pos = luaL_checknumber(L, 2);
// Push false on negative and precision-problematic numbers.
// Better fail than seek to an unknown position.
if (pos < 0.0 || pos >= 9007199254740992.0)
luax_pushboolean(L, false);
else
luax_pushboolean(L, file->seek((uint64)pos));
return 1;
}
int w_File_lines(lua_State *L)
{
File *file = luax_checkfile(L, 1);
lua_pushnumber(L, 0); // File position.
luax_pushboolean(L, file->getMode() != File::CLOSED); // Save current file mode.
if (file->getMode() != File::READ)
{
if (file->getMode() != File::CLOSED)
file->close();
bool success = false;
EXCEPT_GUARD(success = file->open(File::READ);)
if (!success)
return luaL_error(L, "Could not open file.");
}
lua_pushcclosure(L, Filesystem::lines_i, 3);
return 1;
}
int w_File_setBuffer(lua_State *L)
{
File *file = luax_checkfile(L, 1);
const char *str = luaL_checkstring(L, 2);
int64 size = (int64) luaL_optnumber(L, 3, 0.0);
File::BufferMode bufmode;
if (!File::getConstant(str, bufmode))
return luaL_error(L, "Incorrect file buffer mode: %s", str);
bool success = false;
try
{
success = file->setBuffer(bufmode, size);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, success);
return 1;
}
int w_File_getBuffer(lua_State *L)
{
File *file = luax_checkfile(L, 1);
int64 size = 0;
File::BufferMode bufmode = file->getBuffer(size);
const char *str = 0;
if (!File::getConstant(bufmode, str))
return luax_ioError(L, "Unknown file buffer mode.");
lua_pushstring(L, str);
lua_pushnumber(L, (lua_Number) size);
return 2;
}
int w_File_getMode(lua_State *L)
{
File *file = luax_checkfile(L, 1);
File::Mode mode = file->getMode();
const char *str = 0;
if (!File::getConstant(mode, str))
return luax_ioError(L, "Unknown file mode.");
lua_pushstring(L, str);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getSize", w_File_getSize },
{ "open", w_File_open },
{ "close", w_File_close },
{ "isOpen", w_File_isOpen },
{ "read", w_File_read },
{ "write", w_File_write },
{ "flush", w_File_flush },
{ "eof", w_File_eof },
{ "tell", w_File_tell },
{ "seek", w_File_seek },
{ "lines", w_File_lines },
{ "setBuffer", w_File_setBuffer },
{ "getBuffer", w_File_getBuffer },
{ "getMode", w_File_getMode },
{ 0, 0 }
};
extern "C" int luaopen_file(lua_State *L)
{
return luax_register_type(L, "File", functions);
}
} // physfs
} // filesystem
} // love
@@ -0,0 +1,60 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_H
#define LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_H
// LOVE
#include "common/runtime.h"
#include "Filesystem.h"
#include "File.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
// Does not use lua_error, so it's safe to call in exception handling code.
int luax_ioError(lua_State *L, const char *fmt, ...);
File *luax_checkfile(lua_State *L, int idx);
int w_File_getSize(lua_State *L);
int w_File_open(lua_State *L);
int w_File_close(lua_State *L);
int w_File_isOpen(lua_State *L);
int w_File_read(lua_State *L);
int w_File_write(lua_State *L);
int w_File_flush(lua_State *L);
int w_File_eof(lua_State *L);
int w_File_tell(lua_State *L);
int w_File_seek(lua_State *L);
int w_File_lines(lua_State *L);
int w_File_setBuffer(lua_State *L);
int w_File_getBuffer(lua_State *L);
int w_File_getMode(lua_State *L);
extern "C" int luaopen_file(lua_State *L);
} // physfs
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_H
@@ -0,0 +1,71 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_FileData.h"
#include "common/wrap_Data.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
FileData *luax_checkfiledata(lua_State *L, int idx)
{
return luax_checktype<FileData>(L, idx, "FileData", FILESYSTEM_FILE_DATA_T);
}
int w_FileData_getFilename(lua_State *L)
{
FileData *t = luax_checkfiledata(L, 1);
lua_pushstring(L, t->getFilename().c_str());
return 1;
}
int w_FileData_getExtension(lua_State *L)
{
FileData *t = luax_checkfiledata(L, 1);
lua_pushstring(L, t->getExtension().c_str());
return 1;
}
static const luaL_Reg w_FileData_functions[] =
{
// Data
{ "getString", w_Data_getString },
{ "getPointer", w_Data_getPointer },
{ "getSize", w_Data_getSize },
{ "getFilename", w_FileData_getFilename },
{ "getExtension", w_FileData_getExtension },
{ 0, 0 }
};
extern "C" int luaopen_filedata(lua_State *L)
{
return luax_register_type(L, "FileData", w_FileData_functions);
}
} // physfs
} // filesystem
} // love
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_DATA_H
#define LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_DATA_H
// LOVE
#include "common/runtime.h"
#include "filesystem/FileData.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
FileData *luax_checkfiledata(lua_State *L, int idx);
int w_FileData_getFilename(lua_State *L);
int w_FileData_getExtension(lua_State *L);
extern "C" int luaopen_filedata(lua_State *L);
} // physfs
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_PHYSFS_WRAP_FILE_DATA_H
@@ -0,0 +1,617 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Filesystem.h"
// SDL
#include <SDL_loadso.h>
namespace love
{
namespace filesystem
{
namespace physfs
{
static Filesystem *instance = 0;
bool hack_setupWriteDirectory()
{
if (instance != 0)
return instance->setupWriteDirectory();
return false;
}
int w_init(lua_State *L)
{
const char *arg0 = luaL_checkstring(L, 1);
EXCEPT_GUARD(instance->init(arg0);)
return 0;
}
int w_setFused(lua_State *L)
{
// no error checking needed, everything, even nothing
// can be converted to a boolean
instance->setFused(luax_toboolean(L, 1));
return 0;
}
int w_isFused(lua_State *L)
{
luax_pushboolean(L, instance->isFused());
return 1;
}
int w_setIdentity(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
bool append = luax_optboolean(L, 2, false);
if (!instance->setIdentity(arg, append))
return luaL_error(L, "Could not set write directory.");
return 0;
}
int w_getIdentity(lua_State *L)
{
lua_pushstring(L, instance->getIdentity());
return 1;
}
int w_setSource(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
if (!instance->setSource(arg))
return luaL_error(L, "Could not set source.");
return 0;
}
int w_getSource(lua_State *L)
{
lua_pushstring(L, instance->getSource());
return 1;
}
int w_mount(lua_State *L)
{
const char *archive = luaL_checkstring(L, 1);
const char *mountpoint = luaL_checkstring(L, 2);
bool append = luax_optboolean(L, 3, false);
luax_pushboolean(L, instance->mount(archive, mountpoint, append));
return 1;
}
int w_unmount(lua_State *L)
{
const char *archive = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->unmount(archive));
return 1;
}
int w_newFile(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
const char *str = 0;
File::Mode mode = File::CLOSED;
if (lua_isstring(L, 2))
{
str = luaL_checkstring(L, 2);
if (!File::getConstant(str, mode))
return luaL_error(L, "Incorrect file open mode: %s", str);
}
File *t = instance->newFile(filename);
if (mode != File::CLOSED)
{
try
{
if (!t->open(mode))
throw love::Exception("Could not open file.");
}
catch (love::Exception &e)
{
t->release();
return luax_ioError(L, "%s", e.what());
}
}
luax_pushtype(L, "File", FILESYSTEM_FILE_T, t);
return 1;
}
int w_newFileData(lua_State *L)
{
// Single argument: treat as filepath or File.
if (lua_gettop(L) == 1)
{
if (lua_isstring(L, 1))
luax_convobj(L, 1, "filesystem", "newFile");
// Get FileData from the File.
if (luax_istype(L, 1, FILESYSTEM_FILE_T))
{
File *file = luax_checktype<File>(L, 1, "File", FILESYSTEM_FILE_T);
FileData *data = 0;
try
{
data = file->read();
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, data);
return 1;
}
else
return luaL_argerror(L, 1, "string or File expected");
}
size_t length = 0;
const char *str = luaL_checklstring(L, 1, &length);
const char *filename = luaL_checkstring(L, 2);
const char *decstr = lua_isstring(L, 3) ? lua_tostring(L, 3) : 0;
FileData::Decoder decoder = FileData::FILE;
if (decstr && !FileData::getConstant(decstr, decoder))
return luaL_error(L, "Invalid FileData decoder: %s", decstr);
FileData *t = 0;
switch (decoder)
{
case FileData::FILE:
t = instance->newFileData((void *)str, (int)length, filename);
break;
case FileData::BASE64:
t = instance->newFileData(str, filename);
break;
default:
return luaL_error(L, "Invalid FileData decoder: %s", decstr);
}
luax_pushtype(L, "FileData", FILESYSTEM_FILE_DATA_T, t);
return 1;
}
int w_getWorkingDirectory(lua_State *L)
{
lua_pushstring(L, instance->getWorkingDirectory());
return 1;
}
int w_getUserDirectory(lua_State *L)
{
lua_pushstring(L, instance->getUserDirectory());
return 1;
}
int w_getAppdataDirectory(lua_State *L)
{
lua_pushstring(L, instance->getAppdataDirectory());
return 1;
}
int w_getSaveDirectory(lua_State *L)
{
lua_pushstring(L, instance->getSaveDirectory());
return 1;
}
int w_getSourceBaseDirectory(lua_State *L)
{
luax_pushstring(L, instance->getSourceBaseDirectory());
return 1;
}
int w_exists(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->exists(arg));
return 1;
}
int w_isDirectory(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->isDirectory(arg));
return 1;
}
int w_isFile(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->isFile(arg));
return 1;
}
int w_createDirectory(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->createDirectory(arg));
return 1;
}
int w_remove(lua_State *L)
{
const char *arg = luaL_checkstring(L, 1);
luax_pushboolean(L, instance->remove(arg));
return 1;
}
int w_read(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 len = (int64) luaL_optinteger(L, 2, File::ALL);
Data *data = 0;
try
{
data = instance->read(filename, len);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
if (data == 0)
return luax_ioError(L, "File could not be read.");
// Push the string.
lua_pushlstring(L, (const char *) data->getData(), data->getSize());
// Push the size.
lua_pushinteger(L, data->getSize());
// Lua has a copy now, so we can free it.
data->release();
return 2;
}
static int w_write_or_append(lua_State *L, File::Mode mode)
{
const char *filename = luaL_checkstring(L, 1);
const char *input = 0;
size_t len = 0;
if (luax_istype(L, 2, DATA_T))
{
love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T);
input = (const char *) data->getData();
len = data->getSize();
}
else if (lua_isstring(L, 2))
input = lua_tolstring(L, 2, &len);
else
return luaL_argerror(L, 2, "string or Data expected");
// Get how much we should write. Length of string default.
len = luaL_optinteger(L, 3, len);
try
{
if (mode == File::APPEND)
instance->append(filename, (const void *) input, len);
else
instance->write(filename, (const void *) input, len);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
luax_pushboolean(L, true);
return 1;
}
int w_write(lua_State *L)
{
return w_write_or_append(L, File::WRITE);
}
int w_append(lua_State *L)
{
return w_write_or_append(L, File::APPEND);
}
int w_getDirectoryItems(lua_State *L)
{
return instance->getDirectoryItems(L);
}
int w_lines(lua_State *L)
{
File *file;
if (lua_isstring(L, 1))
{
file = instance->newFile(lua_tostring(L, 1));
bool success = false;
EXCEPT_GUARD(success = file->open(File::READ);)
if (!success)
return luaL_error(L, "Could not open file.");
luax_pushtype(L, "File", FILESYSTEM_FILE_T, file);
}
else
return luaL_argerror(L, 1, "expected filename.");
lua_pushcclosure(L, Filesystem::lines_i, 1);
return 1;
}
int w_load(lua_State *L)
{
std::string filename = std::string(luaL_checkstring(L, 1));
Data *data = 0;
try
{
data = instance->read(filename.c_str());
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
int status = luaL_loadbuffer(L, (const char *)data->getData(), data->getSize(), ("@" + filename).c_str());
data->release();
// Load the chunk, but don't run it.
switch (status)
{
case LUA_ERRMEM:
return luaL_error(L, "Memory allocation error: %s\n", lua_tostring(L, -1));
case LUA_ERRSYNTAX:
return luaL_error(L, "Syntax error: %s\n", lua_tostring(L, -1));
default: // success
return 1;
}
}
int w_getLastModified(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 time = 0;
try
{
time = instance->getLastModified(filename);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
lua_pushnumber(L, static_cast<lua_Number>(time));
return 1;
}
int w_getSize(lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
int64 size = -1;
try
{
size = instance->getSize(filename);
}
catch (love::Exception &e)
{
return luax_ioError(L, "%s", e.what());
}
// Error on failure or if size does not fit into a double precision floating-point number.
if (size == -1)
return luax_ioError(L, "Could not determine file size.");
else if (size >= 0x20000000000000LL)
return luax_ioError(L, "Size too large to fit into a Lua number!");
lua_pushnumber(L, (lua_Number) size);
return 1;
}
int loader(lua_State *L)
{
const char *filename = lua_tostring(L, -1);
std::string tmp(filename);
tmp += ".lua";
int size = tmp.size();
for (int i=0; i<size-4; i++)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
}
// Check whether file exists.
if (instance->exists(tmp.c_str()))
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
return w_load(L);
}
tmp = filename;
size = tmp.size();
for (int i=0; i<size; i++)
{
if (tmp[i] == '.')
{
tmp[i] = '/';
}
}
if (instance->isDirectory(tmp.c_str()))
{
tmp += "/init.lua";
if (instance->exists(tmp.c_str()))
{
lua_pop(L, 1);
lua_pushstring(L, tmp.c_str());
// Ok, load it.
return w_load(L);
}
}
lua_pushfstring(L, "\n\tno file \"%s\" in LOVE game directories.\n", (tmp + ".lua").c_str());
return 1;
}
inline const char *library_extension()
{
#ifdef LOVE_WINDOWS
return ".dll";
#else
return ".so";
#endif
}
int extloader(lua_State *L)
{
const char *filename = lua_tostring(L, -1);
std::string tokenized_name(filename);
std::string tokenized_function(filename);
for (unsigned int i = 0; i < tokenized_name.size(); i++)
{
if (tokenized_name[i] == '.')
{
tokenized_name[i] = '/';
tokenized_function[i] = '_';
}
}
tokenized_name += library_extension();
void *handle = SDL_LoadObject((std::string(instance->getAppdataDirectory()) + LOVE_PATH_SEPARATOR LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR + tokenized_name).c_str());
if (!handle && instance->isFused())
handle = SDL_LoadObject((std::string(instance->getSaveDirectory()) + LOVE_PATH_SEPARATOR + tokenized_name).c_str());
if (!handle)
{
lua_pushfstring(L, "\n\tno extension \"%s\" in LOVE paths.\n", filename);
return 1;
}
void *func = SDL_LoadFunction(handle, ("loveopen_" + tokenized_function).c_str());
if (!func)
func = SDL_LoadFunction(handle, ("luaopen_" + tokenized_function).c_str());
if (!func)
{
SDL_UnloadObject(handle);
lua_pushfstring(L, "\n\textension \"%s\" is incompatible.\n", filename);
return 1;
}
lua_pushcfunction(L, (lua_CFunction) func);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "init", w_init },
{ "setFused", w_setFused },
{ "isFused", w_isFused },
{ "setIdentity", w_setIdentity },
{ "getIdentity", w_getIdentity },
{ "setSource", w_setSource },
{ "getSource", w_getSource },
{ "mount", w_mount },
{ "unmount", w_unmount },
{ "newFile", w_newFile },
{ "getWorkingDirectory", w_getWorkingDirectory },
{ "getUserDirectory", w_getUserDirectory },
{ "getAppdataDirectory", w_getAppdataDirectory },
{ "getSaveDirectory", w_getSaveDirectory },
{ "getSourceBaseDirectory", w_getSourceBaseDirectory },
{ "exists", w_exists },
{ "isDirectory", w_isDirectory },
{ "isFile", w_isFile },
{ "createDirectory", w_createDirectory },
{ "remove", w_remove },
{ "read", w_read },
{ "write", w_write },
{ "append", w_append },
{ "getDirectoryItems", w_getDirectoryItems },
{ "lines", w_lines },
{ "load", w_load },
{ "getLastModified", w_getLastModified },
{ "getSize", w_getSize },
{ "newFileData", w_newFileData },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_file,
luaopen_filedata,
0
};
extern "C" int luaopen_love_filesystem(lua_State *L)
{
if (instance == 0)
{
EXCEPT_GUARD(instance = new Filesystem();)
}
else
instance->retain();
love::luax_register_searcher(L, loader, 1);
love::luax_register_searcher(L, extloader, 2);
WrappedModule w;
w.module = instance;
w.name = "filesystem";
w.flags = MODULE_FILESYSTEM_T;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // physfs
} // filesystem
} // love
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FILESYSTEM_PHYSFS_WRAP_FILESYSTEM_H
#define LOVE_FILESYSTEM_PHYSFS_WRAP_FILESYSTEM_H
// LOVE
#include "Filesystem.h"
#include "wrap_File.h"
#include "wrap_FileData.h"
namespace love
{
namespace filesystem
{
namespace physfs
{
bool hack_setupWriteDirectory();
int w_init(lua_State *L);
int w_setFused(lua_State *L);
int w_isFused(lua_State *L);
int w_setIdentity(lua_State *L);
int w_getIdentity(lua_State *L);
int w_setSource(lua_State *L);
int w_getSource(lua_State *L);
int w_mount(lua_State *L);
int w_unmount(lua_State *L);
int w_newFile(lua_State *L);
int w_newFileData(lua_State *L);
int w_getWorkingDirectory(lua_State *L);
int w_getUserDirectory(lua_State *L);
int w_getAppdataDirectory(lua_State *L);
int w_getSaveDirectory(lua_State *L);
int w_getSourceBaseDirectory(lua_State *L);
int w_exists(lua_State *L);
int w_isDirectory(lua_State *L);
int w_isFile(lua_State *L);
int w_createDirectory(lua_State *L);
int w_remove(lua_State *L);
int w_open(lua_State *L);
int w_close(lua_State *L);
int w_read(lua_State *L);
int w_write(lua_State *L);
int w_append(lua_State *L);
int w_getDirectoryItems(lua_State *L);
int w_lines(lua_State *L);
int w_load(lua_State *L);
int w_getLastModified(lua_State *L);
int w_getSize(lua_State *L);
int loader(lua_State *L);
int extloader(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_filesystem(lua_State *L);
} // physfs
} // filesystem
} // love
#endif // LOVE_FILESYSTEM_PHYSFS_WRAP_FILESYSTEM_H
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_FONT_H
#define LOVE_FONT_FONT_H
// LOVE
#include "Rasterizer.h"
#include "image/ImageData.h"
#include "common/Module.h"
#include "common/int.h"
// STD
#include <string>
namespace love
{
namespace font
{
class Font : public Module
{
public:
virtual Rasterizer *newRasterizer(Data *data, int size) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &glyphs) = 0;
virtual Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int length) = 0;
virtual GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph) = 0;
virtual GlyphData *newGlyphData(Rasterizer *r, uint32 glyph) = 0;
// Implement Module
virtual const char *getName() const = 0;
}; // Font
} // font
} // love
#endif // LOVE_FONT_FONT_H
+178
View File
@@ -0,0 +1,178 @@
/**
* Copyright (c) 2006-2013 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 "GlyphData.h"
// UTF-8
#include "libraries/utf8/utf8.h"
// stdlib
#include <iostream>
#include <cstddef>
namespace love
{
namespace font
{
GlyphData::GlyphData(uint32 glyph, GlyphMetrics glyphMetrics, GlyphData::Format f)
: glyph(glyph)
, metrics(glyphMetrics)
, data(0)
, format(f)
{
if (metrics.width > 0 && metrics.height > 0)
{
switch (f)
{
case GlyphData::FORMAT_LUMINANCE_ALPHA:
data = new unsigned char[metrics.width * metrics.height * 2];
break;
case GlyphData::FORMAT_RGBA:
default:
data = new unsigned char[metrics.width * metrics.height * 4];
break;
}
}
}
GlyphData::~GlyphData()
{
delete[] data;
}
void *GlyphData::getData() const
{
return (void *) data;
}
int GlyphData::getSize() const
{
switch (format)
{
case GlyphData::FORMAT_LUMINANCE_ALPHA:
return getWidth() * getHeight() * 2;
break;
case GlyphData::FORMAT_RGBA:
default:
return getWidth() * getHeight() * 4;
break;
}
}
int GlyphData::getHeight() const
{
return metrics.height;
}
int GlyphData::getWidth() const
{
return metrics.width;
}
uint32 GlyphData::getGlyph() const
{
return glyph;
}
std::string GlyphData::getGlyphString() const
{
char u[5] = {0, 0, 0, 0, 0};
ptrdiff_t length = 0;
try
{
char *end = utf8::append(glyph, u);
length = end - u;
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
// Just in case...
if (length < 0)
return "";
return std::string(u, length);
}
int GlyphData::getAdvance() const
{
return metrics.advance;
}
int GlyphData::getBearingX() const
{
return metrics.bearingX;
}
int GlyphData::getBearingY() const
{
return metrics.bearingY;
}
int GlyphData::getMinX() const
{
return this->getBearingX();
}
int GlyphData::getMinY() const
{
return this->getHeight() - this->getBearingY();
}
int GlyphData::getMaxX() const
{
return this->getBearingX() + this->getWidth();
}
int GlyphData::getMaxY() const
{
return this->getBearingY();
}
GlyphData::Format GlyphData::getFormat() const
{
return format;
}
bool GlyphData::getConstant(const char *in, GlyphData::Format &out)
{
return formats.find(in, out);
}
bool GlyphData::getConstant(GlyphData::Format in, const char *&out)
{
return formats.find(in, out);
}
StringMap<GlyphData::Format, GlyphData::FORMAT_MAX_ENUM>::Entry GlyphData::formatEntries[] =
{
{"luminance alpha", GlyphData::FORMAT_LUMINANCE_ALPHA},
{"rgba", GlyphData::FORMAT_RGBA},
};
StringMap<GlyphData::Format, GlyphData::FORMAT_MAX_ENUM> GlyphData::formats(GlyphData::formatEntries, sizeof(GlyphData::formatEntries));
} // font
} // love
+158
View File
@@ -0,0 +1,158 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_GLYPH_DATA_H
#define LOVE_FONT_GLYPH_DATA_H
// LOVE
#include "common/config.h"
#include "common/Data.h"
#include "common/Exception.h"
#include "common/StringMap.h"
#include "common/int.h"
// stdlib
#include <string>
namespace love
{
namespace font
{
/**
* Holds the specific glyph data.
**/
struct GlyphMetrics
{
int height;
int width;
int advance;
int bearingX;
int bearingY;
int spacing;
};
/**
* Holds data for a specic glyph object.
**/
class GlyphData : public Data
{
public:
enum Format
{
FORMAT_LUMINANCE_ALPHA,
FORMAT_RGBA,
FORMAT_MAX_ENUM
};
GlyphData(uint32 glyph, GlyphMetrics glyphMetrics, Format f);
virtual ~GlyphData();
// Implements Data.
void *getData() const;
int getSize() const;
/**
* Gets the height of the glyph.
**/
virtual int getHeight() const;
/**
* Gets the width of the glyph.
**/
virtual int getWidth() const;
/**
* Gets the glyph codepoint itself.
**/
uint32 getGlyph() const;
/**
* Gets the glyph as a UTF-8 string (instead of a UTF-8 code point.)
**/
std::string getGlyphString() const;
/**
* Gets the advance (the space the glyph takes up) of the glyph.
**/
int getAdvance() const;
/**
* Gets bearing (the spacing from origin) along the x-axis of the glyph.
**/
int getBearingX() const;
/**
* Gets bearing (the spacing from origin) along the y-axis of the glyph.
**/
int getBearingY() const;
/**
* Gets the min x value of the glyph.
**/
int getMinX() const;
/**
* Gets the min y value of the glyph.
**/
int getMinY() const;
/**
* Gets the max x value of the glyph.
**/
int getMaxX() const;
/**
* Gets the max y value of the glyph.
**/
int getMaxY() const;
/**
* Gets the format of the glyph data.
**/
Format getFormat() const;
static bool getConstant(const char *in, Format &out);
static bool getConstant(Format in, const char *&out);
private:
// The glyph codepoint itself.
uint32 glyph;
// Glyph metrics.
GlyphMetrics metrics;
// Glyph texture data.
unsigned char *data;
// The format the data's in.
Format format;
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
static StringMap<Format, FORMAT_MAX_ENUM> formats;
}; // GlyphData
} // font
} // love
#endif // LOVE_FONT_GLYPH_DATA_H
@@ -0,0 +1,166 @@
/**
* Copyright (c) 2006-2013 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 "ImageRasterizer.h"
#include "common/Exception.h"
#include <string.h>
namespace love
{
namespace font
{
inline bool equal(const love::image::pixel &a, const love::image::pixel &b)
{
return (a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a);
}
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs)
: imageData(data)
, glyphs(glyphs)
, numglyphs(numglyphs)
{
imageData->retain();
load();
}
ImageRasterizer::~ImageRasterizer()
{
imageData->release();
}
int ImageRasterizer::getLineHeight() const
{
return getHeight();
}
GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
{
GlyphMetrics gm;
memset(&gm, 0, sizeof(GlyphMetrics));
// Set relevant glyph metrics if the glyph is in this ImageFont
std::map<uint32, ImageGlyphData>::const_iterator it = imageGlyphs.find(glyph);
if (it != imageGlyphs.end())
{
gm.width = it->second.width;
gm.advance = it->second.width + it->second.spacing;
}
gm.height = metrics.height;
GlyphData *g = new GlyphData(glyph, gm, GlyphData::FORMAT_RGBA);
if (gm.width == 0)
return g;
// We don't want another thread modifying our ImageData mid-copy.
love::thread::Lock lock(imageData->getMutex());
love::image::pixel *gdpixels = (love::image::pixel *) g->getData();
love::image::pixel *imagepixels = (love::image::pixel *) imageData->getData();
// copy glyph pixels from imagedata to glyphdata
for (int i = 0; i < g->getWidth() * g->getHeight(); i++)
{
love::image::pixel p = imagepixels[it->second.x + (i % gm.width) + (imageData->getWidth() * (i / gm.width))];
// Use transparency instead of the spacer color
if (equal(p, spacer))
gdpixels[i].r = gdpixels[i].g = gdpixels[i].b = gdpixels[i].a = 0;
else
gdpixels[i] = p;
}
return g;
}
void ImageRasterizer::load()
{
love::image::pixel *pixels = (love::image::pixel *) imageData->getData();
int imgw = imageData->getWidth();
int imgh = imageData->getHeight();
// We don't want another thread modifying our ImageData mid-parse.
love::thread::Lock lock(imageData->getMutex());
// Set the only metric that matters
metrics.height = imgh;
// Reading texture data begins
spacer = pixels[0];
int start = 0;
int end = 0;
for (int i = 0; i < numglyphs; ++i)
{
start = end;
// Finds out where the first character starts
while (start < imgw && equal(pixels[start], spacer))
++start;
// set previous glyph's spacing
if (i > 0 && imageGlyphs.size() > 0)
imageGlyphs[glyphs[i - 1]].spacing = (start > end) ? (start - end) : 0;
end = start;
// Find where glyph ends.
while (end < imgw && !equal(pixels[end], spacer))
++end;
if (start >= end)
break;
ImageGlyphData imageGlyph;
imageGlyph.x = start;
imageGlyph.width = end - start;
imageGlyphs[glyphs[i]] = imageGlyph;
}
// Find spacing of last glyph
if (numglyphs > 0)
{
start = end;
while (start < imgw && equal(pixels[start], spacer))
++start;
imageGlyphs[glyphs[numglyphs - 1]].spacing = (start > end) ? (start - end) : 0;
}
}
int ImageRasterizer::getGlyphCount() const
{
return numglyphs;
}
bool ImageRasterizer::hasGlyph(uint32 glyph) const
{
return imageGlyphs.find(glyph) != imageGlyphs.end();
}
} // font
} // love
@@ -0,0 +1,82 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_IMAGE_RASTERIZER_H
#define LOVE_FONT_IMAGE_RASTERIZER_H
// LOVE
#include "filesystem/File.h"
#include "font/Rasterizer.h"
#include "image/ImageData.h"
#include <map>
namespace love
{
namespace font
{
/**
* Holds data for a font object.
**/
class ImageRasterizer : public Rasterizer
{
public:
ImageRasterizer(love::image::ImageData *imageData, uint32 *glyphs, int numglyphs);
virtual ~ImageRasterizer();
// Implement Rasterizer
virtual int getLineHeight() const;
virtual GlyphData *getGlyphData(uint32 glyph) const;
virtual int getGlyphCount() const;
virtual bool hasGlyph(uint32 glyph) const;
private:
// Load all the glyph positions into memory
void load();
// The image data
love::image::ImageData *imageData;
// The glyphs in the font
uint32 *glyphs;
// Number of glyphs in the font
int numglyphs;
// Information about a glyph in the ImageData
struct ImageGlyphData
{
int x;
int width;
int spacing;
};
std::map<uint32, ImageGlyphData> imageGlyphs;
// Color used to identify glyph separation in the source ImageData
love::image::pixel spacer;
}; // ImageRasterizer
} // font
} // love
#endif // LOVE_FONT_IMAGE_RASTERIZER_H
+99
View File
@@ -0,0 +1,99 @@
/**
* Copyright (c) 2006-2013 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 "Rasterizer.h"
// UTF-8
#include "libraries/utf8/utf8.h"
namespace love
{
namespace font
{
Rasterizer::~Rasterizer()
{
}
int Rasterizer::getHeight() const
{
return metrics.height;
}
int Rasterizer::getAdvance() const
{
return metrics.advance;
}
int Rasterizer::getAscent() const
{
return metrics.ascent;
}
int Rasterizer::getDescent() const
{
return metrics.descent;
}
GlyphData *Rasterizer::getGlyphData(const std::string &text) const
{
uint32 codepoint = 0;
try
{
codepoint = utf8::peek_next(text.begin(), text.end());
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
return getGlyphData(codepoint);
}
bool Rasterizer::hasGlyphs(const std::string &text) const
{
if (text.size() == 0)
return false;
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
{
uint32 codepoint = *i++;
if (!hasGlyph(codepoint))
return false;
}
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
return true;
}
} // font
} // love
+117
View File
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_RASTERIZER_H
#define LOVE_FONT_RASTERIZER_H
// LOVE
#include "common/Object.h"
#include "common/int.h"
#include "GlyphData.h"
namespace love
{
namespace font
{
/**
* Holds the specific font metrics.
**/
struct FontMetrics
{
int advance;
int ascent;
int descent;
int height;
};
/**
* Holds data for a font object.
**/
class Rasterizer : public Object
{
public:
virtual ~Rasterizer();
/**
* Gets the max height of the glyphs.
**/
virtual int getHeight() const;
/**
* Gets the max advance of the glyphs.
**/
virtual int getAdvance() const;
/**
* Gets the max ascent (height above baseline) for the font.
**/
virtual int getAscent() const;
/**
* Gets the max descent (height below baseline) for the font.
**/
virtual int getDescent() const;
/**
* Gets the line height of the font.
**/
virtual int getLineHeight() const = 0;
/**
* Gets a specific glyph.
* @param glyph The (UNICODE) glyph codepoint to get data for.
**/
virtual GlyphData *getGlyphData(uint32 glyph) const = 0;
/**
* Gets a specific glyph.
* @param text The (UNICODE) glyph character to get the data for.
**/
virtual GlyphData *getGlyphData(const std::string &text) const;
/**
* Gets the number of glyphs the rasterizer has data for.
**/
virtual int getGlyphCount() const = 0;
/**
* Gets whether this Rasterizer has a specific glyph.
* @param glyph The (UNICODE) glyph codepoint.
**/
virtual bool hasGlyph(uint32 glyph) const = 0;
/**
* Gets whether this Rasterizer has all the glyphs in a string.
* @param text The (UTF-8) string.
**/
virtual bool hasGlyphs(const std::string &text) const;
protected:
FontMetrics metrics;
}; // Rasterizer
} // font
} // love
#endif // LOVE_FONT_RASTERIZER_H
+111
View File
@@ -0,0 +1,111 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Font.h"
#include "TrueTypeRasterizer.h"
#include "font/ImageRasterizer.h"
#include "libraries/utf8/utf8.h"
namespace love
{
namespace font
{
namespace freetype
{
Font::Font()
{
if (FT_Init_FreeType(&library))
throw love::Exception("TrueTypeFont Loading error: FT_Init_FreeType failed\n");
}
Font::~Font()
{
FT_Done_FreeType(library);
}
Rasterizer *Font::newRasterizer(Data *data, int size)
{
return new TrueTypeRasterizer(library, data, size);
}
Rasterizer *Font::newRasterizer(love::image::ImageData *data, const std::string &text)
{
size_t strlen = text.size();
size_t numglyphs = 0;
uint32 *glyphs = new uint32[strlen];
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
glyphs[numglyphs++] = *i++;
}
catch (utf8::exception &e)
{
delete [] glyphs;
throw love::Exception("Decoding error: %s", e.what());
}
Rasterizer *r = newRasterizer(data, glyphs, numglyphs);
delete [] glyphs;
return r;
}
Rasterizer *Font::newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs)
{
return new ImageRasterizer(data, glyphs, numglyphs);
}
GlyphData *Font::newGlyphData(Rasterizer *r, const std::string &text)
{
uint32 codepoint = 0;
try
{
codepoint = utf8::peek_next(text.begin(), text.end());
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
return r->getGlyphData(codepoint);
}
GlyphData *Font::newGlyphData(Rasterizer *r, uint32 glyph)
{
return r->getGlyphData(glyph);
}
const char *Font::getName() const
{
return "love.font.freetype";
}
} // freetype
} // font
} // love
+76
View File
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_FREETYPE_FONT_H
#define LOVE_FONT_FREETYPE_FONT_H
// LOVE
#include "font/Font.h"
// FreeType2
#ifdef LOVE_MACOSX
#include <freetype/ft2build.h>
#else
#include <ft2build.h>
#endif
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
namespace love
{
namespace font
{
namespace freetype
{
class Font : public love::font::Font
{
public:
Font();
/**
* Destructor.
**/
virtual ~Font();
// Implements Font
Rasterizer *newRasterizer(Data *data, int size);
Rasterizer *newRasterizer(love::image::ImageData *data, const std::string &text);
Rasterizer *newRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs);
GlyphData *newGlyphData(Rasterizer *r, const std::string &glyph);
GlyphData *newGlyphData(Rasterizer *r, uint32 glyph);
// Implement Module
const char *getName() const;
private:
// FreeType library
FT_Library library;
}; // Font
} // freetype
} // font
} // love
#endif // LOVE_FONT_FREETYPE_FONT_H
@@ -0,0 +1,121 @@
/**
* Copyright (c) 2006-2013 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 "TrueTypeRasterizer.h"
#include "common/Exception.h"
namespace love
{
namespace font
{
namespace freetype
{
TrueTypeRasterizer::TrueTypeRasterizer(FT_Library library, Data *data, int size)
: data(data)
{
if (FT_New_Memory_Face(library,
(const FT_Byte *)data->getData(), /* first byte in memory */
data->getSize(), /* size in bytes */
0, /* face_index */
&face))
throw love::Exception("TrueTypeFont Loading error: FT_New_Face failed (there is probably a problem with your font file)\n");
FT_Set_Pixel_Sizes(face, size, size);
// Set global metrics
FT_Size_Metrics s = face->size->metrics;
metrics.advance = s.max_advance >> 6;
metrics.ascent = s.ascender >> 6;
metrics.descent = s.descender >> 6;
metrics.height = s.height >> 6;
data->retain();
}
TrueTypeRasterizer::~TrueTypeRasterizer()
{
FT_Done_Face(face);
data->release();
}
int TrueTypeRasterizer::getLineHeight() const
{
return (int)(getHeight() * 1.25);
}
GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
{
love::font::GlyphMetrics glyphMetrics = {};
FT_Glyph ftglyph;
// Initialize
if (FT_Load_Glyph(face, FT_Get_Char_Index(face, glyph), FT_LOAD_DEFAULT))
throw love::Exception("TrueTypeFont Loading vm->error: FT_Load_Glyph failed\n");
if (FT_Get_Glyph(face->glyph, &ftglyph))
throw love::Exception("TrueTypeFont Loading vm->error: FT_Get_Glyph failed\n");
FT_Glyph_To_Bitmap(&ftglyph, FT_RENDER_MODE_NORMAL, 0, 1);
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) ftglyph;
FT_Bitmap &bitmap = bitmap_glyph->bitmap; //just to make things easier
// Get metrics
glyphMetrics.bearingX = face->glyph->metrics.horiBearingX >> 6;
glyphMetrics.bearingY = face->glyph->metrics.horiBearingY >> 6;
glyphMetrics.height = bitmap.rows;
glyphMetrics.width = bitmap.width;
glyphMetrics.advance = face->glyph->metrics.horiAdvance >> 6;
GlyphData *glyphData = new GlyphData(glyph, glyphMetrics, GlyphData::FORMAT_LUMINANCE_ALPHA);
int size = bitmap.rows * bitmap.width;
unsigned char *dst = (unsigned char *) glyphData->getData();
// Note that bitmap.buffer contains only luminosity. We copy that single
// value to our luminosity-alpha format.
for (int i = 0; i < size; i++)
{
dst[2*i] = 255;
dst[2*i+1] = bitmap.buffer[i];
}
// Having copied the data over, we can destroy the glyph
FT_Done_Glyph(ftglyph);
// Return data
return glyphData;
}
int TrueTypeRasterizer::getGlyphCount() const
{
return face->num_glyphs;
}
bool TrueTypeRasterizer::hasGlyph(uint32 glyph) const
{
return FT_Get_Char_Index(face, glyph) != 0;
}
} // freetype
} // font
} // love
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_FREETYPE_TRUE_TYPE_RASTERIZER_H
#define LOVE_FONT_FREETYPE_TRUE_TYPE_RASTERIZER_H
// LOVE
#include "filesystem/File.h"
#include "font/Rasterizer.h"
// TrueType2
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
namespace love
{
namespace font
{
namespace freetype
{
/**
* Holds data for a font object.
**/
class TrueTypeRasterizer : public Rasterizer
{
public:
TrueTypeRasterizer(FT_Library library, Data *data, int size);
virtual ~TrueTypeRasterizer();
// Implement Rasterizer
virtual int getLineHeight() const;
virtual GlyphData *getGlyphData(uint32 glyph) const;
virtual int getGlyphCount() const;
virtual bool hasGlyph(uint32 glyph) const;
private:
// TrueType face
FT_Face face;
// File data
Data *data;
}; // FreetypeRasterizer
} // freetype
} // font
} // love
#endif // LOVE_FONT_FREETYPE_TRUE_TYPE_RASTERIZER_H
@@ -0,0 +1,125 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Font.h"
#include "Font.h"
#include "font/wrap_GlyphData.h"
#include "font/wrap_Rasterizer.h"
#include "TrueTypeRasterizer.h"
namespace love
{
namespace font
{
namespace freetype
{
static Font *instance = 0;
int w_newRasterizer(lua_State *L)
{
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
luax_convobj(L, 1, "filesystem", "newFileData");
Rasterizer *t = 0;
EXCEPT_GUARD(
if (luax_istype(L, 1, IMAGE_IMAGE_DATA_T))
{
love::image::ImageData *d = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
const char *g = luaL_checkstring(L, 2);
std::string glyphs(g);
t = instance->newRasterizer(d, glyphs);
}
else if (luax_istype(L, 1, DATA_T))
{
Data *d = luax_checkdata(L, 1);
int size = luaL_checkint(L, 2);
t = instance->newRasterizer(d, size);
}
)
luax_pushtype(L, "Rasterizer", FONT_RASTERIZER_T, t);
return 1;
}
int w_newGlyphData(lua_State *L)
{
Rasterizer *r = luax_checkrasterizer(L, 1);
GlyphData *t = 0;
// newGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string glyph = luax_checkstring(L, 2);
EXCEPT_GUARD(t = instance->newGlyphData(r, glyph);)
}
else
{
uint32 g = (uint32) luaL_checknumber(L, 2);
t = instance->newGlyphData(r, g);
}
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, t);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "newRasterizer", w_newRasterizer },
{ "newGlyphData", w_newGlyphData },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_glyphdata,
luaopen_rasterizer,
0
};
extern "C" int luaopen_love_font(lua_State *L)
{
if (instance == 0)
{
EXCEPT_GUARD(instance = new Font();)
}
else
instance->retain();
WrappedModule w;
w.module = instance;
w.name = "font";
w.flags = MODULE_T;
w.functions = functions;
w.types = types;
return luax_register_module(L, w);
}
} // freetype
} // font
} // love
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_FREETYPE_WRAP_FONT_H
#define LOVE_FONT_FREETYPE_WRAP_FONT_H
// LOVE
#include "common/config.h"
#include "common/runtime.h"
namespace love
{
namespace font
{
namespace freetype
{
int w_newRasterizer(lua_State *L);
int w_newGlyphData(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_font(lua_State *L);
} // freetype
} // font
} // love
#endif // LOVE_FONT_FREETYPE_WRAP_FONT_H
@@ -0,0 +1,143 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_GlyphData.h"
namespace love
{
namespace font
{
GlyphData *luax_checkglyphdata(lua_State *L, int idx)
{
return luax_checktype<GlyphData>(L, idx, "GlyphData", FONT_GLYPH_DATA_T);
}
int w_GlyphData_getWidth(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
lua_pushinteger(L, t->getWidth());
return 1;
}
int w_GlyphData_getHeight(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
lua_pushinteger(L, t->getHeight());
return 1;
}
int w_GlyphData_getDimensions(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
lua_pushinteger(L, t->getWidth());
lua_pushinteger(L, t->getHeight());
return 2;
}
int w_GlyphData_getGlyph(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
uint32 glyph = t->getGlyph();
lua_pushnumber(L, (lua_Number) glyph);
return 1;
}
int w_GlyphData_getGlyphString(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
EXCEPT_GUARD(luax_pushstring(L, t->getGlyphString());)
return 1;
}
int w_GlyphData_getAdvance(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
lua_pushinteger(L, t->getAdvance());
return 1;
}
int w_GlyphData_getBearing(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
lua_pushinteger(L, t->getBearingX());
lua_pushinteger(L, t->getBearingY());
return 2;
}
int w_GlyphData_getBoundingBox(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
int minX = t->getMinX();
int minY = t->getMinY();
int maxX = t->getMaxX();
int maxY = t->getMaxY();
int width = maxX - minX;
int height = maxY - minY;
lua_pushinteger(L, minX);
lua_pushinteger(L, minY);
lua_pushinteger(L, width);
lua_pushinteger(L, height);
return 4;
}
int w_GlyphData_getFormat(lua_State *L)
{
GlyphData *t = luax_checkglyphdata(L, 1);
const char *str;
if (!GlyphData::getConstant(t->getFormat(), str))
return luaL_error(L, "unknown GlyphData format.");
lua_pushstring(L, str);
return 1;
}
static const luaL_Reg functions[] =
{
// Data
{ "getString", w_Data_getString },
{ "getPointer", w_Data_getPointer },
{ "getSize", w_Data_getSize },
{ "getWidth", w_GlyphData_getWidth },
{ "getHeight", w_GlyphData_getHeight },
{ "getDimensions", w_GlyphData_getDimensions },
{ "getGlyph", w_GlyphData_getGlyph },
{ "getGlyphString", w_GlyphData_getGlyphString },
{ "getAdvance", w_GlyphData_getAdvance },
{ "getBearing", w_GlyphData_getBearing },
{ "getBoundingBox", w_GlyphData_getBoundingBox },
{ "getFormat", w_GlyphData_getFormat },
{ 0, 0 }
};
extern "C" int luaopen_glyphdata(lua_State *L)
{
return luax_register_type(L, "GlyphData", functions);
}
} // font
} // love
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_WRAP_GLYPH_DATA_H
#define LOVE_FONT_WRAP_GLYPH_DATA_H
// LOVE
#include "common/runtime.h"
#include "common/wrap_Data.h"
#include "GlyphData.h"
namespace love
{
namespace font
{
GlyphData *luax_checkglyphdata(lua_State *L, int idx);
int w_GlyphData_getWidth(lua_State *L);
int w_GlyphData_getHeight(lua_State *L);
int w_GlyphData_getDimensions(lua_State *L);
int w_GlyphData_getGlyph(lua_State *L);
int w_GlyphData_getGlyphString(lua_State *L);
int w_GlyphData_getAdvance(lua_State *L);
int w_GlyphData_getBearing(lua_State *L);
int w_GlyphData_getBoundingBox(lua_State *L);
int w_GlyphData_getFormat(lua_State *L);
extern "C" int luaopen_glyphdata(lua_State *L);
} // font
} // love
#endif // LOVE_FONT_WRAP_GLYPH_DATA_H
@@ -0,0 +1,145 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_Rasterizer.h"
#include "common/wrap_Data.h"
namespace love
{
namespace font
{
Rasterizer *luax_checkrasterizer(lua_State *L, int idx)
{
return luax_checktype<Rasterizer>(L, idx, "Rasterizer", FONT_RASTERIZER_T);
}
int w_Rasterizer_getHeight(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getHeight());
return 1;
}
int w_Rasterizer_getAdvance(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getAdvance());
return 1;
}
int w_Rasterizer_getAscent(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getAscent());
return 1;
}
int w_Rasterizer_getDescent(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getDescent());
return 1;
}
int w_Rasterizer_getLineHeight(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getLineHeight());
return 1;
}
int w_Rasterizer_getGlyphData(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
GlyphData *g = 0;
EXCEPT_GUARD(
// getGlyphData accepts a unicode character or a codepoint number.
if (lua_type(L, 2) == LUA_TSTRING)
{
std::string glyph = luax_checkstring(L, 2);
g = t->getGlyphData(glyph);
}
else
{
uint32 glyph = (uint32) luaL_checknumber(L, 2);
g = t->getGlyphData(glyph);
}
)
luax_pushtype(L, "GlyphData", FONT_GLYPH_DATA_T, g);
return 1;
}
int w_Rasterizer_getGlyphCount(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
lua_pushinteger(L, t->getGlyphCount());
return 1;
}
int w_Rasterizer_hasGlyphs(lua_State *L)
{
Rasterizer *t = luax_checkrasterizer(L, 1);
bool hasglyph = false;
int count = lua_gettop(L) - 1;
count = count < 1 ? 1 : count;
EXCEPT_GUARD(
for (int i = 2; i < count + 2; i++)
{
if (lua_type(L, i) == LUA_TSTRING)
hasglyph = t->hasGlyphs(luax_checkstring(L, i));
else
hasglyph = t->hasGlyph((uint32) luaL_checknumber(L, i));
if (!hasglyph)
break;
}
)
luax_pushboolean(L, hasglyph);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getHeight", w_Rasterizer_getHeight },
{ "getAdvance", w_Rasterizer_getAdvance },
{ "getAscent", w_Rasterizer_getAscent },
{ "getDescent", w_Rasterizer_getDescent },
{ "getLineHeight", w_Rasterizer_getLineHeight },
{ "getGlyphData", w_Rasterizer_getGlyphData },
{ "getGlyphCount", w_Rasterizer_getGlyphCount },
{ "hasGlyphs", w_Rasterizer_hasGlyphs },
{ 0, 0 }
};
extern "C" int luaopen_rasterizer(lua_State *L)
{
return luax_register_type(L, "Rasterizer", functions);
}
} // font
} // love
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_FONT_WRAP_RASTERIZER_H
#define LOVE_FONT_WRAP_RASTERIZER_H
// LOVE
#include "common/runtime.h"
#include "Rasterizer.h"
namespace love
{
namespace font
{
Rasterizer *luax_checkrasterizer(lua_State *L, int idx);
int w_Rasterizer_getHeight(lua_State *L);
int w_Rasterizer_getAdvance(lua_State *L);
int w_Rasterizer_getAscent(lua_State *L);
int w_Rasterizer_getDescent(lua_State *L);
int w_Rasterizer_getLineHeight(lua_State *L);
int w_Rasterizer_getGlyphData(lua_State *L);
int w_Rasterizer_getGlyphCount(lua_State *L);
int w_Rasterizer_hasGlyphs(lua_State *L);
extern "C" int luaopen_rasterizer(lua_State *L);
} // font
} // love
#endif // LOVE_FONT_WRAP_RASTERIZER_H
+109
View File
@@ -0,0 +1,109 @@
/**
* Copyright (c) 2006-2011 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_COLOR_H
#define LOVE_GRAPHICS_COLOR_H
namespace love
{
namespace graphics
{
template <typename T>
struct ColorT
{
T r;
T g;
T b;
T a;
ColorT() : r(0), g(0), b(0), a(0) {}
ColorT(T r_, T g_, T b_, T a_) : r(r_), g(g_), b(b_), a(a_) {}
void set(T r_, T g_, T b_, T a_)
{
r = r_;
g = g_;
b = b_;
a = a_;
}
ColorT<T> operator+=(const ColorT<T> &other);
ColorT<T> operator*=(T s);
ColorT<T> operator/=(T s);
};
template <typename T>
ColorT<T> ColorT<T>::operator+=(const ColorT<T> &other)
{
r += other.r;
g += other.g;
b += other.b;
a += other.a;
return *this;
}
template <typename T>
ColorT<T> ColorT<T>::operator*=(T s)
{
r *= s;
g *= s;
b *= s;
a *= s;
return *this;
}
template <typename T>
ColorT<T> ColorT<T>::operator/=(T s)
{
r /= s;
g /= s;
b /= s;
a /= s;
return *this;
}
template <typename T>
ColorT<T> operator+(const ColorT<T> &a, const ColorT<T> &b)
{
ColorT<T> tmp(a);
return tmp += b;
}
template <typename T>
ColorT<T> operator*(const ColorT<T> &a, T s)
{
ColorT<T> tmp(a);
return tmp *= s;
}
template <typename T>
ColorT<T> operator/(const ColorT<T> &a, T s)
{
ColorT<T> tmp(a);
return tmp /= s;
}
typedef ColorT<unsigned char> Color;
typedef ColorT<float> Colorf;
} // graphics
} // love
#endif // LOVE_GRAPHICS_COLOR_H
+65
View File
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_DRAWQABLE_H
#define LOVE_GRAPHICS_DRAWQABLE_H
// LOVE
#include "Drawable.h"
#include "Quad.h"
namespace love
{
namespace graphics
{
/**
* A DrawQable is anything that be drawn in part with a Quad object.
**/
class DrawQable : public Drawable
{
public:
/**
* Destructor.
**/
virtual ~DrawQable() {}
/**
* Draws the object with the specified transformation.
*
* @param quad The Quad object to use to draw the object.
* @param x The position of the object along the x-axis.
* @param y The position of the object along the y-axis.
* @param angle The angle of the object (in radians).
* @param sx The scale factor along the x-axis.
* @param sy The scale factor along the y-axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @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;
};
} // graphics
} // love
#endif // LOVE_GRAPHICS_DRAWQABLE_H
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Drawable.h"
namespace love
{
namespace graphics
{
Drawable::~Drawable()
{
}
} // graphics
} // love
+64
View File
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_DRAWABLE_H
#define LOVE_GRAPHICS_DRAWABLE_H
// LOVE
#include "common/Object.h"
namespace love
{
namespace graphics
{
/**
* A Drawable is anything that can be drawn on screen with a
* position, scale and orientation.
**/
class Drawable : public Object
{
public:
/**
* Destructor.
**/
virtual ~Drawable();
/**
* Draws the object with the specified transformation.
*
* @param x The position of the object along the x-axis.
* @param y The position of the object along the y-axis.
* @param angle The angle of the object (in radians).
* @param sx The scale factor along the x-axis.
* @param sy The scale factor along the y-axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @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;
};
} // graphics
} // love
#endif // LOVE_GRAPHICS_DRAWABLE_H
+173
View File
@@ -0,0 +1,173 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Graphics.h"
namespace love
{
namespace graphics
{
Graphics::~Graphics()
{
}
bool Graphics::getConstant(const char *in, DrawMode &out)
{
return drawModes.find(in, out);
}
bool Graphics::getConstant(DrawMode in, const char *&out)
{
return drawModes.find(in, out);
}
bool Graphics::getConstant(const char *in, AlignMode &out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(AlignMode in, const char *&out)
{
return alignModes.find(in, out);
}
bool Graphics::getConstant(const char *in, BlendMode &out)
{
return blendModes.find(in, out);
}
bool Graphics::getConstant(BlendMode in, const char *&out)
{
return blendModes.find(in, out);
}
bool Graphics::getConstant(const char *in, LineStyle &out)
{
return lineStyles.find(in, out);
}
bool Graphics::getConstant(LineStyle in, const char *&out)
{
return lineStyles.find(in, out);
}
bool Graphics::getConstant(const char *in, LineJoin &out)
{
return lineJoins.find(in, out);
}
bool Graphics::getConstant(LineJoin in, const char *&out)
{
return lineJoins.find(in, out);
}
bool Graphics::getConstant(const char *in, PointStyle &out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(PointStyle in, const char *&out)
{
return pointStyles.find(in, out);
}
bool Graphics::getConstant(const char *in, Support &out)
{
return support.find(in, out);
}
bool Graphics::getConstant(Support in, const char *&out)
{
return support.find(in, out);
}
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{
{ "line", Graphics::DRAW_LINE },
{ "fill", Graphics::DRAW_FILL },
};
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM> Graphics::drawModes(Graphics::drawModeEntries, sizeof(Graphics::drawModeEntries));
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM>::Entry Graphics::alignModeEntries[] =
{
{ "left", Graphics::ALIGN_LEFT },
{ "right", Graphics::ALIGN_RIGHT },
{ "center", Graphics::ALIGN_CENTER },
{ "justify", Graphics::ALIGN_JUSTIFY },
};
StringMap<Graphics::AlignMode, Graphics::ALIGN_MAX_ENUM> Graphics::alignModes(Graphics::alignModeEntries, sizeof(Graphics::alignModeEntries));
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
{
{ "alpha", Graphics::BLEND_ALPHA },
{ "additive", Graphics::BLEND_ADDITIVE },
{ "subtractive", Graphics::BLEND_SUBTRACTIVE },
{ "multiplicative", Graphics::BLEND_MULTIPLICATIVE },
{ "premultiplied", Graphics::BLEND_PREMULTIPLIED },
{ "replace", Graphics::BLEND_REPLACE },
};
StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM> Graphics::blendModes(Graphics::blendModeEntries, sizeof(Graphics::blendModeEntries));
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM>::Entry Graphics::lineStyleEntries[] =
{
{ "smooth", Graphics::LINE_SMOOTH },
{ "rough", Graphics::LINE_ROUGH }
};
StringMap<Graphics::LineStyle, Graphics::LINE_MAX_ENUM> Graphics::lineStyles(Graphics::lineStyleEntries, sizeof(Graphics::lineStyleEntries));
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM>::Entry Graphics::lineJoinEntries[] =
{
{ "none", Graphics::LINE_JOIN_NONE },
{ "miter", Graphics::LINE_JOIN_MITER },
{ "bevel", Graphics::LINE_JOIN_BEVEL }
};
StringMap<Graphics::LineJoin, Graphics::LINE_JOIN_MAX_ENUM> Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries));
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM>::Entry Graphics::pointStyleEntries[] =
{
{ "smooth", Graphics::POINT_SMOOTH },
{ "rough", Graphics::POINT_ROUGH }
};
StringMap<Graphics::PointStyle, Graphics::POINT_MAX_ENUM> Graphics::pointStyles(Graphics::pointStyleEntries, sizeof(Graphics::pointStyleEntries));
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::supportEntries[] =
{
{ "canvas", Graphics::SUPPORT_CANVAS },
{ "hdrcanvas", Graphics::SUPPORT_HDR_CANVAS },
{ "multicanvas", Graphics::SUPPORT_MULTI_CANVAS },
{ "shader", Graphics::SUPPORT_SHADER },
{ "npot", Graphics::SUPPORT_NPOT },
{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
{ "mipmap", Graphics::SUPPORT_MIPMAP },
{ "dxt", Graphics::SUPPORT_DXT },
{ "bc5", Graphics::SUPPORT_BC5 },
};
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
} // graphics
} // love
+185
View File
@@ -0,0 +1,185 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_GRAPHICS_H
#define LOVE_GRAPHICS_GRAPHICS_H
// LOVE
#include "common/Module.h"
#include "common/StringMap.h"
namespace love
{
namespace graphics
{
class Graphics : public Module
{
public:
enum DrawMode
{
DRAW_LINE = 1,
DRAW_FILL,
DRAW_MAX_ENUM
};
enum AlignMode
{
ALIGN_LEFT = 1,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_JUSTIFY,
ALIGN_MAX_ENUM
};
enum BlendMode
{
BLEND_ALPHA = 1,
BLEND_ADDITIVE,
BLEND_SUBTRACTIVE,
BLEND_MULTIPLICATIVE,
BLEND_PREMULTIPLIED,
BLEND_REPLACE,
BLEND_MAX_ENUM
};
enum LineStyle
{
LINE_ROUGH = 1,
LINE_SMOOTH,
LINE_MAX_ENUM
};
enum LineJoin
{
LINE_JOIN_NONE = 1,
LINE_JOIN_MITER,
LINE_JOIN_BEVEL,
LINE_JOIN_MAX_ENUM
};
enum PointStyle
{
POINT_ROUGH = 1,
POINT_SMOOTH,
POINT_MAX_ENUM
};
enum Support
{
SUPPORT_CANVAS = 1,
SUPPORT_HDR_CANVAS,
SUPPORT_MULTI_CANVAS,
SUPPORT_SHADER,
SUPPORT_NPOT,
SUPPORT_SUBTRACTIVE,
SUPPORT_MIPMAP,
SUPPORT_DXT,
SUPPORT_BC5,
SUPPORT_MAX_ENUM
};
enum Renderer
{
RENDERER_OPENGL = 0,
RENDERER_OPENGLES,
RENDERER_MAX_ENUM
};
enum RendererInfo
{
RENDERER_INFO_NAME = 1,
RENDERER_INFO_VERSION,
RENDERER_INFO_VENDOR,
RENDERER_INFO_DEVICE,
RENDERER_INFO_MAX_ENUM
};
virtual ~Graphics();
/**
* Sets the current graphics display viewport dimensions.
**/
virtual void setViewportSize(int width, int height) = 0;
/**
* Sets the current graphics display viewport and initializes the renderer.
* @param width The viewport width.
* @param height The viewport height.
**/
virtual bool setMode(int width, int height) = 0;
/**
* Un-sets the current graphics display mode (uninitializing objects if
* necessary.)
**/
virtual void unSetMode() = 0;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
static bool getConstant(const char *in, AlignMode &out);
static bool getConstant(AlignMode in, const char *&out);
static bool getConstant(const char *in, BlendMode &out);
static bool getConstant(BlendMode in, const char *&out);
static bool getConstant(const char *in, LineStyle &out);
static bool getConstant(LineStyle in, const char *&out);
static bool getConstant(const char *in, LineJoin &out);
static bool getConstant(LineJoin in, const char *&out);
static bool getConstant(const char *in, PointStyle &out);
static bool getConstant(PointStyle in, const char *&out);
static bool getConstant(const char *in, Support &out);
static bool getConstant(Support in, const char *&out);
private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
static StringMap<LineStyle, LINE_MAX_ENUM>::Entry lineStyleEntries[];
static StringMap<LineStyle, LINE_MAX_ENUM> lineStyles;
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM>::Entry lineJoinEntries[];
static StringMap<LineJoin, LINE_JOIN_MAX_ENUM> lineJoins;
static StringMap<PointStyle, POINT_MAX_ENUM>::Entry pointStyleEntries[];
static StringMap<PointStyle, POINT_MAX_ENUM> pointStyles;
static StringMap<Support, SUPPORT_MAX_ENUM>::Entry supportEntries[];
static StringMap<Support, SUPPORT_MAX_ENUM> support;
}; // Graphics
} // graphics
} // love
#endif // LOVE_GRAPHICS_GRAPHICS_H
+96
View File
@@ -0,0 +1,96 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Image.h"
namespace love
{
namespace graphics
{
Image::Filter Image::defaultFilter;
Image::Filter::Filter()
: min(FILTER_LINEAR)
, mag(FILTER_LINEAR)
, mipmap(FILTER_NONE)
, anisotropy(1.0f)
{
}
Image::Wrap::Wrap()
: s(WRAP_CLAMP)
, t(WRAP_CLAMP)
{
}
Image::~Image()
{
}
void Image::setDefaultFilter(const Filter &f)
{
defaultFilter = f;
}
const Image::Filter &Image::getDefaultFilter()
{
return defaultFilter;
}
bool Image::getConstant(const char *in, FilterMode &out)
{
return filterModes.find(in, out);
}
bool Image::getConstant(FilterMode in, const char *&out)
{
return filterModes.find(in, out);
}
bool Image::getConstant(const char *in, WrapMode &out)
{
return wrapModes.find(in, out);
}
bool Image::getConstant(WrapMode in, const char *&out)
{
return wrapModes.find(in, out);
}
StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM>::Entry Image::filterModeEntries[] =
{
{ "linear", Image::FILTER_LINEAR },
{ "nearest", Image::FILTER_NEAREST },
};
StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM> Image::filterModes(Image::filterModeEntries, sizeof(Image::filterModeEntries));
StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM>::Entry Image::wrapModeEntries[] =
{
{ "clamp", Image::WRAP_CLAMP },
{ "repeat", Image::WRAP_REPEAT },
};
StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM> Image::wrapModes(Image::wrapModeEntries, sizeof(Image::wrapModeEntries));
} // graphics
} // love
+95
View File
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_IMAGE_H
#define LOVE_GRAPHICS_IMAGE_H
// LOVE
#include "graphics/Volatile.h"
#include "graphics/DrawQable.h"
#include "common/StringMap.h"
namespace love
{
namespace graphics
{
class Image : public DrawQable, public Volatile
{
public:
enum WrapMode
{
WRAP_CLAMP = 1,
WRAP_REPEAT,
WRAP_MAX_ENUM
};
enum FilterMode
{
FILTER_LINEAR = 1,
FILTER_NEAREST,
FILTER_NONE,
FILTER_MAX_ENUM
};
struct Filter
{
Filter();
FilterMode min;
FilterMode mag;
FilterMode mipmap;
float anisotropy;
};
struct Wrap
{
Wrap();
WrapMode s;
WrapMode t;
};
virtual ~Image();
// The default filter.
static void setDefaultFilter(const Filter &f);
static const Filter &getDefaultFilter();
static bool getConstant(const char *in, FilterMode &out);
static bool getConstant(FilterMode in, const char *&out);
static bool getConstant(const char *in, WrapMode &out);
static bool getConstant(WrapMode in, const char *&out);
private:
// The default texture filter.
static Filter defaultFilter;
static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
}; // Image
} // graphics
} // love
#endif // LOVE_GRAPHICS_IMAGE_H
+83
View File
@@ -0,0 +1,83 @@
/**
* Copyright (c) 2006-2013 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 "Quad.h"
// C
#include <cstring> // For memcpy
namespace love
{
namespace graphics
{
Quad::Quad(const Quad::Viewport &v, float sw, float sh)
: sw(sw)
, sh(sh)
{
memset(vertices, 255, sizeof(Vertex) * NUM_VERTICES);
refresh(v, sw, sh);
}
Quad::~Quad()
{
}
void Quad::refresh(const Quad::Viewport &v, float sw, float sh)
{
viewport = v;
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = 0;
vertices[1].y = v.h;
vertices[2].x = v.w;
vertices[2].y = v.h;
vertices[3].x = v.w;
vertices[3].y = 0;
vertices[0].s = v.x/sw;
vertices[0].t = v.y/sh;
vertices[1].s = v.x/sw;
vertices[1].t = (v.y+v.h)/sh;
vertices[2].s = (v.x+v.w)/sw;
vertices[2].t = (v.y+v.h)/sh;
vertices[3].s = (v.x+v.w)/sw;
vertices[3].t = v.y/sh;
}
void Quad::setViewport(const Quad::Viewport &v)
{
refresh(v, sw, sh);
}
Quad::Viewport Quad::getViewport() const
{
return viewport;
}
const Vertex *Quad::getVertices() const
{
return vertices;
}
} // graphics
} // love
+67
View File
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_QUAD_H
#define LOVE_GRAPHICS_QUAD_H
// LOVE
#include "common/Object.h"
#include "common/math.h"
namespace love
{
namespace graphics
{
class Quad : public Object
{
public:
struct Viewport
{
float x, y;
float w, h;
};
static const size_t NUM_VERTICES = 4;
Quad(const Viewport &v, float sw, float sh);
virtual ~Quad();
void refresh(const Viewport &v, float sw, float sh);
void setViewport(const Viewport &v);
Viewport getViewport() const;
const Vertex *getVertices() const;
private:
Vertex vertices[NUM_VERTICES];
Viewport viewport;
float sw;
float sh;
}; // Quad
} // graphics
} // love
#endif // LOVE_GRAPHICS_QUAD_H
@@ -0,0 +1,69 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Volatile.h"
namespace love
{
namespace graphics
{
// Static members.
std::list<Volatile *> Volatile::all;
Volatile::Volatile()
{
// Insert this object into "all".
all.push_back(this);
}
Volatile::~Volatile()
{
// Remove the pointer to this object.
all.remove(this);
}
bool Volatile::loadAll()
{
bool success = true;
std::list<Volatile *>::iterator i = all.begin();
while (i != all.end())
{
success = success && (*i)->loadVolatile();
i++;
}
return success;
}
void Volatile::unloadAll()
{
std::list<Volatile *>::iterator i = all.begin();
while (i != all.end())
{
(*i)->unloadVolatile();
i++;
}
}
} // graphics
} // love
+93
View File
@@ -0,0 +1,93 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_VOLATILE_H
#define LOVE_GRAPHICS_VOLATILE_H
// STL
#include <list>
namespace love
{
namespace graphics
{
/**
* This class is the superclass of all objects which must completely or
* partially reload when the user changes the display resolution. All
* volatile objects will be notified when the display mode changes.
*
* @author Anders Ruud
**/
class Volatile
{
private:
// A list of all Volatile object currently alive.
static std::list<Volatile *> all;
public:
/**
* Constructor. Automatically adds \c this into the list
* of volatile objects.
**/
Volatile();
/**
* Destructor. Removes \c this from the list of volatile
* objects.
**/
virtual ~Volatile();
/**
* Loads the part(s) of the object which is destroyed when
* the display mode is changed.
*
* @return True if successful, false on errors.
**/
virtual bool loadVolatile() = 0;
/**
* Unloads the part(s) of the objects which would be destroyed
* anyway when the display mode is changed.
**/
virtual void unloadVolatile() = 0;
// Static:
/**
* Calls \c loadVolatile() on each element in the list of volatiles.
*
* @return True if all elements succeeded, false if one or more failed.
**/
static bool loadAll();
/**
* Calls \c unloadVolatile() on each element in the list of volatiles.
**/
static void unloadAll();
}; // Volatile
} // graphics
} // love
#endif // LOVE_GRAPHICS_VOLATILE_H
@@ -0,0 +1,813 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Canvas.h"
#include "Image.h"
#include "Graphics.h"
#include "common/Matrix.h"
#include "common/config.h"
#include <cstring> // For memcpy
#include <limits>
namespace love
{
namespace graphics
{
namespace opengl
{
// strategy for fbo creation, interchangable at runtime:
// none, opengl >= 3.0, extensions
struct FramebufferStrategy
{
virtual ~FramebufferStrategy() {}
/// create a new framebuffer and texture
/**
* @param[out] framebuffer Framebuffer name
* @param[out] img Texture name
* @param[in] width Width of framebuffer
* @param[in] height Height of framebuffer
* @param[in] texture_type Type of the canvas texture.
* @return Creation status
*/
virtual GLenum createFBO(GLuint &, GLuint &, int, int, Canvas::TextureType)
{
return GL_FRAMEBUFFER_UNSUPPORTED;
}
/// Create a stencil buffer and attach it to the active framebuffer object
/**
* @param[in] width Width of the stencil buffer
* @param[in] height Height of the stencil buffer
* @param[out] stencil Name for stencil buffer
* @return Whether the stencil buffer was successfully created
**/
virtual bool createStencil(int, int, GLuint &)
{
return false;
}
/// remove objects
/**
* @param[in] framebuffer Framebuffer name
* @param[in] depth_stencil Name for packed depth and stencil buffer
* @param[in] img Texture name
*/
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
virtual void bindFBO(GLuint) {}
/// attach additional canvases to the active framebuffer for rendering
/**
* @param[in] canvases List of canvases to attach
**/
virtual void setAttachments(const std::vector<Canvas *> &) {}
/// stop using all additional attached canvases
virtual void setAttachments() {}
};
struct FramebufferStrategyCore : public FramebufferStrategy
{
virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
{
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
// create framebuffer
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// generate texture save target
GLint internalFormat;
GLenum format;
switch (texture_type)
{
case Canvas::TYPE_HDR:
internalFormat = GL_RGBA16F;
format = GL_FLOAT;
break;
case Canvas::TYPE_NORMAL:
default:
internalFormat = GL_RGBA;
format = GL_UNSIGNED_BYTE;
}
glGenTextures(1, &img);
gl.bindTexture(img);
gl.setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL);
gl.bindTexture(0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffers(1, &stencil);
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// check status
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
{
gl.deleteTexture(img);
if (depth_stencil != 0)
glDeleteRenderbuffers(1, &depth_stencil);
if (framebuffer != 0)
glDeleteFramebuffers(1, &framebuffer);
}
virtual void bindFBO(GLuint framebuffer)
{
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
virtual void setAttachments()
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
setAttachments();
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
// Attach the canvas textures to the currently bound framebuffer.
for (size_t i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
}
// set up multiple render targets
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLAD_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyCorePacked : public FramebufferStrategyCore
{
virtual bool createStencil(int width, int height, 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);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// check status
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
}
};
struct FramebufferStrategyPackedEXT : public FramebufferStrategy
{
virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
{
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
// create framebuffer
glGenFramebuffersEXT(1, &framebuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
// generate texture save target
GLint internalFormat;
GLenum format;
switch (texture_type)
{
case Canvas::TYPE_HDR:
internalFormat = GL_RGBA16F;
format = GL_FLOAT;
break;
case Canvas::TYPE_NORMAL:
default:
internalFormat = GL_RGBA;
format = GL_UNSIGNED_BYTE;
}
glGenTextures(1, &img);
gl.bindTexture(img);
gl.setTextureFilter(Image::getDefaultFilter());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
0, GL_RGBA, format, NULL);
gl.bindTexture(0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, img, 0);
// check status
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
// unbind framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create combined depth/stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
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;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
{
gl.deleteTexture(img);
if (depth_stencil != 0)
glDeleteRenderbuffersEXT(1, &depth_stencil);
if (framebuffer != 0)
glDeleteFramebuffersEXT(1, &framebuffer);
}
virtual void bindFBO(GLuint framebuffer)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
}
virtual void setAttachments()
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
setAttachments();
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
// Attach the canvas textures to the currently bound framebuffer.
for (size_t i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
}
// set up multiple render targets
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLAD_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
{
virtual bool createStencil(int width, int height, GLuint &stencil)
{
// create stencil buffer
glDeleteRenderbuffers(1, &stencil);
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
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;
}
bool isSupported()
{
GLuint fb = 0, stencil = 0, img = 0;
GLenum status = createFBO(fb, img, 2, 2, Canvas::TYPE_NORMAL);
deleteFBO(fb, stencil, img);
return status == GL_FRAMEBUFFER_COMPLETE;
}
};
FramebufferStrategy *strategy = NULL;
FramebufferStrategy strategyNone;
FramebufferStrategyCore strategyCore;
FramebufferStrategyCorePacked strategyCorePacked;
FramebufferStrategyPackedEXT strategyPackedEXT;
FramebufferStrategyEXT strategyEXT;
Canvas *Canvas::current = NULL;
static void getStrategy()
{
if (!strategy)
{
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
strategy = &strategyCorePacked;
else if (GLAD_ES_VERSION_2_0)
strategy = &strategyCore;
else if (GLAD_EXT_framebuffer_object && GLAD_EXT_packed_depth_stencil)
strategy = &strategyPackedEXT;
else if (GLAD_EXT_framebuffer_object && strategyEXT.isSupported())
strategy = &strategyEXT;
else
strategy = &strategyNone;
}
}
static int maxFBOColorAttachments = 0;
static int maxDrawBuffers = 0;
Canvas::Canvas(int width, int height, TextureType texture_type)
: width(width)
, height(height)
, fbo(0)
, depth_stencil(0)
, img(0)
, texture_type(texture_type)
{
float w = static_cast<float>(width);
float h = static_cast<float>(height);
// world coordinates
vertices[0].x = 0;
vertices[0].y = h;
vertices[1].x = w;
vertices[1].y = h;
vertices[2].x = w;
vertices[2].y = 0;
vertices[3].x = 0;
vertices[3].y = 0;
// texture coordinates
vertices[0].s = 0;
vertices[0].t = 0;
vertices[1].s = 1;
vertices[1].t = 0;
vertices[2].s = 1;
vertices[2].t = 1;
vertices[3].s = 0;
vertices[3].t = 1;
settings.filter = Image::getDefaultFilter();
getStrategy();
loadVolatile();
}
Canvas::~Canvas()
{
// reset framebuffer if still using this one
if (current == this)
stopGrab();
unloadVolatile();
}
bool Canvas::isSupported()
{
getStrategy();
return (strategy != &strategyNone);
}
bool Canvas::isHDRSupported()
{
return GLAD_VERSION_3_0 || (isSupported() && GLAD_ARB_texture_float);
}
bool Canvas::isMultiCanvasSupported()
{
if (!(isSupported() && (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_draw_buffers)))
return false;
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
{
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
}
// system must support at least 4 simultanious active canvases
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
}
void Canvas::bindDefaultCanvas()
{
if (current != NULL)
current->stopGrab();
}
void Canvas::setupGrab()
{
// already grabbing
if (current == this)
return;
// cleanup after previous fbo
if (current != NULL)
current->stopGrab();
// bind the framebuffer object.
glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
strategy->bindFBO(fbo);
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
// Set up orthographic view (no depth)
gl.matrices.projection.push(Matrix::ortho(0.0, width, height, 0.0));
// indicate we are using this fbo
current = this;
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
{
// Whether the new canvas list is different from the old one.
// A more thorough check is done below.
bool canvaseschanged = canvases.size() != attachedCanvases.size();
if (canvases.size() > 0)
{
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))
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
}
for (size_t i = 0; i < canvases.size(); i++)
{
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 (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
}
setupGrab();
// Don't attach anything if there's nothing to change.
if (!canvaseschanged)
return;
// Attach the canvas textures to the active FBO and set up MRTs.
strategy->setAttachments(canvases);
for (size_t i = 0; i < canvases.size(); i++)
canvases[i]->retain();
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases = canvases;
}
void Canvas::startGrab()
{
setupGrab();
if (attachedCanvases.size() == 0)
return;
// make sure the FBO is only using a single canvas
strategy->setAttachments();
// release any previously attached canvases
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
void Canvas::stopGrab()
{
// i am not grabbing. leave me alone
if (current != this)
return;
// bind default
strategy->bindFBO(gl.getDefaultFBO());
gl.matrices.projection.pop();
glPopAttrib();
current = NULL;
}
void Canvas::clear(Color c)
{
if (strategy == &strategyNone)
return;
GLuint previous = gl.getDefaultFBO();
if (current != this)
{
if (current != NULL)
previous = current->fbo;
strategy->bindFBO(fbo);
}
GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
// We don't need to worry about multiple FBO attachments or global clear
// color state when OpenGL 3.0+ is supported.
if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
{
glClearBufferfv(GL_COLOR, 0, glcolor);
if (depth_stencil != 0)
{
GLint stencilvalue = 0;
glClearBufferiv(GL_STENCIL, 0, &stencilvalue);
}
}
else
{
// glClear will clear all active draw buffers, so we need to temporarily
// detach any other canvases (when MRT is being used.)
if (attachedCanvases.size() > 0)
strategy->setAttachments();
// Don't use the state-shadowed gl.setClearColor because we want to save
// the previous clear color.
glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (attachedCanvases.size() > 0)
strategy->setAttachments(attachedCanvases);
// Restore the global clear color.
gl.setClearColor(gl.getClearColor());
}
if (current != this)
strategy->bindFBO(previous);
}
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
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
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
const Vertex *v = quad->getVertices();
// flip texture coordinates vertically.
Vertex w[4];
memcpy(w, v, sizeof(Vertex) * 4);
for (size_t i = 0; i < 4; i++)
w[i].t = 1.0f - w[i].t;
drawv(t, w);
}
bool Canvas::checkCreateStencil()
{
// Do nothing if we've already created the stencil buffer.
if (depth_stencil != 0)
return true;
if (current != this)
strategy->bindFBO(fbo);
bool success = strategy->createStencil(width, height, depth_stencil);
if (current && current != this)
strategy->bindFBO(current->fbo);
else if (!current)
strategy->bindFBO(gl.getDefaultFBO());
return success;
}
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
{
int row = 4 * width;
int size = row * height;
GLubyte *pixels = new GLubyte[size];
GLubyte *flipped = new GLubyte[size];
strategy->bindFBO(fbo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
if (current)
strategy->bindFBO(current->fbo);
else
strategy->bindFBO(gl.getDefaultFBO());
GLubyte *src = pixels, *dst = flipped + size - row;
for (int i = 0; i < height; ++i, dst -= row, src += row)
memcpy(dst, src, row);
love::image::ImageData *img = image->newImageData(width, height, (void *)flipped, true);
// The new ImageData now owns the flipped data, so we don't delete it here.
delete[] pixels;
return img;
}
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
{
if (current != this)
strategy->bindFBO(fbo);
glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
if (current && current != this)
strategy->bindFBO(current->fbo);
else if (!current)
strategy->bindFBO(gl.getDefaultFBO());
}
const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
{
return attachedCanvases;
}
void Canvas::setFilter(const Image::Filter &f)
{
settings.filter = f;
gl.bindTexture(img);
settings.filter.anisotropy = gl.setTextureFilter(f);
}
Image::Filter Canvas::getFilter() const
{
gl.bindTexture(img);
return gl.getTextureFilter();
}
void Canvas::setWrap(const Image::Wrap &w)
{
settings.wrap = w;
gl.bindTexture(img);
gl.setTextureWrap(w);
}
Image::Wrap Canvas::getWrap() const
{
return settings.wrap;
}
bool Canvas::loadVolatile()
{
fbo = depth_stencil = img = 0;
// glTexImage2D is guaranteed to error in this case.
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
{
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
return false;
}
status = strategy->createFBO(fbo, img, width, height, texture_type);
if (status != GL_FRAMEBUFFER_COMPLETE)
return false;
setFilter(settings.filter);
setWrap(settings.wrap);
clear(Color(0, 0, 0, 0));
return true;
}
void Canvas::unloadVolatile()
{
strategy->deleteFBO(fbo, depth_stencil, img);
fbo = depth_stencil = img = 0;
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
int Canvas::getWidth()
{
return width;
}
int Canvas::getHeight()
{
return height;
}
void Canvas::drawv(const Matrix &t, const Vertex *v) const
{
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
gl.prepareDraw();
gl.bindTexture(img);
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].s);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
}
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
@@ -0,0 +1,155 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
#define LOVE_GRAPHICS_OPENGL_CANVAS_H
#include "graphics/DrawQable.h"
#include "graphics/Volatile.h"
#include "graphics/Image.h"
#include "graphics/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
#include "common/math.h"
#include "common/Matrix.h"
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
class Canvas : public DrawQable, public Volatile
{
public:
enum TextureType
{
TYPE_NORMAL,
TYPE_HDR,
TYPE_MAX_ENUM
};
Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
virtual ~Canvas();
/**
* @param canvases A list of other canvases to temporarily attach to this one,
* to allow drawing to multiple canvases at once.
**/
void startGrab(const std::vector<Canvas *> &canvases);
void startGrab();
void stopGrab();
void clear(Color c);
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* @copydoc DrawQable::drawq()
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
**/
bool checkCreateStencil();
love::image::ImageData *getImageData(love::image::Image *image);
void getPixel(unsigned char* pixel_rgba, int x, int y);
const std::vector<Canvas *> &getAttachedCanvases() const;
void setFilter(const Image::Filter &f);
Image::Filter getFilter() const;
void setWrap(const Image::Wrap &w);
Image::Wrap getWrap() const;
int getWidth();
int getHeight();
inline GLenum getStatus() const
{
return status;
}
inline TextureType getTextureType() const
{
return texture_type;
}
bool loadVolatile();
void unloadVolatile();
static bool isSupported();
static bool isHDRSupported();
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();
GLuint getTextureName() const
{
return img;
}
private:
friend class Shader;
GLsizei width;
GLsizei height;
GLuint fbo;
GLuint depth_stencil;
GLuint img;
TextureType texture_type;
Vertex vertices[4];
GLenum status;
struct
{
Image::Filter filter;
Image::Wrap wrap;
} settings;
std::vector<Canvas *> attachedCanvases;
void setupGrab();
void drawv(const Matrix &t, const Vertex *v) const;
static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_CANVAS_H
@@ -0,0 +1,593 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include "Font.h"
#include "font/GlyphData.h"
#include "Image.h"
#include "libraries/utf8/utf8.h"
#include "common/math.h"
#include "common/Matrix.h"
#include <math.h>
#include <sstream>
#include <algorithm> // for max
namespace love
{
namespace graphics
{
namespace opengl
{
const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
: rasterizer(r)
, height(r->getHeight())
, lineHeight(1)
, mSpacing(1)
, filter(filter)
{
this->filter.mipmap = Image::FILTER_NONE;
// Try to find the best texture size match for the font size. default to the
// largest texture size if no rough match is found.
textureSizeIndex = NUM_TEXTURE_SIZES - 1;
for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
{
// Make a rough estimate of the total used texture size, based on glyph
// height. The estimated size is likely larger than the actual total
// size, which is good because texture switching is expensive.
if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
{
textureSizeIndex = i;
break;
}
}
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
love::font::GlyphData *gd = 0;
try
{
gd = r->getGlyphData(32);
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
loadVolatile();
}
catch (love::Exception &)
{
delete gd;
throw;
}
delete gd;
rasterizer->retain();
}
Font::~Font()
{
rasterizer->release();
unloadVolatile();
}
bool Font::initializeTexture(GLint format)
{
GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE_ALPHA : GL_RGBA;
// clear errors before initializing
while (glGetError() != GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_2D,
0,
internalformat,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
0,
format,
GL_UNSIGNED_BYTE,
NULL);
return glGetError() == GL_NO_ERROR;
}
void Font::createTexture()
{
textureX = textureY = rowHeight = TEXTURE_PADDING;
GLuint t;
glGenTextures(1, &t);
textures.push_back(t);
gl.bindTexture(t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
// Initialize the texture, attempting smaller sizes if initialization fails.
bool initialized = false;
while (textureSizeIndex >= 0)
{
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
initialized = initializeTexture(format);
if (initialized || textureSizeIndex <= 0)
break;
--textureSizeIndex;
}
if (!initialized)
{
// Clean up before throwing.
gl.deleteTexture(t);
gl.bindTexture(0);
textures.pop_back();
throw love::Exception("Could not create font texture!");
}
// Fill the texture with transparent black.
std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)textureWidth,
(GLsizei)textureHeight,
format,
GL_UNSIGNED_BYTE,
&emptyData[0]);
setFilter(filter);
}
Font::Glyph *Font::addGlyph(uint32 glyph)
{
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
int w = gd->getWidth();
int h = gd->getHeight();
if (textureX + w + TEXTURE_PADDING > textureWidth)
{
// out of space - new row!
textureX = TEXTURE_PADDING;
textureY += rowHeight;
rowHeight = TEXTURE_PADDING;
}
if (textureY + h + TEXTURE_PADDING > textureHeight)
{
// totally out of space - new texture!
createTexture();
}
Glyph *g = new Glyph;
g->texture = 0;
g->spacing = gd->getAdvance();
memset(g->vertices, 0, sizeof(GlyphVertex) * 4);
// don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
if (w > 0 && h > 0)
{
const GLuint t = textures.back();
gl.bindTexture(t);
glTexSubImage2D(GL_TEXTURE_2D,
0,
textureX,
textureY,
w, h,
(type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
GL_UNSIGNED_BYTE,
gd->getData());
g->texture = t;
const GlyphVertex verts[4] = {
{ 0.0f, 0.0f, float(textureX)/float(textureWidth), float(textureY)/float(textureHeight)},
{ 0.0f, float(h), float(textureX)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), float(h), float(textureX+w)/float(textureWidth), float(textureY+h)/float(textureHeight)},
{float(w), 0.0f, float(textureX+w)/float(textureWidth), float(textureY)/float(textureHeight)},
};
// copy vertex data to the glyph and set proper bearing
for (int i = 0; i < 4; i++)
{
g->vertices[i] = verts[i];
g->vertices[i].x += gd->getBearingX();
g->vertices[i].y -= gd->getBearingY();
}
}
if (w > 0)
textureX += (w + TEXTURE_PADDING);
if (h > 0)
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
delete gd;
glyphs[glyph] = g;
return g;
}
Font::Glyph *Font::findGlyph(uint32 glyph)
{
auto it = glyphs.find(glyph);
if (it != glyphs.end())
return it->second;
else
return addGlyph(glyph);
}
float Font::getHeight() const
{
return static_cast<float>(height);
}
void Font::print(const std::string &text, float x, float y, float extra_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
// Spacing counter and newline handling.
float dx = 0.0f;
float dy = 0.0f;
float lineheight = getBaseline();
// Keeps track of when we need to switch textures in our vertex array.
std::vector<GlyphArrayDrawInfo> glyphinfolist;
// Pre-allocate space for the maximum possible number of vertices.
std::vector<GlyphVertex> glyphverts;
glyphverts.reserve(text.length() * 4);
int vertexcount = 0;
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
{
uint32 g = *i++;
if (g == '\n')
{
// Wrap newline, but do not print it.
dy += floorf(getHeight() * getLineHeight() + 0.5f);
dx = 0.0f;
continue;
}
Glyph *glyph = findGlyph(g);
if (glyph->texture != 0)
{
// Copy the vertices and set their proper relative positions.
for (int j = 0; j < 4; j++)
{
glyphverts.push_back(glyph->vertices[j]);
glyphverts.back().x += dx;
glyphverts.back().y += dy + lineheight;
}
// Check if glyph texture has changed since the last iteration.
if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
{
// keep track of each sub-section of the string whose glyphs use different textures than the previous section
GlyphArrayDrawInfo gdrawinfo;
gdrawinfo.startvertex = vertexcount;
gdrawinfo.vertexcount = 0;
gdrawinfo.texture = glyph->texture;
glyphinfolist.push_back(gdrawinfo);
}
vertexcount += 4;
glyphinfolist.back().vertexcount += 4;
}
// Advance the x position for the next glyph.
dx += glyph->spacing;
// Account for extra spacing given to space characters.
if (g == ' ' && extra_spacing != 0.0f)
dx = floorf(dx + extra_spacing);
}
}
catch (utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
if (vertexcount <= 0 || glyphinfolist.size() == 0)
return;
// Sort glyph draw info list by texture first, and quad position in memory
// second (using the struct's < operator).
std::sort(glyphinfolist.begin(), glyphinfolist.end());
std::vector<uint16> indices;
int indicescount = 0;
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{
if ((it->vertexcount / 4) * 6 > indicescount)
indicescount = (it->vertexcount / 4) * 6;
}
indices.reserve(indicescount);
for (int i = 0; i < indicescount / 6; i++)
{
// First triangle.
indices.push_back(i * 4 + 0);
indices.push_back(i * 4 + 1);
indices.push_back(i * 4 + 2);
// Second triangle.
indices.push_back(i * 4 + 0);
indices.push_back(i * 4 + 2);
indices.push_back(i * 4 + 3);
}
gl.matrices.transform.push(gl.matrices.transform.top());
Matrix t;
t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.top() *= t;
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.prepareDraw();
// We need to draw a new vertex array for every section of the string which
// uses a different texture than the previous section.
for (auto it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
{
gl.bindTexture(it->texture);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[it->startvertex].s);
glDrawElements(GL_TRIANGLES, (it->vertexcount / 4) * 6, GL_UNSIGNED_SHORT, &indices[0]);
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
}
int Font::getWidth(const std::string &str)
{
if (str.size() == 0) return 0;
std::istringstream iss(str);
std::string line;
Glyph *g;
int max_width = 0;
while (getline(iss, line, '\n'))
{
int width = 0;
try
{
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
while (i != end)
{
uint32 c = *i++;
g = findGlyph(c);
width += static_cast<int>(g->spacing * mSpacing);
}
}
catch(utf8::exception &e)
{
throw love::Exception("Decoding error: %s", e.what());
}
if (width > max_width)
max_width = width;
}
return max_width;
}
int Font::getWidth(char character)
{
Glyph *g = findGlyph(character);
return g->spacing;
}
std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int *max_width, std::vector<bool> *wrappedlines)
{
using namespace std;
const float width_space = static_cast<float>(getWidth(' '));
vector<string> lines_to_draw;
int maxw = 0;
//split text at newlines
istringstream iss(text);
string line;
ostringstream string_builder;
while (getline(iss, line, '\n'))
{
// split line into words
vector<string> words;
istringstream word_iss(line);
copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
back_inserter< vector<string> >(words));
// put words back together until a wrap occurs
float width = 0.0f;
float oldwidth = 0.0f;
string_builder.str("");
vector<string>::const_iterator word_iter, wend = words.end();
for (word_iter = words.begin(); word_iter != wend; ++word_iter)
{
const string &word = *word_iter;
width += getWidth(word);
// on wordwrap, push line to line buffer and clear string builder
if (width > wrap && oldwidth > 0)
{
int realw = (int) width;
// remove trailing space
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
string_builder.str("");
width = static_cast<float>(getWidth(word));
realw -= (int) width;
if (realw > maxw)
maxw = realw;
// Indicate that this line was automatically wrapped.
if (wrappedlines)
wrappedlines->push_back(true);
}
string_builder << word << " ";
width += width_space;
oldwidth = width;
}
// push last line
if (width > maxw)
maxw = (int) width;
string tmp = string_builder.str();
lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
// Indicate that this line was not automatically wrapped.
if (wrappedlines)
wrappedlines->push_back(false);
}
if (max_width)
*max_width = maxw;
return lines_to_draw;
}
void Font::setLineHeight(float height)
{
this->lineHeight = height;
}
float Font::getLineHeight() const
{
return lineHeight;
}
void Font::setSpacing(float amount)
{
mSpacing = amount;
}
float Font::getSpacing() const
{
return mSpacing;
}
void Font::setFilter(const Image::Filter &f)
{
filter = f;
for (auto it = textures.begin(); it != textures.end(); ++it)
{
gl.bindTexture(*it);
filter.anisotropy = gl.setTextureFilter(f);
}
}
const Image::Filter &Font::getFilter()
{
return filter;
}
bool Font::loadVolatile()
{
createTexture();
return true;
}
void Font::unloadVolatile()
{
// nuke everything from orbit
std::map<uint32, Glyph *>::iterator it = glyphs.begin();
Glyph *g;
while (it != glyphs.end())
{
g = it->second;
delete g;
glyphs.erase(it++);
}
std::vector<GLuint>::iterator iter = textures.begin();
while (iter != textures.end())
{
gl.deleteTexture(*iter);
iter++;
}
textures.clear();
}
int Font::getAscent() const
{
return rasterizer->getAscent();
}
int Font::getDescent() const
{
return rasterizer->getDescent();
}
float Font::getBaseline() const
{
// 1.25 is magic line height for true type fonts
return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
}
bool Font::hasGlyph(uint32 glyph) const
{
return rasterizer->hasGlyph(glyph);
}
bool Font::hasGlyphs(const std::string &text) const
{
return rasterizer->hasGlyphs(text);
}
} // opengl
} // graphics
} // love
+219
View File
@@ -0,0 +1,219 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_FONT_H
#define LOVE_GRAPHICS_OPENGL_FONT_H
// STD
#include <map>
#include <string>
#include <vector>
// LOVE
#include "common/Object.h"
#include "font/Rasterizer.h"
#include "graphics/Image.h"
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
class Font : public Object, public Volatile
{
public:
Font(love::font::Rasterizer *r, const Image::Filter &filter = Image::getDefaultFilter());
virtual ~Font();
/**
* Prints the text at the designated position with rotation and scaling.
*
* @param text A string.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param extra_spacing Additional spacing added to spaces (" ").
* @param angle The amount of rotation.
* @param sx Scale along the x axis.
* @param sy Scale along the y axis.
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x axis.
* @param ky Shear along the y axis.
**/
void print(const std::string &text, float x, float y, float extra_spacing = 0.0f, float angle = 0.0f, float sx = 1.0f, float sy = 1.0f, float ox = 0.0f, float oy = 0.0f, float kx = 0.0f, float ky = 0.0f);
/**
* Returns the height of the font.
**/
float getHeight() const;
/**
* Returns the width of the passed string.
*
* @param str A string of text.
**/
int getWidth(const std::string &str);
/**
* Returns the width of the passed character.
*
* @param character A character.
**/
int getWidth(char character);
/**
* Returns the maximal width of a wrapped string
* and optionally the number of lines
*
* @param text The input text
* @param wrap The number of pixels to wrap at
* @param max_width Optional output of the maximum width
* @param wrapped_lines Optional output indicating which lines were
* auto-wrapped. Indices correspond to indices of the returned value.
* Returns a vector with the lines.
**/
std::vector<std::string> getWrap(const std::string &text, float wrap, int *max_width = 0, std::vector<bool> *wrapped_lines = 0);
/**
* Sets the line height (which should be a number to multiply the font size by,
* example: line height = 1.2 and size = 12 means that rendered line height = 12*1.2)
* @param height The new line height.
**/
void setLineHeight(float height);
/**
* Returns the line height.
**/
float getLineHeight() const;
/**
* Sets the spacing modifier (changes the spacing between the characters the
* same way that the line height does [multiplication]).
* Note: The spacing must be set BEFORE the font is loaded to have any effect.
* @param amount The amount of modification.
**/
void setSpacing(float amount);
/**
* Returns the spacing modifier.
**/
float getSpacing() const;
void setFilter(const Image::Filter &f);
const Image::Filter &getFilter();
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
// Extra font metrics
int getAscent() const;
int getDescent() const;
float getBaseline() const;
bool hasGlyph(uint32 glyph) const;
bool hasGlyphs(const std::string &text) const;
private:
enum FontType
{
FONT_TRUETYPE = 1,
FONT_IMAGE,
FONT_UNKNOWN
};
struct GlyphVertex
{
float x, y;
float s, t;
};
struct Glyph
{
GLuint texture;
int spacing;
GlyphVertex vertices[4];
};
// used to determine when to change textures in the vertex array generated when printing text
struct GlyphArrayDrawInfo
{
GLuint texture;
int startvertex;
int vertexcount;
// used when sorting with std::sort
// sorts by texture first (binding textures is expensive) and relative position in memory second
bool operator < (const GlyphArrayDrawInfo &other) const
{
if (texture != other.texture)
return texture < other.texture;
else
return startvertex < other.startvertex;
};
};
bool initializeTexture(GLint format);
void createTexture();
Glyph *addGlyph(uint32 glyph);
Glyph *findGlyph(uint32 glyph);
love::font::Rasterizer *rasterizer;
int height;
float lineHeight;
float mSpacing; // modifies the spacing by multiplying it with this value
int textureSizeIndex;
int textureWidth;
int textureHeight;
// vector of packed textures
std::vector<GLuint> textures;
// maps glyphs to glyph texture information
std::map<uint32, Glyph *> glyphs;
FontType type;
Image::Filter filter;
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;
}; // Font
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_FONT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,479 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_GRAPHICS_H
#define LOVE_GRAPHICS_OPENGL_GRAPHICS_H
// STD
#include <iostream>
#include <stack>
#include <vector>
// OpenGL
#include "OpenGL.h"
// LOVE
#include "graphics/Graphics.h"
#include "graphics/Color.h"
#include "image/Image.h"
#include "image/ImageData.h"
#include "window/Window.h"
#include "Font.h"
#include "Image.h"
#include "graphics/Quad.h"
#include "SpriteBatch.h"
#include "ParticleSystem.h"
#include "Canvas.h"
#include "Shader.h"
#include "Mesh.h"
namespace love
{
namespace graphics
{
namespace opengl
{
// During display mode changing, certain
// variables about the OpenGL context are
// lost.
struct DisplayState
{
// Colors.
Color color;
Color backgroundColor;
// Blend mode.
Graphics::BlendMode blendMode;
// Line.
Graphics::LineStyle lineStyle;
Graphics::LineJoin lineJoin;
// Point.
float pointSize;
Graphics::PointStyle pointStyle;
// Scissor.
bool scissor;
OpenGL::Viewport scissorBox;
// Color mask.
bool colorMask[4];
// Default values.
DisplayState()
{
color.set(255,255,255,255);
backgroundColor.set(0, 0, 0, 255);
blendMode = Graphics::BLEND_ALPHA;
lineStyle = Graphics::LINE_SMOOTH;
lineJoin = Graphics::LINE_JOIN_MITER;
pointSize = 1.0f;
pointStyle = Graphics::POINT_SMOOTH;
scissor = false;
colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true;
}
};
class Graphics : public love::graphics::Graphics
{
public:
Graphics();
virtual ~Graphics();
// Implements Module.
const char *getName() const;
DisplayState saveState();
void restoreState(const DisplayState &s);
virtual void setViewportSize(int width, int height);
virtual bool setMode(int width, int height);
virtual void unSetMode();
/**
* Resets the current color, background color,
* line style, and so forth. (This will be called
* when the game reloads.
**/
void reset();
/**
* Clears the screen.
**/
void clear();
/**
* Flips buffers. (Rendered geometry is presented on screen).
**/
void present();
/**
* Gets the width of the current graphics viewport.
**/
int getWidth() const;
/**
* Gets the height of the current graphics viewport.
**/
int getHeight() const;
/**
* True if a graphics viewport is set.
**/
bool isCreated() const;
/**
* Scissor defines a box such that everything outside that box is discarded and not drawn.
* Scissoring is automatically enabled.
* @param x The x-coordinate of the top-left corner.
* @param y The y-coordinate of the top-left corner.
* @param width The width of the box.
* @param height The height of the box.
**/
void setScissor(int x, int y, int width, int height);
/**
* Clears any scissor that has been created.
**/
void setScissor();
/**
* This native Lua function gets the current scissor box in the order of:
* x, y, width, height
**/
int getScissor(lua_State *L) const;
/**
* Enables the stencil buffer and set stencil function to fill it
*/
void defineStencil();
/**
* Set stencil function to mask the following drawing calls using
* the current stencil buffer
* @param invert Invert the mask, i.e. draw everywhere expect where
* the mask is defined.
*/
void useStencil(bool invert = false);
/**
* Disables the stencil buffer
*/
void discardStencil();
/**
* Gets the maximum supported width or height of Images and Canvases on this
* system.
**/
int getMaxImageSize() const;
/**
* Creates an Image object with padding and/or optimization.
**/
Image *newImage(love::image::ImageData *data);
Image *newImage(love::image::CompressedData *cdata);
Quad *newQuad(Quad::Viewport v, float sw, float sh);
/**
* Creates a Font object.
**/
Font *newFont(love::font::Rasterizer *data, const Image::Filter &filter = Image::Filter());
SpriteBatch *newSpriteBatch(Image *image, int size, int usage);
ParticleSystem *newParticleSystem(Image *image, int size);
Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
Shader *newShader(const Shader::ShaderSources &sources);
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
/**
* Sets the foreground color.
* @param c The new foreground color.
**/
void setColor(const Color &c);
/**
* Gets current color.
**/
Color getColor() const;
/**
* Sets the background Color.
**/
void setBackgroundColor(const Color &c);
/**
* Gets the current background color.
**/
Color getBackgroundColor() const;
/**
* Sets the current font.
* @param font A Font object.
**/
void setFont(Font *font);
/**
* Gets the current Font, or nil if none.
**/
Font *getFont() const;
/**
* Sets the enabled color components when rendering.
**/
void setColorMask(bool r, bool g, bool b, bool a);
/**
* Gets the current color mask.
* Returns an array of 4 booleans representing the mask.
**/
const bool *getColorMask() const;
/**
* Sets the current blend mode.
**/
void setBlendMode(BlendMode mode);
/**
* Gets the current blend mode.
**/
BlendMode getBlendMode() const;
/**
* Sets the default filter for images, canvases, and fonts.
**/
void setDefaultFilter(const Image::Filter &f);
/**
* Gets the default filter for images, canvases, and fonts.
**/
const Image::Filter &getDefaultFilter() const;
/**
* Default Image mipmap filter mode and sharpness values.
**/
void setDefaultMipmapFilter(Image::FilterMode filter, float sharpness);
void getDefaultMipmapFilter(Image::FilterMode *filter, float *sharpness) const;
/**
* Sets the line width.
* @param width The new width of the line.
**/
void setLineWidth(float width);
/**
* Sets the line style.
* @param style LINE_ROUGH or LINE_SMOOTH.
**/
void setLineStyle(LineStyle style);
/**
* Sets the line style.
* @param style LINE_ROUGH or LINE_SMOOTH.
**/
void setLineJoin(LineJoin style);
/**
* Gets the line width.
**/
float getLineWidth() const;
/**
* Gets the line style.
**/
LineStyle getLineStyle() const;
/**
* Gets the line style.
**/
LineJoin getLineJoin() const;
/**
* Sets the size of points.
**/
void setPointSize(float size);
/**
* Sets the style of points.
* @param style POINT_SMOOTH or POINT_ROUGH.
**/
void setPointStyle(PointStyle style);
/**
* Gets the point size.
**/
float getPointSize() const;
/**
* Gets the point style.
**/
PointStyle getPointStyle() const;
/**
* Gets the maximum point size supported.
* This may vary from computer to computer.
**/
int getMaxPointSize() const;
/**
* Draws text at the specified coordinates, with rotation and
* scaling along both axes.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param angle The amount of rotation.
* @param sx The scale factor along the x-axis. (1 = normal).
* @param sy The scale factor along the y-axis. (1 = normal).
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
void print(const std::string &str, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Draw formatted text on screen at the specified coordinates.
*
* @param str A string of text.
* @param x The x-coordinate.
* @param y The y-coordinate.
* @param wrap The maximum width of the text area.
* @param align Where to align the text.
* @param angle The amount of rotation.
* @param sx The scale factor along the x-axis. (1 = normal).
* @param sy The scale factor along the y-axis. (1 = normal).
* @param ox The origin offset along the x-axis.
* @param oy The origin offset along the y-axis.
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
void printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Draws a point at (x,y).
* @param x Point along x-axis.
* @param y Point along y-axis.
**/
void point(float x, float y);
/**
* Draws a series of lines connecting the given vertices.
* @param coords Vertex components (x1, y1, ..., xn, yn). If x1,y1 == xn,yn the line will be drawn closed.
* @param count Number of items in the array, i.e. count = 2 * n
**/
void polyline(const float *coords, size_t count);
/**
* Draws a rectangle.
* @param x Position along x-axis for top-left corner.
* @param y Position along y-axis for top-left corner.
* @param w The width of the rectangle.
* @param h The height of the rectangle.
**/
void rectangle(DrawMode mode, float x, float y, float w, float h);
/**
* Draws a circle using the specified arguments.
* @param mode The mode of drawing (line/filled).
* @param x X-coordinate.
* @param y Y-coordinate.
* @param radius Radius of the circle.
* @param points Number of points to use to draw the circle.
**/
void circle(DrawMode mode, float x, float y, float radius, int points = 10);
/**
* Draws an arc using the specified arguments.
* @param mode The mode of drawing (line/filled).
* @param x X-coordinate.
* @param y Y-coordinate.
* @param radius Radius of the arc.
* @param angle1 The angle at which the arc begins.
* @param angle2 The angle at which the arc terminates.
* @param points Number of points to use to draw the arc.
**/
void arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points = 10);
/**
* Draws a polygon with an arbitrary number of vertices.
* @param mode The type of drawing (line/filled).
* @param coords Vertex components (x1, y1, x2, y2, etc.)
* @param count Coord array size
**/
void polygon(DrawMode mode, const float *coords, size_t count);
/**
* Creates a screenshot of the view and saves it to the default folder.
* @param image The love.image module.
* @param copyAlpha If the alpha channel should be copied or set to full opacity (255).
**/
love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true);
/**
* Returns a string containing system-dependent renderer information.
* Returned string can vary greatly between systems! Do not rely on it for
* anything!
* @param infotype The type of information to return.
**/
std::string getRendererInfo(Graphics::RendererInfo infotype) const;
void push();
void pop();
void rotate(float r);
void scale(float x, float y = 1.0f);
void translate(float x, float y);
void shear(float kx, float ky);
void origin();
private:
Font *currentFont;
love::window::Window *currentWindow;
std::vector<double> pixel_size_stack; // stores current size of a pixel (needed for line drawing)
LineStyle lineStyle;
LineJoin lineJoin;
float lineWidth;
size_t matrixLimit;
bool colorMask[4];
int width;
int height;
bool created;
DisplayState savedState;
}; // Graphics
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_GRAPHICS_H
@@ -0,0 +1,648 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Image.h"
// STD
#include <cstring> // For memcpy
#include <algorithm> // for min/max
namespace love
{
namespace graphics
{
namespace opengl
{
float Image::maxMipmapSharpness = 0.0f;
Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE;
float Image::defaultMipmapSharpness = 0.0f;
Image::Image(love::image::ImageData *data)
: data(data)
, cdata(0)
, width(data->getWidth())
, height(data->getHeight())
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(false)
, usingDefaultTexture(false)
{
data->retain();
preload();
}
Image::Image(love::image::CompressedData *cdata)
: data(0)
, cdata(cdata)
, width(cdata->getWidth(0))
, height(cdata->getHeight(0))
, paddedWidth(width)
, paddedHeight(height)
, texture(0)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(true)
, usingDefaultTexture(false)
{
cdata->retain();
preload();
}
Image::~Image()
{
if (data != 0)
data->release();
if (cdata != 0)
cdata->release();
unload();
}
int Image::getWidth() const
{
return width;
}
int Image::getHeight() const
{
return height;
}
const Vertex *Image::getVertices() const
{
return vertices;
}
love::image::ImageData *Image::getImageData() const
{
return data;
}
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
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
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
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
drawv(t, quad->getVertices());
}
void Image::predraw() const
{
bind();
if (width != paddedWidth || height != paddedHeight)
{
// NPOT image padded to POT size, so the texcoords should be scaled.
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(float(width) / float(paddedWidth), float(height) / float(paddedHeight), 0.0f);
glMatrixMode(GL_MODELVIEW);
}
}
void Image::postdraw() const
{
if (width != paddedWidth || height != paddedHeight)
{
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
}
void Image::uploadCompressedMipmaps()
{
if (!isCompressed() || !cdata || !hasCompressedTextureSupport(cdata->getFormat()))
return;
bind();
int count = cdata->getMipmapCount();
// We have to inform OpenGL if the image doesn't have all mipmap levels.
if (GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0 || GLAD_APPLE_texture_max_level)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
}
else if (cdata->getWidth(count-1) > 1 || cdata->getHeight(count-1) > 1)
{
// Telling OpenGL to ignore certain levels isn't always supported.
throw love::Exception("Cannot load mipmaps: "
"compressed image does not have all required levels.");
}
for (int i = 1; i < count; i++)
{
glCompressedTexImage2D(GL_TEXTURE_2D,
i,
getCompressedFormat(cdata->getFormat()),
cdata->getWidth(i),
cdata->getHeight(i),
0,
GLsizei(cdata->getSize(i)),
cdata->getData(i));
}
}
void Image::createMipmaps()
{
// Only valid for Images created with ImageData.
if (!data || isCompressed())
return;
if (!hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system.");
// Some old drivers claim support for NPOT textures, but fail when creating
// mipmaps. We can't detect which systems will do this, so we fail gracefully
// for all NPOT images.
int w = int(width), h = int(height);
if (w != next_p2(w) || h != next_p2(h))
{
throw love::Exception("Cannot create mipmaps: "
"image does not have power of two dimensions.");
}
bind();
// Prevent other threads from changing the ImageData while we upload it.
love::thread::Lock lock(data->getMutex());
if (hasNpot() && (GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
{
if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
{
// AMD/ATI drivers have several bugs when generating mipmaps,
// re-uploading the entire base image seems to be required.
uploadTexture();
// More bugs: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
glEnable(GL_TEXTURE_2D);
}
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0,
0,
(GLsizei)width,
(GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::checkMipmapsCreated()
{
if (mipmapsCreated || filter.mipmap == FILTER_NONE || usingDefaultTexture)
return;
if (isCompressed() && cdata && hasCompressedTextureSupport(cdata->getFormat()))
uploadCompressedMipmaps();
else if (data)
createMipmaps();
else
return;
mipmapsCreated = true;
}
void Image::setFilter(const Image::Filter &f)
{
filter = f;
// We don't want filtering or (attempted) mipmaps on the default texture.
if (usingDefaultTexture)
{
filter.mipmap = FILTER_NONE;
filter.min = filter.mag = FILTER_NEAREST;
}
bind();
filter.anisotropy = gl.setTextureFilter(filter);
checkMipmapsCreated();
}
const Image::Filter &Image::getFilter() const
{
return filter;
}
void Image::setWrap(const Image::Wrap &w)
{
wrap = w;
bind();
gl.setTextureWrap(w);
}
const Image::Wrap &Image::getWrap() const
{
return wrap;
}
void Image::setMipmapSharpness(float sharpness)
{
if (hasMipmapSharpnessSupport())
{
// LOD bias has the range (-maxbias, maxbias)
mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
bind();
// negative bias is sharper
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness);
}
else
mipmapSharpness = 0.0f;
}
float Image::getMipmapSharpness() const
{
return mipmapSharpness;
}
void Image::bind() const
{
if (texture == 0)
return;
gl.bindTexture(texture);
}
void Image::preload()
{
memset(vertices, 255, sizeof(Vertex)*4);
vertices[0].x = 0;
vertices[0].y = 0;
vertices[1].x = 0;
vertices[1].y = (float) height;
vertices[2].x = (float) width;
vertices[2].y = (float) height;
vertices[3].x = (float) width;
vertices[3].y = 0;
vertices[0].s = 0;
vertices[0].t = 0;
vertices[1].s = 0;
vertices[1].t = 1;
vertices[2].s = 1;
vertices[2].t = 1;
vertices[3].s = 1;
vertices[3].t = 0;
filter = getDefaultFilter();
filter.mipmap = defaultMipmapFilter;
}
bool Image::load()
{
return loadVolatile();
}
void Image::unload()
{
return unloadVolatile();
}
bool Image::loadVolatile()
{
if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
{
const char *str;
if (image::CompressedData::getConstant(cdata->getFormat(), str))
{
throw love::Exception("Cannot create image: "
"%s compressed images are not supported on this system.", str);
}
else
throw love::Exception("cannot create image: format is not supported on this system.");
}
if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
glGenTextures(1, &texture);
gl.bindTexture(texture);
filter.anisotropy = gl.setTextureFilter(filter);
gl.setTextureWrap(wrap);
setMipmapSharpness(mipmapSharpness);
paddedWidth = width;
paddedHeight = height;
if (!hasNpot())
{
// NPOT textures will be padded to POT dimensions if necessary.
paddedWidth = next_p2(width);
paddedHeight = next_p2(height);
}
// Use a default texture if the size is too big for the system.
if (paddedWidth > gl.getMaxTextureSize() || paddedHeight > gl.getMaxTextureSize())
{
uploadDefaultTexture();
return true;
}
// Mutex lock will potentially cover texture loading and mipmap creation.
love::thread::EmptyLock lock;
if (data)
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
GLenum glerr = glGetError();
if (glerr != GL_NO_ERROR)
throw love::Exception("Cannot create image (error code 0x%x)", glerr);
usingDefaultTexture = false;
mipmapsCreated = false;
checkMipmapsCreated();
return true;
}
void Image::uploadTexturePadded()
{
if (isCompressed() && cdata)
{
// Padded textures don't really work if they're compressed...
throw love::Exception("Cannot create image: "
"compressed NPOT images are not supported on this system.");
}
else if (data)
{
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
(GLsizei)paddedWidth,
(GLsizei)paddedHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
0);
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
(GLsizei)width,
(GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::uploadTexture()
{
if (isCompressed() && cdata)
{
GLenum format = getCompressedFormat(cdata->getFormat());
glCompressedTexImage2D(GL_TEXTURE_2D,
0,
format,
cdata->getWidth(0),
cdata->getHeight(0),
0,
GLsizei(cdata->getSize(0)),
cdata->getData(0));
}
else if (data)
{
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
(GLsizei)width,
(GLsizei)height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
}
void Image::unloadVolatile()
{
// Delete the hardware texture.
if (texture != 0)
{
gl.deleteTexture(texture);
texture = 0;
}
}
bool Image::refresh()
{
// No effect if the texture hasn't been created yet.
if (texture == 0)
return false;
if (usingDefaultTexture)
{
uploadDefaultTexture();
return true;
}
// We want this lock to potentially cover mipmap creation as well.
love::thread::EmptyLock lock;
bind();
if (data && !isCompressed())
lock.setLock(data->getMutex());
while (glGetError() != GL_NO_ERROR); // Clear errors.
if (hasNpot() || (width == paddedWidth && height == paddedHeight))
uploadTexture();
else
uploadTexturePadded();
if (glGetError() != GL_NO_ERROR)
uploadDefaultTexture();
else
usingDefaultTexture = false;
mipmapsCreated = false;
checkMipmapsCreated();
return true;
}
void Image::uploadDefaultTexture()
{
usingDefaultTexture = true;
bind();
setFilter(filter);
// A nice friendly checkerboard to signify invalid textures...
GLubyte px[] = {0xFF,0xFF,0xFF,0xFF, 0xC0,0xC0,0xC0,0xFF,
0xC0,0xC0,0xC0,0xFF, 0xFF,0xFF,0xFF,0xFF};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
}
void Image::drawv(const Matrix &t, const Vertex *v) const
{
predraw();
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
gl.prepareDraw();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.matrices.transform.pop();
postdraw();
}
void Image::setDefaultMipmapSharpness(float sharpness)
{
defaultMipmapSharpness = sharpness;
}
float Image::getDefaultMipmapSharpness()
{
return defaultMipmapSharpness;
}
void Image::setDefaultMipmapFilter(Image::FilterMode f)
{
defaultMipmapFilter = f;
}
Image::FilterMode Image::getDefaultMipmapFilter()
{
return defaultMipmapFilter;
}
bool Image::isCompressed() const
{
return compressed;
}
GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
{
switch (format)
{
case image::CompressedData::FORMAT_DXT1:
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case image::CompressedData::FORMAT_DXT3:
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case image::CompressedData::FORMAT_DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case image::CompressedData::FORMAT_BC4:
return GL_COMPRESSED_RED_RGTC1;
case image::CompressedData::FORMAT_BC4s:
return GL_COMPRESSED_SIGNED_RED_RGTC1;
case image::CompressedData::FORMAT_BC5:
return GL_COMPRESSED_RG_RGTC2;
case image::CompressedData::FORMAT_BC5s:
return GL_COMPRESSED_SIGNED_RG_RGTC2;
default:
return GL_RGBA;
}
}
bool Image::hasNpot()
{
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_2_0 || GLAD_ARB_texture_non_power_of_two;
}
bool Image::hasAnisotropicFilteringSupport()
{
return GLAD_EXT_texture_filter_anisotropic;
}
bool Image::hasMipmapSupport()
{
return GLAD_ES_VERSION_2_0 || GLAD_VERSION_1_4 || GLAD_SGIS_generate_mipmap;
}
bool Image::hasMipmapSharpnessSupport()
{
return GLAD_VERSION_1_4;
}
bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
{
switch (format)
{
case image::CompressedData::FORMAT_DXT1:
case image::CompressedData::FORMAT_DXT3:
case image::CompressedData::FORMAT_DXT5:
return GLAD_EXT_texture_compression_s3tc;
case image::CompressedData::FORMAT_BC4:
case image::CompressedData::FORMAT_BC4s:
case image::CompressedData::FORMAT_BC5:
case image::CompressedData::FORMAT_BC5s:
return (GLAD_VERSION_3_0 || GLAD_ARB_texture_compression_rgtc || GLAD_EXT_texture_compression_rgtc);
default:
break;
}
return false;
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,220 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_IMAGE_H
#define LOVE_GRAPHICS_OPENGL_IMAGE_H
// LOVE
#include "common/config.h"
#include "common/Matrix.h"
#include "common/Vector.h"
#include "common/math.h"
#include "image/ImageData.h"
#include "image/CompressedData.h"
#include "graphics/Image.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* A drawable image based on OpenGL-textures. This class takes ImageData
* objects and create textures on the GPU for fast drawing.
*
* @author Anders Ruud
**/
class Image : public love::graphics::Image
{
public:
/**
* Creates a new Image. Not that anything is ready to use
* before load is called.
*
* @param data The data from which to load the image.
**/
Image(love::image::ImageData *data);
/**
* Creates a new Image with compressed image data.
*
* @param cdata The compressed data from which to load the image.
**/
Image(love::image::CompressedData *cdata);
/**
* Destructor. Deletes the hardware texture and other resources.
**/
virtual ~Image();
int getWidth() const;
int getHeight() const;
const Vertex *getVertices() const;
love::image::ImageData *getImageData() const;
love::image::CompressedData *getCompressedData() const;
/**
* @copydoc Drawable::draw()
**/
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* @copydoc DrawQable::drawq()
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* 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.
**/
void predraw() const;
void postdraw() const;
/**
* Sets the filter mode.
* @param f The filter mode.
**/
void setFilter(const Image::Filter &f);
const Image::Filter &getFilter() const;
void setWrap(const Image::Wrap &w);
const Image::Wrap &getWrap() const;
void setMipmapSharpness(float sharpness);
float getMipmapSharpness() const;
/**
* Whether this Image is using a compressed texture (via CompressedData).
**/
bool isCompressed() const;
void bind() const;
bool load();
void unload();
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
/**
* Re-uploads the ImageData or CompressedData associated with this Image to
* the GPU, allowing situations where lovers modify an ImageData after image
* creation from the ImageData, and apply the changes with Image:refresh().
**/
bool refresh();
static void setDefaultMipmapSharpness(float sharpness);
static float getDefaultMipmapSharpness();
static void setDefaultMipmapFilter(FilterMode f);
static FilterMode getDefaultMipmapFilter();
static bool hasNpot();
static bool hasAnisotropicFilteringSupport();
static bool hasMipmapSupport();
static bool hasMipmapSharpnessSupport();
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
private:
void uploadDefaultTexture();
void drawv(const Matrix &t, const Vertex *v) const;
friend class Shader;
GLuint getTextureName() const
{
return texture;
}
// The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture.
love::image::ImageData *data;
// Or the Compressed Image Data from which the texture is created. May be
// null if raw ImageData was used to create the texture.
love::image::CompressedData *cdata;
// Width and height of the hardware texture.
int width, height;
// Real dimensions of the texture, if it was auto-padded to POT size.
int paddedWidth, paddedHeight;
// OpenGL texture identifier.
GLuint texture;
// The source vertices of the image.
Vertex vertices[4];
// Mipmap texture LOD bias (sharpness) value.
float mipmapSharpness;
// True if mipmaps have been created for this Image.
bool mipmapsCreated;
// Whether this Image is using a compressed texture.
bool compressed;
// True if the image wasn't able to be properly created and it had to fall
// back to a default texture.
bool usingDefaultTexture;
// The image's filter mode
Image::Filter filter;
// The image's wrap mode
Image::Wrap wrap;
void preload();
void uploadTexturePadded();
void uploadTexture();
void uploadCompressedMipmaps();
void createMipmaps();
void checkMipmapsCreated();
static float maxMipmapSharpness;
static FilterMode defaultMipmapFilter;
static float defaultMipmapSharpness;
GLenum getCompressedFormat(image::CompressedData::Format format) const;
}; // Image
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_IMAGE_H
@@ -0,0 +1,330 @@
/**
* Copyright (c) 2006-2013 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 "Mesh.h"
#include "common/Matrix.h"
#include "common/Exception.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
: vbo(nullptr)
, vertex_count(0)
, ibo(nullptr)
, element_count(0)
, draw_mode(mode)
, image(nullptr)
, colors_enabled(false)
{
setVertices(verts);
}
Mesh::~Mesh()
{
delete vbo;
delete ibo;
}
void Mesh::setVertices(const std::vector<Vertex> &verts)
{
if (verts.size() < 3)
throw love::Exception("At least 3 vertices are required.");
size_t size = sizeof(Vertex) * verts.size();
if (vbo && size > vbo->getSize())
{
delete vbo;
vbo = nullptr;
}
if (!vbo)
{
// Full memory backing because we might access the data at any time.
vbo = VertexBuffer::Create(size, GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
vertex_count = verts.size();
VertexBuffer::Bind vbo_bind(*vbo);
VertexBuffer::Mapper vbo_mapper(*vbo);
// Fill the buffer with the vertices.
memcpy(vbo_mapper.get(), &verts[0], size);
}
const Vertex *Mesh::getVertices() const
{
if (vbo)
{
VertexBuffer::Bind vbo_bind(*vbo);
return (Vertex *) vbo->map();
}
return nullptr;
}
void Mesh::setVertex(size_t index, const Vertex &v)
{
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw. This lets us coalesce the
// buffer transfer calls into just one.
Vertex *vertices = (Vertex *) vbo->map();
vertices[index] = v;
}
Vertex Mesh::getVertex(size_t index) const
{
if (index >= vertex_count)
throw love::Exception("Invalid vertex index: %ld", index + 1);
VertexBuffer::Bind vbo_bind(*vbo);
// We unmap the vertex buffer in Mesh::draw.
Vertex *vertices = (Vertex *) vbo->map();
return vertices[index];
}
size_t Mesh::getVertexCount() const
{
return vertex_count;
}
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);
}
size_t size = sizeof(uint32) * map.size();
if (ibo && size > ibo->getSize())
{
delete ibo;
ibo = nullptr;
}
if (!ibo && size > 0)
{
// Full memory backing because we might access the data at any time.
ibo = VertexBuffer::Create(size, GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, VertexBuffer::BACKING_FULL);
}
element_count = map.size();
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
VertexBuffer::Mapper ibo_map(*ibo);
// Fill the buffer.
memcpy(ibo_map.get(), &map[0], size);
}
}
const uint32 *Mesh::getVertexMap() const
{
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
return (uint32 *) ibo->map();
}
return 0;
}
size_t Mesh::getVertexMapCount() const
{
return element_count;
}
void Mesh::setImage(Image *img)
{
img->retain();
if (image)
image->release();
image = img;
}
void Mesh::setImage()
{
if (image)
image->release();
image = nullptr;
}
Image *Mesh::getImage() const
{
return image;
}
void Mesh::setDrawMode(Mesh::DrawMode mode)
{
draw_mode = mode;
}
Mesh::DrawMode Mesh::getDrawMode() const
{
return draw_mode;
}
void Mesh::setVertexColors(bool enable)
{
colors_enabled = enable;
}
bool Mesh::hasVertexColors() const
{
return colors_enabled;
}
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
const size_t pos_offset = offsetof(Vertex, x);
const size_t tex_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (vertex_count == 0)
return;
if (image)
image->predraw();
else
gl.bindTexture(0);
Matrix m;
m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= m;
VertexBuffer::Bind vbo_bind(*vbo);
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
vbo->unmap();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(pos_offset));
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), vbo->getPointer(tex_offset));
if (hasVertexColors())
{
// Per-vertex colors.
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
}
gl.prepareDraw();
GLenum mode = getGLDrawMode(draw_mode);
if (ibo && element_count > 0)
{
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));
}
else
{
// Normal non-indexed drawing (no custom vertex map.)
glDrawArrays(mode, 0, vertex_count);
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
if (hasVertexColors())
{
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
// Using the color array leaves the GL constant color undefined.
gl.setColor(gl.getColor());
}
gl.matrices.transform.pop();
if (image)
image->postdraw();
}
GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
{
switch (mode)
{
case DRAW_MODE_FAN:
return GL_TRIANGLE_FAN;
case DRAW_MODE_STRIP:
return GL_TRIANGLE_STRIP;
case DRAW_MODE_TRIANGLES:
return GL_TRIANGLES;
case DRAW_MODE_POINTS:
return GL_POINTS;
default:
break;
}
return GL_TRIANGLES;
}
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
{
return drawModes.find(in, out);
}
bool Mesh::getConstant(Mesh::DrawMode in, const char *&out)
{
return drawModes.find(in, out);
}
StringMap<Mesh::DrawMode, Mesh::DRAW_MODE_MAX_ENUM>::Entry Mesh::drawModeEntries[] =
{
{"fan", Mesh::DRAW_MODE_FAN},
{"strip", Mesh::DRAW_MODE_STRIP},
{"triangles", Mesh::DRAW_MODE_TRIANGLES},
{"points", Mesh::DRAW_MODE_POINTS},
};
StringMap<Mesh::DrawMode, Mesh::DRAW_MODE_MAX_ENUM> Mesh::drawModes(Mesh::drawModeEntries, sizeof(Mesh::drawModeEntries));
} // opengl
} // graphics
} // love
+175
View File
@@ -0,0 +1,175 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_MESH_H
#define LOVE_GRAPHICS_OPENGL_MESH_H
// LOVE
#include "common/int.h"
#include "common/math.h"
#include "common/StringMap.h"
#include "graphics/Drawable.h"
#include "Image.h"
#include "VertexBuffer.h"
// C++
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* Holds and draws arbitrary vertex geometry.
* Each vertex in the Mesh has a position, texture coordinate, and color.
**/
class Mesh : public Drawable
{
public:
// How the Mesh's vertices are used when drawing.
// http://escience.anu.edu.au/lecture/cg/surfaceModeling/image/surfaceModeling015.png
enum DrawMode
{
DRAW_MODE_FAN,
DRAW_MODE_STRIP,
DRAW_MODE_TRIANGLES,
DRAW_MODE_POINTS,
DRAW_MODE_MAX_ENUM
};
/**
* Constructor.
* @param verts The vertices to use in the Mesh.
* @param mode The draw mode to use when drawing the Mesh.
**/
Mesh(const std::vector<Vertex> &verts, DrawMode mode = DRAW_MODE_FAN);
virtual ~Mesh();
/**
* Replaces all the vertices in the Mesh with a new set of vertices.
**/
void setVertices(const std::vector<Vertex> &verts);
/**
* Gets all of the vertices in the Mesh as an array.
**/
const Vertex *getVertices() const;
/**
* Sets an individual vertex in the Mesh.
* @param index The index into the list of vertices to use.
* @param v The new vertex.
**/
void setVertex(size_t index, const Vertex &v);
Vertex getVertex(size_t index) const;
/**
* Gets the total number of vertices in the Mesh.
**/
size_t getVertexCount() const;
/**
* Sets the vertex map to use when drawing the Mesh. The vertex map
* determines the order in which vertices are used by the draw mode.
* A 0-element vector is equivalent to the default vertex map:
* {0, 1, 2, 3, 4, ...}
**/
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.
**/
const uint32 *getVertexMap() const;
/**
* Gets the total number of elements in the vertex map array.
**/
size_t getVertexMapCount() const;
/**
* Sets the Image used when drawing the Mesh.
**/
void setImage(Image *img);
/**
* Disables any Image from being used when drawing the Mesh.
**/
void setImage();
/**
* Gets the Image used when drawing the Mesh. May return null if no Image is
* set.
**/
Image *getImage() const;
/**
* Sets the draw mode used when drawing the Mesh.
**/
void setDrawMode(DrawMode mode);
DrawMode getDrawMode() 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.
**/
void setVertexColors(bool enable);
bool hasVertexColors() const;
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
private:
GLenum getGLDrawMode(DrawMode mode) const;
// Vertex buffer.
VertexBuffer *vbo;
size_t vertex_count;
// Element (vertex index) buffer, for the vertex map.
VertexBuffer *ibo;
size_t element_count;
DrawMode draw_mode;
Image *image;
// Whether the per-vertex colors are used when drawing.
bool colors_enabled;
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
}; // Mesh
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_MESH_H
@@ -0,0 +1,645 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
// LOVE
#include "common/config.h"
#include "OpenGL.h"
#include "Shader.h"
#include "common/Exception.h"
// C++
#include <algorithm>
// C
#include <cstring>
namespace love
{
namespace graphics
{
namespace opengl
{
OpenGL::OpenGL()
: contextInitialized(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, vendor(VENDOR_UNKNOWN)
, state()
{
}
bool OpenGL::initContext()
{
if (contextInitialized)
return true;
if (!gladLoadGL())
return false;
initOpenGLFunctions();
initVendor();
initMatrices();
// Store the current color so we don't have to get it through GL later.
GLfloat glcolor[4];
if (GLAD_ES_VERSION_2_0)
glGetVertexAttribfv(GLuint(ATTRIB_COLOR), GL_CURRENT_VERTEX_ATTRIB, glcolor);
else
glGetFloatv(GL_CURRENT_COLOR, glcolor);
state.color.r = glcolor[0] * 255;
state.color.g = glcolor[1] * 255;
state.color.b = glcolor[2] * 255;
state.color.a = glcolor[3] * 255;
// Same with the current clear color.
glGetFloatv(GL_COLOR_CLEAR_VALUE, glcolor);
state.clearColor.r = glcolor[0] * 255;
state.clearColor.g = glcolor[1] * 255;
state.clearColor.b = glcolor[2] * 255;
state.clearColor.a = glcolor[3] * 255;
// Get the current viewport.
glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
// And the current scissor - but we need to compensate for GL scissors
// starting at the bottom left instead of top left.
glGetIntegerv(GL_SCISSOR_BOX, (GLint *) &state.scissor.x);
state.scissor.y = state.viewport.h - (state.scissor.y + state.scissor.h);
if (GLAD_VERSION_1_0)
glGetFloatv(GL_POINT_SIZE, &state.pointSize);
else
state.pointSize = 1.0f;
// Initialize multiple texture unit support for shaders, if available.
state.textureUnits.clear();
if (Shader::isSupported())
{
GLint maxtextureunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits);
state.textureUnits.resize(maxtextureunits, 0);
GLenum curgltextureunit;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &curgltextureunit);
state.curTextureUnit = curgltextureunit - GL_TEXTURE0;
// Retrieve currently bound textures for each texture unit.
for (size_t i = 0; i < state.textureUnits.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[i]);
}
glActiveTexture(curgltextureunit);
}
else
{
// Multitexturing not supported, so we only have 1 texture unit.
state.textureUnits.resize(1, 0);
state.curTextureUnit = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
}
// This will be non-zero on some platforms.
if (Canvas::isSupported())
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint *) &state.defaultFBO);
initMaxValues();
createDefaultTexture();
contextInitialized = true;
return true;
}
void OpenGL::deInitContext()
{
if (!contextInitialized)
return;
contextInitialized = false;
}
void OpenGL::initVendor()
{
const char *vstr = (const char *) glGetString(GL_VENDOR);
if (!vstr)
{
vendor = VENDOR_UNKNOWN;
return;
}
// http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR
if (strstr(vstr, "ATI Technologies"))
vendor = VENDOR_ATI_AMD;
else if (strstr(vstr, "NVIDIA"))
vendor = VENDOR_NVIDIA;
else if (strstr(vstr, "Intel"))
vendor = VENDOR_INTEL;
else if (strstr(vstr, "Mesa"))
vendor = VENDOR_MESA_SOFT;
else if (strstr(vstr, "Apple Computer"))
vendor = VENDOR_APPLE;
else if (strstr(vstr, "Microsoft"))
vendor = VENDOR_MICROSOFT;
else
vendor = VENDOR_UNKNOWN;
}
void OpenGL::initOpenGLFunctions()
{
// The functionality of the core and ARB VBOs are identical, so we can
// assign the pointers of the ARB functions to the names of the core
// functions, if the latter isn't supported but the former is.
if (GLAD_ARB_vertex_buffer_object && !GLAD_VERSION_1_5)
{
fp_glBindBuffer = (pfn_glBindBuffer) fp_glBindBufferARB;
fp_glBufferData = (pfn_glBufferData) fp_glBufferDataARB;
fp_glBufferSubData = (pfn_glBufferSubData) fp_glBufferSubDataARB;
fp_glDeleteBuffers = (pfn_glDeleteBuffers) fp_glDeleteBuffersARB;
fp_glGenBuffers = (pfn_glGenBuffers) fp_glGenBuffersARB;
fp_glGetBufferParameteriv = (pfn_glGetBufferParameteriv) fp_glGetBufferParameterivARB;
fp_glGetBufferPointerv = (pfn_glGetBufferPointerv) fp_glGetBufferPointervARB;
fp_glGetBufferSubData = (pfn_glGetBufferSubData) fp_glGetBufferSubDataARB;
fp_glIsBuffer = (pfn_glIsBuffer) fp_glIsBufferARB;
fp_glMapBuffer = (pfn_glMapBuffer) fp_glMapBufferARB;
fp_glUnmapBuffer = (pfn_glUnmapBuffer) fp_glUnmapBufferARB;
}
}
void OpenGL::initMaxValues()
{
// We'll need this value to clamp anisotropy.
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
else
maxAnisotropy = 1.0f;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
void OpenGL::initMatrices()
{
while (matrices.transform.size() > 0)
matrices.transform.pop();
while (matrices.projection.size() > 0)
matrices.projection.pop();
matrices.transform.push(Matrix());
matrices.projection.push(Matrix());
}
void OpenGL::createDefaultTexture()
{
// Set the 'default' texture (id 0) as a repeating white pixel. Otherwise,
// texture2D calls inside a shader would return black when drawing graphics
// primitives, which would create the need to use different "passthrough"
// shaders for untextured primitives vs images.
GLuint curtexture = state.textureUnits[state.curTextureUnit];
bindTexture(0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLubyte pix = 255;
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pix);
bindTexture(curtexture);
}
void OpenGL::prepareDraw()
{
const Matrix &transform = matrices.transform.top();
const Matrix &proj = matrices.projection.top();
Shader *shader = Shader::current;
if (GLAD_ES_VERSION_2_0 && shader)
{
// Send built-in uniforms to the current shader.
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, transform.getElements(), 1);
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, proj.getElements(), 1);
Matrix tp_matrix(proj * transform);
shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX, 4, tp_matrix.getElements(), 1);
shader->sendBuiltinFloat(Shader::BUILTIN_POINT_SIZE, 1, &state.pointSize, 1);
}
else if (GLAD_VERSION_1_0)
{
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(proj.getElements());
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(transform.getElements());
}
}
void OpenGL::setColor(const Color &c)
{
if (GLAD_ES_VERSION_2_0)
glVertexAttrib4f(GLuint(ATTRIB_COLOR), c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f);
else
glColor4ubv(&c.r);
state.color = c;
}
Color OpenGL::getColor() const
{
return state.color;
}
void OpenGL::setClearColor(const Color &c)
{
glClearColor(c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f);
state.clearColor = c;
}
Color OpenGL::getClearColor() const
{
return state.clearColor;
}
GLint OpenGL::getGLAttrib(OpenGL::VertexAttrib attrib)
{
if (GLAD_ES_VERSION_2_0)
{
// The enum value maps to a generic vertex attribute index.
return GLint(attrib);
}
else
{
switch (attrib)
{
case ATTRIB_POS:
return GL_VERTEX_ARRAY;
case ATTRIB_TEXCOORD:
return GL_TEXTURE_COORD_ARRAY;
case ATTRIB_COLOR:
return GL_COLOR_ARRAY;
default:
return GLint(attrib);
}
}
return -1;
}
void OpenGL::enableVertexAttribArray(OpenGL::VertexAttrib attrib)
{
GLint glattrib = getGLAttrib(attrib);
if (GLAD_ES_VERSION_2_0)
glEnableVertexAttribArray((GLuint) glattrib);
else
glEnableClientState((GLenum) glattrib);
}
void OpenGL::disableVertexAttribArray(OpenGL::VertexAttrib attrib)
{
GLint glattrib = getGLAttrib(attrib);
if (GLAD_ES_VERSION_2_0)
glDisableVertexAttribArray((GLuint) glattrib);
else
glDisableClientState((GLenum) glattrib);
}
void OpenGL::setVertexAttribArray(OpenGL::VertexAttrib attrib, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
if (GLAD_ES_VERSION_2_0)
{
GLboolean normalized = (type == GL_UNSIGNED_BYTE) ? GL_TRUE : GL_FALSE;
glVertexAttribPointer(GLuint(attrib), size, type, normalized, stride, pointer);
}
else
{
switch (attrib)
{
case ATTRIB_POS:
glVertexPointer(size, type, stride, pointer);
break;
case ATTRIB_TEXCOORD:
glTexCoordPointer(size, type, stride, pointer);
break;
case ATTRIB_COLOR:
glColorPointer(size, type, stride, pointer);
break;
default:
break;
}
}
}
void OpenGL::setViewport(const OpenGL::Viewport &v)
{
glViewport(v.x, v.y, v.w, v.h);
state.viewport = v;
// glScissor starts from the lower left, so we compensate when setting the
// scissor. When the viewport is changed, we need to manually update the
// scissor again.
if (v.h != state.viewport.h)
setScissor(state.scissor);
}
OpenGL::Viewport OpenGL::getViewport() const
{
return state.viewport;
}
void OpenGL::setScissor(const OpenGL::Viewport &v)
{
// We need to compensate for glScissor starting from the lower left of the
// viewport instead of the top left.
glScissor(v.x, state.viewport.h - (v.y + v.h), v.w, v.h);
state.scissor = v;
}
OpenGL::Viewport OpenGL::getScissor() const
{
return state.scissor;
}
void OpenGL::setPointSize(float size)
{
if (GLAD_VERSION_1_0)
glPointSize(size);
state.pointSize = size;
}
float OpenGL::getPointSize() const
{
return state.pointSize;
}
GLuint OpenGL::getDefaultFBO() const
{
return state.defaultFBO;
}
void OpenGL::setTextureUnit(int textureunit)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
throw love::Exception("Invalid texture unit index (%d).", textureunit);
if (textureunit != state.curTextureUnit)
{
if (state.textureUnits.size() > 1)
glActiveTexture(GL_TEXTURE0 + textureunit);
else
throw love::Exception("Multitexturing not supported.");
}
state.curTextureUnit = textureunit;
}
void OpenGL::bindTexture(GLuint texture)
{
if (texture != state.textureUnits[state.curTextureUnit])
{
state.textureUnits[state.curTextureUnit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
}
}
void OpenGL::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
throw love::Exception("Invalid texture unit index.");
if (texture != state.textureUnits[textureunit])
{
int oldtextureunit = state.curTextureUnit;
setTextureUnit(textureunit);
state.textureUnits[textureunit] = texture;
glBindTexture(GL_TEXTURE_2D, texture);
if (restoreprev)
setTextureUnit(oldtextureunit);
}
}
void OpenGL::deleteTexture(GLuint texture)
{
// glDeleteTextures binds texture 0 to all texture units the deleted texture
// was bound to before deletion.
std::vector<GLuint>::iterator it;
for (it = state.textureUnits.begin(); it != state.textureUnits.end(); ++it)
{
if (*it == texture)
*it = 0;
}
glDeleteTextures(1, &texture);
}
float OpenGL::setTextureFilter(const graphics::Image::Filter &f)
{
GLint gmin, gmag;
if (f.mipmap == Image::FILTER_NONE)
{
if (f.min == Image::FILTER_NEAREST)
gmin = GL_NEAREST;
else // f.min == Image::FILTER_LINEAR
gmin = GL_LINEAR;
}
else
{
if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_NEAREST_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_NEAREST_MIPMAP_LINEAR;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
gmin = GL_LINEAR_MIPMAP_NEAREST;
else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
gmin = GL_LINEAR_MIPMAP_LINEAR;
else
gmin = GL_LINEAR;
}
switch (f.mag)
{
case Image::FILTER_NEAREST:
gmag = GL_NEAREST;
break;
case Image::FILTER_LINEAR:
default:
gmag = GL_LINEAR;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
float anisotropy = 1.0f;
if (GLAD_EXT_texture_filter_anisotropic)
{
anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
return anisotropy;
}
graphics::Image::Filter OpenGL::getTextureFilter()
{
GLint gmin, gmag;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
Image::Filter f;
switch (gmin)
{
case GL_NEAREST:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_NONE;
break;
case GL_NEAREST_MIPMAP_NEAREST:
f.min = f.mipmap = Image::FILTER_NEAREST;
break;
case GL_NEAREST_MIPMAP_LINEAR:
f.min = Image::FILTER_NEAREST;
f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR_MIPMAP_NEAREST:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NEAREST;
break;
case GL_LINEAR_MIPMAP_LINEAR:
f.min = f.mipmap = Image::FILTER_LINEAR;
break;
case GL_LINEAR:
default:
f.min = Image::FILTER_LINEAR;
f.mipmap = Image::FILTER_NONE;
break;
}
switch (gmag)
{
case GL_NEAREST:
f.mag = Image::FILTER_NEAREST;
break;
case GL_LINEAR:
default:
f.mag = Image::FILTER_LINEAR;
break;
}
if (GLAD_EXT_texture_filter_anisotropic)
glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &f.anisotropy);
return f;
}
void OpenGL::setTextureWrap(const graphics::Image::Wrap &w)
{
GLint gs, gt;
switch (w.s)
{
case Image::WRAP_CLAMP:
gs = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gs = GL_REPEAT;
break;
}
switch (w.t)
{
case Image::WRAP_CLAMP:
gt = GL_CLAMP_TO_EDGE;
break;
case Image::WRAP_REPEAT:
default:
gt = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
}
graphics::Image::Wrap OpenGL::getTextureWrap()
{
GLint gs, gt;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
Image::Wrap w;
switch (gs)
{
case GL_CLAMP_TO_EDGE:
w.s = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.s = Image::WRAP_REPEAT;
break;
}
switch (gt)
{
case GL_CLAMP_TO_EDGE:
w.t = Image::WRAP_CLAMP;
break;
case GL_REPEAT:
default:
w.t = Image::WRAP_REPEAT;
break;
}
return w;
}
int OpenGL::getMaxTextureSize() const
{
return maxTextureSize;
}
OpenGL::Vendor OpenGL::getVendor() const
{
return vendor;
}
// OpenGL class instance singleton.
OpenGL gl;
} // opengl
} // graphics
} // love
@@ -0,0 +1,311 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
#define LOVE_GRAPHICS_OPENGL_OPENGL_H
// LOVE
#include "graphics/Color.h"
#include "graphics/Image.h"
#include "common/Matrix.h"
// GLAD
#include "libraries/glad/gladfuncs.hpp"
// C++
#include <vector>
#include <stack>
// The last argument to AttribPointer takes a buffer offset casted to a pointer.
#define BUFFER_OFFSET(i) ((char *) NULL + (i))
namespace love
{
namespace graphics
{
namespace opengl
{
// Awful, but the library uses the namespace in order to use the functions sanely
// with proper autocomplete in IDEs while having name mangling safety -
// no clashes with other GL libraries when linking, etc.
using namespace glad;
/**
* Thin layer between OpenGL and the rest of the program.
* Internally shadows some OpenGL context state for improved efficiency and
* accuracy (compared to glGet etc.)
* A class is more convenient and readable than plain namespaced functions, but
* typically only one OpenGL object should be used (singleton.)
**/
class OpenGL
{
public:
// OpenGL GPU vendors.
enum Vendor
{
VENDOR_ATI_AMD,
VENDOR_NVIDIA,
VENDOR_INTEL,
VENDOR_MESA_SOFT, // Software renderer.
VENDOR_APPLE, // Software renderer.
VENDOR_MICROSOFT, // Software renderer.
VENDOR_UNKNOWN
};
// Vertex attributes. The values map to OpenGL generic vertex attribute
// indices, when applicable (GLES2.)
enum VertexAttrib
{
ATTRIB_POS = 0,
ATTRIB_TEXCOORD = 1,
ATTRIB_COLOR = 2,
ATTRIB_MAX_ENUM
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
int x, y;
int w, h;
Viewport()
: x(0), y(0), w(0), h(0)
{}
Viewport(int _x, int _y, int _w, int _h)
: x(_x), y(_y), w(_w), h(_h)
{}
};
// Transformation matrix stacks.
struct
{
std::stack<Matrix> transform;
std::stack<Matrix> projection;
} matrices;
OpenGL();
virtual ~OpenGL() {}
/**
* Initializes some required context state based on current and default
* OpenGL state. Call this directly after creating an OpenGL context!
**/
bool initContext();
/**
* Marks current context state as invalid and deletes OpenGL objects owned
* by this class instance. Call this directly before potentially deleting
* an OpenGL context!
**/
void deInitContext();
/**
* Set up necessary state (matrices etc.) for drawing. This *must* be called
* directly before GL draws.
**/
void prepareDraw();
/**
* Sets the current constant color.
**/
void setColor(const Color &c);
/**
* Gets the current constant color.
**/
Color getColor() const;
/**
* Sets the current clear color for all framebuffer objects.
**/
void setClearColor(const Color &c);
/**
* Gets the current clear color.
**/
Color getClearColor() const;
/**
* Enables usage of an array for a vertex attribute when drawing.
* See http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml
**/
void enableVertexAttribArray(VertexAttrib attrib);
/**
* Disables usage of an array for a vertex attribute when drawing.
* See http://www.opengl.org/sdk/docs/man/xhtml/glDisableVertexAttribArray.xml
**/
void disableVertexAttribArray(VertexAttrib attrib);
/**
* Sets the parameters for an array of data for a vertex attribute.
* See http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml
**/
void setVertexAttribArray(VertexAttrib attrib, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
/**
* Sets the OpenGL rendering viewport to the specified rectangle.
* The y-coordinate starts at the top.
**/
void setViewport(const Viewport &v);
/**
* Gets the current OpenGL rendering viewport rectangle.
**/
Viewport getViewport() const;
/**
* Sets the scissor box to the specified rectangle.
* The y-coordinate starts at the top and is flipped internally.
**/
void setScissor(const Viewport &v);
/**
* Gets the current scissor box (regardless of whether scissoring is enabled.)
**/
Viewport getScissor() const;
/**
* Sets the global point size.
**/
void setPointSize(float size);
/**
* Gets the global point size.
**/
float getPointSize() const;
/**
* This will usually be 0 (system drawable), but some platforms require a
* non-zero FBO for rendering.
**/
GLuint getDefaultFBO() const;
/**
* Helper for setting the active texture unit.
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
**/
void setTextureUnit(int textureunit);
/**
* Helper for binding an OpenGL texture.
* Makes sure we aren't redundantly binding textures.
**/
void bindTexture(GLuint texture);
/**
* Helper for binding a texture to a specific texture unit.
*
* @param textureunit Index in the range of [0, maxtextureunits-1]
* @param restoreprev Restore previously bound texture unit when done.
**/
void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev);
/**
* Helper for deleting an OpenGL texture.
* Cleans up if the texture is currently bound.
**/
void deleteTexture(GLuint texture);
/**
* Sets the image filter mode for the currently bound texture.
* Returns the actual amount of anisotropic filtering set.
**/
float setTextureFilter(const graphics::Image::Filter &f);
/**
* Returns the image filter mode for the currently bound texture.
**/
graphics::Image::Filter getTextureFilter();
/**
* Sets the image wrap mode for the currently bound texture.
**/
void setTextureWrap(const graphics::Image::Wrap &w);
/**
* Returns the image wrap mode for the currently bound texture.
**/
graphics::Image::Wrap getTextureWrap();
/**
* Returns the maximum supported width or height of a texture.
**/
int getMaxTextureSize() const;
/**
* Get the GPU vendor of this OpenGL context.
**/
Vendor getVendor() const;
private:
void initVendor();
void initOpenGLFunctions();
void initMaxValues();
void initMatrices();
void createDefaultTexture();
GLint getGLAttrib(VertexAttrib attrib);
bool contextInitialized;
float maxAnisotropy;
int maxTextureSize;
Vendor vendor;
// Tracked OpenGL state.
struct
{
// Current constant color.
Color color;
Color clearColor;
// Texture unit state (currently bound texture for each texture unit.)
std::vector<GLuint> textureUnits;
// Currently active texture unit.
int curTextureUnit;
Viewport viewport;
Viewport scissor;
float pointSize;
GLuint defaultFBO;
} state;
}; // OpenGL
// OpenGL class instance singleton.
extern OpenGL gl;
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_OPENGL_H
@@ -0,0 +1,942 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
//LOVE
#include "common/config.h"
#include "ParticleSystem.h"
#include "common/math.h"
#include "modules/math/RandomGenerator.h"
#include "OpenGL.h"
// STD
#include <algorithm>
#include <cmath>
#include <cstdlib>
namespace love
{
namespace graphics
{
namespace opengl
{
namespace
{
love::math::RandomGenerator rng;
Colorf colorToFloat(const Color &c)
{
return Colorf((float)c.r/255.0f, (float)c.g/255.0f, (float)c.b/255.0f, (float)c.a/255.0f);
}
float calculate_variation(float inner, float outer, float var)
{
float low = inner - (outer/2.0f)*var;
float high = inner + (outer/2.0f)*var;
float r = (float) rng.random();
return low*(1-r)+high*r;
}
} // anonymous namespace
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM>::Entry ParticleSystem::distributionsEntries[] = {
{ "none", ParticleSystem::DISTRIBUTION_NONE },
{ "uniform", ParticleSystem::DISTRIBUTION_UNIFORM },
{ "normal", ParticleSystem::DISTRIBUTION_NORMAL },
};
StringMap<ParticleSystem::AreaSpreadDistribution, ParticleSystem::DISTRIBUTION_MAX_ENUM> ParticleSystem::distributions(ParticleSystem::distributionsEntries, sizeof(ParticleSystem::distributionsEntries));
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM>::Entry ParticleSystem::insertModesEntries[] =
{
{ "top", ParticleSystem::INSERT_MODE_TOP },
{ "bottom", ParticleSystem::INSERT_MODE_BOTTOM },
{ "random", ParticleSystem::INSERT_MODE_RANDOM },
};
StringMap<ParticleSystem::InsertMode, ParticleSystem::INSERT_MODE_MAX_ENUM> ParticleSystem::insertModes(ParticleSystem::insertModesEntries, sizeof(ParticleSystem::insertModesEntries));
ParticleSystem::ParticleSystem(Image *image, uint32 size)
: pMem(NULL)
, pFree(NULL)
, pHead(NULL)
, pTail(NULL)
, particleVerts(NULL)
, ibo(NULL)
, image(image)
, active(true)
, insertMode(INSERT_MODE_TOP)
, maxParticles(0)
, activeParticles(0)
, emissionRate(0)
, emitCounter(0)
, areaSpreadDistribution(DISTRIBUTION_NONE)
, lifetime(-1)
, life(0)
, particleLifeMin(0)
, particleLifeMax(0)
, direction(0)
, spread(0)
, speedMin(0)
, speedMax(0)
, linearAccelerationMin(0, 0)
, linearAccelerationMax(0, 0)
, radialAccelerationMin(0)
, radialAccelerationMax(0)
, tangentialAccelerationMin(0)
, tangentialAccelerationMax(0)
, sizeVariation(0)
, rotationMin(0)
, rotationMax(0)
, spinStart(0)
, spinEnd(0)
, spinVariation(0)
, offsetX(float(image->getWidth())*0.5f)
, offsetY(float(image->getHeight())*0.5f)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid ParticleSystem size.");
sizes.push_back(1.0f);
colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
setBufferSize(size);
image->retain();
}
ParticleSystem::~ParticleSystem()
{
if (this->image != 0)
this->image->release();
deleteBuffers();
}
void ParticleSystem::createBuffers(size_t size)
{
try
{
pFree = pMem = new particle[size];
particleVerts = new love::Vertex[size * 4];
ibo = new VertexIndex(size);
maxParticles = (uint32) size;
}
catch (love::Exception &)
{
deleteBuffers();
throw;
}
catch (std::bad_alloc &)
{
deleteBuffers();
throw love::Exception("Out of memory");
}
}
void ParticleSystem::deleteBuffers()
{
// Clean up for great gracefulness!
delete[] pMem;
delete[] particleVerts;
delete ibo;
pMem = NULL;
particleVerts = NULL;
ibo = NULL;
maxParticles = 0;
activeParticles = 0;
}
void ParticleSystem::setBufferSize(uint32 size)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid buffer size");
deleteBuffers();
createBuffers(size);
reset();
}
uint32 ParticleSystem::getBufferSize() const
{
return maxParticles;
}
void ParticleSystem::addParticle()
{
if (isFull())
return;
// Gets a free particle and updates the allocation pointer.
particle *p = pFree++;
initParticle(p);
switch (insertMode)
{
default:
case INSERT_MODE_TOP:
insertTop(p);
break;
case INSERT_MODE_BOTTOM:
insertBottom(p);
break;
case INSERT_MODE_RANDOM:
insertRandom(p);
break;
}
activeParticles++;
}
void ParticleSystem::initParticle(particle *p)
{
float min,max;
min = particleLifeMin;
max = particleLifeMax;
if (min == max)
p->life = min;
else
p->life = (float) rng.random(min, max);
p->lifetime = p->life;
p->position[0] = position.getX();
p->position[1] = position.getY();
switch (areaSpreadDistribution)
{
case DISTRIBUTION_UNIFORM:
p->position[0] += (float) rng.random(-areaSpread.getX(), areaSpread.getX());
p->position[1] += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
break;
case DISTRIBUTION_NORMAL:
p->position[0] += (float) rng.randomNormal(areaSpread.getX());
p->position[1] += (float) rng.randomNormal(areaSpread.getY());
break;
case DISTRIBUTION_NONE:
default:
break;
}
min = direction - spread/2.0f;
max = direction + spread/2.0f;
p->direction = (float) rng.random(min, max);
p->origin = position;
min = speedMin;
max = speedMax;
float speed = (float) rng.random(min, max);
p->speed = love::Vector(cosf(p->direction), sinf(p->direction));
p->speed *= speed;
p->linearAcceleration.x = (float) rng.random(linearAccelerationMin.x, linearAccelerationMax.x);
p->linearAcceleration.y = (float) rng.random(linearAccelerationMin.y, linearAccelerationMax.y);
min = radialAccelerationMin;
max = radialAccelerationMax;
p->radialAcceleration = (float) rng.random(min, max);
min = tangentialAccelerationMin;
max = tangentialAccelerationMax;
p->tangentialAcceleration = (float) rng.random(min, max);
p->sizeOffset = (float) rng.random(sizeVariation); // time offset for size change
p->sizeIntervalSize = (1.0f - (float) rng.random(sizeVariation)) - p->sizeOffset;
p->size = sizes[(size_t)(p->sizeOffset - .5f) * (sizes.size() - 1)];
min = rotationMin;
max = rotationMax;
p->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
p->rotation = (float) rng.random(min, max);
p->color = colors[0];
}
void ParticleSystem::insertTop(particle *p)
{
if (pHead == NULL)
{
pHead = p;
p->prev = NULL;
}
else
{
pTail->next = p;
p->prev = pTail;
}
p->next = NULL;
pTail = p;
}
void ParticleSystem::insertBottom(particle *p)
{
if (pTail == NULL)
{
pTail = p;
p->next = NULL;
}
else
{
pHead->prev = p;
p->next = pHead;
}
p->prev = NULL;
pHead = p;
}
void ParticleSystem::insertRandom(particle *p)
{
// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
uint64 pos = rng.rand() % ((int64) activeParticles + 1);
// Special case where the particle gets inserted before the head.
if (pos == activeParticles)
{
particle *pA = pHead;
if (pA)
pA->prev = p;
p->prev = NULL;
p->next = pA;
pHead = p;
return;
}
// Inserts the particle after the randomly selected particle.
particle *pA = pMem + pos;
particle *pB = pA->next;
pA->next = p;
if (pB)
pB->prev = p;
else
pTail = p;
p->prev = pA;
p->next = pB;
}
ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
{
// The linked list is updated in this function and old pointers may be
// invalidated. The returned pointer will inform the caller of the new
// pointer to the next particle.
particle *pNext = NULL;
// Removes the particle from the linked list.
if (p->prev)
p->prev->next = p->next;
else
pHead = p->next;
if (p->next)
{
p->next->prev = p->prev;
pNext = p->next;
}
else
pTail = p->prev;
// The (in memory) last particle can now be moved into the free slot.
// It will skip the moving if it happens to be the removed particle.
pFree--;
if (p != pFree)
{
*p = *pFree;
if (pNext == pFree)
pNext = p;
if (p->prev)
p->prev->next = p;
else
pHead = p;
if (p->next)
p->next->prev = p;
else
pTail = p;
}
activeParticles--;
return pNext;
}
void ParticleSystem::setImage(Image *image)
{
Object::AutoRelease imagerelease(this->image);
this->image = image;
this->image->retain();
}
Image *ParticleSystem::getImage() const
{
return image;
}
void ParticleSystem::setInsertMode(InsertMode mode)
{
insertMode = mode;
}
ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
{
return insertMode;
}
void ParticleSystem::setEmissionRate(int rate)
{
if (rate < 0)
throw love::Exception("Invalid emission rate");
emissionRate = rate;
}
int ParticleSystem::getEmissionRate() const
{
return emissionRate;
}
void ParticleSystem::setEmitterLifetime(float life)
{
this->life = lifetime = life;
}
float ParticleSystem::getEmitterLifetime() const
{
return lifetime;
}
void ParticleSystem::setParticleLifetime(float min, float max)
{
particleLifeMin = min;
if (max == 0)
particleLifeMax = min;
else
particleLifeMax = max;
}
void ParticleSystem::getParticleLifetime(float *min, float *max) const
{
if (min)
*min = particleLifeMin;
if (max)
*max = particleLifeMax;
}
void ParticleSystem::setPosition(float x, float y)
{
position = love::Vector(x, y);
}
const love::Vector &ParticleSystem::getPosition() const
{
return position;
}
void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
{
areaSpread = love::Vector(x, y);
areaSpreadDistribution = distribution;
}
ParticleSystem::AreaSpreadDistribution ParticleSystem::getAreaSpreadDistribution() const
{
return areaSpreadDistribution;
}
const love::Vector &ParticleSystem::getAreaSpreadParameters() const
{
return areaSpread;
}
void ParticleSystem::setDirection(float direction)
{
this->direction = direction;
}
float ParticleSystem::getDirection() const
{
return direction;
}
void ParticleSystem::setSpread(float spread)
{
this->spread = spread;
}
float ParticleSystem::getSpread() const
{
return spread;
}
void ParticleSystem::setSpeed(float speed)
{
speedMin = speedMax = speed;
}
void ParticleSystem::setSpeed(float min, float max)
{
speedMin = min;
speedMax = max;
}
void ParticleSystem::getSpeed(float *min, float *max) const
{
if (min)
*min = speedMin;
if (max)
*max = speedMax;
}
void ParticleSystem::setLinearAcceleration(float x, float y)
{
linearAccelerationMin.x = linearAccelerationMax.x = x;
linearAccelerationMin.y = linearAccelerationMax.y = y;
}
void ParticleSystem::setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
{
linearAccelerationMin = love::Vector(xmin, ymin);
linearAccelerationMax = love::Vector(xmax, ymax);
}
void ParticleSystem::getLinearAcceleration(love::Vector *min, love::Vector *max) const
{
if (min)
*min = linearAccelerationMin;
if (max)
*max = linearAccelerationMax;
}
void ParticleSystem::setRadialAcceleration(float acceleration)
{
radialAccelerationMin = radialAccelerationMax = acceleration;
}
void ParticleSystem::setRadialAcceleration(float min, float max)
{
radialAccelerationMin = min;
radialAccelerationMax = max;
}
void ParticleSystem::getRadialAcceleration(float *min, float *max) const
{
if (min)
*min = radialAccelerationMin;
if (max)
*max = radialAccelerationMax;
}
void ParticleSystem::setTangentialAcceleration(float acceleration)
{
tangentialAccelerationMin = tangentialAccelerationMax = acceleration;
}
void ParticleSystem::setTangentialAcceleration(float min, float max)
{
tangentialAccelerationMin = min;
tangentialAccelerationMax = max;
}
void ParticleSystem::getTangentialAcceleration(float *min, float *max) const
{
if (min)
*min = tangentialAccelerationMin;
if (max)
*max = tangentialAccelerationMax;
}
void ParticleSystem::setSize(float size)
{
sizes.resize(1);
sizes[0] = size;
}
void ParticleSystem::setSizes(const std::vector<float> &newSizes)
{
sizes = newSizes;
}
const std::vector<float> &ParticleSystem::getSizes() const
{
return sizes;
}
void ParticleSystem::setSizeVariation(float variation)
{
sizeVariation = variation;
}
float ParticleSystem::getSizeVariation() const
{
return sizeVariation;
}
void ParticleSystem::setRotation(float rotation)
{
rotationMin = rotationMax = rotation;
}
void ParticleSystem::setRotation(float min, float max)
{
rotationMin = min;
rotationMax = max;
}
void ParticleSystem::getRotation(float *min, float *max) const
{
if (min)
*min = rotationMin;
if (max)
*max = rotationMax;
}
void ParticleSystem::setSpin(float spin)
{
spinStart = spin;
spinEnd = spin;
}
void ParticleSystem::setSpin(float start, float end)
{
spinStart = start;
spinEnd = end;
}
void ParticleSystem::getSpin(float *start, float *end) const
{
if (start)
*start = spinStart;
if (end)
*end = spinEnd;
}
void ParticleSystem::setSpinVariation(float variation)
{
spinVariation = variation;
}
float ParticleSystem::getSpinVariation() const
{
return spinVariation;
}
void ParticleSystem::setOffset(float x, float y)
{
offsetX = x;
offsetY = y;
}
love::Vector ParticleSystem::getOffset() const
{
return love::Vector(offsetX, offsetY);
}
void ParticleSystem::setColor(const Color &color)
{
colors.resize(1);
colors[0] = colorToFloat(color);
}
void ParticleSystem::setColor(const std::vector<Color> &newColors)
{
colors.resize(newColors.size());
for (size_t i = 0; i < newColors.size(); ++i)
colors[i] = colorToFloat(newColors[i]);
}
std::vector<Color> ParticleSystem::getColor() const
{
// The particle system stores colors as floats...
std::vector<Color> ncolors(colors.size());
for (size_t i = 0; i < colors.size(); ++i)
{
ncolors[i].r = (unsigned char) (colors[i].r * 255);
ncolors[i].g = (unsigned char) (colors[i].g * 255);
ncolors[i].b = (unsigned char) (colors[i].b * 255);
ncolors[i].a = (unsigned char) (colors[i].a * 255);
}
return ncolors;
}
uint32 ParticleSystem::getCount() const
{
return activeParticles;
}
void ParticleSystem::start()
{
active = true;
}
void ParticleSystem::stop()
{
active = false;
life = lifetime;
emitCounter = 0;
}
void ParticleSystem::pause()
{
active = false;
}
void ParticleSystem::reset()
{
if (pMem == NULL)
return;
pFree = pMem;
pHead = NULL;
pTail = NULL;
activeParticles = 0;
life = lifetime;
emitCounter = 0;
}
void ParticleSystem::emit(uint32 num)
{
if (!active)
return;
num = std::min(num, maxParticles - activeParticles);
while(num--)
addParticle();
}
bool ParticleSystem::isActive() const
{
return active;
}
bool ParticleSystem::isPaused() const
{
return !active && life < lifetime;
}
bool ParticleSystem::isStopped() const
{
return !active && life >= lifetime;
}
bool ParticleSystem::isEmpty() const
{
return activeParticles == 0;
}
bool ParticleSystem::isFull() const
{
return activeParticles == maxParticles;
}
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
{
uint32 pCount = getCount();
if (pCount == 0 || image == NULL || pMem == NULL || particleVerts == NULL)
return;
Color curcolor = gl.getColor();
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
const Vertex *imageVerts = image->getVertices();
Vertex *pVerts = particleVerts;
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.transform(pVerts, imageVerts, 4);
// set the texture coordinate and color data for particle vertices
for (int v = 0; v < 4; v++)
{
pVerts[v].s = imageVerts[v].s;
pVerts[v].t = imageVerts[v].t;
// particle colors are stored as floats (0-1) but vertex colors are stored as unsigned bytes (0-255)
pVerts[v].r = (unsigned char) (p->color.r*255);
pVerts[v].g = (unsigned char) (p->color.g*255);
pVerts[v].b = (unsigned char) (p->color.b*255);
pVerts[v].a = (unsigned char) (p->color.a*255);
}
pVerts += 4;
p = p->next;
}
image->predraw();
gl.prepareDraw();
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), &particleVerts[0].x);
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), &particleVerts[0].s);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), &particleVerts[0].r);
{
VertexBuffer::Bind ibo_bind(*ibo->getVertexBuffer());
glDrawElements(GL_TRIANGLES, ibo->getIndexCount(pCount), ibo->getType(), ibo->getPointer(0));
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
image->postdraw();
gl.matrices.transform.pop();
gl.setColor(curcolor);
}
void ParticleSystem::update(float dt)
{
if (pMem == NULL || 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;
while (p)
{
// Decrease lifespan.
p->life -= dt;
if (p->life <= 0)
p = removeParticle(p);
else
{
// Temp variables.
love::Vector radial, tangential;
love::Vector ppos(p->position[0], p->position[1]);
// Get vector from particle center to particle.
radial = ppos - p->origin;
radial.normalize();
tangential = radial;
// Resize radial acceleration.
radial *= p->radialAcceleration;
// Calculate tangential acceleration.
{
float a = tangential.getX();
tangential.setX(-tangential.getY());
tangential.setY(a);
}
// Resize tangential.
tangential *= p->tangentialAcceleration;
// Update position.
p->speed += (radial+tangential+p->linearAcceleration)*dt;
// Modify position.
ppos += p->speed * dt;
p->position[0] = ppos.getX();
p->position[1] = ppos.getY();
const float t = 1.0f - p->life / p->lifetime;
// Rotate.
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
// Change size according to given intervals:
// i = 0 1 2 3 n-1
// |-------|-------|------|--- ... ---|
// t = 0 1/(n-1) 3/(n-1) 1
//
// `s' is the interpolation variable scaled to the current
// interval width, e.g. if n = 5 and t = 0.3, then the current
// indices are 1,2 and s = 0.3 - 0.25 = 0.05
float s = p->sizeOffset + t * p->sizeIntervalSize; // size variation
s *= (float)(sizes.size() - 1); // 0 <= s < sizes.size()
size_t i = (size_t)s;
size_t k = (i == sizes.size() - 1) ? i : i + 1; // boundary check (prevents failing on t = 1.0f)
s -= (float)i; // transpose s to be in interval [0:1]: i <= s < i + 1 ~> 0 <= s < 1
p->size = sizes[i] * (1.0f - s) + sizes[k] * s;
// Update color according to given intervals (as above)
s = t * (float)(colors.size() - 1);
i = (size_t)s;
k = (i == colors.size() - 1) ? i : i + 1;
s -= (float)i; // 0 <= s <= 1
p->color = colors[i] * (1.0f - s) + colors[k] * s;
// Next particle.
p = p->next;
}
}
}
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
{
return distributions.find(in, out);
}
bool ParticleSystem::getConstant(AreaSpreadDistribution in, const char *&out)
{
return distributions.find(in, out);
}
bool ParticleSystem::getConstant(const char *in, InsertMode &out)
{
return insertModes.find(in, out);
}
bool ParticleSystem::getConstant(InsertMode in, const char *&out)
{
return insertModes.find(in, out);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,632 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
#define LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
// LOVE
#include "common/int.h"
#include "common/math.h"
#include "common/Vector.h"
#include "graphics/Drawable.h"
#include "graphics/Color.h"
#include "Image.h"
#include "VertexBuffer.h"
// STL
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* A class for creating, moving and drawing particles.
* A big thanks to bobthebloke.org
**/
class ParticleSystem : public Drawable
{
public:
/**
* Type of distribution new particles are drawn from: None, uniform, normal.
*/
enum AreaSpreadDistribution
{
DISTRIBUTION_NONE,
DISTRIBUTION_UNIFORM,
DISTRIBUTION_NORMAL,
DISTRIBUTION_MAX_ENUM
};
/**
* Insertion modes of new particles in the list: top, bottom, random.
*/
enum InsertMode
{
INSERT_MODE_TOP,
INSERT_MODE_BOTTOM,
INSERT_MODE_RANDOM,
INSERT_MODE_MAX_ENUM,
};
/**
* Maximum numbers of particles in a ParticleSystem.
* This limit comes from the fact that a quad requires four vertices and the
* OpenGL API where GLsizei is a signed int.
**/
static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
/**
* Creates a particle system with the specified buffersize and image.
**/
ParticleSystem(Image *image, uint32 buffer);
/**
* Deletes any allocated memory.
**/
virtual ~ParticleSystem();
/**
* Sets the image used in the particle system.
* @param image The new image.
**/
void setImage(Image *image);
/**
* Returns the image used when drawing the particle system.
**/
Image *getImage() const;
/**
* Clears the current buffer and allocates the appropriate amount of space for the buffer.
* @param size The new buffer size.
**/
void setBufferSize(uint32 size);
/**
* Returns the total amount of particles this ParticleSystem can have active
* at any given point in time.
**/
uint32 getBufferSize() const;
/**
* Sets the insert mode for new particles.
* @param mode The new insert mode.
*/
void setInsertMode(InsertMode mode);
/**
* Returns the current insert mode.
*/
InsertMode getInsertMode() const;
/**
* Sets the emission rate.
* @param rate The amount of particles per second.
**/
void setEmissionRate(int rate);
/**
* Returns the number of particles created per second.
**/
int getEmissionRate() const;
/**
* Sets the lifetime of the particle emitter (-1 means eternal)
* @param life The lifetime (in seconds).
**/
void setEmitterLifetime(float life);
/**
* Returns the lifetime of the particle emitter.
**/
float getEmitterLifetime() const;
/**
* Sets the life range of the particles.
* @param min The minimum life.
* @param max The maximum life (if 0, then becomes the same as minimum life).
**/
void setParticleLifetime(float min, float max = 0);
/**
* Gets the lifetime of a particle.
* @param[out] min The minimum life.
* @param[out] max The maximum life.
**/
void getParticleLifetime(float *min, float *max) const;
/**
* Sets the position of the center of the emitter.
* Used to move the emitter without changing the position of already existing particles.
* @param x The x-coordinate.
* @param y The y-coordinate.
**/
void setPosition(float x, float y);
/**
* Returns the position of the emitter.
**/
const love::Vector &getPosition() const;
/**
* Sets the emission area spread parameters and distribution type. The interpretation of
* the parameters depends on the distribution type:
*
* * None: Parameters are ignored. No area spread.
* * Uniform: Parameters denote maximal (symmetric) displacement from emitter position.
* * Normal: Parameters denote the standard deviation in x and y direction. x and y are assumed to be uncorrelated.
* @param x First parameter. Interpretation depends on distribution type.
* @param y Second parameter. Interpretation depends on distribution type.
* @param distribution Distribution type
**/
void setAreaSpread(AreaSpreadDistribution distribution, float x, float y);
/**
* Returns area spread distribution type.
**/
AreaSpreadDistribution getAreaSpreadDistribution() const;
/**
* Returns area spread parameters.
**/
const love::Vector &getAreaSpreadParameters() const;
/**
* Sets the direction of the particle emitter.
* @param direction The direction (in degrees).
**/
void setDirection(float direction);
/**
* Returns the direction of the particle emitter (in radians).
**/
float getDirection() const;
/**
* Sets the spread of the particle emitter.
* @param spread The spread (in radians).
**/
void setSpread(float spread);
/**
* Returns the directional spread of the emitter (in radians).
**/
float getSpread() const;
/**
* Sets the speed of the particles.
* @param speed The speed.
**/
void setSpeed(float speed);
/**
* Sets the speed of the particles.
* @param min The minimum speed.
* @param max The maximum speed.
**/
void setSpeed(float min, float max);
/**
* Gets the speed of the particles.
* @param[out] min The minimum speed.
* @param[out] max The maximum speed.
**/
void getSpeed(float *min, float *max) const;
/**
* Sets the linear acceleration (the acceleration along the x and y axes).
* @param x The acceleration along the x-axis.
* @param y The acceleration along the y-axis.
**/
void setLinearAcceleration(float x, float y);
/**
* Sets the linear acceleration (the acceleration along the x and y axes).
* @param xmin The minimum amount of acceleration along the x-axis.
* @param ymin The minimum amount of acceleration along the y-axis.
* @param xmax The maximum amount of acceleration along the x-axis.
* @param ymax The maximum amount of acceleration along the y-axis.
**/
void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
/**
* Gets the linear acceleration of the particles.
* @param[out] min The minimum acceleration.
* @param[out] max The maximum acceleration.
**/
void getLinearAcceleration(love::Vector *min, love::Vector *max) const;
/**
* Sets the radial acceleration (the acceleration towards the particle emitter).
* @param acceleration The amount of acceleration.
**/
void setRadialAcceleration(float acceleration);
/**
* Sets the radial acceleration (the acceleration towards the particle emitter).
* @param min The minimum acceleration.
* @param max The maximum acceleration.
**/
void setRadialAcceleration(float min, float max);
/**
* Gets the radial acceleration.
* @param[out] min The minimum amount of radial acceleration.
* @param[out] max The maximum amount of radial acceleration.
**/
void getRadialAcceleration(float *min, float *max) const;
/**
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
* @param acceleration The amount of acceleration.
**/
void setTangentialAcceleration(float acceleration);
/**
* Sets the tangential acceleration (the acceleration perpendicular to the particle's direction).
* @param min The minimum acceleration.
* @param max The maximum acceleration.
**/
void setTangentialAcceleration(float min, float max);
/**
* Gets the tangential acceleration.
* @param[out] min The minimum tangential acceleration.
* @param[out] max The maximum tangential acceleration.
**/
void getTangentialAcceleration(float *min, float *max) const;
/**
* Sets the size of the sprite (1.0 being the default size).
* @param size The size of the sprite.
**/
void setSize(float size);
/**
* Sets the sizes of the sprite upon creation and upon death (1.0 being the default size).
* @param newSizes Array of sizes
**/
void setSizes(const std::vector<float> &newSizes);
/**
* Returns the sizes of the particle sprites.
**/
const std::vector<float> &getSizes() const;
/**
* Sets the amount of variation to the sprite's beginning size (0 being no variation and 1.0 a random size between start and end).
* @param variation The amount of variation.
**/
void setSizeVariation(float variation);
/**
* Returns the amount of initial size variation between particles.
**/
float getSizeVariation() const;
/**
* Sets the amount of rotation a sprite starts out with.
* @param rotation The amount of rotation.
**/
void setRotation(float rotation);
/**
* Sets the amount of rotation a sprite starts out with (a random value between min and max).
* @param min The minimum amount of rotation.
* @param max The maximum amount of rotation.
**/
void setRotation(float min, float max);
/**
* Gets the initial amount of rotation of a particle, in radians.
* @param[out] min The minimum initial rotation.
* @param[out] max The maximum initial rotation.
**/
void getRotation(float *min, float *max) const;
/**
* Sets the spin of the sprite.
* @param spin The spin of the sprite (in degrees).
**/
void setSpin(float spin);
/**
* Sets the spin of the sprite upon particle creation and death.
* @param start The spin of the sprite upon creation (in radians / second).
* @param end The spin of the sprite upon death (in radians / second).
**/
void setSpin(float start, float end);
/**
* Gets the amount of spin of a particle during its lifetime.
* @param[out] start The initial spin, in radians / s.
* @param[out] end The final spin, in radians / s.
**/
void getSpin(float *start, float *end) const;
/**
* Sets the variation of the start spin (0 being no variation and 1 being a random spin between start and end).
* @param variation The variation.
**/
void setSpinVariation(float variation);
/**
* Returns the amount of variation of the start spin of a particle.
**/
float getSpinVariation() const;
/**
* Sets the particles' offsets for rotation.
* @param x The x offset.
* @param y The y offset.
**/
void setOffset(float x, float y);
/**
* Returns of the particle offset.
**/
love::Vector getOffset() const;
/**
* Sets the color of the particles.
* @param color The color.
**/
void setColor(const Color &color);
/**
* Sets the color of the particles.
* @param newColors Array of colors
**/
void setColor(const std::vector<Color> &newColors);
/**
* Returns the color of the particles.
**/
std::vector<Color> getColor() const;
/**
* Returns the amount of particles that are currently active in the system.
**/
uint32 getCount() const;
/**
* Starts/resumes the particle emitter.
**/
void start();
/**
* Stops the particle emitter and resets.
**/
void stop();
/**
* Pauses the particle emitter.
**/
void pause();
/**
* Resets the particle emitter.
**/
void reset();
/**
* Instantly emits a number of particles.
* @param num The number of particles to emit.
**/
void emit(uint32 num);
/**
* Returns whether the particle emitter is active.
**/
bool isActive() const;
/**
* Returns whether the particle emitter is paused.
**/
bool isPaused() const;
bool isStopped() const;
/**
* Returns whether the particle system is empty of particles or not.
**/
bool isEmpty() const;
/**
* Returns whether the amount of particles has reached the buffer limit or not.
**/
bool isFull() const;
/**
* Draws the particle emitter at the specified position.
* @param x The x-coordinate.
* @param y The y-coordinate.
**/
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
/**
* Updates the particle system.
* @param dt Time since last update.
**/
void update(float dt);
static bool getConstant(const char *in, AreaSpreadDistribution &out);
static bool getConstant(AreaSpreadDistribution in, const char *&out);
static bool getConstant(const char *in, InsertMode &out);
static bool getConstant(InsertMode in, const char *&out);
protected:
// Represents a single particle.
struct particle
{
particle *prev;
particle *next;
float lifetime;
float life;
float position[2];
float direction;
// Particles gravitate towards this point.
love::Vector origin;
love::Vector speed;
love::Vector linearAcceleration;
float radialAcceleration;
float tangentialAcceleration;
float size;
float sizeOffset;
float sizeIntervalSize;
float rotation;
float spinStart;
float spinEnd;
Colorf color;
};
// The max amount of particles.
int bufferSize;
// Pointer to the beginning of the allocated memory.
particle *pMem;
// Pointer to a free particle.
particle *pFree;
// Pointer to the start of the linked list.
particle *pHead;
// Pointer to the end of the linked list.
particle *pTail;
// array of transformed vertex data for all particles, for drawing
Vertex *particleVerts;
// Vertex index buffer.
VertexIndex *ibo;
// The image to be drawn.
Image *image;
// Whether the particle emitter is active.
bool active;
// Insert mode of new particles.
InsertMode insertMode;
// The maximum number of particles.
uint32 maxParticles;
// The number of active particles.
uint32 activeParticles;
// The emission rate (particles/sec).
int emissionRate;
// Used to determine when a particle should be emitted.
float emitCounter;
// The relative position of the particle emitter.
love::Vector position;
// Emission area spread.
AreaSpreadDistribution areaSpreadDistribution;
love::Vector areaSpread;
// The lifetime of the particle emitter (-1 means infinite) and the life it has left.
float lifetime;
float life;
// The particle life.
float particleLifeMin;
float particleLifeMax;
// The direction (and spread) the particles will be emitted in. Measured in radians.
float direction;
float spread;
// The speed.
float speedMin;
float speedMax;
// Acceleration along the x and y axes.
love::Vector linearAccelerationMin;
love::Vector linearAccelerationMax;
// Acceleration towards the emitter's center
float radialAccelerationMin;
float radialAccelerationMax;
// Acceleration perpendicular to the particle's direction.
float tangentialAccelerationMin;
float tangentialAccelerationMax;
// Size.
std::vector<float> sizes;
float sizeVariation;
// Rotation
float rotationMin;
float rotationMax;
// Spin.
float spinStart;
float spinEnd;
float spinVariation;
// Offsets
float offsetX;
float offsetY;
// Color.
std::vector<Colorf> colors;
void createBuffers(size_t size);
void deleteBuffers();
void addParticle();
particle *removeParticle(particle *p);
// Called by addParticle.
void initParticle(particle *p);
void insertTop(particle *p);
void insertBottom(particle *p);
void insertRandom(particle *p);
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM>::Entry insertModesEntries[];
static StringMap<InsertMode, INSERT_MODE_MAX_ENUM> insertModes;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_PARTICLE_SYSTEM_H
@@ -0,0 +1,383 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include <algorithm>
// LOVE
#include "Polyline.h"
// OpenGL
#include "OpenGL.h"
// treat adjacent segments with angles between their directions <5 degree as straight
static const float LINES_PARALLEL_EPS = 0.05f;
namespace love
{
namespace graphics
{
namespace opengl
{
void Polyline::render(const float *coords, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw)
{
static std::vector<Vector> anchors;
anchors.clear();
anchors.reserve(size_hint);
static std::vector<Vector> normals;
normals.clear();
normals.reserve(size_hint);
// prepare vertex arrays
if (draw_overdraw)
halfwidth -= pixel_size * .3;
// compute sleeve
bool is_looping = (coords[0] == coords[count - 2]) && (coords[1] == coords[count - 1]);
Vector s;
if (!is_looping) // virtual starting point at second point mirrored on first point
s = Vector(coords[2] - coords[0], coords[3] - coords[1]);
else // virtual starting point at last vertex
s = Vector(coords[0] - coords[count - 4], coords[1] - coords[count - 3]);
float len_s = s.getLength();
Vector ns = s.getNormal(halfwidth / len_s);
Vector q, r(coords[0], coords[1]);
for (size_t i = 0; i + 3 < count; i += 2)
{
q = r;
r = Vector(coords[i + 2], coords[i + 3]);
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
}
q = r;
r = is_looping ? Vector(coords[2], coords[3]) : r + s;
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
vertex_count = normals.size();
vertices = new Vector[vertex_count];
for (size_t i = 0; i < vertex_count; ++i)
vertices[i] = anchors[i] + normals[i];
if (draw_overdraw)
render_overdraw(normals, pixel_size, is_looping);
}
void NoneJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(ns);
normals.push_back(-ns);
s = (r - q);
len_s = s.getLength();
ns = s.getNormal(hw / len_s);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(-ns);
normals.push_back(ns);
}
/** Calculate line boundary points.
*
* Sketch:
*
* u1
* -------------+---...___
* | ```'''-- ---
* p- - - - - - q- - . _ _ | w/2
* | ` ' ' r +
* -------------+---...___ | w/2
* u2 ```'''-- ---
*
* u1 and u2 depend on four things:
* - the half line width w/2
* - the previous line vertex p
* - the current line vertex q
* - the next line vertex r
*
* u1/u2 are the intersection points of the parallel lines to p-q and q-r,
* i.e. the point where
*
* (q + w/2 * ns) + lambda * (q - p) = (q + w/2 * nt) + mu * (r - q) (u1)
* (q - w/2 * ns) + lambda * (q - p) = (q - w/2 * nt) + mu * (r - q) (u2)
*
* with nt,nt being the normals on the segments s = p-q and t = q-r,
*
* ns = perp(s) / |s|
* nt = perp(t) / |t|.
*
* Using the linear equation system (similar for u2)
*
* q + w/2 * ns + lambda * s - (q + w/2 * nt + mu * t) = 0 (u1)
* <=> q-q + lambda * s - mu * t = (nt - ns) * w/2
* <=> lambda * s - mu * t = (nt - ns) * w/2
*
* the intersection points can be efficiently calculated using Cramer's rule.
*/
void MiterJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
Vector t = (r - q);
float len_t = t.getLength();
Vector nt = t.getNormal(hw / len_t);
anchors.push_back(q);
anchors.push_back(q);
float det = s ^ t;
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
normals.push_back(ns);
normals.push_back(-ns);
}
else
{
// cramers rule
float lambda = ((nt - ns) ^ t) / det;
Vector d = ns + s * lambda;
normals.push_back(d);
normals.push_back(-d);
}
s = t;
ns = nt;
len_s = len_t;
}
/** Calculate line boundary points.
*
* Sketch:
*
* uh1___uh2
* .' '.
* .' q '.
* .' ' ' '.
*.' ' .'. ' '.
* ' .' ul'. '
* p .' '. r
*
*
* ul can be found as above, uh1 and uh2 are much simpler:
*
* uh1 = q + ns * w/2, uh2 = q + nt * w/2
*/
void BevelJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw)
{
Vector t = (r - q);
float len_t = t.getLength();
float det = s ^ t;
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && s * t > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
Vector n = t.getNormal(hw / len_t);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(n);
normals.push_back(-n);
s = t;
len_s = len_t;
return; // early out
}
// cramers rule
Vector nt= t.getNormal(hw / len_t);
float lambda = ((nt - ns) ^ t) / det;
Vector d = ns + s * lambda;
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
if (det > 0) // 'left' turn -> intersection on the top
{
normals.push_back(d);
normals.push_back(-ns);
normals.push_back(d);
normals.push_back(-nt);
}
else
{
normals.push_back(ns);
normals.push_back(-d);
normals.push_back(nt);
normals.push_back(-d);
}
s = t;
len_s = len_t;
ns = nt;
}
void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
{
overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
overdraw = new Vector[overdraw_vertex_count];
// upper segment
for (size_t i = 0; i + 1 < vertex_count; i += 2)
{
overdraw[i] = vertices[i];
overdraw[i+1] = vertices[i] + normals[i] * (pixel_size / normals[i].getLength());
}
// lower segment
for (size_t i = 0; i + 1 < vertex_count; i += 2)
{
size_t k = vertex_count - i - 1;
overdraw[vertex_count + i] = vertices[k];
overdraw[vertex_count + i+1] = vertices[k] + normals[k] * (pixel_size / normals[i].getLength());
}
// if not looping, the outer overdraw vertices need to be displaced
// to cover the line endings, i.e.:
// +- - - - //- - + +- - - - - //- - - +
// +-------//-----+ : +-------//-----+ :
// | core // line | --> : | core // line | :
// +-----//-------+ : +-----//-------+ :
// +- - //- - - - + +- - - //- - - - - +
if (!is_looping)
{
// left edge
Vector spacer = (overdraw[1] - overdraw[3]);
spacer.normalize(pixel_size);
overdraw[1] += spacer;
overdraw[overdraw_vertex_count - 3] += spacer;
// right edge
spacer = (overdraw[vertex_count-1] - overdraw[vertex_count-3]);
spacer.normalize(pixel_size);
overdraw[vertex_count-1] += spacer;
overdraw[vertex_count+1] += spacer;
// we need to draw two more triangles to close the
// overdraw at the line start.
overdraw[overdraw_vertex_count-2] = overdraw[0];
overdraw[overdraw_vertex_count-1] = overdraw[1];
}
}
void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
{
overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
overdraw = new Vector[overdraw_vertex_count];
for (size_t i = 2; i + 3 < vertex_count; i += 4)
{
Vector s = vertices[i] - vertices[i+3];
Vector t = vertices[i] - vertices[i+1];
s.normalize(pixel_size);
t.normalize(pixel_size);
const size_t k = 4 * (i - 2);
overdraw[k ] = vertices[i];
overdraw[k+1] = vertices[i] + s + t;
overdraw[k+2] = vertices[i+1] + s - t;
overdraw[k+3] = vertices[i+1];
overdraw[k+4] = vertices[i+1];
overdraw[k+5] = vertices[i+1] + s - t;
overdraw[k+6] = vertices[i+2] - s - t;
overdraw[k+7] = vertices[i+2];
overdraw[k+8] = vertices[i+2];
overdraw[k+9] = vertices[i+2] - s - t;
overdraw[k+10] = vertices[i+3] - s + t;
overdraw[k+11] = vertices[i+3];
overdraw[k+12] = vertices[i+3];
overdraw[k+13] = vertices[i+3] - s + t;
overdraw[k+14] = vertices[i] + s + t;
overdraw[k+15] = vertices[i];
}
}
Polyline::~Polyline()
{
if (vertices)
delete[] vertices;
if (overdraw)
delete[] overdraw;
}
void Polyline::draw()
{
gl.prepareDraw();
// draw the core line
gl.bindTexture(0);
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) vertices);
glDrawArrays(draw_mode, 0, vertex_count);
if (overdraw)
{
// prepare colors:
Color c = gl.getColor();
Color *colors = new Color[overdraw_vertex_count];
fill_color_array(colors, c);
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, 0, (GLvoid *) overdraw);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colors);
glDrawArrays(draw_mode, 0, overdraw_vertex_count);
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setColor(c);
delete[] colors;
}
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
}
void Polyline::fill_color_array(Color *colors, const Color &c)
{
for (size_t i = 0; i < overdraw_vertex_count; ++i)
{
colors[i] = c;
// avoids branching. equiv to if (i%2 == 1) colors[i].a = 0;
colors[i].a *= GLubyte((i+1) % 2);
}
}
void NoneJoinPolyline::fill_color_array(Color *colors, const Color &c)
{
for (size_t i = 0; i < overdraw_vertex_count; ++i)
{
colors[i] = c;
// if (i % 4 == 1 || i % 4 == 2) colors[i].a = 0
colors[i].a *= GLubyte((i+1) % 4 < 2);
}
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,168 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_POLYLINE_H
#define LOVE_GRAPHICS_OPENGL_POLYLINE_H
#include <vector>
// LOVE
#include "common/Vector.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* Abstract base class for a chain of segments.
* @author Matthias Richter
**/
class Polyline
{
public:
Polyline(GLenum mode = GL_TRIANGLE_STRIP)
: vertices(NULL)
, overdraw(NULL)
, vertex_count(0)
, overdraw_vertex_count(0)
, draw_mode(mode)
{}
virtual ~Polyline();
/**
* @param vertices Vertices defining the core line segments
* @param count Number of coordinates (= size of the array vertices)
* @param size_hint Expected number of vertices of the rendering sleeve around the core line.
* @param halfwidth linewidth / 2.
* @param pixel_size Dimension of one pixel on the screen in world coordinates.
* @param draw_overdraw Fake antialias the line.
*/
void render(const float *vertices, size_t count, size_t size_hint, float halfwidth, float pixel_size, bool draw_overdraw);
/** Draws the line on the screen
*/
void draw();
protected:
virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
virtual void fill_color_array(Color *colors, const Color &c);
/** Calculate line boundary points.
*
* @param[out] anchors Anchor points defining the core line.
* @param[out] normals Normals defining the edge of the sleeve.
* @param[in,out] s Direction of segment pq (updated to the segment qr).
* @param[in,out] len_s Length of segment pq (updated to the segment qr).
* @param[in,out] ns Normal on the segment pq (updated to the segment qr).
* @param[in] q Current point on the line.
* @param[in] r Next point on the line.
* @param[in] hw Half line width (see Polyline.render()).
*/
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw) = 0;
Vector *vertices;
Vector *overdraw;
size_t vertex_count;
size_t overdraw_vertex_count;
GLenum draw_mode;
}; // Polyline
/**
* A Polyline whose segments are not connected.
* @author Matthias Richter
*/
class NoneJoinPolyline : public Polyline
{
public:
NoneJoinPolyline()
// TODO: replace GL_QUADS (indexed triangles?)
: Polyline(GL_QUADS)
{}
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
// discard the first and last two vertices. (these are redundant)
for (size_t i = 0; i < vertex_count - 2; ++i)
this->vertices[i] = this->vertices[i+2];
vertex_count -= 2;
}
protected:
virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
virtual void fill_color_array(Color *colors, const Color &c);
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
/**
* A Polyline whose segments are connected by a sharp edge.
* @author Matthias Richter
*/
class MiterJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, count, halfwidth, pixel_size, draw_overdraw);
}
protected:
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
/**
* A Polyline whose segments are connected by a flat edge.
* @author Matthias Richter
*/
class BevelJoinPolyline : public Polyline
{
public:
void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
{
Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
}
protected:
virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
Vector &s, float &len_s, Vector &ns,
const Vector &q, const Vector &r, float hw);
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_POLYLINE_H
@@ -0,0 +1,803 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include "Shader.h"
#include "Graphics.h"
#include <algorithm>
namespace love
{
namespace graphics
{
namespace opengl
{
namespace
{
// temporarily attaches a shader program (for setting uniforms, etc)
// reattaches the originally active program when destroyed
struct TemporaryAttacher
{
TemporaryAttacher(Shader *shader)
: curShader(shader)
, prevShader(Shader::current)
{
curShader->attach(true);
}
~TemporaryAttacher()
{
if (prevShader != nullptr)
prevShader->attach();
else
curShader->detach();
}
Shader *curShader;
Shader *prevShader;
};
} // anonymous namespace
Shader *Shader::current = nullptr;
Shader *Shader::defaultShader = nullptr;
Shader::ShaderSources Shader::defaultCode[Graphics::RENDERER_MAX_ENUM];
GLint Shader::maxTextureUnits = 0;
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSources &sources)
: shaderSources(sources)
, program(0)
, builtinUniforms()
{
if (shaderSources.empty())
throw love::Exception("Cannot create shader: no source code!");
if (maxTextureUnits <= 0)
{
GLint maxtexunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
maxTextureUnits = std::max(maxtexunits - 1, 0);
}
// initialize global texture id counters if needed
if (textureCounters.size() < (size_t) maxTextureUnits)
textureCounters.resize(maxTextureUnits, 0);
// load shader source and create program object
loadVolatile();
}
Shader::~Shader()
{
if (current == this)
detach();
for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it)
{
it->second->release();
boundRetainables.erase(it);
}
unloadVolatile();
}
GLuint Shader::compileCode(ShaderType type, const std::string &code)
{
GLenum glshadertype;
const char *typestr;
if (!typeNames.find(type, typestr))
typestr = "";
switch (type)
{
case TYPE_VERTEX:
glshadertype = GL_VERTEX_SHADER;
break;
case TYPE_PIXEL:
glshadertype = GL_FRAGMENT_SHADER;
break;
default:
throw love::Exception("Cannot create shader object: unknown shader type.");
break;
}
// clear existing errors
while (glGetError() != GL_NO_ERROR);
GLuint shaderid = glCreateShader(glshadertype);
if (shaderid == 0) // oh no!
{
GLenum err = glGetError();
if (err == GL_INVALID_ENUM)
throw love::Exception("Cannot create %s shader object: %s shaders not supported.", typestr, typestr);
else
throw love::Exception("Cannot create %s shader object.", typestr);
}
const char *src = code.c_str();
size_t srclen = code.length();
glShaderSource(shaderid, 1, (const GLchar **)&src, (GLint *)&srclen);
glCompileShader(shaderid);
// Get any warnings the shader compiler may have produced.
GLint infologlen;
glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
GLchar *infolog = new GLchar[infologlen + 1];
glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
// Save any warnings for later querying.
if (infologlen > 0)
shaderWarnings[type] = infolog;
delete[] infolog;
GLint status;
glGetShaderiv(shaderid, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
throw love::Exception("Cannot compile %s shader code:\n%s",
typestr, shaderWarnings[type].c_str());
}
return shaderid;
}
void Shader::createProgram(const std::vector<GLuint> &shaderids)
{
program = glCreateProgram();
if (program == 0)
throw love::Exception("Cannot create shader program object.");
std::vector<GLuint>::const_iterator it;
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glAttachShader(program, *it);
// We use generic vertex attributes in OpenGL ES 2, so we have to bind the
// attribute indices to names in the shader.
if (GLAD_ES_VERSION_2_0)
{
const char *name = nullptr;
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
{
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.
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glDeleteShader(*it);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
std::string warnings = getProgramWarnings();
glDeleteProgram(program);
program = 0;
throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str());
}
}
void Shader::mapActiveUniforms()
{
uniforms.clear();
GLint numuniforms;
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numuniforms);
GLsizei bufsize;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, (GLint *) &bufsize);
if (bufsize <= 0)
return;
for (int i = 0; i < numuniforms; i++)
{
GLchar *cname = new GLchar[bufsize];
GLsizei namelength;
Uniform u;
glGetActiveUniform(program, (GLuint) i, bufsize, &namelength, &u.count, &u.type, cname);
u.name = std::string(cname, (size_t) namelength);
u.location = glGetUniformLocation(program, u.name.c_str());
u.baseType = getUniformBaseType(u.type);
delete[] cname;
// glGetActiveUniform appends "[0]" to the end of array uniform names...
if (u.name.length() > 3)
{
size_t findpos = u.name.find("[0]");
if (findpos != std::string::npos && findpos == u.name.length() - 3)
u.name.erase(u.name.length() - 3);
}
// Store the uniform locations for any built-in extern variables, in ES.
if (GLAD_ES_VERSION_2_0)
{
BuiltinExtern builtin;
if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location;
}
if (u.location != -1)
uniforms[u.name] = u;
}
}
bool Shader::loadVolatile()
{
// zero out active texture list
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
// Built-in uniform locations default to -1 (nonexistant.)
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
std::vector<GLuint> shaderids;
ShaderSources::const_iterator source;
for (source = shaderSources.begin(); source != shaderSources.end(); ++source)
{
GLuint shaderid = compileCode(source->first, source->second);
shaderids.push_back(shaderid);
}
// All shader programs in ES2 must have a vertex and pixel shader.
if (GLAD_ES_VERSION_2_0)
{
ShaderSources &defaults = defaultCode[Graphics::RENDERER_OPENGLES];
source = shaderSources.find(TYPE_VERTEX);
if (source == shaderSources.end())
shaderids.push_back(compileCode(TYPE_VERTEX, defaults[TYPE_VERTEX]));
source = shaderSources.find(TYPE_PIXEL);
if (source == shaderSources.end())
shaderids.push_back(compileCode(TYPE_PIXEL, defaults[TYPE_PIXEL]));
}
if (shaderids.empty())
throw love::Exception("Cannot create shader: no valid source code!");
createProgram(shaderids);
// Retrieve all active uniform variables in this shader from OpenGL.
mapActiveUniforms();
if (current == this)
{
// make sure glUseProgram gets called.
current = nullptr;
attach();
}
return true;
}
void Shader::unloadVolatile()
{
if (current == this)
glUseProgram(0);
if (program != 0)
{
glDeleteProgram(program);
program = 0;
}
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
shaderWarnings.clear();
}
std::string Shader::getProgramWarnings() const
{
GLint strlen, nullpos;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &strlen);
char *tempstr = new char[strlen+1];
// be extra sure that the error string will be 0-terminated
memset(tempstr, '\0', strlen+1);
glGetProgramInfoLog(program, strlen, &nullpos, tempstr);
tempstr[nullpos] = '\0';
std::string warnings(tempstr);
delete[] tempstr;
return warnings;
}
std::string Shader::getWarnings() const
{
std::string warnings;
const char *typestr;
// Get the individual shader stage warnings
std::map<ShaderType, std::string>::const_iterator it;
for (it = shaderWarnings.begin(); it != shaderWarnings.end(); ++it)
{
if (typeNames.find(it->first, typestr))
warnings += std::string(typestr) + std::string(" shader:\n") + it->second;
}
warnings += getProgramWarnings();
return warnings;
}
void Shader::attach(bool temporary)
{
Shader *oldshader = current;
if (oldshader != this)
{
glUseProgram(program);
current = this;
current->retain();
if (oldshader != nullptr)
oldshader->release();
}
if (!temporary)
{
// make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures!
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
gl.bindTextureToUnit(activeTextureUnits[i], i + 1, false);
}
// We always want to use texture unit 0 for everyhing else.
gl.setTextureUnit(0);
}
}
void Shader::detach()
{
// We always need a shader set in ES2.
if (GLAD_ES_VERSION_2_0)
{
if (defaultShader && current != defaultShader)
defaultShader->attach();
}
else
{
if (current != nullptr)
{
glUseProgram(0);
current = nullptr;
current->release();
}
}
}
const Shader::Uniform &Shader::getUniform(const std::string &name) const
{
std::map<std::string, Uniform>::const_iterator it = uniforms.find(name);
if (it == uniforms.end())
throw love::Exception("Variable '%s' does not exist.\n"
"A common error is to define but not use the variable.", name.c_str());
return it->second;
}
int Shader::getUniformTypeSize(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_FLOAT:
case GL_BOOL:
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return 1;
case GL_INT_VEC2:
case GL_FLOAT_VEC2:
case GL_FLOAT_MAT2:
case GL_BOOL_VEC2:
return 2;
case GL_INT_VEC3:
case GL_FLOAT_VEC3:
case GL_FLOAT_MAT3:
case GL_BOOL_VEC3:
return 3;
case GL_INT_VEC4:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT4:
case GL_BOOL_VEC4:
return 4;
default:
break;
}
return 1;
}
Shader::UniformType Shader::getUniformBaseType(GLenum type) const
{
switch (type)
{
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_FLOAT;
case GL_BOOL:
case GL_BOOL_VEC2:
case GL_BOOL_VEC3:
case GL_BOOL_VEC4:
return UNIFORM_BOOL;
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
return UNIFORM_SAMPLER;
default:
break;
}
return UNIFORM_UNKNOWN;
}
void Shader::checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const
{
if (!program)
throw love::Exception("No active shader program.");
int realsize = getUniformTypeSize(u.type);
if (size != realsize)
throw love::Exception("Value size of %d does not match variable size of %d.", size, realsize);
if ((u.count == 1 && count > 1) || count < 0)
throw love::Exception("Invalid number of values (expected %d, got %d).", u.count, count);
if (u.baseType == UNIFORM_SAMPLER && sendtype != u.baseType)
throw love::Exception("Cannot send a value of this type to an Image variable.");
if ((sendtype == UNIFORM_FLOAT && u.baseType == UNIFORM_INT) || (sendtype == UNIFORM_INT && u.baseType == UNIFORM_FLOAT))
throw love::Exception("Cannot convert between float and int.");
}
void Shader::sendInt(const std::string &name, int size, const GLint *vec, int count)
{
TemporaryAttacher attacher(this);
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_INT);
switch (size)
{
case 4:
glUniform4iv(u.location, count, vec);
break;
case 3:
glUniform3iv(u.location, count, vec);
break;
case 2:
glUniform2iv(u.location, count, vec);
break;
case 1:
default:
glUniform1iv(u.location, count, vec);
break;
}
}
void Shader::sendFloat(const std::string &name, int size, const GLfloat *vec, int count)
{
TemporaryAttacher attacher(this);
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
switch (size)
{
case 4:
glUniform4fv(u.location, count, vec);
break;
case 3:
glUniform3fv(u.location, count, vec);
break;
case 2:
glUniform2fv(u.location, count, vec);
break;
case 1:
default:
glUniform1fv(u.location, count, vec);
break;
}
}
void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int count)
{
TemporaryAttacher attacher(this);
if (size < 2 || size > 4)
{
throw love::Exception("Invalid matrix size: %dx%d "
"(can only set 2x2, 3x3 or 4x4 matrices.)", size,size);
}
const Uniform &u = getUniform(name);
checkSetUniformError(u, size, count, UNIFORM_FLOAT);
switch (size)
{
case 4:
glUniformMatrix4fv(u.location, count, GL_FALSE, m);
break;
case 3:
glUniformMatrix3fv(u.location, count, GL_FALSE, m);
break;
case 2:
default:
glUniformMatrix2fv(u.location, count, GL_FALSE, m);
break;
}
}
void Shader::sendTexture(const std::string &name, GLuint texture)
{
TemporaryAttacher attacher(this);
int textureunit = getTextureUnit(name);
const Uniform &u = getUniform(name);
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(texture, textureunit, false);
glUniform1i(u.location, textureunit);
// reset texture unit
gl.setTextureUnit(0);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTextureUnits[textureunit-1] == 0)
++textureCounters[textureunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTextureUnits[textureunit-1] = texture;
}
void Shader::retainTexture(const std::string &name, Object *texture)
{
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
texture->retain();
boundRetainables[name] = texture;
}
void Shader::sendImage(const std::string &name, Image &image)
{
sendTexture(name, image.getTextureName());
retainTexture(name, &image);
}
void Shader::sendCanvas(const std::string &name, Canvas &canvas)
{
sendTexture(name, canvas.getTextureName());
retainTexture(name, &canvas);
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = textureUnitPool.find(name);
if (it != textureUnitPool.end())
return it->second;
int textureunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
if (freeunit_it != textureCounters.end())
{
// we don't want to use unit 0
textureunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTextureUnits.begin(), activeTextureUnits.end(), 0);
if (nextunit_it == activeTextureUnits.end())
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
textureunit = std::distance(activeTextureUnits.begin(), nextunit_it) + 1;
}
textureUnitPool[name] = textureunit;
return textureunit;
}
bool Shader::hasBuiltinUniform(love::graphics::opengl::Shader::BuiltinExtern builtin) const
{
return builtinUniforms[int(builtin)] != -1;
}
bool Shader::sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m, int count)
{
if (!hasBuiltinUniform(builtin))
return false;
GLint location = builtinUniforms[GLint(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 2:
glUniformMatrix2fv(location, count, GL_FALSE, m);
break;
case 3:
glUniformMatrix3fv(location, count, GL_FALSE, m);
break;
case 4:
glUniformMatrix4fv(location, count, GL_FALSE, m);
break;
default:
return false;
}
return true;
}
bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *vec, int count)
{
if (!hasBuiltinUniform(builtin))
return false;
GLint location = builtinUniforms[GLint(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 1:
glUniform1fv(location, count, vec);
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 3:
glUniform3fv(location, count, vec);
break;
case 4:
glUniform4fv(location, count, vec);
break;
default:
return false;
}
return true;
}
std::string Shader::getGLSLVersion()
{
const char *tmp = nullptr;
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
if (GLAD_ES_VERSION_2_0 || GLAD_VERSION_2_0 || GLAD_ARB_shading_language_100)
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if (tmp == nullptr)
return "0.0";
// the version string always begins with a version number of the format
// major_number.minor_number
// or
// major_number.minor_number.release_number
// we can keep release_number, since it does not affect the check below.
std::string versionstring(tmp);
size_t minorendpos = versionstring.find(' ');
return versionstring.substr(0, minorendpos);
}
bool Shader::isSupported()
{
return GLAD_ES_VERSION_2_0 || (GLAD_VERSION_2_0 && getGLSLVersion() >= "1.2");
}
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntries[] =
{
{"vertex", Shader::TYPE_VERTEX},
{"pixel", Shader::TYPE_PIXEL},
};
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
{
{"VertexPosition", OpenGL::ATTRIB_POS},
{"VertexTexCoord", OpenGL::ATTRIB_TEXCOORD},
{"VertexColor", OpenGL::ATTRIB_COLOR},
};
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{"TransformMatrix", Shader::BUILTIN_TRANSFORM_MATRIX},
{"ProjectionMatrix", Shader::BUILTIN_PROJECTION_MATRIX},
{"TransformProjectionMatrix", Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX},
{"love_PointSize", Shader::BUILTIN_POINT_SIZE},
};
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
} // opengl
} // graphics
} // love
@@ -0,0 +1,252 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_SHADER_H
#define LOVE_GRAPHICS_SHADER_H
// LOVE
#include "common/Object.h"
#include "common/StringMap.h"
#include "graphics/Graphics.h"
#include "OpenGL.h"
#include "Image.h"
#include "Canvas.h"
// STL
#include <string>
#include <map>
#include <vector>
namespace love
{
namespace graphics
{
namespace opengl
{
// A GLSL shader
class Shader : public Object, public Volatile
{
public:
// Pointer to currently active Shader.
static Shader *current;
// Pointer to the current default Shader.
static Shader *defaultShader;
enum ShaderType
{
TYPE_VERTEX,
TYPE_PIXEL,
TYPE_MAX_ENUM
};
// Built-in extern (uniform) variables.
enum BuiltinExtern
{
BUILTIN_TRANSFORM_MATRIX = 0,
BUILTIN_PROJECTION_MATRIX,
BUILTIN_TRANSFORM_PROJECTION_MATRIX,
BUILTIN_POINT_SIZE,
BUILTIN_MAX_ENUM
};
// Type for a list of shader source codes in the form of sources[shadertype] = code
typedef std::map<ShaderType, std::string> ShaderSources;
/**
* Creates a new Shader using a list of source codes.
* Sources must contain either vertex or pixel shader code, or both.
**/
Shader(const ShaderSources &sources);
virtual ~Shader();
// Implements Volatile
virtual bool loadVolatile();
virtual void unloadVolatile();
/**
* Binds this Shader's program to be used when rendering.
*
* @param temporary True if we just want to send values to the shader with no intention of rendering.
**/
void attach(bool temporary = false);
/**
* Detach the currently bound Shader.
* Causes the GPU rendering pipeline to use fixed functionality in place of shader programs.
**/
static void detach();
/**
* Returns any warnings this Shader may have generated.
**/
std::string getWarnings() const;
/**
* Send at least one integer or int-vector value to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of elements in each vector to send.
* A value of 1 indicates a single-component vector (an int).
* @param vec Pointer to the integer or int-vector values.
* @param count Number of integer or int-vector values.
**/
void sendInt(const std::string &name, int size, const GLint *vec, int count);
/**
* Send at least one float or vector value to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of elements in each vector to send.
* A value of 1 indicates a single-component vector (a float).
* @param vec Pointer to the float or float-vector values.
* @param count Number of float or float-vector values.
**/
void sendFloat(const std::string &name, int size, const GLfloat *vec, int count);
/**
* Send at least one matrix to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
* @param size Number of rows/columns in the matrix.
* @param m Pointer to the first element of the first matrix.
* @param count Number of matrices to send.
**/
void sendMatrix(const std::string &name, int size, const GLfloat *m, int count);
/**
* Send an image to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
**/
void sendImage(const std::string &name, Image &image);
/**
* Send a canvas to this Shader as a uniform.
*
* @param name The name of the uniform variable in the source code.
**/
void sendCanvas(const std::string &name, Canvas &canvas);
/**
* Internal use only.
**/
bool hasBuiltinUniform(BuiltinExtern builtin) const;
bool sendBuiltinMatrix(BuiltinExtern builtin, int size, const GLfloat *m, int count);
bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
static std::string getGLSLVersion();
static bool isSupported();
// Default code used when renderers require code for a shader stage.
static ShaderSources defaultCode[Graphics::RENDERER_MAX_ENUM];
private:
// Types of potential uniform variables used in love's shaders.
enum UniformType
{
UNIFORM_FLOAT,
UNIFORM_INT,
UNIFORM_BOOL,
UNIFORM_SAMPLER,
UNIFORM_UNKNOWN
};
// Represents a single uniform/extern shader variable.
struct Uniform
{
GLint location;
GLint count;
GLenum type;
UniformType baseType;
std::string name;
};
// Map active uniform names to their locations.
void mapActiveUniforms();
const Uniform &getUniform(const std::string &name) const;
int getUniformTypeSize(GLenum type) const;
UniformType getUniformBaseType(GLenum type) const;
void checkSetUniformError(const Uniform &u, int size, int count, UniformType sendtype) const;
GLuint compileCode(ShaderType type, const std::string &code);
void createProgram(const std::vector<GLuint> &shaderids);
int getTextureUnit(const std::string &name);
void sendTexture(const std::string &name, GLuint texture);
void retainTexture(const std::string &name, Object *texture);
// Get any warnings or errors generated only by the shader program object.
std::string getProgramWarnings() const;
// List of all shader code attached to this Shader
ShaderSources shaderSources;
// Shader compiler warning strings for individual shader stages.
std::map<ShaderType, std::string> shaderWarnings;
// volatile
GLuint program;
// Locations for any built-in uniform variables.
GLint builtinUniforms[BUILTIN_MAX_ENUM];
// Uniform location buffer map
std::map<std::string, Uniform> uniforms;
// Texture unit pool for setting images
std::map<std::string, GLint> textureUnitPool; // textureUnitPool[name] = textureunit
std::vector<GLuint> activeTextureUnits; // activeTextureUnits[textureunit-1] = textureid
// Uniform name to retainable objects
std::map<std::string, Object*> boundRetainables;
// Max GPU texture units available for sent images
static GLint maxTextureUnits;
// Counts total number of textures bound to each texture unit in all shaders
static std::vector<int> textureCounters;
static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
// Names for the generic vertex attributes used in OpenGL ES 2.
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
// Names for the uniform matrices used in OpenGL ES 2.
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
}; // Shader
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_SHADER_H
@@ -0,0 +1,358 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include "SpriteBatch.h"
// OpenGL
#include "OpenGL.h"
// LOVE
#include "Image.h"
#include "VertexBuffer.h"
// C++
#include <algorithm>
// C
#include <stddef.h>
namespace love
{
namespace graphics
{
namespace opengl
{
SpriteBatch::SpriteBatch(Image *image, int size, int usage)
: image(image)
, size(size)
, next(0)
, color(0)
, array_buf(0)
, element_buf(0)
{
if (size <= 0)
throw love::Exception("Invalid SpriteBatch size.");
GLenum gl_usage;
switch (usage)
{
default:
case USAGE_DYNAMIC:
gl_usage = GL_DYNAMIC_DRAW;
break;
case USAGE_STATIC:
gl_usage = GL_STATIC_DRAW;
break;
case USAGE_STREAM:
gl_usage = GL_STREAM_DRAW;
break;
}
const size_t vertex_size = sizeof(Vertex) * 4 * size;
try
{
array_buf = VertexBuffer::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
element_buf = new VertexIndex(size);
}
catch (love::Exception &)
{
delete array_buf;
delete element_buf;
throw;
}
catch (std::bad_alloc &)
{
delete array_buf;
delete element_buf;
throw love::Exception("Out of memory.");
}
image->retain();
}
SpriteBatch::~SpriteBatch()
{
image->release();
delete color;
delete array_buf;
delete element_buf;
}
int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index /*= -1*/)
{
// Only do this if there's a free slot.
if ((index == -1 && next >= size) || index < -1 || index >= size)
return -1;
// Needed for colors.
memcpy(sprite, image->getVertices(), sizeof(Vertex)*4);
// Transform.
static Matrix t;
t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
t.transform(sprite, sprite, 4);
if (color)
setColorv(sprite, *color);
addv(sprite, (index == -1) ? next : index);
// Increment counter.
if (index == -1)
return next++;
return index;
}
int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index /*= -1*/)
{
// Only do this if there's a free slot.
if ((index == -1 && next >= size) || index < -1 || index >= next)
return -1;
// Needed for colors.
memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4);
static Matrix t;
t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
t.transform(sprite, sprite, 4);
if (color)
setColorv(sprite, *color);
addv(sprite, (index == -1) ? next : index);
// Increment counter.
if (index == -1)
return next++;
return index;
}
void SpriteBatch::clear()
{
// Reset the position of the next index.
next = 0;
}
void *SpriteBatch::lock()
{
VertexBuffer::Bind bind(*array_buf);
return array_buf->map();
}
void SpriteBatch::unlock()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap();
}
void SpriteBatch::setImage(Image *newimage)
{
Object::AutoRelease imagerelease(image);
newimage->retain();
image = newimage;
}
Image *SpriteBatch::getImage()
{
return image;
}
void SpriteBatch::setColor(const Color &color)
{
if (!this->color)
this->color = new Color(color);
else
*(this->color) = color;
}
void SpriteBatch::setColor()
{
delete color;
color = 0;
}
const Color *SpriteBatch::getColor() const
{
return color;
}
int SpriteBatch::getCount() const
{
return next;
}
void SpriteBatch::setBufferSize(int newsize)
{
if (newsize <= 0)
throw love::Exception("Invalid SpriteBatch size.");
if (newsize == size)
return;
// Map (lock) the old VertexBuffer to get a pointer to its data.
void *old_data = lock();
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
VertexBuffer *new_array_buf = 0;
VertexIndex *new_element_buf = 0;
void *new_data = 0;
try
{
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
new_element_buf = new VertexIndex(newsize);
// VBO::map can throw an exception. Also we want to scope the bind.
VertexBuffer::Bind bind(*new_array_buf);
new_data = new_array_buf->map();
}
catch (love::Exception &)
{
delete new_array_buf;
delete new_element_buf;
unlock();
throw;
}
// Copy as much of the old data into the new VertexBuffer as can fit.
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
// We don't need to unmap the old VertexBuffer since we're deleting it.
delete array_buf;
delete element_buf;
array_buf = new_array_buf;
element_buf = new_element_buf;
size = newsize;
next = std::min(next, newsize);
// But we should unmap (unlock) the new one!
unlock();
}
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
{
const size_t vertex_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
if (next == 0)
return;
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
gl.matrices.transform.push(gl.matrices.transform.top());
gl.matrices.transform.top() *= t;
image->predraw();
gl.prepareDraw();
VertexBuffer::Bind array_bind(*array_buf);
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(vertex_offset));
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
Color curcolor = gl.getColor();
// Apply per-sprite color, if a color is set.
if (color)
{
gl.enableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setVertexAttribArray(OpenGL::ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, sizeof(Vertex), array_buf->getPointer(color_offset));
}
glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
if (color)
{
gl.disableVertexAttribArray(OpenGL::ATTRIB_COLOR);
gl.setColor(curcolor);
}
image->postdraw();
gl.matrices.transform.pop();
}
void SpriteBatch::addv(const Vertex *v, int index)
{
static const int sprite_size = 4 * sizeof(Vertex); // bytecount
VertexBuffer::Bind bind(*array_buf);
array_buf->fill(index * sprite_size, sprite_size, v);
}
void SpriteBatch::setColorv(Vertex *v, const Color &color)
{
for (size_t i = 0; i < 4; ++i)
{
v[i].r = color.r;
v[i].g = color.g;
v[i].b = color.b;
v[i].a = color.a;
}
}
bool SpriteBatch::getConstant(const char *in, UsageHint &out)
{
return usageHints.find(in, out);
}
bool SpriteBatch::getConstant(UsageHint in, const char *&out)
{
return usageHints.find(in, out);
}
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM>::Entry SpriteBatch::usageHintEntries[] =
{
{"dynamic", SpriteBatch::USAGE_DYNAMIC},
{"static", SpriteBatch::USAGE_STATIC},
{"stream", SpriteBatch::USAGE_STREAM},
};
StringMap<SpriteBatch::UsageHint, SpriteBatch::USAGE_MAX_ENUM> SpriteBatch::usageHints(usageHintEntries, sizeof(usageHintEntries));
} // opengl
} // graphics
} // love
@@ -0,0 +1,156 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
#define LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
// C
#include <cstring>
// LOVE
#include "common/math.h"
#include "common/Object.h"
#include "common/Matrix.h"
#include "common/StringMap.h"
#include "graphics/Drawable.h"
#include "graphics/Volatile.h"
#include "graphics/Color.h"
#include "graphics/Quad.h"
namespace love
{
namespace graphics
{
namespace opengl
{
// Forward declarations.
class Image;
class VertexBuffer;
class VertexIndex;
class SpriteBatch : public Drawable
{
public:
enum UsageHint
{
USAGE_DYNAMIC = 1,
USAGE_STATIC,
USAGE_STREAM,
USAGE_MAX_ENUM
};
SpriteBatch(Image *image, int size, int usage);
virtual ~SpriteBatch();
int add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
int addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
void clear();
void *lock();
void unlock();
void setImage(Image *newimage);
Image *getImage();
/**
* Set the current color for this SpriteBatch. The sprites added
* after this call will use this color. Note that global color
* will not longer apply to the SpriteBatch if this is used.
*
* @param color The color to use for the following sprites.
*/
void setColor(const Color &color);
/**
* Disable per-sprite colors for this SpriteBatch. The next call to
* draw will use the global color for all sprites.
*/
void setColor();
/**
* Get the current color for this SpriteBatch. Returns NULL if no color is
* set.
**/
const Color *getColor() const;
/**
* Get the number of sprites currently in this SpriteBatch.
**/
int getCount() const;
/**
* Sets the total number of sprites this SpriteBatch can hold.
* Leaves existing sprite data intact when possible.
**/
void setBufferSize(int newsize);
/**
* Get the total number of sprites this SpriteBatch can hold.
**/
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;
static bool getConstant(const char *in, UsageHint &out);
static bool getConstant(UsageHint in, const char *&out);
private:
void addv(const Vertex *v, int index);
/**
* Set the color for vertices.
*
* @param v The vertices to set the color for. Must be an array of
* of size 4.
* @param color The color to assign to each vertex.
*/
void setColorv(Vertex *v, const Color &color);
Image *image;
// Max number of sprites in the batch.
int size;
// 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;
VertexBuffer *array_buf;
VertexIndex *element_buf;
static StringMap<UsageHint, USAGE_MAX_ENUM>::Entry usageHintEntries[];
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
}; // SpriteBatch
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
@@ -0,0 +1,468 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "VertexBuffer.h"
#include "common/Exception.h"
#include "common/config.h"
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <limits>
// Conflicts with std::numeric_limits<GLushort>::max() (Windows).
#ifdef max
# undef max
#endif
namespace love
{
namespace graphics
{
namespace opengl
{
// VertexBuffer
VertexBuffer *VertexBuffer::Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
{
try
{
// Try to create a VBO.
return new VBO(size, target, usage, backing);
}
catch(const love::Exception &)
{
// VBO not supported ... create regular array.
return new VertexArray(size, target, usage, backing);
}
}
VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: is_bound(false)
, is_mapped(false)
, size(size)
, target(target)
, usage(usage)
, backing(backing)
{
}
VertexBuffer::~VertexBuffer()
{
}
// VertexArray
VertexArray::VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, buf(new char[size])
{
}
VertexArray::~VertexArray()
{
delete [] buf;
}
void *VertexArray::map()
{
is_mapped = true;
return buf;
}
void VertexArray::unmap()
{
is_mapped = false;
}
void VertexArray::bind()
{
is_bound = true;
}
void VertexArray::unbind()
{
is_bound = false;
}
void VertexArray::fill(size_t offset, size_t size, const void *data)
{
memcpy(buf + offset, data, size);
}
const void *VertexArray::getPointer(size_t offset) const
{
return buf + offset;
}
// VBO
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
: VertexBuffer(size, target, usage, backing)
, vbo(0)
, memory_map(0)
, is_dirty(false)
{
if (!(GLAD_ARB_vertex_buffer_object || GLAD_VERSION_1_5 || GLAD_ES_VERSION_2_0))
throw love::Exception("Not supported");
// FIXME:
// ES2 can't do glGetBufferSubData.
if (GLAD_ES_VERSION_2_0)
backing = BACKING_FULL;
if (getMemoryBacking() == BACKING_FULL)
memory_map = malloc(getSize());
bool ok = load(false);
if (!ok)
{
free(memory_map);
throw love::Exception("Could not load VBO.");
}
}
VBO::~VBO()
{
if (vbo != 0)
unload(false);
if (memory_map)
free(memory_map);
}
void *VBO::map()
{
if (is_mapped)
return memory_map;
if (!memory_map)
{
memory_map = malloc(getSize());
if (!memory_map)
throw love::Exception("Out of memory (oh the humanity!)");
}
if (is_dirty)
{
glGetBufferSubData(getTarget(), 0, getSize(), memory_map);
is_dirty = false;
}
is_mapped = true;
return memory_map;
}
void VBO::unmap()
{
if (!is_mapped)
return;
// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
// bound here.
if (!is_bound)
{
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
glBufferData(getTarget(), getSize(), NULL, getUsage());
glBufferData(getTarget(), getSize(), memory_map, getUsage());
is_mapped = false;
}
void VBO::bind()
{
if (!is_mapped)
{
glBindBuffer(getTarget(), vbo);
is_bound = true;
}
}
void VBO::unbind()
{
if (is_bound)
glBindBuffer(getTarget(), 0);
is_bound = false;
}
void VBO::fill(size_t offset, size_t size, const void *data)
{
if (is_mapped || getMemoryBacking() == BACKING_FULL)
memcpy(static_cast<char *>(memory_map) + offset, data, size);
if (!is_mapped)
{
// Not all systems have access to some faster paths...
if (GLAD_APPLE_flush_buffer_range)
{
void *mapdata = glMapBuffer(getTarget(), GL_WRITE_ONLY);
if (mapdata)
{
// We specified in VBO::load that we'll do manual flushing.
// Now we tell the driver it only needs to deal with the data
// we changed.
memcpy(static_cast<char *>(mapdata) + offset, data, size);
glFlushMappedBufferRangeAPPLE(getTarget(), offset, size);
}
glUnmapBuffer(getTarget());
}
else
{
// Fall back to a possibly slower SubData (more chance of syncing.)
glBufferSubData(getTarget(), offset, size, data);
}
if (getMemoryBacking() != BACKING_FULL)
is_dirty = true;
}
}
const void *VBO::getPointer(size_t offset) const
{
return BUFFER_OFFSET(offset);
}
bool VBO::loadVolatile()
{
return load(true);
}
void VBO::unloadVolatile()
{
unload(true);
}
bool VBO::load(bool restore)
{
glGenBuffers(1, &vbo);
VertexBuffer::Bind bind(*this);
// Copy the old buffer only if 'restore' was requested.
const GLvoid *src = restore ? memory_map : 0;
while (GL_NO_ERROR != glGetError())
/* clear error messages */;
// We don't want to flush the entire buffer when we just modify a small
// portion of it (VBO::fill without VBO::map), so we'll handle the flushing
// ourselves when we can.
if (GLAD_APPLE_flush_buffer_range)
glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
// Note that if 'src' is '0', no data will be copied.
glBufferData(getTarget(), getSize(), src, getUsage());
GLenum err = glGetError();
return (GL_NO_ERROR == err);
}
void VBO::unload(bool save)
{
// Save data before unloading, if we need to.
if (save && getMemoryBacking() == BACKING_PARTIAL)
{
VertexBuffer::Bind bind(*this);
bool mapped = is_mapped;
map(); // saves buffer content to memory_map.
is_mapped = mapped;
}
glDeleteBuffers(1, &vbo);
vbo = 0;
}
// VertexIndex
size_t VertexIndex::maxSize = 0;
size_t VertexIndex::elementSize = 0;
std::list<size_t> VertexIndex::sizeRefs;
VertexBuffer *VertexIndex::element_array = NULL;
VertexIndex::VertexIndex(size_t size)
: size(size)
{
// The upper limit is the maximum of GLuint divided by six (the number
// of indices per size) and divided by the size of GLuint. This guarantees
// no overflows when calculating the array size in bytes.
// Memory issues will be handled by other exceptions.
if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint))
throw love::Exception("Invalid size.");
addSize(size);
}
VertexIndex::~VertexIndex()
{
removeSize(size);
}
size_t VertexIndex::getSize() const
{
return size;
}
size_t VertexIndex::getIndexCount(size_t elements) const
{
return elements * 6;
}
GLenum VertexIndex::getType(size_t s) const
{
// Calculates if unsigned short is big enough to hold all the vertex indices.
static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT};
return type_table[s * 4 > std::numeric_limits<GLushort>::max()];
// if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT
}
size_t VertexIndex::getElementSize()
{
return elementSize;
}
VertexBuffer *VertexIndex::getVertexBuffer() const
{
return element_array;
}
const void *VertexIndex::getPointer(size_t offset) const
{
return element_array->getPointer(offset);
}
void VertexIndex::addSize(size_t newSize)
{
if (newSize <= maxSize)
{
// Current size is bigger. Append the size to list and sort.
sizeRefs.push_back(newSize);
sizeRefs.sort();
return;
}
// Try to resize before adding it to the list because resize may throw.
resize(newSize);
sizeRefs.push_back(newSize);
}
void VertexIndex::removeSize(size_t oldSize)
{
// TODO: For debugging purposes, this should check if the size was actually found.
sizeRefs.erase(std::find(sizeRefs.begin(), sizeRefs.end(), oldSize));
if (sizeRefs.size() == 0)
{
resize(0);
return;
}
if (oldSize == maxSize)
{
// Shrink if there's a smaller size.
size_t newSize = sizeRefs.back();
if (newSize < maxSize)
resize(newSize);
}
}
void VertexIndex::resize(size_t size)
{
if (size == 0)
{
delete element_array;
element_array = NULL;
maxSize = 0;
return;
}
VertexBuffer *new_element_array;
// Depending on the size, a switch to int and more memory is needed.
GLenum target_type = getType(size);
size_t elem_size = (target_type == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint);
size_t array_size = elem_size * 6 * size;
// Create may throw out-of-memory exceptions.
// VertexIndex will propagate the exception and keep the old VertexBuffer.
try
{
new_element_array = VertexBuffer::Create(array_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
}
catch (std::bad_alloc &)
{
throw love::Exception("Out of memory.");
}
// Allocation of the new VertexBuffer succeeded.
// The old VertexBuffer can now be deleted.
delete element_array;
element_array = new_element_array;
maxSize = size;
elementSize = elem_size;
switch (target_type)
{
case GL_UNSIGNED_SHORT:
fill<GLushort>();
break;
case GL_UNSIGNED_INT:
fill<GLuint>();
break;
}
}
template <typename T>
void VertexIndex::fill()
{
VertexBuffer::Bind bind(*element_array);
VertexBuffer::Mapper mapper(*element_array);
T *indices = (T *) mapper.get();
for (size_t i = 0; i < maxSize; ++i)
{
indices[i*6+0] = i * 4 + 0;
indices[i*6+1] = i * 4 + 1;
indices[i*6+2] = i * 4 + 2;
indices[i*6+3] = i * 4 + 0;
indices[i*6+4] = i * 4 + 2;
indices[i*6+5] = i * 4 + 3;
}
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,519 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
#define LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
// LOVE
#include "graphics/Volatile.h"
// OpenGL
#include "OpenGL.h"
namespace love
{
namespace graphics
{
namespace opengl
{
/**
* VertexBuffer is an abstraction over VBOs (Vertex Buffer Objects), which
* falls back to regular vertex arrays if VBOs are not supported.
*
* This allows code to take advantage of VBOs where available, but still
* work on older systems where it's *not* available. Everyone's happy.
*
* The class is (for now) meant for internal use.
*/
class VertexBuffer
{
public:
// Different guarantees for VertexBuffer data storage.
enum MemoryBacking
{
// The VertexBuffer is will have a valid copy of its data in main memory
// at all times.
BACKING_FULL,
// The VertexBuffer will have a valid copy of its data in main memory
// when it needs to be reloaded and when it's mapped.
BACKING_PARTIAL
};
/**
* Create a new VertexBuffer (either a plain vertex array, or a VBO),
* based on what's supported on the system.
*
* If VBOs are not supported, a plain vertex array will automatically
* be created and returned instead.
*
* @param size The size of the VertexBuffer (in bytes).
* @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
* @param usage GL_DYNAMIC_DRAW, etc.
* @param backing Determines what guarantees are placed on the data.
* @return A new VertexBuffer.
*/
static VertexBuffer *Create(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
/**
* Constructor.
*
* @param size The size of the VertexBuffer in bytes.
* @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
* @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
* @param backing Determines what guarantees are placed on the data.
*/
VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacking backing = BACKING_PARTIAL);
/**
* Destructor. Does nothing, but must be declared virtual.
*/
virtual ~VertexBuffer();
/**
* Get the size of the VertexBuffer, in bytes.
*
* @return The size of the VertexBuffer.
*/
size_t getSize() const
{
return size;
}
/**
* Get the target buffer object.
*
* @return The target buffer object, e.g. GL_ARRAY_BUFFER.
*/
GLenum getTarget() const
{
return target;
}
/**
* Get the usage hint for this VertexBuffer.
*
* @return The usage hint, e.g. GL_DYNAMIC_DRAW.
*/
GLenum getUsage() const
{
return usage;
}
bool isBound() const
{
return is_bound;
}
bool isMapped() const
{
return is_mapped;
}
MemoryBacking getMemoryBacking() const
{
return backing;
}
/**
* Map the VertexBuffer to client memory.
*
* This can be faster for large changes to the buffer. For smaller
* changes, see fill().
*
* The VertexBuffer must be bound to use this function.
*
* @return A pointer to memory which represents the buffer.
*/
virtual void *map() = 0;
/**
* Unmap a previously mapped VertexBuffer. The buffer must be unmapped
* when used to draw elements.
*
* The VertexBuffer must be bound to use this function.
*/
virtual void unmap() = 0;
/**
* Bind the VertexBuffer to its specified target.
* (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
*/
virtual void bind() = 0;
/**
* Unbind a prevously bound VertexBuffer.
*/
virtual void unbind() = 0;
/**
* Fill a portion of the buffer with data.
*
* The VertexBuffer must be bound to use this function.
*
* @param offset The offset in the VertexBuffer to store the data.
* @param size The size of the incoming data.
* @param data Pointer to memory to copy data from.
*/
virtual void fill(size_t offset, size_t size, const void *data) = 0;
/**
* Get a pointer which represents the specified byte offset.
*
* @param offset The byte offset. (0 is first byte).
* @return A pointer which represents the offset.
*/
virtual const void *getPointer(size_t offset) const = 0;
/**
* This helper class can bind a VertexArray temporarily, and
* automatically un-bind when it's destroyed.
*/
class Bind
{
public:
/**
* Bind a VertexBuffer.
*/
Bind(VertexBuffer &buf)
: buf(buf)
{
buf.bind();
}
/**
* Unbinds a VertexBuffer.
*/
~Bind()
{
buf.unbind();
}
private:
// VertexBuffer to work on.
VertexBuffer &buf;
};
class Mapper
{
public:
/**
* Memory-maps a VertexBuffer.
*/
Mapper(VertexBuffer &buffer)
: buf(buffer)
{
elems = buf.map();
}
/**
* unmaps the buffer
*/
~Mapper()
{
buf.unmap();
}
/**
* Get pointer to memory mapped region
*/
void *get()
{
return elems;
}
private:
VertexBuffer &buf;
void *elems;
};
protected:
// Whether the buffer is currently bound.
bool is_bound;
// Whether the buffer is currently mapped to main memory.
bool is_mapped;
private:
// The size of the buffer, in bytes.
size_t size;
// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
GLenum target;
// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
GLenum usage;
//
MemoryBacking backing;
};
/**
* Implementation of VertexBuffer which uses plain arrays to store the data.
*
* This implementation should be supported everywhere, and acts as a fallback
* on systems which do not support VBOs.
*/
class VertexArray : public VertexBuffer
{
public:
/**
* @copydoc VertexBuffer(int, GLenum, GLenum, Backing)
*/
VertexArray(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Frees the data we've allocated.
*/
virtual ~VertexArray();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
private:
// Holds the data.
char *buf;
};
/**
* Vertex Buffer Object (VBO) implementation of VertexBuffer.
*
* This will be used on all systems that support it. It's in general
* faster than vertex arrays, but especially in use-cases where there
* is no need to update the data every frame.
**/
class VBO : public VertexBuffer, public Volatile
{
public:
/**
* @copydoc VertexBuffer(size_t, GLenum, GLenum, Backing)
**/
VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing);
/**
* Deletes the VBOs from OpenGL.
**/
virtual ~VBO();
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
virtual const void *getPointer(size_t offset) const ;
// Implements Volatile.
bool loadVolatile();
void unloadVolatile();
private:
/**
* Creates the VBO, and optionally restores data we saved earlier.
*
* @param restore True to restore data previously saved with 'unload'.
* @return True on success, false otherwise.
*/
bool load(bool restore);
/**
* Optionally save the data in the VBO, then delete it.
*
* @param save True to save the data before deleting.
*/
void unload(bool save);
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// A pointer to mapped memory. Will be inialized on the first
// call to map().
void *memory_map;
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
bool is_dirty;
};
/**
* VertexIndex manages one shared VertexBuffer that stores the indices for an
* element array. Vertex arrays using the vertex structure (or anything else
* that can use the pattern below) can request a size and use it for the
* drawElements call.
*
* indices[i*6 + 0] = i*4 + 0;
* indices[i*6 + 1] = i*4 + 1;
* indices[i*6 + 2] = i*4 + 2;
*
* indices[i*6 + 3] = i*4 + 0;
* indices[i*6 + 4] = i*4 + 2;
* indices[i*6 + 5] = i*4 + 3;
*
* There will always be a large enough VertexBuffer around until all
* VertexIndex instances have been deleted.
*
* Q: Why have something like VertexIndex?
* A: The indices for the SpriteBatch do not change, only the array size
* varies. Using one VertexBuffer for all element arrays removes this
* duplicated data and saves some memory.
*/
class VertexIndex
{
public:
/**
* Adds an entry to the list of sizes and resizes the VertexBuffer
* if needed. A size of 1 allocates a group of 6 indices for 4 vertices
* creating 1 face.
*
* @param size The requested size in groups of 6 indices.
*/
VertexIndex(size_t size);
/**
* Removes an entry from the list of sizes and resizes the VertexBuffer
* if needed.
*/
~VertexIndex();
/**
* Returns the number of index groups.
* This can be used for getIndexCount to get the full count of indices.
*
* @return The number of index groups.
*/
size_t getSize() const;
/**
* Returns the number of indices that the passed element count will have.
* Use VertexIndex::getSize to get the full index count for that
* VertexIndex instance.
*
* @param elements The number of elements to calculate the index count for.
* @return The index count.
*/
size_t getIndexCount(size_t elements) const;
/**
* Returns the integer type of the element array.
* If an optional nonzero size argument is passed, the function returns
* the integer type of the element array of that size.
*
* @param s The size of the array to calculated the integer type of.
* @return The element array integer type.
*/
GLenum getType(size_t s) const;
inline GLenum getType() const
{
return getType(maxSize);
}
/**
* Returns the size in bytes of an element in the element array.
* Can be used with getPointer to calculate an offset into the array based
* on a number of elements.
*
* @return The size of an element in bytes.
**/
size_t getElementSize();
/**
* Returns the pointer to the VertexBuffer.
* The pointer will change if a new size request or removal causes
* a VertexBuffer resize. It is recommended to retrieve the pointer
* value directly before the drawing call.
*
* @return The pointer to the VertexBuffer.
*/
VertexBuffer *getVertexBuffer() const;
/**
* Returns a pointer which represents the specified byte offset.
*
* @param offset The offset in bytes.
* @return A pointer which represents the offset.
*/
const void *getPointer(size_t offset) const;
private:
/**
* Adds a new size to the size list, then sorts and resizes it if needed.
*
* @param newSize The new size to be added.
*/
void addSize(size_t newSize);
/**
* Removes a size from the size list, then sorts and resizes it if needed.
*
* @param oldSize The old size to be removed.
*/
void removeSize(size_t oldSize);
/**
* Resizes the VertexBuffer to the requested size.
* This function takes care of choosing the correct integer type and
* allocating and deleting the VertexBuffer instance. It also has some
* fallback logic in case the memory ran out.
*
* @param size The requested VertexBuffer size. Passing 0 deletes the VertexBuffer without allocating a new one.
*/
void resize(size_t size);
/**
* Adds all indices to the array with the type T.
* There are no checks for the correct types or overflows. The calling
* function should check for that.
*/
template <typename T> void fill();
// The size of the array requested by this instance.
size_t size;
// The size in bytes of an element in the element array.
static size_t elementSize;
// The current VertexBuffer size. 0 means no VertexBuffer.
static size_t maxSize;
// The list of sizes. Needs to be kept sorted in ascending order.
static std::list<size_t> sizeRefs;
// The VertexBuffer for the element array. Can be NULL.
static VertexBuffer *element_array;
};
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H
@@ -0,0 +1,245 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Graphics.h"
#include "wrap_Canvas.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Canvas *luax_checkcanvas(lua_State *L, int idx)
{
return luax_checktype<Canvas>(L, idx, "Canvas", GRAPHICS_CANVAS_T);
}
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);
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();
return 0;
}
int w_Canvas_getImageData(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
love::image::Image *image = luax_getmodule<love::image::Image>(L, "image", MODULE_IMAGE_T);
love::image::ImageData *img = canvas->getImageData(image);
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, img);
return 1;
}
int w_Canvas_getPixel(lua_State * L)
{
Canvas * canvas = luax_checkcanvas(L, 1);
int x = luaL_checkint(L, 2);
int y = luaL_checkint(L, 3);
unsigned char c[4];
EXCEPT_GUARD(canvas->getPixel(c, x, y);)
lua_pushnumber(L, c[0]);
lua_pushnumber(L, c[1]);
lua_pushnumber(L, c[2]);
lua_pushnumber(L, c[3]);
return 4;
}
int w_Canvas_setFilter(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Image::Filter f;
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
canvas->setFilter(f);
return 0;
}
int w_Canvas_getFilter(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
const Image::Filter f = canvas->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Canvas_setWrap(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Image::Wrap w;
const char *sstr = luaL_checkstring(L, 2);
const char *tstr = luaL_optstring(L, 3, sstr);
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
canvas->setWrap(w);
return 0;
}
int w_Canvas_getWrap(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
const Image::Wrap w = canvas->getWrap();
const char *wrap_s;
const char *wrap_t;
Image::getConstant(w.s, wrap_s);
Image::getConstant(w.t, wrap_t);
lua_pushstring(L, wrap_s);
lua_pushstring(L, wrap_t);
return 2;
}
int w_Canvas_clear(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Color c;
if (lua_isnoneornil(L, 2))
{
c.set(0, 0, 0, 0);
}
else if (lua_istable(L, 2))
{
for (int i = 1; i <= 4; i++)
lua_rawgeti(L, 2, i);
c.r = (unsigned char)luaL_checkinteger(L, -4);
c.g = (unsigned char)luaL_checkinteger(L, -3);
c.b = (unsigned char)luaL_checkinteger(L, -2);
c.a = (unsigned char)luaL_optinteger(L, -1, 255);
lua_pop(L, 4);
}
else
{
c.r = (unsigned char)luaL_checkinteger(L, 2);
c.g = (unsigned char)luaL_checkinteger(L, 3);
c.b = (unsigned char)luaL_checkinteger(L, 4);
c.a = (unsigned char)luaL_optinteger(L, 5, 255);
}
canvas->clear(c);
return 0;
}
int w_Canvas_getWidth(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getWidth());
return 1;
}
int w_Canvas_getHeight(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getHeight());
return 1;
}
int w_Canvas_getDimensions(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushnumber(L, canvas->getWidth());
lua_pushnumber(L, canvas->getHeight());
return 2;
}
int w_Canvas_getType(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Canvas::TextureType type = canvas->getTextureType();
const char *str;
Canvas::getConstant(type, str);
lua_pushstring(L, str);
return 1;
}
static const luaL_Reg functions[] =
{
{ "renderTo", w_Canvas_renderTo },
{ "getImageData", w_Canvas_getImageData },
{ "getPixel", w_Canvas_getPixel },
{ "setFilter", w_Canvas_setFilter },
{ "getFilter", w_Canvas_getFilter },
{ "setWrap", w_Canvas_setWrap },
{ "getWrap", w_Canvas_getWrap },
{ "clear", w_Canvas_clear },
{ "getWidth", w_Canvas_getWidth },
{ "getHeight", w_Canvas_getHeight },
{ "getDimensions", w_Canvas_getDimensions },
{ "getType", w_Canvas_getType },
{ 0, 0 }
};
extern "C" int luaopen_canvas(lua_State *L)
{
return luax_register_type(L, "Canvas", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
#define LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
// LOVE
#include "common/runtime.h"
#include "Canvas.h"
namespace love
{
namespace graphics
{
namespace opengl
{
//see Canvas.h
Canvas *luax_checkcanvas(lua_State *L, int idx);
int w_Canvas_renderTo(lua_State *L);
int w_Canvas_getImageData(lua_State *L);
int w_Canvas_getPixel(lua_State * L);
int w_Canvas_setFilter(lua_State *L);
int w_Canvas_getFilter(lua_State *L);
int w_Canvas_setWrap(lua_State *L);
int w_Canvas_getWrap(lua_State *L);
int w_Canvas_clear(lua_State *L);
int w_Canvas_getWidth(lua_State *L);
int w_Canvas_getHeight(lua_State *L);
int w_Canvas_getDimensions(lua_State *L);
int w_Canvas_getType(lua_State *L);
extern "C" int luaopen_canvas(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_CANVAS_H
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Font.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Font *luax_checkfont(lua_State *L, int idx)
{
return luax_checktype<Font>(L, idx, "Font", GRAPHICS_FONT_T);
}
int w_Font_getHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getHeight());
return 1;
}
int w_Font_getWidth(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
EXCEPT_GUARD(lua_pushinteger(L, t->getWidth(str));)
return 1;
}
int w_Font_getWrap(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const char *str = luaL_checkstring(L, 2);
float wrap = (float) luaL_checknumber(L, 3);
int max_width = 0, numlines = 0;
EXCEPT_GUARD(
std::vector<std::string> lines = t->getWrap(str, wrap, &max_width);
numlines = lines.size();
)
lua_pushinteger(L, max_width);
lua_pushinteger(L, numlines);
return 2;
}
int w_Font_setLineHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
float h = (float)luaL_checknumber(L, 2);
t->setLineHeight(h);
return 0;
}
int w_Font_getLineHeight(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getLineHeight());
return 1;
}
int w_Font_setFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
Image::Filter f = t->getFilter();
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
return 0;
}
int w_Font_getFilter(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
const Image::Filter f = t->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Font_getAscent(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getAscent());
return 1;
}
int w_Font_getDescent(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getDescent());
return 1;
}
int w_Font_getBaseline(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
lua_pushnumber(L, t->getBaseline());
return 1;
}
int w_Font_hasGlyphs(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
bool hasglyph = false;
int count = lua_gettop(L) - 1;
count = count < 1 ? 1 : count;
EXCEPT_GUARD(
for (int i = 2; i < count + 2; i++)
{
if (lua_type(L, i) == LUA_TSTRING)
hasglyph = t->hasGlyphs(luax_checkstring(L, i));
else
hasglyph = t->hasGlyph((uint32) luaL_checknumber(L, i));
if (!hasglyph)
break;
}
)
luax_pushboolean(L, hasglyph);
return 1;
}
static const luaL_Reg functions[] =
{
{ "getHeight", w_Font_getHeight },
{ "getWidth", w_Font_getWidth },
{ "getWrap", w_Font_getWrap },
{ "setLineHeight", w_Font_setLineHeight },
{ "getLineHeight", w_Font_getLineHeight },
{ "setFilter", w_Font_setFilter },
{ "getFilter", w_Font_getFilter },
{ "getAscent", w_Font_getAscent },
{ "getDescent", w_Font_getDescent },
{ "getBaseline", w_Font_getBaseline },
{ "hasGlyphs", w_Font_hasGlyphs },
{ 0, 0 }
};
extern "C" int luaopen_font(lua_State *L)
{
return luax_register_type(L, "Font", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
#define LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
// LOVE
#include "common/runtime.h"
#include "Font.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Font *luax_checkfont(lua_State *L, int idx);
int w_Font_getHeight(lua_State *L);
int w_Font_getWidth(lua_State *L);
int w_Font_getWrap(lua_State *L);
int w_Font_setLineHeight(lua_State *L);
int w_Font_getLineHeight(lua_State *L);
int w_Font_setFilter(lua_State *L);
int w_Font_getFilter(lua_State *L);
int w_Font_getAscent(lua_State *L);
int w_Font_getDescent(lua_State *L);
int w_Font_getBaseline(lua_State *L);
int w_Font_hasGlyphs(lua_State *L);
extern "C" int luaopen_font(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_FONT_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
#define LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
// LOVE
#include "wrap_Font.h"
#include "wrap_Image.h"
#include "wrap_Quad.h"
#include "wrap_SpriteBatch.h"
#include "wrap_ParticleSystem.h"
#include "wrap_Canvas.h"
#include "wrap_Shader.h"
#include "wrap_Mesh.h"
#include "Graphics.h"
namespace love
{
namespace graphics
{
namespace opengl
{
int w_reset(lua_State *L);
int w_clear(lua_State *L);
int w_present(lua_State *L);
int w_isCreated(lua_State *L);
int w_getWidth(lua_State *L);
int w_getHeight(lua_State *L);
int w_getDimensions(lua_State *L);
int w_setScissor(lua_State *L);
int w_getScissor(lua_State *L);
int w_setStencil(lua_State *L);
int w_setInvertedStencil(lua_State *L);
int w_getMaxImageSize(lua_State *L);
int w_newImage(lua_State *L);
int w_newQuad(lua_State *L);
int w_newFont(lua_State *L);
int w_newImageFont(lua_State *L);
int w_newSpriteBatch(lua_State *L);
int w_newParticleSystem(lua_State *L);
int w_newCanvas(lua_State *L); // comments in function
int w_newShader(lua_State *L);
int w_newMesh(lua_State *L);
int w_setColor(lua_State *L);
int w_getColor(lua_State *L);
int w_setBackgroundColor(lua_State *L);
int w_getBackgroundColor(lua_State *L);
int w_setFont(lua_State *L);
int w_getFont(lua_State *L);
int w_setColorMask(lua_State *L);
int w_getColorMask(lua_State *L);
int w_setBlendMode(lua_State *L);
int w_getBlendMode(lua_State *L);
int w_setDefaultFilter(lua_State *L);
int w_getDefaultFilter(lua_State *L);
int w_setDefaultMipmapFilter(lua_State *L);
int w_getDefaultMipmapFilter(lua_State *L);
int w_setLineWidth(lua_State *L);
int w_setLineStyle(lua_State *L);
int w_setLineJoin(lua_State *L);
int w_getLineWidth(lua_State *L);
int w_getLineStyle(lua_State *L);
int w_getLineJoin(lua_State *L);
int w_setPointSize(lua_State *L);
int w_setPointStyle(lua_State *L);
int w_getPointSize(lua_State *L);
int w_getPointStyle(lua_State *L);
int w_getMaxPointSize(lua_State *L);
int w_newScreenshot(lua_State *L);
int w_setCanvas(lua_State *L);
int w_getCanvas(lua_State *L);
int w_setShader(lua_State *L);
int w_getShader(lua_State *L);
int w_setDefaultShaderCode(lua_State *L);
int w_isSupported(lua_State *L);
int w_getRendererInfo(lua_State *L);
int w_draw(lua_State *L);
int w_print(lua_State *L);
int w_printf(lua_State *L);
int w_point(lua_State *L);
int w_line(lua_State *L);
int w_rectangle(lua_State *L);
int w_circle(lua_State *L);
int w_arc(lua_State *L);
int w_polygon(lua_State *L);
int w_push(lua_State *L);
int w_pop(lua_State *L);
int w_rotate(lua_State *L);
int w_scale(lua_State *L);
int w_translate(lua_State *L);
int w_shear(lua_State *L);
int w_origin(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_graphics(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_GRAPHICS_H
@@ -0,0 +1,229 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Image *luax_checkimage(lua_State *L, int idx)
{
return luax_checktype<Image>(L, idx, "Image", GRAPHICS_IMAGE_T);
}
int w_Image_getWidth(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getWidth());
return 1;
}
int w_Image_getHeight(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getHeight());
return 1;
}
int w_Image_getDimensions(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
lua_pushnumber(L, t->getWidth());
lua_pushnumber(L, t->getHeight());
return 2;
}
int w_Image_setFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter();
const char *minstr = luaL_checkstring(L, 2);
const char *magstr = luaL_optstring(L, 3, minstr);
if (!Image::getConstant(minstr, f.min))
return luaL_error(L, "Invalid filter mode: %s", minstr);
if (!Image::getConstant(magstr, f.mag))
return luaL_error(L, "Invalid filter mode: %s", magstr);
f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
EXCEPT_GUARD(t->setFilter(f);)
return 0;
}
int w_Image_getFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
const Image::Filter f = t->getFilter();
const char *minstr;
const char *magstr;
Image::getConstant(f.min, minstr);
Image::getConstant(f.mag, magstr);
lua_pushstring(L, minstr);
lua_pushstring(L, magstr);
lua_pushnumber(L, f.anisotropy);
return 3;
}
int w_Image_setMipmapFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
Image::Filter f = t->getFilter();
if (lua_isnoneornil(L, 2))
f.mipmap = Image::FILTER_NONE; // mipmapping is disabled if no argument is given
else
{
const char *mipmapstr = luaL_checkstring(L, 2);
if (!Image::getConstant(mipmapstr, f.mipmap))
return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
}
EXCEPT_GUARD(t->setFilter(f);)
float sharpness = (float) luaL_optnumber(L, 3, 0);
t->setMipmapSharpness(sharpness);
return 0;
}
int w_Image_getMipmapFilter(lua_State *L)
{
Image *t = luax_checkimage(L, 1);
const Image::Filter &f = t->getFilter();
const char *mipmapstr;
if (Image::getConstant(f.mipmap, mipmapstr))
lua_pushstring(L, mipmapstr);
else
lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
lua_pushnumber(L, t->getMipmapSharpness());
return 2;
}
int w_Image_setWrap(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
Image::Wrap w;
const char *sstr = luaL_checkstring(L, 2);
const char *tstr = luaL_optstring(L, 3, sstr);
if (!Image::getConstant(sstr, w.s))
return luaL_error(L, "Invalid wrap mode: %s", sstr);
if (!Image::getConstant(tstr, w.t))
return luaL_error(L, "Invalid wrap mode, %s", tstr);
i->setWrap(w);
return 0;
}
int w_Image_getWrap(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
const Image::Wrap w = i->getWrap();
const char *sstr;
const char *tstr;
Image::getConstant(w.s, sstr);
Image::getConstant(w.t, tstr);
lua_pushstring(L, sstr);
lua_pushstring(L, tstr);
return 2;
}
int w_Image_isCompressed(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
luax_pushboolean(L, i->isCompressed());
return 1;
}
int w_Image_refresh(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
EXCEPT_GUARD(i->refresh();)
return 0;
}
int w_Image_getData(lua_State *L)
{
Image *i = luax_checkimage(L, 1);
if (i->isCompressed())
{
love::image::CompressedData *t = i->getCompressedData();
if (t)
{
t->retain();
luax_pushtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, t);
}
else
lua_pushnil(L);
}
else
{
love::image::ImageData *t = i->getImageData();
if (t)
{
t->retain();
luax_pushtype(L, "ImageData", IMAGE_IMAGE_DATA_T, t);
}
else
lua_pushnil(L);
}
return 1;
}
static const luaL_Reg functions[] =
{
{ "getWidth", w_Image_getWidth },
{ "getHeight", w_Image_getHeight },
{ "getDimensions", w_Image_getDimensions },
{ "setFilter", w_Image_setFilter },
{ "getFilter", w_Image_getFilter },
{ "setWrap", w_Image_setWrap },
{ "getWrap", w_Image_getWrap },
{ "setMipmapFilter", w_Image_setMipmapFilter },
{ "getMipmapFilter", w_Image_getMipmapFilter },
{ "isCompressed", w_Image_isCompressed },
{ "refresh", w_Image_refresh },
{ "getData", w_Image_getData },
{ 0, 0 }
};
extern "C" int luaopen_image(lua_State *L)
{
return luax_register_type(L, "Image", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
#define LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
// LOVE
#include "common/runtime.h"
#include "Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Image *luax_checkimage(lua_State *L, int idx);
int w_Image_getWidth(lua_State *L);
int w_Image_getHeight(lua_State *L);
int w_Image_getDimensions(lua_State *L);
int w_Image_setFilter(lua_State *L);
int w_Image_getFilter(lua_State *L);
int w_Image_setMipmapFilter(lua_State *L);
int w_Image_getMipmapFilter(lua_State *L);
int w_Image_setWrap(lua_State *L);
int w_Image_getWrap(lua_State *L);
int w_Image_isCompressed(lua_State *L);
int w_Image_refresh(lua_State *L);
int w_Image_getData(lua_State *L);
extern "C" int luaopen_image(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_IMAGE_H
@@ -0,0 +1,327 @@
/**
* Copyright (c) 2006-2013 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 "wrap_Mesh.h"
#include "wrap_Image.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh *luax_checkmesh(lua_State *L, int idx)
{
return luax_checktype<Mesh>(L, idx, "Mesh", GRAPHICS_MESH_T);
}
int w_Mesh_setVertex(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t i = size_t(luaL_checkinteger(L, 2) - 1);
Vertex v;
if (lua_istable(L, 3))
{
for (int i = 1; i <= 8; i++)
lua_rawgeti(L, 3, i);
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.r = luaL_optinteger(L, -4, 255);
v.g = luaL_optinteger(L, -3, 255);
v.b = luaL_optinteger(L, -2, 255);
v.a = luaL_optinteger(L, -1, 255);
lua_pop(L, 8);
}
else
{
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.r = luaL_optinteger(L, 7, 255);
v.g = luaL_optinteger(L, 8, 255);
v.b = luaL_optinteger(L, 9, 255);
v.a = luaL_optinteger(L, 10, 255);
}
EXCEPT_GUARD(t->setVertex(i, v);)
return 0;
}
int w_Mesh_getVertex(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t i = (size_t) (luaL_checkinteger(L, 2) - 1);
Vertex v;
EXCEPT_GUARD(v = t->getVertex(i);)
lua_pushnumber(L, v.x);
lua_pushnumber(L, v.y);
lua_pushnumber(L, v.s);
lua_pushnumber(L, v.t);
lua_pushnumber(L, v.r);
lua_pushnumber(L, v.g);
lua_pushnumber(L, v.b);
lua_pushnumber(L, v.a);
return 8;
}
int w_Mesh_setVertices(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
size_t vertex_count = lua_objlen(L, 2);
std::vector<Vertex> vertices;
vertices.reserve(vertex_count);
// Get the vertices from the table.
for (size_t i = 1; i <= vertex_count; i++)
{
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TTABLE)
return luax_typerror(L, 2, "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);
lua_pop(L, 9);
vertices.push_back(v);
}
EXCEPT_GUARD(t->setVertices(vertices);)
return 0;
}
int w_Mesh_getVertices(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const Vertex *vertices = t->getVertices();
size_t count = t->getVertexCount();
lua_createtable(L, count, 0);
for (size_t i = 0; i < count; i++)
{
// Create vertex table.
lua_createtable(L, 8, 0);
lua_pushnumber(L, vertices[i].x);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, vertices[i].y);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, vertices[i].s);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, vertices[i].t);
lua_rawseti(L, -2, 4);
lua_pushnumber(L, vertices[i].r);
lua_rawseti(L, -2, 5);
lua_pushnumber(L, vertices[i].g);
lua_rawseti(L, -2, 6);
lua_pushnumber(L, vertices[i].b);
lua_rawseti(L, -2, 7);
lua_pushnumber(L, vertices[i].a);
lua_rawseti(L, -2, 8);
// Insert vertex table into vertices table.
lua_rawseti(L, -2, i + 1);
}
// Return vertices table.
return 1;
}
int w_Mesh_getVertexCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
lua_pushinteger(L, t->getVertexCount());
return 1;
}
int w_Mesh_setVertexMap(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
bool is_table = lua_istable(L, 2);
int nargs = is_table ? lua_objlen(L, 2) : lua_gettop(L) - 1;
std::vector<uint32> vertexmap;
vertexmap.reserve(nargs);
for (int i = 0; i < nargs; i++)
{
if (is_table)
{
lua_rawgeti(L, 2, i + 1);
vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1));
lua_pop(L, 1);
}
else
vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
}
EXCEPT_GUARD(t->setVertexMap(vertexmap);)
return 0;
}
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();
lua_createtable(L, elements, 0);
for (size_t i = 0; i < elements; i++)
{
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
int w_Mesh_setImage(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
if (lua_isnoneornil(L, 2))
t->setImage();
else
{
Image *img = luax_checkimage(L, 2);
t->setImage(img);
}
return 0;
}
int w_Mesh_getImage(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
Image *img = t->getImage();
if (img == NULL)
return 0;
img->retain();
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, img);
return 1;
}
int w_Mesh_setDrawMode(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const char *str = luaL_checkstring(L, 2);
Mesh::DrawMode mode;
if (!Mesh::getConstant(str, mode))
return luaL_error(L, "Invalid mesh draw mode: %s", str);
t->setDrawMode(mode);
return 0;
}
int w_Mesh_getDrawMode(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
Mesh::DrawMode mode = t->getDrawMode();
const char *str;
if (!Mesh::getConstant(mode, str))
return luaL_error(L, "Unknown mesh draw mode.");
lua_pushstring(L, str);
return 1;
}
int w_Mesh_setVertexColors(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
t->setVertexColors(luax_toboolean(L, 2));
return 0;
}
int w_Mesh_hasVertexColors(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
luax_pushboolean(L, t->hasVertexColors());
return 1;
}
static const luaL_Reg functions[] =
{
{ "setVertex", w_Mesh_setVertex },
{ "getVertex", w_Mesh_getVertex },
{ "setVertices", w_Mesh_setVertices },
{ "getVertices", w_Mesh_getVertices },
{ "getVertexCount", w_Mesh_getVertexCount },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
{ "setImage", w_Mesh_setImage },
{ "getImage", w_Mesh_getImage },
{ "setDrawMode", w_Mesh_setDrawMode },
{ "getDrawMode", w_Mesh_getDrawMode },
{ "setVertexColors", w_Mesh_setVertexColors },
{ "hasVertexColors", w_Mesh_hasVertexColors },
{ 0, 0 }
};
extern "C" int luaopen_mesh(lua_State *L)
{
return luax_register_type(L, "Mesh", functions);
}
} // opengl
} // graphics
} // love
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
#define LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
// LOVE
#include "common/runtime.h"
#include "Mesh.h"
namespace love
{
namespace graphics
{
namespace opengl
{
Mesh *luax_checkmesh(lua_State *L, int idx);
int w_Mesh_setVertex(lua_State *L);
int w_Mesh_getVertex(lua_State *L);
int w_Mesh_setVertices(lua_State *L);
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_setImage(lua_State *L);
int w_Mesh_getImage(lua_State *L);
int w_Mesh_setDrawMode(lua_State *L);
int w_Mesh_getDrawMode(lua_State *L);
int w_Mesh_setVertexColors(lua_State *L);
int w_Mesh_hasVertexColors(lua_State *L);
extern "C" int luaopen_mesh(lua_State *L);
} // opengl
} // graphics
} // love
#endif // LOVE_GRAPHICS_OPENGL_WRAP_MESH_H
@@ -0,0 +1,669 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_ParticleSystem.h"
#include "common/Vector.h"
#include <cstring>
namespace love
{
namespace graphics
{
namespace opengl
{
ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx)
{
return luax_checktype<ParticleSystem>(L, idx, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T);
}
int w_ParticleSystem_setImage(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
Image *i = luax_checkimage(L, 2);
t->setImage(i);
return 0;
}
int w_ParticleSystem_getImage(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
Image *i = t->getImage();
i->retain();
luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, i);
return 1;
}
int w_ParticleSystem_setBufferSize(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_Number arg1 = luaL_checknumber(L, 2);
if (arg1 < 1.0 || arg1 > ParticleSystem::MAX_PARTICLES)
return luaL_error(L, "Invalid buffer size");
EXCEPT_GUARD(t->setBufferSize((uint32) arg1);)
return 0;
}
int w_ParticleSystem_getBufferSize(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushinteger(L, t->getBufferSize());
return 1;
}
int w_ParticleSystem_setInsertMode(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::InsertMode mode;
const char *str = luaL_checkstring(L, 2);
if (!ParticleSystem::getConstant(str, mode))
return luaL_error(L, "Invalid insert mode: '%s'", str);
t->setInsertMode(mode);
return 0;
}
int w_ParticleSystem_getInsertMode(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::InsertMode mode;
mode = t->getInsertMode();
const char *str;
if (!ParticleSystem::getConstant(mode, str))
return luaL_error(L, "Unknown insert mode");
lua_pushstring(L, str);
return 1;
}
int w_ParticleSystem_setEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int arg1 = luaL_checkint(L, 2);
EXCEPT_GUARD(t->setEmissionRate(arg1);)
return 0;
}
int w_ParticleSystem_getEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushinteger(L, t->getEmissionRate());
return 1;
}
int w_ParticleSystem_setEmitterLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setEmitterLifetime(arg1);
return 0;
}
int w_ParticleSystem_getEmitterLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getEmitterLifetime());
return 1;
}
int w_ParticleSystem_setParticleLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setParticleLifetime(arg1, arg2);
return 0;
}
int w_ParticleSystem_getParticleLifetime(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getParticleLifetime(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setPosition(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_checknumber(L, 3);
t->setPosition(arg1, arg2);
return 0;
}
int w_ParticleSystem_getPosition(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector pos = t->getPosition();
lua_pushnumber(L, pos.getX());
lua_pushnumber(L, pos.getY());
return 2;
}
int w_ParticleSystem_setAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution = ParticleSystem::DISTRIBUTION_NONE;
float x = 0.f, y = 0.f;
const char *str = lua_isnoneornil(L, 2) ? 0 : luaL_checkstring(L, 2);
if (str && !ParticleSystem::getConstant(str, distribution))
return luaL_error(L, "Invalid particle distribution: %s", str);
if (distribution != ParticleSystem::DISTRIBUTION_NONE)
{
x = (float) luaL_checknumber(L, 3);
y = (float) luaL_checknumber(L, 4);
if (x < 0.0f || y < 0.0f)
return luaL_error(L, "Invalid area spread parameters (must be >= 0)");
}
t->setAreaSpread(distribution, x, y);
return 0;
}
int w_ParticleSystem_getAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
ParticleSystem::AreaSpreadDistribution distribution = t-> getAreaSpreadDistribution();
const char *str;
ParticleSystem::getConstant(distribution, str);
const love::Vector &p = t->getAreaSpreadParameters();
lua_pushstring(L, str);
lua_pushnumber(L, p.x);
lua_pushnumber(L, p.y);
return 3;
}
int w_ParticleSystem_setDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setDirection(arg1);
return 0;
}
int w_ParticleSystem_getDirection(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getDirection());
return 1;
}
int w_ParticleSystem_setSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpread(arg1);
return 0;
}
int w_ParticleSystem_getSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSpread());
return 1;
}
int w_ParticleSystem_setSpeed(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setSpeed(arg1, arg2);
return 0;
}
int w_ParticleSystem_getSpeed(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getSpeed(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setLinearAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float xmin = (float) luaL_checknumber(L, 2);
float ymin = (float) luaL_checknumber(L, 3);
float xmax = (float) luaL_optnumber(L, 4, xmin);
float ymax = (float) luaL_optnumber(L, 5, ymin);
t->setLinearAcceleration(xmin, ymin, xmax, ymax);
return 0;
}
int w_ParticleSystem_getLinearAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector min, max;
t->getLinearAcceleration(&min, &max);
lua_pushnumber(L, min.x);
lua_pushnumber(L, min.y);
lua_pushnumber(L, max.x);
lua_pushnumber(L, max.y);
return 4;
}
int w_ParticleSystem_setRadialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setRadialAcceleration(arg1, arg2);
return 0;
}
int w_ParticleSystem_getRadialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getRadialAcceleration(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setTangentialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setTangentialAcceleration(arg1, arg2);
return 0;
}
int w_ParticleSystem_getTangentialAcceleration(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getTangentialAcceleration(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setSizes(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
size_t nSizes = lua_gettop(L) - 1;
if (nSizes > 8)
return luaL_error(L, "At most eight (8) sizes may be used.");
if (nSizes <= 1)
{
float size = luax_checkfloat(L, 2);
t->setSize(size);
}
else
{
std::vector<float> sizes(nSizes);
for (size_t i = 0; i < nSizes; ++i)
sizes[i] = luax_checkfloat(L, 1 + i + 1);
t->setSizes(sizes);
}
return 0;
}
int w_ParticleSystem_getSizes(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
const std::vector<float> &sizes = t->getSizes();
for (size_t i = 0; i < sizes.size(); i++)
lua_pushnumber(L, sizes[i]);
return sizes.size();
}
int w_ParticleSystem_setSizeVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
if (arg1 < 0.0f || arg1 > 1.0f)
return luaL_error(L, "Size variation has to be between 0 and 1, inclusive.");
t->setSizeVariation(arg1);
return 0;
}
int w_ParticleSystem_getSizeVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSizeVariation());
return 1;
}
int w_ParticleSystem_setRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setRotation(arg1, arg2);
return 0;
}
int w_ParticleSystem_getRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float min, max;
t->getRotation(&min, &max);
lua_pushnumber(L, min);
lua_pushnumber(L, max);
return 2;
}
int w_ParticleSystem_setSpin(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_optnumber(L, 3, arg1);
t->setSpin(arg1, arg2);
return 0;
}
int w_ParticleSystem_getSpin(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float start, end;
t->getSpin(&start, &end);
lua_pushnumber(L, start);
lua_pushnumber(L, end);
return 2;
}
int w_ParticleSystem_setSpinVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
t->setSpinVariation(arg1);
return 0;
}
int w_ParticleSystem_getSpinVariation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getSpinVariation());
return 1;
}
int w_ParticleSystem_setOffset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float x = (float)luaL_checknumber(L, 2);
float y = (float)luaL_checknumber(L, 3);
t->setOffset(x, y);
return 0;
}
int w_ParticleSystem_getOffset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
love::Vector offset = t->getOffset();
lua_pushnumber(L, offset.getX());
lua_pushnumber(L, offset.getY());
return 2;
}
int w_ParticleSystem_setColors(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
if (lua_istable(L, 2)) // setColors({r,g,b,a}, {r,g,b,a}, ...)
{
size_t nColors = lua_gettop(L) - 1;
if (nColors > 8)
return luaL_error(L, "At most eight (8) colors may be used.");
std::vector<Color> colors(nColors);
for (size_t i = 0; i < nColors; i++)
{
luaL_checktype(L, i + 2, LUA_TTABLE);
if (lua_objlen(L, i + 2) < 3)
return luaL_argerror(L, i + 2, "expected 4 color components");
for (int j = 0; j < 4; j++)
// push args[i+2][j+1] onto the stack
lua_rawgeti(L, i + 2, j + 1);
unsigned char r = (unsigned char) luaL_checkinteger(L, -4);
unsigned char g = (unsigned char) luaL_checkinteger(L, -3);
unsigned char b = (unsigned char) luaL_checkinteger(L, -2);
unsigned char a = (unsigned char) luaL_optinteger(L, -1, 255);
// pop the color components from the stack
lua_pop(L, 4);
colors[i] = Color(r, g, b, a);
}
t->setColor(colors);
}
else // setColors(r,g,b,a, r,g,b,a, ...)
{
int cargs = lua_gettop(L) - 1;
size_t nColors = (cargs + 3) / 4; // nColors = ceil(color_args / 4)
if (cargs != 3 && (cargs % 4 != 0 || cargs == 0))
return luaL_error(L, "Expected red, green, blue, and alpha. Only got %d of 4 components.", cargs % 4);
if (nColors > 8)
return luaL_error(L, "At most eight (8) colors may be used.");
if (nColors == 1)
{
unsigned char r = (unsigned char) luaL_checkinteger(L, 2);
unsigned char g = (unsigned char) luaL_checkinteger(L, 3);
unsigned char b = (unsigned char) luaL_checkinteger(L, 4);
unsigned char a = (unsigned char) luaL_optinteger(L, 5, 255);
t->setColor(Color(r,g,b,a));
}
else
{
std::vector<Color> colors(nColors);
for (size_t i = 0; i < nColors; ++i)
{
unsigned char r = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 1);
unsigned char g = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 2);
unsigned char b = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 3);
unsigned char a = (unsigned char) luaL_checkinteger(L, 1 + i*4 + 4);
colors[i] = Color(r,g,b,a);
}
t->setColor(colors);
}
}
return 0;
}
int w_ParticleSystem_getColors(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
const std::vector<Color> &colors =t->getColor();
for (size_t i = 0; i < colors.size(); i++)
{
lua_createtable(L, 4, 0);
lua_pushinteger(L, colors[i].r);
lua_rawseti(L, -2, 1);
lua_pushinteger(L, colors[i].g);
lua_rawseti(L, -2, 2);
lua_pushinteger(L, colors[i].b);
lua_rawseti(L, -2, 3);
lua_pushinteger(L, colors[i].a);
lua_rawseti(L, -2, 4);
}
return colors.size();
}
int w_ParticleSystem_getCount(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushnumber(L, t->getCount());
return 1;
}
int w_ParticleSystem_start(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->start();
return 0;
}
int w_ParticleSystem_stop(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->stop();
return 0;
}
int w_ParticleSystem_pause(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->pause();
return 0;
}
int w_ParticleSystem_reset(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->reset();
return 0;
}
int w_ParticleSystem_emit(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int num = luaL_checkint(L, 2);
t->emit(num);
return 0;
}
int w_ParticleSystem_isActive(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isActive());
return 1;
}
int w_ParticleSystem_isPaused(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isPaused());
return 1;
}
int w_ParticleSystem_isStopped(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->isStopped());
return 1;
}
int w_ParticleSystem_update(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float dt = (float)luaL_checknumber(L, 2);
t->update(dt);
return 0;
}
static const luaL_Reg functions[] =
{
{ "setImage", w_ParticleSystem_setImage },
{ "getImage", w_ParticleSystem_getImage },
{ "setBufferSize", w_ParticleSystem_setBufferSize },
{ "getBufferSize", w_ParticleSystem_getBufferSize },
{ "setInsertMode", w_ParticleSystem_setInsertMode },
{ "getInsertMode", w_ParticleSystem_getInsertMode },
{ "setEmissionRate", w_ParticleSystem_setEmissionRate },
{ "getEmissionRate", w_ParticleSystem_getEmissionRate },
{ "setEmitterLifetime", w_ParticleSystem_setEmitterLifetime },
{ "getEmitterLifetime", w_ParticleSystem_getEmitterLifetime },
{ "setParticleLifetime", w_ParticleSystem_setParticleLifetime },
{ "getParticleLifetime", w_ParticleSystem_getParticleLifetime },
{ "setPosition", w_ParticleSystem_setPosition },
{ "getPosition", w_ParticleSystem_getPosition },
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
{ "setDirection", w_ParticleSystem_setDirection },
{ "getDirection", w_ParticleSystem_getDirection },
{ "setSpread", w_ParticleSystem_setSpread },
{ "getSpread", w_ParticleSystem_getSpread },
{ "setSpeed", w_ParticleSystem_setSpeed },
{ "getSpeed", w_ParticleSystem_getSpeed },
{ "setLinearAcceleration", w_ParticleSystem_setLinearAcceleration },
{ "getLinearAcceleration", w_ParticleSystem_getLinearAcceleration },
{ "setRadialAcceleration", w_ParticleSystem_setRadialAcceleration },
{ "getRadialAcceleration", w_ParticleSystem_getRadialAcceleration },
{ "setTangentialAcceleration", w_ParticleSystem_setTangentialAcceleration },
{ "getTangentialAcceleration", w_ParticleSystem_getTangentialAcceleration },
{ "setSizes", w_ParticleSystem_setSizes },
{ "getSizes", w_ParticleSystem_getSizes },
{ "setSizeVariation", w_ParticleSystem_setSizeVariation },
{ "getSizeVariation", w_ParticleSystem_getSizeVariation },
{ "setRotation", w_ParticleSystem_setRotation },
{ "getRotation", w_ParticleSystem_getRotation },
{ "setSpin", w_ParticleSystem_setSpin },
{ "getSpin", w_ParticleSystem_getSpin },
{ "setSpinVariation", w_ParticleSystem_setSpinVariation },
{ "getSpinVariation", w_ParticleSystem_getSpinVariation },
{ "setColors", w_ParticleSystem_setColors },
{ "getColors", w_ParticleSystem_getColors },
{ "setOffset", w_ParticleSystem_setOffset },
{ "getOffset", w_ParticleSystem_getOffset },
{ "getCount", w_ParticleSystem_getCount },
{ "start", w_ParticleSystem_start },
{ "stop", w_ParticleSystem_stop },
{ "pause", w_ParticleSystem_pause },
{ "reset", w_ParticleSystem_reset },
{ "emit", w_ParticleSystem_emit },
{ "isActive", w_ParticleSystem_isActive },
{ "isPaused", w_ParticleSystem_isPaused },
{ "isStopped", w_ParticleSystem_isStopped },
{ "update", w_ParticleSystem_update },
{ 0, 0 }
};
extern "C" int luaopen_particlesystem(lua_State *L)
{
return luax_register_type(L, "ParticleSystem", functions);
}
} // opengl
} // graphics
} // love

Some files were not shown because too many files have changed in this diff Show More