Re-add texturing
This commit is contained in:
parent
846f909cfe
commit
196160f37b
@ -14,5 +14,10 @@ add_library(picoled
|
||||
src/ST7789.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(picoled PUBLIC pico_stdlib hardware_i2c hardware_spi)
|
||||
target_link_libraries(picoled PUBLIC
|
||||
pico_stdlib
|
||||
hardware_i2c
|
||||
hardware_spi
|
||||
hardware_dma
|
||||
)
|
||||
target_include_directories(picoled PUBLIC include)
|
||||
|
@ -30,8 +30,12 @@ private:
|
||||
spi_inst_t* spi;
|
||||
uint8_t cs_pin;
|
||||
uint8_t dc_pin;
|
||||
uint8_t dma_tx;
|
||||
|
||||
static pixel format(const pixel& p);
|
||||
|
||||
static void dma_handler();
|
||||
static int cs_pins[12];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -54,27 +54,23 @@ public:
|
||||
|
||||
class triangle
|
||||
{
|
||||
friend class gfx2d;
|
||||
public:
|
||||
vertex2 a;
|
||||
vertex2 b;
|
||||
vertex2 c;
|
||||
|
||||
triangle fix_winding() const;
|
||||
vec3 barycentric(const vec2& p) const;
|
||||
bool contains(const vec2& p) const;
|
||||
|
||||
vec2 min() const;
|
||||
vec2 max() const;
|
||||
|
||||
private:
|
||||
fixed area_double() const;
|
||||
triangle sort_y() const;
|
||||
bool left_sided() const;
|
||||
vec3 barycentric(const vec2& p) const;
|
||||
};
|
||||
|
||||
class gfx2d
|
||||
{
|
||||
public:
|
||||
explicit gfx2d(oled& screen) :
|
||||
screen(&screen)
|
||||
screen(screen)
|
||||
{}
|
||||
|
||||
gfx2d(const gfx2d&) = delete;
|
||||
@ -83,10 +79,10 @@ public:
|
||||
gfx2d& operator=(const gfx2d&) = delete;
|
||||
gfx2d& operator=(gfx2d&&) = delete;
|
||||
|
||||
void draw_triangle(triangle tri, const buffer<pixel>& texture, bool ignore_winding = true);
|
||||
void draw_triangle(const triangle& tri, const buffer<pixel>& texture, bool ignore_winding = true);
|
||||
|
||||
private:
|
||||
oled* screen;
|
||||
oled& screen;
|
||||
};
|
||||
|
||||
}
|
||||
|
160
src/2D.cpp
160
src/2D.cpp
@ -2,86 +2,132 @@
|
||||
#include "picoled/buffer.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pico/stdio.h>
|
||||
|
||||
namespace picoled::gfx
|
||||
{
|
||||
|
||||
triangle triangle::fix_winding() const
|
||||
triangle triangle::sort_y() const
|
||||
{
|
||||
if (((b.position - a.position) ^ (c.position - a.position)) < fixed(0))
|
||||
{
|
||||
return { a, c, b };
|
||||
}
|
||||
else
|
||||
{
|
||||
return { a, b, c };
|
||||
}
|
||||
// sort from top to bottom (lowest -> highest y)
|
||||
triangle t = *this;
|
||||
|
||||
// sort a
|
||||
if (t.a.position.y > t.b.position.y) { std::swap(t.a, t.b); }
|
||||
if (t.b.position.y > t.c.position.y) { std::swap(t.b, t.c); }
|
||||
|
||||
// sort b and c
|
||||
if (t.a.position.y > t.b.position.y) { std::swap(t.a, t.b); }
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool triangle::left_sided() const
|
||||
{
|
||||
// *this must be sorted by y
|
||||
|
||||
const vertex2* leftmost = &a;
|
||||
if (b.position.x < leftmost->position.x) { leftmost = &b; }
|
||||
if (c.position.x < leftmost->position.x) { leftmost = &c; }
|
||||
|
||||
return leftmost == &a || leftmost == &c;
|
||||
}
|
||||
|
||||
vec3 triangle::barycentric(const vec2& p) const
|
||||
{
|
||||
const fixed total = area_double();
|
||||
const fixed u = triangle{ b, a, p }.area_double() / total;
|
||||
const fixed v = triangle{ a, c, p }.area_double() / total;
|
||||
return vec3{ u, v, fixed(1) - u - v };
|
||||
const vec2 ba = b.position - a.position;
|
||||
const vec2 ca = c.position - a.position;
|
||||
const vec2 pa = p - a.position;
|
||||
|
||||
const fixed denominator_inv = fixed(1) / (ba.x * ca.y - ca.x * ba.y);
|
||||
|
||||
const fixed a = (pa.x * ca.y - ca.x * pa.y) * denominator_inv;
|
||||
const fixed b = (ba.x * pa.y - pa.x * ba.y) * denominator_inv;
|
||||
|
||||
return { a, b, fixed(1) - a - b };
|
||||
}
|
||||
|
||||
bool triangle::contains(const vec2& p) const
|
||||
void gfx2d::draw_triangle(const triangle& tri, const buffer<pixel>& texture, bool ignore_winding)
|
||||
{
|
||||
const fixed abp = (b.position - a.position) ^ (p - a.position);
|
||||
const fixed bcp = (c.position - b.position) ^ (p - b.position);
|
||||
const fixed cap = (a.position - c.position) ^ (p - c.position);
|
||||
const triangle sorted_tri = tri.sort_y();
|
||||
const bool left_sided = sorted_tri.left_sided();
|
||||
|
||||
return abp >= fixed(0) && bcp >= fixed(0) && cap >= fixed(0);
|
||||
}
|
||||
// tall segment
|
||||
const fixed inv_slope_ac = (sorted_tri.c.position.x - sorted_tri.a.position.x) / (sorted_tri.c.position.y - sorted_tri.a.position.y);
|
||||
|
||||
vec2 triangle::min() const
|
||||
{
|
||||
return
|
||||
// top other side segment
|
||||
const fixed inv_slope_ab = (sorted_tri.b.position.x - sorted_tri.a.position.x) / (sorted_tri.b.position.y - sorted_tri.a.position.y);
|
||||
|
||||
const fixed upper_left_slope = left_sided ? inv_slope_ac : inv_slope_ab;
|
||||
const fixed upper_right_slope = left_sided ? inv_slope_ab : inv_slope_ac;
|
||||
|
||||
// render upper triangle
|
||||
for (fixed y = 0; y < sorted_tri.b.position.y - sorted_tri.a.position.y + fixed(0.2f); y += 1)
|
||||
{
|
||||
fixed::min(fixed::min(a.position.x, b.position.x), c.position.x),
|
||||
fixed::min(fixed::min(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
const fixed left = y * upper_left_slope + sorted_tri.a.position.x;
|
||||
const fixed right = y * upper_right_slope + sorted_tri.a.position.x;
|
||||
|
||||
vec2 triangle::max() const
|
||||
{
|
||||
return
|
||||
{
|
||||
fixed::max(fixed::max(a.position.x, b.position.x), c.position.x),
|
||||
fixed::max(fixed::max(a.position.y, b.position.y), c.position.y)
|
||||
};
|
||||
}
|
||||
const vec3 barycentric_left = tri.barycentric({ left, y + sorted_tri.a.position.y });
|
||||
const vec3 barycentric_right = tri.barycentric({ right, y + sorted_tri.a.position.y });
|
||||
|
||||
fixed 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));
|
||||
}
|
||||
const fixed u_left = barycentric_left % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed v_left = barycentric_left % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
|
||||
void gfx2d::draw_triangle(triangle tri, const buffer<pixel>& texture, bool ignore_winding)
|
||||
{
|
||||
if (ignore_winding) { tri = tri.fix_winding(); }
|
||||
const fixed u_right = barycentric_right % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed v_right = barycentric_right % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
|
||||
const vec2 min = tri.min();
|
||||
const vec2 max = tri.max();
|
||||
const fixed distance_inv = fixed(1) / (right - left);
|
||||
const fixed u_delta = (u_right - u_left) * distance_inv;
|
||||
const fixed v_delta = (v_right - v_left) * distance_inv;
|
||||
|
||||
for (fixed i = min.x; i <= max.x; i += 1)
|
||||
{
|
||||
for (fixed j = min.y; j <= max.y; j += 1)
|
||||
fixed u = u_left;
|
||||
fixed v = v_left;
|
||||
|
||||
for (fixed x = left; x < right; x += 1)
|
||||
{
|
||||
const vec2 position = { i, j };
|
||||
if (tri.contains(position))
|
||||
{
|
||||
const vec3 barycentric = tri.barycentric(position);
|
||||
const fixed u = barycentric % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed 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);
|
||||
}
|
||||
screen.write(x, y + sorted_tri.a.position.y, texture.read_uv(u, v));
|
||||
u += u_delta;
|
||||
v += v_delta;
|
||||
}
|
||||
}
|
||||
|
||||
// bottom side other segment
|
||||
const fixed inv_slope_bc = (sorted_tri.c.position.x - sorted_tri.b.position.x) / (sorted_tri.c.position.y - sorted_tri.b.position.y);
|
||||
|
||||
const fixed lower_left_slope = left_sided ? inv_slope_ac : inv_slope_bc;
|
||||
const fixed lower_right_slope = left_sided ? inv_slope_bc : inv_slope_ac;
|
||||
|
||||
// render lower triangle
|
||||
for (fixed y = 0; y < sorted_tri.c.position.y - sorted_tri.b.position.y + fixed(0.2f); y += 1)
|
||||
{
|
||||
// going from bottom to top, multiply y by -1
|
||||
const fixed left = -y * lower_left_slope + sorted_tri.c.position.x;
|
||||
const fixed right = -y * lower_right_slope + sorted_tri.c.position.x;
|
||||
|
||||
const vec3 barycentric_left = tri.barycentric({ left, -y + sorted_tri.c.position.y });
|
||||
const vec3 barycentric_right = tri.barycentric({ right, -y + sorted_tri.c.position.y });
|
||||
|
||||
const fixed u_left = barycentric_left % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed v_left = barycentric_left % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
|
||||
const fixed u_right = barycentric_right % vec3{ tri.a.texture_coordinates.x, tri.b.texture_coordinates.x, tri.c.texture_coordinates.x };
|
||||
const fixed v_right = barycentric_right % vec3{ tri.a.texture_coordinates.y, tri.b.texture_coordinates.y, tri.c.texture_coordinates.y };
|
||||
|
||||
const fixed distance_inv = fixed(1) / (right - left);
|
||||
const fixed u_delta = (u_right - u_left) * distance_inv;
|
||||
const fixed v_delta = (v_right - v_left) * distance_inv;
|
||||
|
||||
fixed u = u_left;
|
||||
fixed v = v_left;
|
||||
|
||||
for (fixed x = left; x < right; x += 1)
|
||||
{
|
||||
// going from bottom to top, multiply y by -1
|
||||
screen.write(x, -y + sorted_tri.c.position.y, texture.read_uv(u, v));
|
||||
u += u_delta;
|
||||
v += v_delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
#include <hardware/dma.h>
|
||||
|
||||
namespace picoled
|
||||
{
|
||||
|
||||
@ -9,7 +11,8 @@ st7789::st7789(size_t width, size_t height, spi_inst_t* spi, uint8_t cs_pin, uin
|
||||
oled(width, height),
|
||||
spi(spi),
|
||||
cs_pin(cs_pin),
|
||||
dc_pin(dc_pin)
|
||||
dc_pin(dc_pin),
|
||||
dma_tx(dma_claim_unused_channel(true))
|
||||
{
|
||||
command(0x01); sleep_ms(150); // reset device
|
||||
command(0x11); sleep_ms(10); // stop sleeping
|
||||
@ -46,11 +49,31 @@ st7789::st7789(size_t width, size_t height, spi_inst_t* spi, uint8_t cs_pin, uin
|
||||
command(0x21); // turn on display inversion
|
||||
command(0x13); sleep_ms(10); // set display to normal mode
|
||||
command(0x29); sleep_ms(10); // turn on display
|
||||
|
||||
dma_channel_config c = dma_channel_get_default_config(dma_tx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, spi_get_dreq(spi, true));
|
||||
dma_channel_configure(dma_tx, &c, &spi_get_hw(spi)->dr, nullptr, 3 * get_width() * get_height(), false);
|
||||
|
||||
dma_channel_set_irq0_enabled(dma_tx, true);
|
||||
|
||||
irq_add_shared_handler(DMA_IRQ_0, dma_handler, 0);
|
||||
|
||||
cs_pins[dma_tx] = cs_pin;
|
||||
}
|
||||
|
||||
void st7789::update_impl() const
|
||||
{
|
||||
command(0x2C, inactive_buffer().data_pointer(), get_width() * get_height() * 3);
|
||||
dma_channel_wait_for_finish_blocking(dma_tx); // wait for previous frame to finish
|
||||
|
||||
gpio_put(cs_pin, false); // select chip
|
||||
|
||||
gpio_put(dc_pin, false); // command mode
|
||||
constexpr uint8_t cmd = 0x2C;
|
||||
spi_write_blocking(spi, &cmd, 1);
|
||||
|
||||
gpio_put(dc_pin, true); // parameters are sent via data mode
|
||||
dma_channel_set_read_addr(dma_tx, inactive_buffer().data_pointer(), true); // upload frame
|
||||
}
|
||||
|
||||
void st7789::command(uint8_t cmd) const
|
||||
@ -78,6 +101,7 @@ void st7789::command(uint8_t cmd, const uint8_t* params, size_t param_count) con
|
||||
|
||||
pixel st7789::format(const pixel& p)
|
||||
{
|
||||
/*
|
||||
const uint16_t red_scaled = p.red() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
const uint16_t green_scaled = p.green() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
const uint16_t blue_scaled = p.blue() * 0x3F / 0xFF; // scale to 6-bit color
|
||||
@ -85,8 +109,30 @@ pixel st7789::format(const pixel& p)
|
||||
const uint8_t red = (red_scaled & 0xFF) << 2;
|
||||
const uint8_t green = (green_scaled & 0xFF) << 2;
|
||||
const uint8_t blue = (blue_scaled & 0xFF) << 2;
|
||||
*/
|
||||
|
||||
const uint8_t red = p.red() & 0xFC;
|
||||
const uint8_t green = p.green() & 0xFC;
|
||||
const uint8_t blue = p.blue() & 0xFC;
|
||||
|
||||
return { red, green, blue };
|
||||
}
|
||||
|
||||
void st7789::dma_handler()
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
if (dma_hw->ints0 & (1 << i))
|
||||
{
|
||||
// clear interrupt
|
||||
dma_hw->ints0 = 1 << i;
|
||||
|
||||
// deselect chip
|
||||
gpio_put(cs_pins[i], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int st7789::cs_pins[12] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user