diff --git a/README.md b/README.md index 3aa94dc8..be8ef76f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Features in development include: - Fully-functional AHCI support - ~~VBE support~~ 32-bit color VBE graphics - A new GUI framework in 32-bit color - - [Gr2](https://github.com/TempleProgramming/Gr2) + - CosmicGL renderer - Compiler optimizations for speed improvements - SSE2+ instruction support in compiler and assembler - Network card drivers and a networking stack diff --git a/Zenith-latest-2021-06-25-21_34_44.iso b/Zenith-latest-2021-06-26-04_03_37.iso old mode 100755 new mode 100644 similarity index 83% rename from Zenith-latest-2021-06-25-21_34_44.iso rename to Zenith-latest-2021-06-26-04_03_37.iso index c701500c..68aa9094 Binary files a/Zenith-latest-2021-06-25-21_34_44.iso and b/Zenith-latest-2021-06-26-04_03_37.iso differ diff --git a/src/Home/CosmicGLDemos/DrawBSP/DOOM1.WAD b/src/Home/CosmicGLDemos/DrawBSP/DOOM1.WAD new file mode 100644 index 00000000..1a58f662 Binary files /dev/null and b/src/Home/CosmicGLDemos/DrawBSP/DOOM1.WAD differ diff --git a/src/Home/CosmicGLDemos/DrawBSP/DrawBSP.CC b/src/Home/CosmicGLDemos/DrawBSP/DrawBSP.CC new file mode 100644 index 00000000..960bb057 --- /dev/null +++ b/src/Home/CosmicGLDemos/DrawBSP/DrawBSP.CC @@ -0,0 +1,83 @@ +/* This demo renders a BSP. Put DOOM1.WAD in the directory to try this. */ + +Cd(__DIR__);; + +"\n\n\n\n"; // Seperate from annoying warnings + +I64 wWC = 90; // Window width in columns +I64 wHC = 90; // Window height in rows +I64 wW = wWC * 8; +I64 wH = wHC * 8; +I64 wXC = 1; // Window x in columns +I64 wYC = 2; // Window y in rows +I64 wX = wXC * 8; +I64 wY = wYC * 8; + +SettingsPush; +WinHorz(wXC, wXC + wWC - 1); +WinVert(wYC, wYC + wHC - 1); +DocClear; + +// Prepare framebuffer +CTex2D frameBuf; +Tex2DInit(&frameBuf, TEX2D_RAW, wW, wH); +Tex2DColorFill(&frameBuf, gr_palette[WHITE]); + +// Load BSP and render +CWAD doom1; +WADLoad(&doom1, "DOOM1.WAD"); + +CDoomMap map; +DoomMapLoad(&map, &doom1, "E1M1"); + +// Sprite loading +CTex2D sprite; +Tex2DLoadWADSprite(&sprite, &doom1, "POSSF2F8"); + +WADFree(&doom1); + +CBGR24 cRed; +cRed.r = 255; cRed.b = cRed.g = 0; + +I64 i, a, b, c, d; +for (i = 0; i < map.nLines; i++) +{ + a = map.lines[i].v1->x >> 32; + b = map.lines[i].v1->y >> 32; + c = map.lines[i].v2->x >> 32; + d = map.lines[i].v2->y >> 32; + + a /= 8; + b /= 8; + c /= 8; + d /= 8; + + a += 200; + b += 650; + c += 200; + d += 650; + + if (a < 0 || b < 0 || c < 0 || d < 0 || + a >= wW || b >= wH || c >= wW || d >= wH) + { + // One coordinate outside window + } + else + { + DrawLine(&frameBuf, a, b, c, d, cRed); + } +} + +DrawTexture(&frameBuf, &sprite, 200, 200, TRUE); + +while (CharScan() == 0) +{ + Tex2DDebugDisp(&frameBuf, wX, wY); + Sleep(1); +} + +DoomMapFree(&doom1); +Tex2DFree(&sprite); +Tex2DFree(&framebuf); +SettingsPop; +Exit; diff --git a/src/Home/CosmicGLDemos/DrawPrimitives/DrawPrimitives.CC b/src/Home/CosmicGLDemos/DrawPrimitives/DrawPrimitives.CC new file mode 100644 index 00000000..b7c9d656 --- /dev/null +++ b/src/Home/CosmicGLDemos/DrawPrimitives/DrawPrimitives.CC @@ -0,0 +1,53 @@ +/* This demo shows how to draw primitives to textures. */ + +Cd(__DIR__);; + +I64 wWC = 64; // Window width in columns (512px) +I64 wHC = 48; // Window height in rows (384px) +I64 wW = wWC * 8; +I64 wH = wHC * 8; +I64 wXC = 1; // Window x in columns +I64 wYC = 2; // Window y in rows +I64 wX = wXC * 8; +I64 wY = wYC * 8; + +SettingsPush; +WinHorz(wXC, wXC + wWC - 1); +WinVert(wYC, wYC + wHC - 1); +DocClear; + +// Prepare framebuffer +CTex2D frameBuf; +Tex2DInit(&frameBuf, TEX2D_RAW, wW, wH); +Tex2DColorFill(&frameBuf, gr_palette[WHITE]); + +// Draw Rectangles +DrawRectFill(&frameBuf, 32, 32, 128, 96, gr_palette[CYAN]); + +DrawRectVertGradient(&frameBuf, 64, 128, 96, 256, gr_palette[CYAN], + gr_palette[BLUE]); + +// Gradient's starting transition points can also be modified: +DrawRectVertGradient(&frameBuf, 160, 32, 224, 96, gr_palette[BLACK], + gr_palette[WHITE], 0.65, 0.7); + +DrawRectOutline(&frameBuf, 55, 300, 75, 310, gr_palette[LTGRAY]); + +// You can also define a second color for adding button-like shading +DrawRectSoftOutline(&frameBuf, 100, 200, 130, 210, gr_palette[LTGRAY], + gr_palette[DKGRAY]); + +// Drawing text +DrawChar(&frameBuf, 48, 48, '!', gr_palette[WHITE]); +DrawString(&frameBuf, 16, 104, "When I wrote this, God said: +calls singing moist days excellence.", gr_palette[GREEN]); + +while (CharScan() == 0) +{ + Tex2DDebugDisp(&frameBuf, wX, wY); + Sleep(1); +} + +Tex2DFree(&frameBuf); +SettingsPop; +Exit; diff --git a/src/Home/CosmicGLDemos/ModelLoading/KingTerry.wad b/src/Home/CosmicGLDemos/ModelLoading/KingTerry.wad new file mode 100644 index 00000000..7ef526ca Binary files /dev/null and b/src/Home/CosmicGLDemos/ModelLoading/KingTerry.wad differ diff --git a/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.CC b/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.CC new file mode 100644 index 00000000..7dc55b8b --- /dev/null +++ b/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.CC @@ -0,0 +1,247 @@ +/* This demo shows how to load a custom model (created based on the .obj format +using a converstion script). It uses WAD textures rather than BMP textures +unlike most of the other demos. The model is not transformed in any way and +is rendered directly, so scaling the window will also scale the resulting +render. */ + +Cd(__DIR__);; + +I64 wWC = 80; // Window width in columns (Drop to under 80 for 640x480 displays) +I64 wHC = 45; // Window height in rows (Drop to under 60 for 640x480 displays) +I64 wW = wWC * 8; +I64 wH = wHC * 8; +I64 wXC = 1; // Window x in columns +I64 wYC = 2; // Window y in rows +I64 wX = wXC * 8; +I64 wY = (wYC + 0) * 8; + +SettingsPush; +WinHorz(wXC, wXC + wWC - 1); +WinVert(wYC, wYC + wHC - 1); +DocClear; + +// Framebuffer Initialization +CTex2D frameBuf; +CTex2D depthBuf; + +Tex2DInit(&frameBuf, TEX2D_RAW, wW, wH); +Tex2DInit(&depthBuf, TEX2D_DEPTH, wW, wH); + +// Colors +CBGR24 cBlack; +cBlack.r = 0; cBlack.g = 0; cBlack.b = 0; +CBGR24 cRed; +cRed.r = 255; cRed.g = 20; cRed.b = 20; +CBGR24 cWhite; +cWhite.r = 255; cWhite.g = 255; cWhite.b = 255; + +Tex2DColorFill(&frameBuf, cBlack); + +// Texture Intitalization +CWAD wad; +WADLoad(&wad, "KingTerry.wad"); + +CTex2D TexRevelation; +Tex2DLoadWAD(&TexRevelation, &wad, "Revelation"); + +CTex2D TexKingTerrySkin; +Tex2DLoadWAD(&TexKingTerrySkin, &wad, "KingTerry_Skin"); + +CTex2D TexKingTerryBody; +Tex2DLoadWAD(&TexKingTerryBody, &wad, "KingTerry_Body"); + +CTex2D TexNebula; +Tex2DLoadWAD(&TexNebula, &wad, "Nebula"); + +WADFree(&wad); + +// Generate our own page texture +CTex2D TexPages; +Tex2DInit(&TexPages, TEX2D_RAW, 256, 256); +Tex2DColorFill(&TexPages, cWhite); +DrawString(&TexPages, 16, 20, "I should fix", cBlack); +DrawString(&TexPages, 16, 35, "the UVs on this", cBlack); +DrawString(&TexPages, 16, 50, "book model.", cBlack); + +class CMat +{ + U8 name[32]; + U32 nOffset; // Offset to first triangle + U32 nTris; // Number of triangles +}; + +class CZMHeader +{ + I32 nMats; // Number of materials + U32 nTriOffset; // Triangle array offset + U32 nVertOffset; // Vertex array offset + U32 nUVOffset; // UV array offset + U32 nNormOffset; // Normal array offset +}; + +class CZMTri +{ + U32 iV1; // Vertices + U32 iV2; + U32 iV3; + U32 iN1; // Normals + U32 iN2; + U32 iN3; + U32 iT1; // UVs + U32 iT2; + U32 iT3; +}; + +class CZModel +{ + CMat *mat; + CZMTri *tri; + CVec3 *vert; + CVec2 *uv; + CVec3 *norm; +}; + +class UniformTex +{ + I64 mat; // Material index of model to render + CTex2D *tex; +}; + +class UniformCol +{ + I64 mat; + CBGR24 col; +}; + +// Load model +U8 *MdlHandle = FileRead("TerryTruth.ZM"); +CZMHeader *header = MdlHandle; +CZModel MdlTerry; + +MdlTerry.mat = MdlHandle + 20; +MdlTerry.tri = MdlHandle + header->nTriOffset; +MdlTerry.vert = MdlHandle + header->nVertOffset; +MdlTerry.uv = MdlHandle + header->nUVOffset; +MdlTerry.norm = MdlHandle + header->nNormOffset; + +// Prepare uniforms (unchanging parameters) to feed into shader + +// Book cover is red +UniformCol UniCover; +UniCover.col.r = 150; +UniCover.col.g = 0; +UniCover.col.b = 0; +UniCover.mat = 0; + +// Skin uses skin texture +UniformTex UniKingTerry_Skin; +UniKingTerry_Skin.tex = &TexKingTerrySkin; +UniKingTerry_Skin.mat = 1; + +// Body uses body texture +UniformTex UniKingTerry_Body; +UniKingTerry_Body.tex = &TexKingTerryBody; +UniKingTerry_Body.mat = 2; + +// Hat is black (dark gray) +UniformCol UniHat; +UniHat.col.r = 20; +UniHat.col.g = 20; +UniHat.col.b = 20; +UniHat.mat = 3; + +// Pages uses WAD texture +UniformTex UniPages; +UniPages.tex = &TexPages; // Set this to &TexRevelation for original. +UniPages.mat = 4; + +// Nebula uses WAD texture +UniformTex UniNebula; +UniNebula.tex = &TexNebula; +UniNebula.mat = 5; + +// Generate a texure and color shader +CShader ShdTex, ShdCol; +ShdTex.vertValues = MAlloc(2 * sizeof(I64)); +ShdTex.vertValues[0] = SHD_CVEC2; // UV +ShdTex.vertValues[1] = SHD_CVEC3; // Normal + +ShdCol.vertValues = MAlloc(2 * sizeof(I64)); +ShdCol.vertValues[0] = SHD_CVEC2; // UV +ShdCol.vertValues[1] = SHD_CVEC3; // Normal + +ShdTex.nVertValues = 2; +ShdCol.nVertValues = 2; + +U0 vShaderTex(CTri *tri, F64 *vertOutBuf, U8 *mdlPtr, U8 *uniforms, I64 iTri, I64 nTris) +{ + CZModel *mdl = mdlPtr; + UniformTex *uni = uniforms; + + nTris++; // So I don't have to see the unused warning. + + I64 t = mdl->mat[uni->mat].nOffset + iTri; // Triangle index in triangle array + + // Copy model directly into the triangle, I won't even transform for this demo. + MemCopy(&tri->p[0], &mdl->vert[mdl->tri[t].iV1 - 1], sizeof(CVec3)); + MemCopy(&tri->p[1], &mdl->vert[mdl->tri[t].iV2 - 1], sizeof(CVec3)); + MemCopy(&tri->p[2], &mdl->vert[mdl->tri[t].iV3 - 1], sizeof(CVec3)); + + tri->p[0].z = -1 * tri->p[0].z; + tri->p[1].z = -1 * tri->p[1].z; + tri->p[2].z = -1 * tri->p[2].z; + + // Copy uv and normals into vertOutBuf to be interpolated for the vertex shader. + MemCopy(&vertOutBuf[0], &mdl->uv[mdl->tri[t].iT1 - 1], sizeof(CVec2)); + MemCopy(&vertOutBuf[2], &mdl->norm[mdl->tri[t].iN1 - 1], sizeof(CVec3)); + MemCopy(&vertOutBuf[5], &mdl->uv[mdl->tri[t].iT2 - 1], sizeof(CVec2)); + MemCopy(&vertOutBuf[7], &mdl->norm[mdl->tri[t].iN2 - 1], sizeof(CVec3)); + MemCopy(&vertOutBuf[10], &mdl->uv[mdl->tri[t].iT3 - 1], sizeof(CVec2)); + MemCopy(&vertOutBuf[12], &mdl->norm[mdl->tri[t].iN3 - 1], sizeof(CVec3)); +} + +U0 fShaderTex(CBGR24 *color, F64 *fragInBuf, U8 *uniforms) +{ + UniformTex *uni = uniforms; + Tex2DSampleNorm(color, uni->tex, fragInBuf[0], 1.0-fragInBuf[1]); + // Debug, display normals + // color->r=fragInBuf[2]*255.0; color->g=fragInBuf[3]*255.0; color->b=fragInBuf[4]*255.0; +} + +U0 fShaderCol(CBGR24 *color, F64 *fragInBuf, U8 *uniforms) +{ + UniformCol *uni = uniforms; + color->r = uni->col.r; + color->g = uni->col.g; + color->b = uni->col.b; +} + +ShdTex.FragShd = &fShaderTex; +ShdTex.VertShd = &vShaderTex; +ShdCol.FragShd = &fShaderCol; +ShdCol.VertShd = &vShaderTex; + +Tex2DDepthReset(&depthBuf); +RenderTris(&ShdCol, &frameBuf, &depthBuf, &MdlTerry, &UniCover, MdlTerry.mat[0].nTris); +RenderTris(&ShdTex, &frameBuf, &depthBuf, &MdlTerry, &UniKingTerry_Skin, MdlTerry.mat[1].nTris); +RenderTris(&ShdTex, &frameBuf, &depthBuf, &MdlTerry, &UniKingTerry_Body, MdlTerry.mat[2].nTris); +RenderTris(&ShdCol, &frameBuf, &depthBuf, &MdlTerry, &UniHat, MdlTerry.mat[3].nTris); +RenderTris(&ShdTex, &frameBuf, &depthBuf, &MdlTerry, &UniPages, MdlTerry.mat[4].nTris); +RenderTris(&ShdTex, &frameBuf, $ER$&depthBuf, &MdlTerry, &UniNebula, MdlTerry.mat[5].nTris); + +while (CharScan() == 0) +{ + Tex2DDebugDisp(&frameBuf, wX, wY); + Sleep(1); +} + +Tex2DFree(&frameBuf); +Tex2DFree(&depthBuf); +Tex2DFree(&TexPages); +Tex2DFree(&TexRevelation); +Tex2DFree(&TexKingTerrySkin); +Tex2DFree(&TexKingTerryBody); +Tex2DFree(&TexNebula); + +SettingsPop; +Exit; diff --git a/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.ZM b/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.ZM new file mode 100644 index 00000000..e2839b5f Binary files /dev/null and b/src/Home/CosmicGLDemos/ModelLoading/TerryTruth.ZM differ diff --git a/src/Home/CosmicGLDemos/TextureLoading/RenderTextures.CC b/src/Home/CosmicGLDemos/TextureLoading/RenderTextures.CC new file mode 100644 index 00000000..6748504e --- /dev/null +++ b/src/Home/CosmicGLDemos/TextureLoading/RenderTextures.CC @@ -0,0 +1,30 @@ +/* This demo shows how to initialize, import, and render textures. */ + +Cd(__DIR__);; + +I64 wWC = 32; // Window width in columns (256px) +I64 wHC = 32; // Window height in rows (256px) +I64 wW = wWC * 8; +I64 wH = wHC * 8; +I64 wXC = 1; // Window x in columns +I64 wYC = 2; // Window y in rows +I64 wX = wXC * 8; +I64 wY = wYC * 8; + +SettingsPush; +WinHorz(wXC, wXC + wWC - 1); +WinVert(wYC, wYC + wHC - 1); +DocClear; + +CTex2D texture; +Tex2DLoadBMP(&texture, "SaintTerry.bmp"); + +while (CharScan() == 0) +{ + Tex2DDebugDisp(&texture, wX, wY); + Sleep(1); +} + +Tex2DFree(&texture); +SettingsPop; +Exit; diff --git a/src/Home/CosmicGLDemos/TextureLoading/SaintTerry.bmp b/src/Home/CosmicGLDemos/TextureLoading/SaintTerry.bmp new file mode 100644 index 00000000..13662ee6 Binary files /dev/null and b/src/Home/CosmicGLDemos/TextureLoading/SaintTerry.bmp differ diff --git a/src/Home/CosmicGLDemos/TexturedTriangle/MrGod.bmp b/src/Home/CosmicGLDemos/TexturedTriangle/MrGod.bmp new file mode 100644 index 00000000..53002f46 Binary files /dev/null and b/src/Home/CosmicGLDemos/TexturedTriangle/MrGod.bmp differ diff --git a/src/Home/CosmicGLDemos/TexturedTriangle/UVMappedShader.CC b/src/Home/CosmicGLDemos/TexturedTriangle/UVMappedShader.CC new file mode 100644 index 00000000..20f1dcfc --- /dev/null +++ b/src/Home/CosmicGLDemos/TexturedTriangle/UVMappedShader.CC @@ -0,0 +1,119 @@ +/* This demo shows how to create a custom model format, import a texture, and +program a shader to render a triangle. */ + +Cd(__DIR__);; + +I64 wWC = 32; // Window width in columns (512px) +I64 wHC = 32; // Window height in rows (512px) +I64 wW = wWC * 8; +I64 wH = wHC * 8; +I64 wXC = 1; // Window x in columns +I64 wYC = 2; // Window y in rows +I64 wX = wXC * 8; +I64 wY = (wYC + 4) * 8; + +SettingsPush; +WinHorz(wXC, wXC + wWC - 1); +WinVert(wYC, wYC + wHC + 3); +DocClear; + +// Framebuffer Initialization +CTex2D frameBuf; +CTex2D depthBuf; + +Tex2DInit(&frameBuf, TEX2D_RAW, wW, wH); +Tex2DInit(&depthBuf, TEX2D_DEPTH, wW, wH); + +// Clear framebuffer texture with black background +CBGR24 cBlack; +cBlack.r = 0; cBlack.g = 0; cBlack.b = 0; +Tex2DColorFill(&frameBuf, cBlack); + +// Importing BMP texture +CTex2D texture; +Tex2DLoadBMP(&texture, "MrGod.bmp"); + +// Create custom model format that is quite straightforward +class MDL +{ + I64 nTris; + CTri *tri; + CVec2 *uv; +}; + +// Load it with a texture mapped triangle. +MDL model; +model.nTris = 1; +model.tri = MAlloc(sizeof(CTri)); +model.tri[0].p[0].x = 0.0; +model.tri[0].p[0].y = 0.8; +model.tri[0].p[0].z = 0.0; +model.tri[0].p[1].x = -0.8; +model.tri[0].p[1].y = -0.4; +model.tri[0].p[1].z = 0.0; +model.tri[0].p[2].x = 0.8; +model.tri[0].p[2].y = -0.4; +model.tri[0].p[2].z = 0.0; +model.uv = MAlloc(sizeof(CVec2)*3); +// UVs commonly use coordinates of 0.0-1.0, although you can use direct pixel +// coordinates if you use the pixel coordinate sampler in the shader. +model.uv[0].x = 0.5; +model.uv[0].y = 0.0898; +model.uv[1].x = 0.07421875; +model.uv[1].y = 0.73046875; +model.uv[2].x = 0.9375; +model.uv[2].y = 0.7305; + +// In this example a class is not needed for the uniforms (unchanging +// parameters), we only feed the pointer for the texture we want to sample. +// to the shader. + +// Generate a shader. Each vertex has a UV coordinate (coordinate that +// maps to the texture), so we only need to pass one CVEC2 attribute +// from the vertex shader to the fragment shader. +CShader shd, shdB; +shd.vertValues = MAlloc(sizeof(I64)); +shd.vertValues[0] = SHD_CVEC2; // UV coordinate +shd.nVertValues = 1; + +// The vertex shader gives the normalized device coordinates (screen coordinates +// from -1.0 to 1.0) of each vertex of a specified triangle. Normally you would +// do translations and perspective transforms, but here our model is already in +// NDC. It also passes the UV coordinates of each vertex to the vertOutBuf to +// be interpolated for the fragment shader. +U0 vShader(CTri *tri, F64 *vertOutBuf, U8 *mdlPtr, U8 *uniforms, I64 iTri, I64 nTris) +{ + MDL *mdl = mdlPtr; + MemCopy(tri, &mdl->tri[iTri], sizeof(CTri)); + MemCopy(vertOutBuf, &mdl->uv[iTri*3], sizeof(CVec2)*3); +} + +// The fragment shader calculates what color an individual fragment (pixel) +// should be. Here we use the interpolated UV coordinate from the fragInBuf to +// sample the color from the texture. +U0 fShader(CBGR24 *color, F64 *fragInBuf, U8 *uniforms) +{ + // We will pass in the texture pointer through the uniform later + // when we render. + Tex2DSampleNorm(color, uniforms, fragInBuf[0], fragInBuf[1]); +} + +// Attach vertex and fragment shader +shd.FragShd = &fShader; +shd.VertShd = &vShader; + +while (CharScan() == 0) +{ + // Where &texture is you would usually feed in a pointer to uniforms, + // however since the texture is the only value the shader needs, + // we simply pass in that pointer. + RenderTris(&shd, &frameBuf, &depthBuf, &model, &texture, 1); + Tex2DDebugDisp(&frameBuf, wX, wY); + Sleep(1); +} + +Tex2DFree(&frameBuf); +Tex2DFree(&depthBuf); +Tex2DFree(&texture); +SettingsPop; +Exit; diff --git a/src/Home/Registry.CC b/src/Home/Registry.CC new file mode 100644 index 00000000..b1d56deb --- /dev/null +++ b/src/Home/Registry.CC @@ -0,0 +1,10 @@ +$TR,"Zenith"$ +$ID,2$$TR,"SysMessageFlags"$ +$ID,2$sys_message_flags[0]=0; +$ID,-2$$TR,"SysRegVer"$ +$ID,2$registry_version=2.010; +$ID,-2$$ID,-2$$TR,"Once"$ +$ID,2$$TR,"Zenith"$ +$ID,2$$ID,-2$$TR,"User"$ +$ID,2$$ID,-2$$ID,-2$$TR,"AutoComplete"$ +$ID,2$ac.col=98;ac.row=23;$ID,-2$ \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Docs/CosmicGLOverview.DD b/src/Zenith/CosmicGL/Docs/CosmicGLOverview.DD new file mode 100644 index 00000000..e78258e2 --- /dev/null +++ b/src/Zenith/CosmicGL/Docs/CosmicGLOverview.DD @@ -0,0 +1,247 @@ + +$FG,5$Using this Library$FG$ +$HL,1$#include "MakeCosmicGL"$HL,0$ +$FG,2$Simply include MakeCosmicGL.CC located at the root of this repository in your +project. The #include's beneath each title in the documentation are to help +you find where these functions/classes are implemented. + +$FG,5$Creating/Freeing Textures and Framebuffers$FG$ +$HL,1$#include "Texture" +$HL,0$ +$FG,2$CTex2D is used for both textures and framebuffers:$FG$ + +$HL,1$U0 Tex2DInit(CTex2D *tex, I64 type, I64 w, I64 h); +U0 Tex2DFree(CTex2D *tex);$HL,0$ + +$FG,2$Where $FG$$FG,9$type$FG$ $FG,2$can be:$FG$ +$FG,3$ +TEX2D_RAW$FG$ $FG,2$CBGR24 format$FG$ +$FG,3$TEX2D_DEPTH$FG$ $FG,2$F64 format$FG$ +$FG,3$TEX2D_WAD$FG$ $FG,2$WAD/BMP 8-bit 256-CBGR24 palette compression$FG$ + +$FG,2$Textures must be made for the frame and depth buffer, which can later be drawn +to a window after the image is rendered onto them. Textures that you intend to +use as render targets such as framebuffers can not be compressed:$FG$ + +$HL,1$CTex2D frameBuf, depthBuf; +Tex2DInit(&frameBuf, TEX2D_RAW, 640, 480); +Tex2DInit(&depthBuf, TEX2D_DEPTH, 640, 480);$HL,0$ + +$FG,5$Loading/Clearing Textures$FG$ +$FG,2$Tex2DInit merely allocates space, you can then clear the textures with the +following:$FG$ + +$HL,1$U0 Tex2DColorFill(CTex2D *tex, CBGR24 color); +U0 Tex2DDepthReset(CTex2D *tex); +U0 Tex2DLoadWAD(CTex2D *tex, CWAD *wad, U8 *texName); +$HL,0$ +$FG,5$Sampling Textures$FG$ +$FG,2$To sample a texture location (such as when texture mapping in a fragment +shader), the following are available:$FG$ + +$HL,1$U0 Tex2DSampleNorm(CBGR24 *col, CTex2D *tex, F64 u, F64 v);$HL,0$ $FG,2$Samples with normalized 0.0-1.0 uv coordinates.$FG$ +$HL,1$U0 Tex2DSampleCoord(CBGR24 *col, CTex2D *tex, I64 u, I64 v);$HL,0$ $FG,2$Samples with raw pixel uv coordinates.$FG$ + +$FG,5$Displaying Textures$FG$ +$FG,2$All texture types can be rendered to the OS framebuffer. Textures and +non-color data (depth) are rendered in grayscale.$FG$ + +$HL,1$U0 Tex2DDebugDisp(CTex2D *tex, I64 x, I64 y);$HL,0$ $FG,2$Copies directly to OS display buffer rather than window, will be replaced.$FG$ + +$FG,5$Loading Textures From Common Formats$FG$ +$HL,1$#include "TextureImport" +$HL,0$ +$FG,5$WAD Textures$FG$ +$FG,2$WAD files were a common method of storing textures in early game engines, +popularized by the DOOM/Quake/GldSrc engines. Textures are compressed with +8-bit color pointing to a 256 24-bit color palette. See the WAD section to +learn how to load the CWAD object to pass to this function. + +$FG,2$Example WAD loading:$FG$ +$HL,1$ +CWAD wad; +CTex2D tex; +WADLoad(&wad, "Assets/Textures/HALFLIFE.WAD"); +Tex2DLoadWAD(&tex, &wad, "!waterblue");$HL,1$$HL,0$ $FG,2$The loader will initialize the texture.$FG$ +WADFree(&wad); +$HL,0$ +$FG,5$BMP Images$FG$ +$FG,2$BMP images are an extremely simple format to read. You can load 24-bit uncompressed +BMPs like so:$FG$ + +$HL,1$CTex2D tex; +Tex2DLoadBMP(&tex, "SaintTerry.bmp");$HL,0$ $FG,2$The loader will initialize the texture.$FG$ + +$FG,5$Loading and Reading WAD Files$FG$ +$HL,1$#include "WAD/WADTypes" +#include "WAD/WAD"$HL,0$ +$FG,2$WAD files are a general purpose format for storing a flat hierarchy of +directories called "lumps." They were first used by the DOOM engine to hold +game assets, however they have been used by subsequent engines such as Quake +and GldSrc to hold textures. The general purpose WAD manipulation functions +below can be used to access all WAD formats, although some functionality from +other functions may be limited to specific WAD types.$FG$ +$HL,1$ +U0 WADLoad(CWAD *wad, U8 *fname); +U0 WADFree(CWAD *wad); +$HL,0$ +$FG,2$WAD types:$FG$$FG,3$ +WAD_TYPE_HL +WAD_TYPE_DOOM +$FG$ +$FG,5$Lumps$FG$ +$FG,2$These are named directories with no hierarchy. The format for lumps is slightly +different for DOOM and Quake/Half-Life WADs, so a CWADFileLump union is available +to pass general purpose lump pointers. + +Lump classes:$FG$ +$HL,1$class CWADFileLumpDoom; +class CWADFileLumpHL; +class CWADFileLump;$HL,0$ + +$FG,2$CWADFileLump can be "interpreted" as either with:$FG$ +CWADFileLump.doom +CWADFileLump.hl + +$FG,2$CWAD has an internal hash file for fast lump reading. To find a lump by name, use:$FG$ + +$HL,1$I64 WADFindLump(CWAD *wad, U8 *name, CWADFileLump **lump=NULL);$HL,0$ + +$FG,2$It returns the lump index, or -1 if not found. **lump can be optionally used to get +the handle to the target lump.$FG$ + +$FG,5$Shaders$FG$ +$HL,1$#include "Shader" +$HL,0$ +$FG,2$Shaders are functions that calculate the vertex positions of each triangle in +NDC (normalized device coordinates), and then calculate the color of each +individual fragment (pixel) within the triangles. You can use any model format +you like, as you get to decide what vertex coordinates are returned for each +triangle. $FG$ + +$FG,5$Uniforms$FG$ +$FG,2$Uniforms are unchanging values that will be used by the shader for every +triangle in the model. These are often values like transformation matrices, +texture pointers, or any other material parameters you need. You can provide a +pointer to your uniforms to use in the shader.$FG$ + +$FG,5$Vertex to Fragment Vertex Attributes$FG$ +$FG,2$The vertex shader returns not only the coordinates of the vertices for each +triangle, but also other attributes such as UV coordinates, normals, colors, +etc. Every time the fragment shader is called, the interpolated attributes +for that frag (pixel) are given to the shader.) + +$FG,2$You can choose the structure of the F64 array of attributes to be interpolated +by setting the nVertValues and *vertValues from CShader:$FG$ +$HL,1$ +CShader shd; +shd.nVertValues = 2; $FG,2$In this example, the vertex shader will pass two attributes.$FG$ +shd.vertValues = MAlloc(2 * sizeof(I64)); +shd.vertValues[0] = SHD_CVEC2; $FG,2$For passing UV coordinate to fragment shader.$FG$ +shd.vertValues[1] = SHD_CVEC3;$HL,0$ $FG,2$For passing normal to fragment shader.$FG$ + +$FG,2$Where the vertValues can be of type:$FG$ +$FG,3$ +SHD_F64 +SHD_CVEC2 +SHD_CVEC3 +SHD_CVEC4 +SHD_CMat4 +$FG$ +$FG,5$Vertex Shader$FG$ +$FG,2$The purpose of the vertex shader is to calculate each triangle's vertex +coordinates in NDC (noramalized device coordinates). The renderer will "plot" +or rasterize these coordinates to the specified render target texture. +Fragments outside of the -1:1 range will not be rendered (as they are +off-texture). $FG$ + +$SP,"NDC Coordinates:",BI=1$ + + + + + + + + + + + + + +$HL,1$U0 (*VertShd)(CTri *tri, F64 *vertOutBuf,, U8 *mdlPtr, U8 *uniforms, I64 iTri, i64 nTris);$HL,0$ + +$FG,3$*tri$FG$ $FG,2$OUTPUT: Final triangle coordinates in NDC$FG$ +$FG,3$*mdlPtr$FG$ $FG,2$INPUT: Pointer to model data.$FG$ +$FG,3$*vertOutBuf$FG$ $FG,2$OUTPUT: Array of attributes to be interpolated for the frag shader. In order of: [V0 Attr, V1 Attr, V2 Attr] $FG$ +$FG,3$*uniforms$FG$ $FG,2$INPUT: Pointer to uniforms.$FG$ +$FG,3$iTri$FG$ $FG,2$INPUT: Current triangle.$FG$ +$FG,3$nTris$FG$ $FG,2$INPUT: Total number of triangles in model.$FG$ + +$FG,2$Example of creating and attaching a vertex shader to a CShader:$FG$ + +$HL,1$CShader shd; +U0 vShader(CTri *tri, F64 *vertOutBuf, U8 *mdlPtr, U8 *uniforms, I64 iTri, I64 nTris) +{ + // Set *tri with the iTri from mdlPtr. +} +shd.VertShd = &vShader;$HL,0$ + +$FG,5$Fragment Shader$FG$ +$FG,2$The fragment shader returns the CBGR24 color for a particular fragment (pixel). +It takes uniforms as well as interpolated values from the vertex shader to +calculate each frag's color. + +For example, each vertex of a triangle may have a UV coordinate pointing to the +part of the texture it maps to. The three UV coordinate attributes from the +vertex shader will be interpolated into one UV coordinate sent to the fragment +shader. The fragment shader can then sample that pixel color from the texture +included in the uniforms and return that color. Fragment shaders can be used +for all sorts of purposes, including rendering lighting, shadows, warping, +color filtering, etc.$FG$ + +$HL,1$U0 (*FragShd)(CBGR24 *color, F64 *fragInBuf, U8 *uniforms);$HL,0$ + +$FG,2$Fragment shaders can be created and attached in the same way as vertex shaders:$FG$ + +$HL,1$CShader shd; +U0 fShader(CBGR24 *color, F64 *fragInBuf, U8 *uniforms) +{ + // Set *color using interpolated F64's from fragInBuf and uniforms +} +shd.FragShd = &fShader;$HL,0$ + + + +$FG,5$Rendering$FG$ +$HL,1$#include "Math" +#include "Rasterize" +$HL,0$ +$FG,2$After models, textures, and shaders are initialized, you can render with these +functions. Currently only triangles can be rendered, however support for all +sprite primitives will be added for performance and ease of use (saves the +steps of rendering textured triangles to draw 2D primitives like in GPU +libraries).$FG$ +$HL,1$ +U0 RenderTris(CShader *shd, CTex2D *frameBuf, CTex2D *depthBuf, U8 *mdl, U8 *uniforms, I64 nTris); +$HL,0$ + +õ +¢ÿÿÿ4·ÿÿÿ#ÿ +”ÿÿÿÔÿÿÿ +”ÿÿÿWÔÿÿÿW +”ÿÿÿ”ÿÿÿW +ÔÿÿÿÔÿÿÿW´ÿÿÿ7´ÿÿÿ´ÿÿÿ7Óÿÿÿ7´ÿÿÿ7Ëÿÿÿ ¡ÿÿÿY: +1Ùÿÿÿ4X: +1ÐÿÿÿZ: +1­ÿÿÿ[-1„ÿÿÿ4-1šÿÿÿJ-1 ANDC maps to texture +coordinates when +rasterizing. +8£8 +·¸P +¸P P + P  +¸   +œÿÿÿ*§ÿÿÿB +§ÿÿÿBÁÿÿÿ> +Áÿÿÿ>œÿÿÿ* +Ä+ú= +ú=ÎC +ÎCÄ+ \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Draw/Primitive.CC b/src/Zenith/CosmicGL/Draw/Primitive.CC new file mode 100644 index 00000000..a3b59cdc --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/Primitive.CC @@ -0,0 +1,233 @@ +/** + @defgroup Draw Draw + @brief Functions for drawing to textures. +*/ + +/** + @ingroup Draw + @brief Draw pixel on color texture. Does not check boundaries, point + must be fully inside texture. + + @param[in,out] tex Color texture to draw to. + @param[in] x X position. + @param[in] y Y position. + @param[in] color Color of point. +*/ +U0 DrawPixel(CTex2D *tex, I64 x, I64 y, CBGR24 color) +{ + tex->rawBuf[x + y * tex->w] = color; +} + + +/** + @ingroup Draw + @brief Draw line on color texture. Does not check boundaries, line must + be fully inside texture. + + @param[in,out] tex Color texture to draw to. + @param[in] x0 Start x. + @param[in] y0 Start y. + @param[in] x1 End x. + @param[in] y1 End y. + @param[in] color Color of line. +*/ +U0 DrawLine(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, CBGR24 color) +{ + Bool steep = (Abs(y1 - y0) > Abs(x1 - x0)); + + I64 temp; + + if (steep == TRUE) + { + temp = x0; + x0 = y0; + y0 = temp; + temp = x1; + x1 = y1; + y1 = temp; + } + + if (x0 > x1) + { + temp = x0; + x0 = x1; + x1 = temp; + temp = y0; + y0 = y1; + y1 = temp; + } + + I64 delta_x = x1 - x0; + I64 delta_y = Abs(y1 - y0); + I64 error = delta_x / 2; + + I64 step_y; + + if (y0 < y1) + { + step_y = 1; + } + else + { + step_y = -1; + } + + I64 y = y0; + + + I64 x; + for (x = x0; x < (x1 + 1); x++) + { + if (steep == TRUE) + { + tex->rawBuf[y + x * tex->w] = color; + } + else + { + tex->rawBuf[x + y * tex->w] = color; + } + + error -= delta_y; + + if (error < 0) + { + y += step_y; + error += delta_x; + } + } +} + +/** + @ingroup Draw + @brief Draw single color triangle to color texture. Does not check + boundaries, triangle must be fully inside texture. + + @param[in,out] tex Color texture to draw to. + @param[in] x0 First vertex x. + @param[in] y0 First vertex y. + @param[in] x1 Second vertex x. + @param[in] y1 Second vertex y. + @param[in] x2 Third vertex x. + @param[in] y2 Third vertex y. + @param[in] color Color of triangle. +*/ +U0 DrawTriangle(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, I64 x2, I64 y2, + CBGR24 color) +{ + I64 temp; + F64 ftemp; + + if (y0 > y1) + { + temp = x0; + x0 = x1; + x1 = temp; + temp = y0; + y0 = y1; + y1 = temp; + } + + if (y0 > y2) + { + temp = x0; + x0 = x2; + x2 = temp; + temp = y0; + y0 = y2; + y2 = temp; + } + + if (y1 > y2) + { + temp = x1; + x1 = x2; + x2 = temp; + temp = y1; + y1 = y2; + y2 = temp; + } + + Bool middle_line_drawn = FALSE; + F64 x_delta; + F64 y_delta; + F64 left_delta; + F64 right_delta; + F64 left_x; + F64 right_x; + I64 y, lX, rX; + I64 left_index, right_index; + I64 width; + + // Bottom Half + if (y0 != y1) + { + x_delta = (x1 - x0); + y_delta = (y1 - y0); + left_delta = x_delta / y_delta; + x_delta = (x2 - x0); + y_delta = (y2 - y0); + right_delta = x_delta / y_delta; + + if (left_delta > right_delta) + { + ftemp = left_delta; + left_delta = right_delta; + right_delta = ftemp; + } + + left_x = x0; + right_x = x0; + + middle_line_drawn = TRUE; + + for (y = y0; y < (y1 + 1); y++) + { + lX = left_x + 0.5; + rX = right_x + 0.5; + left_index = y * tex->w + lX; + right_index = y * tex->w + rX; + width = right_index - left_index; + MemSetU32(tex->rawBuf + left_index, color, width); + left_x += left_delta; + right_x += right_delta; + } + } + + // Top Half + if (y1 != y2) + { + x_delta = -(x1 - x2); + y_delta = (y1 - y2); + left_delta = x_delta / y_delta; + x_delta = -(x0 - x2); + y_delta = (y0 - y2); + right_delta = x_delta / y_delta; + + if (left_delta > right_delta) + { + ftemp = left_delta; + left_delta = right_delta; + right_delta = ftemp; + } + + left_x = x2; + right_x = x2; + + if (middle_line_drawn == TRUE) + { + y1 += 1; + } + + for (y = y2; y > (y1 - 1); y--) + { + lX = left_x + 0.5; + rX = right_x + 0.5; + left_index = y * tex->w + lX; + right_index = y * tex->w + rX; + width = right_index - left_index; + MemSetU32(tex->rawBuf + left_index, color, width); + left_x += left_delta; + right_x += right_delta; + } + } +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Draw/Rectangle.CC b/src/Zenith/CosmicGL/Draw/Rectangle.CC new file mode 100644 index 00000000..bcf774b0 --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/Rectangle.CC @@ -0,0 +1,165 @@ + +/** + @ingroup Draw + @brief Draw single color rectangle. + + @param[in,out] tex Texture to draw to. + @param[in] x0 Start corner x. + @param[in] y0 Start corner y. + @param[in] x1 End corner x. + @param[in] y1 End corner y. + @param[in] color Color of rectangle. +*/ +U0 DrawRectFill(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, CBGR24 color) +{ + I64 xMaxIndex = tex->w - 1; + I64 yMaxIndex = tex->h - 1; + I64 xMin = Clamp(Min(x0, x1), 0, xMaxIndex); + I64 xMax = Clamp(Max(x0, x1), 0, xMaxIndex); + I64 yMin = Clamp(Min(y0, y1), 0, yMaxIndex); + I64 yMax = Clamp(Max(y0, y1), 0, yMaxIndex); + I64 xLen = xMax - xMin; + + I64 y; + for (y = yMin; y < yMax; y++) + { + MemSetU32(&tex->rawBuf[xMin + y * tex->w], color, xLen); + } +} + +/** + @ingroup Draw + @brief Draw 1px soft rectangle outline (corners not filled). + + @param[in,out] tex Texture to draw to. + @param[in] x0 Start corner x. + @param[in] y0 Start corner y. + @param[in] x1 End corner x. + @param[in] y1 End corner y. + @param[in] color Color of rectangle. + @param[in] shadeColor (Optional) color of bottom right corner + for shading effect. +*/ +U0 DrawRectSoftOutline(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, + CBGR24 color, CBGR24 shadeColor = 0x7FFFFFFF) +{ + if (shadeColor == 0x7FFFFFFF) + shadeColor = color; + + I64 xMaxIndex = tex->w - 1; + I64 yMaxIndex = tex->w - 1; + I64 xMin = Clamp(Min(x0, x1), 0, xMaxIndex); + I64 xMax = Clamp(Max(x0, x1), 0, xMaxIndex); + I64 yMin = Clamp(Min(y0, y1), 0, yMaxIndex); + I64 yMax = Clamp(Max(y0, y1), 0, yMaxIndex); + I64 xLen = xMax - xMin - 1; + + // Top border + MemSetU32(&tex->rawBuf[xMin + 1 + yMin * tex->w], color, xLen); + + // Bottom border + MemSetU32(&tex->rawBuf[xMin + 1 + yMax * tex->w], shadeColor, xLen); + + // Left/Right border + I64 y; + for (y = yMin + 1; y < yMax; y++) + { + tex->rawBuf[xMin + y * tex->w] = color; + tex->rawBuf[xMax + y * tex->w] = shadeColor; + } +} + +/** + @ingroup Draw + @brief Draw 1px rectangle outline. + + @param[in,out] tex Texture to draw to. + @param[in] x0 Start corner x. + @param[in] y0 Start corner y. + @param[in] x1 End corner x. + @param[in] y1 End corner y. + @param[in] color Color of rectangle. + @param[in] shadeColor (Optional) color of bottom right corner + for shading effect. +*/ +U0 DrawRectOutline(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, + CBGR24 color, CBGR24 shadeColor = 0x7FFFFFFF) +{ + if (shadeColor == 0x7FFFFFFF) + shadeColor = color; + + I64 xMaxIndex = tex->w - 1; + I64 yMaxIndex = tex->w - 1; + I64 xMin = Clamp(Min(x0, x1), 0, xMaxIndex); + I64 xMax = Clamp(Max(x0, x1), 0, xMaxIndex); + I64 yMin = Clamp(Min(y0, y1), 0, yMaxIndex); + I64 yMax = Clamp(Max(y0, y1), 0, yMaxIndex); + I64 xLen = xMax - xMin + 1; + + // Top border + MemSetU32(&tex->rawBuf[xMin + yMin * tex->w], color, xLen); + + // Bottom border + MemSetU32(&tex->rawBuf[xMin + yMax * tex->w], shadeColor, xLen); + + // Left/Right border + I64 y; + for (y = yMin + 1; y < yMax; y++) + { + tex->rawBuf[xMin + y * tex->w] = color; + tex->rawBuf[xMax + y * tex->w] = shadeColor; + } +} + +/** + @ingroup Draw + @brief Draw two color vertical gradient rectangle. + + @param[in,out] tex Texture to draw to. + @param[in] x0 Start corner x. + @param[in] y0 Start corner y. + @param[in] x1 End corner x. + @param[in] y1 End corner y. + @param[in] colorTop Top color of gradient. + @param[in] colorBottom Bottom color of gradient. + @param[in] shadeColor (Optional) color of bottom right corner + for shading effect. + @param[in] tStart (Optional) start of gradient. + @param[in] tEnd (Optional) end of gradient. +*/ +U0 DrawRectVertGradient(CTex2D *tex, I64 x0, I64 y0, I64 x1, I64 y1, + CBGR24 colorTop, CBGR24 colorBottom, F64 tStart = 0.0, F64 tEnd = 1.0) +{ + I64 xMaxIndex = tex->w - 1; + I64 yMaxIndex = tex->h - 1; + I64 xMin = Clamp(Min(x0, x1), 0, xMaxIndex); + I64 xMax = Clamp(Max(x0, x1), 0, xMaxIndex); + I64 yMinReal = Min(y0, y1); + I64 yMin = Clamp(yMinReal, 0, yMaxIndex); + I64 yMaxReal = Max(y0, y1); + I64 yMax = Clamp(yMaxReal, 0, yMaxIndex); + I64 xLen = xMax - xMin; + + F64 yHeight = yMaxReal - yMinReal + 1; + F64 t; + + F64 rDiff = colorBottom.r - colorTop.r; + F64 gDiff = colorBottom.g - colorTop.g; + F64 bDiff = colorBottom.b - colorTop.b; + + F64 r, g, b; + + I64 y; + CBGR24 color; + for (y = yMin; y < yMax; y++) + { + t = Clamp((y - yMinReal) / yHeight, tStart, tEnd); + r = t * rDiff; + g = t * gDiff; + b = t * bDiff; + color.r = colorTop.r + r; + color.g = colorTop.g + g; + color.b = colorTop.b + b; + MemSetU32(&tex->rawBuf[xMin + y * tex->w], color, xLen); + } +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Draw/Text.CC b/src/Zenith/CosmicGL/Draw/Text.CC new file mode 100644 index 00000000..5a740d3f --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/Text.CC @@ -0,0 +1,86 @@ +/** + @ingroup Draw + @brief Draw a single color character to color texture. + + @param[in,out] tex Texture to draw to. + @param[in] x X position to draw character at. + @param[in] y Y position to draw character at. +*/ +U0 DrawChar(CTex2D *tex, I64 x, I64 y, U8 char, CBGR24 color, + U64 *font = text.font) +{ + // Check if off-screen + if (x >= tex->w || x <= -FONT_WIDTH) + return; + + I64 i, j; + for (j = 0; j < FONT_HEIGHT; j++) + { + for (i = 0; i < FONT_WIDTH; i++) + { + if (font[char].u8[j] & (1 << i)) + tex->rawBuf[x + i + (y + j) * tex->w] = color; + } + } +} + +/** + @ingroup Draw + @brief Draw a single color string to color texture. + + @param[in,out] tex Texture to draw to. + @param[in] x X position to draw text at. + @param[in] y Y position to draw text at. + @param[in] color Color of text. + @param[in] font Pointer to font buffer. System font by default. +*/ +U0 DrawString(CTex2D *tex, I64 x, I64 y, U8 *str, CBGR24 color, + U64 *font = text.font) +{ + I64 cur = 0; // String cursor + I64 xCur = x; // Screen cursors + I64 yCur = y; + U8 char = 0; // Character to draw + + while (str[cur] != NULL) + { + switch (str[cur]) + { + // Escape sequence + case '\\': + "ESCAPE SEQ\n"; + switch (str[cur + 1]) + { + case 'n': + xCur = x; + yCur += FONT_HEIGHT; + "NEWLINE\n"; + break; + + // Not a real tab, but what mong uses tabs in labels? + case 't': + xCur += FONT_WIDTH * 4; + break; + + case '\\': + DrawChar(tex, xCur, yCur, '\\', color, font); + xCur += FONT_WIDTH; + break; + + } + cur += 2; + break; + + case ' ': + cur++; + xCur += FONT_WIDTH; + break; + + default: + DrawChar(tex, xCur, yCur, str[cur], color, font); + cur++; + xCur += FONT_WIDTH; + break; + } + } +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Draw/Texture.CC b/src/Zenith/CosmicGL/Draw/Texture.CC new file mode 100644 index 00000000..a0798e5f --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/Texture.CC @@ -0,0 +1,47 @@ + +/** + @ingroup Draw + @brief Draw non-transformed color texture with blit operation. + + Does not yet check if texture being drawn is entirely inside the texture + being drawn to. + + @param[in,out] dst Destination texture to draw to. + @param[in] src Source texture to draw. + @param[in] x X location to draw texture. + @param[in] y Y location to draw texture. + @param[in] transparent (optional) use alpha as mask. False by default. +*/ +U0 DrawTexture(CTex2D *dst, CTex2D *src, I64 x, I64 y, + Bool transparent = FALSE) +{ + I64 xC, yC; // Current width/height position being copied from + + I64 srcW = src->w; // Don't think the compiler is optimizing this so I am + I64 srcH = src->h; + I64 dstW = dst->w; + + if (transparent) + { + for (yC = 0; yC < srcH; yC++) + { + for (xC = 0; xC < srcW; xC++) + { + if (src->rawBuf[xC + yC * srcW].pad == 255) + { + // Assume target doesn't care about alpha byte being copied + dst->rawBuf[(x + xC) + (y + yC) * dstW] = + src->rawBuf[xC + yC * srcW]; + } + } + } + } + else + { + for (yC = 0; yC < srcH; yC++) + { + MemCopy(&dst->rawBuf[x + (y + yC) * dstW], + &src->rawBuf[yC * srcW], sizeof(CBGR24) * srcW); + } + } +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Draw/UIIrix.CC b/src/Zenith/CosmicGL/Draw/UIIrix.CC new file mode 100644 index 00000000..0ae0ae30 --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/UIIrix.CC @@ -0,0 +1,25 @@ + +/* Draws text button, returns col/row for next button in nCol/nRow. */ +U0 DrawTextButton(CTex2D *tex, I64 col, I64 row, U8 *str, I64 size = 0, + I64 *nCol = NULL, I64 *nRow = NULL) +{ + if (size == 0) + size = StrLen(str); + + I64 x = col * FONT_WIDTH; + I64 y = row * FONT_HEIGHT; + + DrawRectSoftOutline(tex, x - 3, y - 3, x + 1 + size * FONT_WIDTH, + y + FONT_HEIGHT + 1, gr_palette[DKGRAY]); + + DrawRectVertGradient(tex, x - 2, y - 2, x + 1 + size * FONT_WIDTH, + y + FONT_HEIGHT + 1, gr_palette[DKGRAY], gr_palette[WHITE]); + + DrawString(tex, x, y, str, gr_palette[BLACK ]); + + if (nCol != NULL) + *nCol += size + 1; + + if (nRow != NULL) + *nRow += 2; +} diff --git a/src/Zenith/CosmicGL/Draw/UISmooth.CC b/src/Zenith/CosmicGL/Draw/UISmooth.CC new file mode 100644 index 00000000..6a3d543f --- /dev/null +++ b/src/Zenith/CosmicGL/Draw/UISmooth.CC @@ -0,0 +1,42 @@ + +// Color palette indexing +#define C_BCKGND 0 +#define C_BCKGNDLIGHT 1 // Light for highlights +#define C_BCKGNDSHD 2 // Shade for shadows +#define C_TEXT 3 + +CBGR24 palette[4] = +{ + 0x282828, // C_BCKGND + 0x383838, // C_BCKGNDLIGHT + 0x101010, // C_BCKGNDSHD + 0xFFFFFF // C_TEXT +}; + + +/* Draws text button, returns col/row for next button in nCol/nRow. */ +U0 DrawTextButton(CTex2D *tex, I64 col, I64 row, U8 *str, I64 size = 0, + I64 *nCol = NULL, I64 *nRow = NULL) +{ + if (size == 0) + size = StrLen(str); + + I64 x = col * FONT_WIDTH; + I64 y = row * FONT_HEIGHT; + + CBGR24 blue = 0x0000FF; + DrawRectSoftOutline(tex, x - 3, y - 3, x + 1 + size * FONT_WIDTH, + y + FONT_HEIGHT + 1, palette[C_BCKGNDLIGHT]); + + DrawRectVertGradient(tex, x - 2, y - 2, x + 1 + size * FONT_WIDTH, + y + FONT_HEIGHT + 1, palette[C_BCKGNDLIGHT], palette[C_BCKGND]); + + DrawString(tex, x, y, str, palette[C_TEXT]); + + if (nCol != NULL) + *nCol += size + 1; + + if (nRow != NULL) + *nRow += 2; +} + diff --git a/src/Zenith/CosmicGL/MakeCosmicGL.CC b/src/Zenith/CosmicGL/MakeCosmicGL.CC new file mode 100644 index 00000000..e7ee6692 --- /dev/null +++ b/src/Zenith/CosmicGL/MakeCosmicGL.CC @@ -0,0 +1,17 @@ +/* Include this file to rebuild the CosmicGL library. */ + +Cd(__DIR__);; +#include "Math/Math" +#include "Texture/Texture" +#include "Draw/Primitive" +#include "Draw/Texture" +#include "Draw/Rectangle" +#include "Draw/Text" +#include "Draw/UISmooth" +#include "Render/Shader" +#include "Render/Rasterize" +#include "WAD/WADTypes" +#include "WAD/WAD" +#include "Texture/TextureImport" +#include "WAD/DoomTypes" +#include "WAD/DoomMap" \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Math/Math.CC b/src/Zenith/CosmicGL/Math/Math.CC new file mode 100644 index 00000000..0c3184a5 --- /dev/null +++ b/src/Zenith/CosmicGL/Math/Math.CC @@ -0,0 +1,382 @@ +/* Legacy math library. Is being replaced\, however it will remain +until Rasterize.CC is ported over to the new math library. */ + + +class CVec4 +{ + F64 x; + F64 y; + F64 z; + F64 w; +}; + +class CVec3 +{ + F64 x; + F64 y; + F64 z; +}; + +class CVec2 +{ + F64 x; + F64 y; +}; + +class CIVec2 +{ + I64 x; + I64 y; +}; + +class CTri +{ + CVec3 p[3]; +}; + +class CMat4 +{ + F64 e[4][4]; +}; + +CVec3 *Add3(CVec3 *a, CVec3 *b) +{ + CVec3 *c = MAlloc(sizeof(CVec3)); + c->x = a->x + b->x; + c->y = a->y + b->y; + c->z = a->z + b->z; + return c; +} + +CVec2 *Add2(CVec2 *a, CVec2 *b) +{ + CVec2 *c = MAlloc(sizeof(CVec2)); + c->x = a->x + b->x; + c->y = a->y + b->y; + return c; +} + +CVec3 *Sub3(CVec3 *a, CVec3 *b) +{ + CVec3 *c = MAlloc(sizeof(CVec3)); + c->x = a->x - b->x; + c->y = a->y - b->y; + c->z = a->z - b->z; + return c; +} + +CVec2 *Sub2(CVec2 *a, CVec2 *b) +{ + CVec2 *c = MAlloc(sizeof(CVec2)); + c->x = a->x - b->x; + c->y = a->y - b->y; + return c; +} + +CVec3 *Div3S(CVec3 *a, F64 b) +{ + CVec3 *c = MAlloc(sizeof(CVec3)); + c->x = a->x / b; + c->y = a->y / b; + c->z = a->z / b; + return c; +} + +CVec2 *Div2S(CVec2 *a, F64 b) +{ + CVec2 *c = MAlloc(sizeof(CVec2)); + c->x = a->x / b; + c->y = a->y / b; + return c; +} + +CVec4 *Mul4S(CVec4 *a, F64 b) +{ + CVec4 *c = MAlloc(sizeof(CVec4)); + c->x = a->x * b; + c->y = a->y * b; + c->z = a->z * b; + c->w = a->w * b; + return c; +} + +CVec3 *Mul3S(CVec3 *a, F64 b) +{ + CVec3 *c = MAlloc(sizeof(CVec3)); + c->x = a->x * b; + c->y = a->y * b; + c->z = a->z * b; + return c; +} + +CVec2 *Mul2S(CVec2 *a, F64 b) +{ + CVec2 *c = MAlloc(sizeof(CVec2)); + c->x = a->x * b; + c->y = a->y * b; + return c; +} + +F64 Dot3(CVec3 *a, CVec3 *b) +{ + return (a->x * b->x) + (a->y * b->y) + (a->z * b->z); +} + +F64 Dot2(CVec2 *a, CVec2 *b) +{ + return (a->x * b->x) + (a->y * b->y); +} + +CVec3 *Norm3(CVec3 *a) +{ + F64 length = Sqrt(Sqr(a->x) + Sqr(a->y) + Sqr(a->z)); + a->x /= length; + a->y /= length; + a->z /= length; + return a; +} + +CVec2 *Norm2(CVec2 *a) +{ + F64 length = Sqrt(Sqr(a->x) + Sqr(a->y)); + a->x /= length; + a->y /= length; + return a; +} + +// Create direction Vec4 from Vec3 +CVec4 *Vec4Dir(CVec3 *a) +{ + CVec4 *b = MAlloc(sizeof(CVec4)); + MemCopy(b, a, 24); + b->w = 0; + return b; +} + +// Create position Vec4 from Vec3 +CVec4 *Vec4Pos(CVec3 *a) +{ + CVec4 *b = MAlloc(sizeof(CVec4)); + MemCopy(b, a, 24); + b->w = 1; + return b; +} + +// Create Vec3 from Vec4 +CVec3 *Vec3Pos(CVec4 *a) +{ + CVec3 *b = MAlloc(sizeof(CVec3)); + MemCopy(b, a, 24); + return b; +} + +CVec4 *MulM44(CMat4 *a, CVec4 *b) +{ + CVec4 *c = MAlloc(sizeof(CVec4)); + c->x = (a->e[0][0] * b->x) + (a->e[0][1] * b->y) + (a->e[0][2] * b->z) + (a->e[0][3] * b->w); + c->y = (a->e[1][0] * b->x) + (a->e[1][1] * b->y) + (a->e[1][2] * b->z) + (a->e[1][3] * b->w); + c->z = (a->e[2][0] * b->x) + (a->e[2][1] * b->y) + (a->e[2][2] * b->z) + (a->e[2][3] * b->w); + c->w = (a->e[3][0] * b->x) + (a->e[3][1] * b->y) + (a->e[3][2] * b->z) + (a->e[3][3] * b->w); + return c; +} + +CMat4 *MulM4M4(CMat4 *a, CMat4 *b) +{ + CMat4 *c = MAlloc(sizeof(CMat4)); + c->e[0][0] = (a->e[0][0] * b->e[0][0])+(a->e[0][1] * b->e[1][0])+(a->e[0][2] * b->e[2][0])+(a->e[0][3] * b->e[3][0]); + c->e[0][1] = (a->e[0][0] * b->e[0][1])+(a->e[0][1] * b->e[1][1])+(a->e[0][2] * b->e[2][1])+(a->e[0][3] * b->e[3][1]); + c->e[0][2] = (a->e[0][0] * b->e[0][2])+(a->e[0][1] * b->e[1][2])+(a->e[0][2] * b->e[2][2])+(a->e[0][3] * b->e[3][2]); + c->e[0][3] = (a->e[0][0] * b->e[0][3])+(a->e[0][1] * b->e[1][3])+(a->e[0][2] * b->e[2][3])+(a->e[0][3] * b->e[3][3]); + + c->e[1][0] = (a->e[1][0] * b->e[0][0])+(a->e[1][1] * b->e[1][0])+(a->e[1][2] * b->e[2][0])+(a->e[1][3] * b->e[3][0]); + c->e[1][1] = (a->e[1][0] * b->e[0][1])+(a->e[1][1] * b->e[1][1])+(a->e[1][2] * b->e[2][1])+(a->e[1][3] * b->e[3][1]); + c->e[1][2] = (a->e[1][0] * b->e[0][2])+(a->e[1][1] * b->e[1][2])+(a->e[1][2] * b->e[2][2])+(a->e[1][3] * b->e[3][2]); + c->e[1][3] = (a->e[1][0] * b->e[0][3])+(a->e[1][1] * b->e[1][3])+(a->e[1][2] * b->e[2][3])+(a->e[1][3] * b->e[3][3]); + + c->e[2][0] = (a->e[2][0] * b->e[0][0])+(a->e[2][1] * b->e[1][0])+(a->e[2][2] * b->e[2][0])+(a->e[2][3] * b->e[3][0]); + c->e[2][1] = (a->e[2][0] * b->e[0][1])+(a->e[2][1] * b->e[1][1])+(a->e[2][2] * b->e[2][1])+(a->e[2][3] * b->e[3][1]); + c->e[2][2] = (a->e[2][0] * b->e[0][2])+(a->e[2][1] * b->e[1][2])+(a->e[2][2] * b->e[2][2])+(a->e[2][3] * b->e[3][2]); + c->e[2][3] = (a->e[2][0] * b->e[0][3])+(a->e[2][1] * b->e[1][3])+(a->e[2][2] * b->e[2][3])+(a->e[2][3] * b->e[3][3]); + + c->e[3][0] = (a->e[3][0] * b->e[0][0])+(a->e[3][1] * b->e[1][0])+(a->e[3][2] * b->e[2][0])+(a->e[3][3] * b->e[3][0]); + c->e[3][1] = (a->e[3][0] * b->e[0][1])+(a->e[3][1] * b->e[1][1])+(a->e[3][2] * b->e[2][1])+(a->e[3][3] * b->e[3][1]); + c->e[3][2] = (a->e[3][0] * b->e[0][2])+(a->e[3][1] * b->e[1][2])+(a->e[3][2] * b->e[2][2])+(a->e[3][3] * b->e[3][2]); + c->e[3][3] = (a->e[3][0] * b->e[0][3])+(a->e[3][1] * b->e[1][3])+(a->e[3][2] * b->e[2][3])+(a->e[3][3] * b->e[3][3]); + return c; +} + +// Normalized Vec4 to RGBA +U0 Vec4Col(CVec4 *col, CBGR24 *bgrcol) +{ + bgrcol->r = (col->x * 255.0); + bgrcol->g = (col->y * 255.0); + bgrcol->b = (col->z * 255.0); + bgrcol->pad = (col->w * 255.0); +} + +// Normalized Vec3 to RGBA +U0 Vec3Col(CVec3 *col, CBGR24 *bgrcol) +{ + bgrcol->r = (col->x * 255.0); + bgrcol->g = (col->y * 255.0); + bgrcol->b = (col->z * 255.0); + bgrcol->pad = 255.0; +} + +U0 Barycentric2(CVec2 *p, CVec2 *a, CVec2 *b, CVec2 *c, F64 *u, F64 *v, F64 *w) +{ + CVec2 *v0 = Sub2(b, a); + CVec2 *v1 = Sub2(c, a); + CVec2 *v2 = Sub2(p, a); + + F64 d00 = Dot2(v0, v0); + F64 d01 = Dot2(v0, v1); + F64 d11 = Dot2(v1, v1); + F64 d20 = Dot2(v2, v0); + F64 d21 = Dot2(v2, v1); + F64 denom = d00 * d11 - d01 * d01; + *v = (d11 * d20 - d01 * d21)/denom; + *w = (d00 * d21 - d01 * d20)/denom; + *u = 1.0 - *v - *w; + + Free(v0); + Free(v1); + Free(v2); +} + +CVec2 Ayy,Bee,Cee, P; +Ayy.x = 0; +Ayy.y = 0; +Bee.x = 3; +Bee.y = 0; +Cee.x = 0; +Cee.y = 3; + +U0 SetTranslateM4(CMat4 *mat, CVec3 *translation) +{ + MemSet(mat, 0, sizeof(CMat4)); + mat->e[0][0] = 1; + mat->e[1][1] = 1; + mat->e[2][2] = 1; + mat->e[3][3] = 1; + mat->e[0][3] = translation->x; + mat->e[1][3] = translation->y; + mat->e[2][3] = translation->z; +} + +U0 SetScaleM4(CMat4 *mat, CVec3 *scale) +{ + MemSet(mat, 0, sizeof(CMat4)); + mat->e[0][0] = scale->x; + mat->e[1][1] = scale->y; + mat->e[2][2] = scale->z; + mat->e[3][3] = 1; +} + +U0 SetRotXM4(CMat4 *mat, F64 rot) +{ + MemSet(mat, 0, sizeof(CMat4)); + mat->e[0][0] = 1; + mat->e[3][3] = 1; + F64 x = Cos(rot); + F64 y = Sin(rot); + mat->e[1][1] = x; + mat->e[1][2] = -y; + mat->e[2][1] = y; + mat->e[2][2] = x; +} + +U0 SetRotYM4(CMat4 *mat, F64 rot) +{ + MemSet(mat, 0, sizeof(CMat4)); + mat->e[1][1] = 1; + mat->e[3][3] = 1; + F64 x = Cos(rot); + F64 y = Sin(rot); + mat->e[0][0] = x; + mat->e[0][2] = y; + mat->e[2][0] = -y; + mat->e[2][2] = x; +} + +U0 SetRotZM4(CMat4 *mat, F64 rot) +{ + MemSet(mat, 0, sizeof(CMat4)); + mat->e[2][2] = 1; + mat->e[3][3] = 1; + F64 x = Cos(rot); + F64 y = Sin(rot); + mat->e[0][0] = x; + mat->e[0][1] = -y; + mat->e[1][0] = y; + mat->e[1][1] = x; +} + +U0 TranslateM4(CMat4 *mat, CVec3 *translation) +{ + // Generate CMat4 translation matrix + CMat4 *translateMat = MAlloc(sizeof(CMat4)); + SetTranslateM4(translateMat, translation); + + + CMat4 *finalMat = MulM4M4(mat, translateMat); + MemCopy(mat, finalMat, sizeof(CMat4)); + Free(finalMat); + Free(translateMat); +} + +U0 F32toF64(U32 *float, U64 *double) +{ + MemCopy(&double->u32[1], float, 4); + + // Isolate parts of F32 + U64 MASK_SIGN = 0b1000000000000000000000000000000000000000000000000000000000000000; + U64 MASK_EXP = 0b0111111110000000000000000000000000000000000000000000000000000000; + U64 MASK_MANT = 0b0000000001111111111111111111111100000000000000000000000000000000; + + U64 EXP = *double & MASK_EXP; + U64 MANT = *double & MASK_MANT; + + // Clear original F64 so that the masked values can be OR'd in + *double = *double & MASK_SIGN; + + // Exponent of F64 is 11 bits instead of 8, shift mantissa by 3 and OR it back in + MANT = MANT >> 3; + *double = *double | MANT; + + // Shift exponent to regular int format + // F32: [1 bit sign][8 bit exponent][23 bit mantissa] + EXP = EXP >> 55; // 23 + 32 to get it in regular int format + + if (EXP != 0) + { + EXP += 896; // -127, +1023 + EXP = EXP << 52; + } + *double = *double | EXP; +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Zenith/CosmicGL/Math/Types.CC b/src/Zenith/CosmicGL/Math/Types.CC new file mode 100644 index 00000000..56170e13 --- /dev/null +++ b/src/Zenith/CosmicGL/Math/Types.CC @@ -0,0 +1,29 @@ +class CVec2 +{ + F64 x, y; +}; + +class CIVec2 +{ + I64 x, y; +}; + +class CVec3 +{ + F64 x, y, z; +}; + +class CIVec3 +{ + I64 x, y, z; +}; + +class CVec4 +{ + F64 x, y, z, w; +}; + +class CIVec4 +{ + I64 x, y, z, w; +}; diff --git a/src/Zenith/CosmicGL/Math/Vec2.CC b/src/Zenith/CosmicGL/Math/Vec2.CC new file mode 100644 index 00000000..36826a46 --- /dev/null +++ b/src/Zenith/CosmicGL/Math/Vec2.CC @@ -0,0 +1,318 @@ +/* Initialize CVec2 from F64 pointer. */ +U0 Vec2(F64 *a, CVec2 *o) +{ + o->x = a[0]; + o->y = a[1]; +} + +/* Copy CVec2 into CVec2. */ +U0 Copy2(CVec2 *a, CVec2 *o) +{ + o->x = a->x; + o->y = a->y; +} + +/* Fill CVec2 with F64 */ +U0 Fill2S(CVec2 *v, F64 val) +{ + v->x = v->y = val; +} + +/* Fill CIVec2 with I64 */ +U0 FillI2S(CIVec2 *v, I64 val) +{ + v->x = v->y = val; +} + +/* Clear CVec2 with zeros. */ +U0 Zero2(CVec2 *v) +{ + v->x = v->y = 0.0; +} + +/* Clear CVec2 with ones. */ +U0 One2(CVec2 *v) +{ + v->x = v->y = 1.0; +} + +/* Dot product of two CVec2. */ +U0 Dot2(CVec2 *a, CVec2 *b, F64 *o) +{ + *o = (a->x * b->x) + (a->y * b->y); +} + +/* Z component of cross product of two CVec2. */ +U0 Cross2(CVec2 *a, CVec2 *b, F64 *o) +{ + *o = (a->x * b->y) - (a->y * b->x); +} + +/* Norm^2 (magnitude^2) of CVec2. */ +U0 NormSqr2(CVec2 *a, F64 *o) +{ + *o = Sqr(a->x) + Sqr(a->y); +} + +/* Norm (magnitude) of CVec2. */ +U0 Norm2(CVec2 *a, F64 *o) +{ + *o = Sqrt(Sqr(v->x) + Sqr(v->y)); +} + +/* Add CVec2 to CVec2. */ +U0 Add2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = a->x + b->x; + o->y = a->y + b->y; +} + +/* Add CVec2 to F64. */ +U0 Add2S(CVec2 *a, F64 b, CVec2 *o) +{ + o->x = a->x + b; + o->y = a->y + b; +} + +/* Sub CVec2 from CVec2. */ +U0 Sub2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = a->x - b->x; + o->y = a->y - b->y; +} + +/* Sub CVec2 from F64. */ +U0 Sub2S(CVec2 *a, F64 b, CVec2 *o) +{ + o->x = a->x - b; + o->y = a->y - b; +} + +/* Multiply CVec2 with CVec2. */ +U0 Mul2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = a->x * b->x; + o->y = a->y * b->y; +} + +/* Scale CVec2 by F64. */ +U0 Scale2S(CVec2 *a, F64 b, CVec2 *o) +{ + o->x = a->x * b; + o->y = a->y * b; +} + +/* Scale CVec2 unit vector by F64. */ +U0 ScaleUnit2(CVec2 *a, F64 b, CVec2 *o) +{ + // Norm2(a, &norm) + F64 norm = Sqrt(Sqr(a->x) + Sqr(a->y)); + + if (norm == 0.0) + { + // Zero2(a) + a->x = a->y = 0.0; + return; + } + + // Scale2(a, b / norm, o); + F64 scale = b / norm; + o->x = a->x * scale; + o->y = a->y * scale; +} + +/* Divide CVec2 by CVec2. */ +U0 Div2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = a->x / b->x; + o->y = b->y / b->y; +} + +/* Divide CVec2 by F64. */ +U0 Div2S(CVec2 *a, F64 b, CVec2 *o) +{ + o->x = a->x / b; + o->y = a->y / b; +} + +/* Square root of CVec2 components. */ +U0 Sqrt2(CVec2 *a, CVec2 *o) +{ + o->x = Sqrt(a->x); + o->y = Sqrt(a->y); +} + +/* Sign of components as -1, 0, or 1. */ +U0 Sign2(CVec2 *a, CVec2 *o) +{ + o->x = ((o->x > 0.0) - (val < 0.0)); + o->y = ((o->y > 0.0) - (val < 0.0)); +} + +/* Add CVec2 to CVec2 to output CVec2. */ +U0 AddAdd2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x += a->x + b->x; + o->y += a->y + b->y; +} + +/* Subtract CVec2 from CVec2, add to output CVec2. */ +U0 SubAdd2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x += a->x - b->x; + o->y += a->y - b->y; +} + +/* Multiply CVec2 by CVec2, add to output CVec2. */ +U0 MulAdd2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x += a->x * b->x; + o->y += a->y * b->y; +} +/* Multiply CVec2 by F64, add to output CVec2. */ +U0 MulAdd2S(CVec2 *a, F64 b, CVec2 *o) +{ + o->x += a->x * b; + o->y += a->y * b; +} + +/* Add maximum of each CVec2 component to output CVec2. */ +U0 MaxAdd2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x += Max(a->x, b->x); + o->y += Max(a->y, b->y); +} + +/* Add minimum of each CVec2 component to output CVec2. */ +U0 MinAdd2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x += Min(a->x, b->x); + o->y += Min(a->y, b->y); +} + +/* Negate vector components (*-1) to output CVec2. */ +U0 NegateTo2(CVec2 *a, CVec2 *o) +{ + o->x = -a->x; + o->y = -a->y; +} + +/* Negate vector components (*-1). */ +U0 Negate2(CVec2 *v) +{ + v->x = -v->x; + v->y = -v->y; +} + +/* Normalize CVec2 to output CVec2. */ +U0 NormalizeTo2(CVec2 *a, CVec2 *o) +{ + // Norm2(a, &norm) + F64 norm = Sqrt(Sqr(a->x) + Sqr(a->y)); + + if (norm == 0.0) + { + // Zero2(o) + o->x = o->y = 0.0; + return; + } + + // Scale2(a, 1.0 / norm, o) + F64 scale = 1.0 / norm; + o->x = a->x * scale; + o->y = a->y * scale; +} + +/* Normalize CVec2. */ +U0 Normalize2(CVec2 *v) +{ + // Norm2(v, &norm) + F64 norm = Sqrt(Sqr(v->x) + Sqr(v->y)); + + if (norm == 0.0) + { + // Zero2(v) + v->x = v->y = 0.0; + return; + } + + // Scale2(v, 1.0 / norm, v) + F64 scale = 1.0 / norm; + v->x = v->x * scale; + v->y = v->y * scale; +} + +/* Rotate CVec2 by angle (F64) to output CVec2. */ +U0 Rotate2(CVec2 *a, F64 b, CVec2 *o) +{ + F64 c, s; + + c = Cos(b); + s = Sin(b); + + o->x = (c * a->x) - (s * a->y); + o->y = (s * a->x) - (c * a->y); +} + +/* Squared distance between two CVec2. */ +U0 DistanceSqr2(CVec2 *a, CVec2 *b, F64 *o) +{ + *o = Sqr(b->x - b->y) + Sqr(b->y - a->y); +} + +/* Distance between two CVec2. */ +U0 Distance2(CVec2 *a, CVec2 *b, F64 *o) +{ + *o = Sqrt(Sqr(b->x - b->y) + Sqr(b->y - a->y)); +} + +/* Minimum components of two CVec2. */ +U0 MinVec2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = Min(a->x, b->x); + o->y = Min(a->y, b->y); +} + +/* Maximum components of two CVec2. */ +U0 MaxVec2(CVec2 *a, CVec2 *b, CVec2 *o) +{ + o->x = Max(a->x, b->x); + o->y = Max(a->y, b->y); +} + +/* Minimum component of CVec2. */ +U0 Min2(CVec2 *a, F64 *o) +{ + *o = Min(a->x, a->y); +} + +/* Maximum component of CVec2. */ +U0 Max2(CVec2 *a, F64 *o) +{ + *o = Max(a->x, a->y); +} + +/* Clamp a CVec2 to min and max values. */ +U0 Clamp2(CVec2 *v, F64 minval, F64 maxval) +{ + v->x = Clamp(v->x, minval, maxval); + v->y = Clamp(v->y, minval, maxval); +} + +/* Linear interpolation between two CVec2. */ +U0 Lerp2(CVec2 *a, CVec2 *b, F64 t, CVec2 *o) +{ + CVec2 s, v; + + s.x = s.y = Clamp(t, 0.0, 1.0); + // Sub2(b, a, &v) + v.x = b->x - a->x; + v.y = b->y - a->y; + // Mul2(s, v, v) + v.x *= s.x; + v.y *= s.y; + // Add(a, &v, o) + o->x = a->x + v.x; + o->y = a->y + v.y; +} + diff --git a/src/Zenith/CosmicGL/Math/Vec3.CC b/src/Zenith/CosmicGL/Math/Vec3.CC new file mode 100644 index 00000000..07b3168a --- /dev/null +++ b/src/Zenith/CosmicGL/Math/Vec3.CC @@ -0,0 +1,429 @@ +/* Initialize CVec3 from F64 pointer. */ +U0 Vec3(F64 *a, CVec3 *o) +{ + o->x = a[0]; + o->y = a[1]; + o->z = a[2]; +} + +/* Copy CVec3 into CVec3. */ +U0 Copy3(CVec3 *a, CVec3 *o) +{ + o->x = a->x; + o->y = a->y; + o->z = a->z; +} + +/* Fill CVec3 with F64 */ +U0 Fill3S(CVec3 *v, F64 val) +{ + v->x = v->y = v->z = val; +} + +/* Fill CIVec3 with I64 */ +U0 FillI3S(CIVec3 *v, I64 val) +{ + v->x = v->y = v->z = val; +} + +/* Clear CVec2 with zeros. */ +U0 Zero3(CVec3 *v) +{ + v->x = v->y = v->z = 0.0; +} + +/* Clear CVec3 with ones. */ +U0 One3(CVec3 *v) +{ + v->x = v->y = v->z = 1.0; +} + +/* Dot product of two CVec3. */ +U0 Dot3(CVec3 *a, CVec3 *b, F64 *o) +{ + *o = (a->x * b->x) + (a->y * b->y) + (a->z * b->z); +} + +/* Norm^2 (magnitude^2) of CVec3. */ +U0 NormSqr3(CVec2 *a, F64 *o) +{ + *o = Sqr(a->x) + Sqr(a->y) + Sqr(a->z); +} + +/* Norm (magnitude) of CVec3. */ +U0 Norm3(CVec3 *a, F64 *o) +{ + *o = Sqrt(Sqr(v->x) + Sqr(v->y) + Sqr(v->z)); +} + +/* Manhattan Distance/Taxicab Norm, sum of component magnitudes of CVec3. */ +U0 ManhattanNorm3(CVec3 *a, F64 *o) +{ + *o = Abs(a->x) + Abs(a->y) + Abs(a->z); +} + +/* Maximum norm (magnitude), largest magnitude of all CVec3 components. */ +U0 MaxNorm3(CVec3 *a, F64 *o) +{ + *o = Max(Max(Abs(a->x), Abs(a->y)), Abs(a->z)); +} + +/* Add CVec3 to CVec3. */ +U0 Add3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x = a->x + b->x; + o->y = a->y + b->y; + o->z = a->z + b->z; +} + +/* Add CVec3 to F64. */ +U0 Add3S(CVec3 *a, F64 b, CVec3 *o) +{ + o->x = a->x + b; + o->y = a->y + b; + o->z = a->z + b; +} + +/* Sub CVec3 from CVec3. */ +U0 Sub3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x = a->x - b->x; + o->y = a->y - b->y; + o->z = a->z - b->z; +} + +/* Sub CVec3 from F64. */ +U0 Sub3S(CVec3 *a, F64 b, CVec3 *o) +{ + o->x = a->x - b; + o->y = a->y - b; + o->z = a->z - b; +} + +/* Multiply CVec3 with CVec3. */ +U0 Mul3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x = a->x * b->x; + o->y = a->y * b->y; + o->z = a->z * b->z; +} + +/* Scale CVec3 by F64. */ +U0 Scale3S(CVec3 *a, F64 b, CVec3 *o) +{ + o->x = a->x * b; + o->y = a->y * b; + o->z = a->z * b; +} + +/* Scale CVec3 unit vector by F64. */ +U0 ScaleUnit3(CVec3 *a, F64 b, CVec3 *o) +{ + // Norm3(a, &norm) + F64 norm = Sqrt(Sqr(a->x) + Sqr(a->y) + Sqr(a->z)); + + if (norm == 0.0) + { + // Zero3(a) + a->x = 0.0; + a->y = 0.0; + a->z = 0.0; + return; + } + + // Scale3(a, b / norm, o); + F64 scale = b / norm; + o->x = a->x * scale; + o->y = a->y * scale; + o->z = a->z * scale; +} + +/* Divide CVec3 by CVec3. */ +U0 Div3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x = a->x / b->x; + o->y = b->y / b->y; + o->z = b->z / b->z; +} + +/* Divide CVec3 by F64. */ +U0 Div3S(CVec3 *a, F64 b, CVec3 *o) +{ + o->x = a->x / b; + o->y = a->y / b; + o->z = a->z / b; +} + +/* Square root of CVec3 components. */ +U0 Sqrt3(CVec3 *a, CVec3 *o) +{ + o->x = Sqrt(a->x); + o->y = Sqrt(a->y); + o->z = Sqrt(a->z); +} + +/* Sign of components as -1, 0, or 1. */ +U0 Sign3(CVec3 *a, CVec3 *o) +{ + o->x = ((o->x > 0.0) - (val < 0.0)); + o->y = ((o->y > 0.0) - (val < 0.0)); + o->z = ((o->z > 0.0) - (val < 0.0)); +} + +/* Add CVec3 to CVec3 to output CVec3. */ +U0 AddAdd3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x += a->x + b->x; + o->y += a->y + b->y; + o->z += a->z + b->z; +} + +/* Subtract CVec3 from CVec3, add to output CVec3. */ +U0 SubAdd3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x += a->x - b->x; + o->y += a->y - b->y; + o->z += a->z - b->z; +} + +/* Multiply CVec3 by CVec3, add to output CVec3. */ +U0 MulAdd2(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x += a->x * b->x; + o->y += a->y * b->y; + o->z += a->z * b->z; +} +/* Multiply CVec3 by F64, add to output CVec3. */ +U0 MulAdd3S(CVec3 *a, F64 b, CVec3 *o) +{ + o->x += a->x * b; + o->y += a->y * b; + o->z += a->z * b; +} + +/* Add maximum of each CVec3 component to output CVec3. */ +U0 MaxAdd3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x += Max(a->x, b->x); + o->y += Max(a->y, b->y); + o->z += Max(a->z, b->z); +} + +/* Add minimum of each CVec3 component to output CVec3. */ +U0 MinAdd2(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x += Min(a->x, b->x); + o->y += Min(a->y, b->y); + o->z == Min(a->z, b->z); +} + +/* Negate vector components (*-1) to output CVec3. */ +U0 NegateTo2(CVec3 *a, CVec3 *o) +{ + o->x = -a->x; + o->y = -a->y; + o->z = -a->z; +} + +/* Negate vector components (*-1). */ +U0 Negate2(CVec3 *v) +{ + v->x = -v->x; + v->y = -v->y; + v->z = -v->z; +} + +/* Normalize CVec3 to output CVec3. */ +U0 NormalizeTo3(CVec3 *a, CVec3 *o) +{ + // Norm3(a, &norm) + F64 norm = Sqrt(Sqr(a->x) + Sqr(a->y) + Sqr(a->z)); + + if (norm == 0.0) + { + // Zero3(o) + o->x = 0.0; + o->y = 0.0; + o->z = 0.0; + return; + } + + // Scale3(a, 1.0 / norm, o) + F64 scale = 1.0 / norm; + o->x = a->x * scale; + o->y = a->y * scale; + o->z = a->z * scale; +} + +/* Normalize CVec3. */ +U0 Normalize3(CVec3 *v) +{ + // Norm3(v, &norm) + F64 norm = Sqrt(Sqr(v->x) + Sqr(v->y) + Sqr(v->z)); + + if (norm == 0.0) + { + // Zero2(v) + v->x = 0.0; + v->y = 0.0; + v->z = 0.0; + return; + } + + // Scale3(v, 1.0 / norm, v) + F64 scale = 1.0 / norm; + v->x = v->x * scale; + v->y = v->y * scale; + v->z = v->z * scale; +} + +/* Cross product of two CVec3. */ +U0 Cross3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + o->x = (a->y * b->z) - (a->z * b->y); + o->y = (a->z * b->x) - (a->x * b->z); + o->z = (a->x * b->y) - (a->y * b->x); +} + +/* Cross product then normalize two CVec3. */ +U0 CrossNormalize3(CVec3 *a, CVec3 *b, CVec3 *o) +{ + // Cross3(a, b, o) + o->x = (a->y * b->z) - (a->z * b->y); + o->y = (a->z * b->x) - (a->x * b->z); + o->z = (a->x * b->y) - (a->y * b->x); + // Normalize3(o) + // Norm3(o, &norm) + F64 norm = Sqrt(Sqr(o->x) + Sqr(o->y) + Sqr(o->z)); + + if (norm == 0.0) + { + // Zero2(o) + o->x = o->y = o->z = 0.0; + return; + } + + // Scale3(o, 1.0 / norm, o) + F64 scale = 1.0 / norm; + o->x = o->x * scale; + o->y = o->y * scale; + o->z = o->z * scale; +} + +/* Angle between two vectors. */ +U0 Angle3(CVec3 *a, CVec3 *b, F64 *o) +{ + F64 norm, normA, normB, dot; + + // Norm3(a, &normA) + normA = Sqrt(Sqr(a->x) + Sqr(a->y) + Sqr(a->z)); + // Norm3(b, &normB) + normB = Sqrt(Sqr(b->x) + Sqr(v->y) + Sqr(b->z)); + norm = 1.0 / (normA * normB); + // Dot(a, b, &dot) + dot = (a->x * b->x) + (a->y * b->y) + (a->z * b->z); + dot *= norm; + + if (dot > 1.0) + { + *o = 0.0; + return; + } + else if (dot < -1.0) + { + *o = Pi; + return; + } + + *o = ArcCos(dot); +} + +/* Rotate CVec3 by angle (F64) through CVec3 axis. */ +U0 Rotate3(CVec3 *a, F64 angle, CVec3 *axis, CVec3 *o) +{ + CVec3 v1, v2, k; + F64 c, s, temp; + + c = Cos(angle); + s = Sin(angle); + + // NormalizeTo3(axis, &k); + // Norm3(axis, &temp) + temp = Sqrt(Sqr(axis->x) + Sqr(axis->y) + Sqr(axis->z)); + + if (temp == 0.0) + { + // Zero3(&k) + k.x = k.y = k.z = 0.0; + return; + } + + // Scale3(axis, 1.0 / temp, &k) + F64 scale = 1.0 / temp; + k.x = axis->x * scale; + k.y = axis->y * scale; + k.z = axis->z * scale; + + // Right Hand, Rodrigues' rotation formula: + // v = v*Cos(t) + (kxv)sin(t) + k*(k.v)(1-cos(t)) + + // Scale3(a, c, &v1) + v1.x = a->x * c; + v1.y = a->y * c; + v1.z = a->z * c; + + // Cross3(&k, a, v2) + v2.x = (k.y * a->z) - (k.z * a->y); + v2.y = (k.z * a->x) - (k.x * a->z); + v2.z = (k.x * a->y) - (k.y * a->x); + + // Scale3(&v1, s, &v2) + v2.x = v1.x * s; + v2.y = v1.y * s; + v2.z = v1.z * s; + + // Add3(&v1, &v2, &v1) + v1.x += v2.x; + v1.y += v2.y; + v1.z += v2.z; + + // Dot3(&k, &v, &temp) + temp = (k.x * v.x) + (k.y * v.y) + (k.z * v.z); + + // Scale3(&k, temp * (1.0 - c), &v2) + temp *= (1.0 - c); + v2.x = k.x * temp; + v2.y = k.y * temp; + v2.z = k.z * temp; + + // Add3(&v1, &v2, o) + o->x = v1.x + v2.x; + o->y = v1.y + v2.y; + o->z = v1.z + v2.z; +} + +/* Apply Mat4 rotation matrix to CVec3. */ +Rotate3M4(CVec3 *a, F64 *m, CVec3 *o) +{ + CVec4 x, y, z, res; + F64 norm, scale; + + // NormalizeTo4(&m[0], &x) + norm = Sqrt(Sqr(m[0][0]) + Sqr(m[0][1]) + Sqr(m[0][2]) + Sqr(m[0][3])); + + if (norm == 0.0) + { + // Zero4(&x) + x.x = x.y = x.z = x.w = 0.0; + return; + } + + // Scale3(v, 1.0 / norm, v) + F64 scale = 1.0 / norm; + v->x = v->x * scale; + v->y = v->y * scale; + v->z = v->z * scale; + // TODO: FINISH!!!! +} + + diff --git a/src/Zenith/CosmicGL/Render/Rasterize.CC b/src/Zenith/CosmicGL/Render/Rasterize.CC new file mode 100644 index 00000000..27f7a4a7 --- /dev/null +++ b/src/Zenith/CosmicGL/Render/Rasterize.CC @@ -0,0 +1,323 @@ +/** + @ingroup Render + @brief Renders triangles to texture. + + @param[in] shd Shader. + @param[in,out] frameBuf Color texture to render to. + @param[in,out] depthBuf Depth texture to render to and check against. + @param[in] mdl Model buffer, shader defines how to read this. + @param[in] uniforms Buffer of unchanging values for shader. + @param[in] nTris Number of triangles to render. +*/ +U0 RenderTris(CShader *shd, CTex2D *frameBuf, CTex2D *depthBuf, U8 *mdl, + U8 *uniforms, I64 nTris) +{ + Bool middle_line_drawn = FALSE; + F64 x_delta; + F64 y_delta; + F64 left_delta; + F64 right_delta; + F64 left_x; + F64 right_x; + I64 y, lX, rX; + I64 left_index, right_index; + I64 width; + + I64 i, j, temp; + F64 ftemp; + F64 z; // Interpolated value to depth check + + // Stores baryocentric coordinates for current fragment + F64 u, v, w; + F64 bc[3]; + + // Vertex coordinates. P is the point to get baryocentric coordinate for + CVec2 p, a, b, c; + + CBGR24 fragColor; // Final color to write to fragment + + // Calculate dimensions and allocate vertex attributes buffer + // to pass interpolated per-frag values to the fragment + // shader from the vertex shader: + + F64 *vOut; // This would be like Out from GLSL, it is 3x the size of fIn (3 vertices) + F64 *fIn; // This would be like In from GLSL + + I64 attrBufSize = 0; + for (i = 0; i < shd->nVertValues; i++) + { + switch (shd->vertValues[i]) + { + case SHD_F64: + attrBufSize += 1; + break; + + case SHD_CVEC2: + attrBufSize += 2; + break; + + case SHD_CVEC3: + attrBufSize += 3; + break; + + case SHD_CMat4: + attrBufSize += 16; + break; + } + } + vOut = MAlloc(attrBufSize * sizeof(F64) * 3); // 3x for 3 vertexes + fIn = MAlloc(attrBufSize * sizeof(F64)); // Single set of interpolated values + + CTri cTri; // Current Triangle in NDC + CIVec2 cTriPx[3]; // Current Triangle in pixels + I64 tri; // Current Triangle Index + I64 VI[3]; // Vertex Indices + + for (tri = 0; tri < nTris; tri++) + { + // Get current triangle in NDC from vertex shader + shd->VertShd(&cTri, vOut, mdl, uniforms, tri, nTris); + + ftemp = 0; + ftemp += Abs(cTri.p[0].x); + ftemp += Abs(cTri.p[0].y); + ftemp += Abs(cTri.p[1].x); + ftemp += Abs(cTri.p[1].y); + ftemp += Abs(cTri.p[2].x); + ftemp += Abs(cTri.p[2].y); + + // Triangle is not outside NDC range, it is on screen + //if (ftemp =< 6) + if (1 == 1) + { + + // This triangle may not be in the correct order. + // y0 should be lowest in texture coordinates, then y1 and y2. + // This means y0 should be the highest in NDC coordinates. + + // In VI (Vertex Indices), the array index is the index of the final point, + // and it's value is the point from the original tri it get's it's + // coordinates from. + VI[0] = 0; + VI[1] = 1; + VI[2] = 2; + + if (cTri.p[VI[0]].y < cTri.p[VI[1]].y) + { + temp = VI[0]; + VI[0] = VI[1]; + VI[1] = temp; + } + + if (cTri.p[VI[0]].y < cTri.p[VI[2]].y) + { + temp = VI[0]; + VI[0] = VI[2]; + VI[2] = temp; + } + + if (cTri.p[VI[1]].y < cTri.p[VI[2]].y) + { + temp = VI[1]; + VI[1] = VI[2]; + VI[2] = temp; + } + + // By now, VI[0] points to the point with the lowest + // y in texture coordinates, and VI[2] points to the highest + + // Convert NDC coordinates into tex coordinates for rasterization process + cTriPx[0].x = ToI64(((cTri.p[VI[0]].x * 0.5) + 0.5) * ToF64(frameBuf->w)); + cTriPx[0].y = ToI64(((-cTri.p[VI[0]].y * 0.5) + 0.5) * ToF64(frameBuf->h)); + cTriPx[1].x = ToI64(((cTri.p[VI[1]].x * 0.5) + 0.5) * ToF64(frameBuf->w)); + cTriPx[1].y = ToI64(((-cTri.p[VI[1]].y * 0.5) + 0.5) * ToF64(frameBuf->h)); + cTriPx[2].x = ToI64(((cTri.p[VI[2]].x * 0.5) + 0.5) * ToF64(frameBuf->w)); + cTriPx[2].y = ToI64(((-cTri.p[VI[2]].y * 0.5) + 0.5) * ToF64(frameBuf->h)); + + + // Prepare F64 triangle point vectors for calculating barycentric coords later + //Userd to use VI[0] + a.x = cTriPx[0].x; + a.y = cTriPx[0].y; + b.x = cTriPx[1].x; + b.y = cTriPx[1].y; + c.x = cTriPx[2].x; + c.y = cTriPx[2].y; + + // Rasterize Bottom Half (Tex coordinates, not NDC) + if (cTriPx[0].y != cTriPx[1].y) + { + x_delta = (cTriPx[1].x - cTriPx[0].x); + y_delta = (cTriPx[1].y - cTriPx[0].y); + left_delta = x_delta / y_delta; + x_delta = (cTriPx[2].x - cTriPx[0].x); + y_delta = (cTriPx[2].y - cTriPx[0].y); + right_delta = x_delta / y_delta; + + if (left_delta > right_delta) + { + ftemp = left_delta; + left_delta = right_delta; + right_delta = ftemp; + } + + left_x = cTriPx[0].x; + right_x = cTriPx[0].x; + + middle_line_drawn = TRUE; + + for (y = cTriPx[0].y; y < (cTriPx[1].y + 1); y++) + { + lX = left_x + 0.5; + rX = right_x + 0.5; + left_index = y * frameBuf->w + lX; + right_index = y * frameBuf->w + rX; + + // Temporary fix, the y should just be clipped and left_x/right_x adjusted accordingly + if (TRUE) + + { + // Check if x coordinate is off screen and clip it. + if (lX < 0) + { + lX = 0; + } + if (lX > frameBuf->w) + { + lX = frameBuf->w; + } + if (rX > frameBuf->w) + { + rX = frameBuf->w; + } + if (rX < 0) + { + rX = 0; + } + + // For every pixel along the line, set it's color + for (i = lX; i < rX; i++) + { + // Get barycentric coordinates (u, v, w) + p.x = i; + p.y = y; + Barycentric2(&p, &a, &b, &c, &bc[VI[0]], &bc[VI[1]], &bc[VI[2]]); + + z = (bc[VI[0]] * cTri.p[VI[0]].z) + (bc[VI[1]] * cTri.p[VI[1]].z) + (bc[VI[2]] * cTri.p[VI[2]].z); + + if (z <= depthBuf->depthBuf[y * frameBuf->w + i]) + { + // Interpolate all vOut values to fIn values for the fragment shader + for (j = 0; j < attrBufSize; j++) + { + fIn[j] = (vOut[j] * bc[0]) + (vOut[j+attrBufSize] * bc[1]) + (vOut[j+(2*attrBufSize)] * bc[2]); + } + + depthBuf->depthBuf[y * frameBuf->w + i] = z; + shd->FragShd(&fragColor, fIn, uniforms); + left_index = y * frameBuf->w + i; + MemSetU32(frameBuf->rawBuf + left_index, fragColor, 1); + } + } + } + left_x += left_delta; + right_x += right_delta; + } + } + + // Rasterize Top Half (Tex coordinates, not NDC) + if (cTriPx[1].y != cTriPx[2].y) + { + x_delta = -(cTriPx[1].x - cTriPx[2].x); + y_delta = (cTriPx[1].y - cTriPx[2].y); + left_delta = x_delta / y_delta; + x_delta = -(cTriPx[0].x - cTriPx[2].x); + y_delta = (cTriPx[0].y - cTriPx[2].y); + right_delta = x_delta / y_delta; + + if (left_delta > right_delta) + { + ftemp = left_delta; + left_delta = right_delta; + right_delta = ftemp; + } + + left_x = cTriPx[2].x; + right_x = cTriPx[2].x; + + if (middle_line_drawn == TRUE) + { + cTriPx[1].y += 1; + } + + a.x = cTriPx[0].x; + a.y = cTriPx[0].y; + b.x = cTriPx[1].x; + b.y = cTriPx[1].y; + c.x = cTriPx[2].x; + c.y = cTriPx[2].y; + + + for (y = cTriPx[2].y; y > (cTriPx[1].y - 1); y--) + { + lX = left_x + 0.5; + rX = right_x + 0.5; + left_index = y * frameBuf->w + lX; + right_index = y * frameBuf->w + rX; + + // Temporary fix, the y should just be clipped and left_x/right_x adjusted accordingly + if (TRUE) + { + if (lX < 0) + { + lX = 0; + } + if (lX > frameBuf->w) + { + lX = frameBuf->w; + } + if (rX > frameBuf->w) + { + rX = frameBuf->w; + } + if (rX < 0) + { + rX = 0; + } + + // For every pixel along the line, set it's color + for (i = lX; i < rX; i++) + { + // Get barycentric coordinates (u, v, w) + p.x = i; + p.y = y; + Barycentric2(&p, &a, &b, &c, &bc[VI[0]], &bc[VI[1]], &bc[VI[2]]); + + z = (bc[VI[0]] * cTri.p[VI[0]].z) + (bc[VI[1]] * cTri.p[VI[1]].z) + (bc[VI[2]] * cTri.p[VI[2]].z); + + if (z <= depthBuf->depthBuf[y * frameBuf->w + i]) + { + // Interpolate all vOut values to fIn values for the fragment shader + for (j = 0; j < attrBufSize; j++) + { + fIn[j] = (vOut[j] * bc[0]) + (vOut[j+attrBufSize] * bc[1]) + (vOut[j+(2*attrBufSize)] * bc[2]); + } + + depthBuf->depthBuf[y * frameBuf->w + i] = z; + shd->FragShd(&fragColor, fIn, uniforms); + left_index = y * frameBuf->w + i; + MemSetU32(frameBuf->rawBuf + left_index, fragColor, 1); + } + } + } + left_x += left_delta; + right_x += right_delta; + } + } + } + + } + + Free(vOut); + Free(fIn); +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Render/Shader.CC b/src/Zenith/CosmicGL/Render/Shader.CC new file mode 100644 index 00000000..d374523b --- /dev/null +++ b/src/Zenith/CosmicGL/Render/Shader.CC @@ -0,0 +1,97 @@ +/** + @defgroup Render Render + @brief For rendering triangle arrays using vertex/fragment shaders. +*/ + +#define SHD_F64 1 /// @ingroup Render +#define SHD_CVEC2 2 /// @ingroup Render +#define SHD_CVEC3 3 /// @ingroup Render +#define SHD_CVEC4 4 /// @ingroup Render +#define SHD_CMat4 5 /// @ingroup Render + +/** + @ingroup Render + @brief User-programmed and passed to renderer. Calculates on-screen + coordinates of every triangle vertex, as well as the color for any given + pixel being rasterized. + + + Render Pipeline: + + 1) Vertex shader feeds in triangle coordinates in NDC format + (Normalized Device Coordinates), as well as other + per-vertex values it would like to interpolate and later + feed to the frag shader. + + 2) Using these NDC triangle coordinates, the rasterizer + calculates pixel positions of the triangles on the texture, + and interpolates the per-vertex values given by the vertex + shader into values for the particular fragment (pixel). + + 3) The fragment shader is called, with the interpolated + fragment values fed in, and it calculates the color to + be drawn at that particular fragment. +*/ +class CShader +{ + /** + @brief The number of vertex attributes to interpolate and pass between + the vertex and fragment shader. + + For example if you wanted to pass an interpolated position and UV + coordinate to the fragment shader, this value would be 2. + */ + I64 nVertValues; + + /** + @brief Array of vertex attribute types that describes the format of + the vertex attributes. See SHD_ macros for types. + + For example if you wanted to pass an interpolated position and UV + coordinate to the fragment shader, this array would be + {SHD_CVEC2, SHD_CVEC3}. + */ + I64 *vertValues; + + /** + @brief The vertex shader serves the purpose of returning + a triangle in NDC (normalized device coordinates), as well as + returning a buffer containing other vertex attributes such as + position/normals/UVs. + + @param[in,out] tri Returned triangle (in NDC). + @param[in,out] vertOutBuf Vertex attributes to be interpolated + for the frag shader. These are in format: + Vertex 0 values, Vertex 1 values, Vertex + 2 values, where values has the format + defined by vertValues in CShader. + @param[in] mdlPtr The pointer to the model to calculate + the output triangle from. There is no + predefined model format, the user can + give the renderer any model they want + and interpret it in this function. + @param[in] uniforms Unchanging variables throughout the render + (ie: perspective/lighting/textures). + @param[in] iTri Index of triangle to calculate. + @param[in] nTris Total triangles being rendered. This + function only calculates one triangle + but this is given if accessing a triangle + in your model depends on this value. + */ + U0 (*VertShd)(CTri *tri, F64 *vertOutBuf, U8 *mdlPtr, U8 *uniforms, + I64 iTri, I64 nTris); + + /** + @brief The fragment shader serves the purpose of calculating + the color for the current fragment (pixel) using the passed-in + interpolated vertex attributes, as well as anything else given in + the uniform buffer. + + @param[in] vertBuf Interpolated vertex attributes at that frag + (ie: position, uv, normal). + @param[in] uniforms Unchanging variables throughout the render + (ie: perspective/lighting/textures). + @param[in,out] color Color calculated for this fragment. + */ + U0 (*FragShd)(CBGR24 *color, F64 *fragInBuf, U8 *uniforms); +}; \ No newline at end of file diff --git a/src/Zenith/CosmicGL/Texture/Texture.CC b/src/Zenith/CosmicGL/Texture/Texture.CC new file mode 100644 index 00000000..c32c0db9 --- /dev/null +++ b/src/Zenith/CosmicGL/Texture/Texture.CC @@ -0,0 +1,199 @@ +/** + @defgroup Tex2D Texture + @brief The core of the library, almost all functions either read from + or write to textures. +*/ + + +/** + @ingroup Tex2D + @brief Raw format is a CBGR24 rawBuf array of size w * h in row-major + order. +*/ +#define TEX2D_RAW 1 + +/** + @ingroup Tex2D + @brief Depth format is a F64 depthBuf array of size w * h in row-major + order. +*/ +#define TEX2D_DEPTH 2 + +/** + @ingroup Tex2D + @brief The CTex2D class +*/ +class CTex2D +{ + I64 type; // Type, see TEX2D_ macros above + I64 w; // Width + I64 h; // Height + I64 l; // Left offset if applicable (for use with sprite textures) + I64 t; // Top offset if applicable (for use with sprite textures) + I64 mipmaps; // Mipmaps, not currently being used + CBGR24 *rawBuf; // Raw color buffer. Size: w * h + F64 *depthBuf; // Depth buffer. Size: w * h, pixels are depth. +}; + +/** + @ingroup Tex2D + @brief Initialize texture. + + @param[in,out] tex Uninitialized texture to initialize. + @param[in] type Type of texture. See TEX2D_ macros for options. + @param[in] w Width + @param[in] h Height + @param[in] l (Optional) left offset for use with sprite textures. + @param[in] t (Optional) top offset for use with sprite textures. +*/ +U0 Tex2DInit(CTex2D *tex, I64 type, I64 w, I64 h, I64 l = 0, I64 t = 0) +{ + tex->w = w; + tex->h = h; + tex->l = l; + tex->t = t; + tex->type = type; + + switch (type) + { + case (TEX2D_RAW): + tex->rawBuf = MAlloc(4 * tex->w * tex->h); + break; + + case (TEX2D_DEPTH): + tex->depthBuf = MAlloc(sizeof(F64) * tex->w * tex->h); + break; + } +} + +/** + @ingroup Tex2D + @brief Free texture. + + @param[in,out] tex Texture to free. +*/ +U0 Tex2DFree(CTex2D *tex) +{ + switch (tex->type) + { + case TEX2D_RAW: + Free(tex->rawBuf); + break; + + case TEX2D_DEPTH: + Free(tex->depthBuf); + break; + } +} + +/** + @ingroup Tex2D + @brief Fill texture with color. Only works with raw textures. + + @param[in,out] tex Texture to color fill. + @param[in] color Color to fill texture with. +*/ +U0 Tex2DColorFill(CTex2D *tex, CBGR24 color) +{ + MemSetU32(tex->rawBuf, color, tex->w * tex->h); +} + +/** + @ingroup Tex2D + @brief Reset depth texture. Set all pixels to far depth. + + @param[in,out] tex Depth texture to reset. +*/ +U0 Tex2DDepthReset(CTex2D *tex) +{ + F64 depth = 2.0; + I64 *iDepth = &depth; + MemSetI64(tex->depthBuf, *iDepth, tex->w * tex->h); +} + +/** + @ingroup Tex2D + @brief Draw texture directly to display buffer. Maps non-visible textures + like depth to a visible palette when drawing for visual debugging. + + This may cause fighting with the operating system and flashing/text or + other visual updates might show through. If using this for an application, + be sure to disable the operating system from drawing to the display buffer + in GrWinUpdate32. + + @param[in] tex Texture to draw to display buffer. + @param[in] x X position on screen to draw texture. + @param[in] y Y position on screen to draw texture. +*/ +U0 Tex2DDebugDisp(CTex2D *tex, I64 x, I64 y) +{ + // i, j = sampling x, y position on texture + I64 i, j; + + // For scaling depth buffer to 8 bit color + F64 scaled; + CBGR24 color; + + for (j = 0; j < tex->h; j++) + { + for (i = 0; i < tex->w; i++) + { + switch (tex->type) + { + case TEX2D_RAW: + text.fb_alias[(x + i) + (y + j) * GR_WIDTH] + = tex->rawBuf[i + j * tex->w]; + break; + + case TEX2D_DEPTH: + // Scaled to display 0-1024 depth + scaled = tex->depthBuf[i + j * tex->w] / 4; + color.r = scaled; + color.g = scaled; + color.b = scaled; + text.fb_alias[(x + i) + (y + j) * GR_WIDTH] = color; + break; + } + } + } +} + +/** + @ingroup Tex2D + @brief Sample color from color textures at normalized UV coordinate + (0.0-1.0). Coordinates over 1.0 loop causing a repeating texture effect. + + Does not handle filtering or mip maps (yet). The MemCopy's are dumb + I will replace with simply returning the color soon. + + @param[in,out] col Sets color at this address to sampled color. + @param[in] tex Texture to sample from. + @param[in] u Normalized (0.0-1.0) x coordinate to sample from. + @param[in] v Normalized (0.0-1.0) y coordinate to sample from. +*/ +U0 Tex2DSampleNorm(CBGR24 *col, CTex2D *tex, F64 u, F64 v) +{ + I64 x = u * tex->w; + I64 y = v * tex->h; + x %= tex->w; + y %= tex->h; + + MemCopy(col, &tex->rawBuf[x + y * tex->w], sizeof(CBGR24)); +} + +/** + @ingroup Tex2D + @brief Sample color from color textures at UV coordinate. Coordinates + over texture dimensions loop causing a repeating texture effect. + + @param[in,out] col Sets color at this address to sampled color. + @param[in] u X coordinate to sample from. + @param[in] v Y coordinate to sample from. +*/ +U0 Tex2DSampleCoord(CBGR24 *col, CTex2D *tex, I64 u, I64 v) +{ + u %= tex->w; + v %= tex->h; + + MemCopy(col, &tex->rawBuf[u + v * tex->w], sizeof(CBGR24)); +} diff --git a/src/Zenith/CosmicGL/Texture/TextureImport.CC b/src/Zenith/CosmicGL/Texture/TextureImport.CC new file mode 100644 index 00000000..5a4e8157 --- /dev/null +++ b/src/Zenith/CosmicGL/Texture/TextureImport.CC @@ -0,0 +1,268 @@ +/** + @ingroup Tex2D + @brief Loads Half-Life texture from WAD file. + + @param[in,out] tex Unitialized texture to load into. + @param[in] wad WAD file to load from. + @param[in] texName Name of texture lump in WAD file to load. +*/ +U0 Tex2DLoadWAD(CTex2D *tex, CWAD *wad, U8 *texName) +{ + if (wad->type != WAD_TYPE_HL) + { + "[ERROR] CAN NOT LOAD %s TEXTURE FROM NON-HALF-LIFE WAD %s. USE LOAD + SPRITE/FLAT/WALL FOR DOOM WADS.\n", texName, wad->name; + return; + } + + // Get lump handle + CWADFileLumpHL *lump; + I64 texIndex = WADFindLump(wad, texName, &lump); + + if (texIndex < 0) + { + "[ERROR] CAN NOT FIND %s TEXTURE IN WAD %s\n", texName, wad->name; + return; + } + + // Map texture class to WAD file buffer + CWADTexHL *wadTex = wad->fileBuf + lump->filePos; + + Tex2DInit(tex, TEX2D_RAW, wadTex->w, wadTex->h); + + // Map pixel array + U8 *pixel = wad->fileBuf + lump->filePos + wadTex->offsets[0]; + + // After the last byte of the fourth mipmap, there are two padding bytes, + // then a palette of 24-bit color (R, G, B). + U8 *palette = wad->fileBuf + lump->filePos + wadTex->offsets[3] + + ((wadTex->w * wadTex->h) / 64) + 2; + // 1/64 here is the relative size of 4th mip map + + I64 i; + CBGR24 color; + color.pad = 255; // Alpha + + for (i = 0; i < wadTex->w * wadTex->h; i++) + { + color.r = palette[pixel[i] * 3]; + color.g = palette[(pixel[i] * 3) + 1]; + color.b = palette[(pixel[i] * 3) + 2]; + + tex->rawBuf[i] = color; + + // Full blue pixels are transparent + if (color == 0xFF0000FF) + tex->rawBuf[i].pad = 0; + } +} + +/** + @internal + @ingroup Tex2D + @brief Loads DOOM patch at memory location into position on texture. + + Relevant locations on texture should be initialized with a transparent + alpha byte. + + @param[in,out] tex Initialized texture to load into. + @param[in] patch Memory location of patch to load from. + @param[in] palette Array of 256 colors. + @param[in] l Left (x) offset. + @param[in] t Top (y) offset. +*/ +U0 Tex2DLoadWADPatch(CTex2D *tex, CWADPatch *patch, CBGR24 *palette, + I64 l, I64 t) +{ + I64 x, y; + CWADPost *post; // Current post being parsed. + + // Direct pointer addition gives wierd results so these are + // intermediate pointers: + I64 patchAddr = patch; + I64 cursor; + + // Iterate through every column in patch + for (x = 0; x < patch->w; x++) + { + // Point post to first post in current column + cursor = patchAddr + patch->columnOffsets[x]; + post = cursor; + + // Post with offset 255 is last post in column and is empty + while (post->yOffset != 255) + { + // Draw every pixel in post to texture buffer + for (y = 0; y < post->length; y++) + { + tex->rawBuf[(x + l) + (y + t + post->yOffset) * tex->w] + = palette[post->pixels[y]]; + } + + // Point post to next post in column + cursor += post->length + 4; // 3 starting bytes + last pad byte + post = cursor; + } + } +} + +/** + @ingroup Tex2D + @brief Loads DOOM sprite from WAD file. + + @param[in,out] tex Unitialized texture to load into. + @param[in] wad WAD file to load from. + @param[in] sprName Name of sprite in WAD file to load. +*/ +U0 Tex2DLoadWADSprite(CTex2D *tex, CWAD *wad, U8 *sprName) +{ + if (wad->type != WAD_TYPE_DOOM) + { + "[ERROR] CAN NOT LOAD %s SPRITE FROM NON-DOOM %s\n", sprName, wad->name; + return; + } + + // Get lump handle + CWADFileLumpDoom *lump; + I64 sprIndex = WADFindLump(wad, sprName, &lump); + + if (sprIndex < 0) + { + "[ERROR] CAN NOT FIND %s SPRITE IN %s\n", sprName, wad->name; + return; + } + + // Map patch class to sprite lump (which is in patch format) + CWADPatch *patch = wad->fileBuf + lump->filePos; + + Tex2DInit(tex, TEX2D_RAW, patch->w, patch->h, patch->l, patch->t); + Tex2DColorFill(tex, 0); // To set alpha to 0 since patch's don't draw + // the transparent pixels, only opaque ones. + Tex2DLoadWADPatch(tex, patch, wad->paletteBuf, 0, 0); +} + +/** + @ingroup Tex2D + @brief Loads DOOM wall texture from WAD file. + + @param[in,out] tex Uninitialized texture to load into. + @param[in] wad WAD file to load from. + @param[in] wallName Name of wall in WAD file to load. +*/ +U0 Tex2DLoadWADWall(CTex2D *tex, CWAD *wad, U8 *wallName) +{ + +} + +/** + @internal + @ingroup Tex2D + @brief BMP file header. +*/ +class CBMPHeader +{ + U8 szVersion[2]; // Should have 'BM' signature + U32 nSize; // File size in bytes + U32 pad; // Unused + U32 nOffset; // Offset from beginning of file to data; + + /* Info header is right after first header, so it is merged into this class */ + U32 nInfoSize; // Size of this info part of the header (=40) + U32 nWidth; // Image width in pixels + U32 nHeight; // Image height in pixels + U16 nPlanes; // Number of planes (=1) + U16 nPxBits; // Number of bits per pixel. This also indicates palette method: + // 1-bit: Monochrome + // 4-bit: 16-color palette + // 8-bit: 256-color palette + // 16-bit: 65565 colors + // 24-bit: 16M colors + U32 nCompression; // Type of compression: + // 0: BI_RGB no-compression + // 1: BI_RLE8 8-bit RLE encoding + // 2: BI_RLE4 4-bit RLE encoding + U32 nImageSize; // Size of image if compressed, (can =0 if not compressed) + U32 nXPxPerM; // Horizontal resolution, pixels per meter + U32 nYPxPerM; // Vertical resolution, pixels per meter + U32 nColors; // Number of actually used colors (ie. 256 for 8-bit color) + U32 nImptColors; // Number of important colors (=0 for all) +}; + +/** + @ingroup Tex2D + @brief Loads BMP image. + + Supports standard uncompressed 24-bit color BMP images. + + @param[in,out] tex Uninitialized texture to load into. + @param[in] fname File name of BMP. +*/ +U0 Tex2DLoadBMP(CTex2D *tex, U8 *fname, Bool mask = FALSE, + Bool invert = TRUE) +{ + U8 *bmp = FileRead(fname); + + // Map locations in buffer; + CBMPHeader *info = bmp; + + //if (StrMatch(bmp->szVersion, "BM") == 0) + if (info->szVersion[0] != 'B' && info->szVersion[1] != 'M') + { + "[ERROR] %s IS NOT A VALID BMP FILE!\n", fname; + return; + } + + if (info->nPxBits != 24 || info->nCompression != 0) + { + "[ERROR] %s IS NOT A 24-BIT UNCOMPRESSED BMP!\n", fname; + return; + } + + // Each pixel is 3 bytes in 24-bit BMP, however CTex2D uses CBGR24 + // which contains a final padding bit (32-bits per pixel). The rows + // also increment in the opposite direction. + I64 imgPointer = bmp + info->nOffset; + I64 xPx, yPx; + + // For checking if a color channel is greater than 0 for masks + U8 channel; + + if (mask) + { + /* + // Initialize with MASK format for 1-bit non-compressed color + Tex2DInit(tex, TEX2D_MASK, info->nWidth, info->nHeight); + + for (yPx = 0; yPx < info->nHeight; yPx++) + { + for (xPx = 0; xPx < info->nWidth; xPx++) + { + channel = *(imgPointer + (xPx + info->nWidth * + (info->nHeight - yPx)) * 3); + + if ((channel == 0 && invert = TRUE) || + (channel > 0 && invert = FALSE)) + { + tex->maskBuf[(xPx + info->nWidth * yPx) / 8] | (1 << (xPx % 8)); + } + } + } + */ + } + else + { + // Initialize with RAW format for 24-bit non-compressed color + Tex2DInit(tex, TEX2D_RAW, info->nWidth, info->nHeight); + + for (yPx = 0; yPx < info->nHeight; yPx++) + { + for (xPx = 0; xPx < info->nWidth; xPx++) + { + MemCopy(&tex->rawBuf[xPx + info->nWidth * yPx], + imgPointer + (xPx + info->nWidth * (info->nHeight - yPx)) + * 3, 3); + } + } + } +} + diff --git a/src/Zenith/CosmicGL/WAD/DoomMap.CC b/src/Zenith/CosmicGL/WAD/DoomMap.CC new file mode 100644 index 00000000..dd49cda3 --- /dev/null +++ b/src/Zenith/CosmicGL/WAD/DoomMap.CC @@ -0,0 +1,263 @@ +/** + @internal + @ingroup Doom + @brief Hash table type for Doom texture hash. +*/ +#define HTT_DOOM_TEX 420 + +/** + @internal + @ingroup Doom + @brief Doom texture hash table size. +*/ +#define DOOM_TEX_TABLE_SIZE 256 + +/** + @internal + @ingroup Doom + @brief Hash for quick access of textures by name. +*/ +class CHashDoomTex : CHash +{ + CTex2D tex; +}; + +/** + @ingroup Doom + @brief Runtime Doom map. +*/ +class CDoomMap +{ + I64 pad; + + CDoomVertex *vertexes; + I64 nVertexes; + + CDoomSide *sides; + I64 nSides; + + CDoomLine *lines; + I64 nLines; + + // CHashTable *texTable; // Hash table for quick access of textures +}; + +/** + @internal + @ingroup Doom + @brief Load blockmap from WAD into Doom map. See CDoomBBox. + + @param[in,out] map Doom map to load into. + @param[in] wad WAD to load from. + @param[in] lumpIndex Index of BLOCKMAP lump in WAD. +*/ +U0 DoomMapLoadBlockmap(CDoomMap *map, CWAD *wad, I64 lumpIndex) +{ + +} + +/** + @internal + @ingroup Doom + Load vertexes from WAD into Doom map. See CDoomVertex. + + @param[in,out] map Doom map to load into. + @param[in] wad WAD to load from. + @param[in] lumpIndex Index of VERTEXES lump in WAD. +*/ +U0 DoomMapLoadVertexes(CDoomMap *map, CWAD *wad, I64 lumpIndex) +{ + CWADFileLumpDoom *lump = &wad->doomLumps[lumpIndex]; + + if (StrNICompare("VERTEXES", lump->name, 8) != 0) + { + "[ERROR] EXPECTED VERTEXES LUMP INDEX DOES NOT POINT TO A + VERTEXES LUMP\n"; + return; + } + + // Map WAD vertex array + CWADVertex *verts = wad->fileBuf + lump->filePos; + + // Calculate and update number of vertexes + I64 nVerts = lump->size / sizeof(CWADVertex); + map->nVertexes = nVerts; + + // Allocate memory for map vertexes + map->vertexes = MAlloc(sizeof(CDoomVertex) * nVerts); + + // Load I16 vertexes to I64 fixed point + I32 xIntermediate; + I32 yIntermediate; + I64 i; + for (i = 0; i < nVerts; i++) + { + // Load I16 to I32 to preserve sign at MSB when shifting 32 bits left + // for I64 fixed point conversion. + xIntermediate = verts[i].x; + yIntermediate = verts[i].y; + + map->vertexes[i].x = xIntermediate << 32; + map->vertexes[i].y = yIntermediate << 32; + } +} + +/** + @internal + @ingroup Doom + @brief Load sectors from WAD into Doom map. See CDoomSector. + + @param[in,out] map Doom map to load into. + @param[in] wad WAD to load from. + @param[in] lumpIndex Index of SECTORS lump in WAD. +*/ +U0 DoomMapLoadSectors(CDoomMap *map, CWAD *wad, I64 lumpIndex) +{ + +} + +/** + @internal + @ingroup Doom + @brief Load sides from WAD into Doom map. See CDoomSide. + + @param[in,out] map Doom map to load into. + @param[in] wad WAD to load from. + @param[in] lumpIndex Index of SIDEDEFS lump in WAD. +*/ +U0 DoomMapLoadSides(CDoomMap *map, CWAD *wad, I64 lumpIndex) +{ + CWADFileLumpDoom *lump = &wad->doomLumps[lumpIndex]; + + if (StrNICompare("SIDEDEFS", lump->name, 8) != 0) + { + "[ERROR] EXPECTED SIDEDEFS LUMP INDEX DOES NOT POINT TO A + SIDEDEFS LUMP\n"; + return; + } + + // Map WAD sidedef array + CWADSidedef *sidedefs = wad->fileBuf + lump->filePos; + + // Calculate and update number of sidedefs + I64 nSidedefs = lump->size / sizeof(CWADSidedef); + map->nSides = nSidedefs; + + // Allocate memory for map sides + map->sides = MAlloc(sizeof(CDoomSide) * nSidedefs); + + I64 i; + for (i = 0; i < nSidedefs; i++) + { + map->sides[i].xOffset = sidedefs[i].xOffset; + map->sides[i].yOffset = sidedefs[i].yOffset; + + /* Todo - Handle wall texture loading */ + } +} + +/** + @internal + @ingroup Doom + @brief Load lines from WAD into Doom map. See CDoomLine. + + @param[in,out] map Doom map to load into. + @param[in] wad WAD to load from. + @param[in] lumpIndex Index of LINEDEFS lump in WAD. +*/ +U0 DoomMapLoadLines(CDoomMap *map, CWAD *wad, I64 lumpIndex) +{ + CWADFileLumpDoom *lump = &wad->doomLumps[lumpIndex]; + + if (StrNICompare("LINEDEFS", lump->name, 8) != 0) + { + "[ERROR] EXPECTED LINEDEFS LUMP INDEX DOES NOT POINT TO A + LINEDEFS LUMP\n"; + return; + } + + // Map WAD linedefs array + CWADLinedef *linedefs = wad->fileBuf + lump->filePos; + + // Calculate and update number of linedefs + I64 nLinedefs = lump->size / sizeof(CWADLinedef); + map->nLines = nLinedefs; + + // Allocate memory for map linedefs + map->lines = MAlloc(sizeof(CDoomLine) * nLinedefs); + + I64 i; + for (i = 0; i < nLinedefs; i++) + { + map->lines[i].v1 = &map->vertexes[linedefs[i].v1]; + map->lines[i].v2 = &map->vertexes[linedefs[i].v2]; + + map->lines[i].frontSide = &map->sides[linedefs[i].frontSidedef]; + map->lines[i].backSide = &map->sides[linedefs[i].backSidedef]; + } +} + +/** + @internal + @ingroup Doom + @brief Builds sector line lists and subsector numbers. Finds block + bounding boxes for sectors. + + @param[in,out] map Map to modify. +*/ +U0 DoomMapGroupLines(CDoomMap *map) +{ + // Look up sector number for each subsector + +} + +/** + @ingroup Doom + @brief Load DOOM map from WAD file. + + @param[in,out] map Uninitialized Doom map to load into. + @param[in] wad WAD to load from. + @param[in] mapName Map from WAD to load (max length is 8 characters). +*/ +U0 DoomMapLoad(CDoomMap *map, CWAD *wad, U8 *mapName) +{ + I64 mapLumpIndex = WADFindLump(wad, mapName, NULL); + + if (mapLumpIndex < 0) + { + "[ERROR] UNABLE TO FIND MAP %s IN WAD\n", mapName; + return; + } + + // Prepare texture hash table + // map->texTable = HashTableNew(DOOM_TEX_TABLE_SIZE); + + //I64 thingsIndex = mapLumpIndex + 1; + + I64 vertexesIndex = mapLumpIndex + 4; + DoomMapLoadVertexes(map, wad, vertexesIndex); + + I64 sidedefsIndex = mapLumpIndex + 3; + DoomMapLoadSides(map, wad, sidedefsIndex); + + I64 linedefsIndex = mapLumpIndex + 2; + DoomMapLoadLines(map, wad, linedefsIndex); + + //I64 segsIndex = mapLumpIndex + 5; + //I64 subsectorsIndex = mapLumpIndex + 6; + //I64 rejectIndex = mapLumpIndex + 7; + //I64 blockmapIndex = mapLumpIndex + 8; +} + +/** + @ingroup Doom + @brief Free Doom map. + + @param[in,out] map Doom map to free. +*/ +U0 DoomMapFree(CDoomMap *map) +{ + // Free(map->vertexes); + // Free(map->lines); + // Free(map->sides); +} \ No newline at end of file diff --git a/src/Zenith/CosmicGL/WAD/DoomTypes.CC b/src/Zenith/CosmicGL/WAD/DoomTypes.CC new file mode 100644 index 00000000..0f990f22 --- /dev/null +++ b/src/Zenith/CosmicGL/WAD/DoomTypes.CC @@ -0,0 +1,125 @@ +/** + @defgroup Doom Doom + @brief Functions for loading Doom maps from WADs and rendering them. +*/ + +/** + @ingroup Doom + @brief Fixed point 2D vertex. +*/ +class CDoomVertex +{ + I64 x; + I64 y; +}; + +/** + @internal + @ingroup Doom + @brief Fixed point 2D bounding box. +*/ +class CDoomBBox +{ + I64 x1; + I64 y1; + I64 x2; + I64 y2; +}; + +/** + @internal + @ingroup Doom + @brief A sector is a 2D section of the map. +*/ +class CDoomSector +{ + I64 floorH; // Floor height + I64 ceilingH; // Ceiling height + + CTex2D *floorTex; + CTex2D *ceilingTex; + + I64 lightLevel; + I64 special; + I16 tag; + + I64 nLines; // Total lines associated with sector + //CDoomLine **lines; // Array of line pointers + CDoomBBox blockBox[4]; // Bounding box for height changes +}; + +/** + @internal + @ingroup Doom + @brief A side contains the textures applied to a line (wall). +*/ +class CDoomSide +{ + I64 xOffset; + I64 yOffset; + + CTex2D *upperTex; + CTex2D *lowerTex; + CTex2D *middleTex; + + CDoomSector *sectorFacing; +}; + + +/** + @internal + @ingroup Doom + @brief Lines make up the shape of the map. A one sided wall is a + solid wall, while a two sided wall is a boundary between sectors. +*/ +class CDoomLine +{ + CDoomVertex *v1; + CDoomVertex *v2; + CDoomSide *frontSide; + CDoomSide *backSide; +}; + +/** + @internal + @ingroup Doom + @brief Clipped line that is used to make up subsectors. +*/ +class CDoomSeg +{ + +} + +/** + @internal + @ingroup Doom + @brief List of segs indicating some or all visible walls that define + a convex BSP leaf. +*/ +class CDoomSubSector +{ + CDoomSector *sector; + I64 nLines; + I64 firstLine; +}; + +#define NF_SUBSECTOR 0x8000 + +/** + @internal + @ingroup Doom + @brief Splits level down a line. On either side of the line is either + another child node or a BSP leaf (subsector). +*/ +class CDoomNode +{ + I64 x; // Partition start coordinates + I64 y; + I64 xChange; // Change from start to end of partition line + I64 yChange; + CDoomBBox rightBox; + CDoomBBox leftBox; + CDoomNode *rightChild; + CDoomNode *leftChild; +}; + diff --git a/src/Zenith/CosmicGL/WAD/WAD.CC b/src/Zenith/CosmicGL/WAD/WAD.CC new file mode 100644 index 00000000..7a961ad0 --- /dev/null +++ b/src/Zenith/CosmicGL/WAD/WAD.CC @@ -0,0 +1,207 @@ +/** + @internal + @ingroup WAD + @brief Half-Life WAD type. +*/ +#define WAD_TYPE_HL 1 + +/** + @internal + @ingroup WAD + @brief Doom WAD type. +*/ +#define WAD_TYPE_DOOM 2 + +/** + @internal + @ingroup WAD + @brief Hash table type for lump hash. +*/ +#define HTT_LUMP 69 + +/** + @internal + @ingroup WAD + @brief Hash for quick access of lump pointer and index by name. +*/ +class CHashLump : CHash +{ + CWADFileLump *lump; // Pointer to lump + // Access with lump->doom/lump->hl + I64 lumpIndex; // Index of lump in WAD +}; + +/** + @ingroup WAD + @brief Class for WAD currently in memory. + + Has easy access handles and a hash table to quickly find lumps. +*/ +class CWAD +{ + U8 *name; + U8 *fileBuf; + CWADInfo *info; // Type, size, pointer to lumps + CWADFileLumpDoom *doomLumps;// Quick access doom lumps + CWADFileLumpHL *hlLumps; // Quick access half life lumps + I64 type; // WAD type + CHashTable *lumpTable; // Hash table for quick access of + // lumps by name. + CHashLump *hashLumps; // Hashes for lumpTable. + CBGR24 *paletteBuf; // Color palette 0 for texture loading if doom +}; + +/** + @ingroup WAD + @brief Finds lump with name and returns it's index in the WAD. Also + points input lump pointer to it if found. + + @param[in] wad WAD to search. + @param[in] name Lump name to find. + @param[in,out] lump Points to lump if found. + @return Lump index in WAD. -1 if not found. +*/ +I64 WADFindLump(CWAD *wad, U8 *name, CWADFileLump **lump = NULL) +{ + CHashLump *lumpHash; + lumpHash = HashFind(name, wad->lumpTable, HTT_LUMP); + + if (lumpHash != NULL) + { + if (lump != NULL) + *lump = lumpHash->lump; + + return lumpHash->lumpIndex; + } + else + { + return -1; + } +} + +/** + @ingroup WAD + @brief Loads WAD file. + + @param[in,out] wad Uninitialized WAD to initialize. + @param[in] fname File name of WAD. +*/ +U0 WADLoad(CWAD *wad, U8 *fname) +{ + wad->fileBuf = FileRead(fname); + wad->name = StrNew(fname); + + // For palette loading if doom WAD + CWADFileLumpDoom *paletteLump; + U8 *paletteColor; + + // Map locations in buffer + wad->info = wad->fileBuf; + + // Allocate hash table + wad->lumpTable = HashTableNew(wad->info->nLumps); // Table + wad->hashLumps = CAlloc(wad->info->nLumps * // Hashes + sizeof(CHashLump)); + + I64 i; + + // Check version and proceed accordingly + U32 *type = wad->info->type; // Cast to U32 + + // 'WAD3': Half Life 'WAD2': Quake + if (*type == 0x33444157 || *type == 0x32444157) + { + wad->type = WAD_TYPE_HL; + + // Map lumps to buffer + wad->hlLumps = wad->fileBuf + wad->info->lumpOffset; + + for (i = 0; i < wad->info->nLumps; i++) + { + // Copy name to hash + wad->hashLumps[i].str = CAlloc(17); // 16 + NULL + MemCopy(wad->hashLumps[i].str, + wad->hlLumps[i].name, 16); + + // Set hash type + wad->hashLumps[i].type = HTT_LUMP; + + // Link lump to hash + wad->hashLumps[i].lump = &wad->hlLumps[i]; + + // Set lump index in hash to this index + wad->hashLumps[i].lumpIndex = i; + + // Add hash to table + HashAdd(&wad->hashLumps[i], wad->lumpTable); + } + return; + } + + // 'IWAD': Doom Internal 'PWAD': Doom Patch + if (*type == 0x44415749 || *type == 0x44415750) + { + wad->type = WAD_TYPE_DOOM; + + // Map lumps to buffer + wad->doomLumps = wad->fileBuf + wad->info->lumpOffset; + + for (i = 0; i < wad->info->nLumps - 10; i++) + { + // Copy name to hash + wad->hashLumps[i].str = CAlloc(9); // 8 + NULL + MemCopy(wad->hashLumps[i].str, + wad->doomLumps[i].name, 8); + + // Set hash type + wad->hashLumps[i].type = HTT_LUMP; + + // Link lump to hash + wad->hashLumps[i].lump = &wad->doomLumps[i]; + + // Set lump index in hash to this index + wad->hashLumps[i].lumpIndex = i; + + // Add hash to table + HashAdd(&wad->hashLumps[i], wad->lumpTable); + } + + // Load palette 0 for texture loading later if it exists + if (WADFindLump(wad, "PLAYPAL", &paletteLump) != -1) + { + wad->paletteBuf = MAlloc(sizeof(CBGR24) * 256); + paletteColor = wad->fileBuf + paletteLump->filePos; + + // Load each 3-byte RGB color into 4 byte BGRA palette + for (i = 0; i < 256; i++) + { + wad->paletteBuf[i].r = paletteColor[i * 3]; + wad->paletteBuf[i].g = paletteColor[i * 3 + 1]; + wad->paletteBuf[i].b = paletteColor[i * 3 + 2]; + wad->paletteBuf[i].pad = 255; // Alpha + } + } + + return; + } + + // Type not recognized + U64 typeStr = 0; + MemCopy(&typeStr, wad->type, 4); + "[ERROR] %s WAD TYPE NOT RECOGNIZED: %s\n", typeStr; +} + +/** + @ingroup WAD + @brief Free WAD. + + @param[in,out] wad WAD to free. +*/ +U0 WADFree(CWAD *wad) +{ + Free(wad->lumpTable->body); + Free(wad->lumpTable); + Free(wad->hashLumps); + Free(wad->fileBuf); +} + diff --git a/src/Zenith/CosmicGL/WAD/WADTypes.CC b/src/Zenith/CosmicGL/WAD/WADTypes.CC new file mode 100644 index 00000000..b65fec3a --- /dev/null +++ b/src/Zenith/CosmicGL/WAD/WADTypes.CC @@ -0,0 +1,283 @@ +/** + @defgroup WAD WAD + @brief WAD file format management and parsing. Supports DOOM and + Half-Life WADs. +*/ + +/** + @internal + @ingroup WAD + @brief WAD file header. +*/ +class CWADInfo +{ + U8 type[4]; // Type of WAD, like IWAD/PWAD/WAD3 + I32 nLumps; // Number of lumps + I32 lumpOffset; // Offset to lump +}; + +/** + @ingroup WAD + @brief DOOM file lump. These point to "lumps" or directories in the WAD. +*/ +class CWADFileLumpDoom +{ + I32 filePos; // Relative offset + I32 size; + U8 name[8]; +}; + +/** + @ingroup WAD + @brief Half-Life file lump. These point to "lumps" or directories in the + WAD. +*/ +class CWADFileLumpHL +{ + I32 filePos; // Relative offset + I32 diskSize; // Size of lump (compressed) + I32 size; // Uncompressed lump size + I8 type; + I8 compression; // 0 if none + I16 pad; + U8 name[16]; +}; + +/** + @ingroup WAD + @brief Union of Half-Life and DOOM lump classes to return as a pointer + from general purpose WAD functions like WADFindLump. +*/ +union CWADFileLump +{ + CWADFileLumpDoom doom; + CWADFileLumpHL hl; +}; + + +/** + @internal + @ingroup WAD + @brief Thing element from a DOOM WAD THINGS lump. + + Describes an entity. +*/ +class CWADThing +{ + I16 x; + I64 y; + I16 angle; + I16 type; + I16 flags; +}; + +/** + @internal + @ingroup WAD + @brief Linedef element from a DOOM WAD LINEDEFS lump. + + Describes a line that makes up a wall/sector boundary on the map. +*/ +class CWADLinedef +{ + I16 v1; // Vertex 1 + I16 v2; // Vertex 2 + I16 flags; + I16 type; + I16 sectorTag; + I16 frontSidedef; // Sidedefs hold texture data for each side of linedef + I16 backSidedef; +}; + +/** + @internal + @ingroup WAD + @brief Sidedef element from a DOOM WAD SIDEDEFS lump. + + Describes a side of a line (wall) and what textures it uses as well as + what sector it faces. +*/ +class CWADSidedef +{ + I16 xOffset; // Texture x offset + I16 yOffset; // Texture y offset + U8 upperTex[8]; // These are found in TEXTUREx lumps + U8 lowerTex[8]; + U8 middleTex[8]; + I16 sectorFaces; // Sector this sidedef faces +}; + +/** + @internal + @ingroup WAD + @brief Vertex element from a DOOM WAD VERTEXES lump. +*/ +class CWADVertex +{ + I16 x; + I16 y; +}; + +/** + @internal + @ingroup WAD + @brief Segment element from a DOOM WAD SEGS lump. + + Describes a segment of a line that makes up the outline of a subsector + (convex BSP leaf). +*/ +class CWADSeg +{ + I16 v1; + I16 v2; + I16 angle; + I16 linedef; + I16 direction; + I16 offset; +}; + +/** + @internal + @ingroup WAD + @brief Sector element from a DOOM WAD SECTORS lump. + + Describes a 2D region of the map. +*/ +class CWADSector +{ + I16 floorHeight; + I16 ceilingHeight; + U8 floorTex[8]; + U8 ceilingTex[8]; + I16 lightLevel; + I16 flags; + I16 tag; +}; + +/** + @internal + @ingroup WAD + @brief Sub-sector element from a DOOM WAD SSECTORS lump. + + Describes a convex BSP leaf, meaning a convex sub section of a sector. +*/ +class CWADSubSector +{ + I16 nLinedefs; + I16 firstLinedef; +}; + +/** + @internal + @ingroup WAD + @brief Node element from a DOOM WAD NODES lump + + Describes a node on a BSP tree. Each node splits a region of the map into + two along a line, with a left/right side child which can either be another + node or a subsector. +*/ +class CWADNode +{ + I16 x; + I16 y; +}; + +/** + @internal + @ingroup WAD + @brief DOOM Patch texture header. + + Points to columns which are variable size arrays of CWADPost. This is a + variable length class. +*/ +class CWADPatch +{ + U16 w; + U16 h; + I16 l; // Offsets to shift origin point when drawing + I16 t; + U32 columnOffsets[1]; // Array of offsets (relative to header) of size + // width. If columns are identical they may point + // to the same column in the file. +}; + +/** + @internal + @ingroup WAD + @brief DOOM Post. + + A post is a vertical segment of opaque pixels in the column of a patch + texture. There may be multiple of these posts in a column, one after the + other, with the end of a column being signifiged by a post with an offset + of 255. This is a variable length class. +*/ +class CWADPost +{ + U8 yOffset; // Offset from start of column. This is 255 if + // signaling end of column. + U8 length; // Length is bytes/pixels + U8 pad; + U8 pixels[1]; // Size is equal to length. Each pixel points to a color + // in the color palette. + // Technically another U8 pad comes here but pixels is variable length +}; + +/** + @internal + @ingroup WAD + @brief Header to DOOM TEXTUREx lumps. +*/ +class CWADTexHeader +{ + I32 nTextures; // Number of textures in TEXTUREx lump + I32 offsets[1]; // Offsets to CWADMapTex's from start of this class +}; + +/** + @internal + @ingroup WAD + @brief Information about DOOM patch and where to find it in the PNAMES + lump. +*/ +class CWADMapPatch +{ + I16 l; // Left offset (x origin) + I16 t; // Top offset (y origin) + I16 patch; // Patch number listed in PNAMES lump + I16 stepdir; // Obsolete, ignored by all DOOM versions + I16 colormap; // Obsolete, ignored by all DOOM versions +}; + +/** + @internal + @ingroup WAD + @brief Texture in a DOOM TEXTUREx lump. + + A texture is composed of multiple patches. However it would have been + too simple for this to point to these patches directly, instead it points + to CWADMapPatch which describes where the patch is found in the PNAMES + lump. +*/ +class CWADMapTex +{ + U8 name[8]; + I32 masked; // Boolean + I16 width; + U16 height; + U8 columnDir[4]; // Obsolete, ignored by all DOOM versions + I16 patchCount; // Number of patches that make up texture + CWADMapPatch patches[1]; // Size dependent on patchCount +}; + +/** + @internal + @ingroup WAD + @brief Header to a Half-Life texture lump. +*/ +class CWADTexHL +{ + U8 name[16]; + U32 w; + U32 h; + U32 offsets[4]; // Relative offset to each mip map +}; \ No newline at end of file diff --git a/src/Zenith/MakeZenith.CC b/src/Zenith/MakeZenith.CC index 80b31c2b..cb94e482 100755 --- a/src/Zenith/MakeZenith.CC +++ b/src/Zenith/MakeZenith.CC @@ -24,4 +24,5 @@ LBts(&sys_run_level, RLf_DOC); #include "ZDefine" #include "WallPaper" #include "ZMouse" +#include "CosmicGL/MakeCosmicGL" Cd("..");;