136 lines
4.9 KiB
C
136 lines
4.9 KiB
C
/*
|
|
Geometry rendering.
|
|
*/
|
|
|
|
#ifndef COM_GEM_H
|
|
#define COM_GEM_H
|
|
|
|
#include "fix.h"
|
|
#include "mat.h"
|
|
#include "pnt.h"
|
|
#include "vec.h"
|
|
#include <stdint.h>
|
|
|
|
/* Projects vertex position to a clip space via reordered row-major MVP matrix.
|
|
It skips calculation of z depth component, as we assume to never use it.
|
|
Instead, 3rd returned component is w value, for future projection.
|
|
|
|
We must never render from inside geometry, as it will break no Z clipping
|
|
assumption (see com_gem_clip_vis_project()).
|
|
*/
|
|
static inline com_vec_t com_gem_vec_project_clip(com_mat_t a, com_vec_t b) {
|
|
com_vec_t result;
|
|
|
|
#define CASE(m_c, m_n) \
|
|
result.a[m_c] = (((int64_t)a.a[m_n * 4 + 0] * b.a[0]) + \
|
|
((int64_t)a.a[m_n * 4 + 1] * b.a[1]) + \
|
|
((int64_t)a.a[m_n * 4 + 2] * b.a[2]) + a.a[m_n * 4 + 3]) >> \
|
|
COM_FIX_FRACBITS;
|
|
|
|
CASE(0, 0);
|
|
CASE(1, 1);
|
|
CASE(2, 3);
|
|
|
|
#undef CASE
|
|
|
|
return result;
|
|
}
|
|
|
|
/* TODO: Move to separate render-specific file. */
|
|
/* TODO: If we clip test bounding volume of a model first we can skip
|
|
* all clipping whatsoever, which might hold true more often, than the cost of
|
|
* testing the volume. */
|
|
/* TODO: It should be possible to have results in pixel position, not -1 to 1.
|
|
*/
|
|
|
|
#define COM_GEM_VERTEX_UNCLIPPED (0 << 0)
|
|
#define COM_GEM_VERTEX_CLIPPED_X (1 << 0)
|
|
#define COM_GEM_VERTEX_CLIPPED_Y (1 << 1)
|
|
/* Attempt to project clip space vertex to NDC, reporting which component lies
|
|
* outside of view. Bit test against those per component. This is needed for
|
|
* determining new view-lying clipped triangles. */
|
|
static inline uint8_t com_gem_clip_vis_project(com_vec_t a, com_pnt_t *out) {
|
|
uint8_t mask = 0;
|
|
if (a.a[0] < -a.a[2] || a.a[0] > a.a[2])
|
|
mask ^= COM_GEM_VERTEX_CLIPPED_X;
|
|
if (a.a[1] < -a.a[2] || a.a[1] > a.a[2])
|
|
mask ^= COM_GEM_VERTEX_CLIPPED_Y;
|
|
out->a[0] = com_fix_div(a.a[0], a.a[2]) / 480 + 240;
|
|
out->a[1] = com_fix_div(a.a[1], a.a[2]) / 640 + 320;
|
|
return mask;
|
|
}
|
|
|
|
static inline void com_gem_draw_triangle(com_pnt_t v0, com_pnt_t v1,
|
|
com_pnt_t v2, uint8_t *out) {
|
|
int minX = com_vec_min(com_vec_from(v0.a[0], v1.a[0], v2.a[0]));
|
|
int minY = com_vec_min(com_vec_from(v0.a[1], v1.a[1], v2.a[1]));
|
|
int maxX = com_vec_max(com_vec_from(v0.a[0], v1.a[0], v2.a[0]));
|
|
int maxY = com_vec_max(com_vec_from(v0.a[1], v1.a[1], v2.a[1]));
|
|
|
|
for (int y = minY; y <= maxY; y++) {
|
|
for (int x = minX; x <= maxX; x++) {
|
|
com_pnt_t p = {.a = {x, y}};
|
|
|
|
// Test the pixel center against all 3 edges
|
|
com_vec_t ws = com_pnt_edge_weight(v0, v1, v2, p);
|
|
|
|
// If the point is inside or on all edges, draw the pixel
|
|
// (Assumes Clockwise vertex ordering)
|
|
if (ws.a[0] >= 0 && ws.a[1] >= 0 && ws.a[2] >= 0) {
|
|
out[(y * 640 + x) * 4 + 0] = 125;
|
|
out[(y * 640 + x) * 4 + 1] = 125;
|
|
out[(y * 640 + x) * 4 + 2] = 125;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void com_gem_draw_triangle_textured(com_pnt_t v0, com_pnt_t v1,
|
|
com_pnt_t v2, uint8_t *out,
|
|
com_pnt_t uv0, com_pnt_t uv1,
|
|
com_pnt_t uv2, uint8_t *tex) {
|
|
int minX = com_vec_min(com_vec_from(v0.a[0], v1.a[0], v2.a[0]));
|
|
int minY = com_vec_min(com_vec_from(v0.a[1], v1.a[1], v2.a[1]));
|
|
int maxX = com_vec_max(com_vec_from(v0.a[0], v1.a[0], v2.a[0]));
|
|
int maxY = com_vec_max(com_vec_from(v0.a[1], v1.a[1], v2.a[1]));
|
|
|
|
com_fix_t const area = com_pnt_area(v0, v1, v2);
|
|
|
|
for (int y = minY; y <= maxY; y++) {
|
|
for (int x = minX; x <= maxX; x++) {
|
|
com_pnt_t p = {.a = {x, y}};
|
|
|
|
/* TODO: Move to a separate weighting function over vec. */
|
|
// Test the pixel center against all 3 edges
|
|
com_vec_t ws = com_pnt_edge_weight(v0, v1, v2, p);
|
|
|
|
// If the point is inside or on all edges, draw the pixel
|
|
// (Assumes Clockwise vertex ordering)
|
|
if (ws.a[0] >= 0 && ws.a[1] >= 0 && ws.a[2] >= 0) {
|
|
// Barycentric weights
|
|
ws = com_vec_div(ws, com_vec_scalar(area));
|
|
|
|
// Affine interpolation of UVs.
|
|
com_fix_t const u =
|
|
(ws.a[0] * uv0.a[0] + ws.a[1] * uv1.a[0] + ws.a[2] * uv2.a[0]) >>
|
|
COM_FIX_FRACBITS;
|
|
com_fix_t const v =
|
|
(ws.a[0] * uv0.a[1] + ws.a[1] * uv1.a[1] + ws.a[2] * uv2.a[1]) >>
|
|
COM_FIX_FRACBITS;
|
|
|
|
// Nearest-neighbor texture lookup, in [0..32) range.
|
|
int32_t tx =
|
|
(int32_t)((u + (COM_FIX_FRACUNIT / 2)) / (COM_FIX_FRACUNIT / 16));
|
|
int32_t ty =
|
|
(int32_t)((v + (COM_FIX_FRACUNIT / 2)) / (COM_FIX_FRACUNIT / 16));
|
|
|
|
out[(y * 640 + x) * 4 + 0] = tex[(ty * 32 + tx) * 3 + 0];
|
|
out[(y * 640 + x) * 4 + 1] = tex[(ty * 32 + tx) * 3 + 1];
|
|
out[(y * 640 + x) * 4 + 2] = tex[(ty * 32 + tx) * 3 + 2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|