Initial commit

This commit is contained in:
Jonas Kulla 2013-09-01 16:27:21 +02:00
commit ff25887f41
119 changed files with 24901 additions and 0 deletions

668
src/audio.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &currentSizeOut)
{
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
View 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 &currentSizeOut);
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
View 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 &current = 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

71
src/tilemap.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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