diff --git a/src/engine/r_camera.ts b/src/engine/r_camera.ts index 139e0d0..8dde606 100644 --- a/src/engine/r_camera.ts +++ b/src/engine/r_camera.ts @@ -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; @@ -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 { @@ -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, }; }; })(); diff --git a/src/engine/r_geometry.ts b/src/engine/r_geometry.ts index 33c1dfb..da1a7b0 100644 --- a/src/engine/r_geometry.ts +++ b/src/engine/r_geometry.ts @@ -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; @@ -45,12 +47,14 @@ 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 = @@ -58,11 +62,14 @@ // 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 @@ -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 @@ -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]; @@ -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, @@ -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]; @@ -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, @@ -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]; @@ -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 @@ -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]; @@ -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(); @@ -639,7 +712,7 @@ default: break; } - if (DEBUG_MODE) R_DebugAxes(); + if (DEBUG_MODE) R_RenderDebugAxes(1000); } function R_Tris (): tri3_t[] @@ -655,6 +728,7 @@ R_LoadGeometry, R_InitUVTable, R_UpdateGeometry, + R_RenderLine, R_RenderGeometries, R_Tris, }; diff --git a/src/typedef.d.ts b/src/typedef.d.ts index 2a2eb71..eba7c89 100644 --- a/src/typedef.d.ts +++ b/src/typedef.d.ts @@ -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; @@ -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 = { @@ -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, };