mirror of
				https://github.com/NoelFB/blah.git
				synced 2025-11-04 01:41:34 +08:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@ -7,14 +7,14 @@ Goal is to be simple and use as few dependencies as possible, to maintain easy b
 | 
			
		||||
#### building
 | 
			
		||||
 - Requires C++17 and CMake 3.12+
 | 
			
		||||
 - Platform Backend
 | 
			
		||||
	- [SDL2](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `SDL2_ENABLED`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES`
 | 
			
		||||
	- [SDL2](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend_sdl2.cpp) can be enabled in CMake with `SDL2_ENABLED`, and setting `SDL2_INCLUDE_DIRS` and `SDL2_LIBRARIES`
 | 
			
		||||
 - Graphics Backend
 | 
			
		||||
	- [OpenGL](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend_gl.cpp) can be enabled in CMake with `OPENGL_ENABLED`.
 | 
			
		||||
	- [D3D11](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend_d3d11.cpp) (unfinished) can be enabled in CMake with `D3D11_ENABLED`.
 | 
			
		||||
 - Other backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend.h).
 | 
			
		||||
 | 
			
		||||
	- [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_gl.cpp) can be enabled in CMake with `OPENGL_ENABLED`.
 | 
			
		||||
	- [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend_d3d11.cpp) (unfinished) can be enabled in CMake with `D3D11_ENABLED`.
 | 
			
		||||
 - Other backends can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/src/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_backend.h).
 | 
			
		||||
 
 | 
			
		||||
#### notes
 | 
			
		||||
 - There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/public/blah/drawing/batch.h) has hard-coded GLSL/HLSL. This will need to change.
 | 
			
		||||
 - There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/include/blah/drawing/batch.h) has hard-coded GLSL/HLSL. This will need to change.
 | 
			
		||||
 - Only floatN/mat3x2/mat4x4 uniforms are supported.
 | 
			
		||||
 - There's no Audio API or backend implementation yet.
 | 
			
		||||
 - No threaded rendering so it will explode if you try that.
 | 
			
		||||
 | 
			
		||||
@ -146,7 +146,7 @@ namespace Blah
 | 
			
		||||
			return &data()[count];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return m_buffer;
 | 
			
		||||
		return (T*)m_buffer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template<class T, size_t Capacity>
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,14 @@ namespace Blah
 | 
			
		||||
 | 
			
		||||
		// ensures the string has the given capacity
 | 
			
		||||
		void reserve(int capacity);
 | 
			
		||||
		
 | 
			
		||||
		// Returns the unicode value at the given index.
 | 
			
		||||
		// Assumes the index is a valid utf8 starting point.
 | 
			
		||||
		uint32_t utf8_at(int index) const;
 | 
			
		||||
 | 
			
		||||
		// Returns the byte-length of the utf8 character.
 | 
			
		||||
		// Assumes the index is a valid utf8 starting point.
 | 
			
		||||
		int utf8_length(int index) const;
 | 
			
		||||
 | 
			
		||||
		// appends the given character
 | 
			
		||||
		Str& append(char c);
 | 
			
		||||
 | 
			
		||||
@ -37,5 +37,12 @@ namespace Blah
 | 
			
		||||
		FilePath get_path_after(const FilePath& path, const FilePath& after);
 | 
			
		||||
		FilePath get_directory_name(const FilePath& path);
 | 
			
		||||
		FilePath normalize(const FilePath& path);
 | 
			
		||||
		FilePath join(const FilePath& a, const FilePath& b);
 | 
			
		||||
 | 
			
		||||
		template<typename ... Args>
 | 
			
		||||
		FilePath join(const FilePath& a, const FilePath& b, const Args&... args)
 | 
			
		||||
		{
 | 
			
		||||
			return join(a, join(b, args...));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -31,8 +31,8 @@ namespace Blah
 | 
			
		||||
		Vec2& operator /=(float rhs);
 | 
			
		||||
		Vec2& operator *=(float rhs);
 | 
			
		||||
		
 | 
			
		||||
		bool operator ==(const Vec2& rhs);
 | 
			
		||||
		bool operator !=(const Vec2& rhs);
 | 
			
		||||
		bool operator ==(const Vec2& rhs) const;
 | 
			
		||||
		bool operator !=(const Vec2& rhs) const;
 | 
			
		||||
		
 | 
			
		||||
		Vec2 normal() const;
 | 
			
		||||
		Vec2 turn_right() const;
 | 
			
		||||
 | 
			
		||||
@ -43,45 +43,20 @@ namespace Blah
 | 
			
		||||
		int64_t read(void* buffer, int64_t length) { return read_into(buffer, length); }
 | 
			
		||||
 | 
			
		||||
		// reads a string. if length < 0, assumes null-terminated
 | 
			
		||||
		String read_string(int length = -1)
 | 
			
		||||
		{
 | 
			
		||||
			String result;
 | 
			
		||||
		String read_string(int length = -1);
 | 
			
		||||
 | 
			
		||||
			if (length < 0)
 | 
			
		||||
			{
 | 
			
		||||
				char next;
 | 
			
		||||
				while (read(&next, 1) && next != '\0')
 | 
			
		||||
					result.append(next);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				result.set_length(length);
 | 
			
		||||
				read_into(result.cstr(), length);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		String read_line()
 | 
			
		||||
		{
 | 
			
		||||
			String result;
 | 
			
		||||
 | 
			
		||||
			char next;
 | 
			
		||||
			while (read(&next, 1) && next != '\n' && next != '\0')
 | 
			
		||||
				result.append(next);
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
		// reads a string until a newline '\n' or null-terminator '\0' is found
 | 
			
		||||
		String read_line();
 | 
			
		||||
 | 
			
		||||
		// reads a number
 | 
			
		||||
		template<class T>
 | 
			
		||||
		template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
 | 
			
		||||
		T read()
 | 
			
		||||
		{
 | 
			
		||||
			return read<T>(Endian::Little);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// reads a number
 | 
			
		||||
		template<class T>
 | 
			
		||||
		template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
 | 
			
		||||
		T read(Endian endian)
 | 
			
		||||
		{
 | 
			
		||||
			T result;
 | 
			
		||||
@ -92,32 +67,20 @@ namespace Blah
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// writes the amount of bytes to the stream from the given buffer, and returns the amount written
 | 
			
		||||
		int64_t write(const void* buffer, int64_t length) 
 | 
			
		||||
		{ 
 | 
			
		||||
			return write_from(buffer, length);
 | 
			
		||||
		}
 | 
			
		||||
		int64_t write(const void* buffer, int64_t length);
 | 
			
		||||
 | 
			
		||||
		// writes a null-terminated string, and returns the amount written
 | 
			
		||||
		int64_t write_cstr(const Str& string)
 | 
			
		||||
		{
 | 
			
		||||
			return write(string.cstr(), string.length() + 1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// writes a null-terminated string, and returns the amount written
 | 
			
		||||
		int64_t write_cstr(const char* cstr)
 | 
			
		||||
		{
 | 
			
		||||
			return write(cstr, strlen(cstr) + 1);
 | 
			
		||||
		}
 | 
			
		||||
		// writes the contents of a string to the stream
 | 
			
		||||
		int64_t write(const String& string);
 | 
			
		||||
 | 
			
		||||
		// writes a number
 | 
			
		||||
		template<class T>
 | 
			
		||||
		template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
 | 
			
		||||
		int64_t write(const T& value)
 | 
			
		||||
		{
 | 
			
		||||
			return write<T>(value, Endian::Little);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// writes a number
 | 
			
		||||
		template<class T>
 | 
			
		||||
		template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
 | 
			
		||||
		int64_t write(const T& value, Endian endian)
 | 
			
		||||
		{
 | 
			
		||||
			T writing = value;
 | 
			
		||||
@ -135,7 +98,4 @@ namespace Blah
 | 
			
		||||
		// writes from the stream from the given buffer, and returns the number of bytes written
 | 
			
		||||
		virtual int64_t write_from(const void* buffer, int64_t length) = 0;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef BLAH_SWAP_ENDIAN
 | 
			
		||||
#undef BLAH_BIG_ENDIAN
 | 
			
		||||
}
 | 
			
		||||
@ -79,6 +79,50 @@ void Str::set_length(int len)
 | 
			
		||||
	m_length = len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t Str::utf8_at(int index) const
 | 
			
		||||
{
 | 
			
		||||
	uint32_t charcode = 0;
 | 
			
		||||
	
 | 
			
		||||
	int t = (unsigned char)(this->operator[](index++));
 | 
			
		||||
	if (t < 128)
 | 
			
		||||
		return t;
 | 
			
		||||
 | 
			
		||||
	int high_bit_mask = (1 << 6) - 1;
 | 
			
		||||
	int high_bit_shift = 0;
 | 
			
		||||
	int total_bits = 0;
 | 
			
		||||
	int other_bits = 6;
 | 
			
		||||
 | 
			
		||||
	while ((t & 0xC0) == 0xC0)
 | 
			
		||||
	{
 | 
			
		||||
		t <<= 1;
 | 
			
		||||
		t &= 0xff;
 | 
			
		||||
		total_bits += 6;
 | 
			
		||||
		high_bit_mask >>= 1;
 | 
			
		||||
		high_bit_shift++;
 | 
			
		||||
		charcode <<= other_bits;
 | 
			
		||||
		charcode |= ((unsigned char)(this->operator[](index++))) & ((1 << other_bits) - 1);
 | 
			
		||||
	}
 | 
			
		||||
	charcode |= ((t >> high_bit_shift) & high_bit_mask) << total_bits;
 | 
			
		||||
 | 
			
		||||
	return charcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Str::utf8_length(int index) const
 | 
			
		||||
{
 | 
			
		||||
	auto c = this->operator[](index);
 | 
			
		||||
	if ((c & 0xFE) == 0xFC)
 | 
			
		||||
		return 6;
 | 
			
		||||
	if ((c & 0xFC) == 0xF8)
 | 
			
		||||
		return 5;
 | 
			
		||||
	if ((c & 0xF8) == 0xF0)
 | 
			
		||||
		return 4;
 | 
			
		||||
	else if ((c & 0xF0) == 0xE0)
 | 
			
		||||
		return 3;
 | 
			
		||||
	else if ((c & 0xE0) == 0xC0)
 | 
			
		||||
		return 2;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Str& Str::append(char c)
 | 
			
		||||
{
 | 
			
		||||
	reserve(m_length + 1);
 | 
			
		||||
 | 
			
		||||
@ -133,4 +133,9 @@ FilePath Path::normalize(const FilePath& path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return normalized;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FilePath Path::join(const FilePath& a, const FilePath& b)
 | 
			
		||||
{
 | 
			
		||||
	return normalize(FilePath(a).append("/").append(b));
 | 
			
		||||
}
 | 
			
		||||
@ -1046,6 +1046,7 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
 | 
			
		||||
	else
 | 
			
		||||
		offset.y = (font.ascent + font.descent + font.height() - font.height_of(text)) * 0.5f;
 | 
			
		||||
 | 
			
		||||
	uint32_t last = 0;
 | 
			
		||||
	for (int i = 0, l = text.length(); i < l; i++)
 | 
			
		||||
	{
 | 
			
		||||
		if (text[i] == '\n')
 | 
			
		||||
@ -1061,29 +1062,34 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
 | 
			
		||||
			else
 | 
			
		||||
				offset.x = -font.width_of_line(text, i + 1) * 0.5f;
 | 
			
		||||
 | 
			
		||||
			last = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO:
 | 
			
		||||
		// This doesn't parse Unicode! 
 | 
			
		||||
		// It will assume it's a 1-byte ASCII char which is incorrect
 | 
			
		||||
		const auto& ch = font[text[i]];
 | 
			
		||||
		// get the character
 | 
			
		||||
		uint32_t next = text.utf8_at(i);
 | 
			
		||||
		const auto& ch = font[next];
 | 
			
		||||
 | 
			
		||||
		// draw it, if the subtexture exists
 | 
			
		||||
		if (ch.subtexture.texture)
 | 
			
		||||
		{
 | 
			
		||||
			Vec2 at = offset + ch.offset;
 | 
			
		||||
 | 
			
		||||
			if (i > 0 && text[i - 1] != '\n')
 | 
			
		||||
			{
 | 
			
		||||
				// TODO:
 | 
			
		||||
				// This doesn't parse Unicode! 
 | 
			
		||||
				at.x += font.get_kerning(text[i - 1], text[i]);
 | 
			
		||||
			}
 | 
			
		||||
				at.x += font.get_kerning(last, next);
 | 
			
		||||
 | 
			
		||||
			tex(ch.subtexture, at, color);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// move forward
 | 
			
		||||
		offset.x += ch.advance;
 | 
			
		||||
 | 
			
		||||
		// increment past current character
 | 
			
		||||
		// (minus 1 since the for loop iterator increments as well)
 | 
			
		||||
		i += text.utf8_length(i) - 1;
 | 
			
		||||
		
 | 
			
		||||
		// keep last codepoint for next char for kerning
 | 
			
		||||
		last = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pop_matrix();
 | 
			
		||||
 | 
			
		||||
@ -77,18 +77,33 @@ SpriteFont& SpriteFont::operator=(SpriteFont && src) noexcept
 | 
			
		||||
float SpriteFont::width_of(const String& text) const
 | 
			
		||||
{
 | 
			
		||||
	float width = 0;
 | 
			
		||||
	float lineWidth = 0;
 | 
			
		||||
	for (auto it = text.begin(); it != text.end(); it++)
 | 
			
		||||
	float line_width = 0;
 | 
			
		||||
 | 
			
		||||
	uint32_t last;
 | 
			
		||||
	for (int i = 0; i < text.length(); i ++)
 | 
			
		||||
	{
 | 
			
		||||
		if (*it == '\n')
 | 
			
		||||
			lineWidth = 0;
 | 
			
		||||
		if (text[i] == '\n')
 | 
			
		||||
		{
 | 
			
		||||
			line_width = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: this doesn't account for Unicode values!
 | 
			
		||||
		uint32_t codepoint = *it;
 | 
			
		||||
		// get codepoint
 | 
			
		||||
		auto next = text.utf8_at(i);
 | 
			
		||||
 | 
			
		||||
		lineWidth += this->operator[](codepoint).advance;
 | 
			
		||||
		if (lineWidth > width)
 | 
			
		||||
			width = lineWidth;
 | 
			
		||||
		// increment length
 | 
			
		||||
		line_width += this->operator[](next).advance;
 | 
			
		||||
		
 | 
			
		||||
		// add kerning
 | 
			
		||||
		if (i > 0)
 | 
			
		||||
			line_width += get_kerning(last, next);
 | 
			
		||||
 | 
			
		||||
		if (line_width > width)
 | 
			
		||||
			width = line_width;
 | 
			
		||||
 | 
			
		||||
		// move to thext utf8 character
 | 
			
		||||
		i += text.utf8_length(i) - 1;
 | 
			
		||||
		last = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return width;
 | 
			
		||||
@ -99,19 +114,31 @@ float SpriteFont::width_of_line(const String& text, int start) const
 | 
			
		||||
	if (start < 0) return 0;
 | 
			
		||||
	if (start >= text.length()) return 0;
 | 
			
		||||
 | 
			
		||||
	float lineWidth = 0;
 | 
			
		||||
	for (auto it = text.begin() + start; it != text.end(); it++)
 | 
			
		||||
	float width = 0;
 | 
			
		||||
 | 
			
		||||
	uint32_t last;
 | 
			
		||||
	for (int i = start; i < text.length(); i ++)
 | 
			
		||||
	{
 | 
			
		||||
		if (*it == '\n')
 | 
			
		||||
			return lineWidth;
 | 
			
		||||
		if (text[i] == '\n')
 | 
			
		||||
			return width;
 | 
			
		||||
 | 
			
		||||
		// TODO: this doesn't account for Unicode values!
 | 
			
		||||
		uint32_t codepoint = *it;
 | 
			
		||||
		// get codepoint
 | 
			
		||||
		auto next = text.utf8_at(i);
 | 
			
		||||
 | 
			
		||||
		lineWidth += this->operator[](codepoint).advance;
 | 
			
		||||
		// increment length
 | 
			
		||||
		width += this->operator[](next).advance;
 | 
			
		||||
		
 | 
			
		||||
		// add kerning
 | 
			
		||||
		if (i > 0)
 | 
			
		||||
			width += get_kerning(last, next);
 | 
			
		||||
 | 
			
		||||
		// move to thext utf8 character
 | 
			
		||||
		i += text.utf8_length(i) - 1;
 | 
			
		||||
 | 
			
		||||
		last = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return lineWidth;
 | 
			
		||||
	return width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float SpriteFont::height_of(const String& text) const
 | 
			
		||||
@ -120,10 +147,11 @@ float SpriteFont::height_of(const String& text) const
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	float height = line_height();
 | 
			
		||||
	for (auto it = text.begin(); it != text.end(); it++)
 | 
			
		||||
	for (int i = 0; i < text.length(); i ++)
 | 
			
		||||
	{
 | 
			
		||||
		if (*it == '\n')
 | 
			
		||||
		if (text[i] == '\n')
 | 
			
		||||
			height += line_height();
 | 
			
		||||
		i += text.utf8_length(i) - 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return height - line_gap;
 | 
			
		||||
 | 
			
		||||
@ -25,8 +25,8 @@ Vec2& Vec2::operator *=(const Vec2& rhs) { x *= rhs.x; y *= rhs.y; return *this;
 | 
			
		||||
Vec2& Vec2::operator/=(float rhs) { x /= rhs; y /= rhs; return *this; }
 | 
			
		||||
Vec2& Vec2::operator*=(float rhs) { x *= rhs; y *= rhs; return *this; }
 | 
			
		||||
 | 
			
		||||
bool Vec2::operator ==(const Vec2& rhs) { return x == rhs.x && y == rhs.y; }
 | 
			
		||||
bool Vec2::operator !=(const Vec2& rhs) { return x != rhs.x || y != rhs.y; }
 | 
			
		||||
bool Vec2::operator ==(const Vec2& rhs) const { return x == rhs.x && y == rhs.y; }
 | 
			
		||||
bool Vec2::operator !=(const Vec2& rhs) const { return x != rhs.x || y != rhs.y; }
 | 
			
		||||
 | 
			
		||||
Vec2 Vec2::normal() const
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -26,4 +26,45 @@ int64_t Stream::pipe(Stream& stream, int64_t length)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// reads a string. if length < 0, assumes null-terminated
 | 
			
		||||
String Stream::read_string(int length)
 | 
			
		||||
{
 | 
			
		||||
	String result;
 | 
			
		||||
 | 
			
		||||
	if (length < 0)
 | 
			
		||||
	{
 | 
			
		||||
		char next;
 | 
			
		||||
		while (read(&next, 1) && next != '\0')
 | 
			
		||||
			result.append(next);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		result.set_length(length);
 | 
			
		||||
		read_into(result.cstr(), length);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String Stream::read_line()
 | 
			
		||||
{
 | 
			
		||||
	String result;
 | 
			
		||||
 | 
			
		||||
	char next;
 | 
			
		||||
	while (read(&next, 1) && next != '\n' && next != '\0')
 | 
			
		||||
		result.append(next);
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t Stream::write(const void* buffer, int64_t length) 
 | 
			
		||||
{ 
 | 
			
		||||
	return write_from(buffer, length);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t Stream::write(const String& string)
 | 
			
		||||
{
 | 
			
		||||
	return write_from(string.begin(), string.length());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user