aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libjin/audio/SDL/je_sdl_audio.cpp146
-rw-r--r--src/libjin/audio/SDL/je_sdl_audio.h138
-rw-r--r--src/libjin/audio/SDL/je_sdl_source.cpp404
-rw-r--r--src/libjin/audio/SDL/je_sdl_source.h272
-rw-r--r--src/libjin/audio/je_audio_manager.cpp15
-rw-r--r--src/libjin/audio/je_audio_manager.h87
-rw-r--r--src/libjin/audio/je_source.cpp30
-rw-r--r--src/libjin/audio/je_source.h116
-rw-r--r--src/libjin/common/je_array.hpp123
-rw-r--r--src/libjin/common/je_common.h7
-rw-r--r--src/libjin/common/je_exception.cpp46
-rw-r--r--src/libjin/common/je_exception.h45
-rw-r--r--src/libjin/common/je_noncopyable.h24
-rw-r--r--src/libjin/common/je_object.h16
-rw-r--r--src/libjin/common/je_pool.hpp177
-rw-r--r--src/libjin/common/je_singleton.hpp80
-rw-r--r--src/libjin/common/je_stringmap.hpp143
-rw-r--r--src/libjin/common/je_subsystem.hpp78
-rw-r--r--src/libjin/common/je_temporary.h31
-rw-r--r--src/libjin/common/je_types.h34
-rw-r--r--src/libjin/common/je_utf8.cpp42
-rw-r--r--src/libjin/common/je_utf8.h34
-rw-r--r--src/libjin/core/README.md1
-rw-r--r--src/libjin/core/je_configuration.h58
-rw-r--r--src/libjin/core/je_version.h52
-rw-r--r--src/libjin/filesystem/je_asset_database.cpp85
-rw-r--r--src/libjin/filesystem/je_asset_database.h114
-rw-r--r--src/libjin/filesystem/je_buffer.h169
-rw-r--r--src/libjin/game/je_application.cpp81
-rw-r--r--src/libjin/game/je_application.h88
-rw-r--r--src/libjin/game/je_entity.cpp0
-rw-r--r--src/libjin/game/je_entity.h27
-rw-r--r--src/libjin/game/je_gameobject.cpp11
-rw-r--r--src/libjin/game/je_gameobject.h83
-rw-r--r--src/libjin/game/je_scene.cpp0
-rw-r--r--src/libjin/game/je_scene.h73
-rw-r--r--src/libjin/graphics/animations/je_animation.cpp14
-rw-r--r--src/libjin/graphics/animations/je_animation.h64
-rw-r--r--src/libjin/graphics/animations/je_animator.cpp14
-rw-r--r--src/libjin/graphics/animations/je_animator.h50
-rw-r--r--src/libjin/graphics/fonts/je_decoder.cpp96
-rw-r--r--src/libjin/graphics/fonts/je_decoder.h97
-rw-r--r--src/libjin/graphics/fonts/je_font.h109
-rw-r--r--src/libjin/graphics/fonts/je_page.h54
-rw-r--r--src/libjin/graphics/fonts/je_text.cpp157
-rw-r--r--src/libjin/graphics/fonts/je_text.h172
-rw-r--r--src/libjin/graphics/fonts/je_texture_font.cpp322
-rw-r--r--src/libjin/graphics/fonts/je_texture_font.h144
-rw-r--r--src/libjin/graphics/fonts/je_ttf.cpp468
-rw-r--r--src/libjin/graphics/fonts/je_ttf.h292
-rw-r--r--src/libjin/graphics/je_bitmap.cpp188
-rw-r--r--src/libjin/graphics/je_bitmap.h195
-rw-r--r--src/libjin/graphics/je_canvas.cpp101
-rw-r--r--src/libjin/graphics/je_canvas.h69
-rw-r--r--src/libjin/graphics/je_color.cpp22
-rw-r--r--src/libjin/graphics/je_color.h119
-rw-r--r--src/libjin/graphics/je_gl.cpp116
-rw-r--r--src/libjin/graphics/je_gl.h140
-rw-r--r--src/libjin/graphics/je_graphic.cpp135
-rw-r--r--src/libjin/graphics/je_graphic.h94
-rw-r--r--src/libjin/graphics/je_graphics.h37
-rw-r--r--src/libjin/graphics/je_image.cpp47
-rw-r--r--src/libjin/graphics/je_image.h61
-rw-r--r--src/libjin/graphics/je_mesh.cpp11
-rw-r--r--src/libjin/graphics/je_mesh.h28
-rw-r--r--src/libjin/graphics/je_shapes.cpp130
-rw-r--r--src/libjin/graphics/je_shapes.h34
-rw-r--r--src/libjin/graphics/je_sprite.cpp133
-rw-r--r--src/libjin/graphics/je_sprite.h81
-rw-r--r--src/libjin/graphics/je_sprite_batch.cpp11
-rw-r--r--src/libjin/graphics/je_sprite_batch.h20
-rw-r--r--src/libjin/graphics/je_sprite_sheet.cpp22
-rw-r--r--src/libjin/graphics/je_sprite_sheet.h33
-rw-r--r--src/libjin/graphics/je_texture.cpp44
-rw-r--r--src/libjin/graphics/je_texture.h57
-rw-r--r--src/libjin/graphics/je_window.cpp130
-rw-r--r--src/libjin/graphics/je_window.h109
-rw-r--r--src/libjin/graphics/particles/je_particle.cpp72
-rw-r--r--src/libjin/graphics/particles/je_particle.h166
-rw-r--r--src/libjin/graphics/particles/je_particle_emitter.cpp105
-rw-r--r--src/libjin/graphics/particles/je_particle_emitter.h136
-rw-r--r--src/libjin/graphics/particles/je_particle_pool.h24
-rw-r--r--src/libjin/graphics/particles/je_particle_system.cpp67
-rw-r--r--src/libjin/graphics/particles/je_particle_system.h115
-rw-r--r--src/libjin/graphics/shaders/built-in/je_default.shader.h21
-rw-r--r--src/libjin/graphics/shaders/built-in/je_font.shader.h21
-rw-r--r--src/libjin/graphics/shaders/built-in/je_texture.shader.h21
-rw-r--r--src/libjin/graphics/shaders/je_base.shader.h89
-rw-r--r--src/libjin/graphics/shaders/je_jsl_compiler.cpp57
-rw-r--r--src/libjin/graphics/shaders/je_jsl_compiler.h45
-rw-r--r--src/libjin/graphics/shaders/je_shader.cpp281
-rw-r--r--src/libjin/graphics/shaders/je_shader.h201
-rw-r--r--src/libjin/input/je_event.cpp8
-rw-r--r--src/libjin/input/je_event.h121
-rw-r--r--src/libjin/input/je_input.h9
-rw-r--r--src/libjin/input/je_joypad.cpp0
-rw-r--r--src/libjin/input/je_joypad.h56
-rw-r--r--src/libjin/input/je_keyboard.cpp0
-rw-r--r--src/libjin/input/je_keyboard.h20
-rw-r--r--src/libjin/input/je_mouse.cpp28
-rw-r--r--src/libjin/input/je_mouse.h49
-rw-r--r--src/libjin/math/README.md1
-rw-r--r--src/libjin/math/je_bezier_curve.cpp0
-rw-r--r--src/libjin/math/je_bezier_curve.h17
-rw-r--r--src/libjin/math/je_constant.h10
-rw-r--r--src/libjin/math/je_ellipse.cpp0
-rw-r--r--src/libjin/math/je_ellipse.h0
-rw-r--r--src/libjin/math/je_math.h83
-rw-r--r--src/libjin/math/je_matrix.cpp194
-rw-r--r--src/libjin/math/je_matrix.h159
-rw-r--r--src/libjin/math/je_quad.h23
-rw-r--r--src/libjin/math/je_random.cpp52
-rw-r--r--src/libjin/math/je_random.h33
-rw-r--r--src/libjin/math/je_transform.cpp99
-rw-r--r--src/libjin/math/je_transform.h49
-rw-r--r--src/libjin/math/je_vector2.hpp63
-rw-r--r--src/libjin/math/je_vector3.hpp41
-rw-r--r--src/libjin/math/je_vector4.hpp45
-rw-r--r--src/libjin/net/je_net_manager.cpp24
-rw-r--r--src/libjin/net/je_net_manager.h50
-rw-r--r--src/libjin/net/je_socket.cpp190
-rw-r--r--src/libjin/net/je_socket.h144
-rw-r--r--src/libjin/time/je_timer.cpp122
-rw-r--r--src/libjin/time/je_timer.h137
-rw-r--r--src/libjin/utils/je_endian.h23
-rw-r--r--src/libjin/utils/je_log.cpp81
-rw-r--r--src/libjin/utils/je_log.h63
-rw-r--r--src/libjin/utils/je_macros.h17
-rw-r--r--src/libjin/utils/je_unittest.cpp108
-rw-r--r--src/libjin/utils/je_utils.h9
130 files changed, 10699 insertions, 0 deletions
diff --git a/src/libjin/audio/SDL/je_sdl_audio.cpp b/src/libjin/audio/SDL/je_sdl_audio.cpp
new file mode 100644
index 0000000..c21d077
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_audio.cpp
@@ -0,0 +1,146 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <iostream>
+
+#include "../../math/je_math.h"
+#include "../../utils/je_log.h"
+
+#include "je_sdl_audio.h"
+#include "je_sdl_source.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+ /* עcallbackƵ̵߳ */
+ static void defaultCallback(void *userdata, Uint8 *stream, int size)
+ {
+ static SDLAudio* audio = static_cast<SDLAudio*>(userdata);
+ if (!audio->goOnProcess())
+ return;
+ audio->lock();
+ audio->processCommands();
+ audio->processSources(stream, size);
+ audio->processBuffer(stream, size);
+ audio->unlock();
+ }
+
+ /*call only once*/ bool SDLAudio::initSystem(const SettingBase* s)
+ {
+ jin_log_info("Initialize audio system.");
+
+ if (SDL_Init(SDL_INIT_AUDIO) < 0)
+ return false;
+
+ SDL_AudioSpec spec;
+ Setting* setting = (Setting*)s;
+ if (setting == nullptr)
+ return false;
+
+ unsigned int samplerate = setting->samplerate;
+ unsigned int samples = clamp<int>(setting->samples, 1, setting->samplerate);
+
+ spec.freq = samplerate; // ÿsample,õ 11025, 22050, 44100 and 48000 Hz.
+ spec.format = AUDIO_S16SYS; // signed 16-bit samples in native byte order
+ spec.channels = SDLAUDIO_CHANNELS; //
+ spec.samples = samples; // ÿβʱһã=setting->samplerateÿֻ1
+ spec.userdata = this;
+ spec.callback = defaultCallback;
+
+ audioDevice = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
+ if (audioDevice == 0)
+ return false;
+ /* start audio */
+ SDL_PauseAudioDevice(audioDevice, 0);
+ return true;
+ }
+
+ /*call only once*/ void SDLAudio::quitSystem()
+ {
+ jin_log_info("Quit audio system.");
+ SDL_CloseAudio();
+ }
+
+ void SDLAudio::lock()
+ {
+ SDL_LockAudioDevice(audioDevice);
+ }
+
+ void SDLAudio::unlock()
+ {
+ SDL_UnlockAudioDevice(audioDevice);
+ }
+
+ bool SDLAudio::goOnProcess()
+ {
+ if (state == SDLAudio::State::STOP)
+ {
+ SDLSourceManager::get()->removeAllSource();
+ pause();
+ return false;
+ }
+ else if (state == SDLAudio::State::PAUSE)
+ return false;
+ else
+ return true;
+ }
+
+ void SDLAudio::processCommands()
+ {
+ SDLSourceManager::get()->processCommands();
+ }
+
+ void SDLAudio::processSources(void* buffer, size_t len)
+ {
+ SDLSourceManager::get()->processSources(buffer, len);
+ }
+
+ void SDLAudio::processBuffer(void* buff, size_t len)
+ {
+ short* buffer = (short*)buff;
+ int samples = (len / SDLAUDIO_BYTEDEPTH) >> 1; // ˫
+ const char L = 0, R = 1;
+ for (int i = 0; i < samples; ++i)
+ {
+ short* clip = buffer + (i << 1);
+ clip[L] *= volume;
+ clip[R] *= volume;
+ }
+ }
+
+ void SDLAudio::play()
+ {
+ state = State::PLAY;
+ }
+
+ void SDLAudio::stop()
+ {
+ state = State::STOP;
+ }
+
+ void SDLAudio::pause()
+ {
+ state = State::PAUSE;
+ }
+
+ void SDLAudio::resume()
+ {
+ state = State::PLAY;
+ }
+
+ void SDLAudio::setVolume(float volume)
+ {
+ this->volume = clamp(volume, 0.0f, 1.0f);
+ }
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl) \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_audio.h b/src/libjin/audio/SDL/je_sdl_audio.h
new file mode 100644
index 0000000..77162d4
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_audio.h
@@ -0,0 +1,138 @@
+#ifndef __JE_AUDIO_SDL_H__
+#define __JE_AUDIO_SDL_H__
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <vector>
+
+#include "../je_audio_manager.h"
+
+#include "je_sdl_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+#define SDLAUDIO_BITDEPTH 16
+#define SDLAUDIO_BYTEDEPTH (SDLAUDIO_BITDEPTH >> 3)
+#define SDLAUDIO_CHANNELS 2
+
+ ///
+ /// Audio system SDL implementation.
+ ///
+ class SDLAudio : public AudioManager<SDLAudio>
+ {
+ public:
+ ///
+ /// SDL audio setting.
+ ///
+ struct Setting : SettingBase
+ {
+ public:
+ int samplerate; // Ƶ
+ int samples; // sample<=samplerate
+ };
+
+ ///
+ /// Play all sources whose state is playing.
+ ///
+ void play() override;
+
+ ///
+ /// Stop and remove all sources from the queue.
+ ///
+ void stop() override;
+
+ ///
+ /// Pause audio.
+ ///
+ void pause() override;
+
+ ///
+ /// Resume audio.
+ ///
+ void resume() override;
+
+ ///
+ /// Set global audio volume.
+ ///
+ void setVolume(float volume) override;
+
+ ///
+ /// Process all commands in the queue.
+ ///
+ void processCommands();
+
+ ///
+ /// Process all sources.
+ ///
+ /// @param buffer Source buffer.
+ /// @param len Source length.
+ ///
+ void processSources(void* buffer, size_t len);
+
+ ///
+ /// Process audio buffer.
+ ///
+ /// @param buffer Audio stream buffer.
+ /// @param len Length of stream buffer.
+ ///
+ void processBuffer(void* buffer, size_t len);
+
+ ///
+ /// Goon process.
+ ///
+ /// @return True if sucessful, otherwise return false.
+ ///
+ bool goOnProcess();
+
+ ///
+ /// Lock audio device.
+ ///
+ void lock();
+
+ ///
+ /// Unlock audio device.
+ ///
+ void unlock();
+
+ private:
+ singleton(SDLAudio);
+
+ ///
+ /// SDL audio constructor.
+ ///
+ SDLAudio() {};
+
+ ///
+ /// SDL audio destructor.
+ ///
+ ~SDLAudio() {};
+
+ ///
+ /// Initialize audio system.
+ ///
+ /// @param setting Audio setting.
+ ///
+ bool initSystem(const SettingBase* setting) override;
+
+ ///
+ /// Quit audio system.
+ ///
+ void quitSystem() override;
+
+ // Audio device id.
+ unsigned int audioDevice;
+
+ };
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl)
+
+#endif // __JE_AUDIO_SDL_H__ \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_source.cpp b/src/libjin/audio/SDL/je_sdl_source.cpp
new file mode 100644
index 0000000..d417bf7
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_source.cpp
@@ -0,0 +1,404 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <exception>
+#include <fstream>
+#include <climits>
+
+#define STB_VORBIS_HEADER_ONLY
+#include "stb/stb_vorbis.c"
+#include "wav/wav.h"
+
+#include "../../math/je_math.h"
+#include "../../utils/je_macros.h"
+
+#include "je_sdl_audio.h"
+#include "je_sdl_source.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+
+#define BITS 8
+
+ typedef struct SDLSourceCommand
+ {
+ typedef enum Action
+ {
+ Nothing = 0,
+ Play,
+ Stop,
+ Pause,
+ Resume,
+ Rewind,
+ SetVolume,
+ SetLoop,
+ SetRate,
+ };
+ Action action;
+ union {
+ int _integer;
+ float _float;
+ bool _boolean;
+ const char* _string;
+ } parameter;
+
+ SDLSource* source;
+ };
+
+ typedef enum CHANNEL
+ {
+ MONO = 1, //
+ STEREO = 2, //
+ };
+
+ typedef /*mask*/ enum STATUS
+ {
+ PLAYING = 1,
+ PAUSED = 2,
+ STOPPED = 4
+ };
+
+#define Command SDLSourceCommand
+#define Action Command::Action
+#define Manager SDLSourceManager
+
+ //std::queue<Command*> Manager::commands;
+ //std::stack<Command*> Manager::commandsPool;
+ //std::vector<SDLSource*> Manager::sources;
+ Manager* Manager::manager = nullptr;
+
+ SDLSource* SDLSource::createSource(const char* file)
+ {
+ std::ifstream fs;
+ fs.open(file, std::ios::binary);
+ if (!fs.is_open())
+ {
+ fs.close();
+ return nullptr;
+ }
+ fs.seekg(0, std::ios::end);
+ int size = fs.tellg();
+ fs.seekg(0, std::ios::beg);
+ char* buffer = (char*)malloc(size);
+ memset(buffer, 0, size);
+ fs.read(buffer, size);
+ fs.close();
+ SDLSource* source = createSource(buffer, size);
+ free(buffer);
+ return source;
+ }
+
+ SDLSource* SDLSource::createSource(void* mem, size_t size)
+ {
+ if (mem == nullptr)
+ return nullptr;
+ SDLSource* source = new SDLSource();
+ try
+ {
+ SourceType format = getType(mem, size);
+ switch (format)
+ {
+ case OGG: source->decode_ogg(mem, size); break;
+ case WAV: source->decode_wav(mem, size); break;
+ }
+ }
+ catch (SourceException& exp)
+ {
+ delete source;
+ return nullptr;
+ };
+ return source;
+ }
+
+ SDLSource::SDLSource()
+ {
+ memset(&status, 0, sizeof(status));
+ memset(&raw, 0, sizeof(raw));
+ status.volume = 1;
+ }
+
+ SDLSource::~SDLSource()
+ {
+ delete raw.data;
+ raw.end = 0;
+ raw.data = 0;
+ }
+
+ void SDLSource::decode_wav(void* mem, int size)
+ {
+ wav_t wav;
+ if (wav_read(&wav, mem, size) == 0)
+ {
+ raw.data = wav.data;
+ raw.length = wav.length * wav.channels * wav.bitdepth / 8;
+ raw.channels = clamp<int>(wav.channels, CHANNEL::MONO, CHANNEL::STEREO);
+ raw.end = (char*)raw.data + raw.length;
+ raw.samplerate = wav.samplerate;
+ raw.bitdepth = wav.bitdepth;
+ raw.samples = wav.length;
+ }
+ else
+ throw SourceException();
+ }
+
+ void SDLSource::decode_ogg(void* _mem, int size)
+ {
+ unsigned char* mem = (unsigned char*)_mem;
+ int channels;
+ int samplerate;
+ short* data = (short*)raw.data;
+ int samples = stb_vorbis_decode_memory(
+ mem,
+ size,
+ &channels,
+ &samplerate,
+ &data
+ );
+ const int bitdepth = sizeof(short) * BITS;
+ raw.channels = channels;
+ raw.samplerate = samplerate;
+ raw.data = data;
+ raw.samples = samples; // һsample
+ raw.length = samples * channels * sizeof(short); // һsample
+ raw.bitdepth = bitdepth;
+ raw.end = (char*)data + raw.length;
+ }
+
+#define ActionNone(T)\
+ do{\
+ Command* cmd = Manager::get()->getCommand();\
+ cmd->action = Action::T; \
+ cmd->source = this; \
+ Manager::get()->pushCommand(cmd); \
+ } while (0)
+
+#define ActionArg(T, ARGT, ARG)\
+ do{\
+ Command* cmd = Manager::get()->getCommand();\
+ cmd->action = Action::T; \
+ cmd->parameter.ARGT = ARG; \
+ cmd->source = this; \
+ Manager::get()->pushCommand(cmd); \
+ }while(0)
+
+#define ActionInt(T, INT) ActionArg(T, _integer, INT)
+#define ActionFloat(T, FLT) ActionArg(T, _float, FLT)
+#define ActionString(T, STR) ActionArg(T, _string, STR)
+#define ActionBool(T, BOL) ActionArg(T, _boolean, BOL)
+
+ void SDLSource::play()
+ {
+ ActionNone(Play);
+ }
+
+ void SDLSource::stop()
+ {
+ ActionNone(Stop);
+ }
+
+ void SDLSource::pause()
+ {
+ ActionNone(Pause);
+ }
+
+ void SDLSource::resume()
+ {
+ ActionNone(Resume);
+ }
+
+ void SDLSource::rewind()
+ {
+ ActionNone(Rewind);
+ }
+
+ inline bool SDLSource::isStopped() const
+ {
+ return is(STOPPED);
+ }
+
+ bool SDLSource::isPaused() const
+ {
+ return is(PAUSED);
+ }
+
+ void SDLSource::setPitch(float pitch)
+ {
+ }
+
+ void SDLSource::setVolume(float volume)
+ {
+ ActionFloat(SetVolume, clamp(volume, 0.0f, 1.0f));
+ }
+
+ void SDLSource::setLoop(bool loop)
+ {
+ ActionBool(SetLoop, loop);
+ }
+
+ void SDLSource::setRate(float rate)
+ {
+ ActionFloat(SetRate, rate);
+ }
+
+ inline void SDLSource::handle(
+ SDLSourceManager* manager,
+ SDLSourceCommand* cmd
+ )
+ {
+ switch (cmd->action)
+ {
+ case Command::Action::Play:
+ manager->removeSource(this);
+ manager->pushSource(this);
+ status.state = PLAYING;
+ status.pos = 0; // rewind
+ break;
+ case Command::Action::Stop:
+ manager->removeSource(this);
+ status.state = STOPPED;
+ status.pos = 0; // rewind
+ break;
+ case Command::Action::Pause:
+ manager->removeSource(this);
+ status.state = PAUSED;
+ break;
+ case Command::Action::Resume:
+ manager->removeSource(this);
+ manager->pushSource(this);
+ status.state = PLAYING;
+ break;
+ case Command::Action::Rewind:
+ status.state = PLAYING;
+ status.pos = 0;
+ break;
+ case Command::Action::SetVolume:
+ status.volume = cmd->parameter._float;
+ break;
+ case Command::Action::SetLoop:
+ status.loop = cmd->parameter._boolean;
+ break;
+ }
+ }
+
+ inline void SDLSource::process(void* buf, size_t size)
+ {
+ short* buffer = (short*)buf; // AUDIO_S16SYS
+ int samples = (size / SDLAUDIO_BYTEDEPTH) >> 1; // ˫
+ const char L = 0, R = 1;
+ for (int i = 0; i < samples; ++i)
+ {
+ char* source = (char*)raw.data + status.pos * (raw.bitdepth / 8) * raw.channels;
+ short l = 0;
+ short r = 0;
+ if (raw.bitdepth == 16)
+ {
+ l = ((short*)source)[L] * status.volume;
+ r = ((short*)source)[L + raw.channels - 1] * status.volume;
+ }
+ else if (raw.bitdepth == 8)
+ {
+ l = source[L] << 8; // << 8 Ŵ16bits
+ r = source[L + raw.channels - 1] << 8;
+ }
+ short* sample = buffer + (i << 1);
+ sample[L] = clamp(sample[L] + l, SHRT_MIN, SHRT_MAX); //
+ sample[R] = clamp(sample[R] + r, SHRT_MIN, SHRT_MAX); //
+ ++status.pos;
+ if (status.pos > raw.samples && status.loop)
+ status.pos = 0; // rewind
+ else if (status.pos > raw.samples && !status.loop)
+ break;
+ }
+ }
+
+ Manager* Manager::get()
+ {
+ return (manager == nullptr ? manager = new Manager() : manager);
+ }
+
+ void Manager::processCommands()
+ {
+ Command* cmd = nullptr;
+ SDLSource* source = nullptr;
+ while (!commands.empty())
+ {
+ cmd = commands.front();
+ if (cmd != nullptr)
+ {
+ source = cmd->source;
+ if (source != nullptr)
+ source->handle(manager, cmd);
+ }
+ commands.pop();
+ }
+ }
+
+ /* AUDIO_S16SYS[size>>1] buffer */
+ void Manager::processSources(void* buf, size_t size)
+ {
+ /* clear render buffer */
+ memset(buf, 0, size);
+ SDLSource* src = nullptr;
+ std::vector<SDLSource*>::iterator it = sources.begin();
+ for (; it != sources.end();)
+ {
+ src = *it;
+ if (src != nullptr)
+ src->process(buf, size);
+ ++it;
+ }
+ }
+
+ void Manager::removeSource(SDLSource* source)
+ {
+ std::vector<SDLSource*>::iterator it = sources.begin();
+ for (it = sources.begin(); it != sources.end(); )
+ {
+ if (*it == source)
+ {
+ it = sources.erase(it);
+ return;
+ }
+ ++it;
+ }
+ }
+
+ void Manager::removeAllSource()
+ {
+ sources.clear();
+ }
+
+ void Manager::pushSource(SDLSource* source)
+ {
+ if (source != nullptr)
+ sources.push_back(source);
+ }
+
+ void Manager::pushCommand(SDLSourceCommand* cmd)
+ {
+ commands.push(cmd);
+ }
+
+ Command* Manager::getCommand()
+ {
+ if (!commandsPool.empty())
+ {
+ Command* cmd = commandsPool.top();
+ commandsPool.pop();
+ return cmd;
+ }
+ return new Command();
+ }
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl) \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_source.h b/src/libjin/audio/SDL/je_sdl_source.h
new file mode 100644
index 0000000..bbc8c7a
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_source.h
@@ -0,0 +1,272 @@
+#ifndef __JE_SOURCE_SDL_H__
+#define __JE_SOURCE_SDL_H__
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <vector>
+#include <queue>
+#include <stack>
+#include <exception>
+
+#include "../je_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+ typedef struct SDLSourceCommand;
+
+ class SDLSourceManager;
+
+ ///
+ /// Audio source SDL implementation.
+ ///
+ class SDLSource : public Source
+ {
+ public:
+ ///
+ /// Create source from raw source data file.
+ ///
+ /// @param file Audio source file.
+ /// @return Return source if create successful, otherwise return null.
+ ///
+ static SDLSource* createSource(const char* file);
+
+ ///
+ /// Create source from raw source data.
+ ///
+ /// @param mem Source data.
+ /// @param size Source data size.
+ /// @return Return source if create successful, otherwise return null.
+ ///
+ static SDLSource* createSource(void* mem, size_t size);
+
+ ///
+ /// Source destructor.
+ ///
+ ~SDLSource();
+
+ ///
+ /// Play source.
+ ///
+ void play() override;
+
+ ///
+ /// Stop source.
+ ///
+ void stop() override;
+
+ ///
+ /// Pause source.
+ ///
+ void pause() override;
+
+ ///
+ /// Resume source.
+ ///
+ void resume() override;
+
+ ///
+ /// Rewind source.
+ ///
+ void rewind() override;
+
+ ///
+ /// Return if the source is stopped.
+ ///
+ /// @return True if the source is stopped, otherwise return false.
+ ///
+ bool isStopped() const override;
+
+ ///
+ /// Return if the source is paused.
+ ///
+ /// @return True if the source is paused(, otherwise return false.
+ ///
+ bool isPaused() const override;
+
+ ///
+ /// Set pitch.
+ ///
+ /// @param pitch Pitch of source.
+ ///
+ void setPitch(float pitch) override;
+
+ ///
+ /// Set volume.
+ ///
+ /// @param volume Volume of source.
+ ///
+ void setVolume(float volume) override;
+
+ ///
+ /// Set source loop.
+ ///
+ /// @param loop Looping or not.
+ ///
+ void setLoop(bool loop) override;
+
+ ///
+ /// Set source rate.
+ ///
+ /// @param rate Rate of source.
+ ///
+ void setRate(float rate) override;
+
+ ///
+ /// Handle a specific command.
+ ///
+ /// @param manager Audio manager.
+ /// @param cmd Source commad.
+ ///
+ inline void handle(SDLSourceManager* manager, SDLSourceCommand* cmd);
+
+ ///
+ /// Process decoded source data.
+ ///
+ /// @param buffer Source data.
+ /// @param size Data size.
+ ///
+ inline void process(void* buffer, size_t size);
+
+ protected:
+ ///
+ /// Source constructor.
+ ///
+ SDLSource();
+
+ ///
+ /// Decode wav file.
+ ///
+ /// @param mem Wav file data.
+ /// @param size Wav data size.
+ ///
+ void decode_wav(void* mem, int size);
+
+ ///
+ /// Decode ogg file.
+ ///
+ /// @param mem ogg file data.
+ /// @param size ogg data size.
+ ///
+ void decode_ogg(void* mem, int size);
+
+ ///
+ /// Check source state.
+ ///
+ /// @param state State to be checked.
+ /// @return True if state is given state, otherwise return false.
+ ///
+ inline bool is(int state) const
+ {
+ return (status.state & state) == state;
+ }
+
+ // Source data.
+ struct {
+ const void* data; // Ƶ
+ int length; // dataֽڳ
+ const void* end; // dataβ = (unsigned char*)data + size
+ int samplerate; // Ƶ
+ unsigned char bitdepth; // ÿsampleıس
+ int samples; // sample = size / (bitdepth / 8)
+ unsigned char channels; // channel1(mono)2(stereo)
+ } raw;
+ // Procedure controller variable.
+ struct {
+ int pos; // ǰŵsample
+ int pitch; // pitch
+ int state; // ǰ״̬
+ bool loop; // loop or not
+ float volume; //
+ } status;
+
+ };
+
+ ///
+ /// Source manager.
+ ///
+ class SDLSourceManager
+ {
+ public:
+ ///
+ /// Get manager singleton.
+ ///
+ /// @return Singleton of SDL source manager.
+ ///
+ static SDLSourceManager* get();
+
+ ///
+ /// Process commands.
+ ///
+ void processCommands();
+
+ ///
+ /// Process sources.
+ ///
+ /// @param buffer Source data.
+ /// @param size Size of source data.
+ ///
+ void processSources(void* buffer, size_t size);
+
+ ///
+ /// Clear source queue.
+ ///
+ /// This function will stop all sources.
+ ///
+ void removeAllSource();
+
+ ///
+ /// Remove specific source.
+ ///
+ /// @param source SDL audio source.
+ ///
+ void removeSource(SDLSource* source);
+
+ ///
+ /// Push specific source into queue.
+ ///
+ /// @param source SDL audio source.
+ ///
+ void pushSource(SDLSource* source);
+
+ ///
+ /// Get command from queue.
+ ///
+ /// @return Command at first place.
+ ///
+ SDLSourceCommand* getCommand();
+
+ ///
+ /// Push command.
+ ///
+ /// @param cmd Spcific command.
+ ///
+ void pushCommand(SDLSourceCommand* cmd);
+
+ private:
+ std::queue<SDLSourceCommand*> commands;
+ std::stack<SDLSourceCommand*> commandsPool;
+ std::vector<SDLSource*> sources; // processing sources
+ static SDLSourceManager* manager;
+
+ };
+
+ class SourceException : public std::exception
+ {
+ const char* what() const throw ()
+ {
+ return "Load Source Exception";
+ }
+ };
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl)
+
+#endif // __JE_SOURCE_SDL_H__ \ No newline at end of file
diff --git a/src/libjin/audio/je_audio_manager.cpp b/src/libjin/audio/je_audio_manager.cpp
new file mode 100644
index 0000000..e451aa3
--- /dev/null
+++ b/src/libjin/audio/je_audio_manager.cpp
@@ -0,0 +1,15 @@
+#include "../core/je_configuration.h"
+#if jin_audio
+
+#include "SDL2/SDL.h"
+#include "je_audio_manager.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
diff --git a/src/libjin/audio/je_audio_manager.h b/src/libjin/audio/je_audio_manager.h
new file mode 100644
index 0000000..120d38e
--- /dev/null
+++ b/src/libjin/audio/je_audio_manager.h
@@ -0,0 +1,87 @@
+#ifndef __JE_AUDIO_H__
+#define __JE_AUDIO_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include "../utils/je_macros.h"
+#include "../common/je_subsystem.hpp"
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ class Source;
+
+ ///
+ /// Audio manager.
+ ///
+ template<class SubAudio>
+ class AudioManager : public Subsystem<SubAudio>
+ {
+ public:
+ ///
+ /// Audio state.
+ ///
+ enum State
+ {
+ PLAY ,
+ STOP ,
+ PAUSE,
+ };
+
+ ///
+ /// Play all sources whose state is playing.
+ ///
+ virtual void play() = 0;
+
+ ///
+ /// Stop and remove all sources from the queue.
+ ///
+ virtual void stop() = 0;
+
+ ///
+ /// Pause audio.
+ ///
+ virtual void pause() = 0;
+
+ ///
+ /// Resume audio.
+ ///
+ virtual void resume() = 0;
+
+ ///
+ /// Set global audio volume.
+ ///
+ virtual void setVolume(float volume) = 0;
+
+ protected:
+ singleton(AudioManager);
+
+ ///
+ /// AudioManager constructor.
+ ///
+ AudioManager()
+ : volume(1)
+ , state(State::PLAY)
+ {};
+
+ ///
+ /// AudioManager destructor.
+ ///
+ virtual ~AudioManager() {};
+
+ float volume;
+ State state;
+
+ };
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
+
+#endif // __JE_AUDIO_H__ \ No newline at end of file
diff --git a/src/libjin/audio/je_source.cpp b/src/libjin/audio/je_source.cpp
new file mode 100644
index 0000000..6bc1f4f
--- /dev/null
+++ b/src/libjin/audio/je_source.cpp
@@ -0,0 +1,30 @@
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include <cstring>
+
+#include "je_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ static int check_header(const void *data, int size, const char *str, int offset) {
+ int len = strlen(str);
+ return (size >= offset + len) && !memcmp((char*)data + offset, str, len);
+ }
+
+ SourceType Source::getType(const void* mem, int size)
+ {
+ if(check_header(mem, size, "WAVE", 8))
+ return SourceType::WAV;
+ if(check_header(mem, size, "OggS", 0))
+ return SourceType::OGG;
+ return SourceType::INVALID;
+ }
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
diff --git a/src/libjin/audio/je_source.h b/src/libjin/audio/je_source.h
new file mode 100644
index 0000000..f60daf9
--- /dev/null
+++ b/src/libjin/audio/je_source.h
@@ -0,0 +1,116 @@
+#ifndef __JE_AUDIO_SOURCE_H__
+#define __JE_AUDIO_SOURCE_H__
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ ///
+ /// Audio source encoding type.
+ ///
+ enum SourceType
+ {
+ INVALID = 0,
+ WAV,
+ OGG,
+ };
+
+ ///
+ /// Audio source.
+ ///
+ class Source
+ {
+ public:
+ ///
+ /// Source constructor.
+ ///
+ Source() {};
+
+ ///
+ /// Source destructor.
+ ///
+ virtual ~Source() {};
+
+ ///
+ /// Start playing source.
+ ///
+ virtual void play() = 0;
+
+ ///
+ /// Stop playing source.
+ ///
+ virtual void stop() = 0;
+
+ ///
+ /// Pause source.
+ ///
+ virtual void pause() = 0;
+
+ ///
+ /// Resume source.
+ ///
+ virtual void resume() = 0;
+
+ ///
+ /// Rewind source.
+ ///
+ virtual void rewind() = 0;
+
+ ///
+ /// Whether the source is playing or not.
+ ///
+ virtual bool isStopped() const = 0;
+
+ ///
+ /// Whether the source is paused or not.
+ ///
+ virtual bool isPaused() const = 0;
+
+ ///
+ /// Set source pitch.
+ ///
+ /// @param pitch Pitch of source.
+ ///
+ virtual void setPitch(float pitch) = 0;
+
+ ///
+ /// Set volume of source.
+ ///
+ /// @param volume Volume of source.
+ ///
+ virtual void setVolume(float volume) = 0;
+
+ ///
+ /// Set source loop.
+ ///
+ /// @param loop Looping or not.
+ ///
+ virtual void setLoop(bool loop) = 0;
+
+ ///
+ /// Set source rate.
+ ///
+ /// @param rate Rate of source.
+ ///
+ virtual void setRate(float rate) = 0;
+
+ protected:
+
+ ///
+ /// Get type of source data.
+ ///
+ static SourceType getType(const void* mem, int size);
+
+ };
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
+
+#endif // __JE_AUDIO_SOURCE_H__ \ No newline at end of file
diff --git a/src/libjin/common/je_array.hpp b/src/libjin/common/je_array.hpp
new file mode 100644
index 0000000..8a5cbf1
--- /dev/null
+++ b/src/libjin/common/je_array.hpp
@@ -0,0 +1,123 @@
+#ifndef __JE_COMMON_ARRAY_H__
+#define __JE_COMMON_ARRAY_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// A array created on heap.
+ ///
+ template<typename T>
+ class Array
+ {
+ public:
+ ///
+ /// Array constructor.
+ ///
+ Array()
+ : length(0)
+ , data(nullptr)
+ {
+ }
+
+ ///
+ /// Array constructor.
+ ///
+ /// @param l Length of array.
+ ///
+ Array(int l)
+ {
+ length = l;
+ data = new T[l];
+ }
+
+ ///
+ /// Array destructor.
+ ///
+ ~Array()
+ {
+ delete[] data;
+ length = 0;
+ }
+
+ ///
+ /// Get address of data.
+ ///
+ /// @return Address of data.
+ ///
+ T* operator &()
+ {
+ return data;
+ }
+
+ ///
+ /// Get specific element of array.
+ ///
+ /// @return Element of array.
+ ///
+ T& operator[](int index)
+ {
+ return data[index];
+ }
+
+ ///
+ /// Bind data with given data.
+ ///
+ /// @param data Data pointer.
+ /// @param length Length of data.
+ ///
+ void bind(T* data, int length)
+ {
+ if (data != nullptr)
+ delete data;
+ this->data = data;
+ this->length = length;
+ }
+
+ ///
+ /// Add an element.
+ ///
+ /// @param v Value of element.
+ ///
+ void add(T value)
+ {
+ int len = length + 1;
+ T* d = new T[len];
+ memcpy(d, data, size());
+ d[length] = value;
+ bind(d, len);
+ }
+
+ ///
+ /// Get size of data in byte.
+ ///
+ /// @return Size of data in byte.
+ ///
+ int size()
+ {
+ return sizeof(T) * length;
+ }
+
+ ///
+ /// Get length of data.
+ ///
+ /// @return Count of data.
+ ///
+ int count()
+ {
+ return length;
+ }
+
+ private:
+ // Disable new and delete.
+ void* operator new(size_t t);
+ void operator delete(void* ptr);
+
+ T * data;
+ unsigned int length;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_common.h b/src/libjin/common/je_common.h
new file mode 100644
index 0000000..31b67c6
--- /dev/null
+++ b/src/libjin/common/je_common.h
@@ -0,0 +1,7 @@
+#ifndef __JE_COMMON_H__
+#define __JE_COMMON_H__
+
+#include "je_exception.h"
+#include "je_array.hpp"
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_exception.cpp b/src/libjin/common/je_exception.cpp
new file mode 100644
index 0000000..5489a42
--- /dev/null
+++ b/src/libjin/common/je_exception.cpp
@@ -0,0 +1,46 @@
+#include <stdarg.h>
+
+#include "je_exception.h"
+
+namespace JinEngine
+{
+
+ Exception::Exception(const char *fmt, ...)
+ {
+ va_list args;
+ int size_buffer = 256, size_out;
+ char *buffer;
+ while (true)
+ {
+ buffer = new char[size_buffer];
+ memset(buffer, 0, size_buffer);
+
+ va_start(args, fmt);
+ size_out = vsnprintf(buffer, size_buffer, fmt, args);
+ va_end(args);
+
+ // see http://perfec.to/vsnprintf/pasprintf.c
+ // if size_out ...
+ // == -1 --> output was truncated
+ // == size_buffer --> output was truncated
+ // == size_buffer-1 --> ambiguous, /may/ have been truncated
+ // > size_buffer --> output was truncated, and size_out
+ // bytes would have been written
+ if (size_out == size_buffer || size_out == -1 || size_out == size_buffer - 1)
+ size_buffer *= 2;
+ else if (size_out > size_buffer)
+ size_buffer = size_out + 2; // to avoid the ambiguous case
+ else
+ break;
+
+ delete[] buffer;
+ }
+ mMessage = std::string(buffer);
+ delete[] buffer;
+ }
+
+ Exception::~Exception() throw()
+ {
+ }
+
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/common/je_exception.h b/src/libjin/common/je_exception.h
new file mode 100644
index 0000000..c319ebd
--- /dev/null
+++ b/src/libjin/common/je_exception.h
@@ -0,0 +1,45 @@
+#ifndef __JE_EXCEPTION_H__
+#define __JE_EXCEPTION_H__
+
+#include <exception>
+#include <string>
+
+namespace JinEngine
+{
+
+ ///
+ /// Jin Exception.
+ ///
+ class Exception : public std::exception
+ {
+ public:
+
+ ///
+ /// Creates a new Exception according to printf-rules.
+ ///
+ /// @param fmt The format string (see printf).
+ ///
+ Exception(const char *fmt, ...);
+ virtual ~Exception() throw();
+
+ ///
+ /// Returns a string containing reason for the exception.
+ ///
+ /// @return A description of the exception.
+ ///
+ inline virtual const char *what() const throw()
+ {
+ return mMessage.c_str();
+ }
+
+ private:
+ ///
+ /// Exception message.
+ ///
+ std::string mMessage;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_noncopyable.h b/src/libjin/common/je_noncopyable.h
new file mode 100644
index 0000000..eff7121
--- /dev/null
+++ b/src/libjin/common/je_noncopyable.h
@@ -0,0 +1,24 @@
+#ifndef __JE_NONCOPYABLE_H__
+#define __JE_NONCOPYABLE_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Class inherites this could not be copied.
+ ///
+ class Noncopyable
+ {
+ public:
+ Noncopyable(void) { }
+ virtual ~Noncopyable(void) { }
+
+ private:
+ Noncopyable(const Noncopyable& other);
+ Noncopyable& operator=(const Noncopyable& other);
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_object.h b/src/libjin/common/je_object.h
new file mode 100644
index 0000000..fb8221f
--- /dev/null
+++ b/src/libjin/common/je_object.h
@@ -0,0 +1,16 @@
+#ifndef __JE_OBJECT_H__
+#define __JE_OBJECT_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Base class of all objects in Jin.
+ ///
+ class Object
+ {
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_pool.hpp b/src/libjin/common/je_pool.hpp
new file mode 100644
index 0000000..cb96c5b
--- /dev/null
+++ b/src/libjin/common/je_pool.hpp
@@ -0,0 +1,177 @@
+///
+/// An O(1) Object Pool. Original from https://www.codeproject.com/Articles/746630/O-Object-Pool-in-Cplusplus
+///
+#ifndef __JE_POOL_H__
+#define __JE_POOL_H__
+
+#include <stdlib.h>
+#include <iostream>
+
+#include "je_types.h"
+
+namespace JinEngine
+{
+
+ class DefaultMemoryAllocator
+ {
+ public:
+ static inline void *Allocate(size_t size)
+ {
+ return ::operator new(size, ::std::nothrow);
+ }
+ static inline void Deallocate(void *pointer, size_t size)
+ {
+ ::operator delete(pointer);
+ }
+ };
+
+ template<typename T, class TMemoryAllocator = DefaultMemoryAllocator>
+ class Pool
+ {
+ private:
+ struct _Node
+ {
+ void *_memory;
+ size_t _capacity;
+ _Node *_nextNode;
+
+ _Node(size_t capacity)
+ {
+ if (capacity < 1)
+ throw std::invalid_argument("capacity must be at least 1.");
+
+ _memory = TMemoryAllocator::Allocate(_itemSize * capacity);
+ if (_memory == NULL)
+ throw std::bad_alloc();
+
+ _capacity = capacity;
+ _nextNode = NULL;
+ }
+ ~_Node()
+ {
+ TMemoryAllocator::Deallocate(_memory, _itemSize * _capacity);
+ }
+ };
+
+ void *_nodeMemory;
+ T *_firstDeleted;
+ size_t _countInNode;
+ size_t _nodeCapacity;
+ _Node _firstNode;
+ _Node *_lastNode;
+ size_t _maxBlockLength;
+
+ static const size_t _itemSize;
+
+ Pool(const Pool<T, TMemoryAllocator> &source);
+ void operator = (const Pool<T, TMemoryAllocator> &source);
+
+ void _AllocateNewNode()
+ {
+ size_t size = _countInNode;
+ if (size >= _maxBlockLength)
+ size = _maxBlockLength;
+ else
+ {
+ size *= 2;
+
+ if (size < _countInNode)
+ throw std::overflow_error("size became too big.");
+
+ if (size >= _maxBlockLength)
+ size = _maxBlockLength;
+ }
+
+ _Node *newNode = new _Node(size);
+ _lastNode->_nextNode = newNode;
+ _lastNode = newNode;
+ _nodeMemory = newNode->_memory;
+ _countInNode = 0;
+ _nodeCapacity = size;
+ }
+
+ public:
+ explicit Pool(size_t initialCapacity = 32, size_t maxBlockLength = 1000000)
+ : _firstDeleted(NULL)
+ , _countInNode(0)
+ , _nodeCapacity(initialCapacity)
+ , _firstNode(initialCapacity)
+ , _maxBlockLength(maxBlockLength)
+ {
+ if (maxBlockLength < 1)
+ throw std::invalid_argument("maxBlockLength must be at least 1.");
+
+ _nodeMemory = _firstNode._memory;
+ _lastNode = &_firstNode;
+ }
+ ~Pool()
+ {
+ _Node *node = _firstNode._nextNode;
+ while (node)
+ {
+ _Node *nextNode = node->_nextNode;
+ delete node;
+ node = nextNode;
+ }
+ }
+
+ T *New()
+ {
+ if (_firstDeleted)
+ {
+ T *result = _firstDeleted;
+ _firstDeleted = *((T **)_firstDeleted);
+ new(result) T();
+ return result;
+ }
+
+ if (_countInNode >= _nodeCapacity)
+ _AllocateNewNode();
+
+ char *address = (char *)_nodeMemory;
+ address += _countInNode * _itemSize;
+ T *result = new(address) T();
+ _countInNode++;
+ return result;
+ }
+
+ // This method is useful if you want to call a non-default constructor.
+ // It should be used like this:
+ // new (pool.GetNextWithoutInitializing()) ObjectType(... parameters ...);
+ T *GetNextWithoutInitializing()
+ {
+ if (_firstDeleted)
+ {
+ T *result = (T *)_firstDeleted;
+ _firstDeleted = *((T **)_firstDeleted);
+ return result;
+ }
+
+ if (_countInNode >= _nodeCapacity)
+ _AllocateNewNode();
+
+ char *address = (char *)_nodeMemory;
+ address += _countInNode * _itemSize;
+ _countInNode++;
+ return (T *)address;
+ }
+ void Delete(T *content)
+ {
+ content->~T();
+
+ *((T **)content) = _firstDeleted;
+ _firstDeleted = content;
+ }
+ void DeleteWithoutDestroying(T *content)
+ {
+ *((T **)content) = _firstDeleted;
+ _firstDeleted = content;
+ }
+ };
+
+ template<typename T, class TMemoryAllocator>
+ const size_t Pool<T, TMemoryAllocator>::_itemSize = ((sizeof(T) + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *);
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_singleton.hpp b/src/libjin/common/je_singleton.hpp
new file mode 100644
index 0000000..d7f52c9
--- /dev/null
+++ b/src/libjin/common/je_singleton.hpp
@@ -0,0 +1,80 @@
+#ifndef __JE_SINGLETON_H__
+#define __JE_SINGLETON_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Singleton base class.
+ ///
+ template<class T>
+ class Singleton
+ {
+ public:
+ ///
+ /// Get singleton.
+ ///
+ /// @param Singleton instance of class.
+ ///
+ static T* get()
+ {
+ if (_instance == nullptr)
+ _instance = new T;
+ return _instance;
+ }
+
+ ///
+ /// Destroy instance of singleton.
+ ///
+ static void destroy()
+ {
+ delete _instance;
+ _instance = nullptr;
+ }
+
+ protected:
+ ///
+ /// Singleton constructor.
+ ///
+ Singleton() {};
+
+ ///
+ /// Singleton destructor.
+ ///
+ virtual ~Singleton() {};
+
+ ///
+ /// Singleton instance.
+ ///
+ static T* _instance;
+
+ private:
+ ///
+ /// Singleton copy constructor.
+ ///
+ /// @param singleton Singleton of class.
+ ///
+ Singleton(const Singleton& singleton);
+
+ ///
+ /// Singleton assignment.
+ ///
+ /// @param singleton Singleton of class.
+ ///
+ Singleton& operator = (const Singleton& singleton);
+
+ };
+
+ ///
+ /// Singleton instance.
+ ///
+ template<class T> T* Singleton<T>::_instance = nullptr;
+
+ ///
+ /// Singleton notation.
+ ///
+ #define singleton(T) friend Singleton<T>
+
+} // namespace JinEngine
+
+#endif // __JE_SINGLETON_H__
diff --git a/src/libjin/common/je_stringmap.hpp b/src/libjin/common/je_stringmap.hpp
new file mode 100644
index 0000000..7a3bd80
--- /dev/null
+++ b/src/libjin/common/je_stringmap.hpp
@@ -0,0 +1,143 @@
+#ifndef __JE_COMMON_SREINGMAP_H__
+#define __JE_COMMON_SREINGMAP_H__
+
+namespace JinEngine
+{
+
+ template<typename T, unsigned SIZE>
+ class StringMap
+ {
+ private:
+
+ struct Record
+ {
+ const char * key;
+ T value;
+ bool set;
+ Record() : set(false) {}
+ };
+
+ const static unsigned MAX = SIZE * 2;
+
+ Record records[MAX];
+ const char * reverse[SIZE];
+
+ public:
+
+ struct Entry
+ {
+ const char * key;
+ T value;
+ };
+
+ StringMap(Entry * entries, unsigned num)
+ {
+
+ for (unsigned i = 0; i < SIZE; ++i)
+ reverse[i] = 0;
+
+ unsigned n = num / sizeof(Entry);
+
+ for (unsigned i = 0; i < n; ++i)
+ {
+ add(entries[i].key, entries[i].value);
+ }
+ }
+
+ bool streq(const char * a, const char * b)
+ {
+ while (*a != 0 && *b != 0)
+ {
+ if (*a != *b)
+ return false;
+ ++a;
+ ++b;
+ }
+
+ return (*a == 0 && *b == 0);
+ }
+
+ bool find(const char * key, T & t)
+ {
+ //unsigned str_hash = djb2(key);
+
+ for (unsigned i = 0; i < MAX; ++i)
+ {
+ //unsigned str_i = (str_hash + i) % MAX; //this isn't used, is this intentional?
+
+ if (records[i].set && streq(records[i].key, key))
+ {
+ t = records[i].value;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool find(T key, const char *& str)
+ {
+ unsigned index = (unsigned)key;
+
+ if (index >= SIZE)
+ return false;
+
+ if (reverse[index] != 0)
+ {
+ str = reverse[index];
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool add(const char * key, T value)
+ {
+ unsigned str_hash = djb2(key);
+ bool inserted = false;
+
+ for (unsigned i = 0; i < MAX; ++i)
+ {
+ unsigned str_i = (str_hash + i) % MAX;
+
+ if (!records[str_i].set)
+ {
+ inserted = true;
+ records[str_i].set = true;
+ records[str_i].key = key;
+ records[str_i].value = value;
+ break;
+ }
+ }
+
+ unsigned index = (unsigned)value;
+
+ if (index >= SIZE)
+ {
+ printf("\nConstant %s out of bounds with %i!\n", key, index);
+ return false;
+ }
+
+ reverse[index] = key;
+
+ return inserted;
+ }
+
+ unsigned djb2(const char * key)
+ {
+ unsigned hash = 5381;
+ int c;
+
+ while ((c = *key++))
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+ }
+
+ }; // StringMap
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_subsystem.hpp b/src/libjin/common/je_subsystem.hpp
new file mode 100644
index 0000000..c015bef
--- /dev/null
+++ b/src/libjin/common/je_subsystem.hpp
@@ -0,0 +1,78 @@
+#ifndef __JE_COMMON_SUBSYSTEM_H__
+#define __JE_COMMON_SUBSYSTEM_H__
+
+#include "../utils/je_macros.h"
+
+#include "je_singleton.hpp"
+
+namespace JinEngine
+{
+
+ ///
+ /// Subsystem class.
+ ///
+ template<class System>
+ class Subsystem : public Singleton<System>
+ {
+ public:
+ ///
+ /// Subsystem setting.
+ ///
+ struct Setting
+ {
+ };
+
+ typedef Setting SettingBase;
+
+ ///
+ /// Initialize subsystem.
+ ///
+ /// @param setting Subsystem setting.
+ /// @return True if initialize sucessful, otherwise return false.
+ ///
+ bool init(const SettingBase* setting = nullptr)
+ {
+ static bool success = initSystem(setting);
+ return success;
+ }
+
+ ///
+ /// Quit subsystem.
+ ///
+ void quit()
+ {
+ // Call only once.
+ static char __dummy__ = (quitSystem(), 1);
+ Singleton<System>::destroy();
+ }
+
+ protected:
+ singleton(System);
+
+ ///
+ /// Subsystem constructor.
+ ///
+ Subsystem() {};
+
+ ///
+ /// Subsystem destructor.
+ ///
+ virtual ~Subsystem()
+ {
+ };
+
+ ///
+ /// Initializer callback.
+ ///
+ virtual bool initSystem(const Setting* setting) = 0;
+
+ ///
+ /// Quit subsystem callback.
+ ///
+ virtual void quitSystem() = 0;
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_temporary.h b/src/libjin/common/je_temporary.h
new file mode 100644
index 0000000..647bfba
--- /dev/null
+++ b/src/libjin/common/je_temporary.h
@@ -0,0 +1,31 @@
+#ifndef __JE_TEMPORARY_H__
+#define __JE_TEMPORARY_H__
+
+namespace JinEngine
+{
+
+ ///
+ /// Class inherites this clound only be created on stack or static zone.
+ ///
+ class Temporary
+ {
+ public:
+ Temporary() {};
+ virtual ~Temporary() {};
+/*
+ protected:
+ void operator delete(void* t)
+ {
+ if(t != nullptr)
+ free(t);
+ }
+*/
+ private:
+ // Disable new operands.
+ void* operator new(size_t);
+
+ };
+
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_types.h b/src/libjin/common/je_types.h
new file mode 100644
index 0000000..e31ce5e
--- /dev/null
+++ b/src/libjin/common/je_types.h
@@ -0,0 +1,34 @@
+#ifndef __JE_TYPES_H__
+#define __JE_TYPES_H__
+#include <stdint.h>
+#include <stdlib.h>
+#include <cstring>
+
+namespace JinEngine
+{
+
+ typedef int8_t int8; ///< Signed integer with a size of 8 bits. Supports values from -128 to 127
+ typedef uint8_t uint8; ///< Unsigned integer with a size of 8 bits. Supports values from 0 to 255.
+ typedef uint8 byte; ///< Unsigned integer with 8 bits (1 byte). Supports 256 values from 0 to 255.
+ typedef int16_t int16; ///< Signed integer with a size of 16 bits. Supports values from -32768 to 32767
+ typedef uint16_t uint16; ///< Unsigned integer with a size of 16 bits. Supports values from 0 to 65535.
+ typedef int32_t int32; ///< Signed integer with a size of 32 bits. Supports values from -2147483648 to 2147483647.
+ typedef uint32_t uint32; ///< Unsigned integer with a size of 32 bits. Supports values from 0 to 4294967295, (2^32 - 1).
+ typedef int64_t int64; ///< Signed integer with a size of 64 bits. Supports values from -(2^63) to (2^63 - 1).
+ typedef uint64_t uint64; ///< Unsigned integer with a size of 64 bits, Supports values from 0 to (2^64 - 1).
+
+ typedef uint32_t uint;
+ typedef int32_t sint;
+
+#define Union(name, ...) \
+union _Ctor{ \
+ _Ctor() { memset(this, 0, sizeof(*this)); } \
+ __VA_ARGS__; \
+} name;
+
+#define Struct(name, ...) \
+struct {__VA_ARGS__;} name;
+
+}
+
+#endif \ No newline at end of file
diff --git a/src/libjin/common/je_utf8.cpp b/src/libjin/common/je_utf8.cpp
new file mode 100644
index 0000000..bd7ce94
--- /dev/null
+++ b/src/libjin/common/je_utf8.cpp
@@ -0,0 +1,42 @@
+#include "../core/je_configuration.h"
+#if jin_os == jin_os_windows
+
+#include "je_utf8.h"
+
+namespace JinEngine
+{
+
+ std::string to_utf8(LPCWSTR wstr)
+ {
+ size_t wide_len = wcslen(wstr) + 1;
+
+ // Get size in UTF-8.
+ int utf8_size = WideCharToMultiByte(CP_UTF8, 0, wstr, wide_len, 0, 0, 0, 0);
+
+ char * utf8_str = new char[utf8_size];
+
+ // Convert to UTF-8.
+ int ok = WideCharToMultiByte(CP_UTF8, 0, wstr, wide_len, utf8_str, utf8_size, 0, 0);
+
+ if (!ok)
+ {
+ delete[] utf8_str;
+ }
+
+ return ok ? std::string(utf8_str) : std::string();
+ }
+
+ void replace_char(std::string & str, char find, char replace)
+ {
+ int length = str.length();
+
+ for (int i = 0; i<length; i++)
+ {
+ if (str[i] == find)
+ str[i] = replace;
+ }
+ }
+
+} // namespace JinEngine
+
+#endif // jin_os == jin_os_windows \ No newline at end of file
diff --git a/src/libjin/common/je_utf8.h b/src/libjin/common/je_utf8.h
new file mode 100644
index 0000000..d840b75
--- /dev/null
+++ b/src/libjin/common/je_utf8.h
@@ -0,0 +1,34 @@
+#ifndef __JE_COMMON_UTF8_H__
+#define __JE_COMMON_UTF8_H__
+
+#include "../core/je_configuration.h"
+#if jin_os == jin_os_windows
+
+#include <string>
+#include <windows.h>
+
+namespace JinEngine
+{
+
+ ///
+ /// Convert the wide string to a UTF-8 encoded string.
+ ///
+ /// @param wstr The wide-char string.
+ /// @return A UTF-8 string.
+ ///
+ std::string to_utf8(LPCWSTR wstr);
+
+ ///
+ /// Replace all occurences of 'find' with 'replace' in a string.
+ ///
+ /// @param str The string to modify.
+ /// @param find The character to match.
+ /// @param replace The character to replace matches.
+ ///
+ void replace_char(std::string & str, char find, char replace);
+
+} // namespace JinEngine
+
+#endif // jin_os == jin_os_windows
+
+#endif // __JE_COMMON_UTF8_H__
diff --git a/src/libjin/core/README.md b/src/libjin/core/README.md
new file mode 100644
index 0000000..b393007
--- /dev/null
+++ b/src/libjin/core/README.md
@@ -0,0 +1 @@
+ļµļƱṩһЩ汾Ϣ \ No newline at end of file
diff --git a/src/libjin/core/je_configuration.h b/src/libjin/core/je_configuration.h
new file mode 100644
index 0000000..80e2748
--- /dev/null
+++ b/src/libjin/core/je_configuration.h
@@ -0,0 +1,58 @@
+#ifndef __JE_COMMON_MODULES_H__
+#define __JE_COMMON_MODULES_H__
+
+#define jin_undefined 0x00
+
+///
+/// Debug output
+///
+#define jin_debug
+
+#define jin_os_windows 0x01
+#define jin_os_mac 0x02
+#define jin_os_linux 0x03
+#define jin_os jin_os_windows
+
+#define jin_graphics_font 0x02
+#define jin_graphics_shader 0x04
+#define jin_graphics_particle 0x08
+#define jin_graphics_animation 0x10
+#define jin_graphics (jin_graphics_font|jin_graphics_shader)
+
+#define jin_audio_sdl 0x01
+#define jin_audio_openal 0x02
+#define jin_audio jin_audio_sdl
+
+#define jin_filesystem_smount 0x01
+#define jin_filesystem jin_filesystem_smount
+
+#define jin_game
+
+#define jin_core
+
+#define jin_input_sdl 0x01
+#define jin_input jin_input_sdl
+
+#define jin_math
+
+#define jin_net_tekcos 0x01
+#define jin_net jin_net_tekcos
+
+#define jin_physics_newton 0x01
+#define jin_physics_box2d 0x02
+//#define jin_physics jin_physics_newton
+
+#define jin_thread_sdl 0x01
+#define jin_thread_cpp 0x02
+#define jin_thread_pthread 0x03
+#define jin_thread jin_thread_sdl
+
+#define jin_time_sdl 0x01
+#define jin_time_cpp 0x02
+#define jin_time jin_time_sdl
+
+#define jin_ai
+
+#define JIN_EXPORT __declspec(dllexport)
+
+#endif // __JE_COMMON_MODULES_H__ \ No newline at end of file
diff --git a/src/libjin/core/je_version.h b/src/libjin/core/je_version.h
new file mode 100644
index 0000000..b5fe379
--- /dev/null
+++ b/src/libjin/core/je_version.h
@@ -0,0 +1,52 @@
+#ifndef __JE_CORE_VERSION_H__
+#define __JE_CORE_VERSION_H__
+
+namespace JinEngine
+{
+ namespace Core
+ {
+
+ ///
+ /// Get version of Jin.
+ ///
+ /// @return Version of Jin.
+ ///
+ const char* getVersion()
+ {
+ return "Jin 0.1";
+ }
+
+ ///
+ /// Get author of Jin.
+ ///
+ /// @return Author of Jin.
+ ///
+ const char* getAuthor()
+ {
+ return "Chai";
+ }
+
+ ///
+ /// Get release of Jin.
+ ///
+ /// @return release string of Jin.
+ ///
+ const char* getRelease()
+ {
+ return "Jin 0.1.1";
+ }
+
+ ///
+ /// Get release of Jin.
+ ///
+ /// @return Revision of Jin.
+ ///
+ int getRevision()
+ {
+ return 101;
+ }
+
+ } // namespace Core
+} // namespace JinEngine
+
+#endif // __JE_CORE_VERSION_H__
diff --git a/src/libjin/filesystem/je_asset_database.cpp b/src/libjin/filesystem/je_asset_database.cpp
new file mode 100644
index 0000000..a8524c5
--- /dev/null
+++ b/src/libjin/filesystem/je_asset_database.cpp
@@ -0,0 +1,85 @@
+#include "../core/je_configuration.h"
+#if defined(jin_filesystem)
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h> /* defines FILENAME_MAX */
+
+#include "../common/je_exception.h"
+
+#include "je_asset_database.h"
+
+namespace JinEngine
+{
+ namespace Filesystem
+ {
+
+ AssetDatabase* AssetDatabase::mAssetDatabase = 0;
+
+ AssetDatabase::AssetDatabase()
+ {
+ mSmt = smtnewshared();
+ }
+
+ AssetDatabase* AssetDatabase::get()
+ {
+ return mAssetDatabase ? mAssetDatabase : (mAssetDatabase = new AssetDatabase());
+ }
+
+ void AssetDatabase::mount(const char * path)
+ {
+ int err = smtmount(mSmt, path);
+ if (err)
+ {
+ printf("%s mounted path %s", smterrstr(err), path);
+ exit(1);
+ }
+ }
+
+ void AssetDatabase::read(const char* path, Buffer& buffer)
+ {
+ size_t size;
+ byte* data = (byte*)smtread(mSmt, path, &size);
+ if (data == nullptr)
+ throw Exception("Could not read file %s.", path);
+ buffer.bind(data, size);
+ }
+
+ Buffer* read(const char* path)
+ {
+ return nullptr;
+ }
+
+ void* AssetDatabase::read(const char* path, unsigned int* len)
+ {
+ return smtread(mSmt, path, len);
+ }
+
+ const char* AssetDatabase::getFull(const char* path)
+ {
+ return smtfullpath(mSmt, path);
+ }
+
+ bool AssetDatabase::isDir(const char* path)
+ {
+ return smtisdir(mSmt, path);
+ }
+
+ bool AssetDatabase::isFile(const char* path)
+ {
+ return smtisreg(mSmt, path);
+ }
+
+ bool AssetDatabase::exists(const char* path)
+ {
+ return smtexists(mSmt, path) == 0;
+ }
+/*
+ std::vector<std::string> AssetDatabase::getFiles(const char* path, bool recursive)
+ {
+ }
+*/
+ } // namespace Filesystem
+} // namespace JinEngine
+
+#endif // jin_filesystem \ No newline at end of file
diff --git a/src/libjin/filesystem/je_asset_database.h b/src/libjin/filesystem/je_asset_database.h
new file mode 100644
index 0000000..e8b1bd1
--- /dev/null
+++ b/src/libjin/filesystem/je_asset_database.h
@@ -0,0 +1,114 @@
+#ifndef __JE_ASSET_DATABASE_H__
+#define __JE_ASSET_DATABASE_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_filesystem)
+
+#include <vector>
+
+#include "smount/smount.h"
+
+#include "je_buffer.h"
+
+namespace JinEngine
+{
+ namespace Filesystem
+ {
+
+ ///
+ /// Assets managment.
+ ///
+ class AssetDatabase
+ {
+ public:
+ ///
+ /// Get asset database singleton.
+ ///
+ /// @param Singleton of asset database.
+ ///
+ static AssetDatabase* get();
+
+ ///
+ /// Asset database constructor.
+ ///
+ AssetDatabase();
+
+ ///
+ /// Set asset root folder.
+ ///
+ /// @param root Root folder of assets.
+ ///
+ void mount(const char* root);
+
+ ///
+ /// Check if the path is directory.
+ ///
+ /// @param path Path under asset folder.
+ /// @return True if the given path is directory, otherwise return false.
+ ///
+ bool isDir(const char* path);
+
+ ///
+ /// Check if the path is file.
+ ///
+ /// @param path Path under asset folder.
+ /// @return True if the given path is file, otherwise return false.
+ ///
+ bool isFile(const char* path);
+
+ ///
+ /// Check if the path exists.
+ /// @param path Given path.
+ /// @return True if path exists, otherwise return false.
+ ///
+ bool exists(const char* path);
+
+ ///
+ /// Read file into a buffer.
+ ///
+ /// @param path Path of file.
+ /// @param buffer Buffer to fill.
+ /// @return True if read sucessful, otherwise return false.
+ ///
+ void read(const char* path, Buffer& buffer);
+
+ ///
+ /// Read file and return data content.
+ ///
+ /// @param path Path of file.
+ /// @param length Length of data.
+ /// @return Data if read sucessful, otherwise return null.
+ ///
+ void* read(const char* path, unsigned int* length);
+
+ ///
+ /// Get files under given directory.
+ ///
+ /// @param path Path of directory.
+ /// @param recursive Recursivily search folder.
+ /// @return File list under given directory.
+ ///
+ std::vector<std::string> getFiles(const char* directory, bool recursive = false);
+
+ ///
+ /// Get full path of asset.
+ ///
+ /// @param path Path of asset.
+ /// @return Full path of asset.
+ ///
+ const char* getFull(const char* path);
+
+ private:
+ static AssetDatabase* mAssetDatabase;
+#if jin_filesystem == jin_filesystem_smount
+ smtShared* mSmt;
+#endif
+
+ };
+
+ } // namespace Filesystem
+} // namespace JinEngine
+
+#endif // jin_filesystem
+
+#endif \ No newline at end of file
diff --git a/src/libjin/filesystem/je_buffer.h b/src/libjin/filesystem/je_buffer.h
new file mode 100644
index 0000000..1111e49
--- /dev/null
+++ b/src/libjin/filesystem/je_buffer.h
@@ -0,0 +1,169 @@
+#ifndef __JE_BUFFER_H__
+#define __JE_BUFFER_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_filesystem)
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../common/je_temporary.h"
+#include "../common/je_types.h"
+
+namespace JinEngine
+{
+ namespace Filesystem
+ {
+
+ ///
+ /// Data buffer allocated on heap.
+ ///
+ class Buffer : public Temporary
+ {
+ public:
+ ///
+ /// Buffer constructor.
+ ///
+ Buffer()
+ : mData(0)
+ , mSize(0)
+ {
+ }
+
+ ///
+ /// Copy constructor.
+ ///
+ /// @param src Buffer source.
+ ///
+ Buffer(const Buffer& src)
+ {
+ delete[] mData;
+ mSize = src.mSize;
+ mData = new byte[mSize];
+ memcpy(mData, src.mData, mSize);
+ }
+
+ ///
+ /// Buffer constructor.
+ ///
+ /// @param data Buffer data.
+ /// @param size Size of buffer.
+ ///
+ Buffer(void* data, int size)
+ {
+ mSize = size;
+ mData = new byte[mSize];
+ memcpy(mData, data, mSize);
+ }
+
+ ///
+ /// Buffer constructor.
+ ///
+ /// @param size Size of data.
+ ///
+ Buffer(size_t size)
+ {
+ mData = new byte[size];
+ memset(mData, 0, size);
+ mSize = size;
+ }
+
+ ///
+ /// Buffer destructor.
+ ///
+ ~Buffer()
+ {
+ delete[] mData;
+ mSize = 0;
+ }
+
+ ///
+ /// Set buffer data.
+ ///
+ /// @param data Buffer data.
+ /// @param size Size of data.
+ ///
+ void set(byte* data, size_t size)
+ {
+ if (data == nullptr)
+ return;
+ if (mSize != size)
+ {
+ delete mData;
+ mData = new byte[size];
+ }
+ mSize = size;
+ memcpy(mData, data, size);
+ }
+
+ ///
+ /// Bind buffer data.
+ ///
+ /// @param data Buffer data.
+ /// @param size Size of buffer.
+ ///
+ void bind(byte* data, size_t size)
+ {
+ if (mData != nullptr)
+ delete mData;
+ mSize = size;
+ mData = data;
+ }
+
+ ///
+ /// Buffer assignment.
+ ///
+ /// @param buffer Buffer to copy.
+ ///
+ void operator = (const Buffer& buffer)
+ {
+ delete[] mData;
+ mSize = buffer.mSize;
+ mData = new byte[mSize];
+ memcpy(mData, buffer.mData, mSize);
+ }
+
+ ///
+ /// Get data addresss.
+ ///
+ /// @return Data address.
+ ///
+ const byte* operator &()
+ {
+ return mData;
+ }
+
+ ///
+ /// Get data size.
+ ///
+ /// @return Size of data.
+ ///
+ const inline size_t size()
+ {
+ return mSize;
+ }
+
+ ///
+ /// Clear data.
+ ///
+ void clear()
+ {
+ if (mData == nullptr)
+ return;
+ delete mData;
+ mData = nullptr;
+ mSize = 0;
+ }
+
+ private:
+ byte* mData;
+ size_t mSize;
+
+ };
+
+ } // namespace Filesystem
+} // namespace JinEngine
+
+#endif // jin_filesystem
+
+#endif \ No newline at end of file
diff --git a/src/libjin/game/je_application.cpp b/src/libjin/game/je_application.cpp
new file mode 100644
index 0000000..723a809
--- /dev/null
+++ b/src/libjin/game/je_application.cpp
@@ -0,0 +1,81 @@
+#include "../core/je_configuration.h"
+#if defined(jin_game)
+
+#include <iostream>
+
+#include "../time/je_timer.h"
+#include "../input/je_event.h"
+#include "../graphics/je_window.h"
+#include "../graphics/je_gl.h"
+#include "../math/je_math.h"
+
+#include "je_application.h"
+
+using namespace JinEngine::Graphics;
+using namespace JinEngine::Input;
+using namespace JinEngine::Time;
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Game
+ {
+
+ Application::Application() :_running(true) {};
+
+ /* default game loop */
+ void Application::run()
+ {
+ if (_onLoad != nullptr)
+ _onLoad();
+ Window* wnd = Window::get();
+ const int FPS = wnd ? wnd->getFPS() : 60;
+ const int MS_PER_UPDATE = 1000.0f / FPS;
+ _running = true;
+ Event e;
+ int current = getMilliSecond();
+ int previous = current;
+ int dt = 0;
+ while (_running)
+ {
+ while (JinEngine::Input::pollEvent(&e))
+ {
+ if (_onEvent != nullptr)
+ _onEvent(&e);
+ if (!_running)
+ goto quitloop;
+ }
+ previous = current;
+ current = getMilliSecond();
+ dt = current - previous;
+ if (_onUpdate != nullptr)
+ _onUpdate(dt);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (_onDraw != nullptr)
+ _onDraw();
+ wnd->swapBuffers();
+ sleep(1);
+ }
+ quitloop:;
+ }
+
+ bool Application::initSystem(const SettingBase* setting)
+ {
+ if (setting == nullptr)
+ return false;
+ Application::Setting* s = (Application::Setting*) setting;
+ _onEvent = s->eventHandler;
+ _onUpdate = s->updater;
+ _onDraw = s->drawer;
+ _onLoad = s->loader;
+ return true;
+ }
+
+ void Application::quitSystem()
+ {
+ }
+
+ } // namespace Core
+} // namespace JinEngine
+
+#endif // jin_game \ No newline at end of file
diff --git a/src/libjin/game/je_application.h b/src/libjin/game/je_application.h
new file mode 100644
index 0000000..094de6a
--- /dev/null
+++ b/src/libjin/game/je_application.h
@@ -0,0 +1,88 @@
+#ifndef __JE_CORE_GAME_H__
+#define __JE_CORE_GAME_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_game)
+
+#include "../common/je_subsystem.hpp"
+#include "../utils/je_macros.h"
+#include "../input/je_Event.h"
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Game
+ {
+
+ ///
+ /// Game class.
+ ///
+ class Application : public Subsystem<Application>
+ {
+ public:
+
+ typedef void(*onLoad)();
+ typedef void(*onEvent)(JinEngine::Input::Event* e);
+ typedef void(*onUpdate)(int dt);
+ typedef void(*onDraw)();
+
+ ///
+ /// Game setting.
+ ///
+ struct Setting : SettingBase
+ {
+ onEvent eventHandler;
+ onUpdate updater;
+ onDraw drawer;
+ onLoad loader;
+ };
+
+ ///
+ /// Main game loop.
+ ///
+ void run();
+
+ ///
+ /// Stop game.
+ ///
+ inline void stop()
+ {
+ _running = false;
+ };
+
+ ///
+ /// Return if game is running.
+ ///
+ /// @return True if game is running, otherwise return false.
+ ///
+ inline bool running()
+ {
+ return _running;
+ };
+
+ private:
+
+ Application();
+ ~Application() {};
+
+ singleton(Application);
+
+ onEvent _onEvent;
+ onUpdate _onUpdate;
+ onDraw _onDraw;
+ onLoad _onLoad;
+
+ bool _running;
+
+ bool initSystem(const SettingBase* setting);
+ void quitSystem();
+
+ };
+
+ } // namespace Core
+} // namespace JinEngine
+
+#endif // jin_game
+
+#endif // __JE_CORE_GAME_H__ \ No newline at end of file
diff --git a/src/libjin/game/je_entity.cpp b/src/libjin/game/je_entity.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/game/je_entity.cpp
diff --git a/src/libjin/game/je_entity.h b/src/libjin/game/je_entity.h
new file mode 100644
index 0000000..80c6ff3
--- /dev/null
+++ b/src/libjin/game/je_entity.h
@@ -0,0 +1,27 @@
+#ifndef __JE_ENTITY_H__
+#define __JE_ENTITY_H__
+
+#include "je_gameobject.h"
+
+namespace JinEngine
+{
+ namespace Game
+ {
+/*
+ ///
+ ///
+ ///
+ class Entity : public GameObject
+ {
+ public:
+ Entity();
+
+ private:
+
+
+ };
+*/
+ } // namespace Game
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/game/je_gameobject.cpp b/src/libjin/game/je_gameobject.cpp
new file mode 100644
index 0000000..1396518
--- /dev/null
+++ b/src/libjin/game/je_gameobject.cpp
@@ -0,0 +1,11 @@
+#include "je_entity.h"
+
+namespace JinEngine
+{
+ namespace Game
+ {
+
+
+
+ } // namespace Game
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/game/je_gameobject.h b/src/libjin/game/je_gameobject.h
new file mode 100644
index 0000000..7c6ec2b
--- /dev/null
+++ b/src/libjin/game/je_gameobject.h
@@ -0,0 +1,83 @@
+#ifndef __JE_GAME_OBJECT_H__
+#define __JE_GAME_OBJECT_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_game)
+
+#include <list>
+#include <map>
+#include <set>
+
+#include "../common/je_object.h"
+#include "../common/je_types.h"
+#include "../graphics/je_sprite.h"
+
+namespace JinEngine
+{
+ namespace Game
+ {
+ /*
+ ///
+ /// Game object base class.
+ ///
+ class GameObject : public Object
+ {
+ public:
+
+ ///
+ ///
+ ///
+ virtual ~GameObject();
+
+ ///
+ ///
+ ///
+ void lifecycle();
+
+ ///
+ ///
+ ///
+ void setVisible(bool isVisible);
+
+ ///
+ ///
+ ///
+ void setActive(bool isActive);
+
+ ///
+ ///
+ ///
+ void setOrder(uint32 order);
+
+ protected:
+ virtual void onAlive();
+ virtual void onUpdate(float dt);
+ virtual void onDraw();
+ virtual void onDestroy();
+
+ uint32 mLayer; // layer where entity belongs
+ uint32 mOrder; // render index in layer
+ uint32 mTag; // tag of entity, support 32 tags now
+ bool mIsVisible; // if the entity is visible or not
+ bool mIsActive; // if the entity is joined into the logic
+
+ Math::Transform mTransform;
+
+ };
+
+ ///
+ /// Entity list. For quickly adding and removing entities.
+ ///
+ typedef std::list<GameObject*> EntityList;
+
+ ///
+ /// Entity set. For searching and keeps entities unique and sorted.
+ ///
+ typedef std::set<GameObject*> EntitySet;
+ */
+ } // namespace Game
+} // namespace JinEngine
+
+#endif // jin_game
+
+#endif \ No newline at end of file
diff --git a/src/libjin/game/je_scene.cpp b/src/libjin/game/je_scene.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/game/je_scene.cpp
diff --git a/src/libjin/game/je_scene.h b/src/libjin/game/je_scene.h
new file mode 100644
index 0000000..811d1a8
--- /dev/null
+++ b/src/libjin/game/je_scene.h
@@ -0,0 +1,73 @@
+#ifndef __JE_GAME_SCENE_H__
+#define __JE_GAME_SCENE_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_game)
+
+#include <map>
+#include <list>
+
+#include "je_entity.h"
+
+namespace JinEngine
+{
+ namespace Game
+ {
+
+ ///
+ /// Handle all entities.
+ ///
+ class Scene
+ {
+ public:
+ ///
+ ///
+ ///
+ void addEntity(Entity* entity);
+
+ ///
+ ///
+ ///
+ EntityList& getEntitiesByTag(uint32 tag);
+
+ ///
+ ///
+ ///
+ EntityList& getEntitiesByLayer(uint32 layer);
+
+ ///
+ ///
+ ///
+ void setEntitiesActiveByTag(uint32 tag);
+
+ ///
+ ///
+ ///
+ void setEntitiesActiveByLayer(uint32 layer);
+
+ ///
+ ///
+ ///
+ void removeEntitiesByLayer(uint32 layer);
+
+ ///
+ ///
+ ///
+ void removeEntitiesByTag(uint32 tag);
+
+ protected:
+ // all entities
+ EntitySet entities;
+ // all entities grouped by layer, render order
+ std::map<uint32, EntityList> layers;
+ // all entities grouped by tag
+ std::map<uint32, EntityList> tags;
+
+ };
+
+ } // namespace Game
+} // namespace JinEngine
+
+#endif // jin_game
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/animations/je_animation.cpp b/src/libjin/graphics/animations/je_animation.cpp
new file mode 100644
index 0000000..4fe673a
--- /dev/null
+++ b/src/libjin/graphics/animations/je_animation.cpp
@@ -0,0 +1,14 @@
+#include "je_animation.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Animations
+ {
+
+
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/animations/je_animation.h b/src/libjin/graphics/animations/je_animation.h
new file mode 100644
index 0000000..9926cf9
--- /dev/null
+++ b/src/libjin/graphics/animations/je_animation.h
@@ -0,0 +1,64 @@
+#ifndef __JE_ANIMATION_H__
+#define __JE_ANIMATION_H__
+
+#include <vector>
+#include <string>
+
+#include "../je_sprite.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Animations
+ {
+
+ ///
+ /// Animation clip with key.
+ ///
+ class Animation
+ {
+ public:
+ void update(float dt);
+
+ void start();
+ void pause();
+ void stop();
+ void rewind();
+ void setSpeed(float speed);
+
+ ///
+ /// Get current frame index.
+ ///
+ uint getCurrentFrameIndex();
+
+ ///
+ ///
+ ///
+ Sprite* getCurrentFrame();
+
+ ///
+ /// Set current frame index.
+ ///
+ /// @param frame Current frame to play.
+ ///
+ void setCurrentFrame(uint frame);
+
+ private:
+ ///
+ /// Key frames.
+ ///
+ std::vector<Sprite*> mFrames;
+
+ ///
+ /// Animation playing speed.
+ ///
+ float mSpeed;
+
+ };
+
+ } // namespace Animations
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/animations/je_animator.cpp b/src/libjin/graphics/animations/je_animator.cpp
new file mode 100644
index 0000000..360bd5d
--- /dev/null
+++ b/src/libjin/graphics/animations/je_animator.cpp
@@ -0,0 +1,14 @@
+#include "je_animator.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Animations
+ {
+
+
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/animations/je_animator.h b/src/libjin/graphics/animations/je_animator.h
new file mode 100644
index 0000000..6510a7d
--- /dev/null
+++ b/src/libjin/graphics/animations/je_animator.h
@@ -0,0 +1,50 @@
+#ifndef __JE_ANIMATOR_H__
+#define __JE_ANIMATOR_H__
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "je_animation.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Animations
+ {
+
+ ///
+ ///
+ ///
+ class Animator
+ {
+ public:
+ void addAnimation(const std::string& key, Animation* clip);
+ bool hasKey(const std::string& key);
+
+ void play();
+ void switchAnimation(const std::string& key);
+
+ ///
+ /// Control clips.
+ ///
+ void stopAnimation();
+ void pauseAnimation();
+ void rewindAnimation();
+ void startAnimation();
+
+ private:
+ ///
+ /// Map a key to clips.
+ ///
+ std::map<std::string, Animation*> mAnimations;
+ Animation* mCurrentAnimation;
+
+ };
+
+ } // namespace Animations
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_decoder.cpp b/src/libjin/graphics/fonts/je_decoder.cpp
new file mode 100644
index 0000000..02112a0
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_decoder.cpp
@@ -0,0 +1,96 @@
+#include <stdlib.h>
+#include <string.h>
+#include "je_decoder.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ /* utf8 byte string to unicode codepoint */
+ static const char *utf8toCodepoint(const char *p, unsigned *res) {
+ return nullptr;
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // decoders
+ /////////////////////////////////////////////////////////////////////////////
+
+ const void* Utf8::decode(const void* data, Codepoint* res) const
+ {
+ const char* p = (char*)data;
+ unsigned x, mask, shift;
+ switch (*p & 0xf0) {
+ case 0xf0: mask = 0x07; shift = 18; break;
+ case 0xe0: mask = 0x0f; shift = 12; break;
+ case 0xc0:
+ case 0xd0: mask = 0x1f; shift = 6; break;
+ default:
+ *res = *p;
+ return p + 1;
+ }
+ x = (*p & mask) << shift;
+ do {
+ if (*(++p) == '\0') {
+ *res = x;
+ return p;
+ }
+ shift -= 6;
+ x |= (*p & 0x3f) << shift;
+ } while (shift);
+ *res = x;
+ return p + 1;
+ }
+
+ const void* Utf8::next(const void* data) const
+ {
+ const char* p = (char*)data;
+ unsigned x, mask, shift;
+ switch (*p & 0xf0) {
+ case 0xf0: mask = 0x07; shift = 18; break;
+ case 0xe0: mask = 0x0f; shift = 12; break;
+ case 0xc0:
+ case 0xd0: mask = 0x1f; shift = 6; break;
+ default:
+ return p + 1;
+ }
+ x = (*p & mask) << shift;
+ do {
+ if (*(++p) == '\0') {
+ return p;
+ }
+ shift -= 6;
+ x |= (*p & 0x3f) << shift;
+ } while (shift);
+ return p + 1;
+ }
+ /*
+ const void* Utf16::decode(const void* data, Codepoint* res) const
+ {
+ return nullptr;
+ }
+
+ const void* Utf16::next(const void* data) const
+ {
+ return nullptr;
+ }
+ */
+ const void* Ascii::decode(const void* data, Codepoint* res) const
+ {
+ const char* p = (char*)data;
+ *res = *p;
+ return p + 1;
+ }
+
+ const void* Ascii::next(const void* data) const
+ {
+ const char* p = (char*)data;
+ return p + 1;
+ }
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_decoder.h b/src/libjin/graphics/fonts/je_decoder.h
new file mode 100644
index 0000000..840cada
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_decoder.h
@@ -0,0 +1,97 @@
+#ifndef __JE_UTF8_H__
+#define __JE_UTF8_H__
+
+#include <vector>
+
+#include "je_text.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ ///
+ /// Text decoder.
+ ///
+ class Decoder
+ {
+ public:
+
+ ///
+ /// Decode a code unit.
+ ///
+ /// @param data Code units.
+ /// @param codepoint Value of code point.
+ /// @return Next code unit location.
+ ///
+ virtual const void* decode(const void* data, Codepoint* codepoint) const = 0;
+
+ ///
+ /// Get next code unit location.
+ ///
+ /// @param data Code units.
+ /// @return Next code unit location.
+ ///
+ virtual const void* next(const void* data) const = 0;
+
+ };
+
+ ///
+ /// Utf-8 decoder.
+ ///
+ class Utf8 : public Decoder
+ {
+ public:
+
+ ///
+ /// Decode a code unit.
+ ///
+ /// @param data Code units.
+ /// @param codepoint Value of code point.
+ /// @return Next code unit location.
+ ///
+ const void* decode(const void* data, Codepoint* codepoint) const override;
+
+ ///
+ /// Get next code unit location.
+ ///
+ /// @param data Code units.
+ /// @return Next code unit location.
+ ///
+ const void* next(const void* data) const override;
+
+ };
+
+ ///
+ /// Ascii decoder.
+ ///
+ class Ascii : public Decoder
+ {
+ public:
+
+ ///
+ /// Decode a code unit.
+ ///
+ /// @param data Code units.
+ /// @param codepoint Value of code point.
+ /// @return Next code unit location.
+ ///
+ const void* decode(const void* data, Codepoint* codepoint) const override;
+
+ ///
+ /// Get next code unit location.
+ ///
+ /// @param data Code units.
+ /// @return Next code unit location.
+ ///
+ const void* next(const void* data) const override;
+
+ };
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_font.h b/src/libjin/graphics/fonts/je_font.h
new file mode 100644
index 0000000..9581b9f
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_font.h
@@ -0,0 +1,109 @@
+#ifndef __JE_FONT_H__
+#define __JE_FONT_H__
+
+#include <vector>
+#include "je_text.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ struct Page;
+
+ //
+ // Font
+ // |- TTF
+ // |- TextureFont
+ //
+
+ ///
+ /// Base Font class.
+ ///
+ class Font
+ {
+ public:
+ ///
+ /// Font constructor.
+ ///
+ Font(unsigned fontsize)
+ : mFontSize(fontsize)
+ {
+ }
+
+ ///
+ /// Font destructor.
+ ///
+ virtual ~Font() {};
+
+ ///
+ /// Create page with given text.
+ ///
+ /// @param text Text to be typesetted.
+ /// @param lineheight Line height of text.
+ /// @param spacing Spacing between characters. 0 by default.
+ /// @return Page if created successfully, otherwise return null.
+ ///
+ virtual Page* typeset(const Text& text, int lineheight, int spacing = 0) = 0;
+
+ ///
+ /// Create page with given unicode codepoints.
+ ///
+ /// @param content Unicode codepoints to be typesetted.
+ /// @param lineheight Line height of text.
+ /// @param spacing Spacing between characters. 0 by default.
+ /// @return Page if created successfully, otherwise return null.
+ ///
+ virtual Page* typeset(const Content& content, int lineheight, int spacing = 0) = 0;
+
+ ///
+ /// Render page to given position.
+ ///
+ /// @param page Page to be rendered.
+ /// @param x X value of the position.
+ /// @param y Y value of the position.
+ ///
+ virtual void render(const Page* page, int x, int y) = 0;
+
+ ///
+ /// Render unicode codepoints to given position.
+ ///
+ /// @param content Unicode codepoints to be typesetted.
+ /// @param x X value of the position.
+ /// @param y Y value of the position.
+ /// @param lineheight Line height of the content.
+ /// @param spacing Spacing between characters.
+ ///
+ virtual void render(const Content& content, int x, int y, int lineheight, int spacing = 0) = 0;
+
+ ///
+ /// Render text to given position.
+ ///
+ /// @param text Text to be rendered.
+ /// @param x X value of the position.
+ /// @param y Y value of the position.
+ /// @param lineheight Line height of the text.
+ /// @param spacing Spacing between characters.
+ ///
+ virtual void render(const Text& text, int x, int y, int lineheight, int spacing = 0) = 0;
+
+ ///
+ /// Get font size.
+ ///
+ /// @return Font size.
+ ///
+ inline unsigned getFontSize() { return mFontSize; };
+
+ protected:
+
+ unsigned mFontSize;
+
+ };
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // __JE_FONT_H__
diff --git a/src/libjin/graphics/fonts/je_page.h b/src/libjin/graphics/fonts/je_page.h
new file mode 100644
index 0000000..707f53a
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_page.h
@@ -0,0 +1,54 @@
+#ifndef __JE_PAGE_H__
+#define __JE_PAGE_H__
+
+#include "../../math/je_vector2.hpp"
+
+#include "je_font.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ class Font;
+
+ ///
+ /// Glyphs data to be rendered.
+ ///
+ struct GlyphVertex
+ {
+ int x, y; ///< screen coordinates
+ float u, v; ///< normalized texture uv
+ };
+
+ ///
+ /// Glyphs info for reducing draw call.
+ ///
+ struct GlyphArrayDrawInfo
+ {
+ GLuint texture; ///< atlas
+ unsigned int start; ///< glyph vertex indecies
+ unsigned int count; ///< glyph vertex count
+ };
+
+ ///
+ /// Page to be rendered.
+ ///
+ /// A page is a pre-rendered text struct for reducing draw call. Each page
+ /// keeps a font pointer which should not be changed.
+ ///
+ struct Page
+ {
+ Font* font;
+ std::vector<GlyphArrayDrawInfo> glyphinfolist;
+ std::vector<GlyphVertex> glyphvertices;
+ Math::Vector2<int> size;
+ };
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // __JE_PAGE_H__
diff --git a/src/libjin/graphics/fonts/je_text.cpp b/src/libjin/graphics/fonts/je_text.cpp
new file mode 100644
index 0000000..80aaa6a
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_text.cpp
@@ -0,0 +1,157 @@
+#include <cstring>
+
+#include "je_text.h"
+#include "je_decoder.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ /////////////////////////////////////////////////////////////////////////////
+ // iterator
+ /////////////////////////////////////////////////////////////////////////////
+
+ Text::Iterator::Iterator(const Iterator& itor)
+ : data(itor.data)
+ , p(itor.p)
+ , encode(itor.encode)
+ , length(itor.length)
+ {
+ switch (encode)
+ {
+ case Encode::UTF8: decoder = new Utf8(); break;
+ case Encode::ASCII: decoder = new Ascii(); break;
+ }
+ }
+
+ Text::Iterator::Iterator(const Encode& _encode, const void* _data, unsigned int _length)
+ : data(_data)
+ , p(_data)
+ , encode(_encode)
+ , length(_length)
+ {
+ switch (encode)
+ {
+ case Encode::UTF8: decoder = new Utf8(); break;
+ case Encode::ASCII: decoder = new Ascii(); break;
+ }
+ }
+
+ Text::Iterator::~Iterator()
+ {
+ delete decoder;
+ }
+
+ Codepoint Text::Iterator::get()
+ {
+ Codepoint codepoint;
+ decoder->decode(p, &codepoint);
+ return codepoint;
+ }
+
+ Codepoint Text::Iterator::operator*()
+ {
+ return get();
+ }
+ /*
+ Text::Iterator Text::Iterator::begin()
+ {
+ Iterator itor(encode, data, length);
+ itor.toBegin();
+ return itor;
+ }
+
+ Text::Iterator Text::Iterator::end()
+ {
+ Iterator itor(encode, data, length);
+ itor.toEnd();
+ return itor;
+ }
+ */
+ void Text::Iterator::toBegin()
+ {
+ p = (const unsigned char*)data;
+ }
+
+ void Text::Iterator::toEnd()
+ {
+ p = (const unsigned char*)data + length;
+ }
+
+ Text::Iterator& Text::Iterator::operator ++()
+ {
+ p = decoder->next(p);
+ return *this;
+ }
+
+ Text::Iterator Text::Iterator::operator ++(int)
+ {
+ p = decoder->next(p);
+ Iterator itor(encode, data, length);
+ itor.p = p;
+ return itor;
+ }
+
+ bool Text::Iterator::operator !=(const Iterator& itor)
+ {
+ return !(data == itor.data
+ && p == itor.p
+ && length == itor.length
+ && encode == itor.encode);
+ }
+
+ bool Text::Iterator::operator ==(const Iterator& itor)
+ {
+ return data == itor.data
+ && p == itor.p
+ && length == itor.length
+ && encode == itor.encode;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // text
+ /////////////////////////////////////////////////////////////////////////////
+
+ Text::Text(Encode encode, const void* data)
+ {
+ unsigned length = strlen((const char*)data);
+ Iterator end = Iterator(encode, data, length);
+ end.toEnd();
+ Iterator it = Iterator(encode, data, length);
+ for (; it != end; ++it)
+ {
+ content.push_back(*it);
+ }
+ }
+
+ Text::Text(Encode encode, const void* data, unsigned length)
+ {
+ Iterator end = Iterator(encode, data, length);
+ end.toEnd();
+ Iterator it = Iterator(encode, data, length);
+ for (; it != end; ++it)
+ {
+ content.push_back(*it);
+ }
+ }
+
+ Text::~Text()
+ {
+ }
+
+ const Content& Text::getContent() const
+ {
+ return content;
+ }
+
+ const Content& Text::operator*() const
+ {
+ return content;
+ }
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_text.h b/src/libjin/graphics/fonts/je_text.h
new file mode 100644
index 0000000..6e6f8b0
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_text.h
@@ -0,0 +1,172 @@
+#ifndef __JE_TEXT_H__
+#define __JE_TEXT_H__
+
+#include <vector>
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ typedef unsigned int Codepoint;
+
+ typedef std::vector<Codepoint> Content;
+
+ class Text;
+
+ class Decoder;
+
+ ///
+ /// Supported text encoding.
+ ///
+ enum Encode
+ {
+ UTF8, ///< utf-8
+ ASCII, ///< ASCII
+ };
+
+ ///
+ /// Decoded text. Saved as unicode codepoints.
+ ///
+ class Text
+ {
+ public:
+ ///
+ ///
+ ///
+ Text(Encode encode, const void* data);
+
+ ///
+ ///
+ ///
+ Text(Encode encode, const void* data, unsigned int length);
+
+ ///
+ ///
+ ///
+ ~Text();
+
+ ///
+ ///
+ ///
+ const Content& getContent() const;
+
+ ///
+ ///
+ ///
+ const Content& operator*() const;
+
+ private:
+ ///
+ ///
+ ///
+ class Iterator
+ {
+ public:
+
+ ///
+ ///
+ ///
+ Iterator(const Iterator& itor);
+
+ ///
+ ///
+ ///
+ Iterator(const Encode& encode, const void* data, unsigned int length);
+
+ ///
+ ///
+ ///
+ ~Iterator();
+
+ ///
+ ///
+ ///
+ Codepoint get();
+
+ //Iterator begin();
+ //Iterator end();
+
+ ///
+ ///
+ ///
+ void toBegin();
+
+ ///
+ ///
+ ///
+ void toEnd();
+
+ ///
+ ///
+ ///
+ Codepoint operator *();
+
+ ///
+ ///
+ ///
+ Iterator& operator ++();
+
+ ///
+ ///
+ ///
+ Iterator operator ++(int);
+
+ ///
+ ///
+ ///
+ bool operator !=(const Iterator& itor);
+
+ ///
+ ///
+ ///
+ bool operator ==(const Iterator& itor);
+
+ private:
+
+ ///
+ ///
+ ///
+ void operator = (const Iterator&);
+
+ ///
+ ///
+ ///
+ const Encode encode;
+
+ ///
+ ///
+ ///
+ const Decoder* decoder;
+
+ ///
+ ///
+ ///
+ const void* p;
+
+ ///
+ ///
+ ///
+ const void* const data;
+
+ ///
+ ///
+ ///
+ unsigned int length;
+
+ };
+
+ ///
+ ///
+ ///
+ Content content;
+
+ };
+
+ }
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_texture_font.cpp b/src/libjin/graphics/fonts/je_texture_font.cpp
new file mode 100644
index 0000000..3599c58
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_texture_font.cpp
@@ -0,0 +1,322 @@
+#include <vector>
+
+#include "../../math/je_vector2.hpp"
+
+#include "../shaders/je_shader.h"
+
+#include "je_texture_font.h"
+
+using namespace std;
+using namespace JinEngine::Math;
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ TextureFont * TextureFont::createTextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, codepoints, cellw, cellh);
+ return tf;
+ }
+
+ TextureFont * TextureFont::createTextureFont(const Bitmap* bitmap, const Text& codepoints, int cellw, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, *codepoints, cellw, cellh);
+ return tf;
+ }
+
+ TextureFont* TextureFont::createTextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, codepoints, mask, cellh);
+ return tf;
+ }
+
+ TextureFont* TextureFont::createTextureFont(const Bitmap* bitmap, const Text& codepoints, Color mask, int cellh)
+ {
+ TextureFont* tf = new TextureFont(bitmap, *codepoints, mask, cellh);
+ return tf;
+ }
+
+ TextureFont::~TextureFont()
+ {
+ }
+
+ const TextureFont::TextureGlyph* TextureFont::findGlyph(Codepoint codepoint) const
+ {
+ auto it = glyphs.find(codepoint);
+ if (it != glyphs.end())
+ {
+ return &it->second;
+ }
+ else
+ return nullptr;
+ }
+
+ Page* TextureFont::typeset(const Content& text, int lineheight, int spacing)
+ {
+ Page* page = new Page();
+ page->font = this;
+ vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ int texture = -1;
+ const TextureGlyph* glyph = nullptr;
+ GlyphVertex vertex;
+ Vector2<int> p(0, 0);
+ int i = 0;
+
+#define glyphvertices_push(_x, _y, _u, _v) \
+ vertex.x = _x; vertex.y = _y;\
+ vertex.u = _u; vertex.v = _v;\
+ glyphvertices.push_back(vertex);\
+
+ for (Codepoint c : text)
+ {
+ // return
+ if (c == 0x0D) continue;
+ // newline
+ if (c == 0x0A)
+ {
+ p.y += lineheight;
+ p.x = 0;
+ continue;
+ }
+ if (c == 0x09)
+ {
+ // tab = 4*space
+ unsigned cw = getCharWidth(0x20);
+ p.x += cw * 4;
+ continue;
+ }
+ glyph = findGlyph(c);
+ if (glyph == nullptr) continue;
+ if (texture != getGLTexture())
+ {
+ texture = getGLTexture();
+ GlyphArrayDrawInfo info;
+ info.start = i;
+ info.count = 0;
+ info.texture = texture;
+ glyphinfolist.push_back(info);
+ }
+ glyphinfolist[glyphinfolist.size() - 1].count += 4;
+ // normalized
+ float w = getWidth(), h = getHeight();
+ float nx = glyph->x / w, ny = glyph->y / h;
+ float nw = glyph->w / w, nh = glyph->h / h;
+ glyphvertices_push(p.x, p.y, nx, ny);
+ glyphvertices_push(p.x, p.y + glyph->h, nx, ny + nh);
+ glyphvertices_push(p.x + glyph->w, p.y + glyph->h, nx + nw, ny + nh);
+ glyphvertices_push(p.x + glyph->w, p.y, nx + nw, ny);
+ p.x += glyph->w + spacing;
+ i += 4;
+ }
+ getTextBox(text, &page->size.w, &page->size.h, lineheight, spacing);
+ return page;
+ }
+
+ int TextureFont::getCharWidth(int c)
+ {
+ auto it = glyphs.find(c);
+ if (it != glyphs.end())
+ {
+ return it->second.w;
+ }
+ return 0;
+ }
+
+ int TextureFont::getCharHeight(int c)
+ {
+ auto it = glyphs.find(c);
+ if (it != glyphs.end())
+ {
+ return it->second.h;
+ }
+ return 0;
+ }
+
+ int TextureFont::getTextWidth(const Content& t, int spacing)
+ {
+ int res = 0;
+ int tmp = 0;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ continue;
+ }
+ if (c == 0x09)
+ {
+ // tab = 4*space
+ unsigned cw = getCharWidth(0x20);
+ tmp += cw * 4;
+ if (tmp > res) res = tmp;
+ continue;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > res) res = tmp;
+ }
+ return res;
+ }
+
+ int TextureFont::getTextHeight(const Content& t, int lineheight)
+ {
+ int res = 0;
+ bool newline = true;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0A)
+ newline = true;
+ else if (c == 0x0D);
+ else if (newline)
+ {
+ newline = false;
+ res += lineheight;
+ }
+ }
+ return res;
+ }
+
+ void TextureFont::getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing)
+ {
+ *w = 0;
+ *h = 0;
+ int tmp = 0;
+ bool newline = true;
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ newline = true;
+ continue;
+ }
+ else if (newline)
+ {
+ newline = false;
+ *h += lineheight;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > *w)
+ *w = tmp;
+ }
+ }
+
+ Page* TextureFont::typeset(const Text& text, int lineheight, int spacing)
+ {
+ return typeset(*text, lineheight, spacing);
+ }
+
+ void TextureFont::render(const Page* page, int x, int y)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ const vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ const vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ Matrix modelMatrix = gl.getModelViewMatrix(x, y, 1, 1, 0, 0, 0);
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ for (int i = 0; i < glyphinfolist.size(); ++i)
+ {
+ const GlyphArrayDrawInfo& info = glyphinfolist[i];
+ shader->setVertexPointer(2, GL_INT, sizeof(GlyphVertex), &glyphvertices[info.start].x);
+ shader->setUVPointer(2, GL_FLOAT, sizeof(GlyphVertex), &glyphvertices[info.start].u);
+ gl.bindTexture(info.texture);
+ gl.drawArrays(GL_QUADS, 0, info.count);
+ gl.bindTexture(0);
+ }
+ }
+
+ void TextureFont::render(const Content& text, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(text, lineheight, spacing);
+ render(page, x, y);
+ delete page;
+ }
+
+ void TextureFont::render(const Text& text, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(text, lineheight, spacing);
+ render(page, x, y);
+ delete page;
+ }
+
+ TextureFont::TextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh)
+ : Graphic(bitmap)
+ , Font(cellh)
+ {
+ TextureGlyph glyph;
+ Vector2<int> count(bitmap->getWidth() / cellw, bitmap->getHeight() / cellh);
+ glyph.w = cellw;
+ glyph.h = cellh;
+ for (int y = 0; y < count.row; ++y)
+ {
+ glyph.y = y * cellh;
+ for (int x = 0; x < count.colum; ++x)
+ {
+ glyph.x = x * cellw;
+ if (x + y * count.colum >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[x + y * count.colum], glyph));
+ }
+ }
+ }
+
+ TextureFont::TextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh)
+ : Graphic(bitmap)
+ , Font(cellh)
+ {
+ TextureGlyph glyph;
+ glyph.h = cellh;
+ int w = bitmap->getWidth();
+ int h = bitmap->getHeight();
+ int i = 0;
+ for (int y = 0; y < h; y += cellh)
+ {
+ glyph.y = y;
+ bool newc = false;
+ for (int x = 0; x <= w; ++x)
+ {
+ if (x == w && newc)
+ {
+ glyph.w = x - glyph.x;
+ if (i >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[i], glyph));
+ ++i;
+ newc = false;
+ break;
+ }
+ Color c = bitmap->getPixels()[x + y * w];
+ if (!newc && c != mask)
+ {
+ glyph.x = x;
+ newc = true;
+ }
+ else if (newc && c == mask)
+ {
+ glyph.w = x - glyph.x;
+ if (i >= codepoints.size())
+ return;
+ glyphs.insert(std::pair<Codepoint, TextureGlyph>(codepoints[i], glyph));
+ if (codepoints[i] == 't')
+ {
+ int a = 10;
+ }
+ ++i;
+ newc = false;
+ }
+ }
+ }
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_texture_font.h b/src/libjin/graphics/fonts/je_texture_font.h
new file mode 100644
index 0000000..8a50699
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_texture_font.h
@@ -0,0 +1,144 @@
+#ifndef __JE_TEXTURE_FONT_H__
+#define __JE_TEXTURE_FONT_H__
+
+#include <map>
+#include <vector>
+
+#include "../../math/je_vector4.hpp"
+
+#include "../je_graphic.h"
+#include "../je_bitmap.h"
+
+#include "je_page.h"
+#include "je_font.h"
+#include "je_text.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ ///
+ ///
+ ///
+ class TextureFont
+ : public Font
+ , public Graphic
+ {
+ public:
+
+ ///
+ ///
+ ///
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh);
+
+ ///
+ ///
+ ///
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Text& text, int cellw, int cellh);
+
+ ///
+ ///
+ ///
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh);
+
+ ///
+ ///
+ ///
+ static TextureFont* createTextureFont(const Bitmap* bitmap, const Text& text, Color mask, int cellh);
+
+ ///
+ ///
+ ///
+ ~TextureFont();
+
+ ///
+ ///
+ ///
+ Page* typeset(const Text& text, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ Page* typeset(const Content& text, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ void render(const Page* page, int x, int y) override;
+
+ ///
+ ///
+ ///
+ void render(const Content& text, int x, int y, int linehgiht, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ void render(const Text& text, int x, int y, int lineheight, int spacing = 0)override;
+
+ private:
+
+ ///
+ ///
+ ///
+ struct TextureGlyph
+ {
+ float x, y, w, h;
+ };
+
+ ///
+ ///
+ ///
+ TextureFont(const Bitmap* bitmap, const Content& codepoints, int cellw, int cellh);
+
+ ///
+ ///
+ ///
+ TextureFont(const Bitmap* bitmap, const Content& codepoints, Color mask, int cellh);
+
+ ///
+ ///
+ ///
+ int getCharWidth(int c);
+
+ ///
+ ///
+ ///
+ int getCharHeight(int c);
+
+ ///
+ ///
+ ///
+ int getTextWidth(const Content& text, int spacing = 0);
+
+ ///
+ ///
+ ///
+ int getTextHeight(const Content& text, int lineheight);
+
+ ///
+ ///
+ ///
+ void getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing = 0);
+
+ ///
+ ///
+ ///
+ const TextureGlyph* findGlyph(Codepoint codepoint) const;
+
+
+ ///
+ ///
+ ///
+ std::map<Codepoint, TextureGlyph> glyphs;
+
+ };
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_ttf.cpp b/src/libjin/graphics/fonts/je_ttf.cpp
new file mode 100644
index 0000000..1f79558
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_ttf.cpp
@@ -0,0 +1,468 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <stdio.h>
+
+#include "../../common/je_array.hpp"
+
+#include "../je_gl.h"
+#include "../je_color.h"
+#include "../shaders/je_shader.h"
+
+#include "je_ttf.h"
+#include "je_page.h"
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb/stb_truetype.h"
+
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ // TTFData
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ TTFData* TTFData::createTTFData(const unsigned char* data, unsigned int size)
+ {
+ TTFData* ttf = nullptr;
+ try
+ {
+ ttf = new TTFData(data, size);
+ return ttf;
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ }
+
+ TTFData::TTFData(const unsigned char* d, unsigned int s)
+ {
+ raw.size = s;
+ raw.data = (unsigned char*)malloc(s);
+ memcpy(raw.data, d, s);
+ if (!stbtt_InitFont(&info, (const unsigned char*)raw.data, 0))
+ {
+ delete raw.data;
+ throw 0;
+ }
+ /* push default fontsize */
+ pushTTFsize(FONT_SIZE);
+ }
+
+ TTFData::~TTFData()
+ {
+ free(raw.data);
+ }
+
+ TTF* TTFData::createTTF(unsigned fontSize)
+ {
+ TTF* ttf;
+ try
+ {
+ ttf = new TTF(this, fontSize);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ return ttf;
+ }
+
+ /*
+ * (0, 0)
+ * +--------------+ ascent
+ * | +--------+ |
+ * | | | |
+ * | | bitmap | |
+ * +--|--------|--+ baseline
+ * | +--------+ |
+ * +--|-----------+ decent
+ * | |
+ * leftSideBearing |
+ * advanceWidth
+ */
+ void TTFData::getVMetrics(int* baseline, int* descent)
+ {
+ float scale = scales.back();
+ int ascent;
+ stbtt_GetFontVMetrics(&info, &ascent, descent, 0);
+ *baseline = (int)(ascent*scale) + 1; // slight adjustment
+ *descent = *baseline - (int)(*descent*scale) + 1;
+ }
+
+ void TTFData::getHMetrics(unsigned int codepoint, int* advanceWidth, int* leftSideBearing)
+ {
+ float scale = scales.back();
+ int adw, lsb;
+ stbtt_GetCodepointHMetrics(&info, codepoint, &adw, &lsb);
+ *advanceWidth = (int)(adw*scale);
+ *leftSideBearing = (int)(lsb*scale);
+ }
+
+ void TTFData::pushTTFsize(unsigned int fs)
+ {
+ float sc = stbtt_ScaleForPixelHeight(&info, fs);
+ scales.push_back(sc);
+ }
+
+ void TTFData::popTTFsize()
+ {
+ /* always keep default ttf size on the bottom of stack */
+ if (scales.size() > 1)
+ scales.pop_back();
+ }
+
+ Channel* TTFData::getCodepointBitmapAlpha(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
+ {
+ float scale = scales.back();
+ Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
+ return bitmap;
+ }
+
+ Color* TTFData::getCodepointBitmap(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const
+ {
+ float scale = scales.back();
+ Channel* bitmap = stbtt_GetCodepointBitmap(&info, scale, scale, codepoint, width, height, xoff, yoff);
+ int w = *width, h = *height;
+ //int xo = *xoff, yo = *yoff;
+ Color* bitmap32 = new Color[w*h];
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ bitmap32[x + y * w].set(0xff, 0xff, 0xff, bitmap[x + y * w]);
+ }
+ }
+ free(bitmap);
+ return bitmap32;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ // TTF
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "../shaders/built-in/je_font.shader.h"
+
+ using namespace std;
+ using namespace JinEngine::Math;
+
+ const int TTF::TEXTURE_WIDTHS[] = { 128, 256, 256, 512, 512, 1024, 1024 };
+ const int TTF::TEXTURE_HEIGHTS[] = { 128, 128, 256, 256, 512, 512, 1024 };
+
+ /* little endian unicode */
+ static const char* unicodeLittleEndian(const char* p, unsigned* res)
+ {
+ }
+
+ ///*static*/ TTF* TTF::createTTF(TTFData* fontData, unsigned int fontSzie)
+ //{
+ // TTF* ttf;
+ // try
+ // {
+ // ttf = new TTF(fontData, fontSzie);
+ // }
+ // catch (...)
+ // {
+ // return nullptr;
+ // }
+ // return ttf;
+ //}
+
+ TTF::TTF(TTFData* f, unsigned int fontSize)
+ : Font(fontSize)
+ , cursor(0, 0)
+ , ttf(f)
+ {
+ ttf->pushTTFsize(fontSize);
+ ttf->getVMetrics(&baseline, &descent);
+ estimateSize();
+ ttf->popTTFsize();
+ /* create a default texture */
+ createAtlas();
+ }
+
+ /* estimate the size of atlas texture */
+ void TTF::estimateSize()
+ {
+ for (int level = 0; level <= TEXTURE_SIZE_LEVEL_MAX; ++level)
+ {
+ if (descent * (descent*0.8) * 96 <= TEXTURE_WIDTHS[level] * TEXTURE_HEIGHTS[level])
+ {
+ textureWidth = TEXTURE_WIDTHS[level];
+ textureHeight = TEXTURE_HEIGHTS[level];
+ break;
+ }
+ }
+ }
+
+ TTF::~TTF()
+ {
+ }
+
+ GLuint TTF::createAtlas()
+ {
+ GLuint t;
+ gl.flushError();
+ t = gl.genTexture();
+ gl.bindTexture(t);
+ gl.setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl.setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl.setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl.setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl.texImage(GL_RGBA8, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE);
+ if (glGetError() != GL_NO_ERROR)
+ {
+ glDeleteTextures(1, &t);
+ gl.bindTexture(0);
+ return 0;
+ }
+ atlases.push_back(t);
+ gl.bindTexture(0);
+ return t;
+ }
+
+ void TTF::render(const Content& t, int x, int y, int lineheight, int spacing)
+ {
+ Page* page = typeset(t, lineheight, spacing);
+ render(page, x, y);
+ delete page;
+ }
+
+ Page* TTF::typeset(const Content& text, int lineheight, int spacing)
+ {
+ Page* page = new Page();
+ page->font = this;
+ vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ int texture = -1;
+ TTFGlyph* glyph = nullptr;
+ GlyphVertex vertex;
+ Vector2<int> p(0, 0);
+ int i = 0;
+
+#define glyphvertices_push(_x, _y, _u, _v) \
+ vertex.x = _x; vertex.y = _y;\
+ vertex.u = _u; vertex.v = _v;\
+ glyphvertices.push_back(vertex);
+
+#define glyphlize(c)\
+ do{\
+ glyph = &findGlyph(c); \
+ if (texture != glyph->atlas) \
+ { \
+ GlyphArrayDrawInfo info; \
+ info.start = i; \
+ info.count = 0; \
+ info.texture = glyph->atlas; \
+ texture = glyph->atlas; \
+ glyphinfolist.push_back(info); \
+ } \
+ glyphinfolist[glyphinfolist.size() - 1].count += 4; \
+ TTFGlyph::Bbox& bbox = glyph->bbox; \
+ glyphvertices_push(p.x, p.y, bbox.x, bbox.y); \
+ glyphvertices_push(p.x, p.y + glyph->height, bbox.x, bbox.y + bbox.h); \
+ glyphvertices_push(p.x + glyph->width, p.y + glyph->height, bbox.x + bbox.w, bbox.y + bbox.h); \
+ glyphvertices_push(p.x + glyph->width, p.y, bbox.x + bbox.w, bbox.y); \
+ }while(0)
+
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ /* new line */
+ p.y += lineheight;
+ p.x = 0;
+ continue;
+ }
+ if (c == 0x09)
+ {
+ // tab = 4*space
+ unsigned cw = getCharWidth(0x20);
+ p.x += cw * 4;
+ continue;
+ }
+ glyphlize(c);
+ p.x += glyph->width + spacing;
+ i += 4;
+ }
+ getTextBox(text, &page->size.w, &page->size.h, lineheight, spacing);
+ return page;
+ }
+
+ Page* TTF::typeset(const Text& text, int lineheight, int spacing)
+ {
+ return typeset(*text, lineheight, spacing);
+ }
+
+ void TTF::render(const Page* page, int x, int y)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ const vector<GlyphArrayDrawInfo>& glyphinfolist = page->glyphinfolist;
+ const vector<GlyphVertex>& glyphvertices = page->glyphvertices;
+ Matrix modelMatrix = gl.getModelViewMatrix(x, y, 1, 1, 0, 0, 0);
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ for (int i = 0; i < glyphinfolist.size(); ++i)
+ {
+ const GlyphArrayDrawInfo& info = glyphinfolist[i];
+ shader->setVertexPointer(2, GL_INT, sizeof(GlyphVertex), &glyphvertices[info.start].x);
+ shader->setUVPointer(2, GL_FLOAT, sizeof(GlyphVertex), &glyphvertices[info.start].u);
+ gl.bindTexture(info.texture);
+ gl.drawArrays(GL_QUADS, 0, info.count);
+ gl.bindTexture(0);
+ }
+ }
+
+ void TTF::render(const Text& text, int x, int y, int lineheight, int spacing /* = 0 */)
+ {
+ render(*text, x, y, lineheight, spacing);
+ }
+
+ int TTF::getCharWidth(int c)
+ {
+ int adw, lsb;
+ ttf->pushTTFsize(mFontSize);
+ ttf->getHMetrics(c, &adw, &lsb);
+ ttf->popTTFsize();
+ return adw;
+ }
+
+ int TTF::getCharHeight(int c)
+ {
+ return descent;
+ }
+
+ int TTF::getTextWidth(const Content& t, int spacing)
+ {
+ ttf->pushTTFsize(mFontSize);
+ int res = 0;
+ int tmp = 0;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ continue;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > res)
+ res = tmp;
+ }
+ ttf->popTTFsize();
+ return res;
+ }
+
+ int TTF::getTextHeight(const Content& t, int lineheight)
+ {
+ ttf->pushTTFsize(mFontSize);
+ int res = 0;
+ bool newline = true;
+ for (Codepoint c : t)
+ {
+ if (c == 0x0A)
+ newline = true;
+ else if (c == 0x0D);
+ else if (newline)
+ {
+ newline = false;
+ res += lineheight;
+ }
+ }
+ ttf->popTTFsize();
+ return res;
+ }
+
+ void TTF::getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing)
+ {
+ ttf->pushTTFsize(mFontSize);
+ *w = 0;
+ *h = 0;
+ int tmp = 0;
+ bool newline = true;
+ for (Codepoint c : text)
+ {
+ if (c == 0x0D)
+ continue;
+ if (c == 0x0A)
+ {
+ tmp = 0;
+ newline = true;
+ continue;
+ }
+ else if (newline)
+ {
+ newline = false;
+ *h += lineheight;
+ }
+ tmp += getCharWidth(c) + spacing;
+ if (tmp > *w)
+ *w = tmp;
+ }
+ ttf->popTTFsize();
+ }
+
+ TTF::TTFGlyph& TTF::bakeGlyph(unsigned int character)
+ {
+ int w, h, xoff, yoff;
+ ttf->pushTTFsize(mFontSize);
+ GLuint atlas = atlases.back();
+ const Color* bitmap = ttf->getCodepointBitmap(character, &w, &h, &xoff, &yoff);
+ int adw, lsb;
+ {
+ /* bake glyph */
+ ttf->getHMetrics(character, &adw, &lsb);
+ ttf->popTTFsize();
+ if (cursor.x + adw > textureWidth)
+ {
+ cursor.x = 0;
+ cursor.y += descent;
+ if (cursor.y + descent * 2 > textureHeight)
+ {
+ /* create new atlas */
+ atlas = createAtlas();
+ cursor.y = 0;
+ }
+ }
+ gl.bindTexture(atlas);
+ gl.texSubImage(cursor.x + xoff, cursor.y + yoff + baseline, w, h, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
+ gl.bindTexture();
+ delete[] bitmap;
+ }
+ TTFGlyph glyph;
+ glyph.atlas = atlas;
+ glyph.bbox.x = cursor.x / (float)textureWidth;
+ glyph.bbox.y = cursor.y / (float)textureHeight;
+ glyph.bbox.w = adw / (float)textureWidth;
+ glyph.bbox.h = descent / (float)textureHeight;
+ glyph.width = adw;
+ glyph.height = descent;
+ glyphs.insert(std::pair<unsigned int, TTFGlyph>(character, glyph));
+ cursor.x += adw;
+ return glyphs[character];
+ }
+
+ TTF::TTFGlyph& TTF::findGlyph(unsigned int character)
+ {
+ map<unsigned int, TTFGlyph>::iterator it = glyphs.find(character);
+ if (it != glyphs.end())
+ return it->second;
+ else
+ return bakeGlyph(character);
+ }
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/fonts/je_ttf.h b/src/libjin/graphics/fonts/je_ttf.h
new file mode 100644
index 0000000..28260f6
--- /dev/null
+++ b/src/libjin/graphics/fonts/je_ttf.h
@@ -0,0 +1,292 @@
+#ifndef __JE_TTF_H__
+#define __JE_TTF_H__
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <vector>
+#include <map>
+
+#include "stb/stb_truetype.h"
+
+#include "../../math/je_quad.h"
+
+#include "../je_color.h"
+#include "../je_graphic.h"
+
+#include "je_page.h"
+#include "je_font.h"
+#include "je_text.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Fonts
+ {
+
+ class TTF;
+
+ //
+ // TTFData
+ // |- TTF(14px)
+ // |- TTF(15px)
+ // .
+ // .
+ // .
+ //
+ class TTFData
+ {
+ public:
+
+ ///
+ ///
+ ///
+ static TTFData* createTTFData(const unsigned char* data, unsigned int size);
+
+ ///
+ ///
+ ///
+ ~TTFData();
+
+ ///
+ ///
+ ///
+ TTF* createTTF(unsigned ttfsize);
+
+ ///
+ ///
+ ///
+ void pushTTFsize(unsigned ttfsize);
+
+ ///
+ ///
+ ///
+ void popTTFsize();
+
+ ///
+ ///
+ ///
+ Channel* getCodepointBitmapAlpha(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const;
+
+ ///
+ ///
+ ///
+ Color* getCodepointBitmap(unsigned int codepoint, int* width, int* height, int* xoff, int* yoff) const;
+
+ ///
+ ///
+ ///
+ void getVMetrics(int* baseline, int* descent);
+
+ ///
+ ///
+ ///
+ void getHMetrics(unsigned int codepoint, int* advanceWidth, int* leftSideBearing);
+
+ private:
+
+ ///
+ ///
+ ///
+ static const unsigned int FONT_SIZE = 12;
+
+ ///
+ ///
+ ///
+ TTFData(const unsigned char* data, unsigned int size);
+
+ ///
+ ///
+ ///
+ stbtt_fontinfo info;
+
+ ///
+ ///
+ ///
+ struct
+ {
+ unsigned char* data;
+ unsigned int size;
+ } raw;
+
+ ///
+ ///
+ ///
+ std::vector<float> scales;
+
+ };
+
+ class TTF : public Font
+ {
+ public:
+ //static TTF* createTTF(TTFData* ttfData, unsigned ttfSzie);
+
+ ///
+ ///
+ ///
+ Page* typeset(const Text& text, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ Page* typeset(const Content& text, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ void render(const Text& text, int x, int y, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ void render(const Content& text, int x, int y, int lineheight, int spacing = 0) override;
+
+ ///
+ ///
+ ///
+ void render(const Page* page, int x, int y) override;
+
+ ///
+ ///
+ ///
+ ~TTF();
+
+ private:
+
+ friend class TTFData;
+
+ ///
+ ///
+ ///
+ struct TTFGlyph
+ {
+ GLuint atlas;
+ // normalized coordinates
+ struct Bbox
+ {
+ float x, y;
+ float w, h;
+ } bbox;
+ // glyph size in pixel
+ unsigned int width, height;
+ };
+
+ ///
+ ///
+ ///
+ static const int TEXTURE_SIZE_LEVELS_COUNT = 7;
+
+ ///
+ ///
+ ///
+ static const int TEXTURE_SIZE_LEVEL_MAX = TEXTURE_SIZE_LEVELS_COUNT - 1;
+
+ ///
+ ///
+ ///
+ static const int TEXTURE_WIDTHS[TEXTURE_SIZE_LEVELS_COUNT];
+
+ ///
+ ///
+ ///
+ static const int TEXTURE_HEIGHTS[TEXTURE_SIZE_LEVELS_COUNT];
+
+ ///
+ ///
+ ///
+ TTF(TTFData* ttf, Codepoint ttfSize);
+
+ ///
+ ///
+ ///
+ void estimateSize();
+
+ ///
+ ///
+ ///
+ GLuint createAtlas();
+
+ ///
+ ///
+ ///
+ TTFGlyph& bakeGlyph(Codepoint character);
+
+ ///
+ ///
+ ///
+ TTFGlyph& findGlyph(Codepoint character);
+
+ ///
+ ///
+ ///
+ int getCharWidth(int c);
+
+ ///
+ ///
+ ///
+ int getCharHeight(int c);
+
+ ///
+ ///
+ ///
+ int getTextWidth(const Content& text, int spacing = 0);
+
+ ///
+ ///
+ ///
+ int getTextHeight(const Content& text, int lineheight);
+
+ ///
+ ///
+ ///
+ void getTextBox(const Content& text, int* w, int* h, int lineheight, int spacing = 0);
+
+ ///
+ ///
+ ///
+ int textureWidth;
+
+ ///
+ ///
+ ///
+ int textureHeight;
+
+ ///
+ ///
+ ///
+ std::vector<GLuint> atlases;
+
+ ///
+ ///
+ ///
+ std::map<Codepoint, TTFGlyph> glyphs;
+
+ ///
+ ///
+ ///
+ TTFData* ttf;
+
+ ///
+ ///
+ ///
+ int baseline;
+
+ ///
+ ///
+ ///
+ int descent;
+
+ ///
+ ///
+ ///
+ Math::Vector2<float> cursor;
+
+ };
+
+ } // namespace Fonts
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics)
+
+#endif // __JE_FONT_H__
diff --git a/src/libjin/graphics/je_bitmap.cpp b/src/libjin/graphics/je_bitmap.cpp
new file mode 100644
index 0000000..cdab46d
--- /dev/null
+++ b/src/libjin/graphics/je_bitmap.cpp
@@ -0,0 +1,188 @@
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb/stb_image.h"
+
+#include "../common/je_exception.h"
+#include "../filesystem/je_asset_database.h"
+#include "../math/je_math.h"
+
+#include "je_bitmap.h"
+
+using namespace JinEngine::Filesystem;
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ Bitmap* Bitmap::createBitmap(const char* path)
+ {
+ AssetDatabase* ad = AssetDatabase::get();
+ Buffer buffer;
+ ad->read(path, buffer);
+ return createBitmap(&buffer, buffer.size());
+ }
+
+ Bitmap* Bitmap::createBitmap(const void* pixel, unsigned width, unsigned height)
+ {
+ Bitmap* bitmap = new Bitmap(width, height);
+ memcpy(bitmap->pixels, pixel, width*height * sizeof(Color));
+ return bitmap;
+ }
+
+ /*static*/ Bitmap* Bitmap::createBitmap(const void* imgData, size_t size)
+ {
+ if (imgData == nullptr)
+ return nullptr;
+ int w, h;
+ void* data = stbi_load_from_memory((unsigned char *)imgData, size, &w, &h, NULL, STBI_rgb_alpha);
+ if (data == nullptr)
+ {
+ throw Exception("Could not create bitmap from image data.");
+ return nullptr;
+ }
+ Bitmap* bitmap = new Bitmap();
+ bitmap->pixels = (Color*)data;
+ bitmap->width = w;
+ bitmap->height = h;
+ return bitmap;
+ }
+
+ /*static*/ Bitmap* Bitmap::createBitmap(int w, int h, Color color)
+ {
+ Bitmap* bitmap = new Bitmap(w, h);
+ if (color != Color::BLACK)
+ bitmap->setPixels(color);
+ return bitmap;
+ }
+
+ /*static*/ Bitmap* Bitmap::createBitmap(int width, int height, std::function<Color(int, int, int, int)> drawer)
+ {
+ Bitmap* bitmap = new Bitmap(width, height);
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ Color c = drawer(width, height, x, y);
+ bitmap->setPixel(c, x, y);
+ }
+ }
+ return bitmap;
+ }
+
+ /*static */ Bitmap* Bitmap::clone(const Bitmap* bitmap)
+ {
+ Bitmap* b = new Bitmap();
+ int w = bitmap->getWidth();
+ int h = bitmap->getHeight();
+ b->resetPixels(bitmap->getPixels(), w, h);
+ return b;
+ }
+
+ Bitmap::Bitmap()
+ : width(0)
+ , height(0)
+ , pixels(nullptr)
+ {
+ }
+
+ Bitmap::Bitmap(unsigned w, unsigned h)
+ {
+ width = w;
+ height = h;
+ pixels = new Color[w*h];
+ if (pixels == nullptr)
+ throw Exception("No enough memory.");
+ }
+
+ Bitmap::~Bitmap()
+ {
+ stbi_image_free(pixels);
+ }
+
+ void Bitmap::bind(Color* p, int w, int h)
+ {
+ if (pixels != nullptr)
+ delete[] pixels;
+ pixels = p;
+ width = w;
+ height = h;
+ }
+
+ void Bitmap::resetPixels(const Color* p, int w, int h)
+ {
+ if (pixels != nullptr)
+ delete[] pixels;
+ pixels = new Color[w*h];
+ if (pixels == nullptr)
+ throw Exception("Not enough memory.");
+ size_t s = w * h * sizeof(Color);
+ memcpy(pixels, p, s);
+ width = w;
+ height = h;
+ }
+
+ void Bitmap::setPixel(const Color& c, int x, int y)
+ {
+ if (pixels == nullptr)
+ throw Exception("Bitmap don't have pixel space.");
+ if (without<int>(x, 0, width - 1) || without<int>(y, 0, height - 1))
+ return;
+ if (x + y * width >= width * height)
+ throw Exception("Pixel <%d, %d> of bitmap is out of range.", x, y);
+ pixels[x + y * width] = c;
+ }
+
+ void Bitmap::resetPixels(const Color& c, int w, int h)
+ {
+ if (pixels != nullptr)
+ delete[] pixels;
+ pixels = new Color[w*h];
+ if (pixels == nullptr)
+ throw Exception("Not enough memory.");
+ width = w;
+ height = h;
+ for (int x = 0; x < w; ++x)
+ {
+ for (int y = 0; y < h; ++y)
+ {
+ pixels[x + y * w] = c;
+ }
+ }
+ }
+
+ void Bitmap::setPixels(Color* p, int count)
+ {
+ if (count > width * height)
+ throw Exception("Pixels are out of range.");
+ size_t s = width * height * sizeof(Color);
+ memcpy(pixels, p, s);
+ }
+
+ void Bitmap::setPixels(Color c)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ for (int y = 0; y < height; ++y)
+ {
+ pixels[x + y * width] = c;
+ }
+ }
+ }
+
+ Color Bitmap::getPixel(int x, int y)
+ {
+ if (pixels == nullptr)
+ return Color::BLACK;
+ if (without<int>(x, 0, width - 1) || without<int>(y, 0, height - 1))
+ return Color::BLACK;
+ return pixels[x + y * width];
+ }
+
+ const Color* Bitmap::getPixels() const
+ {
+ return pixels;
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/je_bitmap.h b/src/libjin/graphics/je_bitmap.h
new file mode 100644
index 0000000..5ab11ca
--- /dev/null
+++ b/src/libjin/graphics/je_bitmap.h
@@ -0,0 +1,195 @@
+#ifndef __JE_BITMAP_H__
+#define __JE_BITMAP_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <functional>
+
+#include "GLee/GLee.h"
+
+#include "../common/je_types.h"
+#include "../math/je_vector2.hpp"
+
+#include "je_color.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ /// A RGBA32 bitmap.
+ ///
+ /// A bitmap keeps pixels and can't draw directly onto screen. To render bitmap, a texture is required. A
+ /// texture is a renderable hard ware side structure which could be handled with GPU. For instance, opengl
+ /// create texture and store it in GPU memory for rendering them onto hdc.
+ ///
+ class Bitmap
+ {
+ public:
+ ///
+ /// Create bitmap from given file.
+ ///
+ /// @param path Path of image file.
+ /// @return Bitmap if create successful, otherwise retrun false.
+ ///
+ static Bitmap* createBitmap(const char* path);
+
+ ///
+ /// Create bitmap by pixels data.
+ ///
+ /// @param pixels Pixels data.
+ /// @param width Width of bitmap.
+ /// @param height Height of bitmap.
+ /// @return Return bitmap pointer if created, otherwise return null.
+ ///
+ static Bitmap* createBitmap(const void* pixels, unsigned width, unsigned height);
+
+ ///
+ /// Create bitmap from compressed image data.
+ ///
+ /// @param imgData Compressed image data.
+ /// @param size Size of image data.
+ /// @return Return bitmap pointer if created, otherwise return null.
+ ///
+ static Bitmap* createBitmap(const void* imgData, size_t size);
+
+ ///
+ /// Create bitmap with specific color and size.
+ ///
+ /// @param width Width of bitmap.
+ /// @param height Height of bitmap.
+ /// @param color Color of bitmap, black by default.
+ /// @return Return bitmap pointer if created, otherwise return null.
+ ///
+ static Bitmap* createBitmap(int width, int height, Color color = Color::BLACK);
+
+ ///
+ /// Create bitmap and set bitmap pixels with given drawer.
+ ///
+ static Bitmap* createBitmap(int width, int height, std::function<Color(int, int, int, int)> drawer);
+
+ ///
+ /// Create bitmap with another one.
+ ///
+ /// @param bitmap Bitmap be cloned.
+ /// @return Return bitmap pointer if created, otherwise return null.
+ ///
+ static Bitmap* clone(const Bitmap* bitmap);
+
+ ///
+ /// Destructor of bitmap
+ ///
+ virtual ~Bitmap();
+
+ ///
+ /// Directly bind pixels with given pixels data
+ ///
+ /// @param pixels Pixels to be binded.
+ /// @param width Width of bitmap
+ /// @param height Height of bitmap
+ ///
+ void bind(Color* pixels, int width, int height);
+
+ ///
+ /// Reset pixel data with given pixels data.
+ ///
+ /// @param pixels Pixels to be set.
+ /// @param width Width of bitmap
+ /// @param height Height of bitmap
+ ///
+ void resetPixels(const Color* pixels, int width, int height);
+
+ ///
+ /// Reset pixel data with given color.
+ ///
+ /// @param color Color to be set.
+ /// @param width Width of bitmap
+ /// @param height Height of bitmap
+ ///
+ void resetPixels(const Color& color, int width, int height);
+
+ ///
+ /// Set pixel with given color.
+ ///
+ /// @param color Color to be set.
+ /// @param x X value of pixel.
+ /// @param y Y value of pixel.
+ ///
+ void setPixel(const Color& color, int x, int y);
+
+ ///
+ /// Set pixels with given color.
+ ///
+ /// @param color Color to be set.
+ ///
+ void setPixels(Color color);
+
+ ///
+ /// Set pixels with given color data.
+ ///
+ /// @param colors New pixels' colors.
+ /// @param count Number of pixels.
+ ///
+ void setPixels(Color* colors, int count);
+
+ ///
+ /// Get pixel in given position.
+ ///
+ /// @param x X value of position.
+ /// @param y Y value of position.
+ ///
+ Color getPixel(int x, int y);
+
+ ///
+ /// Get pixels.
+ /// @return Colors of the bitmap.
+ ///
+ const Color* getPixels() const;
+
+ ///
+ /// Get bitmap width.
+ ///
+ /// @return Width of bitmap.
+ ///
+ inline int getWidth() const { return width; }
+
+ ///
+ /// Get bitmap height.
+ ///
+ /// @return Height of bitmap.
+ ///
+ inline int getHeight() const { return height; }
+
+ ///
+ /// Get bitmap size.
+ ///
+ /// @return Size of bitmap.
+ ///
+ inline Math::Vector2<int> getSize() const { return Math::Vector2<int>(width, height); }
+
+ protected:
+ ///
+ /// Constructor of bitmap.
+ ///
+ Bitmap();
+
+ ///
+ /// Constructor of bitmap.
+ ///
+ /// @param width Width of bitmap.
+ /// @param height Height of bitmap.
+ ///
+ Bitmap(unsigned w, unsigned h);
+
+ Color * pixels;
+ unsigned width, height;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_canvas.cpp b/src/libjin/graphics/je_canvas.cpp
new file mode 100644
index 0000000..8f216e6
--- /dev/null
+++ b/src/libjin/graphics/je_canvas.cpp
@@ -0,0 +1,101 @@
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "../utils/je_macros.h"
+#include "je_canvas.h"
+#include "je_window.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ const Canvas* Canvas::current = nullptr;
+ const Canvas* const Canvas::DEFAULT_CANVAS = new Canvas(0);
+
+ Canvas* Canvas::createCanvas(int w, int h)
+ {
+ return new Canvas(w, h);
+ }
+
+ Canvas::Canvas(GLuint n)
+ : fbo(n)
+ {
+ }
+
+ Canvas::Canvas(int w, int h)
+ : Graphic(w, h)
+ {
+ GLint current_fbo;
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
+
+ // Generate a new render buffer object
+ fbo = gl.genFrameBuffer();
+ gl.bindFrameBuffer(fbo);
+
+ GLuint texture = getGLTexture();
+ gl.bindTexture(texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl.texImage(GL_RGBA8, w, h, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ gl.bindTexture(0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+ // Unbind framebuffer
+ gl.bindFrameBuffer(current_fbo);
+ }
+
+ Canvas::~Canvas()
+ {
+ }
+
+ bool Canvas::isBinded(const Canvas* cvs)
+ {
+ return current == cvs;
+ }
+
+ /**
+ * bind to canvas
+ */
+ void Canvas::bind(Canvas* canvas)
+ {
+ if (isBinded(canvas)) return;
+ current = canvas;
+ gl.bindFrameBuffer(canvas->fbo);
+ int w = canvas->getWidth();
+ int h = canvas->getHeight();
+ // Set view port to canvas.
+ glViewport(0, 0, w, h);
+ gl.setProjectionMatrix(0, w, 0, h, -1, 1);
+ }
+
+ /**
+ * bind to default screen render buffer.
+ * do some coordinates transform work
+ * https://blog.csdn.net/liji_digital/article/details/79370841
+ * https://blog.csdn.net/lyx2007825/article/details/8792475
+ */
+ void Canvas::unbind()
+ {
+ if (isBinded(DEFAULT_CANVAS)) return;
+ current = DEFAULT_CANVAS;
+ /* get window size as viewport */
+ Window* wnd = Window::get();
+ int w = wnd->getW();
+ int h = wnd->getH();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, DEFAULT_CANVAS->fbo);
+
+ /* set viewport on screen */
+ glViewport(0, 0, w, h);
+
+ gl.setProjectionMatrix(0, w, h, 0, -1, 1);
+
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/je_canvas.h b/src/libjin/graphics/je_canvas.h
new file mode 100644
index 0000000..3964517
--- /dev/null
+++ b/src/libjin/graphics/je_canvas.h
@@ -0,0 +1,69 @@
+#ifndef __JE_CANVAS_H__
+#define __JE_CANVAS_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "je_graphic.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ ///
+ /// Renderable canvas.
+ ///
+ /// A canvas is a rendering target.
+ ///
+ class Canvas
+ : public Graphic
+ {
+ public:
+ ///
+ ///
+ ///
+ static Canvas* createCanvas(int w, int h);
+
+ ///
+ ///
+ ///
+ static void bind(Canvas*);
+
+ ///
+ ///
+ ///
+ static void unbind();
+
+ ///
+ ///
+ ///
+ static bool isBinded(const Canvas*);
+
+ ///
+ ///
+ ///
+ ~Canvas();
+
+ protected:
+ static const Canvas* const DEFAULT_CANVAS;
+ static const Canvas* current;
+
+ ///
+ ///
+ ///
+ Canvas(int w, int h);
+
+ ///
+ ///
+ ///
+ Canvas(GLuint n);
+
+ GLuint fbo;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics)
+
+#endif // __JE_CANVAS_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_color.cpp b/src/libjin/graphics/je_color.cpp
new file mode 100644
index 0000000..c939a1d
--- /dev/null
+++ b/src/libjin/graphics/je_color.cpp
@@ -0,0 +1,22 @@
+#include "je_color.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ const Color Color::WHITE = Color(255, 255, 255);
+ const Color Color::BLACK = Color(0, 0, 0);
+ const Color Color::RED = Color(255, 0, 0);
+ const Color Color::GREEN = Color(0, 255, 0);
+ const Color Color::BLUE = Color(0, 0, 255);
+ const Color Color::MAGENTA = Color(255, 0, 255);
+ const Color Color::YELLOW = Color(255, 255, 0);
+
+ const uint32 Color::RMASK = 0x000000ff;
+ const uint32 Color::GMASK = 0x0000ff00;
+ const uint32 Color::BMASK = 0x00ff0000;
+ const uint32 Color::AMASK = 0xff000000;
+
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/je_color.h b/src/libjin/graphics/je_color.h
new file mode 100644
index 0000000..06b8f61
--- /dev/null
+++ b/src/libjin/graphics/je_color.h
@@ -0,0 +1,119 @@
+/**
+* Some color operating here.
+*/
+#ifndef __JE_COLOR_H__
+#define __JE_COLOR_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "../math/je_math.h"
+
+#include "../common/je_types.h"
+#include "../utils/je_endian.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ typedef uint8 Channel;
+
+ class Color
+ {
+ public:
+ // Built-in colors
+ static const Color WHITE;
+ static const Color BLACK;
+ static const Color RED;
+ static const Color GREEN;
+ static const Color BLUE;
+ static const Color MAGENTA;
+ static const Color YELLOW;
+
+ static const uint32 RMASK;
+ static const uint32 GMASK;
+ static const uint32 BMASK;
+ static const uint32 AMASK;
+
+ ///
+ /// Get lerp color with given factor.
+ ///
+ /// @param start Start color.
+ /// @param end End color.
+ /// @param t Factor of interplation.
+ /// @return Color after interplation.
+ ///
+ static Color lerp(Color start, Color end, float t)
+ {
+ t = Math::clamp<float>(t, 0, 1);
+ Color c;
+ c.r = Math::lerp(start.r, end.r, t);
+ c.g = Math::lerp(start.g, end.g, t);
+ c.b = Math::lerp(start.b, end.b, t);
+ c.a = Math::lerp(start.a, end.a, t);
+ return c;
+ }
+
+ ///
+ ///
+ ///
+ Color() { r = g = b = a = 0; };
+
+ ///
+ ///
+ ///
+ Color(unsigned char _r
+ , unsigned char _g
+ , unsigned char _b
+ , unsigned char _a = 255)
+ {
+ r = _r;
+ g = _g;
+ b = _b;
+ a = _a;
+ }
+
+ Color(const Color& c)
+ {
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ a = c.a;
+ }
+
+ void set(unsigned char _r, unsigned char _g, unsigned char _b, unsigned char _a)
+ {
+ r = _r;
+ g = _g;
+ b = _b;
+ a = _a;
+ }
+
+ void operator = (const Color& c)
+ {
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ a = c.a;
+ }
+
+ bool operator == (const Color& c)
+ {
+ return r == c.r && g == c.g && b == c.b && a == c.a;
+ }
+
+ bool operator != (const Color& c)
+ {
+ return !(r == c.r && g == c.g && b == c.b && a == c.a);
+ }
+
+ Channel r, g, b, a;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // jin_graphics
+
+#endif // __JE_COLOR_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_gl.cpp b/src/libjin/graphics/je_gl.cpp
new file mode 100644
index 0000000..c58f0ac
--- /dev/null
+++ b/src/libjin/graphics/je_gl.cpp
@@ -0,0 +1,116 @@
+#define OGL2D_IMPLEMENT
+#include "je_gl.h"
+#include "je_color.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ OpenGL gl;
+
+ OpenGL::OpenGL()
+ : ogl2d::OpenGL()
+ {
+ mModelViewMatrices.push_back(Matrix());
+ mModelViewMatrix.setIdentity();
+ for (Matrix& m : mModelViewMatrices)
+ mModelViewMatrix *= m;
+ }
+
+ void OpenGL::setColor(Channel r, Channel g, Channel b, Channel a)
+ {
+ setColor(Color(r, g, b, a));
+ }
+
+ void OpenGL::setColor(Color c)
+ {
+ mCurrentColor = c;
+ glColor4f(c.r / 255.f, c.g / 255.f, c.b / 255.f, c.a / 255.f);
+ }
+
+ Color OpenGL::getColor()
+ {
+ return mCurrentColor;
+ }
+
+ void OpenGL::clearMatrix()
+ {
+ mModelViewMatrices.clear();
+ mModelViewMatrices.push_back(Matrix());
+ mModelViewMatrix.setIdentity();
+ }
+
+ void OpenGL::push()
+ {
+ mModelViewMatrices.push_back(Matrix());
+ }
+
+ void OpenGL::pop()
+ {
+ if (mModelViewMatrices.size() == 1)
+ return;
+ mModelViewMatrices.pop_back();
+ mModelViewMatrix.setIdentity();
+ for (Matrix& m : mModelViewMatrices)
+ mModelViewMatrix *= m;
+ }
+
+ void OpenGL::translate(float x, float y)
+ {
+ if (mModelViewMatrices.size() == 1)
+ return;
+ Matrix& m = mModelViewMatrices.back();
+ m.translate(x, y);
+ mModelViewMatrix.translate(x, y);
+ }
+
+ void OpenGL::scale(float sx, float sy)
+ {
+ if (mModelViewMatrices.size() == 1)
+ return;
+ Matrix& m = mModelViewMatrices.back();
+ m.scale(sx, sy);
+ mModelViewMatrix.scale(sx, sy);
+ }
+
+ void OpenGL::rotate(float r)
+ {
+ if (mModelViewMatrices.size() == 1)
+ return;
+ Matrix& m = mModelViewMatrices.back();
+ m.rotate(r);
+ mModelViewMatrix.rotate(r);
+ }
+
+ Matrix OpenGL::getModelViewMatrix(float x, float y, float sx, float sy, float r, float ox, float oy)
+ {
+ Matrix m;
+ m.setTransformation(x, y, r, sx, sy, ox, oy);
+ return mModelViewMatrix*m;
+ }
+
+ Math::Matrix OpenGL::getModelViewMatrix(const Math::Transform& tansform)
+ {
+ return mModelViewMatrix * tansform.getMatrix();
+ }
+
+ Matrix OpenGL::getModelViewMatrix()
+ {
+ return mModelViewMatrix;
+ }
+
+ const Matrix& OpenGL::getProjectionMatrix()
+ {
+ return mProjectionMatrix;
+ }
+
+ void OpenGL::setProjectionMatrix(float l, float r, float b, float t, float n, float f)
+ {
+ mProjectionMatrix.setOrtho(l, r, b, t, n, f);
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/je_gl.h b/src/libjin/graphics/je_gl.h
new file mode 100644
index 0000000..134cfee
--- /dev/null
+++ b/src/libjin/graphics/je_gl.h
@@ -0,0 +1,140 @@
+#ifndef __JE_OPENGL_H__
+#define __JE_OPENGL_H__
+
+#include <vector>
+
+#include "../math/je_matrix.h"
+#include "../math/je_transform.h"
+
+#include "GLee/GLee.h"
+#include "ogl/OpenGL.h"
+
+#include "je_color.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ /*class Canvas;
+ class Shader;
+ class Font;
+*/
+ class OpenGL
+ : public ogl2d::OpenGL
+ {
+ public:
+ ///
+ ///
+ ///
+ OpenGL();
+
+ void setColor(Channel r, Channel g, Channel b, Channel a);
+
+ void setColor(Color c);
+
+ Color getColor();
+
+ void clearMatrix();
+
+ void push();
+
+ void translate(float x, float y);
+
+ void scale(float sx, float sy);
+
+ void rotate(float r);
+
+ void pop();
+
+ ///
+ ///
+ ///
+ Math::Matrix getModelViewMatrix(const Math::Transform& tansform);
+
+ ///
+ /// Get model view matrix.
+ ///
+ Math::Matrix getModelViewMatrix(float x, float y, float sx, float sy, float r, float ox, float oy);
+
+ ///
+ /// Get model view matrix.
+ ///
+ Math::Matrix getModelViewMatrix();
+
+ ///
+ /// Set orthogonal matrix.
+ ///
+ void setProjectionMatrix(float l, float r, float b, float t, float n, float f);
+
+ ///
+ /// Get orthogonal matrix.
+ ///
+ const Math::Matrix& getProjectionMatrix();
+
+ ///
+ ///
+ ///
+ void useShader();
+
+ ///
+ ///
+ ///
+ void useFont();
+
+ ///
+ ///
+ ///
+ void useCanvas();
+
+ ///
+ ///
+ ///
+ void unUseShader();
+
+ private:
+
+ ///
+ ///
+ ///
+ std::vector<Math::Matrix> mModelViewMatrices;
+
+ ///
+ ///
+ ///
+ Math::Matrix mModelViewMatrix;
+
+ ///
+ ///
+ ///
+ Math::Matrix mProjectionMatrix;
+
+ ///
+ ///
+ ///
+ Color mCurrentColor;
+/*
+ ///
+ ///
+ ///
+ Canvas* mCurrentCanvas;
+
+ ///
+ ///
+ ///
+ Shader* mCurrentShader;
+
+ ///
+ ///
+ ///
+ Font* mCurrentFont;
+*/
+ };
+
+ // Singleton.
+ extern OpenGL gl;
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // __JE_OPENGL_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_graphic.cpp b/src/libjin/graphics/je_graphic.cpp
new file mode 100644
index 0000000..552eac6
--- /dev/null
+++ b/src/libjin/graphics/je_graphic.cpp
@@ -0,0 +1,135 @@
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <stdlib.h>
+
+#include "../math/je_matrix.h"
+#include "../math/je_vector2.hpp"
+
+#include "shaders/je_shader.h"
+#include "je_graphic.h"
+
+using namespace JinEngine::Math;
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ Graphic::Graphic(int w, int h)
+ : mTexture(0)
+ , mSize(w, h)
+ {
+ mTexture = gl.genTexture();
+ }
+
+ Graphic::Graphic(const Bitmap* bitmap)
+ : mTexture(0)
+ {
+ mSize.w = bitmap->getWidth();
+ mSize.h = bitmap->getHeight();
+
+ const Color* pixels = bitmap->getPixels();
+
+ mTexture = gl.genTexture();
+ gl.bindTexture(mTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl.texImage(GL_RGBA8, mSize.w, mSize.h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ gl.bindTexture(0);
+ }
+
+ Graphic::~Graphic()
+ {
+ glDeleteTextures(1, &mTexture);
+ }
+
+ void Graphic::render(int x, int y, float sx, float sy, float r, float ox, float oy) const
+ {
+ Math::Matrix modelMatrix = gl.getModelViewMatrix(x, y, sx, sy, r, ox, oy);
+ int w = getWidth(), h = getHeight();
+ static float vertexCoords[8];
+ static float textureCoords[8];
+ // Set vertex coordinates.
+ vertexCoords[0] = 0; vertexCoords[1] = 0;
+ vertexCoords[2] = 0; vertexCoords[3] = h;
+ vertexCoords[4] = w; vertexCoords[5] = h;
+ vertexCoords[6] = w; vertexCoords[7] = 0;
+ // Set texture coordinates.
+ textureCoords[0] = 0; textureCoords[1] = 0;
+ textureCoords[2] = 0; textureCoords[3] = 1;
+ textureCoords[4] = 1; textureCoords[5] = 1;
+ textureCoords[6] = 1; textureCoords[7] = 0;
+ // Set shader.
+ Shader* shader = Shader::getCurrentShader();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ shader->setVertexPointer(2, GL_FLOAT, 0, vertexCoords);
+ shader->setUVPointer(2, GL_FLOAT, 0, textureCoords);
+
+ gl.bindTexture(getGLTexture());
+ gl.drawArrays(GL_QUADS, 0, 4);
+ gl.bindTexture(0);
+ }
+
+ void Graphic::render(const Math::Quad& slice, int x, int y, float sx, float sy, float r, float ax, float ay) const
+ {
+ static float vertexCoords[8];
+ static float textureCoords[8];
+
+ // Set vertex coordinates.
+ vertexCoords[0] = 0; vertexCoords[1] = 0;
+ vertexCoords[2] = 0; vertexCoords[3] = slice.h;
+ vertexCoords[4] = slice.w; vertexCoords[5] = slice.h;
+ vertexCoords[6] = slice.w; vertexCoords[7] = 0;
+ // Set texture coordinates.
+ float slx = slice.x / mSize.w;
+ float sly = slice.y / mSize.h;
+ float slw = slice.w / mSize.w;
+ float slh = slice.h / mSize.h;
+ textureCoords[0] = slx; textureCoords[1] = sly;
+ textureCoords[2] = slx; textureCoords[3] = sly + slh;
+ textureCoords[4] = slx + slw; textureCoords[5] = sly + slh;
+ textureCoords[6] = slx + slw; textureCoords[7] = sly;
+
+ Math::Matrix modelMatrix = gl.getModelViewMatrix(x, y, sx, sy, r, ax, ay);
+
+ Shader* shader = Shader::getCurrentShader();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ shader->setVertexPointer(2, GL_FLOAT, 0, vertexCoords);
+ shader->setUVPointer(2, GL_FLOAT, 0, textureCoords);
+
+ gl.bindTexture(getGLTexture());
+ gl.drawArrays(GL_QUADS, 0, 4);
+ gl.bindTexture(0);
+ }
+
+ void Graphic::render(const Math::Transform& transform) const
+ {
+ Vector2<float> position = transform.getPosition();
+ Vector2<float> origin = transform.getOrigin();
+ Vector2<float> scale = transform.getScale();
+ float angle = transform.getRotation();
+ render(position.x, position.y, scale.x, scale.y, angle, origin.x, origin.y);
+ }
+
+ void Graphic::render(const Math::Quad& slice, const Math::Transform& transform) const
+ {
+ Vector2<float> position = transform.getPosition();
+ Vector2<float> origin = transform.getOrigin();
+ Vector2<float> scale = transform.getScale();
+ float angle = transform.getRotation();
+ render(slice, position.x, position.y, scale.x, scale.y, angle, origin.x, origin.y);
+ }
+
+ //void Graphic::setFilter(GLint min, GLint max)
+ //{
+ // glTexParameteri(GL_)
+ //}
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/je_graphic.h b/src/libjin/graphics/je_graphic.h
new file mode 100644
index 0000000..51c8e3d
--- /dev/null
+++ b/src/libjin/graphics/je_graphic.h
@@ -0,0 +1,94 @@
+#ifndef __JE_GRAPHIC_H__
+#define __JE_GRAPHIC_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "../math/je_quad.h"
+#include "../math/je_vector2.hpp"
+#include "../math/je_transform.h"
+
+#include "je_gl.h"
+#include "je_bitmap.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ /// Class inherites Graphic doesn't keep any state such as origin, scale and other properties.
+ ///
+ class Graphic
+ {
+ public:
+ ///
+ ///
+ ///
+ Graphic(int w = 0, int h = 0);
+
+ ///
+ ///
+ ///
+ Graphic(const Bitmap* bitmap);
+
+ ///
+ ///
+ ///
+ virtual ~Graphic();
+
+ ///
+ ///
+ ///
+ inline int getWidth() const { return mSize.w; }
+
+ ///
+ ///
+ ///
+ inline int getHeight() const { return mSize.h; }
+
+ ///
+ /// Get opengl texture token.
+ ///
+ /// @return OpenGL texture token.
+ ///
+ inline GLuint getGLTexture() const { return mTexture; }
+
+ ///
+ ///
+ ///
+ void setFilter(GLint min, GLint max);
+
+ ///
+ /// Render graphic single with given coordinates.
+ ///
+ void render(int x, int y, float sx = 1, float sy = 1, float r = 0, float ox = 0, float oy = 0) const;
+
+ ///
+ /// Render part of graphic single with given coordinates.
+ ///
+ void render(const Math::Quad& slice, int x, int y, float sx = 1, float sy = 1, float r = 0, float ox = 0, float oy = 0) const;
+
+ ///
+ /// Render with transform.
+ ///
+ void render(const Math::Transform& transform) const;
+
+ ///
+ ///
+ ///
+ void render(const Math::Quad& slice, const Math::Transform& transform) const;
+
+ protected:
+ Math::Vector2<uint> mSize;
+
+ private:
+ GLuint mTexture;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics)
+
+#endif // __JE_GRAPHIC_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_graphics.h b/src/libjin/graphics/je_graphics.h
new file mode 100644
index 0000000..979d8f4
--- /dev/null
+++ b/src/libjin/graphics/je_graphics.h
@@ -0,0 +1,37 @@
+#ifndef __JE_GRAPHICS_H__
+#define __JE_GRAPHICS_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "je_canvas.h"
+#include "je_color.h"
+#include "je_shapes.h"
+#include "je_texture.h"
+#include "je_window.h"
+#include "je_bitmap.h"
+#include "je_image.h"
+#include "je_sprite.h"
+#include "je_sprite_sheet.h"
+
+#include "shaders/je_shader.h"
+
+#include "fonts/je_ttf.h"
+#include "fonts/je_text.h"
+#include "fonts/je_texture_font.h"
+
+#include "particles/je_particle_system.h"
+
+//struct Stats
+//{
+// int drawCalls;
+// int drawCallsBatched;
+// int canvasSwitches;
+// int shaderSwitches;
+// int canvases;
+// int images;
+// int fonts;
+// int64 textureMemory;
+//};
+
+#endif // defined(jin_graphics)
+#endif // __JE_GRAPHICS_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_image.cpp b/src/libjin/graphics/je_image.cpp
new file mode 100644
index 0000000..6baf16d
--- /dev/null
+++ b/src/libjin/graphics/je_image.cpp
@@ -0,0 +1,47 @@
+#include "../filesystem/je_asset_database.h"
+
+#include "stb/stb_image.h"
+
+#include "je_image.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ using namespace Filesystem;
+
+ /*static*/ Image* Image::createImage(const void* imgData, size_t size)
+ {
+ if (imgData == nullptr)
+ return nullptr;
+ int w, h;
+ void* data = stbi_load_from_memory((uint8*)imgData, size, &w, &h, NULL, STBI_rgb_alpha);
+ if (data == nullptr)
+ return nullptr;
+ Image* image = new Image();
+ image->pixels = (Color*)data;
+ image->width = w;
+ image->height = h;
+ return image;
+ }
+
+ Image* Image::createImage(const char* path)
+ {
+ AssetDatabase* fs = AssetDatabase::get();
+ Buffer buffer;
+ fs->read(path, buffer);
+ return createImage(&buffer, buffer.size());
+ }
+
+ Image::Image()
+ : Bitmap()
+ {
+ }
+
+ Image::~Image()
+ {
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/je_image.h b/src/libjin/graphics/je_image.h
new file mode 100644
index 0000000..971ac18
--- /dev/null
+++ b/src/libjin/graphics/je_image.h
@@ -0,0 +1,61 @@
+#ifndef __JE_IMAGE_H__
+#define __JE_IMAGE_H__
+
+#include "je_bitmap.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ /// A readonly bitmap.
+ ///
+ /// Just like bitmap but only from image file. The pixels data is readonly.
+ ///
+ class Image
+ : public Bitmap
+ {
+ public:
+ ///
+ /// Create image from image file.
+ ///
+ /// @param path Image path.
+ /// @return Image if created successfully, otherwise return null.
+ ///
+ static Image* createImage(const char* path);
+
+ ///
+ /// Create image from image data.
+ ///
+ /// @param imgData Image data to create image.
+ /// @param size Size of image data.
+ /// @return Image if created successfully, otherwise return null.
+ ///
+ static Image* createImage(const void* imgData, size_t size);
+
+ ///
+ /// Image destructor.
+ ///
+ ~Image();
+
+ private:
+ ///
+ /// Image constructor.
+ ///
+ Image();
+
+ // Disable setters inherited from Bitmap.
+ void bind(Color* pixels, int w, int h);
+ void resetPixels(const Color* pixels, int w, int h);
+ void resetPixels(const Color& pixels, int w, int h);
+ void setPixel(const Color& pixel, int x, int y);
+ void setPixels(Color pixels);
+ void setPixels(Color* pixels);
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_mesh.cpp b/src/libjin/graphics/je_mesh.cpp
new file mode 100644
index 0000000..dd2d61c
--- /dev/null
+++ b/src/libjin/graphics/je_mesh.cpp
@@ -0,0 +1,11 @@
+#include "je_mesh.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+
+
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/je_mesh.h b/src/libjin/graphics/je_mesh.h
new file mode 100644
index 0000000..e0a38f8
--- /dev/null
+++ b/src/libjin/graphics/je_mesh.h
@@ -0,0 +1,28 @@
+#ifndef __JE_MESH_H__
+#define __JE_MESH_H__
+
+#include "je_graphic.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ /// A 2D mesh.
+ ///
+ class Mesh
+ {
+ public:
+ void setGraphic();
+
+
+ private:
+ const Graphic* mGraphic;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_shapes.cpp b/src/libjin/graphics/je_shapes.cpp
new file mode 100644
index 0000000..0b239e7
--- /dev/null
+++ b/src/libjin/graphics/je_shapes.cpp
@@ -0,0 +1,130 @@
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <string>
+
+#include "../math/je_matrix.h"
+#include "../math/je_constant.h"
+
+#include "shaders/je_shader.h"
+#include "je_shapes.h"
+
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ using namespace Math;
+
+ void point(int x, int y)
+ {
+ float verts[] = { x + 0.5f , y + 0.5f };
+
+ Shader* shader = Shader::getCurrentShader();
+ shader->setVertexPointer(2, GL_FLOAT, 0, verts);
+ Matrix modelMatrix = gl.getModelViewMatrix();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+
+ glDrawArrays(GL_POINTS, 0, 1);
+ }
+
+ void points(int n, GLshort* p)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ shader->setVertexPointer(2, GL_SHORT, 0, p);
+ Matrix modelMatrix = gl.getModelViewMatrix();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+
+ glDrawArrays(GL_POINTS, 0, n);
+ }
+
+ void line(int x1, int y1, int x2, int y2)
+ {
+ float verts[] = {
+ x1 + 0.5f, y1 + 0.5f,
+ x2 + 0.5f, y2 + 0.5f
+ };
+
+ Shader* shader = Shader::getCurrentShader();
+ shader->setVertexPointer(2, GL_FLOAT, 0, verts);
+ Matrix modelMatrix = gl.getModelViewMatrix();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+
+ glDrawArrays(GL_LINES, 0, 2);
+ }
+
+ void circle(RenderMode mode, int x, int y, int r)
+ {
+ r = r < 0 ? 0 : r;
+
+ int points = 40;
+ float two_pi = static_cast<float>(PI * 2);
+ if (points <= 0) points = 1;
+ float angle_shift = (two_pi / points);
+ float phi = .0f;
+
+ float *coords = new float[2 * (points + 1)];
+ for (int i = 0; i < points; ++i, phi += angle_shift)
+ {
+ coords[2 * i] = x + r * cos(phi);
+ coords[2 * i + 1] = y + r * sin(phi);
+ }
+
+ coords[2 * points] = coords[0];
+ coords[2 * points + 1] = coords[1];
+
+ polygon(mode, coords, points);
+
+ delete[] coords;
+ }
+
+ void rect(RenderMode mode, int x, int y, int w, int h)
+ {
+ float coords[] = { x + 0.5f, y + 0.5f, x + w + 0.5f, y + 0.5f, x + w + 0.5f, y + h + 0.5f, x + 0.5f, y + h + 0.5f };
+ polygon(mode, coords, 4);
+ }
+
+ void triangle(RenderMode mode, int x1, int y1, int x2, int y2, int x3, int y3)
+ {
+ float coords[] = { x1 + 0.5f, y1 + 0.5f, x2 + 0.5f, y2 + 0.5f, x3 + 0.5f, y3 + 0.5f };
+ polygon(mode, coords, 3);
+ }
+
+ void polygon_line(float* p, int count)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ Matrix modelMatrix = gl.getModelViewMatrix();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ shader->setVertexPointer(2, GL_FLOAT, 0, p);
+
+ glDrawArrays(GL_LINE_LOOP, 0, count);
+ }
+
+ void polygon(RenderMode mode, float* p, int count)
+ {
+ if (mode == LINE)
+ {
+ polygon_line(p, count);
+ }
+ else if (mode == FILL)
+ {
+ Shader* shader = Shader::getCurrentShader();
+ Matrix modelMatrix = gl.getModelViewMatrix();
+ shader->sendMatrix4(SHADER_MODELVIEW_MATRIX, &modelMatrix);
+ shader->sendMatrix4(SHADER_PROJECTION_MATRIX, &gl.getProjectionMatrix());
+ shader->setVertexPointer(2, GL_FLOAT, 0, p);
+
+ glDrawArrays(GL_POLYGON, 0, count);
+ }
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/je_shapes.h b/src/libjin/graphics/je_shapes.h
new file mode 100644
index 0000000..d62592b
--- /dev/null
+++ b/src/libjin/graphics/je_shapes.h
@@ -0,0 +1,34 @@
+#ifndef __JE_GEOMETRY_H__
+#define __JE_GEOMETRY_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "je_color.h"
+#include "je_canvas.h"
+#include "je_texture.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ enum RenderMode {
+ NONE = 0,
+ FILL ,
+ LINE
+ };
+
+ extern void line(int x1, int y1, int x2, int y2);
+ extern void rect(RenderMode mode, int x, int y, int w, int h);
+ extern void triangle(RenderMode mode, int x1, int y1, int x2, int y2, int x3, int y3);
+ extern void circle(RenderMode mode, int x, int y, int r);
+ extern void point(int x, int y);
+ extern void points(int n, GLshort* p, GLubyte* c);
+ extern void polygon(RenderMode mode, float* p, int count);
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // jin_graphics
+
+#endif // __JE_GEOMETRY_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite.cpp b/src/libjin/graphics/je_sprite.cpp
new file mode 100644
index 0000000..b92a2c2
--- /dev/null
+++ b/src/libjin/graphics/je_sprite.cpp
@@ -0,0 +1,133 @@
+#include "shaders/je_shader.h"
+
+#include "je_sprite.h"
+
+using namespace JinEngine::Math;
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ Sprite::Sprite()
+ : mGraphic(nullptr)
+ , mColor(255, 255, 255, 255)
+ , mIsOriginEnum(false)
+ {
+ }
+
+ Sprite::~Sprite()
+ {
+ }
+
+ void Sprite::setQuad(int x, int y, int w, int h)
+ {
+ mQuad.x = x;
+ mQuad.y = y;
+ mQuad.w = w;
+ mQuad.h = h;
+ if (mIsOriginEnum)
+ setOrigin(mOriginEnum);
+ }
+
+ void Sprite::setRotation(float r)
+ {
+ mTransform.setRotation(r);
+ }
+
+ void Sprite::setOrigin(Origin origin)
+ {
+ mIsOriginEnum = true;
+ mOriginEnum = origin;
+ int l = 0, r = 0, t = 0, b = 0;
+ Vector2<int> size = getSize();
+ r = size.w;
+ b = size.h;
+ switch (origin)
+ {
+ case TopLeft:
+ mTransform.setOrigin(1, t);
+ break;
+ case TopCenter:
+ mTransform.setOrigin(r / 2.f, t);
+ break;
+ case TopRight:
+ mTransform.setOrigin(r, t);
+ break;
+ case MiddleLeft:
+ mTransform.setOrigin(1, b / 2.f);
+ break;
+ case MiddleCenter:
+ mTransform.setOrigin(r / 2.f, b / 2.f);
+ break;
+ case MiddleRight:
+ mTransform.setOrigin(r, b / 2.f);
+ break;
+ case BottomLeft:
+ mTransform.setOrigin(l, b);
+ break;
+ case BottomCenter:
+ mTransform.setOrigin(r / 2.f, b);
+ break;
+ case BottomRight:
+ mTransform.setOrigin(r, b);
+ break;
+ }
+ }
+
+ void Sprite::setOrigin(int x, int y)
+ {
+ mTransform.setOrigin(x, y);
+ mIsOriginEnum = false;
+ }
+
+ void Sprite::setPosition(float x, float y)
+ {
+ mTransform.setPosition(x, y);
+ }
+
+ void Sprite::setScale(float x, float y)
+ {
+ mTransform.setScale(x, y);
+ }
+
+ void Sprite::setColor(Color color)
+ {
+ mColor = color;
+ }
+
+ void Sprite::setGraphic(const Graphic* graphic)
+ {
+ mGraphic = graphic;
+ int w = mGraphic->getWidth();
+ int h = mGraphic->getHeight();
+ setQuad(0, 0, w, h);
+ }
+
+ void Sprite::move(float x, float y)
+ {
+ mTransform.move(x, y);
+ }
+
+ void Sprite::rotate(float r)
+ {
+ mTransform.rotate(r);
+ }
+
+ void Sprite::scale(float sx, float sy)
+ {
+ mTransform.scale(sx, sy);
+ }
+
+ void Sprite::render()
+ {
+ Color c = gl.getColor();
+ gl.setColor(mColor);
+ if (mGraphic != nullptr)
+ mGraphic->render(mQuad, mTransform);
+ gl.setColor(c);
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite.h b/src/libjin/graphics/je_sprite.h
new file mode 100644
index 0000000..faf16df
--- /dev/null
+++ b/src/libjin/graphics/je_sprite.h
@@ -0,0 +1,81 @@
+#ifndef __JE_SPRITE_H__
+#define __JE_SPRITE_H__
+
+#include "../common/je_types.h"
+#include "../math/je_vector2.hpp"
+
+#include "shaders/je_shader.h"
+#include "je_color.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ /// A sprite is unit of rendering. Animation is based on sprite, but not texture or other graphic stuff.
+ ///
+ class Sprite
+ {
+ public:
+ Sprite();
+ virtual ~Sprite();
+
+ enum Origin
+ {
+ TopLeft,
+ TopCenter,
+ TopRight,
+ MiddleLeft,
+ MiddleCenter,
+ MiddleRight,
+ BottomLeft,
+ BottomCenter,
+ BottomRight
+ };
+
+ void setQuad(int x, int y, int w, int h);
+ void setRotation(float r);
+ void setOrigin(Origin origin);
+ void setOrigin(int x, int y);
+ void setPosition(float x, float y);
+ void setScale(float sx, float sy);
+ void setColor(Color color);
+ void setGraphic(const Graphic* graphic);
+
+ void move(float x, float y);
+ void rotate(float r);
+ void scale(float sx, float sy);
+
+ float getRotation() { return mTransform.getRotation(); }
+ Math::Vector2<int> getSize() { return Math::Vector2<int>(mQuad.w, mQuad.h); }
+ const Math::Quad& getQuad() { return mQuad; }
+ const Math::Vector2<float>& getPosition() { return mTransform.getPosition(); }
+ const Math::Vector2<float>& getOrigin() { return mTransform.getOrigin(); }
+ const Math::Vector2<float>& getScale() { return mTransform.getScale(); }
+ const Color& getColor() { return mColor; }
+ const Graphic* getGraphic() { return mGraphic; }
+
+ void render();
+
+ private:
+ ///
+ /// Origin must be 0~1 float value.
+ ///
+ Math::Transform mTransform;
+
+ Color mColor;
+
+ Math::Quad mQuad;
+
+ bool mIsOriginEnum;
+ Origin mOriginEnum;
+
+ const Graphic* mGraphic;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite_batch.cpp b/src/libjin/graphics/je_sprite_batch.cpp
new file mode 100644
index 0000000..f339715
--- /dev/null
+++ b/src/libjin/graphics/je_sprite_batch.cpp
@@ -0,0 +1,11 @@
+#include "je_sprite_batch.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+
+
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite_batch.h b/src/libjin/graphics/je_sprite_batch.h
new file mode 100644
index 0000000..64f9805
--- /dev/null
+++ b/src/libjin/graphics/je_sprite_batch.h
@@ -0,0 +1,20 @@
+#ifndef __JE_GRAPHICS_SPRITE_BATCH_H__
+#define __JE_GRAPHICS_SPRITE_BATCH_H__
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ class SpriteBatch
+ {
+ public:
+
+ private:
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite_sheet.cpp b/src/libjin/graphics/je_sprite_sheet.cpp
new file mode 100644
index 0000000..3a08751
--- /dev/null
+++ b/src/libjin/graphics/je_sprite_sheet.cpp
@@ -0,0 +1,22 @@
+#include "je_sprite_sheet.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ SpriteSheet::SpriteSheet(const Graphic* graphic)
+ : mGraphic(graphic)
+ {
+ }
+
+ Sprite* SpriteSheet::createSprite(const Math::Quad& quad)
+ {
+ Sprite* spr = new Sprite();
+ spr->setGraphic(mGraphic);
+ spr->setQuad(quad.x, quad.y, quad.w, quad.h);
+ return spr;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/je_sprite_sheet.h b/src/libjin/graphics/je_sprite_sheet.h
new file mode 100644
index 0000000..8c56c51
--- /dev/null
+++ b/src/libjin/graphics/je_sprite_sheet.h
@@ -0,0 +1,33 @@
+#ifndef __JE_SPRITE_SHEET_H__
+#define __JE_SPRITE_SHEET_H__
+
+#include <vector>
+
+#include "../math/je_quad.h"
+
+#include "je_sprite.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ class SpriteSheet
+ {
+ public:
+ ///
+ /// Create a new sprite in sheet.
+ ///
+ Sprite* createSprite(const Math::Quad& quad);
+
+ SpriteSheet(const Graphic* graphic);
+
+ private:
+ const Graphic* const mGraphic;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/je_texture.cpp b/src/libjin/graphics/je_texture.cpp
new file mode 100644
index 0000000..8aa3f9a
--- /dev/null
+++ b/src/libjin/graphics/je_texture.cpp
@@ -0,0 +1,44 @@
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <fstream>
+
+#include "../utils/je_utils.h"
+#include "../math/je_math.h"
+
+#include "je_texture.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ Texture* Texture::createTexture(const char* path)
+ {
+ Bitmap* bitmap = Bitmap::createBitmap(path);
+ Texture* texture = createTexture(bitmap);
+ delete bitmap;
+ return texture;
+ }
+
+ /*static*/ Texture* Texture::createTexture(Bitmap* bitmap)
+ {
+ Texture* tex = new Texture(bitmap);
+ return tex;
+ }
+
+ Texture::Texture(const Bitmap* bitmap)
+ : Graphic(bitmap)
+ {
+ }
+
+ Texture::~Texture()
+ {
+ }
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/je_texture.h b/src/libjin/graphics/je_texture.h
new file mode 100644
index 0000000..566ba84
--- /dev/null
+++ b/src/libjin/graphics/je_texture.h
@@ -0,0 +1,57 @@
+#ifndef __JE_TEXTURE_H__
+#define __JE_TEXTURE_H__
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "GLee/GLee.h"
+
+#include "je_color.h"
+#include "je_graphic.h"
+#include "je_bitmap.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ ///
+ ///
+ ///
+ class Texture
+ : public Graphic
+ {
+ public:
+ ///
+ ///
+ ///
+ static Texture* createTexture(const char* path);
+
+ ///
+ ///
+ ///
+ static Texture* createTexture(Bitmap* bitmap);
+
+ ///
+ ///
+ ///
+ static Texture* createTexture();
+
+ ///
+ ///
+ ///
+ ~Texture();
+
+ private:
+ ///
+ ///
+ ///
+ Texture(const Bitmap* bitmap);
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // jin_graphics
+
+#endif // __JE_TEXTURE_H__ \ No newline at end of file
diff --git a/src/libjin/graphics/je_window.cpp b/src/libjin/graphics/je_window.cpp
new file mode 100644
index 0000000..c14d290
--- /dev/null
+++ b/src/libjin/graphics/je_window.cpp
@@ -0,0 +1,130 @@
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include <iostream>
+
+#include "../common/je_exception.h"
+#include "../utils/je_utils.h"
+#include "../audio/sdl/je_sdl_audio.h"
+#include "../utils/je_log.h"
+
+#include "shaders/je_shader.h"
+#include "je_window.h"
+#include "je_gl.h"
+#include "je_canvas.h"
+
+using namespace JinEngine::Graphics::Shaders;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+
+ bool Window::initSystem(const SettingBase* s)
+ {
+ jin_log_info("Initialize window system.");
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ return false;
+
+ const Setting* setting = (Setting*)s;
+ mSize.w = setting->width;
+ mSize.h = setting->height;
+ mFps = setting->fps;
+ bool vsync = setting->vsync;
+ const char* title = setting->title;
+ const char* icon = setting->icon;
+
+ if (mWnd)
+ {
+ SDL_DestroyWindow(mWnd);
+ SDL_FlushEvent(SDL_WINDOWEVENT);
+ }
+
+ SDL_GLContext ctx = NULL;
+
+ if (ctx)
+ {
+ SDL_GL_DeleteContext(ctx);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ int wx = SDL_WINDOWPOS_UNDEFINED,
+ wy = SDL_WINDOWPOS_UNDEFINED;
+
+ int flag = SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL;
+ if (setting->fullscreen) flag |= SDL_WINDOW_FULLSCREEN;
+ if (setting->resizable) flag |= SDL_WINDOW_RESIZABLE;
+
+ mWnd = SDL_CreateWindow(title, wx, wy, mSize.w, mSize.h, flag);
+ if (mWnd == NULL)
+ return false;
+
+ // Set window icon
+ try
+ {
+ Bitmap* bitmap = Bitmap::createBitmap(icon);
+ SDL_Surface *surface;
+ Color* pixels = const_cast<Color*>(bitmap->getPixels());
+ uint w = bitmap->getWidth(), h = bitmap->getHeight();
+ surface = SDL_CreateRGBSurfaceFrom(
+ pixels, w, h, 32, w * 4, Color::RMASK, Color::GMASK, Color::BMASK, Color::AMASK);
+ SDL_SetWindowIcon(mWnd, surface);
+ SDL_FreeSurface(surface);
+ } catch (...) {}
+
+ ctx = SDL_GL_CreateContext(mWnd);
+ if (ctx == NULL)
+ return false;
+ SDL_GL_SetSwapInterval(vsync ? 1 : 0);
+ SDL_GL_MakeCurrent(mWnd, ctx);
+ // Default configuration
+ gl.setClearColor(0, 0, 0, 0xff);
+ glClear(GL_COLOR_BUFFER_BIT);
+ gl.pushColor(0xff, 0xff, 0xff, 0xff);
+ gl.enable(GL_BLEND);
+ gl.enable(GL_TEXTURE_2D);
+ gl.setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ // Bind to default canvas
+ Canvas::unbind();
+ Shader::unuse();
+ // Avoid white blinnk.
+ swapBuffers();
+
+ return true;
+ }
+
+ void Window::quitSystem()
+ {
+ jin_log_info("Quit window system.");
+ // disable opengl
+ gl.disable(GL_BLEND);
+ gl.disable(GL_TEXTURE_2D);
+ // close window
+ SDL_DestroyWindow(mWnd);
+ SDL_Quit();
+ }
+
+ void Window::swapBuffers()
+ {
+ if (mWnd)
+ SDL_GL_SwapWindow(mWnd);
+ }
+
+ void Window::setTitle(const char* title)
+ {
+ SDL_SetWindowTitle(mWnd, title);
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // defined(jin_graphics) \ No newline at end of file
diff --git a/src/libjin/graphics/je_window.h b/src/libjin/graphics/je_window.h
new file mode 100644
index 0000000..831f3e6
--- /dev/null
+++ b/src/libjin/graphics/je_window.h
@@ -0,0 +1,109 @@
+#ifndef __JE_RENDER_WINDOW
+#define __JE_RENDER_WINDOW
+#include "../core/je_configuration.h"
+#if defined(jin_graphics)
+
+#include "SDL2/SDL.h"
+
+#include "../utils/je_utils.h"
+#include "../math/je_vector2.hpp"
+#include "../common/je_subsystem.hpp"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ ///
+ ///
+ ///
+ class Window
+ : public Subsystem<Window>
+ {
+ public:
+ ///
+ ///
+ ///
+ struct Setting : SettingBase
+ {
+ public:
+ const char* title; ///< window title
+ const char* icon; ///< window icon
+ bool fullscreen; ///< full screen
+ int width, height; ///< window size
+ bool vsync; ///< vsync
+ int fps; ///< frame per second
+ bool resizable; ///< resizable
+ };
+
+ ///
+ ///
+ ///
+ void setTitle(const char* title);
+
+ ///
+ ///
+ ///
+ inline int getW(){ return mSize.w; }
+
+ ///
+ ///
+ ///
+ inline int getH(){ return mSize.h; }
+
+ ///
+ ///
+ ///
+ inline int getFPS(){ return mFps; }
+
+ ///
+ ///
+ ///
+ void swapBuffers();
+
+ ///
+ ///
+ ///
+ inline void hide() { SDL_HideWindow(mWnd); };
+
+ ///
+ ///
+ ///
+ void show() { SDL_ShowWindow(mWnd); };
+
+ private:
+
+ // declare a singleton
+ singleton(Window);
+
+ ///
+ ///
+ ///
+ Window() {};
+
+ ///
+ ///
+ ///
+ virtual ~Window() {};
+
+ ///
+ ///
+ ///
+ bool initSystem(const SettingBase* setting) override;
+
+ ///
+ ///
+ ///
+ void quitSystem() override;
+
+ SDL_Window* mWnd;
+ JinEngine::Math::Vector2<unsigned int> mSize;
+ int mFps;
+
+ };
+
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // jin_graphics
+
+#endif // __JE_RENDER_WINDOW \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle.cpp b/src/libjin/graphics/particles/je_particle.cpp
new file mode 100644
index 0000000..53f4570
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle.cpp
@@ -0,0 +1,72 @@
+#include "../../math/je_math.h"
+
+#include "je_particle.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ Particle::Particle(const Graphic* grc)
+ : graphic(grc)
+ {
+ reset();
+ }
+
+ void Particle::reset()
+ {
+ transform.set(0, 0, 1, 1, 0, 0, 0);
+ lifeTime = 1.0f;
+ life = 0.0f;
+ speed.set(0, 0);
+ linearAcceleration.set(0, 0);
+ radialAcceleration = 0.0f;
+ angularSpeed = 0;
+ scaleBegin = 1;
+ scaleEnd = 1;
+ color = Color::WHITE;
+ colorStart = Color::WHITE;
+ colorEnd = Color::WHITE;
+ alive = true;
+ }
+
+ void Particle::update(float dt)
+ {
+ float t = life / lifeTime;
+ // Lerp color
+ color.r = lerp<int>(colorStart.r, colorEnd.r, t);
+ color.g = lerp<int>(colorStart.g, colorEnd.g, t);
+ color.b = lerp<int>(colorStart.b, colorEnd.b, t);
+ color.a = lerp<int>(colorStart.a, colorEnd.a, t);
+ // Lerp scale.
+ Vector2<float> scale = transform.getScale();
+ scale.x = lerp<float>(scaleBegin, scaleEnd, t);
+ scale.y = scale.x;
+ transform.setScale(scale.x, scale.y);
+ // Calculate position.
+ speed += linearAcceleration * dt;
+ transform.move(speed * dt);
+ // Calculate rotation.
+ angularSpeed += radialAcceleration * dt;
+ transform.rotate(angularSpeed * dt);
+ // Update life time.
+ life += dt;
+ alive = life < lifeTime;
+ }
+
+ void Particle::render()
+ {
+ Color c = gl.getColor();
+ gl.setColor(color);
+ if (graphic != nullptr)
+ graphic->render(transform);
+ gl.getColor();
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle.h b/src/libjin/graphics/particles/je_particle.h
new file mode 100644
index 0000000..70a2653
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle.h
@@ -0,0 +1,166 @@
+#ifndef __JE_PARTICLE_H__
+#define __JE_PARTICLE_H__
+
+#include "../../math/je_transform.h"
+#include "../../math/je_vector2.hpp"
+#include "../je_color.h"
+#include "../je_graphic.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ class ParticleEmitter;
+
+ ///
+ ///
+ ///
+ struct LifeTimeDef
+ {
+ bool enableRandom = false;
+ Struct(life,
+ struct
+ {
+ float floor, ceil;
+ } random;
+ float life = 1.0f;
+ );
+ };
+
+ struct ScaleOverTimeDef
+ {
+ bool enable = false;
+ float start = 1;
+ float end = 1;
+ };
+
+ struct ColorOverTimeDef
+ {
+ bool enable = false;
+ Color colorStart = Color::WHITE;
+ Color colorEnd = Color::WHITE;
+ };
+
+ struct linearAccelarationDef
+ {
+ Math::Vector2<float> linearAccelaration;
+ };
+
+ struct RadialAccelarationDef
+ {
+ float radialAccelaration = 0.f;
+ };
+
+ struct AngularSpeedDef
+ {
+ bool enableRandom = false;
+ Struct(angularSpeed,
+ struct
+ {
+ float floor = 0;
+ float ceil = 0;
+ } random;
+ float angularSpeed = 0;
+ );
+ };
+
+ ///
+ ///
+ ///
+ struct ParticleDef
+ {
+ private:
+ friend class ParticleEmitter;
+
+ public:
+ // Basic definitions.
+ LifeTimeDef lifeTimeDef; ///<
+ linearAccelarationDef linearAccelarationDef; ///<
+ RadialAccelarationDef radialAccelarationDef; ///<
+ AngularSpeedDef angularSpeedDef; ///<
+ // Optional definitions.
+ ScaleOverTimeDef sizeOverTimeDef; ///<
+ ColorOverTimeDef colorOverTimeDef; ///<
+ };
+
+ ///
+ /// A single particle contains various properties of particle, such as position, accelaration, color
+ /// and other attributes changed over time.
+ ///
+ struct Particle
+ {
+ ///
+ /// Default constructor.
+ ///
+ Particle(const Graphic* graphic);
+
+ ///
+ /// Reset to default.
+ ///
+ void reset();
+
+ ///
+ ///
+ ///
+ void update(float dt);
+
+ ///
+ ///
+ ///
+ void render();
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Whole life time.
+ ///
+ float lifeTime = 1.0f;
+
+ ///
+ /// Current life time.
+ ///
+ float life = 0.0f;
+
+ const Graphic* graphic;
+
+ ///
+ /// Color over lifetime.
+ ///
+ Color color = Color::WHITE;
+ Color colorStart = Color::WHITE;
+ Color colorEnd = Color::WHITE;
+
+ ///
+ /// Position scale rotation origin.
+ ///
+ Math::Transform transform;
+
+ ///
+ /// Speeds.
+ ///
+ Math::Vector2<float> speed;
+ Math::Vector2<float> linearAcceleration;
+ float angularSpeed;
+ float radialAcceleration = 0;
+
+ ///
+ /// Size over lifetime.
+ ///
+ float scaleBegin = 1;
+ float scaleEnd = 1;
+
+ ///
+ /// Is particle still alive? Alive is equivalent to NOT available in particle pool.
+ ///
+ bool alive = true;
+
+ };
+
+ } // namespace Particles
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle_emitter.cpp b/src/libjin/graphics/particles/je_particle_emitter.cpp
new file mode 100644
index 0000000..d8fb78d
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle_emitter.cpp
@@ -0,0 +1,105 @@
+#include <time.h>
+
+#include "../../math/je_random.h"
+
+#include "je_particle_emitter.h"
+#include "je_particle_system.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ static const uint8 ACCURACY_4 = 4;
+ static const uint8 ACCURACY_5 = 5;
+ static const uint8 ACCURACY_6 = 6;
+
+ // Particle emitter
+ static RandomGenerator rng(0xEA44944);
+
+ ParticleEmitter::ParticleEmitter(ParticleSystem& ps)
+ : mPS(ps)
+ , mDef(ps.mDef.emitterDef)
+ , mPDef(ps.mDef.particleDef)
+ , mTime(0)
+ {
+ }
+
+ void ParticleEmitter::update(float dt)
+ {
+ mTime += dt;
+ for (;mTime >= mInterval; mTime -= mInterval)
+ {
+ emit();
+ // Random rate.
+ if (mDef.emitRateDef.enableRandom)
+ mInterval = rng.randf(mDef.emitRateDef.rate.random.floor, mDef.emitRateDef.rate.random.ceil, ACCURACY_5);
+ else
+ mInterval = mDef.emitRateDef.rate.rate;
+ }
+ }
+
+ void ParticleEmitter::emit()
+ {
+ Particle* p = mPS.claim();
+ if (p == nullptr)
+ return;
+ p->reset();
+ // Init position.
+ if (mDef.positionDef.enableRandom)
+ {
+ float x = rng.randf(mDef.positionDef.position.random.floor.x, mDef.positionDef.position.random.ceil.x, ACCURACY_4);
+ float y = rng.randf(mDef.positionDef.position.random.floor.y, mDef.positionDef.position.random.ceil.y, ACCURACY_4);
+ p->transform.setPosition(x, y);
+ }
+ else
+ {
+ p->transform.setPosition(mDef.positionDef.position.position);
+ }
+ // Init speed.
+ float r = 0;
+ if (mDef.directionDef.enableRandom)
+ r = rng.randf(mDef.directionDef.direction.random.floor, mDef.directionDef.direction.random.ceil, ACCURACY_4);
+ else
+ r = mDef.directionDef.direction.direction;
+ float f = 0;
+ if (mDef.forceDef.enableRandom)
+ f = rng.randf(mDef.forceDef.force.random.floor, mDef.forceDef.force.random.ceil, ACCURACY_4);
+ else
+ f = mDef.forceDef.force.force;
+ p->speed.set(f*cos(r), f*sin(r));
+ // Init life time
+ if (mPDef.lifeTimeDef.enableRandom)
+ p->lifeTime = rng.randf(mPDef.lifeTimeDef.life.random.floor, mPDef.lifeTimeDef.life.random.floor, ACCURACY_4);
+ else
+ p->lifeTime = mPDef.lifeTimeDef.life.life;
+ // Init linear accelaration
+ p->linearAcceleration = mPDef.linearAccelarationDef.linearAccelaration;
+ // Init angular accelaration
+ p->radialAcceleration = mPDef.radialAccelarationDef.radialAccelaration;
+ // Init Angular speed.
+ if (mPDef.angularSpeedDef.enableRandom)
+ p->angularSpeed = rng.randf(mPDef.angularSpeedDef.angularSpeed.random.floor, mPDef.angularSpeedDef.angularSpeed.random.ceil, ACCURACY_4);
+ else
+ p->angularSpeed = mPDef.angularSpeedDef.angularSpeed.angularSpeed;
+ // Scale over time
+ if (mPDef.sizeOverTimeDef.enable)
+ {
+ p->scaleBegin = mPDef.sizeOverTimeDef.start;
+ p->scaleEnd = mPDef.sizeOverTimeDef.end;
+ }
+ // Color over time
+ if (mPDef.colorOverTimeDef.enable)
+ {
+ p->colorStart = mPDef.colorOverTimeDef.colorStart;
+ p->colorEnd = mPDef.colorOverTimeDef.colorEnd;
+ }
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle_emitter.h b/src/libjin/graphics/particles/je_particle_emitter.h
new file mode 100644
index 0000000..44bd1fb
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle_emitter.h
@@ -0,0 +1,136 @@
+#ifndef __JE_PARTICLE_EMITTER_H__
+#define __JE_PARTICLE_EMITTER_H__
+
+#include "../../common/je_temporary.h"
+#include "../../math/je_vector2.hpp"
+
+#include "je_particle.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ struct PositionDef
+ {
+ bool enableRandom = false;
+ Struct(position,
+ struct
+ {
+ Math::Vector2<float> floor;
+ Math::Vector2<float> ceil;
+ } random;
+ Math::Vector2<float> position;
+ );
+ };
+
+ struct DirectionDef
+ {
+ bool enableRandom = false;
+ Struct(direction,
+ struct
+ {
+ float floor = 0;
+ float ceil = 0;
+ } random;
+ float direction = 0;
+ );
+ };
+
+ ///
+ /// How many particles emitted per second.
+ ///
+ struct EmitRateDef
+ {
+ bool enableRandom = false;
+ Struct(rate,
+ struct
+ {
+ float floor = 1;
+ float ceil = 1;
+ } random;
+ float rate = 1;
+ );
+ };
+
+ ///
+ /// Initial speed of particle.
+ ///
+ struct ForceDef
+ {
+ bool enableRandom = false;
+ Struct(force,
+ struct
+ {
+ float floor = 1;
+ float ceil = 1;
+ } random;
+ float force = 1;
+ );
+ };
+
+ ///
+ /// Definition of particle emitter.
+ ///
+ struct ParticleEmitterDef
+ {
+ EmitRateDef emitRateDef; ///< Emit rate.
+
+ PositionDef positionDef; ///< Emit position(relativily to the particle system center).
+ DirectionDef directionDef; ///< Emit direction.
+ ForceDef forceDef; ///< Emit force.
+ };
+
+ class ParticleSystem;
+
+ ///
+ /// Emit a single particle.
+ ///
+ class ParticleEmitter
+ {
+ public:
+ ///
+ ///
+ ///
+ ParticleEmitter(ParticleSystem& ps);
+
+ ///
+ ///
+ ///
+ void update(float dt);
+
+ private:
+ ///
+ ///
+ ///
+ ParticleSystem& mPS;
+
+ const ParticleEmitterDef& mDef;
+
+ const ParticleDef& mPDef;
+
+ ///
+ /// Emit particle according to emitter definition and particle definition, particle system should
+ /// assign particle value to the particle in particle pool, but not use this return particle.
+ ///
+ void emit();
+
+ ///
+ ///
+ ///
+ float mTime;
+
+ ///
+ ///
+ ///
+ float mInterval;
+
+ };
+
+ } // namespace Particles
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle_pool.h b/src/libjin/graphics/particles/je_particle_pool.h
new file mode 100644
index 0000000..46cd73a
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle_pool.h
@@ -0,0 +1,24 @@
+#ifndef __JE_PARTICLE_BATCH_H__
+#define __JE_PARTICLE_BATCH_H__
+
+#include "../../common/je_pool.hpp"
+
+#include "je_particle.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ ///
+ /// Particle pool for reducing memory fragmentation.
+ ///
+ typedef Pool<Particle> ParticlePool;
+
+ } // namespace Particles
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle_system.cpp b/src/libjin/graphics/particles/je_particle_system.cpp
new file mode 100644
index 0000000..a81a3c9
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle_system.cpp
@@ -0,0 +1,67 @@
+#include "je_particle_system.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ ParticleSystem::ParticleSystem(const ParticleSystemDef& def)
+ : mDef(def)
+ , mEmitter(*this)
+ , mParticlePool(def.maxParticleCount, sizeof(Particle))
+ {
+ }
+
+ ParticleSystem::~ParticleSystem()
+ {
+ }
+
+ void ParticleSystem::update(float dt)
+ {
+ mEmitter.update(dt);
+ for (int i = 0; i < mAliveParticles.size(); ++i)
+ {
+ Particle* p = mAliveParticles[i];
+ if (p->alive == false)
+ {
+ recycle(i, p);
+ --i;
+ }
+ else
+ {
+ p->update(dt);
+ }
+ }
+ }
+
+ void ParticleSystem::render(float x, float y, float sx /* = 1 */, float sy /* = 1 */, float r /* = 0 */, float ax /* = 0 */, float ay /* = 0 */)
+ {
+ for (Particle* p : mAliveParticles)
+ p->render();
+ }
+
+ void ParticleSystem::setGraphic(const Graphic* graphic)
+ {
+ mGraphic = graphic;
+ }
+
+ Particle* ParticleSystem::claim()
+ {
+ Particle* p = new (mParticlePool.GetNextWithoutInitializing()) Particle(mGraphic);
+ mAliveParticles.push_back(p);
+ return p;
+ }
+
+ void ParticleSystem::recycle(int i, Particle* p)
+ {
+ if (i >= mAliveParticles.size())
+ return;
+ mAliveParticles.erase(mAliveParticles.begin() + i);
+ mParticlePool.Delete(p);
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libjin/graphics/particles/je_particle_system.h b/src/libjin/graphics/particles/je_particle_system.h
new file mode 100644
index 0000000..8ee79ec
--- /dev/null
+++ b/src/libjin/graphics/particles/je_particle_system.h
@@ -0,0 +1,115 @@
+#ifndef __JE_PARTICLE_EMMITTER_H__
+#define __JE_PARTICLE_EMMITTER_H__
+
+#include <vector>
+
+#include "../../common/je_temporary.h"
+#include "../../game/je_gameobject.h"
+
+#include "../je_sprite.h"
+
+#include "je_particle_emitter.h"
+#include "je_particle_pool.h"
+#include "je_particle.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Particles
+ {
+
+ ///
+ /// Definition of particle system.
+ ///
+ struct ParticleSystemDef
+ {
+ ParticleSystemDef() {}
+ uint maxParticleCount = 1; ///< Max count of particles in pool. 1 by default.
+ ParticleEmitterDef emitterDef; ///< Particle emitter definition.
+ ParticleDef particleDef; ///< Particle definition.
+ };
+
+ ///
+ /// Particle emitter, handle all particles it emitts.
+ ///
+ class ParticleSystem/* : public Game::GameObject*/
+ {
+ public:
+ ///
+ /// Particle system constructor
+ ///
+ /// @param def Definition of particle system.
+ ///
+ ParticleSystem(const ParticleSystemDef& def);
+
+ ///
+ /// Particle system destructor.
+ ///
+ ~ParticleSystem();
+
+ ///
+ /// Update particle system and all alive particles.
+ ///
+ void update(float dt);
+
+ ///
+ /// Render particle system.
+ ///
+ void render(float x, float y, float sx = 1, float sy = 1, float r = 0, float ax = 0, float ay = 0);
+
+ ///
+ /// Set sprite to render.
+ ///
+ /// @param sprite Sprite to render.
+ ///
+ void setGraphic(const Graphic* graphic);
+
+ private:
+
+ friend class ParticleEmitter;
+
+ ParticleSystem();
+
+ ///
+ ///
+ ///
+ Particle* claim();
+
+ ///
+ ///
+ ///
+ void recycle(int i, Particle* p);
+
+ ///
+ /// Particle system definition.
+ ///
+ ParticleSystemDef mDef;
+
+ ///
+ /// Sprite to be drawn.
+ ///
+ const Graphic* mGraphic;
+
+ ///
+ /// Particle emitter.
+ ///
+ ParticleEmitter mEmitter;
+
+ ///
+ /// Particle pool.
+ ///
+ ParticlePool mParticlePool;
+
+ ///
+ /// Alive particles, that means these particles could join to the life cycle loop.
+ ///
+ std::vector<Particle*> mAliveParticles;
+
+ };
+
+ } // namespace Particles
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/built-in/je_default.shader.h b/src/libjin/graphics/shaders/built-in/je_default.shader.h
new file mode 100644
index 0000000..3f57c44
--- /dev/null
+++ b/src/libjin/graphics/shaders/built-in/je_default.shader.h
@@ -0,0 +1,21 @@
+// Ĭshader
+static const char* default_shader = R"(
+
+#VERTEX_SHADER
+
+Vertex vert(Vertex v)
+{
+ return v;
+}
+
+#END_VERTEX_SHADER
+
+#FRAGMENT_SHADER
+
+Color frag(Color col, Texture tex, Vertex v)
+{
+ return col * texel(tex, v.uv);
+}
+
+#END_FRAGMENT_SHADER
+)"; \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/built-in/je_font.shader.h b/src/libjin/graphics/shaders/built-in/je_font.shader.h
new file mode 100644
index 0000000..e04c225
--- /dev/null
+++ b/src/libjin/graphics/shaders/built-in/je_font.shader.h
@@ -0,0 +1,21 @@
+// shader
+static const char* font_shader = R"(
+
+#VERTEX_SHADER
+
+Vertex vert(Vertex v)
+{
+ return v;
+}
+
+#END_VERTEX_SHADER
+
+#FRAGMENT_SHADER
+
+Color frag(Color col, Texture tex, Vertex v)
+{
+ return Color(col.rgb, texel(tex, v.uv).a);
+}
+
+#END_FRAGMENT_SHADER
+)"; \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/built-in/je_texture.shader.h b/src/libjin/graphics/shaders/built-in/je_texture.shader.h
new file mode 100644
index 0000000..d1fc86f
--- /dev/null
+++ b/src/libjin/graphics/shaders/built-in/je_texture.shader.h
@@ -0,0 +1,21 @@
+// ͼshader
+static const char* texture_shader = R"(
+
+#VERTEX_SHADER
+
+Vertex vert(Vertex v)
+{
+ return v;
+}
+
+#END_VERTEX_SHADER
+
+#FRAGMENT_SHADER
+
+Color frag(Color col, Texture tex, Vertex v)
+{
+ return col * texel(tex, v.uv);
+}
+
+#END_FRAGMENT_SHADER
+)"; \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/je_base.shader.h b/src/libjin/graphics/shaders/je_base.shader.h
new file mode 100644
index 0000000..34996bc
--- /dev/null
+++ b/src/libjin/graphics/shaders/je_base.shader.h
@@ -0,0 +1,89 @@
+#ifndef __JE_BASE_SHADER_H__
+#define __JE_BASE_SHADER_H__
+
+static const char* base_shared = R"(
+#define Number float
+#define Texture sampler2D
+#define Canvas sampler2D
+#define Color vec4
+#define Vec2 vec2
+#define Vec3 vec3
+#define Vec4 vec4
+
+#define texel texture2D
+
+struct Vertex
+{
+ vec2 xy;
+ vec2 uv;
+};
+
+)";
+
+static const int BASE_SHARED_SIZE = strlen(base_shared);
+
+static const char* base_vertex = R"(
+#version 130 core
+
+%s
+// Projection matrix
+uniform mat4 jin_ProjectionMatrix;
+// Model view matrix
+uniform mat4 jin_ModelViewMatrix;
+
+in vec2 jin_VertexCoords;
+in vec2 jin_TextureCoords;
+
+out vec4 jin_Color;
+out vec2 jin_XY;
+out vec2 jin_UV;
+
+%s
+
+void main()
+{
+ vec4 v = jin_ModelViewMatrix * vec4(jin_VertexCoords, 0, 1.0);
+ Vertex _v = vert(Vertex(v.xy, jin_TextureCoords));
+ gl_Position = jin_ProjectionMatrix * vec4(_v.xy, 0, 1.0f);
+ jin_Color = gl_Color;
+ jin_XY = _v.xy;
+ jin_UV = _v.uv;
+}
+)";
+
+static const int BASE_VERTEX_SHADER_SIZE = strlen(base_vertex) + BASE_SHARED_SIZE;
+
+#define formatVertexShader(buf, program) sprintf(buf,base_vertex, base_shared, program)
+
+static const char* base_fragment = R"(
+#version 130 core
+
+%s
+
+uniform Texture jin_MainTexture;
+
+in vec4 jin_Color;
+in vec2 jin_XY;
+in vec2 jin_UV;
+
+out vec4 jin_OutColor;
+
+%s
+
+void main()
+{
+ jin_OutColor = frag(jin_Color, jin_MainTexture, Vertex(jin_XY, jin_UV));
+}
+)";
+
+static const int BASE_FRAGMENT_SHADER_SIZE = strlen(base_fragment) + BASE_SHARED_SIZE;
+
+#define formatFragmentShader(buf, program) sprintf(buf, base_fragment, base_shared, program)
+
+static const char* SHADER_PROJECTION_MATRIX = "jin_ProjectionMatrix";
+static const char* SHADER_MODELVIEW_MATRIX = "jin_ModelViewMatrix";
+static const char* SHADER_MAIN_TEXTURE = "jin_MainTexture";
+static const char* SHADER_VERTEX_COORDS = "jin_VertexCoords";
+static const char* SHADER_TEXTURE_COORDS = "jin_TextureCoords";
+
+#endif // __JE_BASE_SHADER_H__
diff --git a/src/libjin/graphics/shaders/je_jsl_compiler.cpp b/src/libjin/graphics/shaders/je_jsl_compiler.cpp
new file mode 100644
index 0000000..feb88d4
--- /dev/null
+++ b/src/libjin/graphics/shaders/je_jsl_compiler.cpp
@@ -0,0 +1,57 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#include "../../Filesystem/je_buffer.h"
+
+#include "je_jsl_compiler.h"
+
+using namespace std;
+using namespace JinEngine::Filesystem;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Shaders
+ {
+
+ #include "je_base.shader.h"
+
+ bool JSLCompiler::compile(const string& jsl, string* vertex_shader, string* fragment_shader)
+ {
+ // parse shader source, need some optimizations
+ int loc_VERTEX_SHADER = jsl.find("#VERTEX_SHADER");
+ int loc_END_VERTEX_SHADER = jsl.find("#END_VERTEX_SHADER");
+ int loc_FRAGMENT_SHADER = jsl.find("#FRAGMENT_SHADER");
+ int loc_END_FRAGMENT_SHADER = jsl.find("#END_FRAGMENT_SHADER");
+ if (loc_VERTEX_SHADER == string::npos
+ || loc_END_VERTEX_SHADER == string::npos
+ || loc_FRAGMENT_SHADER == string::npos
+ || loc_END_FRAGMENT_SHADER == string::npos
+ )
+ return false;
+ // Load vertex and fragment shader source into buffers.
+ {
+ // Compile JSL vertex program.
+ int start = loc_VERTEX_SHADER + strlen("#VERTEX_SHADER");
+ *vertex_shader = jsl.substr(start, loc_END_VERTEX_SHADER - start);
+ Buffer vbuffer = Buffer(vertex_shader->length() + BASE_VERTEX_SHADER_SIZE);
+ formatVertexShader((char*)&vbuffer, vertex_shader->c_str());
+ vertex_shader->assign((char*)&vbuffer);
+ }
+ {
+ // Compile JSL fragment program.
+ int start = loc_FRAGMENT_SHADER + strlen("#FRAGMENT_SHADER");
+ *fragment_shader = jsl.substr(start, loc_END_FRAGMENT_SHADER - start);
+ Buffer fbuffer = Buffer(fragment_shader->length() + BASE_FRAGMENT_SHADER_SIZE);
+ formatFragmentShader((char*)&fbuffer, fragment_shader->c_str());
+ fragment_shader->assign((char*)&fbuffer);
+ }
+ return true;
+ }
+
+ } // namespace Shaders
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // (jin_graphics) && (jin_graphics & jin_graphics_shader) \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/je_jsl_compiler.h b/src/libjin/graphics/shaders/je_jsl_compiler.h
new file mode 100644
index 0000000..df1e827
--- /dev/null
+++ b/src/libjin/graphics/shaders/je_jsl_compiler.h
@@ -0,0 +1,45 @@
+#ifndef __JE_JSL_COMPILER_H__
+#define __JE_JSL_COMPILER_H__
+
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#include <string>
+
+#include "../../common/je_singleton.hpp"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Shaders
+ {
+
+ ///
+ /// Compile JSL into GLSL.
+ ///
+ class JSLCompiler : public Singleton<JSLCompiler>
+ {
+ public:
+ ///
+ /// Compile JSL shader source into GLSL.
+ ///
+ /// @param jsl JSL shader source.
+ /// @param glsl_vertex Output of vertex glsl shader source.
+ /// @param glsl_fragment Output of fragment glsl shader source.
+ /// @return True if compile successful, otherwise return false.
+ ///
+ bool compile(const std::string& jsl, std::string* glsl_vertex, std::string* glsl_fragment);
+
+ private:
+ singleton(JSLCompiler);
+
+ };
+
+ } // namespace Shaders
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // (jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#endif // __JE_JSL_COMPILER_H__
diff --git a/src/libjin/graphics/shaders/je_shader.cpp b/src/libjin/graphics/shaders/je_shader.cpp
new file mode 100644
index 0000000..c1abbf0
--- /dev/null
+++ b/src/libjin/graphics/shaders/je_shader.cpp
@@ -0,0 +1,281 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#include <iostream>
+
+#include "../../filesystem/je_buffer.h"
+#include "../../utils/je_macros.h"
+
+#include "je_jsl_compiler.h"
+#include "je_shader.h"
+
+using namespace std;
+using namespace JinEngine::Filesystem;
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Shaders
+ {
+
+ //
+ // default_texture
+ // base_shader
+ // SHADER_FORMAT_SIZE
+ // formatShader
+ //
+#include "built-in/je_default.shader.h"
+
+//
+// https://stackoverflow.com/questions/27941496/use-sampler-without-passing-through-value
+// The default value of a sampler variable is 0. From the GLSL 3.30 spec,
+// section "4.3.5 Uniforms":
+//
+// The link time initial value is either the value of the variable's
+// initializer, if present, or 0 if no initializer is present.Sampler
+// types cannot have initializers.
+//
+// Since a value of 0 means that it's sampling from texture unit 0, it will
+// work without ever setting the value as long as you bind your textures to
+// unit 0. This is well defined behavior.
+//
+// Since texture unit 0 is also the default until you call glActiveTexture()
+// with a value other than GL_TEXTURE0, it's very common to always use unit
+// 0 as long as shaders do not need more than one texture.Which means that
+// often times, setting the sampler uniforms is redundant for simple
+// applications.
+//
+// I would still prefer to always set the values.If nothing else, it makes
+// it clear to anybody reading your code that you really mean to sample from
+// texture unit 0, and did not just forget to set the value.
+//
+ const int DEFAULT_TEXTURE_UNIT = 0;
+
+ /*static*/ Shader* Shader::CurrentShader = nullptr;
+
+ Shader* Shader::createShader(const string& program)
+ {
+ Shader* shader = nullptr;
+ try
+ {
+ shader = new Shader(program);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+ return shader;
+ }
+
+ Shader::Shader(const string& program)
+ : mCurrentTextureUnit(DEFAULT_TEXTURE_UNIT)
+ {
+ if (!compile(program))
+ throw 0;
+ }
+
+ Shader::~Shader()
+ {
+ if (CurrentShader == this)
+ unuse();
+ // delete shader program
+ glDeleteShader(mPID);
+ }
+
+ bool Shader::compile(const string& program)
+ {
+ string vertex_shader, fragment_shader;
+ // Compile JSL shader source into GLSL shader source.
+ JSLCompiler* compiler = JSLCompiler::get();
+ if (!compiler->compile(program, &vertex_shader, &fragment_shader))
+ {
+ return false;
+ }
+#define glsl(SHADER_MODE, SHADER, SRC) \
+do{ \
+const GLchar* src = SRC.c_str(); \
+glShaderSource(SHADER, 1, &src, NULL); \
+glCompileShader(SHADER); \
+GLint success; \
+glGetShaderiv(SHADER, GL_COMPILE_STATUS, &success); \
+if (success == GL_FALSE) \
+ return false; \
+}while(0)
+ // Compile vertex shader.
+ GLuint vid = glCreateShader(GL_VERTEX_SHADER);
+ glsl(GL_VERTEX_SHADER, vid, vertex_shader);
+ // Compile fragment shader.
+ GLuint fid = glCreateShader(GL_FRAGMENT_SHADER);
+ glsl(GL_FRAGMENT_SHADER, fid, fragment_shader);
+#undef glsl
+ // Create OpenGL shader program.
+ mPID = glCreateProgram();
+ glAttachShader(mPID, vid);
+ glAttachShader(mPID, fid);
+ glLinkProgram(mPID);
+ GLint success;
+ glGetProgramiv(mPID, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE)
+ return false;
+ }
+
+ static inline GLint getMaxTextureUnits()
+ {
+ GLint maxTextureUnits = 0;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ return maxTextureUnits;
+ }
+
+ void Shader::use()
+ {
+ glUseProgram(mPID);
+ CurrentShader = this;
+ sendInt(SHADER_MAIN_TEXTURE, DEFAULT_TEXTURE_UNIT);
+ }
+
+ /*static*/ void Shader::unuse()
+ {
+ glUseProgram(0);
+ CurrentShader = nullptr;
+ }
+
+ GLint Shader::claimTextureUnit(const std::string& name)
+ {
+ std::map<std::string, GLint>::iterator unit = mTextureUnits.find(name);
+ if (unit != mTextureUnits.end())
+ return unit->second;
+ static GLint MAX_TEXTURE_UNITS = getMaxTextureUnits();
+ if (++mCurrentTextureUnit >= MAX_TEXTURE_UNITS)
+ return 0;
+ mTextureUnits[name] = mCurrentTextureUnit;
+ return mCurrentTextureUnit;
+ }
+
+#define checkJSL() \
+ if (CurrentShader != this) \
+ return
+
+ void Shader::sendInt(const char* name, int value)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, name);
+ glUniform1i(loc, value);
+ }
+
+ void Shader::sendFloat(const char* variable, float number)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, variable);
+ glUniform1f(loc, number);
+ }
+
+ //
+ // https://www.douban.com/note/627332677/
+ // struct TextureUnit
+ // {
+ // GLuint targetTexture1D;
+ // GLuint targetTexture2D;
+ // GLuint targetTexture3D;
+ // GLuint targetTextureCube;
+ // ...
+ // };
+ //
+ // TextureUnit mTextureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
+ // GLuint mCurrentTextureUnit = 0;
+ //
+ void Shader::sendTexture(const char* variable, const Texture* tex)
+ {
+ checkJSL();
+ GLint location = glGetUniformLocation(mPID, variable);
+ if (location == -1)
+ return;
+ GLint unit = claimTextureUnit(variable);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return;
+ }
+ gl.activeTexUnit(unit);
+ glUniform1i(location, unit);
+ gl.bindTexture(tex->getGLTexture());
+ gl.activeTexUnit(0);
+ }
+
+ void Shader::sendCanvas(const char* variable, const Canvas* canvas)
+ {
+ checkJSL();
+ GLint location = glGetUniformLocation(mPID, variable);
+ if (location == -1)
+ return;
+ GLint unit = claimTextureUnit(variable);
+ if (unit == 0)
+ {
+ // TODO: 쳣󶨵
+ return;
+ }
+ glUniform1i(location, unit);
+ glActiveTexture(GL_TEXTURE0 + unit);
+ gl.bindTexture(canvas->getGLTexture());
+
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ void Shader::sendVec2(const char* name, float x, float y)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, name);
+ glUniform2f(loc, x, y);
+ }
+
+ void Shader::sendVec3(const char* name, float x, float y, float z)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, name);
+ glUniform3f(loc, x, y, z);
+ }
+
+ void Shader::sendVec4(const char* name, float x, float y, float z, float w)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, name);
+ glUniform4f(loc, x, y, z, w);
+ }
+
+ void Shader::sendColor(const char* name, const Color* col)
+ {
+ checkJSL();
+ int loc = glGetUniformLocation(mPID, name);
+ glUniform4f(loc,
+ col->r / 255.f,
+ col->g / 255.f,
+ col->b / 255.f,
+ col->a / 255.f
+ );
+ }
+
+ void Shader::sendMatrix4(const char* name, const Math::Matrix* mat4)
+ {
+ int loc = glGetUniformLocation(mPID, name);
+ glUniformMatrix4fv(loc, 1, GL_FALSE, mat4->getElements());
+ }
+
+ void Shader::setVertexPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers)
+ {
+ GLint loc = glGetAttribLocation(mPID, SHADER_VERTEX_COORDS);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(loc, n, type, GL_FALSE, stride, pointers);
+ }
+
+ void Shader::setUVPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers)
+ {
+ GLint loc = glGetAttribLocation(mPID, SHADER_TEXTURE_COORDS);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(loc, n, type, GL_FALSE, stride, pointers);
+ }
+
+ } // namespace Shaders
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // (jin_graphics) && (jin_graphics & jin_graphics_shader) \ No newline at end of file
diff --git a/src/libjin/graphics/shaders/je_shader.h b/src/libjin/graphics/shaders/je_shader.h
new file mode 100644
index 0000000..2009e79
--- /dev/null
+++ b/src/libjin/graphics/shaders/je_shader.h
@@ -0,0 +1,201 @@
+#ifndef __JE_SHADER_H__
+#define __JE_SHADER_H__
+
+#include "../../core/je_configuration.h"
+#if defined(jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#include <string>
+#include <map>
+
+#include "GLee/GLee.h"
+
+#include "../je_color.h"
+#include "../je_texture.h"
+#include "../je_canvas.h"
+
+#include "je_base.shader.h"
+
+namespace JinEngine
+{
+ namespace Graphics
+ {
+ namespace Shaders
+ {
+
+ ///
+ /// Built in shader program.
+ ///
+ /// Built in shader program written with custom shading language called JSL (jin shading language). A
+ /// JSL program is compiled into glsl, so most glsl built in functions and structs are available in
+ /// JSL.
+ ///
+ class Shader
+ {
+ public:
+ ///
+ /// Create shader program from source code.
+ ///
+ /// @param source The shader source code.
+ ///
+ static Shader* createShader(const std::string& source);
+
+ ///
+ /// Get current shader.
+ ///
+ /// @return Current used shader program.
+ ///
+ static inline Shader* getCurrentShader() { return CurrentShader; }
+
+ ///
+ /// Unuse current shader.
+ ///
+ static void unuse();
+
+ ///
+ /// Destructor of shader.
+ ///
+ virtual ~Shader();
+
+ ///
+ /// Use specific shader.
+ ///
+ void use();
+
+ ///
+ /// Send float value to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param number Value of uniform variable to be sent.
+ ///
+ void sendFloat(const char* name, float number);
+
+ ///
+ /// Send texture to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param texture Texture to be sent.
+ ///
+ void sendTexture(const char* name, const Texture* texture);
+
+ ///
+ /// Send integer value to shader
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param value Value to be sent.
+ ///
+ void sendInt(const char* name, int value);
+
+ ///
+ /// Send 2D vector to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param x X value of the vector to be sent.
+ /// @param y Y value of the vector to be sent.
+ ///
+ void sendVec2(const char* name, float x, float y);
+
+ ///
+ /// Send 3D vector to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param x X value of the vector to be sent.
+ /// @param y Y value of the vector to be sent.
+ /// @param z Z value of the vector to be sent.
+ ///
+ void sendVec3(const char* name, float x, float y, float z);
+
+ ///
+ /// Send 4D vector to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param x X value of the vector to be sent.
+ /// @param y Y value of the vector to be sent.
+ /// @param z Z value of the vector to be sent.
+ /// @param w W value of the vector to be sent.
+ ///
+ void sendVec4(const char* name, float x, float y, float z, float w);
+
+ ///
+ /// Send canvas to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param canvas Canvas to be sent.
+ ///
+ void sendCanvas(const char* name, const Canvas* canvas);
+
+ ///
+ /// Send color to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param color Color to be sent.
+ ///
+ void sendColor(const char* name, const Color* color);
+
+ ///
+ /// Send 4 by 4 matrix to shader.
+ ///
+ /// @param name Name of the uniform variable to be assigned.
+ /// @param mat4 Matrix to be sent.
+ ///
+ void sendMatrix4(const char* name, const Math::Matrix* mat4);
+
+ ///
+ /// Set vertices value.
+ ///
+ /// @param n Number of vertices.
+ /// @param type Data type of each component in the array.
+ /// @param stride Byte offset between consecutive generic vertex attributes.
+ /// @param pointers Pointer to the first component of the first generic vertex attribute in the array.
+ ///
+ void setVertexPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers);
+
+ ///
+ /// Set texture UV coordinates.
+ ///
+ /// @param n Number of vertices.
+ /// @param type Data type of each component in the array.
+ /// @param stride Byte offset between consecutive generic vertex attributes.
+ /// @param pointers Pointer to the first component of the first generic vertex attribute in the array.
+ ///
+ void setUVPointer(int n, GLenum type, GLsizei stride, const GLvoid * pointers);
+
+ protected:
+ ///
+ /// Reference of current used shader.
+ ///
+ static Shader* CurrentShader;
+
+ ///
+ /// Get texture unit of the uniform texture. If not, assign one.
+ ///
+ /// @param name Name of the texture uniform variable.
+ /// @return Texture unit which texture variable be assigned.
+ ///
+ GLint claimTextureUnit(const std::string& name);
+
+ ///
+ /// Shader constructor.
+ ///
+ Shader(const std::string& program);
+
+ ///
+ /// Compile JSL program into GLSL source.
+ ///
+ /// @param program JSL source code.
+ /// @return Return true if compile successed, otherwise return false.
+ ///
+ bool compile(const std::string& program);
+
+ GLuint mPID;
+ GLint mCurrentTextureUnit;
+ std::map<std::string, GLint> mTextureUnits;
+
+ };
+
+ } // namespace Shaders
+ } // namespace Graphics
+} // namespace JinEngine
+
+#endif // (jin_graphics) && (jin_graphics & jin_graphics_shader)
+
+#endif // __JE_SHADER_H__ \ No newline at end of file
diff --git a/src/libjin/input/je_event.cpp b/src/libjin/input/je_event.cpp
new file mode 100644
index 0000000..318ce59
--- /dev/null
+++ b/src/libjin/input/je_event.cpp
@@ -0,0 +1,8 @@
+#include "SDL2\SDL.h"
+
+#include "je_event.h"
+
+namespace JinEngine
+{
+
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/input/je_event.h b/src/libjin/input/je_event.h
new file mode 100644
index 0000000..9fe34ff
--- /dev/null
+++ b/src/libjin/input/je_event.h
@@ -0,0 +1,121 @@
+#ifndef __JE_EVENT_H__
+#define __JE_EVENT_H__
+#include "../core/je_configuration.h"
+#if defined(jin_input)
+
+namespace JinEngine
+{
+ namespace Input
+ {
+ #if jin_input == jin_input_sdl
+ #include "SDL.h"
+
+ typedef SDL_Event Event;
+ typedef SDL_Keycode Key;
+ typedef SDL_MouseWheelEvent Wheel;
+
+ ///
+ ///
+ ///
+ enum EventType {
+ QUIT = SDL_QUIT,
+ /* keyboard events */
+ KEY_DOWN = SDL_KEYDOWN,
+ KEY_UP = SDL_KEYUP,
+ /* mouse events */
+ MOUSE_MOTION = SDL_MOUSEMOTION,
+ MOUSE_BUTTON_DOWN = SDL_MOUSEBUTTONDOWN,
+ MOUSE_BUTTON_UP = SDL_MOUSEBUTTONUP,
+ MOUSE_WHEEL = SDL_MOUSEWHEEL,
+ /* joypad events */
+ JOYBUTTONDOWN = SDL_JOYBUTTONDOWN,
+ JOYBUTTONUP = SDL_JOYBUTTONUP,
+ JOYAXISMOTION = SDL_JOYAXISMOTION,
+ JOYBALLMOTION = SDL_JOYBALLMOTION,
+ JOYHATMOTION = SDL_JOYHATMOTION,
+ JOYDEVICEADDED = SDL_JOYDEVICEADDED,
+ JOYDEVICEREMOVED = SDL_JOYDEVICEREMOVED,
+ CONTROLLERBUTTONDOWN = SDL_CONTROLLERBUTTONDOWN,
+ CONTROLLERBUTTONUP = SDL_CONTROLLERBUTTONUP,
+ CONTROLLERAXISMOTION = SDL_CONTROLLERAXISMOTION,
+ /* window evnets */
+ WINDOW_EVENT = SDL_WINDOWEVENT,
+ };
+
+ ///
+ ///
+ ///
+ enum WindowEvent {
+ WINDOW_SHOWN = SDL_WINDOWEVENT_SHOWN ,
+ WINDOW_HIDDEN = SDL_WINDOWEVENT_HIDDEN ,
+ WINDOW_EXPOSED = SDL_WINDOWEVENT_EXPOSED ,
+ WINDOW_MOVED = SDL_WINDOWEVENT_MOVED ,
+ WINDOW_RESIZED = SDL_WINDOWEVENT_RESIZED ,
+ WINDOW_SIZE_CAHNGE = SDL_WINDOWEVENT_SIZE_CHANGED ,
+ WINDOW_MINIMIZED = SDL_WINDOWEVENT_MINIMIZED ,
+ WINDOW_MAXIMIZED = SDL_WINDOWEVENT_MAXIMIZED ,
+ WINDOW_RESTORED = SDL_WINDOWEVENT_RESTORED ,
+ WINDOW_ENTER = SDL_WINDOWEVENT_ENTER ,
+ WINDOW_LEAVE = SDL_WINDOWEVENT_LEAVE ,
+ WINDOW_FOCUS_GAINED = SDL_WINDOWEVENT_FOCUS_GAINED,
+ WINDOW_FOCUS_LOST = SDL_WINDOWEVENT_FOCUS_LOST ,
+ WINDOW_CLOSE = SDL_WINDOWEVENT_CLOSE ,
+ WINDOW_TAKE_FOCUS = SDL_WINDOWEVENT_TAKE_FOCUS ,
+ WINDOW_HIT_TEST = SDL_WINDOWEVENT_HIT_TEST ,
+ };
+
+ ///
+ ///
+ ///
+ inline int pollEvent(Event* e)
+ {
+ return SDL_PollEvent(e);
+ }
+
+ ///
+ ///
+ ///
+ inline const char* getKeyName(Key key)
+ {
+ return SDL_GetKeyName(key);
+ }
+
+ ///
+ ///
+ ///
+ inline const char* getButtonName(int button)
+ {
+ switch (button)
+ {
+ case 1: return "Left";
+ case 2: return "Middle";
+ case 3: return "Right";
+ case 4: return "WheelUp";
+ case 5: return "WheelDown";
+ default: return "?";
+ }
+ }
+
+ /*
+ inline const char* getWheelName(Wheel wheel)
+ {
+ if (wheel.x == -1)
+ return "left";
+ else if (wheel.x == 1)
+ return "right";
+ else if (wheel.y == -1)
+ return "near";
+ else if (wheel.y == 1)
+ return "far";
+ else
+ return "none";
+ }
+ */
+
+ #endif // jin_input == jin_input_sdl
+ } // namespace Input
+} // namespace JinEngine
+
+#endif // defined(jin_input)
+
+#endif // __JE_EVENT_H__
diff --git a/src/libjin/input/je_input.h b/src/libjin/input/je_input.h
new file mode 100644
index 0000000..2abee0a
--- /dev/null
+++ b/src/libjin/input/je_input.h
@@ -0,0 +1,9 @@
+#ifndef __JE_INPUT_H__
+#define __JE_INPUT_H__
+
+#include "je_event.h"
+#include "je_keyboard.h"
+#include "je_mouse.h"
+#include "je_joypad.h"
+
+#endif \ No newline at end of file
diff --git a/src/libjin/input/je_joypad.cpp b/src/libjin/input/je_joypad.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/input/je_joypad.cpp
diff --git a/src/libjin/input/je_joypad.h b/src/libjin/input/je_joypad.h
new file mode 100644
index 0000000..fb5dc38
--- /dev/null
+++ b/src/libjin/input/je_joypad.h
@@ -0,0 +1,56 @@
+#ifndef __JE_JOYPAD_H__
+#define __JE_JOYPAD_H__
+
+#include <SDL2/SDL.h>
+
+namespace JinEngine
+{
+ namespace Input
+ {
+
+ ///
+ ///
+ ///
+ inline const char* getJoyButtonName(int button)
+ {
+ switch (button)
+ {
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A: return "A"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B : return "B"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X : return "X"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y: return "Y"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK: return "Back"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE: return "Guide"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START: return "Start"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK: return "LeftStick"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK: return "RightStick"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return "LeftShoulder"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return "RightShoulder"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP: return "DpadUp"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN: return "DpadDown"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT: return "DpadLeft"; break;
+ case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return "DpadRight"; break;
+ default: return NULL;
+ }
+ }
+
+ ///
+ ///
+ ///
+ inline const char* getJoyAxisName(int axis)
+ {
+ switch (axis)
+ {
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX: return "LeftX"; break;
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY: return "LeftY"; break;
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX: return "RightX"; break;
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY: return "RightY"; break;
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT: return "TriggerLeft"; break;
+ case SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return "TriggerRight"; break;
+ }
+ }
+
+ } // namespace Input
+} // namespace JinEngine
+
+#endif // __JE_JOYPAD_H__
diff --git a/src/libjin/input/je_keyboard.cpp b/src/libjin/input/je_keyboard.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/input/je_keyboard.cpp
diff --git a/src/libjin/input/je_keyboard.h b/src/libjin/input/je_keyboard.h
new file mode 100644
index 0000000..25cfd5d
--- /dev/null
+++ b/src/libjin/input/je_keyboard.h
@@ -0,0 +1,20 @@
+#ifndef __JE_KEYBOARD_H__
+#define __JE_KEYBOARD_H__
+
+namespace JinEngine
+{
+ namespace Input
+ {
+
+ ///
+ ///
+ ///
+ class Keyboard
+ {
+
+ };
+
+ } // namespace Input
+} // namespace JinEngine
+
+#endif // __JE_KEYBOARD_H__
diff --git a/src/libjin/input/je_mouse.cpp b/src/libjin/input/je_mouse.cpp
new file mode 100644
index 0000000..476b1a2
--- /dev/null
+++ b/src/libjin/input/je_mouse.cpp
@@ -0,0 +1,28 @@
+#include "../core/je_configuration.h"
+#if defined(jin_input)
+
+#include "SDL.h"
+
+#include "je_mouse.h"
+
+namespace JinEngine
+{
+ namespace Input
+ {
+
+ void Mouse::getState(int* x, int* y)
+ {
+ #ifdef jin_input == jin_input_sdl
+ SDL_GetMouseState(x, y);
+ #endif
+ }
+
+ void Mouse::setVisible(bool visible)
+ {
+ SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
+ }
+
+ } // namespace Input
+} // namespace JinEngine
+
+#endif // defined(jin_input) \ No newline at end of file
diff --git a/src/libjin/input/je_mouse.h b/src/libjin/input/je_mouse.h
new file mode 100644
index 0000000..3c4ad9b
--- /dev/null
+++ b/src/libjin/input/je_mouse.h
@@ -0,0 +1,49 @@
+#ifndef __JE_MOUSE_H__
+#define __JE_MOUSE_H__
+#include "../core/je_configuration.h"
+#if defined(jin_input)
+
+#include "../common/je_singleton.hpp"
+
+namespace JinEngine
+{
+ namespace Input
+ {
+
+ ///
+ ///
+ ///
+ class Mouse : public Singleton<Mouse>
+ {
+ public:
+ ///
+ ///
+ ///
+ void getState(int* x, int* y);
+
+ ///
+ ///
+ ///
+ void setVisible(bool visible);
+
+ private:
+ singleton(Mouse);
+
+ ///
+ ///
+ ///
+ Mouse() {};
+
+ ///
+ ///
+ ///
+ ~Mouse() {};
+
+ };
+
+ } // namespace Input
+} // namespace JinEngine
+
+#endif // defined(jin_input)
+
+#endif // __JE_MOUSE_H__
diff --git a/src/libjin/math/README.md b/src/libjin/math/README.md
new file mode 100644
index 0000000..a096332
--- /dev/null
+++ b/src/libjin/math/README.md
@@ -0,0 +1 @@
+ļռΪJinEngineڴģ乲õ \ No newline at end of file
diff --git a/src/libjin/math/je_bezier_curve.cpp b/src/libjin/math/je_bezier_curve.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/math/je_bezier_curve.cpp
diff --git a/src/libjin/math/je_bezier_curve.h b/src/libjin/math/je_bezier_curve.h
new file mode 100644
index 0000000..6925baf
--- /dev/null
+++ b/src/libjin/math/je_bezier_curve.h
@@ -0,0 +1,17 @@
+#ifndef __JIN_BEZIER_CURVE_H__
+#define __JIN_BEZIER_CURVE_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ class BezierCurve
+ {
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_constant.h b/src/libjin/math/je_constant.h
new file mode 100644
index 0000000..45577e7
--- /dev/null
+++ b/src/libjin/math/je_constant.h
@@ -0,0 +1,10 @@
+#ifndef __JE_MATH_CONSTANT_H__
+#define __JE_MATH_CONSTANT_H__
+
+#define PI 3.1415926f
+
+// int16 Χ
+#define INT16_RANGE_LEFT -32768
+#define INT16_RANGE_RIGHT 32767
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_ellipse.cpp b/src/libjin/math/je_ellipse.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/math/je_ellipse.cpp
diff --git a/src/libjin/math/je_ellipse.h b/src/libjin/math/je_ellipse.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/libjin/math/je_ellipse.h
diff --git a/src/libjin/math/je_math.h b/src/libjin/math/je_math.h
new file mode 100644
index 0000000..de57e36
--- /dev/null
+++ b/src/libjin/math/je_math.h
@@ -0,0 +1,83 @@
+#ifndef __JE_UTILS_MATH_H__
+#define __JE_UTILS_MATH_H__
+
+#include "je_constant.h"
+#include "je_matrix.h"
+#include "je_quad.h"
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ #ifdef min
+ #undef min
+ #endif // min
+ #ifdef max
+ #undef max
+ #endif // max
+
+ template<typename T>
+ inline T min(T a, T b)
+ {
+ return a < b ? a : b;
+ }
+
+ template<typename T>
+ inline T max(T a, T b)
+ {
+ return a > b ? a : b;
+ }
+
+ template<typename T>
+ inline T clamp(T a, T mi, T ma)
+ {
+ return min<T>(max<T>(a, mi), ma);
+ }
+
+ template<typename T>
+ inline bool within(T a, T mi, T ma)
+ {
+ return a >= mi && a <= ma;
+ }
+
+ template<typename T>
+ inline bool without(T a, T mi, T ma)
+ {
+ return a < mi || a > ma;
+ }
+
+ template<typename T>
+ inline T abs(T a)
+ {
+ return a > 0 ? a : -a;
+ }
+
+ template<typename T>
+ inline T lowerBound(T a, T lower)
+ {
+ return a < lower ? lower : a;
+ }
+
+ template<typename T>
+ inline T upperBound(T a, T upper)
+ {
+ return a > upper ? upper : a;
+ }
+
+ template<typename T>
+ inline T lerp(T a, T b, float t)
+ {
+ return a + t * (b - a);
+ }
+
+ inline float lerp(float a, float b, float f)
+ {
+ f = clamp<float>(f, 0, 1);
+ return a + f * (b - a);
+ }
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif // __JE_UTILS_MATH_H__
diff --git a/src/libjin/math/je_matrix.cpp b/src/libjin/math/je_matrix.cpp
new file mode 100644
index 0000000..bc1fcea
--- /dev/null
+++ b/src/libjin/math/je_matrix.cpp
@@ -0,0 +1,194 @@
+#include "je_matrix.h"
+
+#include <cstring> // memcpy
+#include <cmath>
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ const Matrix Matrix::Identity;
+
+ // | e0 e4 e8 e12 |
+ // | e1 e5 e9 e13 |
+ // | e2 e6 e10 e14 |
+ // | e3 e7 e11 e15 |
+
+ Matrix::Matrix()
+ {
+ setIdentity();
+ }
+
+ Matrix::~Matrix()
+ {
+ }
+
+ void Matrix::setOrtho(float l, float r, float b, float t, float n, float f)
+ {
+ setIdentity();
+ float w = r - l;
+ float h = t - b;
+ float z = f - n;
+ e[0] = 2 / w;
+ e[5] = 2 / h;
+ e[10] = -2 / z;
+ e[12] = -(r + l) / w;
+ e[13] = -(t + b) / h;
+ e[14] = -(f + n) / z;
+ e[15] = 1;
+ }
+
+ // | e0 e4 e8 e12 |
+ // | e1 e5 e9 e13 |
+ // | e2 e6 e10 e14 |
+ // | e3 e7 e11 e15 |
+ // | e0 e4 e8 e12 |
+ // | e1 e5 e9 e13 |
+ // | e2 e6 e10 e14 |
+ // | e3 e7 e11 e15 |
+
+ Matrix Matrix::operator * (const Matrix & m) const
+ {
+ Matrix t;
+
+ t.e[0] = (e[0] * m.e[0]) + (e[4] * m.e[1]) + (e[8] * m.e[2]) + (e[12] * m.e[3]);
+ t.e[4] = (e[0] * m.e[4]) + (e[4] * m.e[5]) + (e[8] * m.e[6]) + (e[12] * m.e[7]);
+ t.e[8] = (e[0] * m.e[8]) + (e[4] * m.e[9]) + (e[8] * m.e[10]) + (e[12] * m.e[11]);
+ t.e[12] = (e[0] * m.e[12]) + (e[4] * m.e[13]) + (e[8] * m.e[14]) + (e[12] * m.e[15]);
+
+ t.e[1] = (e[1] * m.e[0]) + (e[5] * m.e[1]) + (e[9] * m.e[2]) + (e[13] * m.e[3]);
+ t.e[5] = (e[1] * m.e[4]) + (e[5] * m.e[5]) + (e[9] * m.e[6]) + (e[13] * m.e[7]);
+ t.e[9] = (e[1] * m.e[8]) + (e[5] * m.e[9]) + (e[9] * m.e[10]) + (e[13] * m.e[11]);
+ t.e[13] = (e[1] * m.e[12]) + (e[5] * m.e[13]) + (e[9] * m.e[14]) + (e[13] * m.e[15]);
+
+ t.e[2] = (e[2] * m.e[0]) + (e[6] * m.e[1]) + (e[10] * m.e[2]) + (e[14] * m.e[3]);
+ t.e[6] = (e[2] * m.e[4]) + (e[6] * m.e[5]) + (e[10] * m.e[6]) + (e[14] * m.e[7]);
+ t.e[10] = (e[2] * m.e[8]) + (e[6] * m.e[9]) + (e[10] * m.e[10]) + (e[14] * m.e[11]);
+ t.e[14] = (e[2] * m.e[12]) + (e[6] * m.e[13]) + (e[10] * m.e[14]) + (e[14] * m.e[15]);
+
+ t.e[3] = (e[3] * m.e[0]) + (e[7] * m.e[1]) + (e[11] * m.e[2]) + (e[15] * m.e[3]);
+ t.e[7] = (e[3] * m.e[4]) + (e[7] * m.e[5]) + (e[11] * m.e[6]) + (e[15] * m.e[7]);
+ t.e[11] = (e[3] * m.e[8]) + (e[7] * m.e[9]) + (e[11] * m.e[10]) + (e[15] * m.e[11]);
+ t.e[15] = (e[3] * m.e[12]) + (e[7] * m.e[13]) + (e[11] * m.e[14]) + (e[15] * m.e[15]);
+
+ return t;
+ }
+
+ void Matrix::operator *= (const Matrix & m)
+ {
+ Matrix t = (*this) * m;
+ memcpy((void*)this->e, (void*)t.e, sizeof(float) * 16);
+ }
+
+ const float * Matrix::getElements() const
+ {
+ return e;
+ }
+
+ void Matrix::setIdentity()
+ {
+ memset(e, 0, sizeof(float) * 16);
+ e[0] = e[5] = e[10] = e[15] = 1;
+ }
+
+ void Matrix::setTranslation(float x, float y)
+ {
+ setIdentity();
+ e[12] = x;
+ e[13] = y;
+ }
+
+ void Matrix::setRotation(float rad)
+ {
+ setIdentity();
+ float c = cos(rad), s = sin(rad);
+ e[0] = c; e[4] = -s;
+ e[1] = s; e[5] = c;
+ }
+
+ void Matrix::setScale(float sx, float sy)
+ {
+ setIdentity();
+ e[0] = sx;
+ e[5] = sy;
+ }
+
+ void Matrix::setShear(float kx, float ky)
+ {
+ setIdentity();
+ e[1] = ky;
+ e[4] = kx;
+ }
+
+ void Matrix::setTransformation(float x, float y, float angle, float sx, float sy, float ox, float oy)
+ {
+ memset(e, 0, sizeof(float) * 16); // zero out matrix
+ float c = cos(angle), s = sin(angle);
+ // matrix multiplication carried out on paper:
+ // |1 x| |c -s | |sx | |1 -ox|
+ // | 1 y| |s c | | sy | | 1 -oy|
+ // | 1 | | 1 | | 1 | | 1 |
+ // | 1| | 1| | 1| | 1 |
+ // move rotate scale origin
+ e[10] = e[15] = 1.0f;
+ e[0] = c * sx ; // = a
+ e[1] = s * sx ; // = b
+ e[4] = - s * sy; // = c
+ e[5] = c * sy; // = d
+ e[12] = x - ox * e[0] - oy * e[4];
+ e[13] = y - ox * e[1] - oy * e[5];
+ }
+
+ void Matrix::translate(float x, float y)
+ {
+ Matrix t;
+ t.setTranslation(x, y);
+ this->operator *=(t);
+ }
+
+ void Matrix::rotate(float rad)
+ {
+ Matrix t;
+ t.setRotation(rad);
+ this->operator *=(t);
+ }
+
+ void Matrix::scale(float sx, float sy)
+ {
+ Matrix t;
+ t.setScale(sx, sy);
+ this->operator *=(t);
+ }
+
+ void Matrix::shear(float kx, float ky)
+ {
+ Matrix t;
+ t.setShear(kx, ky);
+ this->operator *=(t);
+ }
+
+ // | x |
+ // | y |
+ // | 0 |
+ // | 1 |
+ // | e0 e4 e8 e12 |
+ // | e1 e5 e9 e13 |
+ // | e2 e6 e10 e14 |
+ // | e3 e7 e11 e15 |
+
+ void Matrix::transform(vertex * dst, const vertex * src, int size) const
+ {
+ for (int i = 0; i<size; ++i)
+ {
+ // Store in temp variables in case src = dst
+ float x = (e[0] * src[i].x) + (e[4] * src[i].y) + (0) + (e[12]);
+ float y = (e[1] * src[i].x) + (e[5] * src[i].y) + (0) + (e[13]);
+
+ dst[i].x = x;
+ dst[i].y = y;
+ }
+ }
+
+ } // namespace Math
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/math/je_matrix.h b/src/libjin/math/je_matrix.h
new file mode 100644
index 0000000..bbc831d
--- /dev/null
+++ b/src/libjin/math/je_matrix.h
@@ -0,0 +1,159 @@
+#ifndef __JE_MATRIX_H__
+#define __JE_MATRIX_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ struct vertex
+ {
+ unsigned char r, g, b, a;
+ float x, y;
+ float s, t;
+ };
+ ///
+ /// This class is the basis for all transformations in LOVE. Althought not
+ /// really needed for 2D, it contains 4x4 elements to be compatible with
+ /// OpenGL without conversions.
+ /// Ҫתõľ
+ /// https://blog.csdn.net/candycat1992/article/details/8830894
+ ///
+ class Matrix
+ {
+ private:
+
+ ///
+ /// | e0 e4 e8 e12 |
+ /// | e1 e5 e9 e13 |
+ /// | e2 e6 e10 e14 |
+ /// | e3 e7 e11 e15 |
+ ///
+ float e[16];
+
+ public:
+
+ static const Matrix Identity;
+
+ ///
+ /// Creates a new identity matrix.
+ ///
+ Matrix();
+
+ ///
+ /// Destructor.
+ ///
+ ~Matrix();
+
+ void setOrtho(float _left, float _right, float _bottom, float _top, float _near, float _far);
+
+ ///
+ /// Multiplies this Matrix with another Matrix, changing neither.
+ /// @param m The Matrix to multiply with this Matrix.
+ /// @return The combined matrix.
+ ///
+ Matrix operator * (const Matrix & m) const;
+
+ ///
+ /// Multiplies a Matrix into this Matrix.
+ /// @param m The Matrix to combine into this Matrix.
+ ///
+ void operator *= (const Matrix & m);
+
+ ///
+ /// Gets a pointer to the 16 array elements.
+ /// @return The array elements.
+ ///
+ const float* getElements() const;
+
+ ///
+ /// Resets this Matrix to the identity matrix.
+ ///
+ void setIdentity();
+
+ ///
+ /// Resets this Matrix to a translation.
+ /// @param x Translation along x-axis.
+ /// @param y Translation along y-axis.
+ ///
+ void setTranslation(float x, float y);
+
+ ///
+ /// Resets this Matrix to a rotation.
+ /// @param r The angle in radians.
+ ///
+ void setRotation(float r);
+
+ ///
+ /// Resets this Matrix to a scale transformation.
+ /// @param sx Scale factor along the x-axis.
+ /// @param sy Scale factor along the y-axis.
+ ///
+ void setScale(float sx, float sy);
+
+ ///
+ /// Resets this Matrix to a shear transformation.
+ /// @param kx Shear along x-axis.
+ /// @param ky Shear along y-axis.
+ ///
+ void setShear(float kx, float ky);
+
+ ///
+ /// Creates a transformation with a certain position, orientation, scale
+ /// and offset. Perfect for Drawables -- what a coincidence!
+ ///
+ /// @param x The translation along the x-axis.
+ /// @param y The translation along the y-axis.
+ /// @param angle The rotation (rad) around the center with offset (ox,oy).
+ /// @param sx Scale along x-axis.
+ /// @param sy Scale along y-axis.
+ /// @param ox The offset for rotation along the x-axis.
+ /// @param oy The offset for rotation along the y-axis.
+ /// @param kx Shear along x-axis
+ /// @param ky Shear along y-axis
+ ///
+ void setTransformation(float x, float y, float angle, float sx, float sy, float ox, float oy);
+
+ ///
+ /// Multiplies this Matrix with a translation.
+ /// @param x Translation along x-axis.
+ /// @param y Translation along y-axis.
+ ///
+ void translate(float x, float y);
+
+ ///
+ /// Multiplies this Matrix with a rotation.
+ /// @param r Angle in radians.
+ ///
+ void rotate(float r);
+
+ ///
+ /// Multiplies this Matrix with a scale transformation.
+ /// @param sx Scale factor along the x-axis.
+ /// @param sy Scale factor along the y-axis.
+ ///
+ void scale(float sx, float sy);
+
+ ///
+ /// Multiplies this Matrix with a shear transformation.
+ /// @param kx Shear along the x-axis.
+ /// @param ky Shear along the y-axis.
+ ///
+ void shear(float kx, float ky);
+
+ ///
+ /// Transforms an array of vertices by this Matrix. The sources and
+ /// destination arrays may be the same.
+ ///
+ /// @param dst Storage for the transformed vertices.
+ /// @param src The source vertices.
+ /// @param size The number of vertices.
+ ///
+ void transform(vertex * dst, const vertex * src, int size) const;
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif // __JE_MATRIX_H__
diff --git a/src/libjin/math/je_quad.h b/src/libjin/math/je_quad.h
new file mode 100644
index 0000000..8a7bd64
--- /dev/null
+++ b/src/libjin/math/je_quad.h
@@ -0,0 +1,23 @@
+#ifndef __JE_QUAD_H__
+#define __JE_QUAD_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ ///
+ ///
+ ///
+ struct Quad
+ {
+ ///
+ ///
+ ///
+ float x, y, w, h;
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif // __JE_QUAD_H__
diff --git a/src/libjin/math/je_random.cpp b/src/libjin/math/je_random.cpp
new file mode 100644
index 0000000..216fd79
--- /dev/null
+++ b/src/libjin/math/je_random.cpp
@@ -0,0 +1,52 @@
+#include <math.h>
+#include "je_random.h"
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ RandomGenerator::RandomGenerator(uint32 seed)
+ : mSeed(seed)
+ {
+ }
+
+ uint32 RandomGenerator::rand()
+ {
+ unsigned int next = mSeed;
+ uint32 result;
+
+ next *= 1103515245;
+ next += 12345;
+ result = (unsigned int)(next / 65536) % 2048;
+
+ next *= 1103515245;
+ next += 12345;
+ result <<= 10;
+ result ^= (unsigned int)(next / 65536) % 1024;
+
+ next *= 1103515245;
+ next += 12345;
+ result <<= 10;
+ result ^= (unsigned int)(next / 65536) % 1024;
+
+ mSeed = next;
+
+ return result;
+ }
+
+ uint32 RandomGenerator::rand(uint32 min, uint32 max)
+ {
+ uint32 n = rand();
+ return n % (max - min + 1) + min;
+ }
+
+ float RandomGenerator::randf(float min, float max, int ac)
+ {
+ uint32 a = pow(10.f, ac);
+ uint32 n = rand(min*a, max*a);
+ return (float)n / a;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/libjin/math/je_random.h b/src/libjin/math/je_random.h
new file mode 100644
index 0000000..0f53155
--- /dev/null
+++ b/src/libjin/math/je_random.h
@@ -0,0 +1,33 @@
+#ifndef __JE_RANDOM_H__
+#define __JE_RANDOM_H__
+
+#include "../common/je_types.h"
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ ///
+ /// A random number generator.
+ ///
+ class RandomGenerator
+ {
+ public:
+ RandomGenerator(uint32 seed);
+
+ uint32 rand();
+
+ uint32 rand(uint32 min, uint32 max);
+
+ float randf(float min, float max, int ac);
+
+ private:
+ uint32 mSeed;
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_transform.cpp b/src/libjin/math/je_transform.cpp
new file mode 100644
index 0000000..c0676cb
--- /dev/null
+++ b/src/libjin/math/je_transform.cpp
@@ -0,0 +1,99 @@
+#include "je_transform.h"
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ Transform::Transform()
+ : mScale(1, 1)
+ , mPosition(0, 0)
+ , mOrigin(0, 0)
+ , mRotation(0)
+ {
+ }
+
+ void Transform::set(float x, float y, float sx, float sy, float r, float ox, float oy)
+ {
+ setPosition(x, y);
+ setScale(sx, sy);
+ setRotation(r);
+ setOrigin(ox, oy);
+ }
+
+ void Transform::setScale(float sx, float sy)
+ {
+ mScale.set(sx, sy);
+ }
+
+ Vector2<float> Transform::getScale() const
+ {
+ return mScale;
+ }
+
+ void Transform::scale(float sx, float sy)
+ {
+ mScale.x *= sx;
+ mScale.y *= sy;
+ }
+
+ void Transform::setPosition(const Vector2<float>& p)
+ {
+ setPosition(p.x, p.y);
+ }
+
+ void Transform::setPosition(float x, float y)
+ {
+ mPosition.set(x, y);
+ }
+
+ Vector2<float> Transform::getPosition() const
+ {
+ return mPosition;
+ }
+
+ void Transform::move(float x, float y)
+ {
+ mPosition.x += x;
+ mPosition.y += y;
+ }
+
+ void Transform::move(const Vector2<float>& v)
+ {
+ move(v.x, v.y);
+ }
+
+ void Transform::setOrigin(float x, float y)
+ {
+ mOrigin.set(x, y);
+ }
+
+ Vector2<float> Transform::getOrigin() const
+ {
+ return mOrigin;
+ }
+
+ void Transform::setRotation(float r)
+ {
+ mRotation = r;
+ }
+
+ float Transform::getRotation() const
+ {
+ return mRotation;
+ }
+
+ void Transform::rotate(float r)
+ {
+ mRotation += r;
+ }
+
+ Matrix Transform::getMatrix() const
+ {
+ Matrix m;
+ m.setTransformation(mPosition.x, mPosition.y, mRotation, mScale.x, mScale.y, mOrigin.x, mOrigin.y);
+ return m;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/libjin/math/je_transform.h b/src/libjin/math/je_transform.h
new file mode 100644
index 0000000..cb1f0ee
--- /dev/null
+++ b/src/libjin/math/je_transform.h
@@ -0,0 +1,49 @@
+#ifndef __JE_TRANSFORM_H__
+#define __JE_TRANSFORM_H__
+
+#include "je_matrix.h"
+#include "je_vector2.hpp"
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ class Transform
+ {
+ public:
+ Transform();
+
+ void set(float x, float y, float sx, float sy, float r, float ox, float oy);
+
+ void setScale(float sx, float sy);
+ Vector2<float> getScale() const;
+ void scale(float sx, float sy);
+
+ void setPosition(float x, float y);
+ void setPosition(const Vector2<float>& p);
+ Vector2<float> getPosition() const;
+ void move(float x, float y);
+ void move(const Vector2<float>& v);
+
+ void setOrigin(float x, float y);
+ Vector2<float> getOrigin() const;
+
+ void setRotation(float r);
+ float getRotation() const;
+ void rotate(float r);
+
+ Matrix getMatrix() const;
+
+ private:
+ Vector2<float> mScale;
+ Vector2<float> mPosition;
+ Vector2<float> mOrigin;
+ float mRotation;
+
+ };
+
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_vector2.hpp b/src/libjin/math/je_vector2.hpp
new file mode 100644
index 0000000..b4cab44
--- /dev/null
+++ b/src/libjin/math/je_vector2.hpp
@@ -0,0 +1,63 @@
+#ifndef __JE_VECTOR_H__
+#define __JE_VECTOR_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ template<typename T>
+ class Vector2
+ {
+ public:
+ Vector2()
+ {
+ data[0] = data[1] = 0;
+ }
+ Vector2(T _x, T _y)
+ {
+ data[0] = _x;
+ data[1] = _y;
+ }
+ Vector2(const Vector2<T>& v)
+ {
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ }
+
+ void operator = (const Vector2<T>& v)
+ {
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ }
+
+ Vector2<T> operator * (float n)
+ {
+ return Vector2<T>(data[0]*n, data[1]*n);
+ }
+
+ void operator +=(const Vector2<T> v)
+ {
+ data[0] += v.data[0];
+ data[1] += v.data[1];
+ }
+
+ void set(T _x, T _y)
+ {
+ data[0] = _x;
+ data[1] = _y;
+ }
+
+ T &x = data[0], &y = data[1]; // xy
+ T &w = data[0], &h = data[1]; // wh
+ T &colum = data[0], &row = data[1]; // colum row
+
+ private:
+ T data[2];
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_vector3.hpp b/src/libjin/math/je_vector3.hpp
new file mode 100644
index 0000000..fdab2d0
--- /dev/null
+++ b/src/libjin/math/je_vector3.hpp
@@ -0,0 +1,41 @@
+#ifndef __JE_VECTOR3_H__
+#define __JE_VECTOR3_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ template<typename T>
+ class Vector3
+ {
+ public:
+ Vector3()
+ {
+ data[0] = data[1] = data[2] = 0;
+ }
+ Vector3(T _x, T _y, T _z)
+ {
+ data[0] = _x;
+ data[1] = _y;
+ data[2] = _z;
+ }
+ Vector3(const Vector3<T>& v)
+ {
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+ }
+
+ T &x = data[0], &y = data[1], &z = data[2]; // xyz
+ T &r = data[0], &g = data[1], &b = data[2]; // rgb
+
+ private:
+ T data[3];
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/math/je_vector4.hpp b/src/libjin/math/je_vector4.hpp
new file mode 100644
index 0000000..5cd5b35
--- /dev/null
+++ b/src/libjin/math/je_vector4.hpp
@@ -0,0 +1,45 @@
+#ifndef __JE_VECTOR4_H__
+#define __JE_VECTOR4_H__
+
+namespace JinEngine
+{
+ namespace Math
+ {
+
+ template<typename T>
+ class Vector4
+ {
+ public:
+ Vector4()
+ {
+ data[0] = data[1] = data[2] = data[3] = 0;
+ }
+ Vector4(T _x, T _y, T _z, T _t)
+ {
+ data[0] = _x;
+ data[1] = _y;
+ data[2] = _z;
+ data[3] = _t;
+ }
+ Vector4(const Vector4<T>& v)
+ {
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+ data[3] = v.data[3];
+ }
+
+ T &x = data[0], &y = data[1], &z = data[2], &t = data[3]; // xyzt
+ T &w = data[2], &h = data[3]; // xywh
+ T &r = data[0], &g = data[1], &b = data[2], &a = data[3]; // rgb
+ T &left = data[0], &right = data[1], &top = data[2], &bottom = data[3]; // lrtb
+
+ private:
+ T data[4];
+
+ };
+
+ } // namespace Math
+} // namespace JinEngine
+
+#endif \ No newline at end of file
diff --git a/src/libjin/net/je_net_manager.cpp b/src/libjin/net/je_net_manager.cpp
new file mode 100644
index 0000000..c4f822e
--- /dev/null
+++ b/src/libjin/net/je_net_manager.cpp
@@ -0,0 +1,24 @@
+#include "je_net_manager.h"
+
+namespace JinEngine
+{
+ namespace Net
+ {
+
+ bool NetManager::initSystem(const SettingBase* setting)
+ {
+ #ifdef _WIN32
+ #if jin_net == jin_net_tekcos
+ tk_init();
+ #endif
+ #endif
+ return true;
+ }
+
+ void NetManager::quitSystem()
+ {
+
+ }
+
+ } // namespace Net
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/net/je_net_manager.h b/src/libjin/net/je_net_manager.h
new file mode 100644
index 0000000..62aa947
--- /dev/null
+++ b/src/libjin/net/je_net_manager.h
@@ -0,0 +1,50 @@
+#ifndef __JE_NET_H__
+#define __JE_NET_H__
+#include "../core/je_configuration.h"
+#if defined(jin_net)
+
+#include "../common/je_subsystem.hpp"
+
+#include "je_socket.h"
+
+namespace JinEngine
+{
+ namespace Net
+ {
+
+ ///
+ ///
+ ///
+ class NetManager : public Subsystem<NetManager>
+ {
+ protected:
+ singleton(NetManager);
+
+ ///
+ ///
+ ///
+ NetManager() {};
+
+ ///
+ ///
+ ///
+ ~NetManager() {};
+
+ ///
+ ///
+ ///
+ bool initSystem(const SettingBase* setting) override;
+
+ ///
+ ///
+ ///
+ void quitSystem() override;
+
+ };
+
+ } // namespace Net
+} // namespace JinEngine
+
+#endif // defined(jin_net)
+
+#endif // __JE_NET_H__
diff --git a/src/libjin/net/je_socket.cpp b/src/libjin/net/je_socket.cpp
new file mode 100644
index 0000000..1810601
--- /dev/null
+++ b/src/libjin/net/je_socket.cpp
@@ -0,0 +1,190 @@
+#include "je_socket.h"
+
+namespace JinEngine
+{
+ namespace Net
+ {
+ Socket::Socket(const Socket& socket)
+ : mHandle(socket.mHandle)
+ , mType(socket.mType)
+ {
+ }
+
+ Socket::Socket(const SocketInformation& info)
+ : mType(info.type)
+ {
+ if (mType == SocketType::TCP)
+ {
+ tk_IPaddress ip;
+ ip.host = info.address;
+ ip.port = info.port;
+ mHandle.tcpHandle = tk_tcp_open(ip);
+ }
+ else if (mType == SocketType::UDP)
+ {
+ mHandle.udpHandle = tk_udp_open(info.port);
+ }
+ }
+
+ Socket::Socket(SocketType type, const char* address, unsigned short port)
+ {
+ mType = type;
+ if (mType == SocketType::TCP)
+ {
+ tk_IPaddress ip;
+ #if jin_net == jin_net_tekcos
+ ip.host = tk_strtohl(address);
+ ip.port = port;
+ mHandle.tcpHandle = tk_tcp_open(ip);
+ #endif
+ }
+ else if (mType == SocketType::UDP)
+ {
+ mHandle.udpHandle = tk_udp_open(port);
+ }
+ }
+
+ Socket::Socket(SocketType type, unsigned int address, unsigned short port)
+ {
+ mType = type;
+ if (mType == SocketType::TCP)
+ {
+ tk_IPaddress ip;
+ ip.host = address;
+ ip.port = port;
+ mHandle.tcpHandle = tk_tcp_open(ip);
+ }
+ else if (mType == SocketType::UDP)
+ {
+ mHandle.udpHandle = tk_udp_open(port);
+ }
+ }
+
+ Socket::Socket(SocketType type, unsigned short port)
+ {
+ mType = type;
+ if (mType == SocketType::TCP)
+ {
+ tk_IPaddress ip;
+ ip.host = 0;
+ ip.port = port;
+ mHandle.tcpHandle = tk_tcp_open(ip);
+ }
+ else if (mType == SocketType::UDP)
+ {
+ mHandle.udpHandle = tk_udp_open(port);
+ }
+ }
+
+ #if jin_net == jin_net_tekcos
+
+ Socket::Socket(const tk_TCPsocket& tcphandle)
+ {
+ mHandle.tcpHandle = tcphandle;
+ }
+
+ Socket::Socket(const tk_UDPsocket& udphandle)
+ {
+ mHandle.udpHandle = udphandle;
+ }
+
+ #endif // jin_net == jin_net_tekcos
+
+ Socket::~Socket()
+ {
+ }
+
+ void Socket::configureBlocking(bool blocking)
+ {
+ if (mType != SocketType::TCP)
+ return;
+ #if jin_net == jin_net_tekcos
+ if (blocking)
+ tk_tcp_blocking(&mHandle.tcpHandle);
+ else
+ tk_tcp_nonblocking(&mHandle.tcpHandle);
+ #endif
+ }
+
+ Socket* Socket::accept()
+ {
+ if (mType != SocketType::TCP)
+ return nullptr;
+ Socket* client;
+ #if jin_net == jin_net_tekcos
+ tk_TCPsocket socket = tk_tcp_accept(&mHandle.tcpHandle);
+ client = new Socket(socket);
+ #endif
+ return client;
+ }
+
+ int Socket::receive(char* buffer, int size)
+ {
+ if (mType != SocketType::TCP)
+ return 0;
+ #if jin_net == jin_net_tekcos
+ int len;
+ tk_tcp_recv(&mHandle.tcpHandle, buffer, size, &len);
+ return len;
+ #endif
+ }
+
+ int Socket::send(char* buffer, int size)
+ {
+ if (mType != SocketType::TCP)
+ return 0;
+ #if jin_net == jin_net_tekcos
+ int len;
+ tk_tcp_send(&mHandle.tcpHandle, buffer, size, &len);
+ return len;
+ #endif
+ }
+
+ void Socket::sendTo(char* buffer, int size, unsigned int address, unsigned int port)
+ {
+ if (mType != SocketType::UDP)
+ return;
+ #if jin_net == jin_net_tekcos
+ tk_UDPpack pack;
+ pack.data = buffer;
+ pack.len = size;
+ pack.ip.host = address;
+ pack.ip.port = port;
+ tk_udp_sendto(&mHandle.udpHandle, &pack);
+ #endif
+ }
+
+ int Socket::receiveFrom(char* buffer, int size, unsigned int address, unsigned int port)
+ {
+ if (mType != SocketType::UDP)
+ return 0;
+ int len;
+ #if jin_net == jin_net_tekcos
+ tk_UDPpack pack;
+ pack.data = buffer;
+ pack.len = size;
+ pack.ip.host = address;
+ pack.ip.port = port;
+ tk_udp_recvfrom(&mHandle.udpHandle, &pack);
+ return pack.len;
+ #endif
+ }
+
+ void Socket::close()
+ {
+ if (mType == SocketType::TCP)
+ {
+ #if jin_net == jin_net_tekcos
+ tk_tcp_close(&mHandle.tcpHandle);
+ #endif
+ }
+ else if (mType == SocketType::UDP)
+ {
+ #if jin_net == jin_net_tekcos
+ tk_udp_close(&mHandle.udpHandle);
+ #endif
+ }
+ }
+
+ } // namespace Net
+} // namespace JinEngine \ No newline at end of file
diff --git a/src/libjin/net/je_socket.h b/src/libjin/net/je_socket.h
new file mode 100644
index 0000000..eb01719
--- /dev/null
+++ b/src/libjin/net/je_socket.h
@@ -0,0 +1,144 @@
+#ifndef __JE_NET_SOCKET_H__
+#define __JE_NET_SOCKET_H__
+#include "../core/je_configuration.h"
+#if defined(jin_net)
+
+#include "tekcos/tekcos.h"
+
+namespace JinEngine
+{
+ namespace Net
+ {
+
+ ///
+ ///
+ ///
+ enum SocketType
+ {
+ TCP,
+ UDP
+ };
+
+ ///
+ ///
+ ///
+ struct SocketInformation
+ {
+ unsigned int address;
+ unsigned short port;
+ SocketType type;
+ };
+
+ ///
+ ///
+ ///
+ class Socket
+ {
+ public:
+
+ ///
+ ///
+ ///
+ Socket() {};
+
+ ///
+ ///
+ ///
+ Socket(const Socket& socket);
+
+ ///
+ ///
+ ///
+ Socket(const SocketInformation& socketInformation);
+
+ ///
+ ///
+ ///
+ Socket(SocketType type, unsigned short port);
+
+ ///
+ ///
+ ///
+ Socket(SocketType type, unsigned int address, unsigned short port);
+
+ ///
+ ///
+ ///
+ Socket(SocketType type, const char* address, unsigned short port);
+
+ ///
+ ///
+ ///
+ ~Socket();
+
+ ///
+ ///
+ ///
+ void configureBlocking(bool bocking);
+
+ ///
+ ///
+ ///
+ Socket* accept();
+
+ ///
+ ///
+ ///
+ int receive(char* buffer, int size);
+
+ ///
+ ///
+ ///
+ int send(char* buffer, int size);
+
+ ///
+ ///
+ ///
+ void sendTo(char* buffer, int size, unsigned int address, unsigned int port);
+
+ ///
+ ///
+ ///
+ int receiveFrom(char* buffer, int size, unsigned int address, unsigned int port);
+
+ ///
+ ///
+ ///
+ void close();
+
+ protected:
+ #if jin_net == jin_net_tekcos
+
+ ///
+ ///
+ ///
+ Socket(const tk_TCPsocket& tcpHandle);
+
+ ///
+ ///
+ ///
+ Socket(const tk_UDPsocket& udpHandle);
+
+ ///
+ ///
+ ///
+ union
+ {
+ tk_TCPsocket tcpHandle;
+ tk_UDPsocket udpHandle;
+ } mHandle;
+ #endif
+
+ ///
+ ///
+ ///
+ SocketType mType;
+
+ };
+
+ } // namespace Net
+} // namespace JinEngine
+
+#endif // defined(jin_net)
+
+#endif // __JE_NET_SOCKET_H__ \ No newline at end of file
diff --git a/src/libjin/time/je_timer.cpp b/src/libjin/time/je_timer.cpp
new file mode 100644
index 0000000..8dc5e86
--- /dev/null
+++ b/src/libjin/time/je_timer.cpp
@@ -0,0 +1,122 @@
+#include "../core/je_configuration.h"
+#if defined(jin_time)
+
+#include "je_timer.h"
+
+namespace JinEngine
+{
+ namespace Time
+ {
+
+
+ Timer::Timer()
+ : mHandlers()
+ {
+ }
+
+ Timer::~Timer()
+ {
+ for (int i = 0; i < mHandlers.size(); ++i)
+ delete mHandlers[i];
+ }
+
+ void Timer::update(float dt)
+ {
+ // Process handler.
+ std::vector<Handler*>::iterator it = mHandlers.begin();
+ for (; it != mHandlers.end(); ++it)
+ (*it)->process(dt);
+ // Erase canceled handler.
+ for (it = mHandlers.begin(); it != mHandlers.end();)
+ {
+ if ((*it)->canceled)
+ {
+ Handler* h = *it;
+ it = mHandlers.erase(it);
+ delete h;
+ }
+ else
+ ++it;
+ }
+ }
+
+ void Timer::cancel(Handler* handler)
+ {
+ if(handler != nullptr)
+ handler->canceled = true;
+ }
+
+ void Timer::cancelAll()
+ {
+ for (auto h : mHandlers)
+ cancel(h);
+ }
+
+ Timer::Handler* Timer::every(float dt, TimerCallback callback, void* p, FinishCallback finish)
+ {
+ Handler* t = new Handler(Handler::EVERY, dt, 0, callback, p, finish);
+ mHandlers.push_back(t);
+ return t;
+ }
+
+ Timer::Handler* Timer::after(float dt, TimerCallback callback, void* p, FinishCallback finish)
+ {
+ Handler* t = new Handler(Handler::AFTER, dt, 0, callback, p, finish);
+ mHandlers.push_back(t);
+ return t;
+ }
+
+ Timer::Handler* Timer::repeat(float dt, int count, TimerCallback callback, void* p, FinishCallback finish)
+ {
+ Handler* t = new Handler(Handler::REPEAT, dt, count, callback, p, finish);
+ mHandlers.push_back(t);
+ return t;
+ }
+
+ Timer::Handler::Handler(Type t, float d, int c, TimerCallback f, void* p, FinishCallback finishcallback)
+ : type(t)
+ , duration(d)
+ , count(c)
+ , tickdown(d)
+ , countdown(c)
+ , callback(f)
+ , paramters(p)
+ , canceled(false)
+ , finishCallback(finishcallback)
+ {
+ }
+
+ Timer::Handler::~Handler()
+ {
+ if (finishCallback != nullptr)
+ finishCallback(paramters);
+ }
+
+ void Timer::Handler::process(float dt)
+ {
+ tickdown -= dt;
+ if (tickdown <= 0)
+ {
+ tickdown += duration;
+ if (!canceled && callback != nullptr)
+ callback(paramters);
+ if (type == EVERY)
+ {
+ }
+ else if (type == AFTER)
+ {
+ canceled = true;
+ }
+ else if (type == REPEAT)
+ {
+ --countdown;
+ if (countdown <= 0)
+ canceled = true;
+ }
+ }
+ }
+
+ } // namespace Time
+} // namespace JinEngine
+
+#endif // defined(jin_time) \ No newline at end of file
diff --git a/src/libjin/time/je_timer.h b/src/libjin/time/je_timer.h
new file mode 100644
index 0000000..31cd322
--- /dev/null
+++ b/src/libjin/time/je_timer.h
@@ -0,0 +1,137 @@
+#ifndef __JE_TIMER_H__
+#define __JE_TIMER_H__
+#include "../core/je_configuration.h"
+#if defined(jin_time)
+
+#include <vector>
+#include <functional>
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Time
+ {
+
+ ///
+ ///
+ ///
+ class Timer
+ {
+ public:
+
+ typedef std::function<void(void*)> TimerCallback;
+
+ typedef std::function<void(void*)> FinishCallback;
+
+ ///
+ ///
+ ///
+ class Handler
+ {
+ public:
+ friend class Timer;
+ enum Type
+ {
+ EVERY,
+ AFTER,
+ REPEAT,
+ };
+ Handler(Type type, float duration, int count = 0, TimerCallback callback = nullptr, void* paramters = nullptr, FinishCallback finishcallback = nullptr);
+ virtual ~Handler();
+ void process(float dt);
+
+ protected:
+ int count;
+ int countdown;
+ float duration;
+ float tickdown;
+ Type type;
+ TimerCallback callback;
+ FinishCallback finishCallback;
+ void* paramters;
+ bool canceled;
+ };
+
+ ///
+ ///
+ ///
+ Timer();
+
+ ///
+ ///
+ ///
+ ~Timer();
+
+ ///
+ ///
+ ///
+ void update(float dt);
+
+ ///
+ ///
+ ///
+ Handler* every(float dt, TimerCallback callback, void* paramters, FinishCallback finish);
+
+ ///
+ ///
+ ///
+ Handler* after(float dt, TimerCallback callback, void* paramters, FinishCallback finish);
+
+ ///
+ ///
+ ///
+ Handler* repeat(float dt, int count, TimerCallback callback, void* paramters, FinishCallback finish);
+
+ ///
+ ///
+ ///
+ void cancel(Handler* handler);
+
+ ///
+ ///
+ ///
+ void cancelAll();
+
+ private:
+
+ std::vector<Handler*> mHandlers;
+
+ };
+
+ ///
+ ///
+ ///
+ inline void sleep(int ms)
+ {
+ #if jin_time == jin_time_sdl
+ SDL_Delay(ms);
+ #endif
+ }
+
+ ///
+ ///
+ ///
+ inline double getSecond()
+ {
+ #if jin_time == jin_time_sdl
+ return SDL_GetTicks() / 1000.f;
+ #endif
+ }
+
+ ///
+ ///
+ ///
+ inline double getMilliSecond()
+ {
+ #if jin_time == jin_time_sdl
+ return SDL_GetTicks();
+ #endif
+ }
+
+ } // namespace Time
+} // namespace JinEngine
+
+#endif // defined(jin_time)
+
+#endif // __JE_TIMER_H__ \ No newline at end of file
diff --git a/src/libjin/utils/je_endian.h b/src/libjin/utils/je_endian.h
new file mode 100644
index 0000000..db8c8fd
--- /dev/null
+++ b/src/libjin/utils/je_endian.h
@@ -0,0 +1,23 @@
+#ifndef jin_endian_lil && jin_endian_big
+
+#define jin_endian_lil 2
+#define jin_endian_big 4
+
+#endif
+
+#ifndef jin_byte_order
+#ifdef __linux__
+#include <endian.h>
+#define jin_byte_order __BYTE_ORDER
+#else /* __linux__ */
+#if defined(__hppa__) || \
+ defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
+ (defined(__MIPS__) && defined(__MISPEB__)) || \
+ defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
+ defined(__sparc__)
+#define jin_byte_order jin_endian_big
+#else
+#define jin_byte_order jin_endian_lil
+#endif
+#endif /* __linux__ */
+#endif /* !SDL_BYTEORDER */ \ No newline at end of file
diff --git a/src/libjin/utils/je_log.cpp b/src/libjin/utils/je_log.cpp
new file mode 100644
index 0000000..e369e14
--- /dev/null
+++ b/src/libjin/utils/je_log.cpp
@@ -0,0 +1,81 @@
+#define LOGHELPER_IMPLEMENT
+#include "je_log.h"
+
+#define hasbit(flag, bit) ((flag & bit) == bit)
+
+unsigned int Loghelper::dir = Loghelper::Direction::DIR_CERR;
+unsigned int Loghelper::levels = Loghelper::Level::LV_ALL;
+std::ofstream Loghelper::fs;
+
+void Loghelper::log(Level _level, const char* _fmt, ...)
+{
+ if (!hasbit(levels, _level))
+ return;
+#define FORMAT_MSG_BUFFER_SIZE (204800)
+ const char* levelStr = nullptr;
+ switch (_level)
+ {
+ case LV_ERROR:
+ levelStr = "[Jin Error]: ";
+ break;
+ case LV_WARNING:
+ levelStr = "[Jin Warning]: ";
+ break;
+ case LV_INFO:
+ levelStr = "[Jin Info]: ";
+ break;
+ case LV_DEBUG:
+ levelStr = "[Jin Debug]: ";
+ break;
+ default:
+ levelStr = "[Jin Unknow]: ";
+ break;
+ }
+ char buffer[FORMAT_MSG_BUFFER_SIZE + 1] = { 0 };
+ strcpy(buffer, levelStr);
+ va_list args;
+ va_start(args, _fmt);
+ vsnprintf(buffer + strlen(buffer), FORMAT_MSG_BUFFER_SIZE, _fmt, args);
+ va_end(args);
+ if (hasbit(dir, DIR_CERR))
+ {
+ std::cerr << buffer << std::endl;
+ }
+ if (hasbit(dir, DIR_FILE))
+ {
+ fs << buffer << std::endl;
+ }
+#undef FORMAT_MSG_BUFFER_SIZE
+}
+
+// ض
+void Loghelper::redirect(unsigned int _dir, char* _path)
+{
+ dir = _dir;
+ if (hasbit(dir, DIR_FILE))
+ {
+ try
+ {
+ fs.open(_path, std::ios_base::app);
+ }
+ catch (std::ios_base::failure& e) {
+ dir = DIR_CERR;
+ log(Level::LV_WARNING, "ضlog· %s ʧ", _path);
+ }
+ }
+}
+
+// ɸѡȼ
+void Loghelper::restrict(unsigned int _levels)
+{
+ levels = _levels;
+}
+
+void Loghelper::close()
+{
+ if (!fs.fail())
+ fs.close();
+ fs.clear();
+}
+
+#undef hasbit
diff --git a/src/libjin/utils/je_log.h b/src/libjin/utils/je_log.h
new file mode 100644
index 0000000..f81bbae
--- /dev/null
+++ b/src/libjin/utils/je_log.h
@@ -0,0 +1,63 @@
+#ifndef __LOG_H__ELPER_H__
+#define __LOG_H__ELPER_H__
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <stdarg.h>
+
+#include "../core/je_configuration.h"
+
+class Loghelper
+{
+public:
+ // logĿ
+ enum Direction
+ {
+ DIR_CERR = 1 << 1, // ׼
+ DIR_FILE = 1 << 2, // logļ
+ };
+
+ // ȼ
+ enum Level
+ {
+ LV_NONE = 0, // none
+ LV_ERROR = 1 << 1, // error
+ LV_WARNING = 1 << 2, // warn
+ LV_INFO = 1 << 3, // info
+ LV_DEBUG = 1 << 4, // debug
+ LV_ALL = 0xffffffff
+ };
+
+ static void log(Level _level, const char* _fmt, ...);
+
+ // ض
+ static void redirect(unsigned int _dir, char* _path = nullptr);
+
+ // ɸѡȼ
+ static void restrict(unsigned int levels);
+
+ static void close();
+
+private:
+ static unsigned int dir; // Ŀ
+ static unsigned int levels; // ȼ
+ static std::ofstream fs; // ļ
+};
+
+
+typedef Loghelper::Level Loglevel;
+
+#if defined(jin_debug)
+ #define jin_log_error(f, ...) Loghelper::log(Loghelper::LV_ERROR, f, __VA_ARGS__)
+ #define jin_log_info(f, ...) Loghelper::log(Loghelper::LV_INFO, f, __VA_ARGS__)
+ #define jin_log_warning(f, ...) Loghelper::log(Loghelper::LV_WARNING, f, __VA_ARGS__)
+ #define jin_log_debug(f, ...) Loghelper::log(Loghelper::LV_DEBUG, f, __VA_ARGS__)
+#else
+ #define jin_log_error(f, ...)
+ #define jin_log_info(f, ...)
+ #define jin_log_warning(f, ...)
+ #define jin_log_debug(f, ...)
+#endif
+
+#endif // __LOG_H__ELPER_H__ \ No newline at end of file
diff --git a/src/libjin/utils/je_macros.h b/src/libjin/utils/je_macros.h
new file mode 100644
index 0000000..6e4e7a9
--- /dev/null
+++ b/src/libjin/utils/je_macros.h
@@ -0,0 +1,17 @@
+#ifndef __JE_MACROS_H__
+#define __JE_MACROS_H__
+#include <cstring>
+
+//#define implement // ʵֽӿ
+//
+//#define shared // ķ
+//
+//#define MASK // enum
+//
+//#define onlyonce // ֻһ
+//#define CALLONCE(call) static char __dummy__=(call, 1) // ֻһ
+//#define SAFECALL(func, params) if(func) func(params)
+//
+//#define zero(mem) memset(&mem, 0, sizeof(mem))
+
+#endif \ No newline at end of file
diff --git a/src/libjin/utils/je_unittest.cpp b/src/libjin/utils/je_unittest.cpp
new file mode 100644
index 0000000..294351b
--- /dev/null
+++ b/src/libjin/utils/je_unittest.cpp
@@ -0,0 +1,108 @@
+#include "je_utils.h"
+#if UNITTEST
+
+#include <iostream>
+#include <stdio.h>
+#include <fstream>
+#include "../audio/sdl/source.h"
+#include "../audio/sdl/audio.h"
+
+using namespace JinEngine::audio;
+using namespace std;
+
+int main(int argc, char* argv[])
+{
+ SDLAudio* audio = SDLAudio::get();
+ audio->init(0);
+ SDLSource* source = SDLSource::createSource("a.ogg");
+ SDLSource* source2 = SDLSource::createSource("a.wav");
+ //source->play();
+ source2->play();
+ source->setLoop(true);
+ source2->setLoop(true);
+ int i = 0;
+ while (true)
+ {
+ SDL_Delay(1000);
+ }
+ audio->quit();
+ return 0;
+}
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "SDL2/SDL.h"
+
+#include <3rdparty/cmixer/cmixer.h>
+
+static SDL_mutex* audio_mutex;
+
+static void lock_handler(cm_Event *e) {
+ if (e->type == CM_EVENT_LOCK) {
+ SDL_LockMutex(audio_mutex);
+ }
+ if (e->type == CM_EVENT_UNLOCK) {
+ SDL_UnlockMutex(audio_mutex);
+ }
+}
+
+
+static void audio_callback(void *udata, Uint8 *stream, int size) {
+ cm_process((cm_Int16*)stream, size / 2);
+}
+
+
+int main(int argc, char **argv) {
+ SDL_AudioDeviceID dev;
+ SDL_AudioSpec fmt, got;
+ cm_Source *src;
+ cm_Source* src2;
+
+
+ SDL_Init(SDL_INIT_AUDIO);
+ audio_mutex = SDL_CreateMutex();
+
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.freq = 44100;
+ fmt.format = AUDIO_S16;
+ fmt.channels = 2;
+ fmt.samples = 1024;
+ fmt.callback = audio_callback;
+
+ dev = SDL_OpenAudioDevice(NULL, 0, &fmt, &got, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
+ if (dev == 0) {
+ fprintf(stderr, "Error: failed to open audio device '%s'\n", SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ cm_init(got.freq);
+ cm_set_lock(lock_handler);
+ cm_set_master_gain(0.5);
+
+ SDL_PauseAudioDevice(dev, 0);
+
+ src = cm_new_source_from_file("a.ogg");
+ src2 = cm_new_source_from_file("loop.wav");
+ if (!src) {
+ fprintf(stderr, "Error: failed to create source '%s'\n", cm_get_error());
+ exit(EXIT_FAILURE);
+ }
+ cm_set_loop(src2, 1);
+
+ cm_play(src);
+ cm_play(src2);
+
+ printf("Press [return] to exit\n");
+ getchar();
+
+ cm_destroy_source(src);
+ SDL_CloseAudioDevice(dev);
+ SDL_Quit();
+
+ return EXIT_SUCCESS;
+}
+*/
+
+#endif \ No newline at end of file
diff --git a/src/libjin/utils/je_utils.h b/src/libjin/utils/je_utils.h
new file mode 100644
index 0000000..a77c126
--- /dev/null
+++ b/src/libjin/utils/je_utils.h
@@ -0,0 +1,9 @@
+#ifndef __JE_UTILS_H__
+#define __JE_UTILS_H__
+
+#include "je_macros.h"
+#include "je_endian.h"
+
+#define UNITTEST 0
+
+#endif \ No newline at end of file