Skip to content

Commit

Permalink
feat: add functionality for rendering lines in 3-D
Browse files Browse the repository at this point in the history
  * prefix the existing subroutines `ToViewSpace`, `ToClipSpace`
    by `Tri` to designate they're associated with triangles in
    particular
  * export the default basis vectors `R_Right`, `R_Down`, and
    `R_Fwd` from `r_camera.ts`
  * add new subroutines `R_VecToViewSpace` and `R_VecToClipSpace`
    that transform the given vector to respective coordinate spaces
  * add a new subroutine `R_RenderLine` that renders a given line
    in 3-D
  * replace the old subroutine `R_DebugAxes` with the new
    `R_RenderDebugAxes` which uses the aforementioned subroutine
    `R_RenderLine`
  • Loading branch information
emre-aki committed Nov 11, 2023
1 parent 20154a1 commit 420bce8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 64 deletions.
61 changes: 20 additions & 41 deletions src/engine/r_camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
const FOV_X = G_Const.FOV_X, FOV_Y = G_Const.FOV_Y;
const MAX_MOV_TILT = G_Const.MAX_MOV_TILT;
const SCREEN_W = G_Const.SCREEN_W, SCREEN_H = G_Const.SCREEN_H;
const SCREEN_W_2 = G_Const.SCREEN_W_2, SCREEN_H_2 = G_Const.SCREEN_H_2;

const M_Mat4 = __import__M_Mat4();
const M_QuickInv4 = M_Mat4.M_QuickInv4;
Expand Down Expand Up @@ -231,16 +230,27 @@
matLookAt = R_LookAt(camPos, M_Add3(camPos, camFwd), camDown);
}

function R_ToViewSpace (triangle: tri3_t): tri3_t
function R_TriToViewSpace (triangle: tri3_t): tri3_t
{
return M_TransformTri3(matLookAt, triangle);
}

function R_ToClipSpace (triangle: tri3_t): tri3_t
function R_TriToClipSpace (triangle: tri3_t): tri3_t
{
return M_TransformTri3(matPerspective, triangle);
}

function R_VecToViewSpace (vec: vec3_t): vec3_t
{
return M_Vec3FromVec4(M_Transform4(matLookAt, M_Vec4FromVec3(vec, 1)));
}

function R_VecToClipSpace (vec: vec3_t): vec3_t
{
return M_Vec3FromVec4(M_Transform4(matPerspective,
M_Vec4FromVec3(vec, 1)));
}

function R_GetCameraState (): cam3_t
{
return {
Expand Down Expand Up @@ -299,53 +309,22 @@
5, 90, "#FF0000", 14);
}

/* FIXME: move this function to a more sensible file/module */
function R_DebugAxes (): void
{
const originViewSpace4 = M_Transform4(matLookAt,
M_Vec4FromVec3(ORIGIN, 1));
const originClipSpace3 = M_Vec3FromVec4(M_Transform4(matPerspective,
originViewSpace4));
const axesViewSpace3 = R_ToViewSpace(Tri3(RIGHT, DOWN, FWD));
const axesClipSpace3 = R_ToClipSpace(axesViewSpace3);
const rightClipSpace3 = axesClipSpace3[0];
const upClipSpace3 = axesClipSpace3[1];
const fwdClipSpace3 = axesClipSpace3[2];
const originScreen2 = [originClipSpace3[0] * SCREEN_W_2 + SCREEN_W_2,
originClipSpace3[1] * SCREEN_H_2 + SCREEN_H_2];
const rightScreen2 = [rightClipSpace3[0] * SCREEN_W_2 + SCREEN_W_2,
rightClipSpace3[1] * SCREEN_H_2 + SCREEN_H_2];
const upScreen2 = [upClipSpace3[0] * SCREEN_W_2 + SCREEN_W_2,
upClipSpace3[1] * SCREEN_H_2 + SCREEN_H_2];
const fwdScreen2 = [fwdClipSpace3[0] * SCREEN_W_2 + SCREEN_W_2,
fwdClipSpace3[1] * SCREEN_H_2 + SCREEN_H_2];
if (originViewSpace4[2] > 0 || axesViewSpace3[0][2] > 0)
R_DrawLine(originScreen2[0], originScreen2[1],
rightScreen2[0], rightScreen2[1],
255, 0, 0, 255, 2);
if (originViewSpace4[2] > 0 || axesViewSpace3[1][2] > 0)
R_DrawLine(originScreen2[0], originScreen2[1],
upScreen2[0], upScreen2[1],
0, 255, 0, 255, 2);
if (originViewSpace4[2] > 0 || axesViewSpace3[2][2] > 0)
R_DrawLine(originScreen2[0], originScreen2[1],
fwdScreen2[0], fwdScreen2[1],
0, 0, 255, 255, 2);
}

