Add texturing
This commit is contained in:
parent
10e9176d13
commit
dfeccd50b9
@ -7,7 +7,6 @@ add_library(picoled
|
||||
include/picoled/SSD1306.h
|
||||
|
||||
src/picoled.cpp
|
||||
src/buffer.cpp
|
||||
src/2D.cpp
|
||||
src/SSD1306.cpp
|
||||
)
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
|
||||
uint8_t grayscale() const
|
||||
{
|
||||
uint16_t unscaled = red() + green() + blue();
|
||||
uint16_t unscaled = static_cast<uint16_t>(r) + static_cast<uint16_t>(g) + static_cast<uint16_t>(b);
|
||||
return unscaled / 3;
|
||||
}
|
||||
|
||||
|
@ -6,23 +6,24 @@
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
// behavior when out of bounds
|
||||
enum class buffer_read_mode
|
||||
{
|
||||
empty, // use default constructor for T
|
||||
wrap, // use modular arithmetic to put back in bounds
|
||||
clamp // pick from the corresponding edge
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class buffer
|
||||
{
|
||||
public:
|
||||
// behavior when out of bounds
|
||||
enum class ReadMode
|
||||
{
|
||||
Default, // use default constructor for T
|
||||
Wrap, // use modular arithmetic to put back in bounds
|
||||
Clamp // pick from the corresponding edge
|
||||
};
|
||||
|
||||
buffer(size_t width, size_t height) :
|
||||
width(width),
|
||||
height(height),
|
||||
data(new T[width * height])
|
||||
{}
|
||||
{
|
||||
}
|
||||
~buffer()
|
||||
{
|
||||
delete[] data;
|
||||
@ -44,17 +45,22 @@ public:
|
||||
data[x + y * width] = pixel;
|
||||
}
|
||||
|
||||
T read(int x, int y, ReadMode mode = ReadMode::Default)
|
||||
T read_uv(float x, float y, buffer_read_mode mode = buffer_read_mode::clamp) const
|
||||
{
|
||||
return read(x * width, y * height, mode);
|
||||
}
|
||||
|
||||
T read(int x, int y, buffer_read_mode mode = buffer_read_mode::clamp) const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ReadMode::Default:
|
||||
case buffer_read_mode::empty:
|
||||
return read_default(x, y);
|
||||
|
||||
case ReadMode::Wrap:
|
||||
case buffer_read_mode::wrap:
|
||||
return read_wrap(x, y);
|
||||
|
||||
case ReadMode::Clamp:
|
||||
case buffer_read_mode::clamp:
|
||||
return read_clamp(x, y);
|
||||
}
|
||||
|
||||
@ -74,13 +80,13 @@ private:
|
||||
size_t height;
|
||||
T* data;
|
||||
|
||||
T read_default(int x, int y)
|
||||
T read_default(int x, int y) const
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) { return T(); }
|
||||
return data[x + y * width];
|
||||
}
|
||||
|
||||
T read_wrap(int x, int y)
|
||||
T read_wrap(int x, int y) const
|
||||
{
|
||||
if (x < 0)
|
||||
{
|
||||
@ -103,7 +109,7 @@ private:
|
||||
return data[x + y * width];
|
||||
}
|
||||
|
||||
T read_clamp(int x, int y)
|
||||
T read_clamp(int x, int y) const
|
||||
{
|
||||
if (x < 0) { x = 0; }
|
||||
else if (x >= width) { x = width - 1; }
|
||||
|
@ -21,18 +21,53 @@ public:
|
||||
float operator^(const vec2& other) const { return x * other.y - y * other.x; }
|
||||
};
|
||||
|
||||
class vec3
|
||||
{
|
||||
public:
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
vec3 operator+(const vec3& other) const { return { x + other.x, y + other.y, z + other.z }; }
|
||||
vec3 operator-(const vec3& other) const { return { x - other.x, y - other.y, z - other.z }; }
|
||||
vec3 operator*(const vec3& other) const { return { x * other.x, y * other.y, z * other.z }; }
|
||||
vec3 operator/(const vec3& other) const { return { x / other.x, y / other.y, z / other.z }; }
|
||||
|
||||
float operator%(const vec3& other) const { return x * other.x + y * other.y + z * other.z; }
|
||||
vec3 operator^(const vec3& other) const
|
||||
{
|
||||
return
|
||||
{
|
||||
y * other.z - z * other.y,
|
||||
z * other.x - x * other.z,
|
||||
x * other.y - y * other.x
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class vertex2
|
||||
{
|
||||
public:
|
||||
vec2 position;
|
||||
vec2 texture_coordinates;
|
||||
};
|
||||
|
||||
class triangle
|
||||
{
|
||||
public:
|
||||
vec2 a;
|
||||
vec2 b;
|
||||
vec2 c;
|
||||
vertex2 a;
|
||||
vertex2 b;
|
||||
vertex2 c;
|
||||
|
||||
triangle fix_winding() const;
|
||||
bool inside(const vec2& p) const;
|
||||
vec3 barycentric(const vec2& p) const;
|
||||
bool contains(const vec2& p) const;
|
||||
|
||||
vec2 min() const;
|
||||
vec2 max() const;
|
||||
|
||||
private:
|
||||
float area_double() const;
|
||||
};
|
||||
|
||||
class gfx2d
|
||||
@ -48,7 +83,7 @@ public:
|
||||
gfx2d& operator=(const gfx2d&) = delete;
|
||||
gfx2d& operator=(gfx2d&&) = delete;
|
||||
|
||||
void draw_triangle(triangle tri, pixel p = 0xFF, bool ignore_winding = true);
|
||||
void draw_triangle(triangle tri, const buffer<pixel>& texture, bool ignore_winding = true);
|
||||
|
||||
private:
|
||||
oled* screen;
|
||||
|
48
src/2D.cpp
48
src/2D.cpp
@ -1,13 +1,16 @@
|
||||
#include "picoled/gfx/2D.h"
|
||||
#include "picoled/buffer.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <pico/stdio.h>
|
||||
|
||||
namespace picoled::gfx
|
||||
{
|
||||
|
||||
triangle triangle::fix_winding() const
|
||||
{
|
||||
if (((b - a) ^ (c - a)) < 0)
|
||||
if (((b.position - a.position) ^ (c.position - a.position)) < 0)
|
||||
{
|
||||
return { a, c, b };
|
||||
}
|
||||
@ -17,11 +20,19 @@ triangle triangle::fix_winding() const
|
||||
}
|
||||
}
|
||||
|
||||
bool triangle::inside(const vec2& p) const
|
||||
vec3 triangle::barycentric(const vec2& p) const
|
||||
{
|
||||
const float abp = (b - a) ^ (p - a);
|
||||
const float bcp = (c - b) ^ (p - b);
|
||||
const float cap = (a - c) ^ (p - c);
|
||||
const float total = area_double();
|
||||
const float u = triangle{ b, a, p }.area_double() / total;
|
||||
const float v = triangle{ a, c, p }.area_double() / total;
|
||||
return vec3{ u, v, 1 - u - v };
|
||||
}
|
||||
|
||||
bool triangle::contains(const vec2& p) const
|
||||
{
|
||||
const float abp = (b.position - a.position) ^ (p - a.position);
|
||||
const float bcp = (c.position - b.position) ^ (p - b.position);
|
||||
const float cap = (a.position - c.position) ^ (p - c.position);
|
||||
|
||||
return abp >= 0 && bcp >= 0 && cap >= 0;
|
||||
}
|
||||
@ -30,8 +41,8 @@ vec2 triangle::min() const
|
||||
{
|
||||
return
|
||||
{
|
||||
fminf(fminf(a.x, b.x), c.x),
|
||||
fminf(fminf(a.y, b.y), c.y)
|
||||
fminf(fminf(a.position.x, b.position.x), c.position.x),
|
||||
fminf(fminf(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
|
||||
@ -39,12 +50,19 @@ vec2 triangle::max() const
|
||||
{
|
||||
return
|
||||
{
|
||||
fmaxf(fmaxf(a.x, b.x), c.x),
|
||||
fmaxf(fmaxf(a.y, b.y), c.y)
|
||||
fmaxf(fmaxf(a.position.x, b.position.x), c.position.x),
|
||||
fmaxf(fmaxf(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
|
||||
void gfx2d::draw_triangle(triangle tri, pixel p, bool ignore_winding)
|
||||
float triangle::area_double() const
|
||||
{
|
||||
// don't divide by two since it would cancel out
|
||||
// in the barycentric coordinate calculation anyway
|
||||
return std::abs((b.position - a.position) ^ (c.position - a.position));
|
||||
}
|
||||
|
||||
void gfx2d::draw_triangle(triangle tri, const buffer<pixel>& texture, bool ignore_winding)
|
||||
{
|
||||
if (ignore_winding) { tri = tri.fix_winding(); }
|
||||
|
||||
@ -55,7 +73,15 @@ void gfx2d::draw_triangle(triangle tri, pixel p, bool ignore_winding)
|
||||
{
|
||||
for (float j = min.y; j <= max.y; j += 0.75f)
|
||||
{
|
||||
if (tri.inside({ i, j })) { screen->write(i, j, p); }
|
||||
const vec2 position = { i, j };
|
||||
if (tri.contains(position))
|
||||
{
|
||||
const vec3 barycentric = tri.barycentric(position);
|
||||
const float u = barycentric % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const float v = barycentric % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
const pixel read = texture.read_uv(u, v, buffer_read_mode::clamp);
|
||||
screen->write(i, j, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ ssd1306::~ssd1306()
|
||||
void ssd1306::update_impl()
|
||||
{
|
||||
// clear display to zero
|
||||
memset(&buffer[1], 0, get_width() * get_height() * sizeof(*buffer));
|
||||
memset(&buffer[1], 0, get_width() * get_height() * sizeof(*buffer) / 8);
|
||||
|
||||
for (int x = 0; x < get_width(); x++)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user