mirror of
https://github.com/love2d/love-android.git
synced 2026-08-18 19:54:18 +02:00
imported Löve GLES branch (changeset 1ba9037e558b)
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ¤t_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, ¤t_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
|
||||
@@ -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
|
||||
@@ -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, >);
|
||||
|
||||
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
Reference in New Issue
Block a user