window.__import__R_Camera = function ()
{
return {
R_Origin: ORIGIN,
R_Bwd: BWD,
R_Right: RIGHT,
R_Down: DOWN,
R_Fwd: FWD,
R_InitCamera,
R_UpdateCamera,
R_GetCameraState,
R_GetProjectionOrigin,
R_ToViewSpace,
R_ToClipSpace,
R_TriToViewSpace,
R_TriToClipSpace,
R_VecToViewSpace,
R_VecToClipSpace,
R_DebugStats,
R_DebugAxes,
};
};
})();
104 changes: 89 additions & 15 deletions src/engine/r_geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

const M_Collision = __import__M_Collision();
const M_TimeBeforePlaneCollision3 = M_Collision.M_TimeBeforePlaneCollision3;
const M_LineSegmentVsPlaneCollision3 =
M_Collision.M_LineSegmentVsPlaneCollision3;
const M_BoundingBoxVsBoundingBoxCollision3 =
M_Collision.M_BoundingBoxVsBoundingBoxCollision3;

Expand All @@ -45,24 +47,29 @@
const Vec3 = M_Vec3.M_Vec3;

const R_Camera = __import__R_Camera();
const R_DebugAxes = R_Camera.R_DebugAxes;
const R_ToViewSpace = R_Camera.R_ToViewSpace;
const R_ToClipSpace = R_Camera.R_ToClipSpace;
const R_TriToViewSpace = R_Camera.R_TriToViewSpace;
const R_TriToClipSpace = R_Camera.R_TriToClipSpace;
const R_VecToViewSpace = R_Camera.R_VecToViewSpace;
const R_VecToClipSpace = R_Camera.R_VecToClipSpace;
const R_GetProjectionOrigin = R_Camera.R_GetProjectionOrigin;

const R_Draw = __import__R_Draw();
const R_DrawLine = R_Draw.R_DrawLine;
const R_DrawTriangle_Wireframe = R_Draw.R_DrawTriangle_Wireframe;
const R_FillTriangle_Flat = R_Draw.R_FillTriangle_Flat;
const R_DrawTriangle_Textured_Perspective =
R_Draw.R_DrawTriangle_Textured_Perspective;

// the center of the projection (near-clipping) plane
let projectionOrigin: vec3_t;
/* base vectors in world space */
const ORIGIN = R_Camera.R_Origin;
const BWD = R_Camera.R_Bwd, FWD = M_Scale3(BWD, -1);
const RIGHT = R_Camera.R_Right;
const DOWN = R_Camera.R_Down;
const FWD = R_Camera.R_Fwd;

// TODO: make a separate lighting controller module, maybe??
const DIRECTIONAL_LIGHT = BWD;
const DIRECTIONAL_LIGHT = M_Scale3(FWD, -1);

