Implementing textured quads.

This commit is contained in:
Adam Becker 2016-11-06 09:11:04 -07:00
parent 9eb540f12f
commit d61e89cf71
8 changed files with 359 additions and 141 deletions

View file

@ -21,7 +21,7 @@ set(SOURCE_FILES
src/bus.hpp
src/psxact.cpp
src/utility.cpp
src/utility.hpp)
src/utility.hpp src/gpu/gpu_draw_gouraud.cpp src/gpu/gpu_draw_texture.cpp)
add_executable(psxact ${SOURCE_FILES})

View file

@ -15,8 +15,8 @@ available, use one of them if you wish.
## Current Status
Currently, the emulator hangs after drawing the Sony™ logo while trying
to communicate with the CD-ROM drive. Textured primitives are not implemented,
nor are they attempted.
to communicate with the CD-ROM drive. Textured primitives are partially
implemented.
![Current status](images/current.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,7 +1,5 @@
#include "gpu_core.hpp"
#include "../utility.hpp"
#include <stdexcept>
#include <stdio.h>
static gpu::state_t state;
@ -30,6 +28,14 @@ static int gp0_command_size[256] = {
gpu::vram_t gpu::vram;
uint16_t gpu::read_vram(int x, int y) {
return vram.h[(y * 1024) + x];
}
void gpu::write_vram(int x, int y, uint16_t color) {
vram.h[(y * 1024) + x] = color;
}
// --=============--
// I/O Functions
// --=============--
@ -57,13 +63,29 @@ static inline uint32_t read_stat() {
(1 << 28);
}
static gpu::point_t gp0_to_point(const uint32_t &vertex, const uint32_t &color) {
gpu::point_t p;
p.x = int(vertex & 0xffff);
p.y = int(vertex >> 16);
p.r = (color >> 0) & 0xff;
p.g = (color >> 8) & 0xff;
p.b = (color >> 16) & 0xff;
static gpu::gouraud::pixel_t gp0_to_gouraud_pixel(uint32_t vertex, uint32_t color) {
gpu::gouraud::pixel_t p;
p.point.x = int(vertex & 0xffff);
p.point.y = int(vertex >> 16);
p.color.r = (color >> 0) & 0xff;
p.color.g = (color >> 8) & 0xff;
p.color.b = (color >> 16) & 0xff;
return p;
}
static gpu::texture::pixel_t gp0_to_texture_pixel(uint32_t vertex, uint32_t color, uint32_t texcoord) {
gpu::texture::pixel_t p;
p.point.x = int(vertex & 0xffff);
p.point.y = int(vertex >> 16);
p.color.r = (color >> 0) & 0xff;
p.color.g = (color >> 8) & 0xff;
p.color.b = (color >> 16) & 0xff;
p.u = (texcoord >> 0) & 0xff;
p.v = (texcoord >> 8) & 0xff;
return p;
}
@ -119,12 +141,12 @@ static inline void write_gp0(uint32_t data) {
auto vertex3 = get_gp0_data();
auto vertex4 = get_gp0_data();
auto v0 = gp0_to_point(vertex1, color);
auto v1 = gp0_to_point(vertex2, color);
auto v2 = gp0_to_point(vertex3, color);
auto v3 = gp0_to_point(vertex4, color);
auto v0 = gp0_to_gouraud_pixel(vertex1, color);
auto v1 = gp0_to_gouraud_pixel(vertex2, color);
auto v2 = gp0_to_gouraud_pixel(vertex3, color);
auto v3 = gp0_to_gouraud_pixel(vertex4, color);
gpu::draw_poly4(v0, v1, v2, v3);
gpu::gouraud::draw_poly4({v0, v1, v2, v3});
break;
}
@ -139,12 +161,18 @@ static inline void write_gp0(uint32_t data) {
auto vertex4 = get_gp0_data();
auto tcoord4 = get_gp0_data();
auto v0 = gp0_to_point(vertex1, color);
auto v1 = gp0_to_point(vertex2, color);
auto v2 = gp0_to_point(vertex3, color);
auto v3 = gp0_to_point(vertex4, color);
gpu::texture::poly4_t p;
gpu::draw_poly4(v0, v1, v2, v3);
p.v0 = gp0_to_texture_pixel(vertex1, color, tcoord1);
p.v1 = gp0_to_texture_pixel(vertex2, color, tcoord2);
p.v2 = gp0_to_texture_pixel(vertex3, color, tcoord3);
p.v3 = gp0_to_texture_pixel(vertex4, color, tcoord4);
p.clut_x = ((tcoord1 >> 16) & 0x03f) * 16;
p.clut_y = ((tcoord1 >> 22) & 0x1ff) * 1;
p.base_u = ((tcoord2 >> 16) & 0x00f) * 64;
p.base_v = ((tcoord2 >> 20) & 0x001) * 256;
gpu::texture::draw_poly4(p);
break;
}
@ -156,11 +184,11 @@ static inline void write_gp0(uint32_t data) {
auto color3 = get_gp0_data();
auto vertex3 = get_gp0_data();
auto v0 = gp0_to_point(vertex1, color1);
auto v1 = gp0_to_point(vertex2, color2);
auto v2 = gp0_to_point(vertex3, color3);
auto v0 = gp0_to_gouraud_pixel(vertex1, color1);
auto v1 = gp0_to_gouraud_pixel(vertex2, color2);
auto v2 = gp0_to_gouraud_pixel(vertex3, color3);
gpu::draw_poly3(v0, v1, v2);
gpu::gouraud::draw_poly3({v0, v1, v2});
break;
}
@ -174,12 +202,12 @@ static inline void write_gp0(uint32_t data) {
auto color4 = get_gp0_data();
auto vertex4 = get_gp0_data();
auto v0 = gp0_to_point(vertex1, color1);
auto v1 = gp0_to_point(vertex2, color2);
auto v2 = gp0_to_point(vertex3, color3);
auto v3 = gp0_to_point(vertex4, color4);
auto v0 = gp0_to_gouraud_pixel(vertex1, color1);
auto v1 = gp0_to_gouraud_pixel(vertex2, color2);
auto v2 = gp0_to_gouraud_pixel(vertex3, color3);
auto v3 = gp0_to_gouraud_pixel(vertex4, color4);
gpu::draw_poly4(v0, v1, v2, v3);
gpu::gouraud::draw_poly4({v0, v1, v2, v3});
break;
}

View file

@ -57,28 +57,75 @@ namespace gpu {
void write(int size, uint32_t address, uint32_t data);
struct point_t {
int x;
int y;
uint16_t read_vram(int x, int y);
void write_vram(int x, int y, uint16_t color);
struct color_t {
int r;
int g;
int b;
};
struct triangle_t {
point_t v[3];
struct point_t {
int x;
int y;
};
struct quad_t {
point_t v0;
point_t v1;
point_t v2;
point_t v3;
};
void draw_point(int x, int y, int r, int g, int b);
void draw_poly3(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2);
namespace gouraud {
struct pixel_t {
point_t point;
color_t color;
};
void draw_poly4(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2, const gpu::point_t &v3);
struct poly3_t {
pixel_t v[3];
};
struct poly4_t {
pixel_t v[4];
};
void draw_poly3(const gpu::gouraud::poly3_t &p);
void draw_poly4(const gpu::gouraud::poly4_t &p);
}
namespace texture {
struct pixel_t {
point_t point;
color_t color;
int u;
int v;
};
struct poly3_t {
pixel_t v0;
pixel_t v1;
pixel_t v2;
int clut_x;
int clut_y;
int base_u;
int base_v;
};
struct poly4_t {
pixel_t v0;
pixel_t v1;
pixel_t v2;
pixel_t v3;
int clut_x;
int clut_y;
int base_u;
int base_v;
};
void draw_poly3(const poly3_t &p);
void draw_poly4(const poly4_t &p);
}
}
#endif //PSXACT_GPU_CORE_HPP

View file

@ -14,109 +14,19 @@ static int dither_lut[4][4] = {
{ 3, -1, 2, -2 }
};
void draw_point(const int &x, const int &y, const int &r, const int &g, const int &b) {
void gpu::draw_point(int x, int y, int r, int g, int b) {
auto address = (y << 10) + x;
auto dither = dither_lut[y & 3][x & 3];
auto r_ = clip<0, 255>(r + dither);
auto g_ = clip<0, 255>(g + dither);
auto b_ = clip<0, 255>(b + dither);
r = clip<0, 255>(r + dither);
g = clip<0, 255>(g + dither);
b = clip<0, 255>(b + dither);
auto color =
(((r_ >> 3) & 0x1f) << 0) |
(((g_ >> 3) & 0x1f) << 5) |
(((b_ >> 3) & 0x1f) << 10);
(((r >> 3) & 0x1f) << 0) |
(((g >> 3) & 0x1f) << 5) |
(((b >> 3) & 0x1f) << 10);
gpu::vram.h[address] = uint16_t(color);
}
static int min3(int a, int b, int c) {
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
return c;
}
static int max3(int a, int b, int c) {
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
static int edge(const gpu::point_t& a, const gpu::point_t& b, const gpu::point_t& c) {
return ((b.x - a.x) * (c.y - a.y)) - ((b.y - a.y) * (c.x - a.x));
}
static bool is_top_left(const gpu::point_t &a, const gpu::point_t &b) {
return (b.y == a.y && b.x > a.x) || b.y < a.y;
}
static void draw_poly3_(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2) {
int min_x = min3(v0.x, v1.x, v2.x);
int min_y = min3(v0.y, v1.y, v2.y);
int max_x = max3(v0.x, v1.x, v2.x);
int max_y = max3(v0.y, v1.y, v2.y);
bool is_top_left_12 = is_top_left(v1, v2);
bool is_top_left_20 = is_top_left(v2, v0);
bool is_top_left_01 = is_top_left(v0, v1);
int A01 = v0.y - v1.y, B01 = v1.x - v0.x;
int A12 = v1.y - v2.y, B12 = v2.x - v1.x;
int A20 = v2.y - v0.y, B20 = v0.x - v2.x;
gpu::point_t p = { min_x, min_y };
int w0_row = edge(v1, v2, p);
int w1_row = edge(v2, v0, p);
int w2_row = edge(v0, v1, p);
for (p.y = min_y; p.y <= max_y; p.y++) {
int w0 = w0_row;
int w1 = w1_row;
int w2 = w2_row;
for (p.x = min_x; p.x <= max_x; p.x++) {
bool draw =
(w0 > 0 || (w0 == 0 && is_top_left_12)) &&
(w1 > 0 || (w1 == 0 && is_top_left_20)) &&
(w2 > 0 || (w2 == 0 && is_top_left_01));
if (draw) {
auto r = ((v0.r * w0) + (v1.r * w1) + (v2.r * w2)) / (w0 + w1 + w2);
auto g = ((v0.g * w0) + (v1.g * w1) + (v2.g * w2)) / (w0 + w1 + w2);
auto b = ((v0.b * w0) + (v1.b * w1) + (v2.b * w2)) / (w0 + w1 + w2);
draw_point(p.x, p.y, r, g, b);
}
w0 += A12;
w1 += A20;
w2 += A01;
}
w0_row += B12;
w1_row += B20;
w2_row += B01;
}
}
static inline int double_area(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2) {
auto e0 = (v1.x - v0.x) * (v1.y + v0.y);
auto e1 = (v2.x - v1.x) * (v2.y + v1.y);
auto e2 = (v0.x - v2.x) * (v0.y + v2.y);
return e0 + e1 + e2;
}
void gpu::draw_poly3(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2) {
if (double_area(v0, v1, v2) < 0) {
draw_poly3_(v0, v1, v2);
} else {
draw_poly3_(v0, v2, v1);
}
}
void gpu::draw_poly4(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2, const gpu::point_t &v3) {
gpu::draw_poly3(v0, v1, v2);
gpu::draw_poly3(v1, v2, v3);
}

View file

@ -0,0 +1,109 @@
#include "gpu_core.hpp"
void fill_gouraud(const gpu::gouraud::pixel_t &v0, const int &w0,
const gpu::gouraud::pixel_t &v1, const int &w1,
const gpu::gouraud::pixel_t &v2, const int &w2, int x, int y) {
int area = w0 + w1 + w2;
int r = ((v0.color.r * w0) + (v1.color.r * w1) + (v2.color.r * w2)) / area;
int g = ((v0.color.g * w0) + (v1.color.g * w1) + (v2.color.g * w2)) / area;
int b = ((v0.color.b * w0) + (v1.color.b * w1) + (v2.color.b * w2)) / area;
gpu::draw_point(x, y, r, g, b);
}
static int min3(int a, int b, int c) {
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
return c;
}
static int max3(int a, int b, int c) {
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
static int edge(const gpu::point_t& a, const gpu::point_t& b, const gpu::point_t& c) {
return ((b.x - a.x) * (c.y - a.y)) - ((b.y - a.y) * (c.x - a.x));
}
static bool is_top_left(const gpu::point_t &a, const gpu::point_t &b) {
return (b.y == a.y && b.x > a.x) || b.y < a.y;
}
static void fill_poly3_gouraud(const gpu::gouraud::pixel_t &v0, const gpu::gouraud::pixel_t &v1, const gpu::gouraud::pixel_t &v2) {
int min_x = min3(v0.point.x, v1.point.x, v2.point.x);
int min_y = min3(v0.point.y, v1.point.y, v2.point.y);
int max_x = max3(v0.point.x, v1.point.x, v2.point.x);
int max_y = max3(v0.point.y, v1.point.y, v2.point.y);
bool is_top_left_12 = is_top_left(v1.point, v2.point);
bool is_top_left_20 = is_top_left(v2.point, v0.point);
bool is_top_left_01 = is_top_left(v0.point, v1.point);
int x01 = v0.point.y - v1.point.y, y01 = v1.point.x - v0.point.x;
int x12 = v1.point.y - v2.point.y, y12 = v2.point.x - v1.point.x;
int x20 = v2.point.y - v0.point.y, y20 = v0.point.x - v2.point.x;
gpu::point_t p = { min_x, min_y };
int w0_row = edge(v1.point, v2.point, p);
int w1_row = edge(v2.point, v0.point, p);
int w2_row = edge(v0.point, v1.point, p);
for (p.y = min_y; p.y <= max_y; p.y++) {
int w0 = w0_row;
int w1 = w1_row;
int w2 = w2_row;
for (p.x = min_x; p.x <= max_x; p.x++) {
bool draw =
(w0 > 0 || (w0 == 0 && is_top_left_12)) &&
(w1 > 0 || (w1 == 0 && is_top_left_20)) &&
(w2 > 0 || (w2 == 0 && is_top_left_01));
if (draw) {
fill_gouraud(v0, w0, v1, w1, v2, w2, p.x, p.y);
}
w0 += x12;
w1 += x20;
w2 += x01;
}
w0_row += y12;
w1_row += y20;
w2_row += y01;
}
}
static inline int double_area(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2) {
auto e0 = (v1.x - v0.x) * (v1.y + v0.y);
auto e1 = (v2.x - v1.x) * (v2.y + v1.y);
auto e2 = (v0.x - v2.x) * (v0.y + v2.y);
return e0 + e1 + e2;
}
void gpu::gouraud::draw_poly3(const poly3_t &p) {
const auto &p0 = p.v[0];
const auto &p1 = p.v[1];
const auto &p2 = p.v[2];
if (double_area(p0.point, p1.point, p2.point) < 0) {
fill_poly3_gouraud(p0, p1, p2);
} else {
fill_poly3_gouraud(p0, p2, p1);
}
}
void gpu::gouraud::draw_poly4(const poly4_t &p) {
auto &v0 = p.v[0];
auto &v1 = p.v[1];
auto &v2 = p.v[2];
auto &v3 = p.v[3];
draw_poly3({ v0, v1, v2 });
draw_poly3({ v1, v2, v3 });
}

View file

@ -0,0 +1,124 @@
#include "gpu_core.hpp"
void fill_texture(const gpu::texture::poly3_t &p,
const int &w0,
const int &w1,
const int &w2, int x, int y) {
int area = w0 + w1 + w2;
int u = ((p.v0.u * w0) + (p.v1.u * w1) + (p.v2.u * w2)) / area;
int v = ((p.v0.v * w0) + (p.v1.v * w1) + (p.v2.v * w2)) / area;
auto texel = gpu::read_vram(p.base_u + (u / 4), p.base_v + v);
int index = 0;
switch (u & 3) {
case 0: index = (texel >> 0) & 0xf; break;
case 1: index = (texel >> 4) & 0xf; break;
case 2: index = (texel >> 8) & 0xf; break;
case 3: index = (texel >> 12) & 0xf; break;
}
auto color = gpu::read_vram(p.clut_x + index, p.clut_y);
if (color == 0) {
return;
}
gpu::write_vram(x, y, color);
}
static int min3(int a, int b, int c) {
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
return c;
}
static int max3(int a, int b, int c) {
if (a >= b && a >= c) return a;
if (b >= a && b >= c) return b;
return c;
}
static int edge(const gpu::point_t& a, const gpu::point_t& b, const gpu::point_t& c) {
return ((b.x - a.x) * (c.y - a.y)) - ((b.y - a.y) * (c.x - a.x));
}
static bool is_top_left(const gpu::point_t &a, const gpu::point_t &b) {
return (b.y == a.y && b.x > a.x) || b.y < a.y;
}
static void fill_poly3_texture(const gpu::texture::poly3_t &t) {
int min_x = min3(t.v0.point.x, t.v1.point.x, t.v2.point.x);
int min_y = min3(t.v0.point.y, t.v1.point.y, t.v2.point.y);
int max_x = max3(t.v0.point.x, t.v1.point.x, t.v2.point.x);
int max_y = max3(t.v0.point.y, t.v1.point.y, t.v2.point.y);
bool is_top_left_12 = is_top_left(t.v1.point, t.v2.point);
bool is_top_left_20 = is_top_left(t.v2.point, t.v0.point);
bool is_top_left_01 = is_top_left(t.v0.point, t.v1.point);
int x01 = t.v0.point.y - t.v1.point.y, y01 = t.v1.point.x - t.v0.point.x;
int x12 = t.v1.point.y - t.v2.point.y, y12 = t.v2.point.x - t.v1.point.x;
int x20 = t.v2.point.y - t.v0.point.y, y20 = t.v0.point.x - t.v2.point.x;
gpu::point_t p = { min_x, min_y };
int w0_row = edge(t.v1.point, t.v2.point, p);
int w1_row = edge(t.v2.point, t.v0.point, p);
int w2_row = edge(t.v0.point, t.v1.point, p);
for (p.y = min_y; p.y <= max_y; p.y++) {
int w0 = w0_row;
int w1 = w1_row;
int w2 = w2_row;
for (p.x = min_x; p.x <= max_x; p.x++) {
bool draw =
(w0 > 0 || (w0 == 0 && is_top_left_12)) &&
(w1 > 0 || (w1 == 0 && is_top_left_20)) &&
(w2 > 0 || (w2 == 0 && is_top_left_01));
if (draw) {
fill_texture(t, w0, w1, w2, p.x, p.y);
}
w0 += x12;
w1 += x20;
w2 += x01;
}
w0_row += y12;
w1_row += y20;
w2_row += y01;
}
}
static inline int double_area(const gpu::point_t &v0, const gpu::point_t &v1, const gpu::point_t &v2) {
auto e0 = (v1.x - v0.x) * (v1.y + v0.y);
auto e1 = (v2.x - v1.x) * (v2.y + v1.y);
auto e2 = (v0.x - v2.x) * (v0.y + v2.y);
return e0 + e1 + e2;
}
void gpu::texture::draw_poly3(const gpu::texture::poly3_t &p) {
auto &v0 = p.v0;
auto &v1 = p.v1;
auto &v2 = p.v2;
if (double_area(v0.point, v1.point, v2.point) < 0) {
fill_poly3_texture({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v});
} else {
fill_poly3_texture({v0, v2, v1, p.clut_x, p.clut_y, p.base_u, p.base_v});
}
}
void gpu::texture::draw_poly4(const gpu::texture::poly4_t &p) {
auto &v0 = p.v0;
auto &v1 = p.v1;
auto &v2 = p.v2;
auto &v3 = p.v3;
gpu::texture::draw_poly3({v0, v1, v2, p.clut_x, p.clut_y, p.base_u, p.base_v});
gpu::texture::draw_poly3({v1, v2, v3, p.clut_x, p.clut_y, p.base_u, p.base_v});
}