Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Brad Svercl 2021-01-14 21:15:45 -06:00
commit 2e533e2a31
12 changed files with 190 additions and 91 deletions

View File

@ -7,14 +7,14 @@ Goal is to be simple and use as few dependencies as possible, to maintain easy b
#### building #### building
- Requires C++17 and CMake 3.12+ - Requires C++17 and CMake 3.12+
- Platform Backend - 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 - 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`. - [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/private/blah/internal/graphics_backend_d3d11.cpp) (unfinished) can be enabled in CMake with `D3D11_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/private/blah/internal/platform_backend.h) or [Graphics Backend](https://github.com/NoelFB/blah/blob/master/private/blah/internal/graphics_backend.h). - 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 #### 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. - Only floatN/mat3x2/mat4x4 uniforms are supported.
- There's no Audio API or backend implementation yet. - There's no Audio API or backend implementation yet.
- No threaded rendering so it will explode if you try that. - No threaded rendering so it will explode if you try that.

View File

@ -146,7 +146,7 @@ namespace Blah
return &data()[count]; return &data()[count];
} }
return m_buffer; return (T*)m_buffer;
} }
template<class T, size_t Capacity> template<class T, size_t Capacity>

View File

@ -76,6 +76,14 @@ namespace Blah
// ensures the string has the given capacity // ensures the string has the given capacity
void reserve(int 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 // appends the given character
Str& append(char c); Str& append(char c);

View File

@ -37,5 +37,12 @@ namespace Blah
FilePath get_path_after(const FilePath& path, const FilePath& after); FilePath get_path_after(const FilePath& path, const FilePath& after);
FilePath get_directory_name(const FilePath& path); FilePath get_directory_name(const FilePath& path);
FilePath normalize(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...));
}
} }
} }

View File

@ -31,8 +31,8 @@ namespace Blah
Vec2& operator /=(float rhs); Vec2& operator /=(float rhs);
Vec2& operator *=(float rhs); Vec2& operator *=(float rhs);
bool operator ==(const Vec2& rhs); bool operator ==(const Vec2& rhs) const;
bool operator !=(const Vec2& rhs); bool operator !=(const Vec2& rhs) const;
Vec2 normal() const; Vec2 normal() const;
Vec2 turn_right() const; Vec2 turn_right() const;

View File