let nTris: number; // total number of triangles in the model
let tris3: tri3_t[]; // a pool of raw triangle data
Expand Down Expand Up @@ -248,7 +255,7 @@
let nTrianglesAfterCulling = 0;
for (let i = 0; i < nTris; ++i)
{
const triView = R_ToViewSpace(transformedTris3[i]);
const triView = R_TriToViewSpace(transformedTris3[i]);
const aView = triView[0];
if (
// frustum-culling: is the triangle at least partially within
Expand Down Expand Up @@ -410,8 +417,8 @@
( tri0: number,
tri1: number ): number
{
const tri0View = R_ToViewSpace(transformedTris3[tri0]);
const tri1View = R_ToViewSpace(transformedTris3[tri1]);
const tri0View = R_TriToViewSpace(transformedTris3[tri0]);
const tri1View = R_TriToViewSpace(transformedTris3[tri1]);

return tri1View[0][2] + tri1View[1][2] + tri1View[2][2] -
tri0View[0][2] - tri0View[1][2] - tri0View[2][2];
Expand All @@ -438,7 +445,7 @@
{
const triIndex = culledBuffer[i];
const triWorld = transformedTris3[triIndex];
const triView = R_ToViewSpace(triWorld);
const triView = R_TriToViewSpace(triWorld);
// keep a buffer of clipped triangles for drawing
const clippedTriQueue = Array(2) as [tri3_t, tri3_t];
const nClipResult = R_ClipGeometryAgainstNearPlane(triView,
Expand All @@ -447,7 +454,7 @@
for (let j = 0; j < nClipResult; ++j)
{
const triFrustum = clippedTriQueue[j];
const triClip = R_ToClipSpace(triFrustum);
const triClip = R_TriToClipSpace(triFrustum);
const aClip3 = triClip[0];
const bClip3 = triClip[1];
const cClip3 = triClip[2];
Expand All @@ -473,7 +480,7 @@
{
const triIndex = culledBuffer[i];
const triWorld = transformedTris3[triIndex];
const triView = R_ToViewSpace(triWorld);
const triView = R_TriToViewSpace(triWorld);
// the original triangle after having clipped against the near-plane
const clippedTriQueue = Array(2) as [tri3_t, tri3_t];
const nClipResult = R_ClipGeometryAgainstNearPlane(triView,
Expand All @@ -491,7 +498,7 @@
for (let j = 0; j < nClipResult; ++j)
{
const triFrustum = clippedTriQueue[j];
const triClip = R_ToClipSpace(triFrustum);
const triClip = R_TriToClipSpace(triFrustum);
const aClip3 = triClip[0];
const bClip3 = triClip[1];
const cClip3 = triClip[2];
Expand Down Expand Up @@ -540,7 +547,7 @@
const triIndex = culledBuffer[i];
const triWorld = transformedTris3[triIndex];
const uvMap = uvTable3[triIndex];
const triView = R_ToViewSpace(triWorld);
const triView = R_TriToViewSpace(triWorld);
// the original triangle after having clipped against the near-plane
const clippedTriQueue = Array(2) as [tri3_t, tri3_t];
// the vertex normals after having clipped against the near-plane
Expand All @@ -561,7 +568,7 @@
const triFrustum = clippedTriQueue[j];
const triVertexNormals = clippedTriVertexNormalQueue[j];
const uvFrustum = clippedUvMapQueue[j];
const triClip = R_ToClipSpace(triFrustum);
const triClip = R_TriToClipSpace(triFrustum);
const aClip3 = triClip[0];
const bClip3 = triClip[1];
const cClip3 = triClip[2];
Expand Down Expand Up @@ -618,6 +625,72 @@
nTrisOnScreen[1] = nCulledBuffer;
}

//
// R_RenderLine
// Render a 3-D line
//
// Vertices `src` and `dest` are in world space
//
function
R_RenderLine
( src: vec3_t, dest: vec3_t,
r: number, g: number, b: number, a: number,
stroke: number ): void
{
const srcView = R_VecToViewSpace(src);
const destView = R_VecToViewSpace(dest);
const zNear = projectionOrigin[2];
const zSrc = srcView[2], zDest = destView[2];
let srcClip: vec3_t, destClip: vec3_t;
// both ends of the line are behind the near-clipping plane, nothing to
// draw, exit early
if (zSrc < zNear && zDest < zNear) return;
// both ends of the line are in front of the near-clipping plane, no
// need to clip
if (zSrc >= zNear && zDest >= zNear)
{
srcClip = R_VecToClipSpace(srcView);
destClip = R_VecToClipSpace(destView);
}
/* one end of the line is behind, while the other is in front: it has to
* be clipped against the near-clipping plane
*/
else
{
const intersect = M_LineSegmentVsPlaneCollision3(srcView,
destView,
projectionOrigin,
FWD);
if (!intersect) return; // how would this ever happen!?
/* clip the `src` endpoint */
if (zDest > zSrc)
{
srcClip = R_VecToClipSpace(intersect);
destClip = R_VecToClipSpace(destView);

}
/* clip the `dest` endpoint */
else
{
srcClip = R_VecToClipSpace(srcView);
destClip = R_VecToClipSpace(intersect);
}
}
R_DrawLine(srcClip[0] * SCREEN_W_2 + SCREEN_W_2,
srcClip[1] * SCREEN_H_2 + SCREEN_H_2,
destClip[0] * SCREEN_W_2 + SCREEN_W_2,
destClip[1] * SCREEN_H_2 + SCREEN_H_2,
r, g, b, a,
stroke);
}

function R_RenderDebugAxes (length: number): void
{
R_RenderLine(ORIGIN, M_Scale3(RIGHT, length), 255, 0, 0, 255, 2);
R_RenderLine(ORIGIN, M_Scale3(DOWN, length), 0, 255, 0, 255, 2);
R_RenderLine(ORIGIN, M_Scale3(FWD, length), 0, 0, 255, 255, 2);
}

function R_RenderGeometries (nTrisOnScreen: Uint32Array): void
{
R_CullGeometry();
Expand All @@ -639,7 +712,7 @@
default:
break;
}
if (DEBUG_MODE) R_DebugAxes();
if (DEBUG_MODE) R_RenderDebugAxes(1000);
}

function R_Tris (): tri3_t[]
Expand All @@ -655,6 +728,7 @@
R_LoadGeometry,
R_InitUVTable,
R_UpdateGeometry,
R_RenderLine,
R_RenderGeometries,
R_Tris,
};
Expand Down
27 changes: 19 additions & 8 deletions src/typedef.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,27 +403,30 @@ type R_InitCamera = (
type R_UpdateCamera = (mult: number) => void;
type R_GetCameraState = () => cam3_t;
type R_GetProjectionOrigin = () => vec3_t;
type R_ToViewSpace = (triangle: tri3_t) => tri3_t;
type R_ToClipSpace = (triangle: tri3_t) => tri3_t;
type R_TriToViewSpace = (triangle: tri3_t) => tri3_t;
type R_TriToClipSpace = (triangle: tri3_t) => tri3_t;
type R_VecToViewSpace = (vec: vec3_t) => vec3_t;
type R_VecToClipSpace = (vec: vec3_t) => vec3_t;

type R_DebugStats = (
deltaT: number,
nTrisOnScreen: Uint32Array
) => void;

type R_DebugAxes = () => void;

type __Mod__R_Camera = {
R_Origin: vec3_t,
R_Bwd: vec3_t,
R_Right: vec3_t,
R_Down: vec3_t,
R_Fwd: vec3_t,
R_InitCamera: R_InitCamera,
R_UpdateCamera: R_UpdateCamera,
R_GetCameraState: R_GetCameraState,
R_GetProjectionOrigin: R_GetProjectionOrigin,
R_ToViewSpace: R_ToViewSpace,
R_ToClipSpace: R_ToClipSpace,
R_TriToViewSpace: R_TriToViewSpace,
R_TriToClipSpace: R_TriToClipSpace,
R_VecToViewSpace: R_VecToViewSpace,
R_VecToClipSpace: R_VecToClipSpace,
R_DebugStats: R_DebugStats,
R_DebugAxes: R_DebugAxes,
};

declare function __import__R_Camera (): __Mod__R_Camera;
Expand Down Expand Up @@ -586,6 +589,13 @@ type R_InitUVTable = (

type R_UpdateGeometry = () => void;
type R_RenderGeometries = (nTrisOnScreen: Uint32Array) => void;

type R_RenderLine = (
src: vec3_t, dest: vec3_t,
r: number, g: number, b: number, a: number,
stroke: number
) => void;

type R_Tris = () => tri3_t[];

type __Mod__R_Geometry = {
Expand All @@ -594,6 +604,7 @@ type __Mod__R_Geometry = {
R_LoadGeometry: R_LoadGeometry,
R_InitUVTable: R_InitUVTable,
R_UpdateGeometry: R_UpdateGeometry,
R_RenderLine: R_RenderLine,
R_RenderGeometries: R_RenderGeometries,
R_Tris: R_Tris,
};
Expand Down

0 comments on commit 420bce8

Please sign in to comment.