Initial commit
This commit is contained in:
commit
ff25887f41
119 changed files with 24901 additions and 0 deletions
668
src/audio.cpp
Normal file
668
src/audio.cpp
Normal file
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
** audio.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "util.h"
|
||||
#include "intrulist.h"
|
||||
#include "filesystem.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <SFML/Audio/Music.hpp>
|
||||
#include <SFML/Audio/Sound.hpp>
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <SFML/System/Thread.hpp>
|
||||
#include <SFML/System/Clock.hpp>
|
||||
#include <SFML/System/Sleep.hpp>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
|
||||
#include "SDL2/SDL_thread.h"
|
||||
|
||||
//#include "SDL2/SDL_mixer.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define FADE_SLEEP 10
|
||||
#define SOUND_MAG_SIZE 10
|
||||
#define SOUND_MAX_MEM (50*1000*1000) // 5 MB
|
||||
|
||||
struct MusicEntity
|
||||
{
|
||||
sf::Music music;
|
||||
QByteArray filename;
|
||||
FileStream currentData;
|
||||
SDL_mutex *mutex;
|
||||
|
||||
int pitch;
|
||||
|
||||
volatile bool fading;
|
||||
struct
|
||||
{
|
||||
unsigned int duration;
|
||||
float msStep;
|
||||
volatile bool terminate;
|
||||
sf::Thread *thread;
|
||||
} fadeData;
|
||||
|
||||
/* normalized */
|
||||
float intVolume;
|
||||
float extVolume;
|
||||
|
||||
bool extPaused;
|
||||
bool noFadeInFlag;
|
||||
|
||||
MusicEntity()
|
||||
: currentData(0),
|
||||
pitch(100),
|
||||
fading(false),
|
||||
intVolume(1),
|
||||
extVolume(1),
|
||||
extPaused(false),
|
||||
noFadeInFlag(false)
|
||||
{
|
||||
music.setLoop(true);
|
||||
fadeData.thread = 0;
|
||||
mutex = SDL_CreateMutex();
|
||||
}
|
||||
|
||||
~MusicEntity()
|
||||
{
|
||||
SDL_DestroyMutex(mutex);
|
||||
}
|
||||
|
||||
void play(const QByteArray &filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
terminateFade();
|
||||
|
||||
volume = bound<int>(volume, 0, 100);
|
||||
pitch = bound<int>(pitch, 50, 150);
|
||||
|
||||
if (filename == this->filename
|
||||
&& volume == (int)music.getVolume()
|
||||
// && pitch == music.getPitch()
|
||||
&& music.getStatus() == sf::Music::Playing)
|
||||
return;
|
||||
|
||||
if (filename == this->filename
|
||||
&& music.getStatus() == sf::Music::Playing)
|
||||
// && pitch == music.getPitch())
|
||||
{
|
||||
intVolume = (float) volume / 100;
|
||||
updateVolumeSync();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(mutex);
|
||||
|
||||
this->music.stop();
|
||||
|
||||
this->filename = filename;
|
||||
|
||||
currentData.close();
|
||||
|
||||
currentData =
|
||||
gState->fileSystem().openRead(filename.constData(), FileSystem::Audio);
|
||||
|
||||
if (!this->music.openFromStream(currentData))
|
||||
return;
|
||||
|
||||
intVolume = (float) volume / 100;
|
||||
updateVolume();
|
||||
// this->music.setPitch((float) pitch / 100);
|
||||
|
||||
if (!extPaused)
|
||||
this->music.play();
|
||||
else
|
||||
noFadeInFlag = true;
|
||||
|
||||
SDL_UnlockMutex(mutex);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
terminateFade();
|
||||
|
||||
stopPriv();
|
||||
}
|
||||
|
||||
void fade(unsigned int ms)
|
||||
{
|
||||
if (music.getStatus() != sf::Music::Playing)
|
||||
return;
|
||||
|
||||
if (fading)
|
||||
return;
|
||||
|
||||
delete fadeData.thread;
|
||||
fadeData.thread = new sf::Thread(&MusicEntity::processFade, this);
|
||||
fadeData.duration = ms;
|
||||
fadeData.terminate = false;
|
||||
|
||||
fading = true;
|
||||
fadeData.thread->launch();
|
||||
}
|
||||
|
||||
void updateVolume()
|
||||
{
|
||||
music.setVolume(intVolume * extVolume * 100);
|
||||
}
|
||||
|
||||
void updateVolumeSync()
|
||||
{
|
||||
SDL_LockMutex(mutex);
|
||||
updateVolume();
|
||||
SDL_UnlockMutex(mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
void terminateFade()
|
||||
{
|
||||
if (!fading)
|
||||
return;
|
||||
|
||||
/* Tell our thread to wrap up and wait for it */
|
||||
fadeData.terminate = true;
|
||||
fadeData.thread->wait();
|
||||
|
||||
fading = false;
|
||||
}
|
||||
|
||||
void stopPriv()
|
||||
{
|
||||
SDL_LockMutex(mutex);
|
||||
music.stop();
|
||||
SDL_UnlockMutex(mutex);
|
||||
|
||||
filename = QByteArray();
|
||||
}
|
||||
|
||||
void processFade()
|
||||
{
|
||||
float msStep = music.getVolume() / fadeData.duration;
|
||||
sf::Clock timer;
|
||||
sf::Time sleepTime = sf::milliseconds(FADE_SLEEP);
|
||||
unsigned int currentDur = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int elapsed = timer.getElapsedTime().asMilliseconds();
|
||||
timer.restart();
|
||||
currentDur += elapsed;
|
||||
|
||||
if (music.getStatus() != sf::Music::Playing)
|
||||
break;
|
||||
|
||||
if (fadeData.terminate)
|
||||
break;
|
||||
|
||||
if (currentDur >= fadeData.duration)
|
||||
break;
|
||||
|
||||
intVolume = (float) (music.getVolume() - (elapsed * msStep)) / 100;
|
||||
updateVolumeSync();
|
||||
|
||||
sf::sleep(sleepTime);
|
||||
}
|
||||
|
||||
stopPriv();
|
||||
fading = false;
|
||||
}
|
||||
};
|
||||
|
||||
//struct SoundBuffer
|
||||
//{
|
||||
// Mix_Chunk *sdlBuffer;
|
||||
// QByteArray filename;
|
||||
// IntruListLink<SoundBuffer> link;
|
||||
|
||||
// SoundBuffer()
|
||||
// : link(this)
|
||||
// {}
|
||||
//};
|
||||
|
||||
//struct SoundEntity
|
||||
//{
|
||||
// IntruList<SoundBuffer> buffers;
|
||||
// QHash<QByteArray, SoundBuffer*> bufferHash;
|
||||
// uint cacheSize; // in chunks, for now
|
||||
// int channelIndex;
|
||||
|
||||
// SoundEntity()
|
||||
// : cacheSize(0),
|
||||
// channelIndex(0)
|
||||
// {
|
||||
// Mix_AllocateChannels(SOUND_MAG_SIZE);
|
||||
// }
|
||||
|
||||
// ~SoundEntity()
|
||||
// {
|
||||
// IntruListLink<SoundBuffer> *iter = buffers.iterStart();
|
||||
// iter = iter->next;
|
||||
|
||||
// for (int i = 0; i < buffers.getSize(); ++i)
|
||||
// {
|
||||
// SoundBuffer *buffer = iter->data;
|
||||
// iter = iter->next;
|
||||
// delete buffer;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void play(const char *filename,
|
||||
// int volume,
|
||||
// int pitch)
|
||||
// {
|
||||
// (void) pitch;
|
||||
|
||||
// volume = bound(volume, 0, 100);
|
||||
|
||||
// Mix_Chunk *buffer = requestBuffer(filename);
|
||||
|
||||
// int nextChIdx = channelIndex++;
|
||||
// if (channelIndex > SOUND_MAG_SIZE-1)
|
||||
// channelIndex = 0;
|
||||
|
||||
// Mix_HaltChannel(nextChIdx);
|
||||
// Mix_Volume(nextChIdx, ((float) MIX_MAX_VOLUME / 100) * volume);
|
||||
// Mix_PlayChannelTimed(nextChIdx, buffer, 0, -1);
|
||||
// }
|
||||
|
||||
// void stop()
|
||||
// {
|
||||
// /* Stop all channels */
|
||||
// Mix_HaltChannel(-1);
|
||||
// }
|
||||
|
||||
//private:
|
||||
// Mix_Chunk *requestBuffer(const char *filename)
|
||||
// {
|
||||
// SoundBuffer *buffer = bufferHash.value(filename, 0);
|
||||
|
||||
// if (buffer)
|
||||
// {
|
||||
// /* Buffer still in cashe.
|
||||
// * Move to front of priority list */
|
||||
// buffers.remove(buffer->link);
|
||||
// buffers.append(buffer->link);
|
||||
|
||||
// return buffer->sdlBuffer;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// /* Buffer not in cashe, needs to be loaded */
|
||||
// SDL_RWops ops;
|
||||
// gState->fileSystem().openRead(ops, filename, FileSystem::Audio);
|
||||
|
||||
// Mix_Chunk *sdlBuffer = Mix_LoadWAV_RW(&ops, 1);
|
||||
|
||||
// if (!sdlBuffer)
|
||||
// {
|
||||
// SDL_RWclose(&ops);
|
||||
// throw Exception(Exception::RGSSError, "Unable to read sound file");
|
||||
// }
|
||||
|
||||
// buffer = new SoundBuffer;
|
||||
// buffer->sdlBuffer = sdlBuffer;
|
||||
// buffer->filename = filename;
|
||||
|
||||
// ++cacheSize;
|
||||
|
||||
// bufferHash.insert(filename, buffer);
|
||||
// buffers.prepend(buffer->link);
|
||||
|
||||
// if (cacheSize > 20)
|
||||
// {
|
||||
// SoundBuffer *last = buffers.tail();
|
||||
// bufferHash.remove(last->filename);
|
||||
// buffers.remove(last->link);
|
||||
// --cacheSize;
|
||||
|
||||
// qDebug() << "Deleted buffer" << last->filename;
|
||||
|
||||
// delete last;
|
||||
// }
|
||||
|
||||
// return buffer->sdlBuffer;
|
||||
// }
|
||||
// }
|
||||
//};
|
||||
|
||||
struct SoundBuffer
|
||||
{
|
||||
sf::SoundBuffer sfBuffer;
|
||||
QByteArray filename;
|
||||
IntruListLink<SoundBuffer> link;
|
||||
|
||||
SoundBuffer()
|
||||
: link(this)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SoundEntity
|
||||
{
|
||||
IntruList<SoundBuffer> buffers;
|
||||
QHash<QByteArray, SoundBuffer*> bufferHash;
|
||||
unsigned int bufferSamples;
|
||||
|
||||
sf::Sound soundMag[SOUND_MAG_SIZE];
|
||||
int magIndex;
|
||||
|
||||
SoundEntity()
|
||||
: bufferSamples(0),
|
||||
magIndex(0)
|
||||
{}
|
||||
|
||||
~SoundEntity()
|
||||
{
|
||||
QHash<QByteArray, SoundBuffer*>::iterator iter;
|
||||
for (iter = bufferHash.begin(); iter != bufferHash.end(); ++iter)
|
||||
delete iter.value();
|
||||
}
|
||||
|
||||
void play(const char *filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
(void) pitch;
|
||||
|
||||
volume = bound<int>(volume, 0, 100);
|
||||
|
||||
sf::SoundBuffer &buffer = allocateBuffer(filename);
|
||||
|
||||
int soundIndex = magIndex++;
|
||||
if (magIndex > SOUND_MAG_SIZE-1)
|
||||
magIndex = 0;
|
||||
|
||||
sf::Sound &sound = soundMag[soundIndex];
|
||||
sound.stop();
|
||||
sound.setBuffer(buffer);
|
||||
sound.setVolume(volume);
|
||||
|
||||
sound.play();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
for (int i = 0; i < SOUND_MAG_SIZE; i++)
|
||||
soundMag[i].stop();
|
||||
}
|
||||
|
||||
private:
|
||||
sf::SoundBuffer &allocateBuffer(const char *filename)
|
||||
{
|
||||
SoundBuffer *buffer = bufferHash.value(filename, 0);
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
/* Buffer still in cashe.
|
||||
* Move to front of priority list */
|
||||
|
||||
buffers.remove(buffer->link);
|
||||
buffers.append(buffer->link);
|
||||
|
||||
return buffer->sfBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Buffer not in cashe, needs to be loaded */
|
||||
|
||||
FileStream data =
|
||||
gState->fileSystem().openRead(filename, FileSystem::Audio);
|
||||
|
||||
buffer = new SoundBuffer;
|
||||
buffer->sfBuffer.loadFromStream(data);
|
||||
bufferSamples += buffer->sfBuffer.getSampleCount();
|
||||
|
||||
data.close();
|
||||
|
||||
// qDebug() << "SoundCache: Current memory consumption:" << bufferSamples/2;
|
||||
|
||||
buffer->filename = filename;
|
||||
|
||||
bufferHash.insert(filename, buffer);
|
||||
buffers.prepend(buffer->link);
|
||||
|
||||
// FIXME this part would look better if it actually looped until enough memory is freed
|
||||
/* If memory limit is reached, delete lowest priority buffer.
|
||||
* Samples are 2 bytes big */
|
||||
if ((bufferSamples/2) > SOUND_MAX_MEM && !buffers.isEmpty())
|
||||
{
|
||||
SoundBuffer *last = buffers.tail();
|
||||
bufferHash.remove(last->filename);
|
||||
buffers.remove(last->link);
|
||||
bufferSamples -= last->sfBuffer.getSampleCount();
|
||||
|
||||
qDebug() << "Deleted buffer" << last->filename;
|
||||
|
||||
delete last;
|
||||
}
|
||||
|
||||
return buffer->sfBuffer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AudioPrivate
|
||||
{
|
||||
MusicEntity bgm;
|
||||
MusicEntity bgs;
|
||||
MusicEntity me;
|
||||
|
||||
SoundEntity se;
|
||||
|
||||
sf::Thread *meWatchThread;
|
||||
bool meWatchRunning;
|
||||
bool meWatchThreadTerm;
|
||||
|
||||
AudioPrivate()
|
||||
: meWatchThread(0),
|
||||
meWatchRunning(false),
|
||||
meWatchThreadTerm(false)
|
||||
{
|
||||
me.music.setLoop(false);
|
||||
}
|
||||
|
||||
~AudioPrivate()
|
||||
{
|
||||
if (meWatchThread)
|
||||
{
|
||||
meWatchThreadTerm = true;
|
||||
meWatchThread->wait();
|
||||
delete meWatchThread;
|
||||
}
|
||||
|
||||
bgm.stop();
|
||||
bgs.stop();
|
||||
me.stop();
|
||||
se.stop();
|
||||
}
|
||||
|
||||
void scheduleMeWatch()
|
||||
{
|
||||
if (meWatchRunning)
|
||||
return;
|
||||
|
||||
meWatchRunning = true;
|
||||
|
||||
if (meWatchThread)
|
||||
meWatchThread->wait();
|
||||
|
||||
bgm.extPaused = true;
|
||||
|
||||
delete meWatchThread;
|
||||
meWatchThread = new sf::Thread(&AudioPrivate::meWatchFunc, this);
|
||||
meWatchThread->launch();
|
||||
}
|
||||
|
||||
void meWatchFunc()
|
||||
{
|
||||
// FIXME Need to catch the case where an ME is started while
|
||||
// the BGM is still being faded in from this function
|
||||
sf::Time sleepTime = sf::milliseconds(FADE_SLEEP);
|
||||
const int bgmFadeOutSteps = 20;
|
||||
const int bgmFadeInSteps = 100;
|
||||
|
||||
/* Fade out BGM */
|
||||
for (int i = bgmFadeOutSteps; i > 0; --i)
|
||||
{
|
||||
if (meWatchThreadTerm)
|
||||
return;
|
||||
|
||||
if (bgm.music.getStatus() != sf::Music::Playing)
|
||||
{
|
||||
bgm.extVolume = 0;
|
||||
bgm.updateVolumeSync();
|
||||
break;
|
||||
}
|
||||
|
||||
bgm.extVolume = (1.0 / bgmFadeOutSteps) * (i-1);
|
||||
bgm.updateVolumeSync();
|
||||
sf::sleep(sleepTime);
|
||||
}
|
||||
|
||||
SDL_LockMutex(bgm.mutex);
|
||||
if (bgm.music.getStatus() == sf::Music::Playing)
|
||||
bgm.music.pause();
|
||||
SDL_UnlockMutex(bgm.mutex);
|
||||
|
||||
/* Linger while ME plays */
|
||||
while (me.music.getStatus() == sf::Music::Playing)
|
||||
{
|
||||
if (meWatchThreadTerm)
|
||||
return;
|
||||
|
||||
sf::sleep(sleepTime);
|
||||
}
|
||||
|
||||
SDL_LockMutex(bgm.mutex);
|
||||
bgm.extPaused = false;
|
||||
|
||||
if (bgm.music.getStatus() == sf::Music::Paused)
|
||||
bgm.music.play();
|
||||
SDL_UnlockMutex(bgm.mutex);
|
||||
|
||||
/* Fade in BGM again */
|
||||
for (int i = 0; i < bgmFadeInSteps; ++i)
|
||||
{
|
||||
if (meWatchThreadTerm)
|
||||
return;
|
||||
|
||||
SDL_LockMutex(bgm.mutex);
|
||||
|
||||
if (bgm.music.getStatus() != sf::Music::Playing || bgm.noFadeInFlag)
|
||||
{
|
||||
bgm.noFadeInFlag = false;
|
||||
bgm.extVolume = 1;
|
||||
bgm.updateVolume();
|
||||
bgm.music.play();
|
||||
SDL_UnlockMutex(bgm.mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
bgm.extVolume = (1.0 / bgmFadeInSteps) * (i+1);
|
||||
bgm.updateVolume();
|
||||
|
||||
SDL_UnlockMutex(bgm.mutex);
|
||||
|
||||
sf::sleep(sleepTime);
|
||||
}
|
||||
|
||||
meWatchRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
Audio::Audio()
|
||||
: p(new AudioPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Audio::bgmPlay(const char *filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
p->bgm.play(filename, volume, pitch);
|
||||
}
|
||||
|
||||
void Audio::bgmStop()
|
||||
{
|
||||
p->bgm.stop();
|
||||
}
|
||||
|
||||
void Audio::bgmFade(int time)
|
||||
{
|
||||
p->bgm.fade(time);
|
||||
}
|
||||
|
||||
|
||||
void Audio::bgsPlay(const char *filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
p->bgs.play(filename, volume, pitch);
|
||||
}
|
||||
|
||||
void Audio::bgsStop()
|
||||
{
|
||||
p->bgs.stop();
|
||||
}
|
||||
|
||||
void Audio::bgsFade(int time)
|
||||
{
|
||||
p->bgs.fade(time);
|
||||
}
|
||||
|
||||
|
||||
void Audio::mePlay(const char *filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
p->me.play(filename, volume, pitch);
|
||||
p->scheduleMeWatch();
|
||||
}
|
||||
|
||||
void Audio::meStop()
|
||||
{
|
||||
p->me.stop();
|
||||
}
|
||||
|
||||
void Audio::meFade(int time)
|
||||
{
|
||||
p->me.fade(time);
|
||||
}
|
||||
|
||||
|
||||
void Audio::sePlay(const char *filename,
|
||||
int volume,
|
||||
int pitch)
|
||||
{
|
||||
p->se.play(filename, volume, pitch);
|
||||
}
|
||||
|
||||
void Audio::seStop()
|
||||
{
|
||||
p->se.stop();
|
||||
}
|
||||
|
||||
Audio::~Audio() { delete p; }
|
52
src/audio.h
Normal file
52
src/audio.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
** audio.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
struct AudioPrivate;
|
||||
|
||||
class Audio
|
||||
{
|
||||
public:
|
||||
Audio();
|
||||
~Audio();
|
||||
|
||||
void bgmPlay(const char *filename, int volume = 100, int pitch = 100);
|
||||
void bgmStop();
|
||||
void bgmFade(int time);
|
||||
|
||||
void bgsPlay(const char *filename, int volume = 100, int pitch = 100);
|
||||
void bgsStop();
|
||||
void bgsFade(int time);
|
||||
|
||||
void mePlay(const char *filename, int volume = 100, int pitch = 100);
|
||||
void meStop();
|
||||
void meFade(int time);
|
||||
|
||||
void sePlay(const char *filename, int volume = 100, int pitch = 100);
|
||||
void seStop();
|
||||
|
||||
private:
|
||||
AudioPrivate *p;
|
||||
};
|
||||
|
||||
#endif // AUDIO_H
|
199
src/autotiles.cpp
Normal file
199
src/autotiles.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
struct StaticRect { float x, y, w, h; };
|
||||
|
||||
extern const StaticRect autotileRects[] =
|
||||
{
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 80.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 48.5, 64.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 32.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 0.5, 64.5, 15, 15 },
|
||||
{ 16.5, 64.5, 15, 15 },
|
||||
{ 16.5, 80.5, 15, 15 },
|
||||
{ 0.5, 80.5, 15, 15 },
|
||||
{ 0.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 16.5, 80.5, 15, 15 },
|
||||
{ 0.5, 80.5, 15, 15 },
|
||||
{ 0.5, 64.5, 15, 15 },
|
||||
{ 16.5, 64.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 0.5, 80.5, 15, 15 },
|
||||
{ 0.5, 64.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 0.5, 80.5, 15, 15 },
|
||||
{ 32.5, 32.5, 15, 15 },
|
||||
{ 48.5, 32.5, 15, 15 },
|
||||
{ 48.5, 48.5, 15, 15 },
|
||||
{ 32.5, 48.5, 15, 15 },
|
||||
{ 32.5, 32.5, 15, 15 },
|
||||
{ 48.5, 32.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 48.5, 15, 15 },
|
||||
{ 32.5, 32.5, 15, 15 },
|
||||
{ 48.5, 32.5, 15, 15 },
|
||||
{ 48.5, 48.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 32.5, 32.5, 15, 15 },
|
||||
{ 48.5, 32.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 32.5, 48.5, 15, 15 },
|
||||
{ 64.5, 64.5, 15, 15 },
|
||||
{ 80.5, 64.5, 15, 15 },
|
||||
{ 80.5, 80.5, 15, 15 },
|
||||
{ 64.5, 80.5, 15, 15 },
|
||||
{ 64.5, 64.5, 15, 15 },
|
||||
{ 80.5, 64.5, 15, 15 },
|
||||
{ 80.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 64.5, 15, 15 },
|
||||
{ 80.5, 80.5, 15, 15 },
|
||||
{ 64.5, 80.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 64.5, 15, 15 },
|
||||
{ 80.5, 80.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 32.5, 96.5, 15, 15 },
|
||||
{ 48.5, 96.5, 15, 15 },
|
||||
{ 48.5, 112.5, 15, 15 },
|
||||
{ 32.5, 112.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 48.5, 96.5, 15, 15 },
|
||||
{ 48.5, 112.5, 15, 15 },
|
||||
{ 32.5, 112.5, 15, 15 },
|
||||
{ 32.5, 96.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 112.5, 15, 15 },
|
||||
{ 32.5, 112.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 48.5, 112.5, 15, 15 },
|
||||
{ 32.5, 112.5, 15, 15 },
|
||||
{ 0.5, 64.5, 15, 15 },
|
||||
{ 80.5, 64.5, 15, 15 },
|
||||
{ 80.5, 80.5, 15, 15 },
|
||||
{ 0.5, 80.5, 15, 15 },
|
||||
{ 32.5, 32.5, 15, 15 },
|
||||
{ 48.5, 32.5, 15, 15 },
|
||||
{ 48.5, 112.5, 15, 15 },
|
||||
{ 32.5, 112.5, 15, 15 },
|
||||
{ 0.5, 32.5, 15, 15 },
|
||||
{ 16.5, 32.5, 15, 15 },
|
||||
{ 16.5, 48.5, 15, 15 },
|
||||
{ 0.5, 48.5, 15, 15 },
|
||||
{ 0.5, 32.5, 15, 15 },
|
||||
{ 16.5, 32.5, 15, 15 },
|
||||
{ 80.5, 16.5, 15, 15 },
|
||||
{ 0.5, 48.5, 15, 15 },
|
||||
{ 64.5, 32.5, 15, 15 },
|
||||
{ 80.5, 32.5, 15, 15 },
|
||||
{ 80.5, 48.5, 15, 15 },
|
||||
{ 64.5, 48.5, 15, 15 },
|
||||
{ 64.5, 32.5, 15, 15 },
|
||||
{ 80.5, 32.5, 15, 15 },
|
||||
{ 80.5, 48.5, 15, 15 },
|
||||
{ 64.5, 16.5, 15, 15 },
|
||||
{ 64.5, 96.5, 15, 15 },
|
||||
{ 80.5, 96.5, 15, 15 },
|
||||
{ 80.5, 112.5, 15, 15 },
|
||||
{ 64.5, 112.5, 15, 15 },
|
||||
{ 64.5, 0.5, 15, 15 },
|
||||
{ 80.5, 96.5, 15, 15 },
|
||||
{ 80.5, 112.5, 15, 15 },
|
||||
{ 64.5, 112.5, 15, 15 },
|
||||
{ 0.5, 96.5, 15, 15 },
|
||||
{ 16.5, 96.5, 15, 15 },
|
||||
{ 16.5, 112.5, 15, 15 },
|
||||
{ 0.5, 112.5, 15, 15 },
|
||||
{ 0.5, 96.5, 15, 15 },
|
||||
{ 80.5, 0.5, 15, 15 },
|
||||
{ 16.5, 112.5, 15, 15 },
|
||||
{ 0.5, 112.5, 15, 15 },
|
||||
{ 0.5, 32.5, 15, 15 },
|
||||
{ 80.5, 32.5, 15, 15 },
|
||||
{ 80.5, 48.5, 15, 15 },
|
||||
{ 0.5, 48.5, 15, 15 },
|
||||
{ 0.5, 32.5, 15, 15 },
|
||||
{ 16.5, 32.5, 15, 15 },
|
||||
{ 16.5, 112.5, 15, 15 },
|
||||
{ 0.5, 112.5, 15, 15 },
|
||||
{ 0.5, 96.5, 15, 15 },
|
||||
{ 80.5, 96.5, 15, 15 },
|
||||
{ 80.5, 112.5, 15, 15 },
|
||||
{ 0.5, 112.5, 15, 15 },
|
||||
{ 64.5, 32.5, 15, 15 },
|
||||
{ 80.5, 32.5, 15, 15 },
|
||||
{ 80.5, 112.5, 15, 15 },
|
||||
{ 64.5, 112.5, 15, 15 },
|
||||
{ 0.5, 32.5, 15, 15 },
|
||||
{ 80.5, 32.5, 15, 15 },
|
||||
{ 16.5, 112.5, 15, 15 },
|
||||
{ 64.5, 112.5, 15, 15 },
|
||||
{ 0.5, 0.5, 15, 15 },
|
||||
{ 16.5, 0.5, 15, 15 },
|
||||
{ 16.5, 16.5, 15, 15 },
|
||||
{ 0.5, 16.5, 15, 15 }
|
||||
};
|
||||
|
||||
extern const int autotileRectsN = sizeof(autotileRects) / sizeof(autotileRects[0]);
|
43
src/binding.h
Normal file
43
src/binding.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
** binding.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BINDING_H
|
||||
#define BINDING_H
|
||||
|
||||
struct ScriptBinding
|
||||
{
|
||||
/* Starts the part where the binding takes over,
|
||||
* loading the compressed scripts and executing them.
|
||||
* This function returns as soon as the scripts finish
|
||||
* execution or an error is encountered */
|
||||
void (*execute) (void);
|
||||
|
||||
/* Instructs the binding
|
||||
* to immediately terminate script execution. This
|
||||
* function will perform a longjmp instead of returning,
|
||||
* so be careful about any variables with local storage */
|
||||
void (*terminate) (void);
|
||||
};
|
||||
|
||||
/* VTable defined in the binding source */
|
||||
extern ScriptBinding *scriptBinding;
|
||||
|
||||
#endif // BINDING_H
|
573
src/bitmap.cpp
Normal file
573
src/bitmap.cpp
Normal file
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
** bitmap.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bitmap.h"
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_image.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
#include "quadarray.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "glstate.h"
|
||||
#include "texpool.h"
|
||||
#include "shader.h"
|
||||
#include "filesystem.h"
|
||||
#include "font.h"
|
||||
#include "eventthread.h"
|
||||
|
||||
#define DISP_CLASS_NAME "bitmap"
|
||||
|
||||
struct BitmapPrivate
|
||||
{
|
||||
TexFBO tex;
|
||||
|
||||
/* 'setPixel()' calls are cached and executed
|
||||
* in batches on 'flush()' */
|
||||
PointArray pointArray;
|
||||
|
||||
Font *font;
|
||||
|
||||
BitmapPrivate()
|
||||
{
|
||||
font = &gState->defaultFont();
|
||||
}
|
||||
|
||||
void bindTextureWithMatrix()
|
||||
{
|
||||
Tex::bind(tex.tex);
|
||||
Tex::bindMatrix(tex.width, tex.height);
|
||||
}
|
||||
|
||||
void bindFBO()
|
||||
{
|
||||
FBO::bind(tex.fbo);
|
||||
}
|
||||
|
||||
void pushSetViewport() const
|
||||
{
|
||||
glState.pushSetViewport(tex.width, tex.height);
|
||||
}
|
||||
|
||||
void popViewport() const
|
||||
{
|
||||
glState.popViewport();
|
||||
}
|
||||
|
||||
void blitQuad(Quad &quad)
|
||||
{
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
quad.draw();
|
||||
glState.blendMode.pop();
|
||||
}
|
||||
|
||||
void flushPoints()
|
||||
{
|
||||
if (pointArray.count() == 0)
|
||||
return;
|
||||
|
||||
Tex::unbind();
|
||||
bindFBO();
|
||||
pushSetViewport();
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
pointArray.commit();
|
||||
pointArray.draw();
|
||||
pointArray.reset();
|
||||
|
||||
glState.blendMode.pop();
|
||||
popViewport();
|
||||
}
|
||||
|
||||
void fillRect(const IntRect &rect,
|
||||
const Vec4 &color)
|
||||
{
|
||||
flushPoints();
|
||||
|
||||
bindFBO();
|
||||
|
||||
glState.scissorTest.pushSet(true);
|
||||
glState.scissorBox.pushSet(rect);
|
||||
glState.clearColor.pushSet(color);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glState.clearColor.pop();
|
||||
glState.scissorBox.pop();
|
||||
glState.scissorTest.pop();
|
||||
}
|
||||
|
||||
static void ensureFormat(SDL_Surface *&surf, Uint32 format)
|
||||
{
|
||||
if (surf->format->format == format)
|
||||
return;
|
||||
|
||||
SDL_Surface *surfConv = SDL_ConvertSurfaceFormat(surf, format, 0);
|
||||
SDL_FreeSurface(surf);
|
||||
surf = surfConv;
|
||||
}
|
||||
};
|
||||
|
||||
Bitmap::Bitmap(const char *filename)
|
||||
{
|
||||
SDL_RWops ops;
|
||||
gState->fileSystem().openRead(ops, filename, FileSystem::Image);
|
||||
SDL_Surface *imgSurf = IMG_Load_RW(&ops, 1);
|
||||
|
||||
if (!imgSurf)
|
||||
throw Exception(Exception::SDLError, "SDL: %s", SDL_GetError());
|
||||
|
||||
p = new BitmapPrivate;
|
||||
|
||||
p->ensureFormat(imgSurf, SDL_PIXELFORMAT_ABGR8888);
|
||||
|
||||
p->tex = gState->texPool().request(imgSurf->w, imgSurf->h);
|
||||
|
||||
Tex::bind(p->tex.tex);
|
||||
Tex::uploadImage(p->tex.width, p->tex.height, imgSurf->pixels, GL_RGBA);
|
||||
|
||||
SDL_FreeSurface(imgSurf);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height)
|
||||
{
|
||||
p = new BitmapPrivate;
|
||||
|
||||
p->tex = gState->texPool().request(width, height);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(const Bitmap &other)
|
||||
{
|
||||
p = new BitmapPrivate;
|
||||
|
||||
p->tex = gState->texPool().request(other.width(), other.height());
|
||||
|
||||
other.flush();
|
||||
blt(0, 0, other, rect());
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
int Bitmap::width() const
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
return p->tex.width;
|
||||
}
|
||||
|
||||
int Bitmap::height() const
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
return p->tex.height;
|
||||
}
|
||||
|
||||
IntRect Bitmap::rect() const
|
||||
{
|
||||
return IntRect(0, 0, width(), height());
|
||||
}
|
||||
|
||||
void Bitmap::blt(int x, int y,
|
||||
const Bitmap &source, const IntRect &rect,
|
||||
int opacity)
|
||||
{
|
||||
stretchBlt(IntRect(x, y, rect.w, rect.h),
|
||||
source, rect, opacity);
|
||||
}
|
||||
|
||||
void Bitmap::stretchBlt(const IntRect &destRect,
|
||||
const Bitmap &source, const IntRect &sourceRect,
|
||||
int opacity)
|
||||
{
|
||||
GUARD_DISPOSED;
|
||||
|
||||
opacity = bound(opacity, 0, 255);
|
||||
|
||||
if (opacity == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// else if (opacity == 255) /* Fast blit */
|
||||
// {
|
||||
// flush();
|
||||
|
||||
// FBO::bind(source.p->tex.fbo, FBO::Read);
|
||||
// FBO::bind(p->tex.fbo, FBO::Draw);
|
||||
|
||||
// FBO::blit(sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h,
|
||||
// destRect.x, destRect.y, destRect.w, destRect.h);
|
||||
// }
|
||||
else /* Fragment pipeline */
|
||||
{
|
||||
flush();
|
||||
|
||||
float normOpacity = (float) opacity / 255.0f;
|
||||
|
||||
TexFBO &gpTex = gState->gpTexFBO(destRect.w, destRect.h);
|
||||
|
||||
FBO::bind(gpTex.fbo, FBO::Draw);
|
||||
FBO::bind(p->tex.fbo, FBO::Read);
|
||||
FBO::blit(destRect.x, destRect.y, 0, 0, destRect.w, destRect.h);
|
||||
|
||||
FloatRect bltSubRect((float) sourceRect.x / source.width(),
|
||||
(float) sourceRect.y / source.height(),
|
||||
((float) source.width() / sourceRect.w) * ((float) destRect.w / gpTex.width),
|
||||
((float) source.height() / sourceRect.h) * ((float) destRect.h / gpTex.height));
|
||||
|
||||
BltShader &shader = gState->bltShader();
|
||||
shader.bind();
|
||||
shader.setDestination(gpTex.tex);
|
||||
shader.setSubRect(bltSubRect);
|
||||
shader.setOpacity(normOpacity);
|
||||
|
||||
Quad quad;
|
||||
quad.setTexPosRect(sourceRect, destRect);
|
||||
quad.setColor(Vec4(1, 1, 1, normOpacity));
|
||||
|
||||
source.p->bindTextureWithMatrix();
|
||||
p->bindFBO();
|
||||
p->pushSetViewport();
|
||||
|
||||
p->blitQuad(quad);
|
||||
|
||||
p->popViewport();
|
||||
shader.unbind();
|
||||
}
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::fillRect(int x, int y,
|
||||
int width, int height,
|
||||
const Vec4 &color)
|
||||
{
|
||||
fillRect(IntRect(x, y, width, height), color);
|
||||
}
|
||||
|
||||
void Bitmap::fillRect(const IntRect &rect, const Vec4 &color)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->fillRect(rect, color);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::gradientFillRect(int x, int y,
|
||||
int width, int height,
|
||||
const Vec4 &color1, const Vec4 &color2,
|
||||
bool vertical)
|
||||
{
|
||||
gradientFillRect(IntRect(x, y, width, height), color1, color2, vertical);
|
||||
}
|
||||
|
||||
void Bitmap::gradientFillRect(const IntRect &rect,
|
||||
const Vec4 &color1, const Vec4 &color2,
|
||||
bool vertical)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
flush();
|
||||
|
||||
Quad quad;
|
||||
|
||||
if (vertical)
|
||||
{
|
||||
quad.vert[0].color = color2;
|
||||
quad.vert[1].color = color2;
|
||||
quad.vert[2].color = color1;
|
||||
quad.vert[3].color = color1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quad.vert[0].color = color1;
|
||||
quad.vert[3].color = color1;
|
||||
quad.vert[1].color = color2;
|
||||
quad.vert[2].color = color2;
|
||||
}
|
||||
|
||||
quad.setPosRect(rect);
|
||||
|
||||
Tex::unbind();
|
||||
p->bindFBO();
|
||||
p->pushSetViewport();
|
||||
|
||||
p->blitQuad(quad);
|
||||
|
||||
p->popViewport();
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::clearRect(int x, int y, int width, int height)
|
||||
{
|
||||
clearRect(IntRect(x, y, width, height));
|
||||
}
|
||||
|
||||
void Bitmap::clearRect(const IntRect &rect)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->fillRect(rect, Vec4());
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::clear()
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
/* Any queued points won't be visible after this anyway */
|
||||
p->pointArray.reset();
|
||||
|
||||
p->bindFBO();
|
||||
|
||||
glState.clearColor.pushSet(Vec4());
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glState.clearColor.pop();
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
Vec4 Bitmap::getPixel(int x, int y) const
|
||||
{
|
||||
GUARD_DISPOSED;
|
||||
|
||||
if (x < 0 || y < 0 || x >= width() || y >= height())
|
||||
return Vec4();
|
||||
|
||||
flush();
|
||||
|
||||
p->bindFBO();
|
||||
|
||||
glState.viewport.push();
|
||||
Vec4 pixel = FBO::getPixel(x, y, width(), height());
|
||||
glState.viewport.pop();
|
||||
|
||||
return pixel;
|
||||
}
|
||||
|
||||
void Bitmap::setPixel(int x, int y, const Vec4 &color)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->pointArray.append(Vec2(x+.5, y+.5), color);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::hueChange(int hue)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if ((hue % 360) == 0)
|
||||
return;
|
||||
|
||||
flush();
|
||||
|
||||
TexFBO newTex = gState->texPool().request(width(), height());
|
||||
|
||||
FloatRect texRect(rect());
|
||||
|
||||
Quad quad;
|
||||
quad.setTexPosRect(texRect, texRect);
|
||||
|
||||
/* Calculate hue parameter */
|
||||
hue = wrapRange(hue, 0, 359);
|
||||
float hueAdj = -((M_PI * 2) / 360) * hue;
|
||||
|
||||
HueShader &shader = gState->hueShader();
|
||||
shader.bind();
|
||||
shader.setHueAdjust(hueAdj);
|
||||
shader.setInputTexture(p->tex.tex);
|
||||
|
||||
FBO::bind(newTex.fbo);
|
||||
Tex::bindMatrix(width(), height());
|
||||
p->pushSetViewport();
|
||||
|
||||
p->blitQuad(quad);
|
||||
|
||||
shader.unbind();
|
||||
|
||||
p->popViewport();
|
||||
|
||||
gState->texPool().release(p->tex);
|
||||
p->tex = newTex;
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Bitmap::drawText(int x, int y,
|
||||
int width, int height,
|
||||
const char *str, int align)
|
||||
{
|
||||
drawText(IntRect(x, y, width, height), str, align);
|
||||
}
|
||||
|
||||
void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (*str == '\0')
|
||||
return;
|
||||
|
||||
flush();
|
||||
|
||||
TTF_Font *font = p->font->getSdlFont();
|
||||
|
||||
SDL_Color c;
|
||||
p->font->getColor()->toSDLColor(c);
|
||||
|
||||
SDL_Surface *txtSurf;
|
||||
|
||||
if (gState->rtData().config.solidFonts)
|
||||
txtSurf = TTF_RenderUTF8_Solid(font, str, c);
|
||||
else
|
||||
txtSurf = TTF_RenderUTF8_Blended(font, str, c);
|
||||
|
||||
p->ensureFormat(txtSurf, SDL_PIXELFORMAT_ARGB8888);
|
||||
|
||||
int alignX = rect.x;
|
||||
|
||||
switch (align)
|
||||
{
|
||||
default:
|
||||
case Left :
|
||||
break;
|
||||
|
||||
case Center :
|
||||
alignX += (rect.w - txtSurf->w) / 2;
|
||||
break;
|
||||
|
||||
case Right :
|
||||
alignX += rect.w - txtSurf->w;
|
||||
break;
|
||||
}
|
||||
|
||||
if (alignX < rect.x)
|
||||
alignX = rect.x;
|
||||
|
||||
int alignY = rect.y + (rect.h - txtSurf->h) / 2;
|
||||
|
||||
float squeeze = (float) rect.w / txtSurf->w;
|
||||
|
||||
if (squeeze > 1)
|
||||
squeeze = 1;
|
||||
|
||||
FloatRect posRect(alignX, alignY, txtSurf->w * squeeze, txtSurf->h);
|
||||
|
||||
Vec2 gpTexSize;
|
||||
gState->ensureTexSize(txtSurf->w, txtSurf->h, gpTexSize);
|
||||
|
||||
// if (str[1] != '\0')
|
||||
{
|
||||
/* Aquire a partial copy of the destination
|
||||
* buffer we're about to render to */
|
||||
TexFBO &gpTex2 = gState->gpTexFBO(posRect.w, posRect.h);
|
||||
|
||||
FBO::bind(gpTex2.fbo, FBO::Draw);
|
||||
FBO::bind(p->tex.fbo, FBO::Read);
|
||||
FBO::blit(posRect.x, posRect.y, 0, 0, posRect.w, posRect.h);
|
||||
|
||||
FloatRect bltRect(0, 0,
|
||||
gpTexSize.x / gpTex2.width,
|
||||
gpTexSize.y / gpTex2.height);
|
||||
|
||||
BltShader &shader = gState->bltShader();
|
||||
shader.bind();
|
||||
shader.setSource();
|
||||
shader.setDestination(gpTex2.tex);
|
||||
shader.setSubRect(bltRect);
|
||||
shader.setOpacity(p->font->getColor()->norm.w);
|
||||
}
|
||||
|
||||
gState->bindTex();
|
||||
Tex::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_BGRA);
|
||||
Tex::setSmooth(true);
|
||||
|
||||
Quad quad;
|
||||
quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h));
|
||||
quad.setPosRect(posRect);
|
||||
SDL_FreeSurface(txtSurf);
|
||||
|
||||
p->bindFBO();
|
||||
p->pushSetViewport();
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
quad.draw();
|
||||
|
||||
glState.blendMode.pop();
|
||||
p->popViewport();
|
||||
|
||||
FragShader::unbind();
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
IntRect Bitmap::textSize(const char *str)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
TTF_Font *font = p->font->getSdlFont();
|
||||
|
||||
int w, h;
|
||||
TTF_SizeUTF8(font, str, &w, &h);
|
||||
|
||||
// if (strlen(str) == 1)
|
||||
// TTF_GlyphMetrics(font, *str, 0, 0, 0, 0, &w);
|
||||
|
||||
return IntRect(0, 0, w, h);
|
||||
}
|
||||
|
||||
DEF_ATTR_SIMPLE(Bitmap, Font, Font*, p->font)
|
||||
|
||||
void Bitmap::flush() const
|
||||
{
|
||||
p->flushPoints();
|
||||
}
|
||||
|
||||
TexFBO &Bitmap::getGLTypes()
|
||||
{
|
||||
return p->tex;
|
||||
}
|
||||
|
||||
void Bitmap::bindTexWithMatrix()
|
||||
{
|
||||
p->bindTextureWithMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Bitmap::releaseResources()
|
||||
{
|
||||
gState->texPool().release(p->tex);
|
||||
delete p;
|
||||
}
|
115
src/bitmap.h
Normal file
115
src/bitmap.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
** bitmap.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BITMAP_H
|
||||
#define BITMAP_H
|
||||
|
||||
#include "disposable.h"
|
||||
#include "etc-internal.h"
|
||||
#include "etc.h"
|
||||
|
||||
#include "sigc++/signal.h"
|
||||
|
||||
class Font;
|
||||
struct TexFBO;
|
||||
|
||||
struct BitmapPrivate;
|
||||
// FIXME make this class use proper RGSS classes again
|
||||
class Bitmap : public Disposable
|
||||
{
|
||||
public:
|
||||
Bitmap(const char *filename);
|
||||
Bitmap(int width, int height);
|
||||
/* Clone constructor */
|
||||
Bitmap(const Bitmap &other);
|
||||
~Bitmap();
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
IntRect rect() const;
|
||||
|
||||
void blt(int x, int y,
|
||||
const Bitmap &source, const IntRect &rect,
|
||||
int opacity = 255);
|
||||
|
||||
void stretchBlt(const IntRect &destRect,
|
||||
const Bitmap &source, const IntRect &sourceRect,
|
||||
int opacity = 255);
|
||||
|
||||
void fillRect(int x, int y,
|
||||
int width, int height,
|
||||
const Vec4 &color);
|
||||
void fillRect(const IntRect &rect, const Vec4 &color);
|
||||
|
||||
/* RGSS2 */
|
||||
void gradientFillRect(int x, int y,
|
||||
int width, int height,
|
||||
const Vec4 &color1, const Vec4 &color2,
|
||||
bool vertical = false);
|
||||
void gradientFillRect(const IntRect &rect,
|
||||
const Vec4 &color1, const Vec4 &color2,
|
||||
bool vertical = false);
|
||||
|
||||
void clearRect(int x, int y,
|
||||
int width, int height);
|
||||
void clearRect(const IntRect &rect);
|
||||
|
||||
// void radialBlur(int angle, int divisions);
|
||||
// /* ----- */
|
||||
|
||||
void clear();
|
||||
|
||||
Vec4 getPixel(int x, int y) const;
|
||||
void setPixel(int x, int y, const Vec4 &color);
|
||||
|
||||
void hueChange(int hue);
|
||||
|
||||
enum TextAlign
|
||||
{
|
||||
Left = 0,
|
||||
Center = 1,
|
||||
Right = 2
|
||||
};
|
||||
|
||||
void drawText(int x, int y,
|
||||
int width, int height,
|
||||
const char *str, int align = Left);
|
||||
|
||||
void drawText(const IntRect &rect,
|
||||
const char *str, int align = Left);
|
||||
|
||||
IntRect textSize(const char *str);
|
||||
|
||||
DECL_ATTR(Font, Font*)
|
||||
|
||||
void flush() const;
|
||||
TexFBO &getGLTypes();
|
||||
void bindTexWithMatrix();
|
||||
|
||||
sigc::signal<void> modified;
|
||||
|
||||
private:
|
||||
void releaseResources();
|
||||
|
||||
BitmapPrivate *p;
|
||||
};
|
||||
|
||||
#endif // BITMAP_H
|
87
src/config.cpp
Normal file
87
src/config.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
** config.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
#include <QRegExp>
|
||||
|
||||
Config::Config()
|
||||
: debugMode(false),
|
||||
winResizable(false),
|
||||
fullscreen(false),
|
||||
vsync(false),
|
||||
defScreenW(640),
|
||||
defScreenH(480),
|
||||
solidFonts(false),
|
||||
gameFolder(".")
|
||||
{}
|
||||
|
||||
void Config::read()
|
||||
{
|
||||
QSettings confFile("mkxp.conf", QSettings::IniFormat);
|
||||
|
||||
#define READ_VAL(key, Type) key = confFile.value(#key, key).to##Type()
|
||||
|
||||
READ_VAL(debugMode, Bool);
|
||||
READ_VAL(winResizable, Bool);
|
||||
READ_VAL(fullscreen, Bool);
|
||||
READ_VAL(vsync, Bool);
|
||||
READ_VAL(defScreenW, Int);
|
||||
READ_VAL(defScreenH, Int);
|
||||
READ_VAL(solidFonts, Bool);
|
||||
READ_VAL(gameFolder, ByteArray);
|
||||
READ_VAL(customScript, ByteArray);
|
||||
|
||||
QStringList _rtps = confFile.value("RTPs").toStringList();
|
||||
Q_FOREACH(const QString &s, _rtps)
|
||||
rtps << s.toUtf8();
|
||||
}
|
||||
|
||||
void Config::readGameINI()
|
||||
{
|
||||
if (!customScript.isEmpty())
|
||||
{
|
||||
game.title = basename(customScript.constData());
|
||||
return;
|
||||
}
|
||||
|
||||
QSettings gameINI(gameFolder + "/Game.ini", QSettings::IniFormat);
|
||||
QFileInfo finfo(gameFolder.constData());
|
||||
game.title = gameINI.value("Game/Title", finfo.baseName()).toByteArray();
|
||||
|
||||
/* Gotta read the "Scripts" entry manually because Qt can't handle '\' */
|
||||
QFile gameINIFile(gameFolder + "/Game.ini");
|
||||
if (gameINIFile.open(QFile::ReadOnly))
|
||||
{
|
||||
QString gameINIContents = gameINIFile.readAll();
|
||||
QRegExp scriptsRE(".*Scripts=(.*)\r\nTitle=.*");
|
||||
if (scriptsRE.exactMatch(gameINIContents))
|
||||
{
|
||||
game.scripts = scriptsRE.cap(1).toUtf8();
|
||||
game.scripts.replace('\\', '/');
|
||||
}
|
||||
}
|
||||
}
|
58
src/config.h
Normal file
58
src/config.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
** config.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
|
||||
struct Config
|
||||
{
|
||||
bool debugMode;
|
||||
|
||||
bool winResizable;
|
||||
bool fullscreen;
|
||||
bool vsync;
|
||||
|
||||
int defScreenW;
|
||||
int defScreenH;
|
||||
|
||||
bool solidFonts;
|
||||
|
||||
QByteArray gameFolder;
|
||||
|
||||
QByteArray customScript;
|
||||
QVector<QByteArray> rtps;
|
||||
|
||||
/* Game INI contents */
|
||||
struct {
|
||||
QByteArray scripts;
|
||||
QByteArray title;
|
||||
} game;
|
||||
|
||||
Config();
|
||||
|
||||
void read();
|
||||
void readGameINI();
|
||||
};
|
||||
|
||||
#endif // CONFIG_H
|
123
src/debuglogger.cpp
Normal file
123
src/debuglogger.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
** debuglogger.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "debuglogger.h"
|
||||
|
||||
#include "GL/glew.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTime>
|
||||
#include <QTextStream>
|
||||
#include <QDebug>
|
||||
|
||||
struct DebugLoggerPrivate
|
||||
{
|
||||
QFile logFile;
|
||||
QTextStream *stream;
|
||||
|
||||
DebugLoggerPrivate(const char *logFilename)
|
||||
{
|
||||
if (logFilename)
|
||||
{
|
||||
logFile.setFileName(logFilename);
|
||||
logFile.open(QFile::WriteOnly);
|
||||
stream = new QTextStream(&logFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new QTextStream(stderr, QIODevice::WriteOnly);
|
||||
}
|
||||
}
|
||||
|
||||
~DebugLoggerPrivate()
|
||||
{
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void writeTimestamp()
|
||||
{
|
||||
QTime time = QTime::currentTime();
|
||||
*stream << "[" << time.toString().toAscii() << "] ";
|
||||
}
|
||||
|
||||
void writeLine(const char *line)
|
||||
{
|
||||
*stream << line << "\n";
|
||||
stream->flush();
|
||||
}
|
||||
};
|
||||
|
||||
static void amdDebugFunc(GLuint id,
|
||||
GLenum category,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
GLvoid* userParam)
|
||||
{
|
||||
DebugLoggerPrivate *p = static_cast<DebugLoggerPrivate*>(userParam);
|
||||
|
||||
(void) id;
|
||||
(void) category;
|
||||
(void) severity;
|
||||
(void) length;
|
||||
|
||||
p->writeTimestamp();
|
||||
p->writeLine(message);
|
||||
}
|
||||
|
||||
static void arbDebugFunc(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar* message,
|
||||
GLvoid* userParam)
|
||||
{
|
||||
DebugLoggerPrivate *p = static_cast<DebugLoggerPrivate*>(userParam);
|
||||
|
||||
(void) source;
|
||||
(void) type;
|
||||
(void) id;
|
||||
(void) severity;
|
||||
(void) length;
|
||||
|
||||
p->writeTimestamp();
|
||||
p->writeLine(message);
|
||||
}
|
||||
|
||||
DebugLogger::DebugLogger(const char *filename)
|
||||
{
|
||||
p = new DebugLoggerPrivate(filename);
|
||||
|
||||
if (GLEW_KHR_debug)
|
||||
glDebugMessageCallback(arbDebugFunc, p);
|
||||
else if (GLEW_ARB_debug_output)
|
||||
glDebugMessageCallbackARB(arbDebugFunc, p);
|
||||
else if (GLEW_AMD_debug_output)
|
||||
glDebugMessageCallbackAMD(amdDebugFunc, p);
|
||||
else
|
||||
qDebug() << "DebugLogger: no debug extensions found";
|
||||
}
|
||||
|
||||
DebugLogger::~DebugLogger()
|
||||
{
|
||||
delete p;
|
||||
}
|
37
src/debuglogger.h
Normal file
37
src/debuglogger.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
** debuglogger.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DEBUGLOGGER_H
|
||||
#define DEBUGLOGGER_H
|
||||
|
||||
struct DebugLoggerPrivate;
|
||||
|
||||
class DebugLogger
|
||||
{
|
||||
public:
|
||||
DebugLogger(const char *filename = 0);
|
||||
~DebugLogger();
|
||||
|
||||
private:
|
||||
DebugLoggerPrivate *p;
|
||||
};
|
||||
|
||||
#endif // DEBUGLOGGER_H
|
63
src/disposable.h
Normal file
63
src/disposable.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
** disposable.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DISPOSABLE_H
|
||||
#define DISPOSABLE_H
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
#include "sigc++/signal.h"
|
||||
|
||||
class Disposable
|
||||
{
|
||||
public:
|
||||
Disposable()
|
||||
: disposed(false)
|
||||
{}
|
||||
|
||||
virtual ~Disposable() {}
|
||||
|
||||
void dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
releaseResources();
|
||||
disposed = true;
|
||||
wasDisposed();
|
||||
}
|
||||
|
||||
bool isDisposed() const { return disposed; }
|
||||
|
||||
sigc::signal<void> wasDisposed;
|
||||
|
||||
protected:
|
||||
virtual void releaseResources() = 0;
|
||||
|
||||
private:
|
||||
bool disposed;
|
||||
};
|
||||
|
||||
/* Every cpp needs to define DISP_CLASS_NAME for itself (lowercase) */
|
||||
#define GUARD_DISPOSED \
|
||||
{ if (isDisposed()) throw Exception(Exception::RGSSError, "disposed %S", DISP_CLASS_NAME); }
|
||||
|
||||
#endif // DISPOSABLE_H
|
251
src/etc-internal.h
Normal file
251
src/etc-internal.h
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
** etc-internal.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ETC_TYPES_H
|
||||
#define ETC_TYPES_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
struct Vec2
|
||||
{
|
||||
float x, y;
|
||||
|
||||
Vec2()
|
||||
: x(0), y(0)
|
||||
{}
|
||||
|
||||
Vec2(float x, float y)
|
||||
: x(x), y(y)
|
||||
{}
|
||||
|
||||
bool operator==(const Vec2 &other) const
|
||||
{
|
||||
return (x == other.x && y == other.y);
|
||||
}
|
||||
};
|
||||
|
||||
struct Vec4
|
||||
{
|
||||
float x, y, z, w;
|
||||
|
||||
Vec4()
|
||||
: x(0), y(0), z(0), w(0)
|
||||
{}
|
||||
|
||||
Vec4(float x, float y, float z, float w)
|
||||
: x(x), y(y), z(z), w(w)
|
||||
{}
|
||||
|
||||
bool operator==(const Vec4 &other) const
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z && w == other.w);
|
||||
}
|
||||
};
|
||||
|
||||
struct Vec2i
|
||||
{
|
||||
int x, y;
|
||||
|
||||
Vec2i()
|
||||
: x(0), y(0)
|
||||
{}
|
||||
|
||||
Vec2i(int x, int y)
|
||||
: x(x), y(y)
|
||||
{}
|
||||
|
||||
bool operator==(const Vec2i &other)
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
/* Simple Vertex */
|
||||
struct SVertex
|
||||
{
|
||||
Vec2 pos;
|
||||
Vec2 texPos;
|
||||
|
||||
static const void *posOffset()
|
||||
{
|
||||
return (const void*) 0;
|
||||
}
|
||||
|
||||
static const void *texPosOffset()
|
||||
{
|
||||
return (const void*) sizeof(Vec2);
|
||||
}
|
||||
};
|
||||
|
||||
/* Color Vertex */
|
||||
struct CVertex
|
||||
{
|
||||
Vec2 pos;
|
||||
Vec4 color;
|
||||
|
||||
CVertex()
|
||||
: color(1, 1, 1, 1)
|
||||
{}
|
||||
|
||||
static const void *posOffset()
|
||||
{
|
||||
return (const void*) 0;
|
||||
}
|
||||
|
||||
static const void *colorOffset()
|
||||
{
|
||||
return (const void*) sizeof(pos);
|
||||
}
|
||||
};
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Vec2 pos;
|
||||
Vec2 texPos;
|
||||
Vec4 color;
|
||||
|
||||
Vertex()
|
||||
: color(1, 1, 1, 1)
|
||||
{}
|
||||
|
||||
static const void *posOffset()
|
||||
{
|
||||
return (const void*) 0;
|
||||
}
|
||||
|
||||
static const void *texPosOffset()
|
||||
{
|
||||
return (const void*) sizeof(Vec2);
|
||||
}
|
||||
|
||||
static const void *colorOffset()
|
||||
{
|
||||
return (const void*) sizeof(SVertex);
|
||||
}
|
||||
};
|
||||
|
||||
struct IntRect
|
||||
{
|
||||
int x, y, w, h;
|
||||
|
||||
IntRect()
|
||||
: x(0), y(0), w(0), h(0)
|
||||
{}
|
||||
|
||||
IntRect(int x, int y, int w, int h)
|
||||
: x(x), y(y), w(w), h(h)
|
||||
{}
|
||||
|
||||
bool operator==(const IntRect &other) const
|
||||
{
|
||||
return (x == other.x && y == other.y &&
|
||||
w == other.w && h == other.h);
|
||||
}
|
||||
};
|
||||
|
||||
struct StaticRect { float x, y, w, h; };
|
||||
|
||||
struct FloatRect
|
||||
{
|
||||
float x, y, w, h;
|
||||
|
||||
FloatRect()
|
||||
: x(0), y(0), w(0), h(0)
|
||||
{}
|
||||
|
||||
FloatRect(float x, float y, float w, float h)
|
||||
: x(x), y(y), w(w), h(h)
|
||||
{}
|
||||
|
||||
FloatRect(const StaticRect &d)
|
||||
: x(d.x), y(d.y), w(d.w), h(d.h)
|
||||
{}
|
||||
|
||||
FloatRect(const IntRect &r)
|
||||
: x(r.x), y(r.y), w(r.w), h(r.h)
|
||||
{}
|
||||
|
||||
Vec2 topLeft() const { return Vec2(x, y); }
|
||||
Vec2 bottomLeft() const { return Vec2(x, y+h); }
|
||||
Vec2 topRight() const { return Vec2(x+w, y); }
|
||||
Vec2 bottomRight() const { return Vec2(x+w, y+h); }
|
||||
|
||||
void shrinkHalf()
|
||||
{
|
||||
x += 0.5;
|
||||
y += 0.5;
|
||||
w -= 1.0;
|
||||
h -= 1.0;
|
||||
}
|
||||
|
||||
FloatRect vFlipped() const
|
||||
{
|
||||
return FloatRect(x, y+h, w, -h);
|
||||
}
|
||||
|
||||
FloatRect hFlipped() const
|
||||
{
|
||||
return FloatRect(x+w, y, -w, h);
|
||||
}
|
||||
|
||||
Vec2 corner(int i) const
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0 : return topLeft();
|
||||
case 1 : return topRight();
|
||||
case 2 : return bottomRight();
|
||||
case 3 : return bottomLeft();
|
||||
default : return Vec2();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Value between 0 and 255 with internal
|
||||
* normalized representation */
|
||||
struct NormValue
|
||||
{
|
||||
int unNorm;
|
||||
float norm;
|
||||
|
||||
NormValue()
|
||||
: unNorm(0),
|
||||
norm(0)
|
||||
{}
|
||||
|
||||
NormValue(int unNorm)
|
||||
: unNorm(unNorm),
|
||||
norm(unNorm / 255.0)
|
||||
{}
|
||||
|
||||
void operator =(int value)
|
||||
{
|
||||
unNorm = bound(value, 0, 255);
|
||||
norm = unNorm / 255.0;
|
||||
}
|
||||
|
||||
operator int() const
|
||||
{
|
||||
return unNorm;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ETC_TYPES_H
|
316
src/etc.cpp
Normal file
316
src/etc.cpp
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
** etc.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "etc.h"
|
||||
|
||||
#include "serial-util.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "SDL2/SDL_types.h"
|
||||
#include "SDL2/SDL_pixels.h"
|
||||
|
||||
Color::Color(double red, double green, double blue, double alpha)
|
||||
: red(red), green(green), blue(blue), alpha(alpha)
|
||||
{
|
||||
updateInternal();
|
||||
}
|
||||
|
||||
Color::Color(const Vec4 &norm)
|
||||
: norm(norm)
|
||||
{
|
||||
updateExternal();
|
||||
}
|
||||
|
||||
Color::Color(const Color &o)
|
||||
: red(o.red), green(o.green), blue(o.blue), alpha(o.alpha),
|
||||
norm(o.norm)
|
||||
{}
|
||||
|
||||
bool Color::operator==(const Color &o) const
|
||||
{
|
||||
return red == o.red &&
|
||||
green == o.green &&
|
||||
blue == o.blue &&
|
||||
alpha == o.alpha;
|
||||
}
|
||||
|
||||
void Color::updateInternal()
|
||||
{
|
||||
norm.x = red / 255;
|
||||
norm.y = green / 255;
|
||||
norm.z = blue / 255;
|
||||
norm.w = alpha / 255;
|
||||
}
|
||||
|
||||
void Color::updateExternal()
|
||||
{
|
||||
red = norm.x * 255;
|
||||
green = norm.y * 255;
|
||||
blue = norm.z * 255;
|
||||
alpha = norm.w * 255;
|
||||
}
|
||||
|
||||
void Color::set(double red, double green, double blue, double alpha)
|
||||
{
|
||||
this->red = red;
|
||||
this->green = green;
|
||||
this->blue = blue;
|
||||
this->alpha = alpha;
|
||||
|
||||
updateInternal();
|
||||
}
|
||||
|
||||
void Color::setRed(double value)
|
||||
{
|
||||
red = value;
|
||||
norm.x = bound<double>(value, 0, 255) / 255;
|
||||
}
|
||||
|
||||
void Color::setGreen(double value)
|
||||
{
|
||||
green = value;
|
||||
norm.y = bound<double>(value, 0, 255) / 255;
|
||||
}
|
||||
|
||||
void Color::setBlue(double value)
|
||||
{
|
||||
blue = value;
|
||||
norm.z = bound<double>(value, 0, 255) / 255;
|
||||
}
|
||||
|
||||
void Color::setAlpha(double value)
|
||||
{
|
||||
alpha = value;
|
||||
norm.w = bound<double>(value, 0, 255) / 255;
|
||||
}
|
||||
|
||||
void Color::toSDLColor(SDL_Color &c) const
|
||||
{
|
||||
c.r = bound<double>(red, 0, 255);
|
||||
c.g = bound<double>(green, 0, 255);
|
||||
c.b = bound<double>(blue, 0, 255);
|
||||
// c.a = bound<double>(alpha, 0, 255);
|
||||
c.a = 255;
|
||||
}
|
||||
|
||||
/* Serializable */
|
||||
int Color::serialSize() const
|
||||
{
|
||||
return 4 * 8;
|
||||
}
|
||||
|
||||
void Color::serialize(char *buffer) const
|
||||
{
|
||||
char *buf = buffer;
|
||||
|
||||
write_double(&buf, red);
|
||||
write_double(&buf, green);
|
||||
write_double(&buf, blue);
|
||||
write_double(&buf, alpha);
|
||||
}
|
||||
|
||||
Color *Color::deserialize(const char *data, int len)
|
||||
{
|
||||
if (len != 32)
|
||||
throw Exception(Exception::ArgumentError, "Color: Serialized data invalid");
|
||||
|
||||
Color *c = new Color();
|
||||
uint i = 0;
|
||||
|
||||
c->red = read_double(data, i);
|
||||
c->green = read_double(data, i);
|
||||
c->blue = read_double(data, i);
|
||||
c->alpha = read_double(data, i);
|
||||
c->updateInternal();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
Tone::Tone(double red, double green, double blue, double gray)
|
||||
: red(red), green(green), blue(blue), gray(gray)
|
||||
{
|
||||
updateInternal();
|
||||
}
|
||||
|
||||
Tone::Tone(const Tone &o)
|
||||
: red(o.red), green(o.green), blue(o.blue), gray(o.gray),
|
||||
norm(o.norm)
|
||||
{}
|
||||
|
||||
bool Tone::operator==(const Tone &o) const
|
||||
{
|
||||
return red == o.red &&
|
||||
green == o.green &&
|
||||
blue == o.blue &&
|
||||
gray == o.gray;
|
||||
}
|
||||
|
||||
void Tone::updateInternal()
|
||||
{
|
||||
norm.x = (float) bound<double>(red, -255, 255) / 255;
|
||||
norm.y = (float) bound<double>(green, -255, 255) / 255;
|
||||
norm.z = (float) bound<double>(blue, -255, 255) / 255;
|
||||
norm.w = (float) bound<double>(gray, 0, 255) / 255;
|
||||
}
|
||||
|
||||
void Tone::set(double red, double green, double blue, double gray)
|
||||
{
|
||||
this->red = red;
|
||||
this->green = green;
|
||||
this->blue = blue;
|
||||
this->gray = gray;
|
||||
|
||||
updateInternal();
|
||||
}
|
||||
|
||||
void Tone::setRed(double value)
|
||||
{
|
||||
red = value;
|
||||
norm.x = (float) bound<double>(value, -255, 255) / 255;
|
||||
}
|
||||
|
||||
void Tone::setGreen(double value)
|
||||
{
|
||||
green = value;
|
||||
norm.y = (float) bound<double>(value, -255, 255) / 255;
|
||||
}
|
||||
|
||||
void Tone::setBlue(double value)
|
||||
{
|
||||
blue = value;
|
||||
norm.z = (float) bound<double>(value, -255, 255) / 255;
|
||||
}
|
||||
|
||||
void Tone::setGray(double value)
|
||||
{
|
||||
gray = value;
|
||||
norm.w = (float) bound<double>(value, 0, 255) / 255;
|
||||
}
|
||||
|
||||
/* Serializable */
|
||||
int Tone::serialSize() const
|
||||
{
|
||||
return 4 * 8;
|
||||
}
|
||||
|
||||
void Tone::serialize(char *buffer) const
|
||||
{
|
||||
char *buf = buffer;
|
||||
|
||||
write_double(&buf, red);
|
||||
write_double(&buf, green);
|
||||
write_double(&buf, blue);
|
||||
write_double(&buf, gray);
|
||||
}
|
||||
|
||||
Tone *Tone::deserialize(const char *data, int len)
|
||||
{
|
||||
if (len != 32)
|
||||
throw Exception(Exception::ArgumentError, "Tone: Serialized data invalid");
|
||||
|
||||
Tone *t = new Tone();
|
||||
uint i = 0;
|
||||
|
||||
t->red = read_double(data, i);
|
||||
t->green = read_double(data, i);
|
||||
t->blue = read_double(data, i);
|
||||
t->gray = read_double(data, i);
|
||||
t->updateInternal();
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
Rect::Rect(int x, int y, int width, int height)
|
||||
: x(x), y(y), width(width), height(height)
|
||||
{}
|
||||
|
||||
Rect::Rect(const Rect &o)
|
||||
: x(o.x), y(o.y),
|
||||
width(o.width), height(o.height)
|
||||
{}
|
||||
|
||||
Rect::Rect(const IntRect &r)
|
||||
: x(r.x), y(r.y), width(r.w), height(r.h)
|
||||
{}
|
||||
|
||||
bool Rect::operator==(const Rect &o) const
|
||||
{
|
||||
return x == o.x &&
|
||||
y == o.y &&
|
||||
width == o.width &&
|
||||
height == o.height;
|
||||
}
|
||||
|
||||
void Rect::operator=(const IntRect &rect)
|
||||
{
|
||||
x = rect.x;
|
||||
y = rect.y;
|
||||
width = rect.w;
|
||||
height = rect.h;
|
||||
}
|
||||
|
||||
void Rect::set(int x, int y, int w, int h)
|
||||
{
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
width = w;
|
||||
height = h;
|
||||
valueChanged();
|
||||
}
|
||||
|
||||
void Rect::empty()
|
||||
{
|
||||
x = y = width = height = 0;
|
||||
valueChanged();
|
||||
}
|
||||
|
||||
int Rect::serialSize() const
|
||||
{
|
||||
return 4 * 4;
|
||||
}
|
||||
|
||||
void Rect::serialize(char *buffer) const
|
||||
{
|
||||
char *buf = buffer;
|
||||
|
||||
write_int32(&buf, x);
|
||||
write_int32(&buf, y);
|
||||
write_int32(&buf, width);
|
||||
write_int32(&buf, height);
|
||||
}
|
||||
|
||||
Rect *Rect::deserialize(const char *data, int len)
|
||||
{
|
||||
if (len != 16)
|
||||
throw Exception(Exception::ArgumentError, "Rect: Serialized data invalid");
|
||||
|
||||
Rect *r = new Rect();
|
||||
uint i = 0;
|
||||
|
||||
r->x = read_int32(data, i);
|
||||
r->y = read_int32(data, i);
|
||||
r->width = read_int32(data, i);
|
||||
r->height = read_int32(data, i);
|
||||
|
||||
return r;
|
||||
}
|
207
src/etc.h
Normal file
207
src/etc.h
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
** etc.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ETC_H
|
||||
#define ETC_H
|
||||
|
||||
#include "sigc++/signal.h"
|
||||
|
||||
#include "serializable.h"
|
||||
#include "etc-internal.h"
|
||||
|
||||
struct SDL_Color;
|
||||
|
||||
enum BlendType
|
||||
{
|
||||
BlendNone = -1, /* Only internal use */
|
||||
|
||||
BlendNormal = 0,
|
||||
BlendAddition = 1,
|
||||
BlendSubstraction = 2
|
||||
};
|
||||
|
||||
struct Color : public Serializable
|
||||
{
|
||||
Color()
|
||||
: red(0), green(0), blue(0), alpha(0)
|
||||
{}
|
||||
|
||||
Color(double red, double green, double blue, double alpha = 255);
|
||||
Color(const Vec4 &norm);
|
||||
Color(const Color &o);
|
||||
|
||||
bool operator==(const Color &o) const;
|
||||
|
||||
void updateInternal();
|
||||
void updateExternal();
|
||||
|
||||
void set(double red, double green, double blue, double alpha);
|
||||
void setRed(double value);
|
||||
void setGreen(double value);
|
||||
void setBlue(double value);
|
||||
void setAlpha(double value);
|
||||
|
||||
double getRed() const { return red; }
|
||||
double getGreen() const { return green; }
|
||||
double getBlue() const { return blue; }
|
||||
double getAlpha() const { return alpha; }
|
||||
|
||||
bool hasEffect()
|
||||
{
|
||||
return (alpha != 0);
|
||||
}
|
||||
|
||||
void toSDLColor(SDL_Color &c) const;
|
||||
|
||||
/* Serializable */
|
||||
int serialSize() const;
|
||||
void serialize(char *buffer) const;
|
||||
static Color *deserialize(const char *data, int len);
|
||||
|
||||
/* Range (0.0 ~ 255.0) */
|
||||
double red;
|
||||
double green;
|
||||
double blue;
|
||||
double alpha;
|
||||
|
||||
/* Normalized (0.0 ~ 1.0) */
|
||||
Vec4 norm;
|
||||
};
|
||||
|
||||
struct Tone : public Serializable
|
||||
{
|
||||
Tone()
|
||||
: red(0), green(0), blue(0), gray(0)
|
||||
{}
|
||||
|
||||
Tone(double red, double green, double blue, double gray = 0);
|
||||
Tone(const Tone &o);
|
||||
|
||||
bool operator==(const Tone &o) const;
|
||||
|
||||
void updateInternal();
|
||||
|
||||
void set(double red, double green, double blue, double gray);
|
||||
void setRed(double value);
|
||||
void setGreen(double value);
|
||||
void setBlue(double value);
|
||||
void setGray(double value);
|
||||
|
||||
double getRed() const { return red; }
|
||||
double getGreen() const { return green; }
|
||||
double getBlue() const { return blue; }
|
||||
double getGray() const { return gray; }
|
||||
|
||||
bool hasEffect()
|
||||
{
|
||||
return ((int)red != 0 ||
|
||||
(int)green != 0 ||
|
||||
(int)blue != 0 ||
|
||||
(int)gray != 0);
|
||||
}
|
||||
|
||||
/* Serializable */
|
||||
int serialSize() const;
|
||||
void serialize(char *buffer) const;
|
||||
static Tone *deserialize(const char *data, int len);
|
||||
|
||||
/* Range (-255.0 ~ 255.0) */
|
||||
double red;
|
||||
double green;
|
||||
double blue;
|
||||
/* Range (0.0 ~ 255.0) */
|
||||
double gray;
|
||||
|
||||
/* Normalized (-1.0 ~ 1.0) */
|
||||
Vec4 norm;
|
||||
};
|
||||
|
||||
struct Rect : public Serializable
|
||||
{
|
||||
Rect()
|
||||
: x(0), y(0), width(0), height(0)
|
||||
{}
|
||||
|
||||
Rect(int x, int y, int width, int height);
|
||||
Rect(const Rect &o);
|
||||
Rect(const IntRect &r);
|
||||
|
||||
bool operator==(const Rect &o) const;
|
||||
void operator=(const IntRect &rect);
|
||||
void set(int x, int y, int w, int h);
|
||||
|
||||
FloatRect toFloatRect() const
|
||||
{
|
||||
return FloatRect(x, y, width, height);
|
||||
}
|
||||
|
||||
IntRect toIntRect()
|
||||
{
|
||||
return IntRect(x, y, width, height);
|
||||
}
|
||||
|
||||
void empty();
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return !(x || y || width || height);
|
||||
}
|
||||
|
||||
DECL_ATTR_INLINE(X, int, x)
|
||||
DECL_ATTR_INLINE(Y, int, y)
|
||||
DECL_ATTR_INLINE(Width, int, width)
|
||||
DECL_ATTR_INLINE(Height, int, height)
|
||||
|
||||
int serialSize() const;
|
||||
void serialize(char *buffer) const;
|
||||
static Rect *deserialize(const char *data, int len);
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
sigc::signal<void> valueChanged;
|
||||
};
|
||||
|
||||
/* For internal use.
|
||||
* All drawable classes have properties of one or more of the above
|
||||
* types, which in an interpreted environment act as independent
|
||||
* objects, and rely on the GC to clean them up. When a drawable
|
||||
* class is constructed, these properties must have default objects
|
||||
* that are constructed with the class. C++ however has no GC, so
|
||||
* there is no way to clean them up when used directly with it.
|
||||
* Therefore the default objects are first created embedded in the
|
||||
* drawable class (so they get destroyed automatically from within
|
||||
* C++ if no pointers were changed), and the binding then takes
|
||||
* care of properly allocating new, independent objects and replacing
|
||||
* the defaults. Thus both C++ and the interpreted API can be used
|
||||
* without memory leakage.
|
||||
* This can be removed at a later point when no testing directly
|
||||
* from C++ is needed anymore. */
|
||||
struct EtcTemps
|
||||
{
|
||||
Color color;
|
||||
Tone tone;
|
||||
Rect rect;
|
||||
};
|
||||
|
||||
#endif // ETC_H
|
259
src/eventthread.cpp
Normal file
259
src/eventthread.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
** eventthread.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "eventthread.h"
|
||||
|
||||
#include "SDL2/SDL_events.h"
|
||||
#include "SDL2/SDL_joystick.h"
|
||||
#include "SDL2/SDL_messagebox.h"
|
||||
#include "SDL2/SDL_timer.h"
|
||||
#include "SDL2/SDL_thread.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
bool EventThread::keyStates[] = { false };
|
||||
|
||||
EventThread::JoyState EventThread::joyState =
|
||||
{
|
||||
0, 0, { false }
|
||||
};
|
||||
|
||||
EventThread::MouseState EventThread::mouseState =
|
||||
{
|
||||
0, 0, { false }
|
||||
};
|
||||
|
||||
const Uint32 REQUEST_TERMINATION = SDL_USEREVENT+0;
|
||||
const Uint32 REQUEST_SETFULLSCREEN = SDL_USEREVENT+1;
|
||||
const Uint32 REQUEST_WINRESIZE = SDL_USEREVENT+2;
|
||||
const Uint32 SHOW_MESSAGEBOX = SDL_USEREVENT+3;
|
||||
|
||||
|
||||
EventThread::EventThread()
|
||||
: fullscreen(false)
|
||||
{}
|
||||
|
||||
void EventThread::process(RGSSThreadData &rtData)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_Window *win = rtData.window;
|
||||
WindowSizeNotify &windowSizeMsg = rtData.windowSizeMsg;
|
||||
|
||||
fullscreen = rtData.config.fullscreen;
|
||||
|
||||
bool terminate = false;
|
||||
|
||||
SDL_Joystick *js = 0;
|
||||
if (SDL_NumJoysticks() > 0)
|
||||
js = SDL_JoystickOpen(0);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!SDL_WaitEvent(&event))
|
||||
{
|
||||
qDebug() << "EventThread: Event error";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT :
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_RESIZED :
|
||||
windowSizeMsg.notifyChange(event.window.data1,
|
||||
event.window.data2);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_CLOSE :
|
||||
terminate = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST :
|
||||
resetInput();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_QUIT :
|
||||
case REQUEST_TERMINATION :
|
||||
terminate = true;
|
||||
qDebug() << "EventThread termination requested";
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN :
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_RETURN &&
|
||||
(event.key.keysym.mod & KMOD_LALT))
|
||||
{
|
||||
setFullscreen(win, !fullscreen);
|
||||
break;
|
||||
}
|
||||
|
||||
keyStates[event.key.keysym.scancode] = true;
|
||||
break;
|
||||
|
||||
case REQUEST_SETFULLSCREEN :
|
||||
setFullscreen(win, static_cast<bool>(event.user.code));
|
||||
break;
|
||||
|
||||
case REQUEST_WINRESIZE :
|
||||
SDL_SetWindowSize(win, event.window.data1, event.window.data2);
|
||||
break;
|
||||
|
||||
case SHOW_MESSAGEBOX :
|
||||
SDL_ShowSimpleMessageBox(event.user.code,
|
||||
rtData.config.game.title.constData(),
|
||||
(const char*) event.user.data1, win);
|
||||
free(event.user.data1);
|
||||
msgBoxDone = true;
|
||||
break;
|
||||
|
||||
case SDL_KEYUP :
|
||||
keyStates[event.key.keysym.scancode] = false;
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONDOWN :
|
||||
joyState.buttons[event.jbutton.button] = true;
|
||||
break;
|
||||
|
||||
case SDL_JOYBUTTONUP :
|
||||
joyState.buttons[event.jbutton.button] = false;
|
||||
break;
|
||||
|
||||
case SDL_JOYAXISMOTION :
|
||||
if (event.jaxis.axis == 0)
|
||||
joyState.xAxis = event.jaxis.value;
|
||||
else
|
||||
joyState.yAxis = event.jaxis.value;
|
||||
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED :
|
||||
if (event.jdevice.which > 0)
|
||||
break;
|
||||
|
||||
js = SDL_JoystickOpen(0);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEREMOVED :
|
||||
resetInput();
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN :
|
||||
mouseState.buttons[event.button.button] = true;
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP :
|
||||
mouseState.buttons[event.button.button] = false;
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION :
|
||||
mouseState.x = event.motion.x;
|
||||
mouseState.y = event.motion.y;
|
||||
break;
|
||||
}
|
||||
|
||||
if (terminate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (SDL_JoystickGetAttached(js))
|
||||
SDL_JoystickClose(js);
|
||||
}
|
||||
|
||||
void EventThread::cleanup()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SHOW_MESSAGEBOX)
|
||||
{
|
||||
free(event.user.data1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventThread::resetInput()
|
||||
{
|
||||
memset(&keyStates, 0, sizeof(keyStates));
|
||||
memset(&joyState, 0, sizeof(joyState));
|
||||
memset(&mouseState.buttons, 0, sizeof(mouseState.buttons));
|
||||
}
|
||||
|
||||
void EventThread::setFullscreen(SDL_Window *win, bool mode)
|
||||
{
|
||||
SDL_SetWindowFullscreen
|
||||
(win, mode ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
fullscreen = mode;
|
||||
}
|
||||
|
||||
void EventThread::requestTerminate()
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = REQUEST_TERMINATION;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void EventThread::requestFullscreenMode(bool mode)
|
||||
{
|
||||
if (mode == fullscreen)
|
||||
return;
|
||||
|
||||
SDL_Event event;
|
||||
event.type = REQUEST_SETFULLSCREEN;
|
||||
event.user.code = static_cast<Sint32>(mode);
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void EventThread::requestWindowResize(int width, int height)
|
||||
{
|
||||
SDL_Event event;
|
||||
event.type = REQUEST_WINRESIZE;
|
||||
event.window.data1 = width;
|
||||
event.window.data2 = height;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void EventThread::showMessageBox(const char *body, int flags)
|
||||
{
|
||||
msgBoxDone = false;
|
||||
|
||||
SDL_Event event;
|
||||
event.user.code = flags;
|
||||
event.user.data1 = strdup(body);
|
||||
event.type = SHOW_MESSAGEBOX;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
/* Keep repainting screen while box is open */
|
||||
gState->graphics().repaintWait(&msgBoxDone);
|
||||
/* Prevent endless loops */
|
||||
resetInput();
|
||||
}
|
||||
|
||||
bool EventThread::getFullscreen()
|
||||
{
|
||||
return fullscreen;
|
||||
}
|
169
src/eventthread.h
Normal file
169
src/eventthread.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
** eventthread.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EVENTTHREAD_H
|
||||
#define EVENTTHREAD_H
|
||||
|
||||
#include "config.h"
|
||||
#include "etc-internal.h"
|
||||
|
||||
#include "SDL2/SDL_scancode.h"
|
||||
#include "SDL2/SDL_joystick.h"
|
||||
#include "SDL2/SDL_mouse.h"
|
||||
|
||||
#include "SDL2/SDL_mutex.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QVector>
|
||||
|
||||
struct RGSSThreadData;
|
||||
struct SDL_Thread;
|
||||
struct SDL_Window;
|
||||
|
||||
class EventThread
|
||||
{
|
||||
public:
|
||||
static bool keyStates[SDL_NUM_SCANCODES];
|
||||
|
||||
struct JoyState
|
||||
{
|
||||
int xAxis;
|
||||
int yAxis;
|
||||
|
||||
bool buttons[16];
|
||||
};
|
||||
|
||||
static JoyState joyState;
|
||||
|
||||
struct MouseState
|
||||
{
|
||||
int x, y;
|
||||
bool buttons[SDL_BUTTON_X2+1];
|
||||
};
|
||||
|
||||
static MouseState mouseState;
|
||||
|
||||
EventThread();
|
||||
|
||||
void process(RGSSThreadData &rtData);
|
||||
void cleanup();
|
||||
|
||||
/* Called from render thread */
|
||||
void requestFullscreenMode(bool mode);
|
||||
void requestWindowResize(int width, int height);
|
||||
|
||||
void requestTerminate();
|
||||
|
||||
bool getFullscreen();
|
||||
|
||||
void showMessageBox(const char *body, int flags = 0);
|
||||
|
||||
private:
|
||||
void resetInput();
|
||||
void setFullscreen(SDL_Window *, bool mode);
|
||||
bool fullscreen;
|
||||
volatile bool msgBoxDone;
|
||||
};
|
||||
|
||||
|
||||
struct WindowSizeNotify
|
||||
{
|
||||
SDL_mutex *mutex;
|
||||
|
||||
volatile bool changedFlag;
|
||||
volatile int w, h;
|
||||
|
||||
WindowSizeNotify()
|
||||
{
|
||||
mutex = SDL_CreateMutex();
|
||||
changedFlag = false;
|
||||
w = h = 0;
|
||||
}
|
||||
|
||||
~WindowSizeNotify()
|
||||
{
|
||||
SDL_DestroyMutex(mutex);
|
||||
}
|
||||
|
||||
/* Done from the sending side */
|
||||
void notifyChange(int w, int h)
|
||||
{
|
||||
SDL_LockMutex(mutex);
|
||||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
changedFlag = true;
|
||||
|
||||
SDL_UnlockMutex(mutex);
|
||||
}
|
||||
|
||||
/* Done from the receiving side */
|
||||
bool pollChange(int *w, int *h)
|
||||
{
|
||||
if (!changedFlag)
|
||||
return false;
|
||||
|
||||
SDL_LockMutex(mutex);
|
||||
|
||||
*w = this->w;
|
||||
*h = this->h;
|
||||
changedFlag = false;
|
||||
|
||||
SDL_UnlockMutex(mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct RGSSThreadData
|
||||
{
|
||||
/* Main thread sets this to request render thread to terminate */
|
||||
volatile bool rqTerm;
|
||||
/* In response, render thread sets this to confirm
|
||||
* that it received the request and isn't stuck */
|
||||
volatile bool rqTermAck;
|
||||
|
||||
EventThread *ethread;
|
||||
WindowSizeNotify windowSizeMsg;
|
||||
|
||||
const char *argv0;
|
||||
|
||||
SDL_Window *window;
|
||||
|
||||
Vec2 sizeResoRatio;
|
||||
|
||||
Config config;
|
||||
|
||||
QByteArray rgssErrorMsg;
|
||||
|
||||
RGSSThreadData(EventThread *ethread,
|
||||
const char *argv0,
|
||||
SDL_Window *window)
|
||||
: rqTerm(false),
|
||||
rqTermAck(false),
|
||||
ethread(ethread),
|
||||
argv0(argv0),
|
||||
window(window),
|
||||
sizeResoRatio(1, 1)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif // EVENTTHREAD_H
|
63
src/exception.h
Normal file
63
src/exception.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
** exception.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EXCEPTION_H
|
||||
#define EXCEPTION_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <stdio.h>
|
||||
|
||||
struct Exception
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
RGSSError,
|
||||
NoFileError,
|
||||
IOError,
|
||||
|
||||
/* Already defined by ruby */
|
||||
TypeError,
|
||||
ArgumentError,
|
||||
|
||||
/* New types introduced in mkxp */
|
||||
PHYSFSError,
|
||||
SDLError,
|
||||
MKXPError
|
||||
};
|
||||
|
||||
Type type;
|
||||
QByteArray fmt;
|
||||
QByteArray arg1;
|
||||
QByteArray arg2;
|
||||
|
||||
Exception(Type type, QByteArray fmt,
|
||||
QByteArray arg1 = QByteArray(),
|
||||
QByteArray arg2 = QByteArray())
|
||||
: type(type), fmt(fmt), arg1(arg1), arg2(arg2)
|
||||
{}
|
||||
|
||||
void snprintf(char *buffer, size_t bufSize) const
|
||||
{
|
||||
::snprintf(buffer, bufSize, fmt.constData(), arg1.constData(), arg2.constData());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EXCEPTION_H
|
735
src/filesystem.cpp
Normal file
735
src/filesystem.cpp
Normal file
|
@ -0,0 +1,735 @@
|
|||
/*
|
||||
** filesystem.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "filesystem.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "physfs.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
struct RGSS_entryData
|
||||
{
|
||||
qint64 offset;
|
||||
quint64 size;
|
||||
quint32 startMagic;
|
||||
};
|
||||
|
||||
struct RGSS_entryHandle
|
||||
{
|
||||
RGSS_entryData data;
|
||||
quint32 currentMagic;
|
||||
quint64 currentOffset;
|
||||
PHYSFS_Io *io;
|
||||
|
||||
RGSS_entryHandle(const RGSS_entryData &data)
|
||||
: data(data),
|
||||
currentMagic(data.startMagic),
|
||||
currentOffset(0)
|
||||
{}
|
||||
|
||||
RGSS_entryHandle(const RGSS_entryHandle &other)
|
||||
: data(other.data),
|
||||
currentMagic(other.currentMagic),
|
||||
currentOffset(other.currentOffset)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef QList<QByteArray> QByteList;
|
||||
|
||||
struct RGSS_archiveData
|
||||
{
|
||||
PHYSFS_Io *archiveIo;
|
||||
QHash<QByteArray, RGSS_entryData> entryHash;
|
||||
QHash<QByteArray, bool> dirHash;
|
||||
};
|
||||
|
||||
static bool
|
||||
readUint32(PHYSFS_Io *io, quint32 &result)
|
||||
{
|
||||
char buff[4];
|
||||
PHYSFS_sint64 count = io->read(io, buff, 4);
|
||||
|
||||
result = ((buff[0] << 0x00) & 0x000000FF) |
|
||||
((buff[1] << 0x08) & 0x0000FF00) |
|
||||
((buff[2] << 0x10) & 0x00FF0000) |
|
||||
((buff[3] << 0x18) & 0xFF000000) ;
|
||||
|
||||
return (count == 4);
|
||||
}
|
||||
|
||||
#define RGSS_HEADER_1 0x53534752
|
||||
#define RGSS_HEADER_2 0x1004441
|
||||
|
||||
#define RGSS_MAGIC 0xDEADCAFE
|
||||
|
||||
#define PHYSFS_ALLOC(type) \
|
||||
static_cast<type*>(PHYSFS_getAllocator()->Malloc(sizeof(type)))
|
||||
|
||||
static inline quint32
|
||||
advanceMagic(quint32 &magic)
|
||||
{
|
||||
quint32 old = magic;
|
||||
|
||||
magic = magic * 7 + 3;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
struct MagicState
|
||||
{
|
||||
quint32 magic;
|
||||
quint64 offset;
|
||||
|
||||
MagicState(quint64 offset = 0)
|
||||
: offset(offset)
|
||||
{
|
||||
magic = RGSS_MAGIC;
|
||||
|
||||
for (uint i = 0; i < (offset/4); ++i)
|
||||
advanceBlock();
|
||||
}
|
||||
|
||||
quint8 advancePath()
|
||||
{
|
||||
quint8 ret = magic & 0xFF;
|
||||
|
||||
offset++;
|
||||
advanceBlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
quint8 advanceData()
|
||||
{
|
||||
quint8 ret = magic & 0xFF;
|
||||
|
||||
if (offset++ % 4 == 0)
|
||||
advanceBlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
void advanceBlock()
|
||||
{
|
||||
magic = magic * 7 + 3;
|
||||
}
|
||||
};
|
||||
|
||||
static PHYSFS_sint64
|
||||
RGSS_ioRead(PHYSFS_Io *self, void *buffer, PHYSFS_uint64 len)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
|
||||
quint64 toRead = qMin(entry->data.size - entry->currentOffset, len);
|
||||
quint64 offs = entry->currentOffset;
|
||||
|
||||
entry->io->seek(entry->io, entry->data.offset + offs);
|
||||
|
||||
quint64 buffI = 0;
|
||||
for (quint64 o = offs; o < offs + toRead;)
|
||||
{
|
||||
quint8 bitOffset = (0x8 * (o % 4));
|
||||
quint8 magicByte = (entry->currentMagic >> bitOffset) & 0xFF;
|
||||
|
||||
quint8 byte;
|
||||
entry->io->read(entry->io, &byte, 1);
|
||||
|
||||
((quint8*) buffer)[buffI++] = byte ^ magicByte;
|
||||
|
||||
if (++o % 4 == 0)
|
||||
advanceMagic(entry->currentMagic);
|
||||
}
|
||||
|
||||
entry->currentOffset += toRead;
|
||||
|
||||
return toRead;
|
||||
}
|
||||
|
||||
static int
|
||||
RGSS_ioSeek(PHYSFS_Io *self, PHYSFS_uint64 offset)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
|
||||
if (offset == entry->currentOffset)
|
||||
return 1;
|
||||
|
||||
if (offset > entry->data.size-1)
|
||||
return 0;
|
||||
|
||||
/* If rewinding, we need to rewind to begining */
|
||||
if (offset < entry->currentOffset)
|
||||
{
|
||||
entry->currentOffset = 0;
|
||||
entry->currentMagic = entry->data.startMagic;
|
||||
}
|
||||
|
||||
/* For each 4 bytes sought, advance magic */
|
||||
quint64 dwordsSought = (offset - entry->currentOffset) / 4;
|
||||
for (quint64 i = 0; i < dwordsSought; ++i)
|
||||
advanceMagic(entry->currentMagic);
|
||||
|
||||
entry->currentOffset = offset;
|
||||
entry->io->seek(entry->io, entry->data.offset + entry->currentOffset);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PHYSFS_sint64
|
||||
RGSS_ioTell(PHYSFS_Io *self)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
|
||||
return entry->currentOffset;
|
||||
}
|
||||
|
||||
static PHYSFS_sint64
|
||||
RGSS_ioLength(PHYSFS_Io *self)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
|
||||
return entry->data.size;
|
||||
}
|
||||
|
||||
static PHYSFS_Io*
|
||||
RGSS_ioDuplicate(PHYSFS_Io *self)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
RGSS_entryHandle *entryDup = new RGSS_entryHandle(*entry);
|
||||
|
||||
PHYSFS_Io *dup = PHYSFS_ALLOC(PHYSFS_Io);
|
||||
*dup = *self;
|
||||
dup->opaque = entryDup;
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
static void
|
||||
RGSS_ioDestroy(PHYSFS_Io *self)
|
||||
{
|
||||
RGSS_entryHandle *entry = static_cast<RGSS_entryHandle*>(self->opaque);
|
||||
|
||||
delete entry;
|
||||
|
||||
PHYSFS_getAllocator()->Free(self);
|
||||
}
|
||||
|
||||
static const PHYSFS_Io RGSS_IoTemplate =
|
||||
{
|
||||
0, /* version */
|
||||
0, /* opaque */
|
||||
RGSS_ioRead,
|
||||
0, /* write */
|
||||
RGSS_ioSeek,
|
||||
RGSS_ioTell,
|
||||
RGSS_ioLength,
|
||||
RGSS_ioDuplicate,
|
||||
0, /* flush */
|
||||
RGSS_ioDestroy
|
||||
};
|
||||
|
||||
static void*
|
||||
RGSS_openArchive(PHYSFS_Io *io, const char *, int forWrite)
|
||||
{
|
||||
if (forWrite)
|
||||
return 0;
|
||||
|
||||
/* Check header */
|
||||
quint32 header1, header2;
|
||||
readUint32(io, header1);
|
||||
readUint32(io, header2);
|
||||
|
||||
if (header1 != RGSS_HEADER_1 || header2 != RGSS_HEADER_2)
|
||||
return 0;
|
||||
|
||||
RGSS_archiveData *data = new RGSS_archiveData;
|
||||
data->archiveIo = io;
|
||||
|
||||
quint32 magic = RGSS_MAGIC;
|
||||
|
||||
while (true)
|
||||
{
|
||||
/* Read filename length,
|
||||
* if nothing was read, no files remain */
|
||||
quint32 nameLen;
|
||||
if (!readUint32(io, nameLen))
|
||||
break;
|
||||
|
||||
nameLen ^= advanceMagic(magic);
|
||||
|
||||
static char nameBuf[512];
|
||||
uint i;
|
||||
for (i = 0; i < nameLen; ++i)
|
||||
{
|
||||
char c;
|
||||
io->read(io, &c, 1);
|
||||
nameBuf[i] = c ^ (advanceMagic(magic) & 0xFF);
|
||||
if (nameBuf[i] == '\\')
|
||||
nameBuf[i] = '/';
|
||||
}
|
||||
nameBuf[i] = 0;
|
||||
|
||||
quint32 entrySize;
|
||||
readUint32(io, entrySize);
|
||||
entrySize ^= advanceMagic(magic);
|
||||
|
||||
RGSS_entryData entry;
|
||||
entry.offset = io->tell(io);
|
||||
entry.size = entrySize;
|
||||
entry.startMagic = magic;
|
||||
|
||||
data->entryHash.insert(nameBuf, entry);
|
||||
|
||||
/* Test for new folder */
|
||||
for (i = nameLen; i > 0; i--)
|
||||
if (nameBuf[i] == '/')
|
||||
{
|
||||
nameBuf[i] = '\0';
|
||||
if (!data->dirHash.contains(nameBuf))
|
||||
data->dirHash.insert(nameBuf, true);
|
||||
}
|
||||
|
||||
io->seek(io, entry.offset + entry.size);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
RGSS_enumerateFiles(void *opaque, const char *dirname,
|
||||
PHYSFS_EnumFilesCallback cb,
|
||||
const char *origdir, void *callbackdata)
|
||||
{
|
||||
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
|
||||
|
||||
QString dirn(dirname);
|
||||
|
||||
char dirBuf[512];
|
||||
char baseBuf[512];
|
||||
QByteList keys = data->entryHash.keys();
|
||||
keys += data->dirHash.keys();
|
||||
|
||||
Q_FOREACH (const QByteArray &filename, keys)
|
||||
{
|
||||
/* Get the filename directory part */
|
||||
strcpy(dirBuf, filename.constData());
|
||||
strcpy(baseBuf, filename.constData());
|
||||
|
||||
/* Extract path and basename */
|
||||
const char *dirpath = "";
|
||||
char *basename = dirBuf;
|
||||
|
||||
for (int i = filename.size(); i >= 0; i--)
|
||||
if (dirBuf[i] == '/')
|
||||
{
|
||||
dirBuf[i] = '\0';
|
||||
dirpath = dirBuf;
|
||||
|
||||
basename = &dirBuf[i+1];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Compare to provided dirname */
|
||||
if (strcmp(dirpath, dirname) == 0)
|
||||
cb(callbackdata, origdir, basename);
|
||||
}
|
||||
}
|
||||
|
||||
static PHYSFS_Io*
|
||||
RGSS_openRead(void *opaque, const char *filename)
|
||||
{
|
||||
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
|
||||
|
||||
if (!data->entryHash.contains(filename))
|
||||
return 0;
|
||||
|
||||
RGSS_entryHandle *entry = new RGSS_entryHandle(data->entryHash[filename]);
|
||||
entry->io = data->archiveIo->duplicate(data->archiveIo);
|
||||
|
||||
PHYSFS_Io *io = PHYSFS_ALLOC(PHYSFS_Io);
|
||||
|
||||
*io = RGSS_IoTemplate;
|
||||
io->opaque = entry;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
static int
|
||||
RGSS_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
|
||||
{
|
||||
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
|
||||
|
||||
bool hasFile = data->entryHash.contains(filename);
|
||||
bool hasDir = data->dirHash.contains(filename);
|
||||
|
||||
if (!hasFile && !hasDir)
|
||||
{
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stat->modtime =
|
||||
stat->createtime =
|
||||
stat->accesstime = 0;
|
||||
stat->readonly = 1;
|
||||
|
||||
if (hasFile)
|
||||
{
|
||||
RGSS_entryData &entry = data->entryHash[filename];
|
||||
|
||||
stat->filesize = entry.size;
|
||||
stat->filetype = PHYSFS_FILETYPE_REGULAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
stat->filesize = 0;
|
||||
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
RGSS_closeArchive(void *opaque)
|
||||
{
|
||||
RGSS_archiveData *data = static_cast<RGSS_archiveData*>(opaque);
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
static PHYSFS_Io*
|
||||
RGSS_noop1(void*, const char*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
RGSS_noop2(void*, const char*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const PHYSFS_Archiver RGSS_Archiver =
|
||||
{
|
||||
0,
|
||||
{
|
||||
"RGSSAD",
|
||||
"RGSS encrypted archive format",
|
||||
"Jonas Kulla <Nyocurio@gmail.com>",
|
||||
"http://k-du.de/rgss/rgss.html",
|
||||
0 /* symlinks not supported */
|
||||
},
|
||||
RGSS_openArchive,
|
||||
RGSS_enumerateFiles,
|
||||
RGSS_openRead,
|
||||
RGSS_noop1, /* openWrite */
|
||||
RGSS_noop1, /* openAppend */
|
||||
RGSS_noop2, /* remove */
|
||||
RGSS_noop2, /* mkdir */
|
||||
RGSS_stat,
|
||||
RGSS_closeArchive
|
||||
};
|
||||
|
||||
FileStream::FileStream(PHYSFS_File *file)
|
||||
{
|
||||
p = file;
|
||||
}
|
||||
|
||||
FileStream::~FileStream()
|
||||
{
|
||||
// if (p)
|
||||
// PHYSFS_close(p);
|
||||
}
|
||||
|
||||
void FileStream::operator=(const FileStream &o)
|
||||
{
|
||||
p = o.p;
|
||||
}
|
||||
|
||||
sf::Int64 FileStream::read(void *data, sf::Int64 size)
|
||||
{
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return PHYSFS_readBytes(p, data, size);
|
||||
}
|
||||
|
||||
sf::Int64 FileStream::seek(sf::Int64 position)
|
||||
{
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
int success = PHYSFS_seek(p, (PHYSFS_uint64) position);
|
||||
|
||||
return success ? position : -1;
|
||||
}
|
||||
|
||||
sf::Int64 FileStream::tell()
|
||||
{
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return PHYSFS_tell(p);
|
||||
}
|
||||
|
||||
sf::Int64 FileStream::getSize()
|
||||
{
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return PHYSFS_fileLength(p);
|
||||
}
|
||||
|
||||
sf::Int64 FileStream::write(const void *data, sf::Int64 size)
|
||||
{
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return PHYSFS_writeBytes(p, data, size);
|
||||
}
|
||||
|
||||
void FileStream::close()
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
PHYSFS_close(p);
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void enumCB(void *, const char *origdir,
|
||||
const char *fname)
|
||||
{
|
||||
qDebug() << origdir << fname;
|
||||
char buf[128];
|
||||
sprintf(buf, "%s/%s", origdir, fname);
|
||||
PHYSFS_enumerateFilesCallback(buf, enumCB, 0);
|
||||
}
|
||||
|
||||
FileSystem::FileSystem(const char *argv0)
|
||||
{
|
||||
PHYSFS_init(argv0);
|
||||
PHYSFS_registerArchiver(&RGSS_Archiver);
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem()
|
||||
{
|
||||
if (PHYSFS_deinit() == 0)
|
||||
qDebug() << "PhyFS failed to deinit.";
|
||||
}
|
||||
|
||||
void FileSystem::addPath(const char *path)
|
||||
{
|
||||
PHYSFS_mount(path, 0, 1);
|
||||
}
|
||||
|
||||
static const char *imgExt[] =
|
||||
{
|
||||
"jpg",
|
||||
"png"
|
||||
};
|
||||
|
||||
static const char *audExt[] =
|
||||
{
|
||||
// "mid",
|
||||
// "midi",
|
||||
"mp3",
|
||||
"ogg",
|
||||
"wav",
|
||||
"wma" // FIXME this prolly isn't even supported lol
|
||||
};
|
||||
|
||||
static const char *fonExt[] =
|
||||
{
|
||||
"ttf"
|
||||
};
|
||||
|
||||
struct FileExtensions
|
||||
{
|
||||
const char **ext;
|
||||
int count;
|
||||
} fileExtensions[] =
|
||||
{
|
||||
{ imgExt, ARRAY_SIZE(imgExt) },
|
||||
{ audExt, ARRAY_SIZE(audExt) },
|
||||
{ fonExt, ARRAY_SIZE(fonExt) }
|
||||
};
|
||||
|
||||
static const char *completeFileName(const char *filename,
|
||||
FileSystem::FileType type)
|
||||
{
|
||||
static char buff[1024];
|
||||
|
||||
if (PHYSFS_exists(filename))
|
||||
return filename;
|
||||
|
||||
if (type != FileSystem::Undefined)
|
||||
{
|
||||
FileExtensions *extTest = &fileExtensions[type];
|
||||
for (int i = 0; i < extTest->count; ++i)
|
||||
{
|
||||
snprintf(buff, sizeof(buff), "%s.%s", filename, extTest->ext[i]);
|
||||
if (PHYSFS_exists(buff))
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PHYSFS_File *openReadInt(const char *filename,
|
||||
FileSystem::FileType type)
|
||||
{
|
||||
const char *foundName = completeFileName(filename, type);
|
||||
|
||||
if (!foundName)
|
||||
throw Exception(Exception::NoFileError,
|
||||
"No such file or directory - %s", filename);
|
||||
|
||||
PHYSFS_File *handle = PHYSFS_openRead(foundName);
|
||||
if (!handle)
|
||||
throw Exception(Exception::PHYSFSError, "PhysFS: %s", PHYSFS_getLastError());
|
||||
|
||||
PHYSFS_fileLength(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
FileStream FileSystem::openRead(const char *filename,
|
||||
FileType type)
|
||||
{
|
||||
PHYSFS_File *handle = openReadInt(filename, type);
|
||||
|
||||
return FileStream(handle);
|
||||
}
|
||||
|
||||
static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops)
|
||||
{
|
||||
return static_cast<PHYSFS_File*>(ops->hidden.unknown.data1);
|
||||
}
|
||||
|
||||
static Sint64 SDL_RWopsSize(SDL_RWops *ops)
|
||||
{
|
||||
PHYSFS_File *f = sdlPHYS(ops);
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
return PHYSFS_fileLength(f);
|
||||
}
|
||||
|
||||
static Sint64 SDL_RWopsSeek(SDL_RWops *ops, Sint64 offset, int whence)
|
||||
{
|
||||
PHYSFS_File *f = sdlPHYS(ops);
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
Sint64 base;
|
||||
switch (whence)
|
||||
{
|
||||
default:
|
||||
case RW_SEEK_SET :
|
||||
base = 0;
|
||||
break;
|
||||
case RW_SEEK_CUR :
|
||||
base = PHYSFS_tell(f);
|
||||
break;
|
||||
case RW_SEEK_END :
|
||||
base = PHYSFS_fileLength(f);
|
||||
break;
|
||||
}
|
||||
|
||||
int result = PHYSFS_seek(f, base + offset);
|
||||
|
||||
return (result != 0) ? PHYSFS_tell(f) : -1;
|
||||
}
|
||||
|
||||
static size_t SDL_RWopsRead(SDL_RWops *ops, void *buffer, size_t size, size_t maxnum)
|
||||
{
|
||||
PHYSFS_File *f = sdlPHYS(ops);
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
PHYSFS_sint64 result = PHYSFS_readBytes(f, buffer, size*maxnum);
|
||||
|
||||
return (result != -1) ? (result / size) : 0;
|
||||
}
|
||||
|
||||
static size_t SDL_RWopsWrite(SDL_RWops *ops, const void *buffer, size_t size, size_t num)
|
||||
{
|
||||
PHYSFS_File *f = sdlPHYS(ops);
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
PHYSFS_sint64 result = PHYSFS_writeBytes(f, buffer, size*num);
|
||||
|
||||
return (result != -1) ? (result / size) : 0;
|
||||
}
|
||||
|
||||
static int SDL_RWopsClose(SDL_RWops *ops)
|
||||
{
|
||||
PHYSFS_File *f = sdlPHYS(ops);
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
int result = PHYSFS_close(f);
|
||||
|
||||
return (result != 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
const Uint32 SDL_RWOPS_PHYSFS = SDL_RWOPS_UNKNOWN+10;
|
||||
|
||||
void FileSystem::openRead(SDL_RWops &ops,
|
||||
const char *filename,
|
||||
FileType type)
|
||||
{
|
||||
PHYSFS_File *handle = openReadInt(filename, type);
|
||||
|
||||
ops.size = SDL_RWopsSize;
|
||||
ops.seek = SDL_RWopsSeek;
|
||||
ops.read = SDL_RWopsRead;
|
||||
ops.write = SDL_RWopsWrite;
|
||||
ops.close = SDL_RWopsClose;
|
||||
|
||||
ops.type = SDL_RWOPS_PHYSFS;
|
||||
ops.hidden.unknown.data1 = handle;
|
||||
}
|
||||
|
||||
bool FileSystem::exists(const char *filename, FileType type)
|
||||
{
|
||||
const char *foundName = completeFileName(filename, type);
|
||||
|
||||
return (foundName != 0);
|
||||
}
|
82
src/filesystem.h
Normal file
82
src/filesystem.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
** filesystem.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILESYSTEM_H
|
||||
#define FILESYSTEM_H
|
||||
|
||||
#include "SFML/System/InputStream.hpp"
|
||||
|
||||
#include "SDL2/SDL_rwops.h"
|
||||
|
||||
struct PHYSFS_File;
|
||||
|
||||
class FileStream : public sf::InputStream
|
||||
{
|
||||
public:
|
||||
FileStream(PHYSFS_File *);
|
||||
~FileStream();
|
||||
|
||||
void operator=(const FileStream &o);
|
||||
|
||||
sf::Int64 read(void *data, sf::Int64 size);
|
||||
sf::Int64 seek(sf::Int64 position);
|
||||
sf::Int64 tell();
|
||||
sf::Int64 getSize();
|
||||
|
||||
sf::Int64 write(const void *data, sf::Int64 size);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
PHYSFS_File *p; /* NULL denotes invalid stream */
|
||||
};
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
public:
|
||||
FileSystem(const char *argv0);
|
||||
~FileSystem();
|
||||
|
||||
void addPath(const char *path);
|
||||
|
||||
/* For extension supplementing */
|
||||
enum FileType
|
||||
{
|
||||
Image = 0,
|
||||
Audio,
|
||||
Font,
|
||||
Undefined
|
||||
};
|
||||
|
||||
FileStream openRead(const char *filename,
|
||||
FileType type = Undefined);
|
||||
|
||||
void openRead(SDL_RWops &ops,
|
||||
const char *filename,
|
||||
FileType type = Undefined);
|
||||
|
||||
bool exists(const char *filename,
|
||||
FileType type = Undefined);
|
||||
};
|
||||
|
||||
extern const Uint32 SDL_RWOPS_PHYSFS;
|
||||
|
||||
#endif // FILESYSTEM_H
|
92
src/flashable.h
Normal file
92
src/flashable.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
** flashable.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FLASHABLE_H
|
||||
#define FLASHABLE_H
|
||||
|
||||
#include "etc.h"
|
||||
#include "etc-internal.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class Flashable
|
||||
{
|
||||
public:
|
||||
Flashable()
|
||||
: flashColor(0, 0, 0, 0),
|
||||
flashing(false),
|
||||
emptyFlashFlag(false)
|
||||
{}
|
||||
|
||||
virtual ~Flashable() {}
|
||||
|
||||
void flash(const Vec4 *color, int duration)
|
||||
{
|
||||
if (duration < 1)
|
||||
return;
|
||||
|
||||
flashing = true;
|
||||
this->duration = duration;
|
||||
counter = 0;
|
||||
|
||||
if (!color)
|
||||
{
|
||||
emptyFlashFlag = true;
|
||||
return;
|
||||
}
|
||||
|
||||
flashColor = *color;
|
||||
flashAlpha = flashColor.w;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if (!flashing)
|
||||
return;
|
||||
|
||||
if (++counter > duration)
|
||||
{
|
||||
/* Flash finished. Cleanup */
|
||||
flashColor = Vec4(0, 0, 0, 0);
|
||||
flashing = false;
|
||||
emptyFlashFlag = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* No need to update flash color on empty flash */
|
||||
if (emptyFlashFlag)
|
||||
return;
|
||||
|
||||
float prog = (float) counter / duration;
|
||||
flashColor.w = flashAlpha * (1 - prog);
|
||||
}
|
||||
|
||||
protected:
|
||||
Vec4 flashColor;
|
||||
bool flashing;
|
||||
bool emptyFlashFlag;
|
||||
private:
|
||||
float flashAlpha;
|
||||
int duration;
|
||||
int counter;
|
||||
};
|
||||
|
||||
#endif // FLASHABLE_H
|
240
src/font.cpp
Normal file
240
src/font.cpp
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
** font.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "font.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "filesystem.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include "../assets/liberation.ttf.xxd"
|
||||
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
#include <QPair>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define BUNDLED_FONT liberation
|
||||
|
||||
#define BUNDLED_FONT_D(f) assets_## f ##_ttf
|
||||
#define BUNDLED_FONT_L(f) assets_## f ##_ttf_len
|
||||
|
||||
// Go fuck yourself CPP
|
||||
#define BNDL_F_D(f) BUNDLED_FONT_D(f)
|
||||
#define BNDL_F_L(f) BUNDLED_FONT_L(f)
|
||||
|
||||
typedef QPair<QByteArray, int> FontKey;
|
||||
|
||||
struct FontPoolPrivate
|
||||
{
|
||||
QHash<FontKey, TTF_Font*> hash;
|
||||
};
|
||||
|
||||
FontPool::FontPool()
|
||||
{
|
||||
p = new FontPoolPrivate;
|
||||
}
|
||||
|
||||
FontPool::~FontPool()
|
||||
{
|
||||
QHash<FontKey, TTF_Font*>::const_iterator iter;
|
||||
for (iter = p->hash.begin(); iter != p->hash.end(); ++iter)
|
||||
TTF_CloseFont(iter.value());
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
static SDL_RWops *openBundledFont()
|
||||
{
|
||||
return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT));
|
||||
}
|
||||
|
||||
_TTF_Font *FontPool::request(const char *filename,
|
||||
int size)
|
||||
{
|
||||
// FIXME Find out how font path resolution is done in VX/Ace
|
||||
QByteArray nameKey = QByteArray(filename).toLower();
|
||||
nameKey.replace(' ', '_');
|
||||
|
||||
bool useBundled = false;
|
||||
QByteArray path = QByteArray("Fonts/") + nameKey;
|
||||
if (!gState->fileSystem().exists(path.constData(), FileSystem::Font))
|
||||
{
|
||||
useBundled = true;
|
||||
nameKey = " bundled";
|
||||
}
|
||||
|
||||
FontKey key(nameKey, size);
|
||||
|
||||
TTF_Font *font = p->hash.value(key, 0);
|
||||
|
||||
if (font)
|
||||
{
|
||||
// static int i=0;qDebug() << "FontPool: <?+>" << i++;
|
||||
return font;
|
||||
}
|
||||
|
||||
// qDebug() << "FontPool: <?->";
|
||||
|
||||
/* Not in hash, create */
|
||||
SDL_RWops *ops;
|
||||
|
||||
if (useBundled)
|
||||
{
|
||||
ops = openBundledFont();
|
||||
}
|
||||
else
|
||||
{
|
||||
ops = SDL_AllocRW();
|
||||
gState->fileSystem().openRead(*ops, path.constData(), FileSystem::Font);
|
||||
}
|
||||
|
||||
// FIXME 0.9 is guesswork at this point
|
||||
font = TTF_OpenFontRW(ops, 1, (float) size * .90);
|
||||
|
||||
if (!font)
|
||||
throw Exception(Exception::SDLError, "SDL: %s", SDL_GetError());
|
||||
|
||||
p->hash.insert(key, font);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
struct FontPrivate
|
||||
{
|
||||
QByteArray name;
|
||||
int size;
|
||||
bool bold;
|
||||
bool italic;
|
||||
Color *color;
|
||||
|
||||
Color colorTmp;
|
||||
|
||||
static QByteArray defaultName;
|
||||
static int defaultSize;
|
||||
static bool defaultBold;
|
||||
static bool defaultItalic;
|
||||
static Color *defaultColor;
|
||||
|
||||
static Color defaultColorTmp;
|
||||
|
||||
TTF_Font *sdlFont;
|
||||
|
||||
FontPrivate(const char *name = 0,
|
||||
int size = 0)
|
||||
: name(name ? QByteArray(name) : defaultName),
|
||||
size(size ? size : defaultSize),
|
||||
bold(defaultBold),
|
||||
italic(defaultItalic),
|
||||
color(&colorTmp),
|
||||
colorTmp(*defaultColor)
|
||||
{
|
||||
sdlFont = gState->fontPool().request(this->name.constData(),
|
||||
this->size);
|
||||
}
|
||||
};
|
||||
|
||||
QByteArray FontPrivate::defaultName = "Arial";
|
||||
int FontPrivate::defaultSize = 22;
|
||||
bool FontPrivate::defaultBold = false;
|
||||
bool FontPrivate::defaultItalic = false;
|
||||
Color *FontPrivate::defaultColor = &FontPrivate::defaultColorTmp;
|
||||
|
||||
Color FontPrivate::defaultColorTmp(255, 255, 255, 255);
|
||||
|
||||
bool Font::doesExist(const char *name)
|
||||
{
|
||||
QByteArray path = QByteArray("fonts/") + QByteArray(name);
|
||||
|
||||
return gState->fileSystem().exists(path.constData(), FileSystem::Font);
|
||||
}
|
||||
|
||||
Font::Font(const char *name,
|
||||
int size)
|
||||
{
|
||||
p = new FontPrivate(name, size);
|
||||
}
|
||||
|
||||
Font::~Font()
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
|
||||
const char *Font::getName() const
|
||||
{
|
||||
return p->name.constData();
|
||||
}
|
||||
|
||||
void Font::setName(const char *value)
|
||||
{
|
||||
p->name = value;
|
||||
}
|
||||
|
||||
void Font::setSize(int value)
|
||||
{
|
||||
if (p->size == value)
|
||||
return;
|
||||
|
||||
p->size = value;
|
||||
p->sdlFont = gState->fontPool().request(p->name.constData(), value);
|
||||
}
|
||||
|
||||
#undef CHK_DISP
|
||||
#define CHK_DISP
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Font, Size, int, p->size)
|
||||
DEF_ATTR_SIMPLE(Font, Bold, bool, p->bold)
|
||||
DEF_ATTR_SIMPLE(Font, Italic, bool, p->italic)
|
||||
DEF_ATTR_SIMPLE(Font, Color, Color*, p->color)
|
||||
|
||||
DEF_ATTR_SIMPLE_STATIC(Font, DefaultSize, int, FontPrivate::defaultSize)
|
||||
DEF_ATTR_SIMPLE_STATIC(Font, DefaultBold, bool, FontPrivate::defaultBold)
|
||||
DEF_ATTR_SIMPLE_STATIC(Font, DefaultItalic, bool, FontPrivate::defaultItalic)
|
||||
DEF_ATTR_SIMPLE_STATIC(Font, DefaultColor, Color*, FontPrivate::defaultColor)
|
||||
|
||||
const char *Font::getDefaultName()
|
||||
{
|
||||
return FontPrivate::defaultName.constData();
|
||||
}
|
||||
|
||||
void Font::setDefaultName(const char *value)
|
||||
{
|
||||
FontPrivate::defaultName = value;
|
||||
}
|
||||
|
||||
_TTF_Font *Font::getSdlFont()
|
||||
{
|
||||
int style = TTF_STYLE_NORMAL;
|
||||
|
||||
if (p->bold)
|
||||
style |= TTF_STYLE_BOLD;
|
||||
|
||||
if (p->italic)
|
||||
style |= TTF_STYLE_ITALIC;
|
||||
|
||||
TTF_SetFontStyle(p->sdlFont, style);
|
||||
|
||||
return p->sdlFont;
|
||||
}
|
76
src/font.h
Normal file
76
src/font.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
** font.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FONT_H
|
||||
#define FONT_H
|
||||
|
||||
#include "etc.h"
|
||||
#include "util.h"
|
||||
|
||||
struct _TTF_Font;
|
||||
struct FontPoolPrivate;
|
||||
|
||||
class FontPool
|
||||
{
|
||||
public:
|
||||
FontPool();
|
||||
~FontPool();
|
||||
|
||||
_TTF_Font *request(const char *filename,
|
||||
int size);
|
||||
|
||||
private:
|
||||
FontPoolPrivate *p;
|
||||
};
|
||||
|
||||
struct FontPrivate;
|
||||
|
||||
class Font
|
||||
{
|
||||
public:
|
||||
static bool doesExist(const char *name);
|
||||
|
||||
Font(const char *name = 0,
|
||||
int size = 0);
|
||||
~Font();
|
||||
|
||||
const char *getName() const;
|
||||
void setName(const char *value);
|
||||
|
||||
DECL_ATTR( Size, int )
|
||||
DECL_ATTR( Bold, bool )
|
||||
DECL_ATTR( Italic, bool )
|
||||
DECL_ATTR( Color, Color* )
|
||||
|
||||
DECL_ATTR_STATIC( DefaultName, const char* )
|
||||
DECL_ATTR_STATIC( DefaultSize, int )
|
||||
DECL_ATTR_STATIC( DefaultBold, bool )
|
||||
DECL_ATTR_STATIC( DefaultItalic, bool )
|
||||
DECL_ATTR_STATIC( DefaultColor, Color* )
|
||||
|
||||
/* internal */
|
||||
_TTF_Font *getSdlFont();
|
||||
|
||||
private:
|
||||
FontPrivate *p;
|
||||
};
|
||||
|
||||
#endif // FONT_H
|
388
src/gl-util.h
Normal file
388
src/gl-util.h
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
** gl-util.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLUTIL_H
|
||||
#define GLUTIL_H
|
||||
|
||||
#include "GL/glew.h"
|
||||
|
||||
#include "etc-internal.h"
|
||||
#include <QDebug>
|
||||
|
||||
/* Struct wrapping GLuint for some light type safety */
|
||||
#define DEF_GL_ID \
|
||||
struct ID \
|
||||
{ \
|
||||
GLuint gl; \
|
||||
explicit ID(GLuint gl = 0) \
|
||||
: gl(gl) \
|
||||
{} \
|
||||
ID &operator=(const ID &o) \
|
||||
{ \
|
||||
gl = o.gl; \
|
||||
return *this; \
|
||||
} \
|
||||
bool operator==(const ID &o) const \
|
||||
{ \
|
||||
return gl == o.gl; \
|
||||
} \
|
||||
};
|
||||
|
||||
namespace Tex
|
||||
{
|
||||
DEF_GL_ID
|
||||
|
||||
inline ID gen()
|
||||
{
|
||||
ID id;
|
||||
glGenTextures(1, &id.gl);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline void del(ID id)
|
||||
{
|
||||
glDeleteTextures(1, &id.gl);
|
||||
}
|
||||
|
||||
inline void bind(ID id)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, id.gl);
|
||||
}
|
||||
|
||||
inline void unbind()
|
||||
{
|
||||
bind(ID(0));
|
||||
}
|
||||
|
||||
inline void bindMatrix(int width, int height, int xOffset = 0)
|
||||
{
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glLoadIdentity();
|
||||
glScalef(1.f / width, 1.f / height, 1.f);
|
||||
glTranslatef(xOffset, 0, 0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
inline void bindWithMatrix(ID id, int width, int height, int xOffset = 0)
|
||||
{
|
||||
bind(id);
|
||||
bindMatrix(width, height, xOffset);
|
||||
}
|
||||
|
||||
inline void uploadImage(GLsizei width, GLsizei height, const void *data, GLenum format)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
inline void uploadSubImage(GLint x, GLint y, GLsizei width, GLsizei height, const void *data, GLenum format)
|
||||
{
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, format, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
inline void allocEmpty(GLsizei width, GLsizei height)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
}
|
||||
|
||||
inline void setRepeat(bool mode)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
inline void setSmooth(bool mode)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode ? GL_LINEAR : GL_NEAREST);
|
||||
}
|
||||
}
|
||||
|
||||
namespace RB
|
||||
{
|
||||
DEF_GL_ID
|
||||
|
||||
inline ID gen()
|
||||
{
|
||||
ID id;
|
||||
glGenRenderbuffers(1, &id.gl);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline void del(ID id)
|
||||
{
|
||||
glDeleteRenderbuffers(1, &id.gl);
|
||||
}
|
||||
|
||||
inline void bind(ID id)
|
||||
{
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, id.gl);
|
||||
}
|
||||
|
||||
inline void unbind()
|
||||
{
|
||||
bind(ID(0));
|
||||
}
|
||||
|
||||
inline void allocEmpty(GLsizei width, GLsizei height)
|
||||
{
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FBO
|
||||
{
|
||||
DEF_GL_ID
|
||||
|
||||
enum Mode
|
||||
{
|
||||
Draw = 0,
|
||||
Read = 1,
|
||||
Default = 2
|
||||
};
|
||||
|
||||
inline ID gen()
|
||||
{
|
||||
ID id;
|
||||
glGenFramebuffers(1, &id.gl);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline void del(ID id)
|
||||
{
|
||||
glDeleteFramebuffers(1, &id.gl);
|
||||
}
|
||||
|
||||
inline void bind(ID id, Mode mode = Default)
|
||||
{
|
||||
static const GLenum modes[] =
|
||||
{
|
||||
GL_DRAW_FRAMEBUFFER,
|
||||
GL_READ_FRAMEBUFFER,
|
||||
GL_FRAMEBUFFER
|
||||
};
|
||||
|
||||
glBindFramebuffer(modes[mode], id.gl);
|
||||
}
|
||||
|
||||
inline void unbind(Mode mode = Default)
|
||||
{
|
||||
bind(ID(0), mode);
|
||||
}
|
||||
|
||||
inline void setTexTarget(Tex::ID texTarget, unsigned colorAttach = 0)
|
||||
{
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + colorAttach, GL_TEXTURE_2D, texTarget.gl, 0);
|
||||
}
|
||||
|
||||
inline void setRBTarget(RB::ID rbTarget, unsigned colorAttach = 0)
|
||||
{
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + colorAttach, GL_RENDERBUFFER, rbTarget.gl);
|
||||
}
|
||||
|
||||
inline void blit(int srcX, int srcY,
|
||||
int srcW, int srcH,
|
||||
int dstX, int dstY,
|
||||
int dstW, int dstH)
|
||||
{
|
||||
glBlitFramebuffer(srcX, srcY, srcX+srcW, srcY+srcH,
|
||||
dstX, dstY, dstX+dstW, dstY+dstH,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
|
||||
inline void blit(int srcX, int srcY,
|
||||
int dstX, int dstY,
|
||||
int srcW, int srcH)
|
||||
{
|
||||
blit(srcX, srcY, srcW, srcH, dstX, dstY, srcW, srcH);
|
||||
}
|
||||
|
||||
inline Vec4 getPixel(int x, int y, int texWidth, int texHeight)
|
||||
{
|
||||
Vec4 pixel;
|
||||
|
||||
glViewport(0, 0, texWidth, texHeight);
|
||||
glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, &pixel.x);
|
||||
|
||||
return pixel;
|
||||
}
|
||||
}
|
||||
|
||||
namespace VAO
|
||||
{
|
||||
DEF_GL_ID
|
||||
|
||||
inline ID gen()
|
||||
{
|
||||
ID id;
|
||||
glGenVertexArrays(1, &id.gl);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline void del(ID id)
|
||||
{
|
||||
glDeleteVertexArrays(1, &id.gl);
|
||||
}
|
||||
|
||||
inline void bind(ID id)
|
||||
{
|
||||
glBindVertexArray(id.gl);
|
||||
}
|
||||
|
||||
inline void unbind()
|
||||
{
|
||||
bind(ID(0));
|
||||
}
|
||||
}
|
||||
|
||||
template<GLenum target>
|
||||
struct GenericBO
|
||||
{
|
||||
DEF_GL_ID
|
||||
|
||||
inline static ID gen()
|
||||
{
|
||||
ID id;
|
||||
glGenBuffers(1, &id.gl);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
inline static void del(ID id)
|
||||
{
|
||||
glDeleteBuffers(1, &id.gl);
|
||||
}
|
||||
|
||||
inline static void bind(ID id)
|
||||
{
|
||||
glBindBuffer(target, id.gl);
|
||||
}
|
||||
|
||||
inline static void unbind()
|
||||
{
|
||||
bind(ID(0));
|
||||
}
|
||||
|
||||
inline static void uploadData(GLsizeiptr size, const GLvoid *data, GLenum usage = GL_STATIC_DRAW)
|
||||
{
|
||||
glBufferData(target, size, data, usage);
|
||||
}
|
||||
|
||||
inline static void uploadSubData(GLintptr offset, GLsizeiptr size, const GLvoid *data)
|
||||
{
|
||||
glBufferSubData(target, offset, size, data);
|
||||
}
|
||||
|
||||
inline static void allocEmpty(GLsizeiptr size, GLenum usage = GL_STATIC_DRAW)
|
||||
{
|
||||
uploadData(size, 0, usage);
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct GenericBO<GL_ARRAY_BUFFER> VBO;
|
||||
typedef struct GenericBO<GL_ELEMENT_ARRAY_BUFFER> IBO;
|
||||
|
||||
struct TexFBO
|
||||
{
|
||||
Tex::ID tex;
|
||||
FBO::ID fbo;
|
||||
int width, height;
|
||||
|
||||
TexFBO()
|
||||
: tex(0), fbo(0), width(0), height(0)
|
||||
{}
|
||||
|
||||
bool operator==(const TexFBO &other) const
|
||||
{
|
||||
return (tex == other.tex) && (fbo == other.fbo);
|
||||
}
|
||||
|
||||
static inline void init(TexFBO &obj)
|
||||
{
|
||||
obj.tex = Tex::gen();
|
||||
obj.fbo = FBO::gen();
|
||||
Tex::bind(obj.tex);
|
||||
Tex::setRepeat(false);
|
||||
Tex::setSmooth(false);
|
||||
}
|
||||
|
||||
static inline void allocEmpty(TexFBO &obj, int width, int height)
|
||||
{
|
||||
Tex::bind(obj.tex);
|
||||
Tex::allocEmpty(width, height);
|
||||
obj.width = width;
|
||||
obj.height = height;
|
||||
}
|
||||
|
||||
static inline void linkFBO(TexFBO &obj)
|
||||
{
|
||||
FBO::bind(obj.fbo);
|
||||
FBO::setTexTarget(obj.tex);
|
||||
}
|
||||
|
||||
static inline void fini(TexFBO &obj)
|
||||
{
|
||||
FBO::del(obj.fbo);
|
||||
Tex::del(obj.tex);
|
||||
}
|
||||
};
|
||||
|
||||
struct RBFBO
|
||||
{
|
||||
RB::ID rb;
|
||||
FBO::ID fbo;
|
||||
int width, height;
|
||||
|
||||
RBFBO()
|
||||
: rb(0), fbo(0), width(0), height(0)
|
||||
{}
|
||||
|
||||
static inline void init(RBFBO &obj)
|
||||
{
|
||||
obj.rb = RB::gen();
|
||||
obj.fbo = FBO::gen();
|
||||
}
|
||||
|
||||
static inline void allocEmpty(RBFBO &obj, int width, int height)
|
||||
{
|
||||
RB::bind(obj.rb);
|
||||
RB::allocEmpty(width, height);
|
||||
obj.width = width;
|
||||
obj.height = height;
|
||||
}
|
||||
|
||||
static inline void linkFBO(RBFBO &obj)
|
||||
{
|
||||
FBO::bind(obj.fbo);
|
||||
FBO::setRBTarget(obj.rb);
|
||||
}
|
||||
|
||||
static inline void fini(RBFBO &obj)
|
||||
{
|
||||
FBO::del(obj.fbo);
|
||||
RB::del(obj.rb);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GLUTIL_H
|
66
src/global-ibo.h
Normal file
66
src/global-ibo.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
** global-ibo.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLOBALIBO_H
|
||||
#define GLOBALIBO_H
|
||||
|
||||
#include <gl-util.h>
|
||||
#include <QVector>
|
||||
|
||||
struct GlobalIBO
|
||||
{
|
||||
IBO::ID ibo;
|
||||
QVector<quint32> buffer;
|
||||
|
||||
GlobalIBO()
|
||||
{
|
||||
ibo = IBO::gen();
|
||||
}
|
||||
|
||||
~GlobalIBO()
|
||||
{
|
||||
IBO::del(ibo);
|
||||
}
|
||||
|
||||
void ensureSize(int quadCount)
|
||||
{
|
||||
if (buffer.size() >= quadCount*6)
|
||||
return;
|
||||
|
||||
int startInd = buffer.size() / 6;
|
||||
buffer.reserve(quadCount*6);
|
||||
|
||||
for (int i = startInd; i < quadCount; ++i)
|
||||
{
|
||||
static const int indTemp[] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
for (int j = 0; j < 6; ++j)
|
||||
buffer.append(i * 4 + indTemp[j]);
|
||||
}
|
||||
|
||||
IBO::bind(ibo);
|
||||
IBO::uploadData(buffer.count() * sizeof(int),
|
||||
buffer.constData());
|
||||
IBO::unbind();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GLOBALIBO_H
|
271
src/globalstate.cpp
Normal file
271
src/globalstate.cpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
** globalstate.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "globalstate.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "filesystem.h"
|
||||
#include "graphics.h"
|
||||
#include "input.h"
|
||||
#include "audio.h"
|
||||
#include "glstate.h"
|
||||
#include "shader.h"
|
||||
#include "texpool.h"
|
||||
#include "font.h"
|
||||
#include "eventthread.h"
|
||||
#include "gl-util.h"
|
||||
#include "global-ibo.h"
|
||||
#include "binding.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
GlobalState *GlobalState::instance = 0;
|
||||
static GlobalIBO *globalIBO = 0;
|
||||
|
||||
#define GAME_ARCHIVE "Game.rgssad"
|
||||
|
||||
struct GlobalStatePrivate
|
||||
{
|
||||
void *bindingData;
|
||||
SDL_Window *sdlWindow;
|
||||
Scene *screen;
|
||||
|
||||
FileSystem fileSystem;
|
||||
|
||||
EventThread &eThread;
|
||||
RGSSThreadData &rtData;
|
||||
Config &config;
|
||||
|
||||
Graphics graphics;
|
||||
Input input;
|
||||
Audio audio;
|
||||
|
||||
GLState _glState;
|
||||
|
||||
SpriteShader spriteShader;
|
||||
TransShader transShader;
|
||||
SimpleTransShader sTransShader;
|
||||
HueShader hueShader;
|
||||
BltShader bltShader;
|
||||
|
||||
TexPool texPool;
|
||||
FontPool fontPool;
|
||||
|
||||
Font *defaultFont;
|
||||
|
||||
Tex::ID globalTex;
|
||||
int globalTexW, globalTexH;
|
||||
|
||||
TexFBO gpTexFBO;
|
||||
|
||||
unsigned int stampCounter;
|
||||
|
||||
GlobalStatePrivate(RGSSThreadData *threadData)
|
||||
: bindingData(0),
|
||||
sdlWindow(threadData->window),
|
||||
fileSystem(threadData->argv0),
|
||||
eThread(*threadData->ethread),
|
||||
rtData(*threadData),
|
||||
config(threadData->config),
|
||||
graphics(threadData),
|
||||
stampCounter(0)
|
||||
{
|
||||
if (!config.gameFolder.isEmpty())
|
||||
{
|
||||
int unused = chdir(config.gameFolder.constData());
|
||||
(void) unused;
|
||||
fileSystem.addPath(config.gameFolder.constData());
|
||||
}
|
||||
|
||||
// FIXME find out correct archive filename
|
||||
QByteArray archPath = threadData->config.gameFolder + "/" GAME_ARCHIVE;
|
||||
|
||||
if (QFile::exists(archPath.constData()))
|
||||
fileSystem.addPath(archPath.constData());
|
||||
|
||||
for (int i = 0; i < config.rtps.count(); ++i)
|
||||
fileSystem.addPath(config.rtps[i].constData());
|
||||
|
||||
globalTexW = 128;
|
||||
globalTexH = 64;
|
||||
|
||||
globalTex = Tex::gen();
|
||||
Tex::bind(globalTex);
|
||||
Tex::setRepeat(false);
|
||||
Tex::setSmooth(false);
|
||||
Tex::allocEmpty(globalTexW, globalTexH);
|
||||
|
||||
TexFBO::init(gpTexFBO);
|
||||
/* Reuse starting values */
|
||||
TexFBO::allocEmpty(gpTexFBO, globalTexW, globalTexH);
|
||||
TexFBO::linkFBO(gpTexFBO);
|
||||
}
|
||||
|
||||
~GlobalStatePrivate()
|
||||
{
|
||||
Tex::del(globalTex);
|
||||
TexFBO::fini(gpTexFBO);
|
||||
}
|
||||
};
|
||||
|
||||
void GlobalState::initInstance(RGSSThreadData *threadData)
|
||||
{
|
||||
globalIBO = new GlobalIBO();
|
||||
globalIBO->ensureSize(1);
|
||||
|
||||
GlobalState *state = new GlobalState(threadData);
|
||||
|
||||
GlobalState::instance = state;
|
||||
|
||||
state->p->defaultFont = new Font();
|
||||
}
|
||||
|
||||
void GlobalState::finiInstance()
|
||||
{
|
||||
delete GlobalState::instance->p->defaultFont;
|
||||
|
||||
delete GlobalState::instance;
|
||||
|
||||
delete globalIBO;
|
||||
}
|
||||
|
||||
void GlobalState::setScreen(Scene &screen)
|
||||
{
|
||||
p->screen = &screen;
|
||||
}
|
||||
|
||||
#define GSATT(type, lower) \
|
||||
type GlobalState :: lower() \
|
||||
{ \
|
||||
return p->lower; \
|
||||
}
|
||||
|
||||
GSATT(void*, bindingData)
|
||||
GSATT(SDL_Window*, sdlWindow)
|
||||
GSATT(Scene*, screen)
|
||||
GSATT(FileSystem&, fileSystem)
|
||||
GSATT(EventThread&, eThread)
|
||||
GSATT(RGSSThreadData&, rtData)
|
||||
GSATT(Config&, config)
|
||||
GSATT(Graphics&, graphics)
|
||||
GSATT(Input&, input)
|
||||
GSATT(Audio&, audio)
|
||||
GSATT(GLState&, _glState)
|
||||
GSATT(SpriteShader&, spriteShader)
|
||||
GSATT(TransShader&, transShader)
|
||||
GSATT(SimpleTransShader&, sTransShader)
|
||||
GSATT(HueShader&, hueShader)
|
||||
GSATT(BltShader&, bltShader)
|
||||
GSATT(TexPool&, texPool)
|
||||
GSATT(FontPool&, fontPool)
|
||||
|
||||
void GlobalState::setBindingData(void *data)
|
||||
{
|
||||
p->bindingData = data;
|
||||
}
|
||||
|
||||
void GlobalState::ensureQuadIBO(int minSize)
|
||||
{
|
||||
globalIBO->ensureSize(minSize);
|
||||
}
|
||||
|
||||
void GlobalState::bindQuadIBO()
|
||||
{
|
||||
IBO::bind(globalIBO->ibo);
|
||||
}
|
||||
|
||||
void GlobalState::bindTex()
|
||||
{
|
||||
Tex::bind(p->globalTex);
|
||||
Tex::allocEmpty(p->globalTexW, p->globalTexH);
|
||||
Tex::bindMatrix(p->globalTexW, p->globalTexH);
|
||||
}
|
||||
|
||||
void GlobalState::ensureTexSize(int minW, int minH, Vec2 ¤tSizeOut)
|
||||
{
|
||||
if (minW > p->globalTexW)
|
||||
p->globalTexW = findNextPow2(minW);
|
||||
|
||||
if (minH > p->globalTexH)
|
||||
p->globalTexH = findNextPow2(minH);
|
||||
|
||||
currentSizeOut = Vec2(p->globalTexW, p->globalTexH);
|
||||
}
|
||||
|
||||
TexFBO &GlobalState::gpTexFBO(int minW, int minH)
|
||||
{
|
||||
bool needResize = false;
|
||||
|
||||
if (minW > p->gpTexFBO.width)
|
||||
{
|
||||
p->gpTexFBO.width = findNextPow2(minW);
|
||||
needResize = true;
|
||||
}
|
||||
|
||||
if (minH > p->gpTexFBO.height)
|
||||
{
|
||||
p->gpTexFBO.height = findNextPow2(minH);
|
||||
needResize = true;
|
||||
}
|
||||
|
||||
if (needResize)
|
||||
{
|
||||
Tex::bind(p->gpTexFBO.tex);
|
||||
Tex::allocEmpty(p->gpTexFBO.width, p->gpTexFBO.height);
|
||||
}
|
||||
|
||||
return p->gpTexFBO;
|
||||
}
|
||||
|
||||
void GlobalState::checkShutdown()
|
||||
{
|
||||
if (!p->rtData.rqTerm)
|
||||
return;
|
||||
|
||||
p->rtData.rqTermAck = true;
|
||||
p->texPool.disable();
|
||||
scriptBinding->terminate();
|
||||
}
|
||||
|
||||
Font &GlobalState::defaultFont()
|
||||
{
|
||||
return *p->defaultFont;
|
||||
}
|
||||
|
||||
unsigned int GlobalState::genTimeStamp()
|
||||
{
|
||||
return p->stampCounter++;
|
||||
}
|
||||
|
||||
GlobalState::GlobalState(RGSSThreadData *threadData)
|
||||
{
|
||||
p = new GlobalStatePrivate(threadData);
|
||||
p->screen = p->graphics.getScreen();
|
||||
}
|
||||
|
||||
GlobalState::~GlobalState()
|
||||
{
|
||||
delete p;
|
||||
}
|
120
src/globalstate.h
Normal file
120
src/globalstate.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
** globalstate.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLOBALSTATE_H
|
||||
#define GLOBALSTATE_H
|
||||
|
||||
#include "sigc++/signal.h"
|
||||
|
||||
#define gState GlobalState::instance
|
||||
#define glState gState->_glState()
|
||||
|
||||
struct GlobalStatePrivate;
|
||||
struct RGSSThreadData;
|
||||
struct GlobalIBO;
|
||||
struct mrb_state;
|
||||
struct SDL_Window;
|
||||
struct TexFBO;
|
||||
|
||||
class Scene;
|
||||
class FileSystem;
|
||||
class EventThread;
|
||||
class Graphics;
|
||||
class Input;
|
||||
class Audio;
|
||||
class GLState;
|
||||
class SpriteShader;
|
||||
class TransShader;
|
||||
class SimpleTransShader;
|
||||
class HueShader;
|
||||
class BltShader;
|
||||
class TexPool;
|
||||
class FontPool;
|
||||
class Font;
|
||||
struct GlobalIBO;
|
||||
struct Config;
|
||||
struct Vec2;
|
||||
|
||||
struct GlobalState
|
||||
{
|
||||
void *bindingData();
|
||||
void setBindingData(void *data);
|
||||
|
||||
SDL_Window *sdlWindow();
|
||||
|
||||
Scene *screen();
|
||||
void setScreen(Scene &screen);
|
||||
|
||||
FileSystem &fileSystem();
|
||||
|
||||
EventThread &eThread();
|
||||
RGSSThreadData &rtData();
|
||||
Config &config();
|
||||
|
||||
Graphics &graphics();
|
||||
Input &input();
|
||||
Audio &audio();
|
||||
|
||||
GLState &_glState();
|
||||
|
||||
SpriteShader &spriteShader();
|
||||
TransShader &transShader();
|
||||
SimpleTransShader &sTransShader();
|
||||
HueShader &hueShader();
|
||||
BltShader &bltShader();
|
||||
|
||||
TexPool &texPool();
|
||||
FontPool &fontPool();
|
||||
|
||||
Font &defaultFont();
|
||||
|
||||
sigc::signal<void> prepareDraw;
|
||||
|
||||
unsigned int genTimeStamp();
|
||||
|
||||
/* Returns global quad IBO, and ensures it has indices
|
||||
* for at least minSize quads */
|
||||
void ensureQuadIBO(int minSize);
|
||||
void bindQuadIBO();
|
||||
|
||||
/* Global general purpose texture */
|
||||
void bindTex();
|
||||
void ensureTexSize(int minW, int minH, Vec2 ¤tSizeOut);
|
||||
|
||||
TexFBO &gpTexFBO(int minW, int minH);
|
||||
|
||||
/* Checks EventThread's shutdown request flag and if set,
|
||||
* requests the binding to terminate. In this case, this
|
||||
* function will most likely not return */
|
||||
void checkShutdown();
|
||||
|
||||
static GlobalState *instance;
|
||||
static void initInstance(RGSSThreadData *threadData);
|
||||
static void finiInstance();
|
||||
|
||||
private:
|
||||
GlobalState(RGSSThreadData *threadData);
|
||||
~GlobalState();
|
||||
|
||||
GlobalStatePrivate *p;
|
||||
};
|
||||
|
||||
#endif // GLOBALSTATE_H
|
139
src/glstate.cpp
Normal file
139
src/glstate.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
** glstate.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glstate.h"
|
||||
#include "GL/glew.h"
|
||||
#include "SDL2/SDL_rect.h"
|
||||
#include "etc.h"
|
||||
|
||||
void GLClearColor::apply(const Vec4 &value)
|
||||
{
|
||||
glClearColor(value.x, value.y, value.z, value.w);
|
||||
}
|
||||
|
||||
void GLScissorBox::apply(const IntRect &value)
|
||||
{
|
||||
glScissor(value.x, value.y, value.w, value.h);
|
||||
}
|
||||
|
||||
void GLScissorBox::setIntersect(const IntRect &value)
|
||||
{
|
||||
IntRect ¤t = get();
|
||||
|
||||
SDL_Rect r1 = { current.x, current.y, current.w, current.h };
|
||||
SDL_Rect r2 = { value.x, value.y, value.w, value.h };
|
||||
|
||||
SDL_Rect result;
|
||||
if (!SDL_IntersectRect(&r1, &r2, &result))
|
||||
result.w = result.h = 0;
|
||||
|
||||
set(IntRect(result.x, result.y, result.w, result.h));
|
||||
}
|
||||
|
||||
void GLScissorTest::apply(const bool &value)
|
||||
{
|
||||
value ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
void GLTexture2D::apply(const bool &value)
|
||||
{
|
||||
value ? glEnable(GL_TEXTURE_2D) : glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
void GLBlendMode::apply(const BlendType &value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case BlendNone :
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
break;
|
||||
|
||||
case BlendNormal :
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
|
||||
case BlendAddition :
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE, GL_ONE);
|
||||
break;
|
||||
|
||||
case BlendSubstraction :
|
||||
// FIXME Alpha calculation is untested
|
||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE, GL_ONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLViewport::apply(const IntRect &value)
|
||||
{
|
||||
glViewport(value.x, value.y, value.w, value.h);
|
||||
}
|
||||
|
||||
|
||||
void GLState::setViewport(int width, int height)
|
||||
{
|
||||
viewport.set(IntRect(0, 0, width, height));
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
void GLState::pushSetViewport(int width, int height)
|
||||
{
|
||||
viewport.pushSet(IntRect(0, 0, width, height));
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
void GLState::popViewport()
|
||||
{
|
||||
viewport.pop();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
GLState::Caps::Caps()
|
||||
{
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
|
||||
}
|
||||
|
||||
GLState::GLState()
|
||||
{
|
||||
clearColor.init(Vec4(0, 0, 0, 1));
|
||||
blendMode.init(BlendNormal);
|
||||
scissorTest.init(false);
|
||||
scissorBox.init(IntRect(0, 0, 640, 480));
|
||||
texture2D.init(true);
|
||||
}
|
145
src/glstate.h
Normal file
145
src/glstate.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
** glstate.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLSTATE_H
|
||||
#define GLSTATE_H
|
||||
|
||||
#include "etc.h"
|
||||
#include <QStack>
|
||||
|
||||
template<typename T>
|
||||
struct GLProperty
|
||||
{
|
||||
void init(const T &value)
|
||||
{
|
||||
current = value;
|
||||
apply(value);
|
||||
}
|
||||
|
||||
void push() { stack.push(current); }
|
||||
void pop() { set(stack.pop()); }
|
||||
T &get() { return current; }
|
||||
void set(const T &value)
|
||||
{
|
||||
if (value == current)
|
||||
return;
|
||||
|
||||
init(value);
|
||||
}
|
||||
|
||||
void pushSet(const T &value)
|
||||
{
|
||||
push();
|
||||
set(value);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void apply(const T &value) = 0;
|
||||
|
||||
T current;
|
||||
QStack<T> stack;
|
||||
};
|
||||
|
||||
// Not needed
|
||||
template<typename T>
|
||||
struct GLPropSaver
|
||||
{
|
||||
GLPropSaver(GLProperty<T> &p)
|
||||
: p(p)
|
||||
{
|
||||
p.push();
|
||||
}
|
||||
|
||||
~GLPropSaver()
|
||||
{
|
||||
p.pop();
|
||||
}
|
||||
|
||||
private:
|
||||
GLProperty<T> &p;
|
||||
};
|
||||
|
||||
|
||||
class GLClearColor : public GLProperty<Vec4>
|
||||
{
|
||||
void apply(const Vec4 &);
|
||||
};
|
||||
|
||||
class GLScissorBox : public GLProperty<IntRect>
|
||||
{
|
||||
public:
|
||||
/* Sets the intersection of the current box with value */
|
||||
void setIntersect(const IntRect &value);
|
||||
|
||||
private:
|
||||
void apply(const IntRect &value);
|
||||
};
|
||||
|
||||
class GLScissorTest : public GLProperty<bool>
|
||||
{
|
||||
void apply(const bool &value);
|
||||
};
|
||||
|
||||
class GLTexture2D : public GLProperty<bool>
|
||||
{
|
||||
void apply(const bool &value);
|
||||
};
|
||||
|
||||
class GLBlendMode : public GLProperty<BlendType>
|
||||
{
|
||||
void apply(const BlendType &value);
|
||||
};
|
||||
|
||||
class GLViewport : public GLProperty<IntRect>
|
||||
{
|
||||
void apply(const IntRect &value);
|
||||
};
|
||||
|
||||
|
||||
class GLState
|
||||
{
|
||||
public:
|
||||
GLClearColor clearColor;
|
||||
GLScissorBox scissorBox;
|
||||
GLScissorTest scissorTest;
|
||||
GLTexture2D texture2D;
|
||||
GLBlendMode blendMode;
|
||||
GLViewport viewport;
|
||||
|
||||
/* These functions pushSet/pop both the viewport
|
||||
* and a glOrtho projection matrix.
|
||||
* Useful for setting up rendering to FBOs of differing sizes */
|
||||
void setViewport(int width, int height);
|
||||
void pushSetViewport(int width, int height);
|
||||
void popViewport();
|
||||
|
||||
struct Caps
|
||||
{
|
||||
int maxTexSize;
|
||||
|
||||
Caps();
|
||||
|
||||
} caps;
|
||||
|
||||
GLState();
|
||||
};
|
||||
|
||||
#endif // GLSTATE_H
|
855
src/graphics.cpp
Normal file
855
src/graphics.cpp
Normal file
|
@ -0,0 +1,855 @@
|
|||
/*
|
||||
** graphics.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "graphics.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "gl-util.h"
|
||||
#include "globalstate.h"
|
||||
#include "glstate.h"
|
||||
#include "shader.h"
|
||||
#include "scene.h"
|
||||
#include "quad.h"
|
||||
#include "eventthread.h"
|
||||
#include "texpool.h"
|
||||
#include "bitmap.h"
|
||||
#include "etc-internal.h"
|
||||
#include "binding.h"
|
||||
|
||||
#include "SDL2/SDL_video.h"
|
||||
#include "SDL2/SDL_timer.h"
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#include "SFML/System/Clock.hpp"
|
||||
|
||||
struct TimerQuery
|
||||
{
|
||||
GLuint query;
|
||||
static bool queryActive;
|
||||
bool thisQueryActive;
|
||||
|
||||
TimerQuery()
|
||||
: thisQueryActive(false)
|
||||
{
|
||||
glGenQueries(1, &query);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
if (queryActive)
|
||||
return;
|
||||
|
||||
if (thisQueryActive)
|
||||
return;
|
||||
|
||||
glBeginQuery(GL_TIME_ELAPSED, query);
|
||||
queryActive = true;
|
||||
thisQueryActive = true;
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
if (!thisQueryActive)
|
||||
return;
|
||||
|
||||
glEndQuery(GL_TIME_ELAPSED);
|
||||
queryActive = false;
|
||||
thisQueryActive = false;
|
||||
}
|
||||
|
||||
bool getResult(GLuint64 *result)
|
||||
{
|
||||
if (thisQueryActive)
|
||||
return false;
|
||||
|
||||
GLint isReady;
|
||||
glGetQueryObjectiv(query, GL_QUERY_RESULT_AVAILABLE, &isReady);
|
||||
|
||||
if (isReady != GL_TRUE)
|
||||
{
|
||||
// qDebug() << "TimerQuery result not ready";
|
||||
return false;
|
||||
}
|
||||
|
||||
glGetQueryObjectui64v(query, GL_QUERY_RESULT, result);
|
||||
|
||||
if (glGetError() == GL_INVALID_OPERATION)
|
||||
{
|
||||
qDebug() << "Something went wrong with getting TimerQuery results";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GLuint64 getResultSync()
|
||||
{
|
||||
if (thisQueryActive)
|
||||
return 0;
|
||||
|
||||
GLuint64 result;
|
||||
GLint isReady = GL_FALSE;
|
||||
|
||||
while (isReady == GL_FALSE)
|
||||
glGetQueryObjectiv(query, GL_QUERY_RESULT_AVAILABLE, &isReady);
|
||||
|
||||
glGetQueryObjectui64v(query, GL_QUERY_RESULT, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
~TimerQuery()
|
||||
{
|
||||
if (thisQueryActive)
|
||||
end();
|
||||
|
||||
glDeleteQueries(1, &query);
|
||||
}
|
||||
};
|
||||
|
||||
bool TimerQuery::queryActive = false;
|
||||
|
||||
struct GPUTimer
|
||||
{
|
||||
TimerQuery queries[2];
|
||||
const int iter;
|
||||
|
||||
uchar ind;
|
||||
quint64 acc;
|
||||
qint32 counter;
|
||||
bool first;
|
||||
|
||||
GPUTimer(int iter)
|
||||
: iter(iter),
|
||||
ind(0),
|
||||
acc(0),
|
||||
counter(0),
|
||||
first(true)
|
||||
{}
|
||||
|
||||
void startTiming()
|
||||
{
|
||||
queries[ind].begin();
|
||||
}
|
||||
|
||||
void endTiming()
|
||||
{
|
||||
queries[ind].end();
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
|
||||
swapInd();
|
||||
|
||||
GLuint64 result;
|
||||
if (!queries[ind].getResult(&result))
|
||||
return;
|
||||
|
||||
acc += result;
|
||||
|
||||
if (++counter < iter)
|
||||
return;
|
||||
|
||||
qDebug() << " Avg. GPU time:" << ((double) acc / (iter * 1000 * 1000)) << "ms";
|
||||
acc = counter = 0;
|
||||
}
|
||||
|
||||
void swapInd()
|
||||
{
|
||||
ind = ind ? 0 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct CPUTimer
|
||||
{
|
||||
const int iter;
|
||||
|
||||
quint64 acc;
|
||||
qint32 counter;
|
||||
sf::Clock clock;
|
||||
|
||||
CPUTimer(int iter)
|
||||
: iter(iter),
|
||||
acc(0),
|
||||
counter(0)
|
||||
{
|
||||
}
|
||||
|
||||
void startTiming()
|
||||
{
|
||||
clock.restart();
|
||||
}
|
||||
|
||||
void endTiming()
|
||||
{
|
||||
acc += clock.getElapsedTime().asMicroseconds();
|
||||
|
||||
if (++counter < iter)
|
||||
return;
|
||||
|
||||
qDebug() << "Avg. CPU time:" << ((double) acc / (iter * 1000)) << "ms";
|
||||
acc = counter = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct PingPong
|
||||
{
|
||||
TexFBO rt[2];
|
||||
unsigned srcInd, dstInd;
|
||||
int screenW, screenH;
|
||||
|
||||
PingPong(int screenW, int screenH)
|
||||
: srcInd(0), dstInd(1),
|
||||
screenW(screenW), screenH(screenH)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
TexFBO::init(rt[i]);
|
||||
TexFBO::allocEmpty(rt[i], screenW, screenH);
|
||||
TexFBO::linkFBO(rt[i]);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
~PingPong()
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
TexFBO::fini(rt[i]);
|
||||
}
|
||||
|
||||
/* Binds FBO of last good buffer for reading */
|
||||
void bindLastBuffer()
|
||||
{
|
||||
FBO::bind(rt[dstInd].fbo, FBO::Read);
|
||||
}
|
||||
|
||||
/* Better not call this during render cycles */
|
||||
void resize(int width, int height)
|
||||
{
|
||||
screenW = width;
|
||||
screenH = height;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
Tex::bind(rt[i].tex);
|
||||
Tex::allocEmpty(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void startRender()
|
||||
{
|
||||
bind();
|
||||
}
|
||||
|
||||
void swapRender()
|
||||
{
|
||||
swapIndices();
|
||||
|
||||
/* Discard dest buffer */
|
||||
Tex::bind(rt[dstInd].tex);
|
||||
Tex::allocEmpty(screenW, screenH);
|
||||
|
||||
bind();
|
||||
}
|
||||
|
||||
void blitFBOs()
|
||||
{
|
||||
FBO::blit(0, 0, 0, 0, screenW, screenH);
|
||||
}
|
||||
|
||||
void finishRender()
|
||||
{
|
||||
FBO::unbind(FBO::Draw);
|
||||
FBO::bind(rt[dstInd].fbo, FBO::Read);
|
||||
}
|
||||
|
||||
private:
|
||||
void bind()
|
||||
{
|
||||
Tex::bindWithMatrix(rt[srcInd].tex, screenW, screenH, true);
|
||||
FBO::bind(rt[srcInd].fbo, FBO::Read);
|
||||
FBO::bind(rt[dstInd].fbo, FBO::Draw);
|
||||
}
|
||||
|
||||
void swapIndices()
|
||||
{
|
||||
unsigned tmp = srcInd;
|
||||
srcInd = dstInd;
|
||||
dstInd = tmp;
|
||||
}
|
||||
};
|
||||
|
||||
class ScreenScene : public Scene
|
||||
{
|
||||
public:
|
||||
ScreenScene(int width, int height)
|
||||
: pp(width, height),
|
||||
brightEffect(false),
|
||||
actW(width), actH(height)
|
||||
{
|
||||
updateReso(width, height);
|
||||
brightnessQuad.setColor(Vec4());
|
||||
}
|
||||
|
||||
void composite()
|
||||
{
|
||||
const int w = geometry.rect.w;
|
||||
const int h = geometry.rect.h;
|
||||
|
||||
gState->prepareDraw();
|
||||
|
||||
pp.startRender();
|
||||
|
||||
glState.setViewport(w, h);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
Scene::composite();
|
||||
|
||||
if (brightEffect)
|
||||
{
|
||||
glState.texture2D.pushSet(false);
|
||||
brightnessQuad.draw();
|
||||
glState.texture2D.pop();
|
||||
}
|
||||
|
||||
pp.finishRender();
|
||||
}
|
||||
|
||||
void requestViewportRender(Vec4 &c, Vec4 &f, Vec4 &t)
|
||||
{
|
||||
pp.swapRender();
|
||||
pp.blitFBOs();
|
||||
|
||||
SpriteShader &shader = gState->spriteShader();
|
||||
shader.bind();
|
||||
shader.resetUniforms();
|
||||
shader.setColor(c);
|
||||
shader.setFlash(f);
|
||||
shader.setTone(t);
|
||||
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
Tex::bindMatrix(geometry.rect.w, geometry.rect.h);
|
||||
screenQuad.draw();
|
||||
|
||||
glState.blendMode.pop();
|
||||
shader.unbind();
|
||||
}
|
||||
|
||||
void setBrightness(float norm)
|
||||
{
|
||||
brightnessQuad.setColor(Vec4(0, 0, 0, 1.0 - norm));
|
||||
|
||||
brightEffect = norm < 1.0;
|
||||
}
|
||||
|
||||
void updateReso(int width, int height)
|
||||
{
|
||||
geometry.rect.w = width;
|
||||
geometry.rect.h = height;
|
||||
|
||||
screenQuad.setTexPosRect(geometry.rect, geometry.rect);
|
||||
brightnessQuad.setTexPosRect(geometry.rect, geometry.rect);
|
||||
|
||||
notifyGeometryChange();
|
||||
}
|
||||
|
||||
void setResolution(int width, int height)
|
||||
{
|
||||
pp.resize(width, height);
|
||||
updateReso(width, height);
|
||||
}
|
||||
|
||||
void setScreenSize(int width, int height)
|
||||
{
|
||||
actW = width;
|
||||
actH = height;
|
||||
}
|
||||
|
||||
PingPong &getPP()
|
||||
{
|
||||
return pp;
|
||||
}
|
||||
|
||||
private:
|
||||
PingPong pp;
|
||||
Quad screenQuad;
|
||||
Quad brightnessQuad;
|
||||
bool brightEffect;
|
||||
int actW, actH;
|
||||
};
|
||||
|
||||
struct FPSLimiter
|
||||
{
|
||||
unsigned lastTickCount;
|
||||
unsigned mspf; /* ms per frame */
|
||||
|
||||
FPSLimiter(unsigned desiredFPS)
|
||||
: lastTickCount(SDL_GetTicks())
|
||||
{
|
||||
setDesiredFPS(desiredFPS);
|
||||
}
|
||||
|
||||
void setDesiredFPS(unsigned value)
|
||||
{
|
||||
mspf = 1000 / value;
|
||||
}
|
||||
|
||||
void delay()
|
||||
{
|
||||
unsigned tmpTicks = SDL_GetTicks();
|
||||
unsigned tickDelta = tmpTicks - lastTickCount;
|
||||
lastTickCount = tmpTicks;
|
||||
|
||||
int toDelay = mspf - tickDelta;
|
||||
if (toDelay < 0)
|
||||
toDelay = 0;
|
||||
|
||||
SDL_Delay(toDelay);
|
||||
lastTickCount = SDL_GetTicks();
|
||||
}
|
||||
};
|
||||
|
||||
struct Timer
|
||||
{
|
||||
uint64_t lastTicks;
|
||||
uint64_t acc;
|
||||
int counter;
|
||||
|
||||
Timer()
|
||||
: lastTicks(SDL_GetPerformanceCounter()),
|
||||
acc(0),
|
||||
counter(0)
|
||||
{}
|
||||
};
|
||||
|
||||
struct GraphicsPrivate
|
||||
{
|
||||
/* Screen resolution */
|
||||
Vec2i scRes;
|
||||
/* Actual screen size */
|
||||
Vec2i scSize;
|
||||
|
||||
ScreenScene screen;
|
||||
RGSSThreadData *threadData;
|
||||
|
||||
int frameRate;
|
||||
int frameCount;
|
||||
int brightness;
|
||||
|
||||
FPSLimiter fpsLimiter;
|
||||
|
||||
GPUTimer gpuTimer;
|
||||
CPUTimer cpuTimer;
|
||||
|
||||
bool frozen;
|
||||
TexFBO frozenScene;
|
||||
TexFBO currentScene;
|
||||
Quad screenQuad;
|
||||
RBFBO transBuffer;
|
||||
|
||||
GraphicsPrivate()
|
||||
: scRes(640, 480),
|
||||
scSize(scRes),
|
||||
screen(scRes.x, scRes.y),
|
||||
frameRate(40),
|
||||
frameCount(0),
|
||||
brightness(255),
|
||||
fpsLimiter(frameRate),
|
||||
gpuTimer(frameRate),
|
||||
cpuTimer(frameRate),
|
||||
frozen(false)
|
||||
{
|
||||
TexFBO::init(frozenScene);
|
||||
TexFBO::allocEmpty(frozenScene, scRes.x, scRes.y);
|
||||
TexFBO::linkFBO(frozenScene);
|
||||
|
||||
TexFBO::init(currentScene);
|
||||
TexFBO::allocEmpty(currentScene, scRes.x, scRes.y);
|
||||
TexFBO::linkFBO(currentScene);
|
||||
|
||||
FloatRect screenRect(0, 0, scRes.x, scRes.y);
|
||||
screenQuad.setTexPosRect(screenRect, screenRect);
|
||||
|
||||
RBFBO::init(transBuffer);
|
||||
RBFBO::allocEmpty(transBuffer, scRes.x, scRes.y);
|
||||
RBFBO::linkFBO(transBuffer);
|
||||
}
|
||||
|
||||
~GraphicsPrivate()
|
||||
{
|
||||
TexFBO::fini(frozenScene);
|
||||
TexFBO::fini(currentScene);
|
||||
|
||||
RBFBO::fini(transBuffer);
|
||||
}
|
||||
|
||||
void updateScreenResoRatio()
|
||||
{
|
||||
Vec2 &ratio = gState->rtData().sizeResoRatio;
|
||||
ratio.x = (float) scRes.x / scSize.x;
|
||||
ratio.y = (float) scRes.y / scSize.y;
|
||||
}
|
||||
|
||||
void checkResize()
|
||||
{
|
||||
if (threadData->windowSizeMsg.pollChange(&scSize.x, &scSize.y))
|
||||
{
|
||||
screen.setScreenSize(scSize.x, scSize.y);
|
||||
updateScreenResoRatio();
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
threadData->rqTermAck = true;
|
||||
gState->texPool().disable();
|
||||
|
||||
scriptBinding->terminate();
|
||||
}
|
||||
|
||||
void swapGLBuffer()
|
||||
{
|
||||
SDL_GL_SwapWindow(threadData->window);
|
||||
fpsLimiter.delay();
|
||||
|
||||
++frameCount;
|
||||
}
|
||||
|
||||
void compositeToBuffer(FBO::ID fbo)
|
||||
{
|
||||
screen.composite();
|
||||
FBO::bind(fbo, FBO::Draw);
|
||||
FBO::blit(0, 0, 0, 0, scRes.x, scRes.y);
|
||||
}
|
||||
|
||||
void blitBuffer()
|
||||
{
|
||||
FBO::blit(0, 0, 0, 0, scRes.x, scRes.y);
|
||||
}
|
||||
|
||||
void blitBufferScaled()
|
||||
{
|
||||
FBO::blit(0, 0, scRes.x, scRes.y, 0, 0, scSize.x, scSize.y);
|
||||
}
|
||||
|
||||
void blitBufferFlippedScaled()
|
||||
{
|
||||
FBO::blit(0, 0, scRes.x, scRes.y, 0, scSize.y, scSize.x, -scSize.y);
|
||||
}
|
||||
|
||||
/* Blits currently bound read FBO to screen (upside-down) */
|
||||
void blitToScreen()
|
||||
{
|
||||
FBO::unbind(FBO::Draw);
|
||||
blitBufferFlippedScaled();
|
||||
}
|
||||
|
||||
void redrawScreen()
|
||||
{
|
||||
screen.composite();
|
||||
blitToScreen();
|
||||
|
||||
swapGLBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
Graphics::Graphics(RGSSThreadData *data)
|
||||
{
|
||||
p = new GraphicsPrivate;
|
||||
p->threadData = data;
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
|
||||
void Graphics::update()
|
||||
{
|
||||
gState->checkShutdown();
|
||||
|
||||
// p->cpuTimer.endTiming();
|
||||
// p->gpuTimer.startTiming();
|
||||
|
||||
if (p->frozen)
|
||||
return;
|
||||
|
||||
p->checkResize();
|
||||
p->redrawScreen();
|
||||
|
||||
// p->gpuTimer.endTiming();
|
||||
// p->cpuTimer.startTiming();
|
||||
}
|
||||
|
||||
void Graphics::wait(int duration)
|
||||
{
|
||||
for (int i = 0; i < duration; ++i)
|
||||
{
|
||||
gState->checkShutdown();
|
||||
p->checkResize();
|
||||
p->redrawScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::fadeout(int duration)
|
||||
{
|
||||
if (p->frozen)
|
||||
FBO::bind(p->frozenScene.fbo, FBO::Read);
|
||||
|
||||
for (int i = duration-1; i > -1; --i)
|
||||
{
|
||||
setBrightness((255.0 / duration) * i);
|
||||
|
||||
if (p->frozen)
|
||||
{
|
||||
p->blitToScreen();
|
||||
p->swapGLBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::fadein(int duration)
|
||||
{
|
||||
if (p->frozen)
|
||||
FBO::bind(p->frozenScene.fbo, FBO::Read);
|
||||
|
||||
for (int i = 0; i < duration; ++i)
|
||||
{
|
||||
setBrightness((255.0 / duration) * i);
|
||||
|
||||
if (p->frozen)
|
||||
{
|
||||
p->blitToScreen();
|
||||
p->swapGLBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::freeze()
|
||||
{
|
||||
p->frozen = true;
|
||||
|
||||
gState->checkShutdown();
|
||||
p->checkResize();
|
||||
|
||||
/* Capture scene into frozen buffer */
|
||||
p->compositeToBuffer(p->frozenScene.fbo);
|
||||
}
|
||||
|
||||
void Graphics::transition(int duration,
|
||||
const char *filename,
|
||||
int vague)
|
||||
{
|
||||
vague = bound(vague, 0, 512);
|
||||
Bitmap *transMap = filename ? new Bitmap(filename) : 0;
|
||||
|
||||
setBrightness(255);
|
||||
|
||||
/* Capture new scene */
|
||||
p->compositeToBuffer(p->currentScene.fbo);
|
||||
|
||||
if (transMap)
|
||||
{
|
||||
TransShader &shader = gState->transShader();
|
||||
shader.bind();
|
||||
shader.setFrozenScene(p->frozenScene.tex);
|
||||
shader.setCurrentScene(p->currentScene.tex);
|
||||
shader.setTransMap(transMap->getGLTypes().tex);
|
||||
shader.setVague(vague / 512.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleTransShader &shader = gState->sTransShader();
|
||||
shader.bind();
|
||||
shader.setFrozenScene(p->frozenScene.tex);
|
||||
shader.setCurrentScene(p->currentScene.tex);
|
||||
}
|
||||
|
||||
Tex::bindMatrix(p->scRes.x, p->scRes.y);
|
||||
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
for (int i = 0; i < duration; ++i)
|
||||
{
|
||||
if (p->threadData->rqTerm)
|
||||
{
|
||||
FragShader::unbind();
|
||||
delete transMap;
|
||||
p->shutdown();
|
||||
}
|
||||
|
||||
const float prog = i * (1.0 / duration);
|
||||
|
||||
if (transMap)
|
||||
gState->transShader().setProg(prog);
|
||||
else
|
||||
gState->sTransShader().setProg(prog);
|
||||
|
||||
FBO::bind(p->transBuffer.fbo);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
p->screenQuad.draw();
|
||||
|
||||
p->checkResize();
|
||||
|
||||
FBO::bind(p->transBuffer.fbo, FBO::Read);
|
||||
p->blitToScreen();
|
||||
|
||||
p->swapGLBuffer();
|
||||
}
|
||||
|
||||
glState.blendMode.pop();
|
||||
|
||||
FragShader::unbind();
|
||||
|
||||
delete transMap;
|
||||
|
||||
p->frozen = false;
|
||||
}
|
||||
|
||||
Bitmap *Graphics::snapToBitmap()
|
||||
{
|
||||
Bitmap *bitmap = new Bitmap(width(), height());
|
||||
|
||||
p->compositeToBuffer(bitmap->getGLTypes().fbo);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void Graphics::frameReset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Graphics::width() const
|
||||
{
|
||||
return p->scRes.x;
|
||||
}
|
||||
|
||||
int Graphics::height() const
|
||||
{
|
||||
return p->scRes.y;
|
||||
}
|
||||
|
||||
void Graphics::resizeScreen(int width, int height)
|
||||
{
|
||||
width = bound(width, 1, 640);
|
||||
height = bound(height, 1, 480);
|
||||
|
||||
Vec2i size(width, height);
|
||||
|
||||
if (p->scRes == size)
|
||||
return;
|
||||
|
||||
gState->eThread().requestWindowResize(width, height);
|
||||
|
||||
p->scRes = size;
|
||||
|
||||
p->screen.setResolution(width, height);
|
||||
|
||||
Tex::bind(p->frozenScene.tex);
|
||||
Tex::allocEmpty(width, height);
|
||||
Tex::bind(p->currentScene.tex);
|
||||
Tex::allocEmpty(width, height);
|
||||
|
||||
FloatRect screenRect(0, 0, width, height);
|
||||
p->screenQuad.setTexPosRect(screenRect, screenRect);
|
||||
|
||||
RB::bind(p->transBuffer.rb);
|
||||
RB::allocEmpty(width, height);
|
||||
|
||||
p->updateScreenResoRatio();
|
||||
}
|
||||
|
||||
#undef RET_IF_DISP
|
||||
#define RET_IF_DISP(x)
|
||||
|
||||
#undef CHK_DISP
|
||||
#define CHK_DISP
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Graphics, FrameRate, int, p->frameRate)
|
||||
DEF_ATTR_RD_SIMPLE(Graphics, Brightness, int, p->brightness)
|
||||
|
||||
DEF_ATTR_SIMPLE(Graphics, FrameCount, int, p->frameCount)
|
||||
|
||||
void Graphics::setFrameRate(int value)
|
||||
{
|
||||
p->frameRate = bound(value, 10, 120);
|
||||
p->fpsLimiter.setDesiredFPS(p->frameRate);
|
||||
}
|
||||
|
||||
void Graphics::setBrightness(int value)
|
||||
{
|
||||
value = bound(value, 0, 255);
|
||||
|
||||
if (p->brightness == value)
|
||||
return;
|
||||
|
||||
p->brightness = value;
|
||||
p->screen.setBrightness(value / 255.0);
|
||||
}
|
||||
|
||||
bool Graphics::getFullscreen() const
|
||||
{
|
||||
return p->threadData->ethread->getFullscreen();
|
||||
}
|
||||
|
||||
void Graphics::setFullscreen(bool value)
|
||||
{
|
||||
p->threadData->ethread->requestFullscreenMode(value);
|
||||
}
|
||||
|
||||
Scene *Graphics::getScreen() const
|
||||
{
|
||||
return &p->screen;
|
||||
}
|
||||
|
||||
void Graphics::repaintWait(volatile bool *exitCond)
|
||||
{
|
||||
if (*exitCond)
|
||||
return;
|
||||
|
||||
/* Repaint the screen with the last good frame we drew */
|
||||
p->screen.getPP().bindLastBuffer();
|
||||
FBO::unbind(FBO::Draw);
|
||||
|
||||
while (!*exitCond)
|
||||
{
|
||||
gState->checkShutdown();
|
||||
|
||||
p->blitBufferFlippedScaled();
|
||||
SDL_GL_SwapWindow(p->threadData->window);
|
||||
p->fpsLimiter.delay();
|
||||
}
|
||||
}
|
73
src/graphics.h
Normal file
73
src/graphics.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
** graphics.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GRAPHICS_H
|
||||
#define GRAPHICS_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
class Scene;
|
||||
class Bitmap;
|
||||
struct RGSSThreadData;
|
||||
struct GraphicsPrivate;
|
||||
|
||||
class Graphics
|
||||
{
|
||||
public:
|
||||
Graphics(RGSSThreadData *data);
|
||||
~Graphics();
|
||||
|
||||
void update();
|
||||
void freeze();
|
||||
void transition(int duration = 8,
|
||||
const char *filename = 0,
|
||||
int vague = 40);
|
||||
|
||||
void wait(int duration);
|
||||
void fadeout(int duration);
|
||||
void fadein(int duration);
|
||||
|
||||
Bitmap *snapToBitmap();
|
||||
|
||||
void frameReset();
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
void resizeScreen(int width, int height);
|
||||
|
||||
DECL_ATTR( FrameRate, int )
|
||||
DECL_ATTR( FrameCount, int )
|
||||
DECL_ATTR( Brightness, int )
|
||||
|
||||
/* Non-standard extension */
|
||||
DECL_ATTR( Fullscreen, bool )
|
||||
|
||||
/* <internal> */
|
||||
Scene *getScreen() const;
|
||||
/* Repaint screen with static image until exitCond
|
||||
* turns true. Used in EThread::showMessageBox() */
|
||||
void repaintWait(volatile bool *exitCond);
|
||||
|
||||
private:
|
||||
GraphicsPrivate *p;
|
||||
};
|
||||
|
||||
#endif // GRAPHICS_H
|
669
src/input.cpp
Normal file
669
src/input.cpp
Normal file
|
@ -0,0 +1,669 @@
|
|||
/*
|
||||
** input.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "input.h"
|
||||
#include "globalstate.h"
|
||||
#include "eventthread.h"
|
||||
#include "exception.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "SDL2/SDL_scancode.h"
|
||||
#include "SDL2/SDL_mouse.h"
|
||||
|
||||
#include <QVector>
|
||||
#include "string.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
const int Input::buttonCodeSize = 24;
|
||||
|
||||
struct ButtonState
|
||||
{
|
||||
bool pressed;
|
||||
bool triggered;
|
||||
bool repeated;
|
||||
|
||||
ButtonState()
|
||||
: pressed(false),
|
||||
triggered(false),
|
||||
repeated(false)
|
||||
{}
|
||||
};
|
||||
|
||||
struct KbBindingData
|
||||
{
|
||||
SDL_Scancode source;
|
||||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
struct JsBindingData
|
||||
{
|
||||
unsigned int source;
|
||||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
struct Binding
|
||||
{
|
||||
Binding(Input::ButtonCode target = Input::None)
|
||||
: target(target)
|
||||
{}
|
||||
|
||||
virtual bool sourceActive() const = 0;
|
||||
virtual bool sourceRepeatable() const = 0;
|
||||
|
||||
Input::ButtonCode target;
|
||||
};
|
||||
|
||||
/* Keyboard binding */
|
||||
struct KbBinding : public Binding
|
||||
{
|
||||
KbBinding() {}
|
||||
|
||||
KbBinding(const KbBindingData &data)
|
||||
: Binding(data.target),
|
||||
source(data.source)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return EventThread::keyStates[source];
|
||||
}
|
||||
|
||||
bool sourceRepeatable() const
|
||||
{
|
||||
// return (source >= sf::Keyboard::A && source <= sf::Keyboard::Num9) ||
|
||||
// (source >= sf::Keyboard::Left && source <= sf::Keyboard::F15);
|
||||
return (source >= SDL_SCANCODE_A && source <= SDL_SCANCODE_0) ||
|
||||
(source >= SDL_SCANCODE_RIGHT && source <= SDL_SCANCODE_UP) ||
|
||||
(source >= SDL_SCANCODE_F1 && source <= SDL_SCANCODE_F12);
|
||||
}
|
||||
|
||||
SDL_Scancode source;
|
||||
};
|
||||
|
||||
/* Joystick button binding */
|
||||
struct JsButtonBinding : public Binding
|
||||
{
|
||||
JsButtonBinding() {}
|
||||
|
||||
JsButtonBinding(const JsBindingData &data)
|
||||
: Binding(data.target),
|
||||
source(data.source)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return EventThread::joyState.buttons[source];
|
||||
}
|
||||
|
||||
bool sourceRepeatable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int source;
|
||||
};
|
||||
|
||||
/* Joystick axis binding */
|
||||
struct JsAxisBinding : public Binding
|
||||
{
|
||||
JsAxisBinding() {}
|
||||
|
||||
JsAxisBinding(int *source,
|
||||
int compareValue,
|
||||
Input::ButtonCode target)
|
||||
: Binding(target),
|
||||
source(source),
|
||||
compareValue(compareValue)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return (*source == compareValue);
|
||||
}
|
||||
|
||||
bool sourceRepeatable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int *source;
|
||||
int compareValue;
|
||||
};
|
||||
|
||||
/* Mouse button binding */
|
||||
struct MsBinding : public Binding
|
||||
{
|
||||
MsBinding() {}
|
||||
|
||||
MsBinding(int buttonIndex,
|
||||
Input::ButtonCode target)
|
||||
: Binding(target),
|
||||
index(buttonIndex)
|
||||
{}
|
||||
|
||||
bool sourceActive() const
|
||||
{
|
||||
return EventThread::mouseState.buttons[index];
|
||||
}
|
||||
|
||||
bool sourceRepeatable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int index;
|
||||
};
|
||||
|
||||
/* Not rebindable */
|
||||
//static const KbBindingData staticKbBindings[] =
|
||||
//{
|
||||
// { sf::Keyboard::Left, Input::Left },
|
||||
// { sf::Keyboard::Right, Input::Right },
|
||||
// { sf::Keyboard::Up, Input::Up },
|
||||
// { sf::Keyboard::Down, Input::Down },
|
||||
// { sf::Keyboard::LShift, Input::Shift },
|
||||
// { sf::Keyboard::RShift, Input::Shift },
|
||||
// { sf::Keyboard::LControl, Input::Ctrl },
|
||||
// { sf::Keyboard::RControl, Input::Ctrl },
|
||||
// { sf::Keyboard::LAlt, Input::Alt },
|
||||
// { sf::Keyboard::RAlt, Input::Alt },
|
||||
// { sf::Keyboard::F5, Input::F5 },
|
||||
// { sf::Keyboard::F6, Input::F6 },
|
||||
// { sf::Keyboard::F7, Input::F7 },
|
||||
// { sf::Keyboard::F8, Input::F8 },
|
||||
// { sf::Keyboard::F9, Input::F9 }
|
||||
//};
|
||||
static const KbBindingData staticKbBindings[] =
|
||||
{
|
||||
{ SDL_SCANCODE_LEFT, Input::Left },
|
||||
{ SDL_SCANCODE_RIGHT, Input::Right },
|
||||
{ SDL_SCANCODE_UP, Input::Up },
|
||||
{ SDL_SCANCODE_DOWN, Input::Down },
|
||||
{ SDL_SCANCODE_LSHIFT, Input::Shift },
|
||||
{ SDL_SCANCODE_RSHIFT, Input::Shift },
|
||||
{ SDL_SCANCODE_LCTRL, Input::Ctrl },
|
||||
{ SDL_SCANCODE_RCTRL, Input::Ctrl },
|
||||
{ SDL_SCANCODE_LALT, Input::Alt },
|
||||
{ SDL_SCANCODE_RALT, Input::Alt },
|
||||
{ SDL_SCANCODE_F5, Input::F5 },
|
||||
{ SDL_SCANCODE_F6, Input::F6 },
|
||||
{ SDL_SCANCODE_F7, Input::F7 },
|
||||
{ SDL_SCANCODE_F8, Input::F8 },
|
||||
{ SDL_SCANCODE_F9, Input::F9 }
|
||||
};
|
||||
|
||||
static elementsN(staticKbBindings);
|
||||
|
||||
/* Rebindable */
|
||||
//static const KbBindingData defaultKbBindings[] =
|
||||
//{
|
||||
// { sf::Keyboard::Space, Input::C },
|
||||
// { sf::Keyboard::Return, Input::C },
|
||||
// { sf::Keyboard::Escape, Input::B },
|
||||
// { sf::Keyboard::Num0, Input::B },
|
||||
// { sf::Keyboard::LShift, Input::A },
|
||||
// { sf::Keyboard::RShift, Input::A },
|
||||
// { sf::Keyboard::Z, Input::A },
|
||||
// { sf::Keyboard::X, Input::B },
|
||||
// { sf::Keyboard::C, Input::C },
|
||||
// { sf::Keyboard::V, Input::None },
|
||||
// { sf::Keyboard::B, Input::None },
|
||||
// { sf::Keyboard::A, Input::X },
|
||||
// { sf::Keyboard::S, Input::Y },
|
||||
// { sf::Keyboard::D, Input::Z },
|
||||
// { sf::Keyboard::Q, Input::L },
|
||||
// { sf::Keyboard::W, Input::R }
|
||||
//};
|
||||
static const KbBindingData defaultKbBindings[] =
|
||||
{
|
||||
{ SDL_SCANCODE_SPACE, Input::C },
|
||||
{ SDL_SCANCODE_RETURN, Input::C },
|
||||
{ SDL_SCANCODE_ESCAPE, Input::B },
|
||||
{ SDL_SCANCODE_KP_0, Input::B },
|
||||
{ SDL_SCANCODE_LSHIFT, Input::A },
|
||||
{ SDL_SCANCODE_RSHIFT, Input::A },
|
||||
{ SDL_SCANCODE_Z, Input::A },
|
||||
{ SDL_SCANCODE_X, Input::B },
|
||||
{ SDL_SCANCODE_C, Input::C },
|
||||
{ SDL_SCANCODE_V, Input::None },
|
||||
{ SDL_SCANCODE_B, Input::None },
|
||||
{ SDL_SCANCODE_S, Input::X },
|
||||
{ SDL_SCANCODE_A, Input::Y },
|
||||
{ SDL_SCANCODE_D, Input::Z },
|
||||
{ SDL_SCANCODE_Q, Input::L },
|
||||
{ SDL_SCANCODE_W, Input::R }
|
||||
};
|
||||
|
||||
static elementsN(defaultKbBindings);
|
||||
|
||||
/* Rebindable */
|
||||
static const JsBindingData defaultJsBindings[] =
|
||||
{
|
||||
{ 0, Input::A },
|
||||
{ 1, Input::B },
|
||||
{ 2, Input::C },
|
||||
{ 3, Input::X },
|
||||
{ 4, Input::Y },
|
||||
{ 5, Input::Z },
|
||||
{ 6, Input::L },
|
||||
{ 7, Input::R },
|
||||
{ 8, Input::None },
|
||||
{ 9, Input::None }
|
||||
};
|
||||
|
||||
static elementsN(defaultJsBindings);
|
||||
|
||||
static const int mapToIndex[] =
|
||||
{
|
||||
0, 0,
|
||||
1, 0, 2, 0, 3, 0, 4, 0,
|
||||
0,
|
||||
5, 6, 7, 8, 9, 10, 11, 12,
|
||||
0, 0,
|
||||
13, 14, 15,
|
||||
0,
|
||||
16, 17, 18, 19, 20,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 22, 23
|
||||
};
|
||||
|
||||
static elementsN(mapToIndex);
|
||||
|
||||
static const Input::ButtonCode dirs[] =
|
||||
{ Input::Down, Input::Left, Input::Right, Input::Up };
|
||||
|
||||
static const int dirFlags[] =
|
||||
{
|
||||
1 << Input::Down,
|
||||
1 << Input::Left,
|
||||
1 << Input::Right,
|
||||
1 << Input::Up
|
||||
};
|
||||
|
||||
/* Dir4 is always zero on these combinations */
|
||||
static const int deadDirFlags[] =
|
||||
{
|
||||
dirFlags[0] | dirFlags[3],
|
||||
dirFlags[1] | dirFlags[2]
|
||||
};
|
||||
|
||||
static const Input::ButtonCode otherDirs[4][3] =
|
||||
{
|
||||
{ Input::Left, Input::Right, Input::Up }, // Down
|
||||
{ Input::Down, Input::Up, Input::Right }, // Left
|
||||
{ Input::Down, Input::Up, Input::Left }, // Right
|
||||
{ Input::Left, Input::Right, Input::Up } // Up
|
||||
};
|
||||
|
||||
struct InputPrivate
|
||||
{
|
||||
QVector<KbBinding> kbBindings;
|
||||
QVector<JsAxisBinding> jsABindings;
|
||||
QVector<JsButtonBinding> jsBBindings;
|
||||
QVector<MsBinding> msBindings;
|
||||
|
||||
/* Collective binding array */
|
||||
QVector<Binding*> bindings;
|
||||
|
||||
ButtonState *states;
|
||||
ButtonState *statesOld;
|
||||
|
||||
Input::ButtonCode repeating;
|
||||
unsigned int repeatCount;
|
||||
|
||||
struct
|
||||
{
|
||||
int active;
|
||||
Input::ButtonCode previous;
|
||||
} dir4Data;
|
||||
|
||||
struct
|
||||
{
|
||||
int active;
|
||||
} dir8Data;
|
||||
|
||||
|
||||
InputPrivate()
|
||||
{
|
||||
initKbBindings();
|
||||
initJsBindings();
|
||||
initMsBindings();
|
||||
|
||||
states = new ButtonState[Input::buttonCodeSize];
|
||||
statesOld = new ButtonState[Input::buttonCodeSize];
|
||||
|
||||
// Clear buffers
|
||||
clearBuffer();
|
||||
swapBuffers();
|
||||
clearBuffer();
|
||||
|
||||
repeating = Input::None;
|
||||
repeatCount = 0;
|
||||
|
||||
dir4Data.active = 0;
|
||||
dir4Data.previous = Input::None;
|
||||
|
||||
dir8Data.active = 0;
|
||||
}
|
||||
|
||||
~InputPrivate()
|
||||
{
|
||||
delete states;
|
||||
delete statesOld;
|
||||
}
|
||||
|
||||
inline ButtonState &getStateCheck(int code)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (code < 0 || code > mapToIndexN-1)
|
||||
index = 0;
|
||||
else
|
||||
index = mapToIndex[code];
|
||||
|
||||
return states[index];
|
||||
}
|
||||
|
||||
inline ButtonState &getState(Input::ButtonCode code)
|
||||
{
|
||||
return states[mapToIndex[code]];
|
||||
}
|
||||
|
||||
inline ButtonState &getOldState(Input::ButtonCode code)
|
||||
{
|
||||
return statesOld[mapToIndex[code]];
|
||||
}
|
||||
|
||||
void swapBuffers()
|
||||
{
|
||||
ButtonState *tmp = states;
|
||||
states = statesOld;
|
||||
statesOld = tmp;
|
||||
}
|
||||
|
||||
void clearBuffer()
|
||||
{
|
||||
static int size = sizeof(ButtonState) * Input::buttonCodeSize;
|
||||
memset(states, 0, size);
|
||||
}
|
||||
|
||||
void initKbBindings()
|
||||
{
|
||||
kbBindings.resize(staticKbBindingsN+defaultKbBindingsN);
|
||||
|
||||
int n = 0;
|
||||
for (int i = 0; i < staticKbBindingsN; ++i)
|
||||
kbBindings[n++] = KbBinding(staticKbBindings[i]);
|
||||
|
||||
for (int i = 0; i < defaultKbBindingsN; ++i)
|
||||
kbBindings[n++] = KbBinding(defaultKbBindings[i]);
|
||||
|
||||
/* Add to binging array */
|
||||
for (int i = 0; i < kbBindings.count(); ++i)
|
||||
bindings.append(&kbBindings[i]);
|
||||
}
|
||||
|
||||
void initJsBindings()
|
||||
{
|
||||
/* Create axis bindings */
|
||||
jsABindings.resize(4);
|
||||
|
||||
int i = 0;
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, 0x7FFF, Input::Right);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.xAxis, -0x8000, Input::Left);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, 0x7FFF, Input::Down);
|
||||
jsABindings[i++] = JsAxisBinding(&EventThread::joyState.yAxis, -0x8000, Input::Up);
|
||||
|
||||
/* Create button bindings */
|
||||
jsBBindings.resize(defaultJsBindingsN);
|
||||
|
||||
for (int i = 0; i < defaultJsBindingsN; ++i)
|
||||
jsBBindings[i] = JsButtonBinding(defaultJsBindings[i]);
|
||||
|
||||
/* Add to binging array */
|
||||
for (int i = 0; i < jsABindings.count(); ++i)
|
||||
bindings.append(&jsABindings[i]);
|
||||
|
||||
for (int i = 0; i < jsBBindings.count(); ++i)
|
||||
bindings.append(&jsBBindings[i]);
|
||||
}
|
||||
|
||||
void initMsBindings()
|
||||
{
|
||||
msBindings.resize(3);
|
||||
|
||||
int i = 0;
|
||||
msBindings[i++] = MsBinding(SDL_BUTTON_LEFT, Input::MouseLeft);
|
||||
msBindings[i++] = MsBinding(SDL_BUTTON_MIDDLE, Input::MouseMiddle);
|
||||
msBindings[i++] = MsBinding(SDL_BUTTON_RIGHT, Input::MouseRight);
|
||||
|
||||
/* Add to binding array */
|
||||
for (int i = 0; i < msBindings.count(); ++i)
|
||||
bindings.append(&msBindings[i]);
|
||||
}
|
||||
|
||||
void pollBindings(Input::ButtonCode &repeatCand)
|
||||
{
|
||||
Q_FOREACH (const Binding *b, bindings)
|
||||
pollBindingPriv(*b, repeatCand);
|
||||
|
||||
updateDir4();
|
||||
updateDir8();
|
||||
}
|
||||
|
||||
void pollBindingPriv(const Binding &b,
|
||||
Input::ButtonCode &repeatCand)
|
||||
{
|
||||
if (!b.sourceActive())
|
||||
return;
|
||||
|
||||
if (b.target == Input::None)
|
||||
return;
|
||||
|
||||
ButtonState &state = getState(b.target);
|
||||
ButtonState &oldState = getOldState(b.target);
|
||||
|
||||
state.pressed = true;
|
||||
|
||||
/* Must have been released before to trigger */
|
||||
if (!oldState.pressed)
|
||||
state.triggered = true;
|
||||
|
||||
/* Unbound keys don't create/break repeat */
|
||||
if (repeatCand != Input::None)
|
||||
return;
|
||||
|
||||
if (repeating != b.target &&
|
||||
!oldState.pressed)
|
||||
{
|
||||
if (b.sourceRepeatable())
|
||||
repeatCand = b.target;
|
||||
else
|
||||
/* Unrepeatable keys still break current repeat */
|
||||
repeating = Input::None;
|
||||
}
|
||||
}
|
||||
|
||||
void updateDir4()
|
||||
{
|
||||
int dirFlag = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
dirFlag |= (getState(dirs[i]).pressed ? dirFlags[i] : 0);
|
||||
|
||||
if (dirFlag == deadDirFlags[0] || dirFlag == deadDirFlags[1])
|
||||
{
|
||||
dir4Data.active = Input::None;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir4Data.previous != Input::None)
|
||||
{
|
||||
/* Check if prev still pressed */
|
||||
if (getState(dir4Data.previous).pressed)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
Input::ButtonCode other =
|
||||
otherDirs[(dir4Data.previous/2)-1][i];
|
||||
|
||||
if (!getState(other).pressed)
|
||||
continue;
|
||||
|
||||
dir4Data.active = other;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (!getState(dirs[i]).pressed)
|
||||
continue;
|
||||
|
||||
dir4Data.active = dirs[i];
|
||||
dir4Data.previous = dirs[i];
|
||||
return;
|
||||
}
|
||||
|
||||
dir4Data.active = Input::None;
|
||||
dir4Data.previous = Input::None;
|
||||
}
|
||||
|
||||
void updateDir8()
|
||||
{
|
||||
static const int combos[4][4] =
|
||||
{
|
||||
{ 2, 1, 3, 0 },
|
||||
{ 1, 4, 0, 7 },
|
||||
{ 3, 0, 6, 9 },
|
||||
{ 0, 7, 9, 8 }
|
||||
};
|
||||
|
||||
dir8Data.active = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Input::ButtonCode one = dirs[i];
|
||||
|
||||
if (!getState(one).pressed)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
Input::ButtonCode other = otherDirs[i][j];
|
||||
|
||||
if (!getState(other).pressed)
|
||||
continue;
|
||||
|
||||
dir8Data.active = combos[(one/2)-1][(other/2)-1];
|
||||
return;
|
||||
}
|
||||
|
||||
dir8Data.active = one;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Input::Input()
|
||||
{
|
||||
p = new InputPrivate;
|
||||
}
|
||||
|
||||
void Input::update()
|
||||
{
|
||||
gState->checkShutdown();
|
||||
|
||||
p->swapBuffers();
|
||||
p->clearBuffer();
|
||||
|
||||
ButtonCode repeatCand = None;
|
||||
|
||||
/* Poll all bindings */
|
||||
p->pollBindings(repeatCand);
|
||||
|
||||
/* Check for new repeating key */
|
||||
if (repeatCand != None && repeatCand != p->repeating)
|
||||
{
|
||||
p->repeating = repeatCand;
|
||||
p->repeatCount = 0;
|
||||
p->getState(repeatCand).repeated = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if repeating key is still pressed */
|
||||
if (p->getState(p->repeating).pressed)
|
||||
{
|
||||
p->repeatCount++;
|
||||
|
||||
/* Repeatsequence is [r...............(r...)+] */
|
||||
if (p->repeatCount > 15 && ((p->repeatCount % 4) == 0))
|
||||
p->getState(p->repeating).repeated = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
p->repeating = None;
|
||||
}
|
||||
|
||||
bool Input::isPressed(int button)
|
||||
{
|
||||
return p->getStateCheck(button).pressed;
|
||||
}
|
||||
|
||||
bool Input::isTriggered(int button)
|
||||
{
|
||||
return p->getStateCheck(button).triggered;
|
||||
}
|
||||
|
||||
bool Input::isRepeated(int button)
|
||||
{
|
||||
return p->getStateCheck(button).repeated;
|
||||
}
|
||||
|
||||
int Input::dir4Value()
|
||||
{
|
||||
return p->dir4Data.active;
|
||||
}
|
||||
|
||||
int Input::dir8Value()
|
||||
{
|
||||
return p->dir8Data.active;
|
||||
}
|
||||
|
||||
int Input::mouseX()
|
||||
{
|
||||
return EventThread::mouseState.x * gState->rtData().sizeResoRatio.x;
|
||||
}
|
||||
|
||||
int Input::mouseY()
|
||||
{
|
||||
return EventThread::mouseState.y * gState->rtData().sizeResoRatio.y;
|
||||
}
|
||||
|
||||
Input::~Input()
|
||||
{
|
||||
delete p;
|
||||
}
|
70
src/input.h
Normal file
70
src/input.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
** input.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
struct InputPrivate;
|
||||
|
||||
class Input
|
||||
{
|
||||
public:
|
||||
enum ButtonCode
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Down = 2, Left = 4, Right = 6, Up = 8,
|
||||
|
||||
A = 11, B = 12, C = 13,
|
||||
X = 14, Y = 15, Z = 16,
|
||||
L = 17, R = 18,
|
||||
|
||||
Shift = 21, Ctrl = 22, Alt = 23,
|
||||
|
||||
F5 = 25, F6 = 26, F7 = 27, F8 = 28, F9 = 29,
|
||||
|
||||
/* Non-standard extensions */
|
||||
MouseLeft = 38, MouseMiddle = 39, MouseRight = 40
|
||||
};
|
||||
|
||||
static const int buttonCodeSize;
|
||||
|
||||
Input();
|
||||
~Input();
|
||||
|
||||
void update();
|
||||
|
||||
bool isPressed(int button);
|
||||
bool isTriggered(int button);
|
||||
bool isRepeated(int button);
|
||||
|
||||
int dir4Value();
|
||||
int dir8Value();
|
||||
|
||||
/* Non-standard extensions */
|
||||
int mouseX();
|
||||
int mouseY();
|
||||
|
||||
private:
|
||||
InputPrivate *p;
|
||||
};
|
||||
|
||||
#endif // INPUT_H
|
138
src/intrulist.h
Normal file
138
src/intrulist.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
** intrulist.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTRULIST_H
|
||||
#define INTRULIST_H
|
||||
|
||||
template <typename T>
|
||||
struct IntruListLink
|
||||
{
|
||||
IntruListLink<T> *prev;
|
||||
IntruListLink<T> *next;
|
||||
T *data;
|
||||
|
||||
IntruListLink(T *data)
|
||||
: prev(0),
|
||||
next(0),
|
||||
data(data)
|
||||
{}
|
||||
|
||||
~IntruListLink()
|
||||
{
|
||||
if (prev && next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IntruList
|
||||
{
|
||||
IntruListLink<T> root;
|
||||
int size;
|
||||
|
||||
public:
|
||||
IntruList()
|
||||
: root(0),
|
||||
size(0)
|
||||
{
|
||||
root.prev = &root;
|
||||
root.next = &root;
|
||||
}
|
||||
|
||||
void prepend(IntruListLink<T> &node)
|
||||
{
|
||||
root.next->prev = &node;
|
||||
node.prev = &root;
|
||||
node.next = root.next;
|
||||
root.next = &node;
|
||||
|
||||
size++;
|
||||
}
|
||||
|
||||
void append(IntruListLink<T> &node)
|
||||
{
|
||||
root.prev->next = &node;
|
||||
node.next = &root;
|
||||
node.prev = root.prev;
|
||||
root.prev = &node;
|
||||
|
||||
size++;
|
||||
}
|
||||
|
||||
void insertBefore(IntruListLink<T> &node,
|
||||
IntruListLink<T> &prev)
|
||||
{
|
||||
node.next = &prev;
|
||||
node.prev = prev.prev;
|
||||
prev.prev->next = &node;
|
||||
prev.prev = &node;
|
||||
|
||||
size++;
|
||||
}
|
||||
|
||||
void remove(IntruListLink<T> &node)
|
||||
{
|
||||
if (!node.next)
|
||||
return;
|
||||
|
||||
node.prev->next = node.next;
|
||||
node.next->prev = node.prev;
|
||||
|
||||
node.prev = 0;
|
||||
node.next = 0;
|
||||
|
||||
size--;
|
||||
}
|
||||
|
||||
T *tail() const
|
||||
{
|
||||
IntruListLink<T> *node = root.prev;
|
||||
if (node == &root)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
IntruListLink<T> *begin()
|
||||
{
|
||||
return root.next;
|
||||
}
|
||||
|
||||
IntruListLink<T> *end()
|
||||
{
|
||||
return &root;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return root.next == &root;
|
||||
}
|
||||
|
||||
int getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // INTRULIST_H
|
209
src/main.cpp
Normal file
209
src/main.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
** main.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "GL/glew.h"
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_image.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "eventthread.h"
|
||||
#include "debuglogger.h"
|
||||
|
||||
#include "binding.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
static const char *reqExt[] =
|
||||
{
|
||||
"GL_ARB_fragment_program",
|
||||
"GL_ARB_fragment_shader",
|
||||
"GL_ARB_framebuffer_object",
|
||||
"GL_ARB_imaging",
|
||||
"GL_ARB_shader_objects",
|
||||
"GL_ARB_shading_language_100",
|
||||
"GL_ARB_texture_non_power_of_two",
|
||||
"GL_ARB_vertex_array_object",
|
||||
"GL_ARB_vertex_buffer_object",
|
||||
"GL_EXT_bgra",
|
||||
"GL_EXT_blend_func_separate",
|
||||
"GL_EXT_blend_subtract",
|
||||
"GL_EXT_framebuffer_blit",
|
||||
0
|
||||
};
|
||||
|
||||
int rgssThreadFun(void *userdata)
|
||||
{
|
||||
RGSSThreadData *threadData = static_cast<RGSSThreadData*>(userdata);
|
||||
SDL_Window *win = threadData->window;
|
||||
SDL_GLContext ctx;
|
||||
|
||||
/* Setup GL context */
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
if (threadData->config.debugMode)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
|
||||
ctx = SDL_GL_CreateContext(win);
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
threadData->rgssErrorMsg =
|
||||
QByteArray("Error creating context: ") + SDL_GetError();
|
||||
threadData->ethread->requestTerminate();
|
||||
threadData->rqTermAck = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (glewInit() != GLEW_OK)
|
||||
{
|
||||
threadData->rgssErrorMsg = "Error initializing glew";
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
threadData->ethread->requestTerminate();
|
||||
threadData->rqTermAck = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for required GL extensions */
|
||||
const char **ext = reqExt;
|
||||
for (int i = 0; ext[i]; ++i)
|
||||
{
|
||||
if (!glewIsSupported(ext[i]))
|
||||
{
|
||||
threadData->rgssErrorMsg =
|
||||
QByteArray("Required GL extension \"") + ext[i] + "\" not present";
|
||||
threadData->ethread->requestTerminate();
|
||||
threadData->rqTermAck = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(threadData->config.vsync ? 1 : 0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
DebugLogger dLogger;
|
||||
|
||||
GlobalState::initInstance(threadData);
|
||||
|
||||
/* Start script execution */
|
||||
scriptBinding->execute();
|
||||
|
||||
threadData->ethread->requestTerminate();
|
||||
|
||||
GlobalState::finiInstance();
|
||||
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int, char *argv[])
|
||||
{
|
||||
Config conf;
|
||||
|
||||
conf.read();
|
||||
conf.readGameINI();
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
|
||||
{
|
||||
qDebug() << "Error initializing SDL:" << SDL_GetError();
|
||||
return 0;
|
||||
}
|
||||
|
||||
IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG);
|
||||
TTF_Init();
|
||||
|
||||
SDL_SetHint("SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0");
|
||||
|
||||
SDL_Window *win;
|
||||
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
|
||||
if (conf.winResizable)
|
||||
winFlags |= SDL_WINDOW_RESIZABLE;
|
||||
if (conf.fullscreen)
|
||||
winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
|
||||
win = SDL_CreateWindow(conf.game.title.constData(),
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
conf.defScreenW, conf.defScreenH, winFlags);
|
||||
|
||||
if (!win)
|
||||
{
|
||||
qDebug() << "Error creating window";
|
||||
return 0;
|
||||
}
|
||||
|
||||
EventThread eventThread;
|
||||
RGSSThreadData rtData(&eventThread, argv[0], win);
|
||||
rtData.config = conf;
|
||||
|
||||
/* Start RGSS thread */
|
||||
SDL_Thread *rgssThread =
|
||||
SDL_CreateThread(rgssThreadFun, "rgss", &rtData);
|
||||
|
||||
/* Start event processing */
|
||||
eventThread.process(rtData);
|
||||
|
||||
/* Request RGSS thread to stop */
|
||||
rtData.rqTerm = true;
|
||||
|
||||
/* Wait for RGSS thread response */
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
{
|
||||
/* We can stop waiting when the request was ack'd */
|
||||
if (rtData.rqTermAck)
|
||||
{
|
||||
qDebug() << "RGSS thread ack'd request after" << i*10 << "ms";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Give RGSS thread some time to respond */
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
/* If RGSS thread ack'd request, wait for it to shutdown,
|
||||
* otherwise abandon hope and just end the process as is. */
|
||||
if (rtData.rqTermAck)
|
||||
SDL_WaitThread(rgssThread, 0);
|
||||
else
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.constData(),
|
||||
"The RGSS script seems to be stuck and mkxp will now force quit", win);
|
||||
|
||||
if (!rtData.rgssErrorMsg.isEmpty())
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, conf.game.title.constData(),
|
||||
rtData.rgssErrorMsg.constData(), win);
|
||||
|
||||
/* Clean up any remainin events */
|
||||
eventThread.cleanup();
|
||||
|
||||
qDebug() << "Shutting down.";
|
||||
|
||||
SDL_DestroyWindow(win);
|
||||
|
||||
TTF_Quit();
|
||||
IMG_Quit();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
217
src/plane.cpp
Normal file
217
src/plane.cpp
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
** plane.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "plane.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "bitmap.h"
|
||||
#include "etc.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
#include "transform.h"
|
||||
#include "etc-internal.h"
|
||||
#include "shader.h"
|
||||
#include "glstate.h"
|
||||
|
||||
struct PlanePrivate
|
||||
{
|
||||
Bitmap *bitmap;
|
||||
NormValue opacity;
|
||||
BlendType blendType;
|
||||
Color *color;
|
||||
Tone *tone;
|
||||
|
||||
int ox, oy;
|
||||
float zoomX, zoomY;
|
||||
|
||||
Scene::Geometry sceneGeo;
|
||||
|
||||
bool quadSourceDirty;
|
||||
|
||||
Quad quad;
|
||||
|
||||
EtcTemps tmp;
|
||||
|
||||
PlanePrivate()
|
||||
: bitmap(0),
|
||||
opacity(255),
|
||||
blendType(BlendNormal),
|
||||
color(&tmp.color),
|
||||
tone(&tmp.tone),
|
||||
ox(0), oy(0),
|
||||
zoomX(1), zoomY(1),
|
||||
quadSourceDirty(false)
|
||||
{}
|
||||
|
||||
void updateQuadSource()
|
||||
{
|
||||
FloatRect srcRect;
|
||||
srcRect.x = (sceneGeo.yOrigin + ox) / zoomX;
|
||||
srcRect.y = (sceneGeo.xOrigin + oy) / zoomY;
|
||||
srcRect.w = sceneGeo.rect.w / zoomX;
|
||||
srcRect.h = sceneGeo.rect.h / zoomY;
|
||||
|
||||
quad.setTexRect(srcRect);
|
||||
}
|
||||
};
|
||||
|
||||
Plane::Plane(Viewport *viewport)
|
||||
: ViewportElement(viewport)
|
||||
{
|
||||
p = new PlanePrivate();
|
||||
|
||||
onGeometryChange(scene->getGeometry());
|
||||
}
|
||||
|
||||
#define DISP_CLASS_NAME "plane"
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Plane, OX, int, p->ox)
|
||||
DEF_ATTR_RD_SIMPLE(Plane, OY, int, p->oy)
|
||||
DEF_ATTR_RD_SIMPLE(Plane, ZoomX, float, p->zoomX)
|
||||
DEF_ATTR_RD_SIMPLE(Plane, ZoomY, float, p->zoomY)
|
||||
DEF_ATTR_RD_SIMPLE(Plane, BlendType, int, p->blendType)
|
||||
|
||||
DEF_ATTR_SIMPLE(Plane, Opacity, int, p->opacity)
|
||||
DEF_ATTR_SIMPLE(Plane, Bitmap, Bitmap*, p->bitmap)
|
||||
DEF_ATTR_SIMPLE(Plane, Color, Color*, p->color)
|
||||
DEF_ATTR_SIMPLE(Plane, Tone, Tone*, p->tone)
|
||||
|
||||
Plane::~Plane()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
void Plane::setOX(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->ox = value;
|
||||
p->quadSourceDirty = true;
|
||||
}
|
||||
|
||||
void Plane::setOY(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->oy = value;
|
||||
p->quadSourceDirty = true;
|
||||
}
|
||||
|
||||
void Plane::setZoomX(float value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->zoomX = value;
|
||||
p->quadSourceDirty = true;
|
||||
}
|
||||
|
||||
void Plane::setZoomY(float value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->zoomY = value;
|
||||
p->quadSourceDirty = true;
|
||||
}
|
||||
|
||||
void Plane::setBlendType(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
switch (value)
|
||||
{
|
||||
default :
|
||||
case BlendNormal :
|
||||
p->blendType = BlendNormal;
|
||||
return;
|
||||
case BlendAddition :
|
||||
p->blendType = BlendAddition;
|
||||
return;
|
||||
case BlendSubstraction :
|
||||
p->blendType = BlendSubstraction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Plane::draw()
|
||||
{
|
||||
if (!p->bitmap)
|
||||
return;
|
||||
|
||||
if (p->bitmap->isDisposed())
|
||||
return;
|
||||
|
||||
if (!p->opacity)
|
||||
return;
|
||||
|
||||
if (p->quadSourceDirty)
|
||||
{
|
||||
p->updateQuadSource();
|
||||
p->quadSourceDirty = false;
|
||||
}
|
||||
|
||||
if (p->color->hasEffect() || p->tone->hasEffect() || p->opacity != 255)
|
||||
{
|
||||
SpriteShader &shader = gState->spriteShader();
|
||||
|
||||
shader.bind();
|
||||
shader.setTone(p->tone->norm);
|
||||
shader.setColor(p->color->norm);
|
||||
shader.setFlash(Vec4());
|
||||
shader.setOpacity(p->opacity.norm);
|
||||
shader.setBushOpacity(1);
|
||||
shader.setBushDepth(0);
|
||||
}
|
||||
|
||||
glState.blendMode.pushSet(p->blendType);
|
||||
|
||||
p->bitmap->flush();
|
||||
p->bitmap->bindTexWithMatrix();
|
||||
Tex::setRepeat(true);
|
||||
|
||||
p->quad.draw();
|
||||
|
||||
Tex::setRepeat(false);
|
||||
FragShader::unbind();
|
||||
}
|
||||
|
||||
void Plane::onGeometryChange(const Scene::Geometry &geo)
|
||||
{
|
||||
p->quad.setPosRect(FloatRect(geo.rect));
|
||||
|
||||
p->sceneGeo = geo;
|
||||
p->quadSourceDirty = true;
|
||||
}
|
||||
|
||||
void Plane::aboutToAccess() const
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
}
|
||||
|
||||
|
||||
void Plane::releaseResources()
|
||||
{
|
||||
unlink();
|
||||
|
||||
delete p;
|
||||
}
|
63
src/plane.h
Normal file
63
src/plane.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
** plane.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLANE_H
|
||||
#define PLANE_H
|
||||
|
||||
#include "disposable.h"
|
||||
#include "viewport.h"
|
||||
|
||||
#include "SFML/Graphics/Sprite.hpp"
|
||||
#include "SFML/Graphics/RectangleShape.hpp"
|
||||
|
||||
class Bitmap;
|
||||
struct Color;
|
||||
struct Tone;
|
||||
|
||||
struct PlanePrivate;
|
||||
|
||||
class Plane : public ViewportElement, public Disposable
|
||||
{
|
||||
public:
|
||||
Plane(Viewport *viewport = 0);
|
||||
~Plane();
|
||||
|
||||
DECL_ATTR( Bitmap, Bitmap* )
|
||||
DECL_ATTR( OX, int )
|
||||
DECL_ATTR( OY, int )
|
||||
DECL_ATTR( ZoomX, float )
|
||||
DECL_ATTR( ZoomY, float )
|
||||
DECL_ATTR( Opacity, int )
|
||||
DECL_ATTR( BlendType, int )
|
||||
DECL_ATTR( Color, Color* )
|
||||
DECL_ATTR( Tone, Tone* )
|
||||
|
||||
private:
|
||||
PlanePrivate *p;
|
||||
|
||||
void draw();
|
||||
void onGeometryChange(const Scene::Geometry &);
|
||||
void aboutToAccess() const;
|
||||
|
||||
void releaseResources();
|
||||
};
|
||||
|
||||
#endif // PLANE_H
|
182
src/quad.h
Normal file
182
src/quad.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
** quad.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QUAD_H
|
||||
#define QUAD_H
|
||||
|
||||
#include "GL/glew.h"
|
||||
#include "etc-internal.h"
|
||||
#include "gl-util.h"
|
||||
#include "globalstate.h"
|
||||
#include "global-ibo.h"
|
||||
|
||||
struct Quad
|
||||
{
|
||||
Vertex vert[4];
|
||||
VBO::ID vbo;
|
||||
VAO::ID vao;
|
||||
bool vboDirty;
|
||||
|
||||
static void setPosRect(CVertex *vert, const FloatRect &r)
|
||||
{
|
||||
int i = 0;
|
||||
vert[i++].pos = r.topLeft();
|
||||
vert[i++].pos = r.topRight();
|
||||
vert[i++].pos = r.bottomRight();
|
||||
vert[i++].pos = r.bottomLeft();
|
||||
}
|
||||
|
||||
static void setColor(CVertex *vert, const Vec4 &c)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
vert[i].color = c;
|
||||
}
|
||||
|
||||
static void setPosRect(SVertex *vert, const FloatRect &r)
|
||||
{
|
||||
int i = 0;
|
||||
vert[i++].pos = r.topLeft();
|
||||
vert[i++].pos = r.topRight();
|
||||
vert[i++].pos = r.bottomRight();
|
||||
vert[i++].pos = r.bottomLeft();
|
||||
}
|
||||
|
||||
static void setTexRect(SVertex *vert, const FloatRect &r)
|
||||
{
|
||||
int i = 0;
|
||||
vert[i++].texPos = r.topLeft();
|
||||
vert[i++].texPos = r.topRight();
|
||||
vert[i++].texPos = r.bottomRight();
|
||||
vert[i++].texPos = r.bottomLeft();
|
||||
}
|
||||
|
||||
static void setPosRect(Vertex *vert, const FloatRect &r)
|
||||
{
|
||||
int i = 0;
|
||||
vert[i++].pos = r.topLeft();
|
||||
vert[i++].pos = r.topRight();
|
||||
vert[i++].pos = r.bottomRight();
|
||||
vert[i++].pos = r.bottomLeft();
|
||||
}
|
||||
|
||||
static void setTexRect(Vertex *vert, const FloatRect &r)
|
||||
{
|
||||
int i = 0;
|
||||
vert[i++].texPos = r.topLeft();
|
||||
vert[i++].texPos = r.topRight();
|
||||
vert[i++].texPos = r.bottomRight();
|
||||
vert[i++].texPos = r.bottomLeft();
|
||||
}
|
||||
|
||||
static int setTexPosRect(SVertex *vert, const FloatRect &tex, const FloatRect &pos)
|
||||
{
|
||||
setPosRect(vert, pos);
|
||||
setTexRect(vert, tex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setTexPosRect(Vertex *vert, const FloatRect &tex, const FloatRect &pos)
|
||||
{
|
||||
setPosRect(vert, pos);
|
||||
setTexRect(vert, tex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Quad()
|
||||
: vbo(VBO::gen()),
|
||||
vao(VAO::gen()),
|
||||
vboDirty(true)
|
||||
{
|
||||
VAO::bind(vao);
|
||||
VBO::bind(vbo);
|
||||
gState->bindQuadIBO();
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glColorPointer (4, GL_FLOAT, sizeof(Vertex), Vertex::colorOffset());
|
||||
glVertexPointer (2, GL_FLOAT, sizeof(Vertex), Vertex::posOffset());
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), Vertex::texPosOffset());
|
||||
|
||||
VAO::unbind();
|
||||
VBO::unbind();
|
||||
IBO::unbind();
|
||||
|
||||
setColor(Vec4(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
~Quad()
|
||||
{
|
||||
VAO::del(vao);
|
||||
VBO::del(vbo);
|
||||
}
|
||||
|
||||
void updateBuffer()
|
||||
{
|
||||
VBO::bind(vbo);
|
||||
VBO::allocEmpty(sizeof(Vertex) * 4, GL_DYNAMIC_DRAW);
|
||||
VBO::uploadData(sizeof(Vertex) * 4, vert, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
void setPosRect(const FloatRect &r)
|
||||
{
|
||||
setPosRect(vert, r);
|
||||
vboDirty = true;
|
||||
}
|
||||
|
||||
void setTexRect(const FloatRect &r)
|
||||
{
|
||||
setTexRect(vert, r);
|
||||
vboDirty = true;
|
||||
}
|
||||
|
||||
void setTexPosRect(const FloatRect &tex, const FloatRect &pos)
|
||||
{
|
||||
setTexPosRect(vert, tex, pos);
|
||||
vboDirty = true;
|
||||
}
|
||||
|
||||
void setColor(const Vec4 &c)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
vert[i].color = c;
|
||||
|
||||
vboDirty = true;
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
if (vboDirty)
|
||||
{
|
||||
updateBuffer();
|
||||
vboDirty = false;
|
||||
}
|
||||
|
||||
VAO::bind(vao);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||
VAO::unbind();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QUAD_H
|
173
src/quadarray.h
Normal file
173
src/quadarray.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
** quadarray.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QUADARRAY_H
|
||||
#define QUADARRAY_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include "gl-util.h"
|
||||
#include "globalstate.h"
|
||||
#include "global-ibo.h"
|
||||
|
||||
typedef quint32 index_t;
|
||||
#define _GL_INDEX_TYPE GL_UNSIGNED_INT
|
||||
|
||||
struct ColorQuadArray
|
||||
{
|
||||
QVector<Vertex> vertices;
|
||||
|
||||
VBO::ID vbo;
|
||||
VAO::ID vao;
|
||||
|
||||
int quadCount;
|
||||
|
||||
ColorQuadArray()
|
||||
: quadCount(0)
|
||||
{
|
||||
vbo = VBO::gen();
|
||||
vao = VAO::gen();
|
||||
|
||||
VAO::bind(vao);
|
||||
VBO::bind(vbo);
|
||||
gState->bindQuadIBO();
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glVertexPointer (2, GL_FLOAT, sizeof(Vertex), Vertex::posOffset());
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), Vertex::texPosOffset());
|
||||
glColorPointer (4, GL_FLOAT, sizeof(Vertex), Vertex::colorOffset());
|
||||
|
||||
VAO::unbind();
|
||||
IBO::unbind();
|
||||
VBO::unbind();
|
||||
}
|
||||
|
||||
~ColorQuadArray()
|
||||
{
|
||||
VBO::del(vbo);
|
||||
VAO::del(vao);
|
||||
}
|
||||
|
||||
void resize(int size)
|
||||
{
|
||||
vertices.resize(size * 4);
|
||||
quadCount = size;
|
||||
}
|
||||
|
||||
/* This needs to be called after the final 'append()' call
|
||||
* and previous to the first 'draw()' call. */
|
||||
void commit()
|
||||
{
|
||||
VBO::bind(vbo);
|
||||
VBO::uploadData(vertices.size() * sizeof(Vertex), vertices.constData(), GL_DYNAMIC_DRAW);
|
||||
VBO::unbind();
|
||||
|
||||
gState->ensureQuadIBO(quadCount);
|
||||
}
|
||||
|
||||
void draw(uint offset, uint count)
|
||||
{
|
||||
VAO::bind(vao);
|
||||
|
||||
const char *_offset = (const char*) 0 + offset * 6 * sizeof(index_t);
|
||||
glDrawElements(GL_TRIANGLES, count * 6, _GL_INDEX_TYPE, _offset);
|
||||
|
||||
VAO::unbind();
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
draw(0, quadCount);
|
||||
}
|
||||
|
||||
int count() const
|
||||
{
|
||||
return quadCount;
|
||||
}
|
||||
};
|
||||
|
||||
struct PointArray
|
||||
{
|
||||
QVector<Vertex> vertices;
|
||||
VBO::ID vbo;
|
||||
VAO::ID vao;
|
||||
|
||||
PointArray()
|
||||
{
|
||||
vbo = VBO::gen();
|
||||
vao = VAO::gen();
|
||||
|
||||
VAO::bind(vao);
|
||||
VBO::bind(vbo);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (const GLvoid*) 0);
|
||||
glColorPointer (4, GL_FLOAT, sizeof(Vertex), (const GLvoid*) sizeof(SVertex));
|
||||
|
||||
VAO::unbind();
|
||||
VBO::unbind();
|
||||
}
|
||||
|
||||
~PointArray()
|
||||
{
|
||||
VBO::del(vbo);
|
||||
VAO::del(vao);
|
||||
}
|
||||
|
||||
void append(const Vec2 &pos, const Vec4 &color)
|
||||
{
|
||||
Vertex vert;
|
||||
vert.pos = pos;
|
||||
vert.color = color;
|
||||
vertices.append(vert);
|
||||
}
|
||||
|
||||
void commit()
|
||||
{
|
||||
VBO::bind(vbo);
|
||||
VBO::uploadData(vertices.size() * sizeof(Vertex), vertices.constData());
|
||||
VBO::unbind();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
VAO::bind(vao);
|
||||
glDrawArrays(GL_POINTS, 0, count());
|
||||
VAO::unbind();
|
||||
}
|
||||
|
||||
int count()
|
||||
{
|
||||
return vertices.count();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QUADARRAY_H
|
146
src/scene.cpp
Normal file
146
src/scene.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
** scene.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "scene.h"
|
||||
#include "globalstate.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Scene::Scene()
|
||||
{
|
||||
geometry.xOrigin = geometry.yOrigin = 0;
|
||||
geometry.rect = IntRect();
|
||||
}
|
||||
|
||||
void Scene::insert(SceneElement &element)
|
||||
{
|
||||
IntruListLink<SceneElement> *iter;
|
||||
|
||||
for (iter = elements.begin(); iter != elements.end(); iter = iter->next)
|
||||
{
|
||||
SceneElement *e = iter->data;
|
||||
|
||||
if (element.z <= e->z)
|
||||
{
|
||||
if (element.z == e->z)
|
||||
if (element.creationStamp > e->creationStamp)
|
||||
continue;
|
||||
|
||||
elements.insertBefore(element.link, *iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
elements.append(element.link);
|
||||
}
|
||||
|
||||
void Scene::reinsert(SceneElement &element)
|
||||
{
|
||||
elements.remove(element.link);
|
||||
insert(element);
|
||||
}
|
||||
|
||||
void Scene::notifyGeometryChange()
|
||||
{
|
||||
IntruListLink<SceneElement> *iter;
|
||||
|
||||
for (iter = elements.begin(); iter != elements.end(); iter = iter->next)
|
||||
{
|
||||
iter->data->onGeometryChange(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::composite()
|
||||
{
|
||||
IntruListLink<SceneElement> *iter;
|
||||
|
||||
for (iter = elements.begin(); iter != elements.end(); iter = iter->next)
|
||||
{
|
||||
SceneElement *e = iter->data;
|
||||
|
||||
if (e->visible)
|
||||
e->draw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SceneElement::SceneElement(Scene &scene, int z)
|
||||
: link(this),
|
||||
creationStamp(gState->genTimeStamp()),
|
||||
z(z),
|
||||
visible(true),
|
||||
scene(&scene)
|
||||
{
|
||||
scene.insert(*this);
|
||||
}
|
||||
|
||||
SceneElement::~SceneElement()
|
||||
{
|
||||
unlink();
|
||||
}
|
||||
|
||||
void SceneElement::setScene(Scene &scene)
|
||||
{
|
||||
unlink();
|
||||
|
||||
this->scene = &scene;
|
||||
|
||||
scene.insert(*this);
|
||||
|
||||
onGeometryChange(scene.getGeometry());
|
||||
}
|
||||
|
||||
int SceneElement::getZ() const
|
||||
{
|
||||
aboutToAccess();
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
void SceneElement::setZ(int value)
|
||||
{
|
||||
aboutToAccess();
|
||||
|
||||
if (z == value)
|
||||
return;
|
||||
|
||||
z = value;
|
||||
scene->reinsert(*this);
|
||||
}
|
||||
|
||||
bool SceneElement::getVisible() const
|
||||
{
|
||||
aboutToAccess();
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void SceneElement::setVisible(bool value)
|
||||
{
|
||||
aboutToAccess();
|
||||
|
||||
visible = value;
|
||||
}
|
||||
|
||||
void SceneElement::unlink()
|
||||
{
|
||||
scene->elements.remove(link);
|
||||
}
|
96
src/scene.h
Normal file
96
src/scene.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
** scene.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SCENE_H
|
||||
#define SCENE_H
|
||||
|
||||
#include "util.h"
|
||||
#include "intrulist.h"
|
||||
#include "etc.h"
|
||||
#include "etc-internal.h"
|
||||
|
||||
class SceneElement;
|
||||
class Viewport;
|
||||
class Window;
|
||||
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
struct Geometry
|
||||
{
|
||||
int xOrigin, yOrigin;
|
||||
IntRect rect;
|
||||
};
|
||||
|
||||
Scene();
|
||||
virtual ~Scene() {}
|
||||
|
||||
virtual void composite();
|
||||
virtual void requestViewportRender(Vec4& /*color*/, Vec4& /*flash*/, Vec4& /*tone*/) {}
|
||||
|
||||
const Geometry &getGeometry() const { return geometry; }
|
||||
|
||||
protected:
|
||||
void insert(SceneElement &element);
|
||||
void reinsert(SceneElement &element);
|
||||
|
||||
/* Notify all elements that geometry has changed */
|
||||
void notifyGeometryChange();
|
||||
|
||||
IntruList<SceneElement> elements;
|
||||
Geometry geometry;
|
||||
|
||||
friend class SceneElement;
|
||||
friend class Window;
|
||||
};
|
||||
|
||||
class SceneElement
|
||||
{
|
||||
public:
|
||||
SceneElement(Scene &scene, int z = 0);
|
||||
virtual ~SceneElement();
|
||||
|
||||
void setScene(Scene &scene);
|
||||
|
||||
DECL_ATTR_VIRT( Z, int )
|
||||
DECL_ATTR_VIRT( Visible, bool )
|
||||
|
||||
/* Disposable classes reimplement this to
|
||||
* check if they're disposed before access */
|
||||
virtual void aboutToAccess() const {}
|
||||
|
||||
protected:
|
||||
virtual void draw() = 0;
|
||||
virtual void onGeometryChange(const Scene::Geometry &) {}
|
||||
|
||||
void unlink();
|
||||
|
||||
IntruListLink<SceneElement> link;
|
||||
const unsigned int creationStamp;
|
||||
int z;
|
||||
bool visible;
|
||||
Scene *scene;
|
||||
|
||||
friend class Scene;
|
||||
friend class Viewport;
|
||||
};
|
||||
|
||||
#endif // SCENE_H
|
105
src/serial-util.h
Normal file
105
src/serial-util.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
** serial-util.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERIALUTIL_H
|
||||
#define SERIALUTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned uint;
|
||||
|
||||
static inline int16_t
|
||||
read_int16(const char *data, uint &i)
|
||||
{
|
||||
int16_t result = (data[i] & 0x000000FF) | ((data[i+1] << 8) & 0x0000FF00);
|
||||
i += 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
read_int32(const char *data, uint &i)
|
||||
{
|
||||
int32_t result = ((data[i+0] << 0x00) & 0x000000FF)
|
||||
| ((data[i+1] << 0x08) & 0x0000FF00)
|
||||
| ((data[i+2] << 0x10) & 0x00FF0000)
|
||||
| ((data[i+3] << 0x18) & 0xFF000000);
|
||||
i += 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_int16(char **data, int16_t value)
|
||||
{
|
||||
*(*data)++ = (value >> 0) & 0xFF;
|
||||
*(*data)++ = (value >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_int32(char **data, int32_t value)
|
||||
{
|
||||
*(*data)++ = (value >> 0x00) & 0xFF;
|
||||
*(*data)++ = (value >> 0x08) & 0xFF;
|
||||
*(*data)++ = (value >> 0x10) & 0xFF;
|
||||
*(*data)++ = (value >> 0x18) & 0xFF;
|
||||
}
|
||||
|
||||
union doubleInt
|
||||
{
|
||||
double d;
|
||||
int64_t i;
|
||||
};
|
||||
|
||||
static inline void
|
||||
write_double(char **data, double value)
|
||||
{
|
||||
doubleInt di;
|
||||
di.d = value;
|
||||
|
||||
*(*data)++ = (di.i >> 0x00) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x08) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x10) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x18) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x20) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x28) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x30) & 0xFF;
|
||||
*(*data)++ = (di.i >> 0x38) & 0xFF;
|
||||
}
|
||||
|
||||
static inline double
|
||||
read_double(const char *data, uint &i)
|
||||
{
|
||||
doubleInt di;
|
||||
di.i = (((int64_t)data[i+0] << 0x00) & 0x00000000000000FF)
|
||||
| (((int64_t)data[i+1] << 0x08) & 0x000000000000FF00)
|
||||
| (((int64_t)data[i+2] << 0x10) & 0x0000000000FF0000)
|
||||
| (((int64_t)data[i+3] << 0x18) & 0x00000000FF000000)
|
||||
| (((int64_t)data[i+4] << 0x20) & 0x000000FF00000000)
|
||||
| (((int64_t)data[i+5] << 0x28) & 0x0000FF0000000000)
|
||||
| (((int64_t)data[i+6] << 0x30) & 0x00FF000000000000)
|
||||
| (((int64_t)data[i+7] << 0x38) & 0xFF00000000000000);
|
||||
i += 8;
|
||||
|
||||
return di.d;
|
||||
}
|
||||
|
||||
#endif // SERIALUTIL_H
|
39
src/serializable.h
Normal file
39
src/serializable.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
** serializable.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERIALIZABLE_H
|
||||
#define SERIALIZABLE_H
|
||||
|
||||
struct Serializable
|
||||
{
|
||||
virtual ~Serializable() {}
|
||||
|
||||
virtual int serialSize() const = 0;
|
||||
virtual void serialize(char *buffer) const = 0;
|
||||
};
|
||||
|
||||
template<class C>
|
||||
C *deserialize(const char *data)
|
||||
{
|
||||
return C::deserialize(data);
|
||||
}
|
||||
|
||||
#endif // SERIALIZABLE_H
|
270
src/shader.cpp
Normal file
270
src/shader.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
** shader.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "shader.h"
|
||||
|
||||
#include "GL/glew.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "../shader/sprite.frag.xxd"
|
||||
#include "../shader/hue.frag.xxd"
|
||||
#include "../shader/trans.frag.xxd"
|
||||
#include "../shader/transSimple.frag.xxd"
|
||||
#include "../shader/bitmapBlit.frag.xxd"
|
||||
|
||||
#define GET_U(name) u_##name = glGetUniformLocation(program, #name)
|
||||
|
||||
FragShader::~FragShader()
|
||||
{
|
||||
glUseProgram(0);
|
||||
glDeleteProgram(program);
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
|
||||
void FragShader::bind()
|
||||
{
|
||||
glUseProgram(program);
|
||||
}
|
||||
|
||||
void FragShader::unbind()
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void FragShader::init(const unsigned char *source, int length)
|
||||
{
|
||||
GLint success;
|
||||
|
||||
shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(shader, 1, (const GLchar**) &source, (const GLint*) &length);
|
||||
glCompileShader(shader);
|
||||
|
||||
glGetObjectParameterivARB(shader, GL_COMPILE_STATUS, &success);
|
||||
Q_ASSERT(success);
|
||||
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, shader);
|
||||
glLinkProgram(program);
|
||||
|
||||
glGetObjectParameterivARB(program, GL_LINK_STATUS, &success);
|
||||
Q_ASSERT(success);
|
||||
}
|
||||
|
||||
void FragShader::initFromFile(const char *filename)
|
||||
{
|
||||
QFile shaderFile(filename);
|
||||
shaderFile.open(QFile::ReadOnly);
|
||||
QByteArray contents = shaderFile.readAll();
|
||||
shaderFile.close();
|
||||
|
||||
init((const unsigned char*) contents.constData(), contents.size());
|
||||
}
|
||||
|
||||
void FragShader::setVec4Uniform(GLint location, const Vec4 &vec)
|
||||
{
|
||||
glUniform4f(location, vec.x, vec.y, vec.z, vec.w);
|
||||
}
|
||||
|
||||
void FragShader::setTexUniform(GLint location, unsigned unitIndex, Tex::ID texture)
|
||||
{
|
||||
GLenum texUnit = GL_TEXTURE0 + unitIndex;
|
||||
|
||||
glActiveTexture(texUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.gl);
|
||||
glUniform1i(location, unitIndex);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
|
||||
TransShader::TransShader()
|
||||
{
|
||||
init(shader_trans_frag,
|
||||
shader_trans_frag_len);
|
||||
|
||||
GET_U(currentScene);
|
||||
GET_U(frozenScene);
|
||||
GET_U(transMap);
|
||||
GET_U(prog);
|
||||
GET_U(vague);
|
||||
}
|
||||
|
||||
void TransShader::setCurrentScene(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_currentScene, 0, tex);
|
||||
}
|
||||
|
||||
void TransShader::setFrozenScene(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_frozenScene, 1, tex);
|
||||
}
|
||||
|
||||
void TransShader::setTransMap(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_transMap, 2, tex);
|
||||
}
|
||||
|
||||
void TransShader::setProg(float value)
|
||||
{
|
||||
glUniform1f(u_prog, value);
|
||||
}
|
||||
|
||||
void TransShader::setVague(float value)
|
||||
{
|
||||
glUniform1f(u_vague, value);
|
||||
}
|
||||
|
||||
|
||||
SimpleTransShader::SimpleTransShader()
|
||||
{
|
||||
init(shader_transSimple_frag,
|
||||
shader_transSimple_frag_len);
|
||||
|
||||
GET_U(currentScene);
|
||||
GET_U(frozenScene);
|
||||
GET_U(prog);
|
||||
}
|
||||
|
||||
void SimpleTransShader::setCurrentScene(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_currentScene, 0, tex);
|
||||
}
|
||||
|
||||
void SimpleTransShader::setFrozenScene(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_frozenScene, 1, tex);
|
||||
}
|
||||
|
||||
void SimpleTransShader::setProg(float value)
|
||||
{
|
||||
glUniform1f(u_prog, value);
|
||||
}
|
||||
|
||||
|
||||
SpriteShader::SpriteShader()
|
||||
{
|
||||
init(shader_sprite_frag,
|
||||
shader_sprite_frag_len);
|
||||
|
||||
GET_U(tone);
|
||||
GET_U(color);
|
||||
GET_U(flash);
|
||||
GET_U(opacity);
|
||||
GET_U(bushDepth);
|
||||
GET_U(bushOpacity);
|
||||
|
||||
bind();
|
||||
resetUniforms();
|
||||
unbind();
|
||||
}
|
||||
|
||||
void SpriteShader::resetUniforms()
|
||||
{
|
||||
setTone(Vec4());
|
||||
setColor(Vec4());
|
||||
setFlash(Vec4());
|
||||
setOpacity(1);
|
||||
setBushDepth(0);
|
||||
setBushOpacity(0.5);
|
||||
}
|
||||
|
||||
void SpriteShader::setTone(const Vec4 &tone)
|
||||
{
|
||||
setVec4Uniform(u_tone, tone);
|
||||
}
|
||||
|
||||
void SpriteShader::setColor(const Vec4 &color)
|
||||
{
|
||||
setVec4Uniform(u_color, color);
|
||||
}
|
||||
|
||||
void SpriteShader::setFlash(const Vec4 &flash)
|
||||
{
|
||||
setVec4Uniform(u_flash, flash);
|
||||
}
|
||||
|
||||
void SpriteShader::setOpacity(float value)
|
||||
{
|
||||
glUniform1f(u_opacity, value);
|
||||
}
|
||||
|
||||
void SpriteShader::setBushDepth(float value)
|
||||
{
|
||||
glUniform1f(u_bushDepth, value);
|
||||
}
|
||||
|
||||
void SpriteShader::setBushOpacity(float value)
|
||||
{
|
||||
glUniform1f(u_bushOpacity, value);
|
||||
}
|
||||
|
||||
|
||||
HueShader::HueShader()
|
||||
{
|
||||
init(shader_hue_frag,
|
||||
shader_hue_frag_len);
|
||||
|
||||
GET_U(hueAdjust);
|
||||
GET_U(inputTexture);
|
||||
}
|
||||
|
||||
void HueShader::setHueAdjust(float value)
|
||||
{
|
||||
glUniform1f(u_hueAdjust, value);
|
||||
}
|
||||
|
||||
void HueShader::setInputTexture(Tex::ID tex)
|
||||
{
|
||||
setTexUniform(u_inputTexture, 0, tex);
|
||||
}
|
||||
|
||||
|
||||
BltShader::BltShader()
|
||||
{
|
||||
init(shader_bitmapBlit_frag,
|
||||
shader_bitmapBlit_frag_len);
|
||||
|
||||
GET_U(source);
|
||||
GET_U(destination);
|
||||
GET_U(subRect);
|
||||
GET_U(opacity);
|
||||
}
|
||||
|
||||
void BltShader::setSource()
|
||||
{
|
||||
glUniform1i(u_source, 0);
|
||||
}
|
||||
|
||||
void BltShader::setDestination(const Tex::ID value)
|
||||
{
|
||||
setTexUniform(u_destination, 1, value);
|
||||
}
|
||||
|
||||
void BltShader::setSubRect(const FloatRect &value)
|
||||
{
|
||||
glUniform4f(u_subRect, value.x, value.y, value.w, value.h);
|
||||
}
|
||||
|
||||
void BltShader::setOpacity(float value)
|
||||
{
|
||||
glUniform1f(u_opacity, value);
|
||||
}
|
120
src/shader.h
Normal file
120
src/shader.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
** shader.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SHADER_H
|
||||
#define SHADER_H
|
||||
|
||||
#include "etc-internal.h"
|
||||
#include "gl-util.h"
|
||||
|
||||
class FragShader
|
||||
{
|
||||
public:
|
||||
void bind();
|
||||
static void unbind();
|
||||
|
||||
protected:
|
||||
~FragShader();
|
||||
|
||||
void init(const unsigned char *source, int length);
|
||||
void initFromFile(const char *filename);
|
||||
|
||||
void setVec4Uniform(GLint location, const Vec4 &vec);
|
||||
void setTexUniform(GLint location, unsigned unitIndex, Tex::ID texture);
|
||||
|
||||
GLuint shader;
|
||||
GLuint program;
|
||||
};
|
||||
|
||||
class TransShader : public FragShader
|
||||
{
|
||||
public:
|
||||
TransShader();
|
||||
|
||||
void setCurrentScene(Tex::ID tex);
|
||||
void setFrozenScene(Tex::ID tex);
|
||||
void setTransMap(Tex::ID tex);
|
||||
void setProg(float value);
|
||||
void setVague(float value);
|
||||
|
||||
private:
|
||||
GLint u_currentScene, u_frozenScene, u_transMap, u_prog, u_vague;
|
||||
};
|
||||
|
||||
class SimpleTransShader : public FragShader
|
||||
{
|
||||
public:
|
||||
SimpleTransShader();
|
||||
|
||||
void setCurrentScene(Tex::ID tex);
|
||||
void setFrozenScene(Tex::ID tex);
|
||||
void setProg(float value);
|
||||
|
||||
private:
|
||||
GLint u_currentScene, u_frozenScene, u_prog;
|
||||
};
|
||||
|
||||
class SpriteShader : public FragShader
|
||||
{
|
||||
public:
|
||||
SpriteShader();
|
||||
|
||||
void resetUniforms();
|
||||
void setTone(const Vec4 &value);
|
||||
void setColor(const Vec4 &value);
|
||||
void setFlash(const Vec4 &value);
|
||||
void setOpacity(float value);
|
||||
void setBushDepth(float value);
|
||||
void setBushOpacity(float value);
|
||||
|
||||
private:
|
||||
GLint u_tone, u_opacity, u_color, u_flash, u_bushDepth, u_bushOpacity;
|
||||
};
|
||||
|
||||
class HueShader : public FragShader
|
||||
{
|
||||
public:
|
||||
HueShader();
|
||||
|
||||
void setHueAdjust(float value);
|
||||
void setInputTexture(Tex::ID tex);
|
||||
|
||||
private:
|
||||
GLint u_hueAdjust, u_inputTexture;
|
||||
};
|
||||
|
||||
/* Bitmap blit */
|
||||
class BltShader : public FragShader
|
||||
{
|
||||
public:
|
||||
BltShader();
|
||||
|
||||
void setSource();
|
||||
void setDestination(const Tex::ID value);
|
||||
void setDestCoorF(const Vec2 &value);
|
||||
void setSubRect(const FloatRect &value);
|
||||
void setOpacity(float value);
|
||||
|
||||
private:
|
||||
GLint u_source, u_destination, u_subRect, u_opacity;
|
||||
};
|
||||
|
||||
#endif // SHADER_H
|
351
src/sprite.cpp
Normal file
351
src/sprite.cpp
Normal file
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
** sprite.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sprite.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "bitmap.h"
|
||||
#include "etc.h"
|
||||
#include "etc-internal.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
#include "transform.h"
|
||||
#include "shader.h"
|
||||
#include "glstate.h"
|
||||
|
||||
#include "sigc++/connection.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
struct SpritePrivate
|
||||
{
|
||||
Bitmap *bitmap;
|
||||
|
||||
Quad quad;
|
||||
Transform trans;
|
||||
|
||||
Rect *srcRect;
|
||||
sigc::connection srcRectCon;
|
||||
|
||||
bool mirrored;
|
||||
int bushDepth;
|
||||
float efBushDepth;
|
||||
NormValue bushOpacity;
|
||||
NormValue opacity;
|
||||
BlendType blendType;
|
||||
|
||||
Color *color;
|
||||
Tone *tone;
|
||||
|
||||
EtcTemps tmp;
|
||||
|
||||
SpritePrivate()
|
||||
: bitmap(0),
|
||||
srcRect(&tmp.rect),
|
||||
mirrored(false),
|
||||
bushDepth(0),
|
||||
efBushDepth(0),
|
||||
bushOpacity(128),
|
||||
opacity(255),
|
||||
blendType(BlendNormal),
|
||||
color(&tmp.color),
|
||||
tone(&tmp.tone)
|
||||
|
||||
{
|
||||
updateSrcRectCon();
|
||||
}
|
||||
|
||||
~SpritePrivate()
|
||||
{
|
||||
srcRectCon.disconnect();
|
||||
}
|
||||
|
||||
void recomputeBushDepth()
|
||||
{
|
||||
/* Calculate effective (normalized) bush depth */
|
||||
float texBushDepth = (bushDepth / trans.getScale().y) -
|
||||
(srcRect->y + srcRect->height) +
|
||||
bitmap->height();
|
||||
|
||||
efBushDepth = 1.0 - texBushDepth / bitmap->height();
|
||||
}
|
||||
|
||||
void onSrcRectChange()
|
||||
{
|
||||
if (mirrored)
|
||||
quad.setTexRect(srcRect->toFloatRect().hFlipped());
|
||||
else
|
||||
quad.setTexRect(srcRect->toFloatRect());
|
||||
|
||||
quad.setPosRect(IntRect(0, 0, srcRect->width, srcRect->height));
|
||||
recomputeBushDepth();
|
||||
}
|
||||
|
||||
void updateSrcRectCon()
|
||||
{
|
||||
/* Cut old connection */
|
||||
srcRectCon.disconnect();
|
||||
/* Create new one */
|
||||
srcRectCon = srcRect->valueChanged.connect
|
||||
(sigc::mem_fun(this, &SpritePrivate::onSrcRectChange));
|
||||
}
|
||||
};
|
||||
|
||||
Sprite::Sprite(Viewport *viewport)
|
||||
: ViewportElement(viewport)
|
||||
{
|
||||
p = new SpritePrivate;
|
||||
onGeometryChange(scene->getGeometry());
|
||||
}
|
||||
|
||||
Sprite::~Sprite()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
#define DISP_CLASS_NAME "sprite"
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Bitmap, Bitmap*, p->bitmap)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, SrcRect, Rect*, p->srcRect)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, X, int, p->trans.getPosition().x)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Y, int, p->trans.getPosition().y)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, OX, int, p->trans.getOrigin().x)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, OY, int, p->trans.getOrigin().y)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, ZoomX, float, p->trans.getScale().x)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, ZoomY, float, p->trans.getScale().y)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Angle, float, p->trans.getRotation())
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Mirror, bool, p->mirrored)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, BushDepth, int, p->bushDepth)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, BlendType, int, p->blendType)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height)
|
||||
|
||||
DEF_ATTR_SIMPLE(Sprite, BushOpacity, int, p->bushOpacity)
|
||||
DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity)
|
||||
DEF_ATTR_SIMPLE(Sprite, Color, Color*, p->color)
|
||||
DEF_ATTR_SIMPLE(Sprite, Tone, Tone*, p->tone)
|
||||
|
||||
void Sprite::setBitmap(Bitmap *bitmap)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->bitmap == bitmap)
|
||||
return;
|
||||
|
||||
p->bitmap = bitmap;
|
||||
*p->srcRect = bitmap->rect();
|
||||
p->onSrcRectChange();
|
||||
p->quad.setPosRect(p->srcRect->toFloatRect());
|
||||
}
|
||||
|
||||
void Sprite::setSrcRect(Rect *rect)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->srcRect == rect)
|
||||
return;
|
||||
|
||||
p->srcRect = rect;
|
||||
p->updateSrcRectCon();
|
||||
|
||||
if (p->bitmap)
|
||||
p->onSrcRectChange();
|
||||
}
|
||||
|
||||
void Sprite::setX(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getPosition().x == value)
|
||||
return;
|
||||
|
||||
p->trans.setPosition(Vec2(value, getY()));
|
||||
}
|
||||
|
||||
void Sprite::setY(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getPosition().y == value)
|
||||
return;
|
||||
|
||||
p->trans.setPosition(Vec2(getX(), value));
|
||||
}
|
||||
|
||||
void Sprite::setOX(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getOrigin().x == value)
|
||||
return;
|
||||
|
||||
p->trans.setOrigin(Vec2(value, getOY()));
|
||||
}
|
||||
|
||||
void Sprite::setOY(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getOrigin().y == value)
|
||||
return;
|
||||
|
||||
p->trans.setOrigin(Vec2(getOX(), value));
|
||||
}
|
||||
|
||||
void Sprite::setZoomX(float value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getScale().x == value)
|
||||
return;
|
||||
|
||||
p->trans.setScale(Vec2(value, getZoomY()));
|
||||
}
|
||||
|
||||
void Sprite::setZoomY(float value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getScale().y == value)
|
||||
return;
|
||||
|
||||
p->trans.setScale(Vec2(getZoomX(), value));
|
||||
p->recomputeBushDepth();
|
||||
}
|
||||
|
||||
void Sprite::setAngle(float value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->trans.getRotation() == value)
|
||||
return;
|
||||
|
||||
p->trans.setRotation(value);
|
||||
}
|
||||
|
||||
void Sprite::setMirror(bool mirrored)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->mirrored == mirrored)
|
||||
return;
|
||||
|
||||
p->mirrored = mirrored;
|
||||
p->onSrcRectChange();
|
||||
}
|
||||
|
||||
void Sprite::setBushDepth(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->bushDepth == value)
|
||||
return;
|
||||
|
||||
p->bushDepth = value;
|
||||
p->recomputeBushDepth();
|
||||
}
|
||||
|
||||
void Sprite::setBlendType(int type)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default :
|
||||
case BlendNormal :
|
||||
p->blendType = BlendNormal;
|
||||
return;
|
||||
case BlendAddition :
|
||||
p->blendType = BlendAddition;
|
||||
return;
|
||||
case BlendSubstraction :
|
||||
p->blendType = BlendSubstraction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disposable */
|
||||
void Sprite::releaseResources()
|
||||
{
|
||||
unlink();
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
/* SceneElement */
|
||||
void Sprite::draw()
|
||||
{
|
||||
if (!p->bitmap)
|
||||
return;
|
||||
|
||||
if (p->bitmap->isDisposed())
|
||||
return;
|
||||
|
||||
if (emptyFlashFlag)
|
||||
return;
|
||||
|
||||
if (p->color->hasEffect() || p->tone->hasEffect() || p->opacity != 255 || flashing || p->bushDepth != 0)
|
||||
{
|
||||
SpriteShader &shader = gState->spriteShader();
|
||||
|
||||
shader.bind();
|
||||
shader.setFlash(Vec4());
|
||||
shader.setTone(p->tone->norm);
|
||||
shader.setOpacity(p->opacity.norm);
|
||||
shader.setBushDepth(p->efBushDepth);
|
||||
shader.setBushOpacity(p->bushOpacity.norm);
|
||||
|
||||
/* When both flashing and effective color are set,
|
||||
* the one with higher alpha will be blended */
|
||||
const Vec4 *blend = (flashing && flashColor.w > p->color->norm.w) ?
|
||||
&flashColor : &p->color->norm;
|
||||
|
||||
shader.setColor(*blend);
|
||||
}
|
||||
|
||||
glState.blendMode.pushSet(p->blendType);
|
||||
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(p->trans.getMatrix());
|
||||
|
||||
p->bitmap->flush();
|
||||
p->bitmap->bindTexWithMatrix();
|
||||
|
||||
p->quad.draw();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glState.blendMode.pop();
|
||||
|
||||
FragShader::unbind();
|
||||
}
|
||||
|
||||
void Sprite::onGeometryChange(const Scene::Geometry &geo)
|
||||
{
|
||||
/* Offset at which the sprite will be drawn
|
||||
* relative to screen origin */
|
||||
int xOffset = geo.rect.x - geo.xOrigin;
|
||||
int yOffset = geo.rect.y - geo.yOrigin;
|
||||
|
||||
p->trans.setGlobalOffset(xOffset, yOffset);
|
||||
}
|
73
src/sprite.h
Normal file
73
src/sprite.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
** sprite.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SPRITE_H
|
||||
#define SPRITE_H
|
||||
|
||||
#include "scene.h"
|
||||
#include "flashable.h"
|
||||
#include "disposable.h"
|
||||
#include "viewport.h"
|
||||
#include "util.h"
|
||||
|
||||
class Bitmap;
|
||||
struct Color;
|
||||
struct Tone;
|
||||
struct Rect;
|
||||
|
||||
struct SpritePrivate;
|
||||
|
||||
class Sprite : public ViewportElement, public Flashable, public Disposable
|
||||
{
|
||||
public:
|
||||
Sprite(Viewport *viewport = 0);
|
||||
~Sprite();
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
DECL_ATTR( Bitmap, Bitmap* )
|
||||
DECL_ATTR( SrcRect, Rect* )
|
||||
DECL_ATTR( X, int )
|
||||
DECL_ATTR( Y, int )
|
||||
DECL_ATTR( OX, int )
|
||||
DECL_ATTR( OY, int )
|
||||
DECL_ATTR( ZoomX, float )
|
||||
DECL_ATTR( ZoomY, float )
|
||||
DECL_ATTR( Angle, float )
|
||||
DECL_ATTR( Mirror, bool )
|
||||
DECL_ATTR( BushDepth, int )
|
||||
DECL_ATTR( BushOpacity, int )
|
||||
DECL_ATTR( Opacity, int )
|
||||
DECL_ATTR( BlendType, int )
|
||||
DECL_ATTR( Color, Color* )
|
||||
DECL_ATTR( Tone, Tone* )
|
||||
|
||||
private:
|
||||
SpritePrivate *p;
|
||||
|
||||
void draw();
|
||||
void onGeometryChange(const Scene::Geometry &);
|
||||
|
||||
void releaseResources();
|
||||
};
|
||||
|
||||
#endif // SPRITE_H
|
211
src/table.cpp
Normal file
211
src/table.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
** table.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "table.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "serial-util.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/* Init normally */
|
||||
Table::Table(int x, int y /*= 1*/, int z /*= 1*/)
|
||||
:m_x(x), m_y(y), m_z(z)
|
||||
{
|
||||
data = static_cast<int16_t*>(calloc(x * y * z, sizeof(int16_t)));
|
||||
}
|
||||
|
||||
Table::~Table()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
int16_t Table::get(int x, int y, int z) const
|
||||
{
|
||||
return data[m_x*m_y*z + m_x*y + x];
|
||||
}
|
||||
|
||||
void Table::set(int16_t value, int x, int y, int z)
|
||||
{
|
||||
data[m_x*m_y*z + m_x*y + x] = value;
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
void Table::resize(int x, int y, int z)
|
||||
{
|
||||
if (x == m_x && y == m_y && z == m_z)
|
||||
return;
|
||||
|
||||
// Fastpath: only z changed
|
||||
if (x == m_x && y == m_y)
|
||||
{
|
||||
data = static_cast<int16_t*>(realloc(data, m_x * m_y * z * sizeof(int16_t)));
|
||||
int diff = z - m_z;
|
||||
if (diff > 0)
|
||||
memset(data + (m_x * m_y * m_z), 0, diff * m_x * m_y * sizeof(int16_t));
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
int16_t *newData = static_cast<int16_t*>(calloc(x * y * z, sizeof(int16_t)));
|
||||
|
||||
for (int i = 0; i < MIN(x, m_x); ++i)
|
||||
for (int j = 0; j < MIN(y, m_y); ++j)
|
||||
for (int k = 0; k < MIN(z, m_z); k++)
|
||||
{
|
||||
int index = x*y*k + x*j + i;
|
||||
newData[index] = at(i, j, k);
|
||||
}
|
||||
|
||||
free(data);
|
||||
data = newData;
|
||||
}
|
||||
|
||||
done:
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Table::resize(int x, int y)
|
||||
{
|
||||
if (x == m_x && y == m_y)
|
||||
return;
|
||||
|
||||
// Fastpath: treat table as two dimensional
|
||||
if (m_z == 1)
|
||||
{
|
||||
// Fastpath: only y changed
|
||||
if (x == m_x)
|
||||
{
|
||||
data = static_cast<int16_t*>(realloc(data, m_x * y * sizeof(int16_t)));
|
||||
int diff = y - m_y;
|
||||
if (diff > 0)
|
||||
memset(data + (m_x * m_y), 0, diff * m_x * sizeof(int16_t));
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
int16_t *newData = static_cast<int16_t*>(calloc(x * y, sizeof(int16_t)));
|
||||
|
||||
for (int i = 0; i < MIN(x, m_x); ++i)
|
||||
for (int j = 0; j < MIN(y, m_y); ++j)
|
||||
{
|
||||
int index = x*j + i;
|
||||
newData[index] = at(i, j);
|
||||
}
|
||||
|
||||
free(data);
|
||||
data = newData;
|
||||
}
|
||||
|
||||
done:
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
resize(x, y, m_z);
|
||||
}
|
||||
}
|
||||
|
||||
void Table::resize(int x)
|
||||
{
|
||||
if (x == m_x)
|
||||
return;
|
||||
|
||||
// Fastpath: treat table as one dimensional
|
||||
if (m_y == 1 && m_z == 1)
|
||||
{
|
||||
data = static_cast<int16_t*>(realloc(data, x * sizeof(int16_t)));
|
||||
int diff = x - m_x;
|
||||
if (diff > 0)
|
||||
memset(data + (m_x), 0, diff * sizeof(int16_t));
|
||||
|
||||
m_x = x;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(x, m_y);
|
||||
}
|
||||
|
||||
/* Serializable */
|
||||
int Table::serialSize() const
|
||||
{
|
||||
/* header + data */
|
||||
return 20 + (m_x * m_y * m_z) * 2;
|
||||
}
|
||||
|
||||
void Table::serialize(char *buffer) const
|
||||
{
|
||||
char *buff_p = buffer;
|
||||
|
||||
write_int32(&buff_p, 3);
|
||||
write_int32(&buff_p, m_x);
|
||||
write_int32(&buff_p, m_y);
|
||||
write_int32(&buff_p, m_z);
|
||||
write_int32(&buff_p, m_x * m_y * m_z);
|
||||
|
||||
for (int i = 0; i < m_z; ++i)
|
||||
for (int j = 0; j < m_y; ++j)
|
||||
for (int k = 0; k < m_x; k++)
|
||||
write_int16(&buff_p, at(k, j, i));
|
||||
}
|
||||
|
||||
|
||||
Table *Table::deserialize(const char *data, int len)
|
||||
{
|
||||
if (len < 20)
|
||||
throw Exception(Exception::RGSSError, "Marshal: Table: bad file format");
|
||||
|
||||
uint idx = 0;
|
||||
|
||||
read_int32(data, idx);
|
||||
int x = read_int32(data, idx);
|
||||
int y = read_int32(data, idx);
|
||||
int z = read_int32(data, idx);
|
||||
int size = read_int32(data, idx);
|
||||
|
||||
if (size != x*y*z)
|
||||
throw Exception(Exception::RGSSError, "Marshal: Table: bad file format");
|
||||
|
||||
if (len != 20 + x*y*z*2)
|
||||
throw Exception(Exception::RGSSError, "Marshal: Table: bad file format");
|
||||
|
||||
Table *t = new Table(x, y, z);
|
||||
|
||||
for (int i = 0; i < z; ++i)
|
||||
for (int j = 0; j < y; ++j)
|
||||
for (int k = 0; k < x; k++)
|
||||
t->at(k, j, i) = read_int16(data, idx);
|
||||
|
||||
return t;
|
||||
}
|
73
src/table.h
Normal file
73
src/table.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
** table.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_H
|
||||
#define TABLE_H
|
||||
|
||||
#include "serializable.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sigc++/signal.h"
|
||||
|
||||
class Table : public Serializable
|
||||
{
|
||||
public:
|
||||
Table(int x, int y = 1, int z = 1);
|
||||
~Table();
|
||||
|
||||
int xSize() const { return m_x; }
|
||||
int ySize() const { return m_y; }
|
||||
int zSize() const { return m_z; }
|
||||
|
||||
int16_t get(int x, int y = 0, int z = 0) const;
|
||||
void set(int16_t value, int x, int y = 0, int z = 0);
|
||||
|
||||
inline int16_t &at(int x, int y = 0, int z = 0)
|
||||
{
|
||||
int16_t &value = data[m_x*m_y*z + m_x*y + x];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int16_t &at(int x, int y = 0, int z = 0) const
|
||||
{
|
||||
int16_t &value = data[m_x*m_y*z + m_x*y + x];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void resize(int x, int y, int z);
|
||||
void resize(int x, int y);
|
||||
void resize(int x);
|
||||
|
||||
int serialSize() const;
|
||||
void serialize(char *buffer) const;
|
||||
static Table *deserialize(const char *data, int len);
|
||||
|
||||
sigc::signal<void> modified;
|
||||
|
||||
private:
|
||||
int m_x, m_y, m_z;
|
||||
|
||||
int16_t *data;
|
||||
};
|
||||
|
||||
#endif // TABLE_H
|
204
src/texpool.cpp
Normal file
204
src/texpool.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
** texpool.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "texpool.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QQueue>
|
||||
#include <QList>
|
||||
#include <QLinkedList>
|
||||
#include <QPair>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
typedef QPair<quint16, quint16> Size;
|
||||
typedef QQueue<TexFBO> ObjList;
|
||||
|
||||
static quint32 byteCount(Size &s)
|
||||
{
|
||||
return s.first * s.second * 4;
|
||||
}
|
||||
|
||||
struct CacheObject
|
||||
{
|
||||
TexFBO obj;
|
||||
QLinkedList<TexFBO>::iterator prioIter;
|
||||
|
||||
bool operator==(const CacheObject &o)
|
||||
{
|
||||
return obj == o.obj;
|
||||
}
|
||||
};
|
||||
|
||||
typedef QList<CacheObject> CObjList;
|
||||
|
||||
struct TexPoolPrivate
|
||||
{
|
||||
/* Contains all cached TexFBOs, grouped by size */
|
||||
QHash<Size, CObjList> poolHash;
|
||||
|
||||
/* Contains all cached TexFBOs, sorted by release time */
|
||||
QLinkedList<TexFBO> priorityQueue;
|
||||
|
||||
/* Maximal allowed cache memory */
|
||||
const quint32 maxMemSize;
|
||||
|
||||
/* Current amound of memory consumed by the cache */
|
||||
quint32 memSize;
|
||||
|
||||
/* Current amount of TexFBOs cached */
|
||||
quint16 objCount;
|
||||
|
||||
/* Has this pool been disabled? */
|
||||
bool disabled;
|
||||
|
||||
TexPoolPrivate(quint32 maxMemSize)
|
||||
: maxMemSize(maxMemSize),
|
||||
memSize(0),
|
||||
objCount(0),
|
||||
disabled(false)
|
||||
{}
|
||||
};
|
||||
|
||||
TexPool::TexPool(quint32 maxMemSize)
|
||||
{
|
||||
p = new TexPoolPrivate(maxMemSize);
|
||||
}
|
||||
|
||||
TexPool::~TexPool()
|
||||
{
|
||||
while (!p->priorityQueue.isEmpty())
|
||||
{
|
||||
TexFBO obj = p->priorityQueue.takeFirst();
|
||||
TexFBO::fini(obj);
|
||||
--p->objCount;
|
||||
}
|
||||
|
||||
Q_ASSERT(p->objCount == 0);
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
TexFBO TexPool::request(int width, int height)
|
||||
{
|
||||
CacheObject cobj;
|
||||
Size size(width, height);
|
||||
|
||||
/* See if we can statisfy request from cache */
|
||||
CObjList &bucket = p->poolHash[size];
|
||||
|
||||
if (!bucket.isEmpty())
|
||||
{
|
||||
/* Found one! */
|
||||
cobj = bucket.takeLast();
|
||||
|
||||
//p->priorityQueue.removeOne(obj);
|
||||
p->priorityQueue.erase(cobj.prioIter);
|
||||
|
||||
p->memSize -= byteCount(size);
|
||||
--p->objCount;
|
||||
|
||||
// qDebug() << "TexPool: <?+> (" << width << height << ")";
|
||||
|
||||
return cobj.obj;
|
||||
}
|
||||
|
||||
// FIXME check here that requested dimensions don't exceed OpenGL limits
|
||||
|
||||
/* Nope, create it instead */
|
||||
TexFBO::init(cobj.obj);
|
||||
TexFBO::allocEmpty(cobj.obj, width, height);
|
||||
TexFBO::linkFBO(cobj.obj);
|
||||
|
||||
// qDebug() << "TexPool: <?-> (" << width << height << ")";
|
||||
|
||||
return cobj.obj;
|
||||
}
|
||||
|
||||
void TexPool::release(TexFBO &obj)
|
||||
{
|
||||
if (obj.tex == Tex::ID(0) || obj.fbo == FBO::ID(0))
|
||||
{
|
||||
TexFBO::fini(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->disabled)
|
||||
{
|
||||
/* If we're disabled, delete without caching */
|
||||
// qDebug() << "TexPool: <!#> (" << obj.width << obj.height << ")";
|
||||
TexFBO::fini(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
Size size(obj.width, obj.height);
|
||||
|
||||
quint32 newMemSize = p->memSize + byteCount(size);
|
||||
|
||||
/* If caching this object would spill over the allowed memory budget,
|
||||
* delete least used objects until we're good again */
|
||||
while (newMemSize > p->maxMemSize)
|
||||
{
|
||||
if (p->objCount == 0)
|
||||
break;
|
||||
|
||||
// qDebug() << "TexPool: <!~> Size:" << p->memSize;
|
||||
|
||||
/* Retrieve object with lowest priority for deletion */
|
||||
CacheObject last;
|
||||
last.obj = p->priorityQueue.last();
|
||||
Size removedSize(last.obj.width, last.obj.height);
|
||||
|
||||
CObjList &bucket = p->poolHash[removedSize];
|
||||
Q_ASSERT(bucket.contains(last));
|
||||
bucket.removeOne(last);
|
||||
p->priorityQueue.removeLast();
|
||||
|
||||
TexFBO::fini(last.obj);
|
||||
|
||||
quint32 removedMem = byteCount(removedSize);
|
||||
newMemSize -= removedMem;
|
||||
p->memSize -= removedMem;
|
||||
--p->objCount;
|
||||
|
||||
// qDebug() << "TexPool: <!-> (" << last.obj.tex << last.obj.fbo << ")";
|
||||
}
|
||||
|
||||
/* Retain object */
|
||||
p->priorityQueue.prepend(obj);
|
||||
CacheObject cobj;
|
||||
cobj.obj = obj;
|
||||
cobj.prioIter = p->priorityQueue.begin();
|
||||
CObjList &bucket = p->poolHash[size];
|
||||
bucket.append(cobj);
|
||||
|
||||
p->memSize += byteCount(size);
|
||||
++p->objCount;
|
||||
|
||||
// qDebug() << "TexPool: <!+> (" << obj.width << obj.height << ") Current size:" << p->memSize;
|
||||
}
|
||||
|
||||
void TexPool::disable()
|
||||
{
|
||||
p->disabled = true;
|
||||
}
|
||||
|
||||
|
44
src/texpool.h
Normal file
44
src/texpool.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
** texpool.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TEXPOOL_H
|
||||
#define TEXPOOL_H
|
||||
|
||||
#include "gl-util.h"
|
||||
|
||||
struct TexPoolPrivate;
|
||||
|
||||
class TexPool
|
||||
{
|
||||
public:
|
||||
TexPool(quint32 maxMemSize = 20000000 /* 20 MB */);
|
||||
~TexPool();
|
||||
|
||||
TexFBO request(int width, int height);
|
||||
void release(TexFBO &obj);
|
||||
|
||||
void disable();
|
||||
|
||||
private:
|
||||
TexPoolPrivate *p;
|
||||
};
|
||||
|
||||
#endif // TEXPOOL_H
|
1216
src/tilemap.cpp
Normal file
1216
src/tilemap.cpp
Normal file
File diff suppressed because it is too large
Load diff
71
src/tilemap.h
Normal file
71
src/tilemap.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
** tilemap.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TILEMAP_H
|
||||
#define TILEMAP_H
|
||||
|
||||
#include "disposable.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
class Viewport;
|
||||
class Bitmap;
|
||||
class Table;
|
||||
|
||||
struct TilemapPrivate;
|
||||
|
||||
class Tilemap : public Disposable
|
||||
{
|
||||
public:
|
||||
class Autotiles
|
||||
{
|
||||
public:
|
||||
void set(int i, Bitmap *bitmap);
|
||||
Bitmap *get(int i) const;
|
||||
|
||||
private:
|
||||
TilemapPrivate *p;
|
||||
friend class Tilemap;
|
||||
};
|
||||
|
||||
Tilemap(Viewport *viewport = 0);
|
||||
~Tilemap();
|
||||
|
||||
void update();
|
||||
|
||||
Autotiles &getAutotiles() const;
|
||||
|
||||
DECL_ATTR( Viewport, Viewport* )
|
||||
DECL_ATTR( Tileset, Bitmap* )
|
||||
DECL_ATTR( MapData, Table* )
|
||||
DECL_ATTR( FlashData, Table* )
|
||||
DECL_ATTR( Priorities, Table* )
|
||||
DECL_ATTR( Visible, bool )
|
||||
DECL_ATTR( OX, int )
|
||||
DECL_ATTR( OY, int )
|
||||
|
||||
private:
|
||||
TilemapPrivate *p;
|
||||
|
||||
void releaseResources();
|
||||
};
|
||||
|
||||
#endif // TILEMAP_H
|
214
src/tilequad.cpp
Normal file
214
src/tilequad.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
** tilequad.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tilequad.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
|
||||
namespace TileQuads
|
||||
{
|
||||
|
||||
int oneDimCount(int tileDimension,
|
||||
int destDimension)
|
||||
{
|
||||
if (tileDimension <= 0)
|
||||
return 0;
|
||||
|
||||
int fullCount = destDimension / tileDimension;
|
||||
int partSize = destDimension % tileDimension;
|
||||
|
||||
return fullCount + (partSize ? 1 : 0);
|
||||
}
|
||||
|
||||
int twoDimCount(int tileW, int tileH,
|
||||
int destW, int destH)
|
||||
{
|
||||
return oneDimCount(tileW, destW) *
|
||||
oneDimCount(tileH, destH);
|
||||
}
|
||||
|
||||
int buildH(const IntRect &sourceRect,
|
||||
int width, int x, int y,
|
||||
Vertex *verts)
|
||||
{
|
||||
if (width <= 0)
|
||||
return 0;
|
||||
|
||||
int fullCount = width / sourceRect.w;
|
||||
int partSize = width % sourceRect.w;
|
||||
|
||||
FloatRect _sourceRect(sourceRect);
|
||||
FloatRect destRect(x, y, sourceRect.w, sourceRect.h);
|
||||
|
||||
/* Full size quads */
|
||||
for (int x = 0; x < fullCount; ++x)
|
||||
{
|
||||
Vertex *vert = &verts[x*4];
|
||||
|
||||
Quad::setTexRect(vert, _sourceRect);
|
||||
Quad::setPosRect(vert, destRect);
|
||||
|
||||
destRect.x += sourceRect.w;
|
||||
}
|
||||
|
||||
if (partSize)
|
||||
{
|
||||
Vertex *vert = &verts[fullCount*4];
|
||||
|
||||
_sourceRect.w = partSize;
|
||||
destRect.w = partSize;
|
||||
|
||||
Quad::setTexRect(vert, _sourceRect);
|
||||
Quad::setPosRect(vert, destRect);
|
||||
}
|
||||
|
||||
return fullCount + (partSize ? 1 : 0);
|
||||
}
|
||||
|
||||
int buildV(const IntRect &sourceRect,
|
||||
int height, int ox, int oy,
|
||||
Vertex *verts)
|
||||
{
|
||||
if (height <= 0)
|
||||
return 0;
|
||||
|
||||
int fullCount = height / sourceRect.h;
|
||||
int partSize = height % sourceRect.h;
|
||||
|
||||
FloatRect _sourceRect(sourceRect);
|
||||
FloatRect destRect(ox, oy, sourceRect.w, sourceRect.h);
|
||||
|
||||
/* Full size quads */
|
||||
for (int y = 0; y < fullCount; ++y)
|
||||
{
|
||||
Vertex *vert = &verts[y*4];
|
||||
|
||||
Quad::setTexRect(vert, _sourceRect);
|
||||
Quad::setPosRect(vert, destRect);
|
||||
|
||||
destRect.y += sourceRect.h;
|
||||
}
|
||||
|
||||
if (partSize)
|
||||
{
|
||||
Vertex *vert = &verts[fullCount*4];
|
||||
|
||||
_sourceRect.h = partSize;
|
||||
destRect.h = partSize;
|
||||
|
||||
Quad::setTexRect(vert, _sourceRect);
|
||||
Quad::setPosRect(vert, destRect);
|
||||
}
|
||||
|
||||
return fullCount + (partSize ? 1 : 0);
|
||||
}
|
||||
|
||||
int build(const IntRect &sourceRect,
|
||||
const IntRect &destRect,
|
||||
Vertex *verts)
|
||||
{
|
||||
int ox = destRect.x;
|
||||
int oy = destRect.y;
|
||||
int width = destRect.w;
|
||||
int height = destRect.h;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return 0;
|
||||
|
||||
int fullCount = height / sourceRect.h;
|
||||
int partSize = height % sourceRect.h;
|
||||
|
||||
int rowTileCount = oneDimCount(sourceRect.w, width);
|
||||
|
||||
int qCount = 0;
|
||||
|
||||
int v = 0;
|
||||
for (int i = 0; i < fullCount; ++i)
|
||||
{
|
||||
qCount += buildH(sourceRect, width, ox, oy, &verts[v]);
|
||||
|
||||
v += rowTileCount*4;
|
||||
oy += sourceRect.h;
|
||||
}
|
||||
|
||||
if (partSize)
|
||||
{
|
||||
IntRect partSourceRect = sourceRect;
|
||||
partSourceRect.h = partSize;
|
||||
|
||||
qCount += buildH(partSourceRect, width, ox, oy, &verts[v]);
|
||||
}
|
||||
|
||||
return qCount;
|
||||
}
|
||||
|
||||
static void buildFrameInt(const IntRect &rect,
|
||||
FloatRect *quadRects /* 9 big */)
|
||||
{
|
||||
int w = rect.w; int h = rect.h;
|
||||
int x1 = rect.x; int x2 = x1 + w;
|
||||
int y1 = rect.y; int y2 = y1 + h;
|
||||
|
||||
int i = 0;
|
||||
/* Corners - tl, tr, br, bl */
|
||||
quadRects[i++] = FloatRect(x1, y1, 2, 2);
|
||||
quadRects[i++] = FloatRect(x2-2, y1, 2, 2);
|
||||
quadRects[i++] = FloatRect(x2-2, y2-2, 2, 2);
|
||||
quadRects[i++] = FloatRect(x1, y2-2, 2, 2);
|
||||
|
||||
/* Sides - l, r, t, b */
|
||||
quadRects[i++] = FloatRect(x1, y1+2, 2, h-4);
|
||||
quadRects[i++] = FloatRect(x2-2, y1+2, 2, h-4);
|
||||
quadRects[i++] = FloatRect(x1+2, y1, w-4, 2);
|
||||
quadRects[i++] = FloatRect(x1+2, y2-2, w-4, 2);
|
||||
|
||||
/* Center */
|
||||
quadRects[i++] = FloatRect(x1+2, y1+2, w-4, h-4);
|
||||
}
|
||||
|
||||
int buildFrameSource(const IntRect &rect,
|
||||
Vertex *vert /* 36 big */)
|
||||
{
|
||||
FloatRect quadRects[9];
|
||||
|
||||
buildFrameInt(rect, quadRects);
|
||||
|
||||
for (int i = 0; i < 9; ++i)
|
||||
Quad::setTexRect(&vert[i*4], quadRects[i]);
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
int buildFrame(const IntRect &rect,
|
||||
Vertex *vert /* 36 big */)
|
||||
{
|
||||
FloatRect quadRects[9];
|
||||
|
||||
buildFrameInt(rect, quadRects);
|
||||
|
||||
for (int i = 0; i < 9; ++i)
|
||||
Quad::setPosRect(&vert[i*4], quadRects[i]);
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
}
|
67
src/tilequad.h
Normal file
67
src/tilequad.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
** tilequad.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TILEQUAD_H
|
||||
#define TILEQUAD_H
|
||||
|
||||
#include "etc-internal.h"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
/* Tiled Quads
|
||||
*
|
||||
* These functions enable drawing a tiled subrectangle
|
||||
* of a texture,
|
||||
* but no advanced stuff like rotation, scaling etc.
|
||||
*/
|
||||
|
||||
#include "quadarray.h"
|
||||
|
||||
namespace TileQuads
|
||||
{
|
||||
/* Calculate needed quad counts */
|
||||
int oneDimCount(int tileDimension,
|
||||
int destDimension);
|
||||
int twoDimCount(int tileW, int tileH,
|
||||
int destW, int destH);
|
||||
|
||||
/* Build tiling quads */
|
||||
int buildH(const IntRect &sourceRect,
|
||||
int width, int x, int y,
|
||||
Vertex *verts);
|
||||
|
||||
int buildV(const IntRect &sourceRect,
|
||||
int height, int ox, int oy,
|
||||
Vertex *verts);
|
||||
|
||||
int build(const IntRect &sourceRect,
|
||||
const IntRect &destRect,
|
||||
Vertex *verts);
|
||||
|
||||
/* Build a quad "frame" (see Window cursor_rect) */
|
||||
int buildFrame(const IntRect &rect,
|
||||
Vertex *vert /* 36 big */);
|
||||
|
||||
int buildFrameSource(const IntRect &rect,
|
||||
Vertex *vert /* 36 big */);
|
||||
}
|
||||
|
||||
#endif // TILEQUAD_H
|
140
src/transform.h
Normal file
140
src/transform.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* transform.h
|
||||
*
|
||||
* This file is part of mkxp. It is based on parts of "SFML 2.0"
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SFML - Simple and Fast Multimedia Library
|
||||
// Copyright (C) 2007-2012 Laurent Gomila (laurent.gom@gmail.com)
|
||||
//
|
||||
// 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 TRANSFORM_H
|
||||
#define TRANSFORM_H
|
||||
|
||||
#include "etc-internal.h"
|
||||
#include "math.h"
|
||||
#include "string.h"
|
||||
|
||||
class Transform
|
||||
{
|
||||
public:
|
||||
Transform()
|
||||
: scale(1, 1),
|
||||
rotation(0),
|
||||
xOffset(0), yOffset(0),
|
||||
dirty(true)
|
||||
{
|
||||
memset(matrix, 0, sizeof(matrix));
|
||||
|
||||
matrix[10] = 1;
|
||||
matrix[15] = 1;
|
||||
}
|
||||
|
||||
Vec2 &getPosition() { return position; }
|
||||
Vec2 &getScale() { return scale; }
|
||||
Vec2 &getOrigin() { return origin; }
|
||||
float getRotation() { return rotation; }
|
||||
|
||||
void setPosition(const Vec2 &value)
|
||||
{
|
||||
position = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void setScale(const Vec2 &value)
|
||||
{
|
||||
scale = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void setOrigin(const Vec2 &value)
|
||||
{
|
||||
origin = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void setRotation(float value)
|
||||
{
|
||||
rotation = value;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void setGlobalOffset(int x, int y)
|
||||
{
|
||||
xOffset = x;
|
||||
yOffset = y;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
const float *getMatrix()
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
updateMatrix();
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private:
|
||||
void updateMatrix()
|
||||
{
|
||||
if (rotation >= 360 || rotation < -360)
|
||||
rotation = (float) fmod(rotation, 360);
|
||||
if (rotation < 0)
|
||||
rotation += 360;
|
||||
|
||||
float angle = rotation * 3.141592654f / 180.f;
|
||||
float cosine = (float) cos(angle);
|
||||
float sine = (float) sin(angle);
|
||||
float sxc = scale.x * cosine;
|
||||
float syc = scale.y * cosine;
|
||||
float sxs = scale.x * sine;
|
||||
float sys = scale.y * sine;
|
||||
float tx = -origin.x * sxc - origin.y * sys + position.x + xOffset;
|
||||
float ty = origin.x * sxs - origin.y * syc + position.y + yOffset;
|
||||
|
||||
matrix[0] = sxc;
|
||||
matrix[1] = -sxs;
|
||||
matrix[4] = sys;
|
||||
matrix[5] = syc;
|
||||
matrix[12] = tx;
|
||||
matrix[13] = ty;
|
||||
}
|
||||
|
||||
Vec2 position;
|
||||
Vec2 origin;
|
||||
Vec2 scale;
|
||||
float rotation;
|
||||
|
||||
/* Silently added to position */
|
||||
int xOffset, yOffset;
|
||||
|
||||
float matrix[16];
|
||||
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
#endif // TRANSFORM_H
|
112
src/util.h
Normal file
112
src/util.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
** util.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
static inline int
|
||||
wrapRange(int value, int min, int max)
|
||||
{
|
||||
if (value >= min && value <= max)
|
||||
return value;
|
||||
|
||||
while (value < min)
|
||||
value += (max - min);
|
||||
|
||||
return value % (max - min);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T bound(T value, T min, T max)
|
||||
{
|
||||
if (value < min)
|
||||
return min;
|
||||
|
||||
if (value > max)
|
||||
return max;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T min(T value1, T value2)
|
||||
{
|
||||
return (value1 < value2) ? value1 : value2;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T max(T value1, T value2)
|
||||
{
|
||||
return (value1 > value2) ? value1 : value2;
|
||||
}
|
||||
|
||||
static inline int
|
||||
findNextPow2(int start)
|
||||
{
|
||||
int i = 1;
|
||||
while (i < start)
|
||||
i <<= 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(obj) (sizeof(obj) / sizeof((obj)[0]))
|
||||
|
||||
#define elementsN(obj) const int obj##N = ARRAY_SIZE(obj)
|
||||
|
||||
#define DECL_ATTR_DETAILED(name, type, keyword1, keyword2) \
|
||||
keyword1 type get##name() keyword2; \
|
||||
keyword1 void set##name(type value);
|
||||
|
||||
#define DECL_ATTR(name, type) DECL_ATTR_DETAILED(name, type, , const)
|
||||
#define DECL_ATTR_VIRT(name, type) DECL_ATTR_DETAILED(name, type, virtual, const)
|
||||
#define DECL_ATTR_STATIC(name, type) DECL_ATTR_DETAILED(name, type, static, )
|
||||
#define DECL_ATTR_INLINE(name, type, loc) \
|
||||
type get##name() const { return loc; } \
|
||||
void set##name(type value) { loc = value; }
|
||||
|
||||
/* Undef this if not needed */
|
||||
#define CHK_DISP GUARD_DISPOSED
|
||||
|
||||
#define DEF_ATTR_RD_SIMPLE_DETAILED(klass, name, type, location, keyword1) \
|
||||
type klass :: get##name() keyword1 \
|
||||
{ \
|
||||
CHK_DISP \
|
||||
return location; \
|
||||
}
|
||||
|
||||
#define DEF_ATTR_SIMPLE_DETAILED(klass, name, type, location, keyword1) \
|
||||
DEF_ATTR_RD_SIMPLE_DETAILED(klass, name, type, location, keyword1) \
|
||||
void klass :: set##name(type value) \
|
||||
{ \
|
||||
CHK_DISP \
|
||||
location = value; \
|
||||
}
|
||||
|
||||
#define DEF_ATTR_RD_SIMPLE(klass, name, type, location) \
|
||||
DEF_ATTR_RD_SIMPLE_DETAILED(klass, name, type, location, const)
|
||||
#define DEF_ATTR_SIMPLE(klass, name, type, location) \
|
||||
DEF_ATTR_SIMPLE_DETAILED(klass, name, type, location, const)
|
||||
|
||||
#define DEF_ATTR_SIMPLE_STATIC(klass, name, type, location) \
|
||||
DEF_ATTR_SIMPLE_DETAILED(klass, name, type, location, )
|
||||
|
||||
#endif // UTIL_H
|
245
src/viewport.cpp
Normal file
245
src/viewport.cpp
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
** viewport.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "viewport.h"
|
||||
|
||||
#include "globalstate.h"
|
||||
#include "etc.h"
|
||||
#include "util.h"
|
||||
#include "quad.h"
|
||||
#include "glstate.h"
|
||||
|
||||
#include "SDL2/SDL_rect.h"
|
||||
|
||||
#include "sigc++/connection.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
struct ViewportPrivate
|
||||
{
|
||||
/* Needed for geometry changes */
|
||||
Viewport *self;
|
||||
|
||||
Rect *rect;
|
||||
sigc::connection rectCon;
|
||||
|
||||
Color *color;
|
||||
Tone *tone;
|
||||
|
||||
IntRect screenRect;
|
||||
int isOnScreen;
|
||||
|
||||
EtcTemps tmp;
|
||||
|
||||
ViewportPrivate(int x, int y, int width, int height, Viewport *self)
|
||||
: self(self),
|
||||
rect(&tmp.rect),
|
||||
color(&tmp.color),
|
||||
tone(&tmp.tone),
|
||||
isOnScreen(false)
|
||||
{
|
||||
rect->set(x, y, width, height);
|
||||
updateRectCon();
|
||||
}
|
||||
|
||||
~ViewportPrivate()
|
||||
{
|
||||
rectCon.disconnect();
|
||||
}
|
||||
|
||||
void onRectChange()
|
||||
{
|
||||
self->geometry.rect = rect->toIntRect();
|
||||
self->notifyGeometryChange();
|
||||
recomputeOnScreen();
|
||||
}
|
||||
|
||||
void updateRectCon()
|
||||
{
|
||||
rectCon.disconnect();
|
||||
rectCon = rect->valueChanged.connect
|
||||
(sigc::mem_fun(this, &ViewportPrivate::onRectChange));
|
||||
}
|
||||
|
||||
void recomputeOnScreen()
|
||||
{
|
||||
SDL_Rect r1 = { screenRect.x, screenRect.y,
|
||||
screenRect.w, screenRect.h };
|
||||
|
||||
SDL_Rect r2 = { rect->x, rect->y,
|
||||
rect->width, rect->height };
|
||||
|
||||
SDL_Rect result;
|
||||
isOnScreen = SDL_IntersectRect(&r1, &r2, &result);
|
||||
}
|
||||
|
||||
bool needsEffectRender(bool flashing)
|
||||
{
|
||||
bool rectEffective = !rect->isEmpty();
|
||||
bool colorToneEffective = color->hasEffect() || tone->hasEffect() || flashing;
|
||||
|
||||
return (rectEffective && colorToneEffective && isOnScreen);
|
||||
}
|
||||
};
|
||||
|
||||
Viewport::Viewport(int x, int y, int width, int height)
|
||||
: SceneElement(*gState->screen()),
|
||||
sceneLink(this)
|
||||
{
|
||||
initViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
Viewport::Viewport(Rect *rect)
|
||||
: SceneElement(*gState->screen()),
|
||||
sceneLink(this)
|
||||
{
|
||||
initViewport(rect->x, rect->y, rect->width, rect->height);
|
||||
}
|
||||
|
||||
void Viewport::initViewport(int x, int y, int width, int height)
|
||||
{
|
||||
p = new ViewportPrivate(x, y, width, height, this);
|
||||
|
||||
/* Set our own geometry */
|
||||
geometry.rect = IntRect(x, y, width, height);
|
||||
|
||||
/* Handle parent geometry */
|
||||
onGeometryChange(scene->getGeometry());
|
||||
}
|
||||
|
||||
Viewport::~Viewport()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
#define DISP_CLASS_NAME "viewport"
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Viewport, OX, int, geometry.xOrigin)
|
||||
DEF_ATTR_RD_SIMPLE(Viewport, OY, int, geometry.yOrigin)
|
||||
DEF_ATTR_RD_SIMPLE(Viewport, Rect, Rect*, p->rect)
|
||||
|
||||
DEF_ATTR_SIMPLE(Viewport, Color, Color*, p->color)
|
||||
DEF_ATTR_SIMPLE(Viewport, Tone, Tone*, p->tone)
|
||||
|
||||
void Viewport::setOX(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (geometry.xOrigin == value)
|
||||
return;
|
||||
|
||||
geometry.xOrigin = value;
|
||||
notifyGeometryChange();
|
||||
}
|
||||
|
||||
void Viewport::setOY(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (geometry.yOrigin == value)
|
||||
return;
|
||||
|
||||
geometry.yOrigin = value;
|
||||
notifyGeometryChange();
|
||||
}
|
||||
|
||||
void Viewport::setRect(Rect *value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->rect == value)
|
||||
return;
|
||||
|
||||
p->rect = value;
|
||||
p->updateRectCon();
|
||||
p->onRectChange();
|
||||
}
|
||||
|
||||
/* Scene */
|
||||
void Viewport::composite()
|
||||
{
|
||||
if (emptyFlashFlag)
|
||||
return;
|
||||
|
||||
bool renderEffect = p->needsEffectRender(flashing);
|
||||
|
||||
if (elements.getSize() == 0 && !renderEffect)
|
||||
return;
|
||||
|
||||
// What to do here:
|
||||
// 1. Setup scissor box
|
||||
// 2. Geometry offsets for elements should be taken care off by Scene
|
||||
// 3. Call Scene::composite, or manually iterate and draw??
|
||||
|
||||
/* Setup scissor */
|
||||
glState.scissorTest.pushSet(true);
|
||||
glState.scissorBox.pushSet(p->rect->toIntRect());
|
||||
|
||||
Scene::composite();
|
||||
|
||||
/* If any effects are visible, request parent Scene to
|
||||
* render them. */
|
||||
if (renderEffect)
|
||||
scene->requestViewportRender
|
||||
(p->color->norm, flashColor, p->tone->norm);
|
||||
|
||||
glState.scissorBox.pop();
|
||||
glState.scissorTest.pop();
|
||||
}
|
||||
|
||||
/* SceneElement */
|
||||
void Viewport::draw()
|
||||
{
|
||||
composite();
|
||||
}
|
||||
|
||||
void Viewport::onGeometryChange(const Geometry &geo)
|
||||
{
|
||||
p->screenRect = geo.rect;
|
||||
p->recomputeOnScreen();
|
||||
}
|
||||
|
||||
/* Disposable */
|
||||
void Viewport::releaseResources()
|
||||
{
|
||||
unlink();
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
|
||||
ViewportElement::ViewportElement(Viewport *viewport, int z)
|
||||
: SceneElement(viewport ? *viewport : *gState->screen(), z),
|
||||
m_viewport(viewport)
|
||||
{}
|
||||
|
||||
Viewport *ViewportElement::getViewport() const
|
||||
{
|
||||
return m_viewport;
|
||||
}
|
||||
|
||||
void ViewportElement::setViewport(Viewport *viewport)
|
||||
{
|
||||
m_viewport = viewport;
|
||||
setScene(viewport ? *viewport : *gState->screen());
|
||||
onViewportChange();
|
||||
onGeometryChange(scene->getGeometry());
|
||||
}
|
80
src/viewport.h
Normal file
80
src/viewport.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
** viewport.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef VIEWPORT_H
|
||||
#define VIEWPORT_H
|
||||
|
||||
#include "scene.h"
|
||||
#include "flashable.h"
|
||||
#include "disposable.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "SFML/Graphics/Rect.hpp"
|
||||
|
||||
struct ViewportPrivate;
|
||||
|
||||
class Viewport : public Scene, public SceneElement, public Flashable, public Disposable
|
||||
{
|
||||
public:
|
||||
Viewport(int x, int y, int width, int height);
|
||||
Viewport(Rect *rect);
|
||||
~Viewport();
|
||||
|
||||
DECL_ATTR( Rect, Rect* )
|
||||
DECL_ATTR( OX, int )
|
||||
DECL_ATTR( OY, int )
|
||||
DECL_ATTR( Color, Color* )
|
||||
DECL_ATTR( Tone, Tone* )
|
||||
|
||||
private:
|
||||
void initViewport(int x, int y, int width, int height);
|
||||
void geometryChanged();
|
||||
|
||||
void composite();
|
||||
void draw();
|
||||
void onGeometryChange(const Geometry &);
|
||||
bool isEffectiveViewport(Rect *&, Color *&, Tone *&) const;
|
||||
|
||||
void releaseResources();
|
||||
|
||||
ViewportPrivate *p;
|
||||
friend struct ViewportPrivate;
|
||||
|
||||
IntruListLink<Scene> sceneLink;
|
||||
};
|
||||
|
||||
class ViewportElement : public SceneElement
|
||||
{
|
||||
public:
|
||||
ViewportElement(Viewport *viewport = 0, int z = 0);
|
||||
|
||||
Viewport *getViewport() const;
|
||||
|
||||
void setViewport(Viewport *viewport = 0);
|
||||
|
||||
protected:
|
||||
virtual void onViewportChange() {}
|
||||
|
||||
private:
|
||||
Viewport *m_viewport;
|
||||
};
|
||||
|
||||
#endif // VIEWPORT_H
|
888
src/window.cpp
Normal file
888
src/window.cpp
Normal file
|
@ -0,0 +1,888 @@
|
|||
/*
|
||||
** window.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include "viewport.h"
|
||||
#include "globalstate.h"
|
||||
#include "bitmap.h"
|
||||
#include "etc.h"
|
||||
#include "etc-internal.h"
|
||||
#include "tilequad.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
#include "quadarray.h"
|
||||
#include "texpool.h"
|
||||
#include "glstate.h"
|
||||
|
||||
#include "sigc++/connection.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
template<typename T>
|
||||
struct Sides
|
||||
{
|
||||
T l, r, t, b;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Corners
|
||||
{
|
||||
T tl, tr, bl, br;
|
||||
};
|
||||
|
||||
static IntRect backgroundSrc(0, 0, 128, 128);
|
||||
|
||||
static IntRect cursorSrc(128, 64, 32, 32);
|
||||
|
||||
static IntRect pauseAniSrc[] =
|
||||
{
|
||||
IntRect(160, 64, 16, 16),
|
||||
IntRect(176, 64, 16, 16),
|
||||
IntRect(160, 80, 16, 16),
|
||||
IntRect(176, 80, 16, 16)
|
||||
};
|
||||
|
||||
static Sides<IntRect> bordersSrc =
|
||||
{
|
||||
IntRect(128, 16, 16, 32),
|
||||
IntRect(176, 16, 16, 32),
|
||||
IntRect(144, 0, 32, 16),
|
||||
IntRect(144, 48, 32, 16)
|
||||
};
|
||||
|
||||
static Corners<IntRect> cornersSrc =
|
||||
{
|
||||
IntRect(128, 0, 16, 16),
|
||||
IntRect(176, 0, 16, 16),
|
||||
IntRect(128, 48, 16, 16),
|
||||
IntRect(176, 48, 16, 16)
|
||||
};
|
||||
|
||||
static Sides<IntRect> scrollArrowSrc =
|
||||
{
|
||||
IntRect(144, 24, 8, 16),
|
||||
IntRect(168, 24, 8, 16),
|
||||
IntRect(152, 16, 16, 8),
|
||||
IntRect(152, 40, 16, 8)
|
||||
};
|
||||
|
||||
///* Cycling */
|
||||
//static unsigned char cursorAniAlpha[] =
|
||||
//{
|
||||
// /* Fade out */
|
||||
// 0xFF, 0xF0, 0xE8, 0xE0, 0xD8, 0xD0, 0xC8, 0xC0,
|
||||
// 0xB8, 0xB0, 0xA8, 0xA0, 0x98, 0x90, 0x88, 0x80,
|
||||
// /* Fade in */
|
||||
// 0x78, 0x80, 0x88, 0x90, 0x98, 0xA0, 0xA8, 0xB0,
|
||||
// 0xB8, 0xC0, 0xC8, 0xD0, 0xD8, 0xE0, 0xE8, 0xF0
|
||||
//};
|
||||
|
||||
static unsigned char cursorAniAlpha[] =
|
||||
{
|
||||
/* Fade out */
|
||||
0xFF, 0xF7, 0xEF, 0xE7, 0xDF, 0xD7, 0xCF, 0xC7,
|
||||
0xBF, 0xB7, 0xAF, 0xA7, 0x9F, 0x97, 0x8F, 0x87,
|
||||
/* Fade in */
|
||||
0x7F, 0x87, 0x8F, 0x97, 0x9F, 0xA7, 0xAF, 0xB7,
|
||||
0xBF, 0xC7, 0xCF, 0xD7, 0xDF, 0xE7, 0xEF, 0xF7
|
||||
};
|
||||
|
||||
static elementsN(cursorAniAlpha);
|
||||
|
||||
/* Cycling */
|
||||
static unsigned char pauseAniQuad[] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
static elementsN(pauseAniQuad);
|
||||
|
||||
/* No cycle */
|
||||
static unsigned char pauseAniAlpha[] =
|
||||
{
|
||||
0x00, 0x20, 0x40, 0x60,
|
||||
0x80, 0xA0, 0xC0, 0xE0,
|
||||
0xFF
|
||||
};
|
||||
|
||||
static elementsN(pauseAniAlpha);
|
||||
|
||||
/* Points to an array of quads which it doesn't own.
|
||||
* Useful for setting alpha of quads stored inside
|
||||
* bigger arrays */
|
||||
struct QuadChunk
|
||||
{
|
||||
Vertex *vert;
|
||||
int count; /* In quads */
|
||||
|
||||
QuadChunk()
|
||||
: vert(0), count(0)
|
||||
{}
|
||||
|
||||
void setAlpha(float value)
|
||||
{
|
||||
for (int i = 0; i < count*4; ++i)
|
||||
vert[i].color.w = value;
|
||||
}
|
||||
};
|
||||
|
||||
/* Vocabulary:
|
||||
*
|
||||
* Base: Base layer of window; includes background and borders.
|
||||
* Drawn at z+0.
|
||||
*
|
||||
* Controls: Controls layer of window; includes scroll arrows,
|
||||
* pause animation, cursor rectangle and contents bitmap.
|
||||
* Drawn at z+2.
|
||||
*
|
||||
* Scroll arrows: Arrows that appear automatically when a part of
|
||||
* the contents bitmap is not visible in either upper, lower, left
|
||||
* or right direction.
|
||||
*
|
||||
* Pause: Animation that displays an animating icon in the bottom
|
||||
* center of the window, usually indicating user input is awaited,
|
||||
* such as when text is displayed.
|
||||
*
|
||||
* Cursor: Blinking rectangle that usually displays a selection to
|
||||
* the user.
|
||||
*
|
||||
* Contents: User settable bitmap that is drawn inside the window,
|
||||
* clipped to a 16 pixel smaller rectangle. Position is adjusted
|
||||
* with OX/OY.
|
||||
*
|
||||
* BaseTex: If the window has an opacity <255, we have to prerender
|
||||
* the base to a texture and draw that. Otherwise, we can draw the
|
||||
* quad array directly to the screen.
|
||||
*/
|
||||
|
||||
struct WindowPrivate
|
||||
{
|
||||
Bitmap *windowskin;
|
||||
Bitmap *contents;
|
||||
bool bgStretch;
|
||||
Rect *cursorRect;
|
||||
bool active;
|
||||
bool pause;
|
||||
|
||||
sigc::connection cursorRectCon;
|
||||
|
||||
Vec2i sceneOffset;
|
||||
|
||||
Vec2i position;
|
||||
Vec2i size;
|
||||
Vec2i contentsOffset;
|
||||
|
||||
NormValue opacity;
|
||||
NormValue backOpacity;
|
||||
NormValue contentsOpacity;
|
||||
|
||||
bool baseVertDirty;
|
||||
bool opacityDirty;
|
||||
bool baseTexDirty;
|
||||
|
||||
ColorQuadArray baseQuadArray;
|
||||
|
||||
/* Used when opacity < 255 */
|
||||
TexFBO baseTex;
|
||||
bool useBaseTex;
|
||||
|
||||
QuadChunk backgroundVert;
|
||||
|
||||
Quad baseTexQuad;
|
||||
|
||||
struct WindowControls : public ViewportElement
|
||||
{
|
||||
WindowPrivate *p;
|
||||
|
||||
WindowControls(WindowPrivate *p,
|
||||
Viewport *viewport = 0)
|
||||
: ViewportElement(viewport),
|
||||
p(p)
|
||||
{
|
||||
setZ(2);
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
p->drawControls();
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
unlink();
|
||||
}
|
||||
};
|
||||
|
||||
WindowControls controlsElement;
|
||||
|
||||
ColorQuadArray controlsQuadArray;
|
||||
int controlsQuadCount;
|
||||
|
||||
Quad contentsQuad;
|
||||
|
||||
QuadChunk pauseAniVert;
|
||||
QuadChunk cursorVert;
|
||||
|
||||
unsigned char cursorAniAlphaIdx;
|
||||
unsigned char pauseAniAlphaIdx;
|
||||
unsigned char pauseAniQuadIdx;
|
||||
|
||||
bool controlsVertDirty;
|
||||
|
||||
EtcTemps tmp;
|
||||
|
||||
sigc::connection prepareCon;
|
||||
|
||||
WindowPrivate(Viewport *viewport = 0)
|
||||
: windowskin(0),
|
||||
contents(0),
|
||||
bgStretch(true),
|
||||
cursorRect(&tmp.rect),
|
||||
active(true),
|
||||
pause(false),
|
||||
opacity(255),
|
||||
backOpacity(255),
|
||||
contentsOpacity(255),
|
||||
baseVertDirty(true),
|
||||
opacityDirty(true),
|
||||
baseTexDirty(true),
|
||||
controlsElement(this, viewport),
|
||||
cursorAniAlphaIdx(0),
|
||||
pauseAniAlphaIdx(0),
|
||||
pauseAniQuadIdx(0),
|
||||
controlsVertDirty(true)
|
||||
{
|
||||
refreshCursorRectCon();
|
||||
|
||||
controlsQuadArray.resize(14);
|
||||
TileQuads::buildFrameSource(cursorSrc, controlsQuadArray.vertices.data());
|
||||
cursorVert.count = 9;
|
||||
pauseAniVert.count = 1;
|
||||
|
||||
prepareCon = gState->prepareDraw.connect
|
||||
(sigc::mem_fun(this, &WindowPrivate::prepare));
|
||||
}
|
||||
|
||||
~WindowPrivate()
|
||||
{
|
||||
gState->texPool().release(baseTex);
|
||||
cursorRectCon.disconnect();
|
||||
prepareCon.disconnect();
|
||||
}
|
||||
|
||||
void onCursorRectChange()
|
||||
{
|
||||
controlsVertDirty = true;
|
||||
}
|
||||
|
||||
void refreshCursorRectCon()
|
||||
{
|
||||
cursorRectCon.disconnect();
|
||||
cursorRectCon = cursorRect->valueChanged.connect
|
||||
(sigc::mem_fun(this, &WindowPrivate::onCursorRectChange));
|
||||
}
|
||||
|
||||
void buildBaseVert()
|
||||
{
|
||||
int w = size.x;
|
||||
int h = size.y;
|
||||
|
||||
IntRect bgRect(2, 2, w - 4, h - 4);
|
||||
|
||||
Sides<IntRect> borderRects;
|
||||
borderRects.l = IntRect(0, 8, 16, h-16);
|
||||
borderRects.r = IntRect(w-16, 8, 16, h-16);
|
||||
borderRects.t = IntRect(8, 0, w-16, 16 );
|
||||
borderRects.b = IntRect(8, h-16, w-16, 16 );
|
||||
|
||||
Corners<IntRect> cornerRects;
|
||||
cornerRects.tl = IntRect(0, 0, 16, 16);
|
||||
cornerRects.tr = IntRect(w-16, 0, 16, 16);
|
||||
cornerRects.bl = IntRect(0, h-16, 16, 16);
|
||||
cornerRects.br = IntRect(w-16, h-16, 16, 16);
|
||||
|
||||
/* Required quad count */
|
||||
int count = 0;
|
||||
|
||||
/* Background */
|
||||
if (bgStretch)
|
||||
backgroundVert.count = 1;
|
||||
else
|
||||
backgroundVert.count =
|
||||
TileQuads::twoDimCount(128, 128, bgRect.w, bgRect.h);
|
||||
|
||||
count += backgroundVert.count;
|
||||
|
||||
/* Borders (sides) */
|
||||
count += TileQuads::oneDimCount(32, w-16) * 2;
|
||||
count += TileQuads::oneDimCount(32, h-16) * 2;
|
||||
|
||||
/* Corners */
|
||||
count += 4;
|
||||
|
||||
/* Our vertex array */
|
||||
baseQuadArray.resize(count);
|
||||
Vertex *vert = baseQuadArray.vertices.data();
|
||||
|
||||
int i = 0;
|
||||
backgroundVert.vert = &vert[i];
|
||||
|
||||
/* Background */
|
||||
if (bgStretch)
|
||||
{
|
||||
Quad::setTexRect(&vert[i*4], backgroundSrc);
|
||||
Quad::setPosRect(&vert[i*4], bgRect);
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += TileQuads::build(backgroundSrc, bgRect, &vert[i*4]);
|
||||
}
|
||||
|
||||
/* Borders */
|
||||
i += TileQuads::buildH(bordersSrc.t, w-16, 8, 0, &vert[i*4]);
|
||||
i += TileQuads::buildH(bordersSrc.b, w-16, 8, h-16, &vert[i*4]);
|
||||
i += TileQuads::buildV(bordersSrc.l, h-16, 0, 8, &vert[i*4]);
|
||||
i += TileQuads::buildV(bordersSrc.r, h-16, w-16, 8, &vert[i*4]);
|
||||
|
||||
/* Corners */
|
||||
i += Quad::setTexPosRect(&vert[i*4], cornersSrc.tl, cornerRects.tl);
|
||||
i += Quad::setTexPosRect(&vert[i*4], cornersSrc.tr, cornerRects.tr);
|
||||
i += Quad::setTexPosRect(&vert[i*4], cornersSrc.bl, cornerRects.bl);
|
||||
i += Quad::setTexPosRect(&vert[i*4], cornersSrc.br, cornerRects.br);
|
||||
|
||||
for (int j = 0; j < count*4; ++j)
|
||||
vert[j].color = Vec4(1, 1, 1, 1);
|
||||
|
||||
|
||||
FloatRect texRect = FloatRect(0, 0, size.x, size.y);
|
||||
baseTexQuad.setTexPosRect(texRect, texRect);
|
||||
|
||||
baseTexDirty = true;
|
||||
}
|
||||
|
||||
void updateBaseAlpha()
|
||||
{
|
||||
/* This is always applied unconditionally */
|
||||
backgroundVert.setAlpha(backOpacity.norm);
|
||||
|
||||
baseTexQuad.setColor(Vec4(1, 1, 1, opacity.norm));
|
||||
|
||||
baseTexDirty = true;
|
||||
}
|
||||
|
||||
void ensureBaseTexReady()
|
||||
{
|
||||
/* Make sure texture is big enough */
|
||||
int newW = baseTex.width;
|
||||
int newH = baseTex.height;
|
||||
bool resizeNeeded = false;
|
||||
|
||||
if (size.x > baseTex.width)
|
||||
{
|
||||
newW = findNextPow2(size.x);
|
||||
resizeNeeded = true;
|
||||
}
|
||||
if (size.y > baseTex.height)
|
||||
{
|
||||
newH = findNextPow2(size.y);
|
||||
resizeNeeded = true;
|
||||
}
|
||||
|
||||
if (!resizeNeeded)
|
||||
return;
|
||||
|
||||
gState->texPool().release(baseTex);
|
||||
baseTex = gState->texPool().request(newW, newH);
|
||||
|
||||
qDebug() << "Allocated bg tex:" << newW << newH;
|
||||
|
||||
baseTexDirty = true;
|
||||
}
|
||||
|
||||
void redrawBaseTex()
|
||||
{
|
||||
/* Discard old buffer */
|
||||
Tex::bind(baseTex.tex);
|
||||
Tex::allocEmpty(baseTex.width, baseTex.height);
|
||||
Tex::unbind();
|
||||
|
||||
FBO::bind(baseTex.fbo);
|
||||
glState.pushSetViewport(baseTex.width, baseTex.height);
|
||||
glState.clearColor.pushSet(Vec4());
|
||||
|
||||
/* Clear texture */
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
/* Repaint base */
|
||||
windowskin->flush();
|
||||
windowskin->bindTexWithMatrix();
|
||||
Tex::setSmooth(true);
|
||||
glState.pushSetViewport(baseTex.width, baseTex.height);
|
||||
|
||||
/* We need to blit the background without blending,
|
||||
* because we want to retain its correct alpha value.
|
||||
* Otherwise it would be mutliplied by the backgrounds 0 alpha */
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
baseQuadArray.draw(0, backgroundVert.count);
|
||||
|
||||
/* Now draw the rest (ie. the frame) with blending */
|
||||
glState.blendMode.set(BlendNormal);
|
||||
|
||||
baseQuadArray.draw(backgroundVert.count, baseQuadArray.count()-backgroundVert.count);
|
||||
|
||||
glState.blendMode.pop();
|
||||
glState.popViewport();
|
||||
Tex::setSmooth(false);
|
||||
|
||||
glState.clearColor.pop();
|
||||
glState.popViewport();
|
||||
}
|
||||
|
||||
void buildControlsVert()
|
||||
{
|
||||
int i = 0;
|
||||
Vertex *vert = controlsQuadArray.vertices.data();
|
||||
|
||||
/* Cursor */
|
||||
if (!cursorRect->isEmpty())
|
||||
{
|
||||
/* Effective cursor rect has 16 xy offset to window */
|
||||
IntRect effectRect(cursorRect->x+16, cursorRect->y+16,
|
||||
cursorRect->width, cursorRect->height);
|
||||
cursorVert.vert = &vert[i*4];
|
||||
i += TileQuads::buildFrame(effectRect, &vert[i*4]);
|
||||
}
|
||||
|
||||
/* Scroll arrows */
|
||||
int scrollLRY = (size.y - 16) / 2;
|
||||
int scrollTBX = (size.x - 16) / 2;
|
||||
|
||||
Sides<IntRect> scrollArrows;
|
||||
|
||||
scrollArrows.l = IntRect(4, scrollLRY, 8, 16);
|
||||
scrollArrows.r = IntRect(size.x - 12, scrollLRY, 8, 16);
|
||||
scrollArrows.t = IntRect(scrollTBX, 4, 16, 8);
|
||||
scrollArrows.b = IntRect(scrollTBX, size.y - 12, 16, 8);
|
||||
|
||||
if (contents)
|
||||
{
|
||||
if (contentsOffset.x > 0)
|
||||
i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.l, scrollArrows.l);
|
||||
|
||||
if (contentsOffset.y > 0)
|
||||
i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.t, scrollArrows.t);
|
||||
|
||||
if ((size.x - 32) < (contents->width() - contentsOffset.x))
|
||||
i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.r, scrollArrows.r);
|
||||
|
||||
if ((size.y - 32) < (contents->height() - contentsOffset.y))
|
||||
i += Quad::setTexPosRect(&vert[i*4], scrollArrowSrc.b, scrollArrows.b);
|
||||
}
|
||||
|
||||
/* Pause animation */
|
||||
if (pause)
|
||||
{
|
||||
pauseAniVert.vert = &vert[i*4];
|
||||
i += Quad::setTexPosRect(&vert[i*4], pauseAniSrc[pauseAniQuad[pauseAniQuadIdx]],
|
||||
FloatRect((size.x - 16) / 2, size.y - 16, 16, 16));
|
||||
}
|
||||
|
||||
controlsQuadArray.commit();
|
||||
controlsQuadCount = i;
|
||||
}
|
||||
|
||||
void prepare()
|
||||
{
|
||||
bool updateBaseQuadArray = false;
|
||||
|
||||
if (baseVertDirty)
|
||||
{
|
||||
buildBaseVert();
|
||||
baseVertDirty = false;
|
||||
updateBaseQuadArray = true;
|
||||
}
|
||||
if (opacityDirty)
|
||||
{
|
||||
updateBaseAlpha();
|
||||
opacityDirty = false;
|
||||
updateBaseQuadArray = true;
|
||||
}
|
||||
|
||||
if (updateBaseQuadArray)
|
||||
baseQuadArray.commit();
|
||||
|
||||
/* If opacity has effect, we must prerender to a texture
|
||||
* and then draw this texture instead of the quad array */
|
||||
useBaseTex = opacity < 255;
|
||||
|
||||
if (useBaseTex)
|
||||
{
|
||||
ensureBaseTexReady();
|
||||
|
||||
if (baseTexDirty)
|
||||
{
|
||||
redrawBaseTex();
|
||||
baseTexDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawBase()
|
||||
{
|
||||
if (!windowskin)
|
||||
return;
|
||||
|
||||
if (size == Vec2i(0, 0))
|
||||
return;
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glTranslatef(position.x + sceneOffset.x,
|
||||
position.y + sceneOffset.y, 0);
|
||||
|
||||
if (useBaseTex)
|
||||
{
|
||||
Tex::bindWithMatrix(baseTex.tex, baseTex.width, baseTex.height);
|
||||
baseTexQuad.draw();
|
||||
}
|
||||
else
|
||||
{
|
||||
windowskin->flush();
|
||||
windowskin->bindTexWithMatrix();
|
||||
Tex::setSmooth(true);
|
||||
|
||||
baseQuadArray.draw();
|
||||
|
||||
Tex::setSmooth(false);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void drawControls()
|
||||
{
|
||||
if (!windowskin)
|
||||
return;
|
||||
|
||||
if (size == Vec2i(0, 0))
|
||||
return;
|
||||
|
||||
if (controlsVertDirty)
|
||||
{
|
||||
buildControlsVert();
|
||||
updateControls();
|
||||
controlsVertDirty = false;
|
||||
}
|
||||
|
||||
/* Actual on screen coordinates */
|
||||
int effectX = position.x + sceneOffset.x;
|
||||
int effectY = position.y + sceneOffset.y;
|
||||
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glTranslatef(effectX, effectY, 0);
|
||||
|
||||
IntRect windowRect(effectX, effectY, size.x, size.y);
|
||||
IntRect contentsRect(effectX+16, effectY+16, size.x-32, size.y-32);
|
||||
|
||||
glState.scissorTest.pushSet(true);
|
||||
glState.scissorBox.push();
|
||||
glState.scissorBox.setIntersect(windowRect);
|
||||
|
||||
/* Draw arrows / cursors */
|
||||
windowskin->bindTexWithMatrix();
|
||||
controlsQuadArray.draw(0, controlsQuadCount);
|
||||
|
||||
if (contents)
|
||||
{
|
||||
/* Draw contents bitmap */
|
||||
glState.scissorBox.setIntersect(contentsRect);
|
||||
glTranslatef(16-contentsOffset.x, 16-contentsOffset.y, 0);
|
||||
|
||||
contents->flush();
|
||||
contents->bindTexWithMatrix();
|
||||
contentsQuad.draw();
|
||||
}
|
||||
|
||||
glState.scissorBox.pop();
|
||||
glState.scissorTest.pop();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void updateControls()
|
||||
{
|
||||
bool updateArray = false;
|
||||
|
||||
if (active && cursorVert.vert)
|
||||
{
|
||||
float alpha = cursorAniAlpha[cursorAniAlphaIdx] / 255.0;
|
||||
|
||||
cursorVert.setAlpha(alpha);
|
||||
|
||||
updateArray = true;
|
||||
}
|
||||
|
||||
if (pause && pauseAniVert.vert)
|
||||
{
|
||||
float alpha = pauseAniAlpha[pauseAniAlphaIdx] / 255.0;
|
||||
FloatRect frameRect = pauseAniSrc[pauseAniQuad[pauseAniQuadIdx]];
|
||||
|
||||
pauseAniVert.setAlpha(alpha);
|
||||
Quad::setTexRect(pauseAniVert.vert, frameRect);
|
||||
|
||||
updateArray = true;
|
||||
}
|
||||
|
||||
if (updateArray)
|
||||
controlsQuadArray.commit();
|
||||
}
|
||||
|
||||
void stepAnimations()
|
||||
{
|
||||
if (++cursorAniAlphaIdx == cursorAniAlphaN)
|
||||
cursorAniAlphaIdx = 0;
|
||||
|
||||
if (pauseAniAlphaIdx < pauseAniAlphaN-1)
|
||||
++pauseAniAlphaIdx;
|
||||
|
||||
if (++pauseAniQuadIdx == pauseAniQuadN)
|
||||
pauseAniQuadIdx = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Window::Window(Viewport *viewport)
|
||||
: ViewportElement(viewport)
|
||||
{
|
||||
p = new WindowPrivate(viewport);
|
||||
onGeometryChange(scene->getGeometry());
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
void Window::update()
|
||||
{
|
||||
p->updateControls();
|
||||
p->stepAnimations();
|
||||
}
|
||||
|
||||
#define DISP_CLASS_NAME "window"
|
||||
|
||||
DEF_ATTR_SIMPLE(Window, Windowskin, Bitmap*, p->windowskin)
|
||||
DEF_ATTR_SIMPLE(Window, X, int, p->position.x)
|
||||
DEF_ATTR_SIMPLE(Window, Y, int, p->position.y)
|
||||
|
||||
DEF_ATTR_RD_SIMPLE(Window, Contents, Bitmap*, p->contents)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Stretch, bool, p->bgStretch)
|
||||
DEF_ATTR_RD_SIMPLE(Window, CursorRect, Rect*, p->cursorRect)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Active, bool, p->active)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Pause, bool, p->pause)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Width, int, p->size.x)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Height, int, p->size.y)
|
||||
DEF_ATTR_RD_SIMPLE(Window, OX, int, p->contentsOffset.x)
|
||||
DEF_ATTR_RD_SIMPLE(Window, OY, int, p->contentsOffset.y)
|
||||
DEF_ATTR_RD_SIMPLE(Window, Opacity, int, p->opacity)
|
||||
DEF_ATTR_RD_SIMPLE(Window, BackOpacity, int, p->backOpacity)
|
||||
DEF_ATTR_RD_SIMPLE(Window, ContentsOpacity, int, p->contentsOpacity)
|
||||
|
||||
void Window::setContents(Bitmap *value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
p->contents = value;
|
||||
p->controlsVertDirty = true;
|
||||
|
||||
if (value)
|
||||
p->contentsQuad.setTexPosRect(value->rect(), value->rect());
|
||||
}
|
||||
|
||||
void Window::setStretch(bool value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (value == p->bgStretch)
|
||||
return;
|
||||
|
||||
p->bgStretch = value;
|
||||
p->baseVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setCursorRect(Rect *value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->cursorRect == value)
|
||||
return;
|
||||
|
||||
p->cursorRect = value;
|
||||
|
||||
p->refreshCursorRectCon();
|
||||
p->onCursorRectChange();
|
||||
}
|
||||
|
||||
void Window::setActive(bool value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->active == value)
|
||||
return;
|
||||
|
||||
p->active = value;
|
||||
p->cursorAniAlphaIdx = 0;
|
||||
}
|
||||
|
||||
void Window::setPause(bool value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->pause == value)
|
||||
return;
|
||||
|
||||
p->pause = value;
|
||||
p->pauseAniAlphaIdx = 0;
|
||||
p->pauseAniQuadIdx = 0;
|
||||
p->controlsVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setWidth(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->size.x == value)
|
||||
return;
|
||||
|
||||
p->size.x = value;
|
||||
p->baseVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setHeight(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->size.y == value)
|
||||
return;
|
||||
|
||||
p->size.y = value;
|
||||
p->baseVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setOX(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->contentsOffset.x == value)
|
||||
return;
|
||||
|
||||
p->contentsOffset.x = value;
|
||||
p->controlsVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setOY(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->contentsOffset.y == value)
|
||||
return;
|
||||
|
||||
p->contentsOffset.y = value;
|
||||
p->controlsVertDirty = true;
|
||||
}
|
||||
|
||||
void Window::setOpacity(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->opacity == value)
|
||||
return;
|
||||
|
||||
p->opacity = value;
|
||||
p->opacityDirty = true;
|
||||
}
|
||||
|
||||
void Window::setBackOpacity(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->backOpacity == value)
|
||||
return;
|
||||
|
||||
p->backOpacity = value;
|
||||
p->opacityDirty = true;
|
||||
}
|
||||
|
||||
void Window::setContentsOpacity(int value)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
||||
if (p->contentsOpacity == value)
|
||||
return;
|
||||
|
||||
p->contentsOpacity = value;
|
||||
p->contentsQuad.setColor(Vec4(1, 1, 1, p->contentsOpacity.norm));
|
||||
}
|
||||
|
||||
void Window::draw()
|
||||
{
|
||||
p->drawBase();
|
||||
}
|
||||
|
||||
void Window::onGeometryChange(const Scene::Geometry &geo)
|
||||
{
|
||||
p->sceneOffset.x = geo.rect.x - geo.xOrigin;
|
||||
p->sceneOffset.y = geo.rect.y - geo.yOrigin;
|
||||
}
|
||||
|
||||
void Window::setZ(int value)
|
||||
{
|
||||
ViewportElement::setZ(value);
|
||||
|
||||
p->controlsElement.setZ(value + 2);
|
||||
}
|
||||
|
||||
void Window::setVisible(bool value)
|
||||
{
|
||||
ViewportElement::setVisible(value);
|
||||
|
||||
p->controlsElement.setVisible(value);
|
||||
}
|
||||
|
||||
void Window::onViewportChange()
|
||||
{
|
||||
p->controlsElement.setScene(*this->scene);
|
||||
}
|
||||
|
||||
void Window::releaseResources()
|
||||
{
|
||||
p->controlsElement.release();
|
||||
|
||||
unlink();
|
||||
|
||||
delete p;
|
||||
}
|
72
src/window.h
Normal file
72
src/window.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
** window.h
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 2 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** mkxp is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include "viewport.h"
|
||||
#include "disposable.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
class Bitmap;
|
||||
struct Rect;
|
||||
|
||||
struct WindowPrivate;
|
||||
|
||||
class Window : public ViewportElement, public Disposable
|
||||
{
|
||||
public:
|
||||
Window(Viewport *viewport = 0);
|
||||
~Window();
|
||||
|
||||
void update();
|
||||
|
||||
DECL_ATTR( Windowskin, Bitmap* )
|
||||
DECL_ATTR( Contents, Bitmap* )
|
||||
DECL_ATTR( Stretch, bool )
|
||||
DECL_ATTR( CursorRect, Rect* )
|
||||
DECL_ATTR( Active, bool )
|
||||
DECL_ATTR( Pause, bool )
|
||||
DECL_ATTR( X, int )
|
||||
DECL_ATTR( Y, int )
|
||||
DECL_ATTR( Width, int )
|
||||
DECL_ATTR( Height, int )
|
||||
DECL_ATTR( OX, int )
|
||||
DECL_ATTR( OY, int )
|
||||
DECL_ATTR( Opacity, int )
|
||||
DECL_ATTR( BackOpacity, int )
|
||||
DECL_ATTR( ContentsOpacity, int )
|
||||
|
||||
private:
|
||||
WindowPrivate *p;
|
||||
|
||||
void draw();
|
||||
void onGeometryChange(const Scene::Geometry &);
|
||||
void setZ(int value);
|
||||
void setVisible(bool value);
|
||||
|
||||
void onViewportChange();
|
||||
|
||||
void releaseResources();
|
||||
};
|
||||
|
||||
#endif // WINDOW_H
|
Loading…
Add table
Add a link
Reference in a new issue