@ -43,45 +43,20 @@ namespace Blah
int64_t read(void* buffer, int64_t length) { return read_into(buffer, length); } int64_t read(void* buffer, int64_t length) { return read_into(buffer, length); }
// reads a string. if length < 0, assumes null-terminated // reads a string. if length < 0, assumes null-terminated
String read_string(int length = -1) String read_string(int length = -1);
{
String result;
if (length < 0) // reads a string until a newline '\n' or null-terminator '\0' is found
{ String read_line();
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 number // reads a number
template<class T> template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
T read() T read()
{ {
return read<T>(Endian::Little); return read<T>(Endian::Little);
} }
// reads a number // 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 read(Endian endian)
{ {
T result; 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 // 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) int64_t write(const void* buffer, int64_t length);
{
return write_from(buffer, length);
}
// writes a null-terminated string, and returns the amount written // writes the contents of a string to the stream
int64_t write_cstr(const Str& string) int64_t write(const String& 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 a number // 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) int64_t write(const T& value)
{ {
return write<T>(value, Endian::Little); return write<T>(value, Endian::Little);
} }
// writes a number // 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) int64_t write(const T& value, Endian endian)
{ {
T writing = value; T writing = value;
@ -136,6 +99,3 @@ namespace Blah
virtual int64_t write_from(const void* buffer, int64_t length) = 0; virtual int64_t write_from(const void* buffer, int64_t length) = 0;
}; };
} }
#undef BLAH_SWAP_ENDIAN
#undef BLAH_BIG_ENDIAN

View File

@ -79,6 +79,50 @@ void Str::set_length(int len)
m_length = 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) Str& Str::append(char c)
{ {
reserve(m_length + 1); reserve(m_length + 1);

View File

@ -134,3 +134,8 @@ FilePath Path::normalize(const FilePath& path)
return normalized; return normalized;
} }
FilePath Path::join(const FilePath& a, const FilePath& b)
{
return normalize(FilePath(a).append("/").append(b));
}

View File

@ -1046,6 +1046,7 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
else else
offset.y = (font.ascent + font.descent + font.height() - font.height_of(text)) * 0.5f; 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++) for (int i = 0, l = text.length(); i < l; i++)
{ {
if (text[i] == '\n') if (text[i] == '\n')
@ -1061,29 +1062,34 @@ void Batch::str(const SpriteFont& font, const String& text, const Vec2& pos, Tex
else else
offset.x = -font.width_of_line(text, i + 1) * 0.5f; offset.x = -font.width_of_line(text, i + 1) * 0.5f;
last = 0;
continue; continue;
} }
// TODO: // get the character
// This doesn't parse Unicode! uint32_t next = text.utf8_at(i);
// It will assume it's a 1-byte ASCII char which is incorrect const auto& ch = font[next];
const auto& ch = font[text[i]];
// draw it, if the subtexture exists
if (ch.subtexture.texture) if (ch.subtexture.texture)
{ {
Vec2 at = offset + ch.offset; Vec2 at = offset + ch.offset;
if (i > 0 && text[i - 1] != '\n') if (i > 0 && text[i - 1] != '\n')
{ at.x += font.get_kerning(last, next);
// TODO:
// This doesn't parse Unicode!
at.x += font.get_kerning(text[i - 1], text[i]);
}
tex(ch.subtexture, at, color); tex(ch.subtexture, at, color);
} }
// move forward
offset.x += ch.advance; 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(); pop_matrix();

View File

@ -77,18 +77,33 @@ SpriteFont& SpriteFont::operator=(SpriteFont && src) noexcept
float SpriteFont::width_of(const String& text) const float SpriteFont::width_of(const String& text) const
{ {
float width = 0; float width = 0;
float lineWidth = 0; float line_width = 0;
for (auto it = text.begin(); it != text.end(); it++)
uint32_t last;
for (int i = 0; i < text.length(); i ++)
{ {
if (*it == '\n') if (text[i] == '\n')
lineWidth = 0; {
line_width = 0;
continue;
}
// TODO: this doesn't account for Unicode values! // get codepoint
uint32_t codepoint = *it; auto next = text.utf8_at(i);
lineWidth += this->operator[](codepoint).advance; // increment length
if (lineWidth > width) line_width += this->operator[](next).advance;
width = lineWidth;
// 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; return width;
@ -99,19 +114,31 @@ float SpriteFont::width_of_line(const String& text, int start) const
if (start < 0) return 0; if (start < 0) return 0;
if (start >= text.length()) return 0; if (start >= text.length()) return 0;
float lineWidth = 0; float width = 0;
for (auto it = text.begin() + start; it != text.end(); it++)
uint32_t last;
for (int i = start; i < text.length(); i ++)
{ {
if (*it == '\n') if (text[i] == '\n')
return lineWidth; return width;
// TODO: this doesn't account for Unicode values! // get codepoint
uint32_t codepoint = *it; 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 float SpriteFont::height_of(const String& text) const
@ -120,10 +147,11 @@ float SpriteFont::height_of(const String& text) const
return 0; return 0;
float height = line_height(); 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(); height += line_height();
i += text.utf8_length(i) - 1;
} }
return height - line_gap; return height - line_gap;

View File

@ -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; }
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) const { 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; }
Vec2 Vec2::normal() const Vec2 Vec2::normal() const
{ {

View File

@ -27,3 +27,44 @@ int64_t Stream::pipe(Stream& stream, int64_t length)
return result; 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());
}