aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/graphics
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2018-11-18 23:31:17 +0800
committerchai <chaifix@163.com>2018-11-18 23:31:17 +0800
commitf0f340dec7821cee103ab9267ef941a917ef4dc4 (patch)
tree2f77f6ce6bdc9f63f002c13d4c261e1d6a9c1729 /src/libjin/graphics
parentfc7b4579e49aaeecc81919e247e03f68bd5abfd4 (diff)
*目录改为小写
Diffstat (limited to 'src/libjin/graphics')
-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
56 files changed, 5641 insertions, 0 deletions
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