/* ** Polygon Doom software renderer ** Copyright (c) 2016 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** */ #include #include "templates.h" #include "doomdef.h" #include "i_system.h" #include "w_wad.h" #include "v_video.h" #include "doomstat.h" #include "st_stuff.h" #include "g_game.h" #include "g_level.h" #include "r_data/r_translate.h" #include "v_palette.h" #include "r_data/colormaps.h" #include "poly_triangle.h" #include "swrenderer/drawers/r_draw_rgba.h" #include "screen_triangle.h" #include "x86.h" static void SortVertices(const TriDrawTriangleArgs *args, ShadedTriVertex **sortedVertices) { sortedVertices[0] = args->v1; sortedVertices[1] = args->v2; sortedVertices[2] = args->v3; if (sortedVertices[1]->y < sortedVertices[0]->y) std::swap(sortedVertices[0], sortedVertices[1]); if (sortedVertices[2]->y < sortedVertices[0]->y) std::swap(sortedVertices[0], sortedVertices[2]); if (sortedVertices[2]->y < sortedVertices[1]->y) std::swap(sortedVertices[1], sortedVertices[2]); } void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread) { // Sort vertices by Y position ShadedTriVertex *sortedVertices[3]; SortVertices(args, sortedVertices); int clipright = args->clipright; int clipbottom = args->clipbottom; // Ranges that different triangles edges are active int topY = (int)(sortedVertices[0]->y + 0.5f); int midY = (int)(sortedVertices[1]->y + 0.5f); int bottomY = (int)(sortedVertices[2]->y + 0.5f); topY = MAX(topY, 0); midY = clamp(midY, 0, clipbottom); bottomY = MIN(bottomY, clipbottom); if (topY >= bottomY) return; // Find start/end X positions for each line covered by the triangle: int leftEdge[MAXHEIGHT]; int rightEdge[MAXHEIGHT]; float longDX = sortedVertices[2]->x - sortedVertices[0]->x; float longDY = sortedVertices[2]->y - sortedVertices[0]->y; float longStep = longDX / longDY; float longPos = sortedVertices[0]->x + longStep * (topY + 0.5f - sortedVertices[0]->y) + 0.5f; if (topY < midY) { float shortDX = sortedVertices[1]->x - sortedVertices[0]->x; float shortDY = sortedVertices[1]->y - sortedVertices[0]->y; float shortStep = shortDX / shortDY; float shortPos = sortedVertices[0]->x + shortStep * (topY + 0.5f - sortedVertices[0]->y) + 0.5f; for (int y = topY; y < midY; y++) { int x0 = (int)shortPos; int x1 = (int)longPos; if (x1 < x0) std::swap(x0, x1); x0 = clamp(x0, 0, clipright); x1 = clamp(x1, 0, clipright); leftEdge[y] = x0; rightEdge[y] = x1; shortPos += shortStep; longPos += longStep; } } if (midY < bottomY) { float shortDX = sortedVertices[2]->x - sortedVertices[1]->x; float shortDY = sortedVertices[2]->y - sortedVertices[1]->y; float shortStep = shortDX / shortDY; float shortPos = sortedVertices[1]->x + shortStep * (midY + 0.5f - sortedVertices[1]->y) + 0.5f; for (int y = midY; y < bottomY; y++) { int x0 = (int)shortPos; int x1 = (int)longPos; if (x1 < x0) std::swap(x0, x1); x0 = clamp(x0, 0, clipright); x1 = clamp(x1, 0, clipright); leftEdge[y] = x0; rightEdge[y] = x1; shortPos += shortStep; longPos += longStep; } } // Draw the triangle: int bmode = (int)args->uniforms->BlendMode(); auto drawfunc = args->destBgra ? ScreenTriangle::SpanDrawers32[bmode] : ScreenTriangle::SpanDrawers8[bmode]; float stepXW = args->gradientX.W; float v1X = args->v1->x; float v1Y = args->v1->y; float v1W = args->v1->w; bool depthTest = args->uniforms->DepthTest(); bool stencilTest = true; bool writeColor = args->uniforms->WriteColor(); bool writeStencil = args->uniforms->WriteStencil(); bool writeDepth = args->uniforms->WriteDepth(); uint8_t stencilTestValue = args->uniforms->StencilTestValue(); uint8_t stencilWriteValue = args->uniforms->StencilWriteValue(); int num_cores = thread->num_cores; for (int y = topY + thread->skipped_by_thread(topY); y < bottomY; y += num_cores) { int x = leftEdge[y]; int xend = rightEdge[y]; float *zbufferLine = args->zbuffer + args->pitch * y; uint8_t *stencilLine = args->stencilbuffer + args->pitch * y; float startX = x + (0.5f - v1X); float startY = y + (0.5f - v1Y); float posXW = v1W + stepXW * startX + args->gradientY.W * startY + args->depthOffset; #ifndef NO_SSE __m128 mstepXW = _mm_set1_ps(stepXW * 4.0f); __m128 mfirstStepXW = _mm_setr_ps(0.0f, stepXW, stepXW + stepXW, stepXW + stepXW + stepXW); while (x < xend) { int xstart = x; if (depthTest && stencilTest) { int xendsse = x + ((xend - x) / 4); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15 && stencilLine[x] == stencilTestValue && stencilLine[x + 1] == stencilTestValue && stencilLine[x + 2] == stencilTestValue && stencilLine[x + 3] == stencilTestValue && x < xendsse) { if (writeDepth) _mm_storeu_ps(zbufferLine + x, mposXW); mposXW = _mm_add_ps(mposXW, mstepXW); x += 4; } posXW = _mm_cvtss_f32(mposXW); while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend) { if (writeDepth) zbufferLine[x] = posXW; posXW += stepXW; x++; } } else if (depthTest) { int xendsse = x + ((xend - x) / 4); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 15 && x < xendsse) { if (writeDepth) _mm_storeu_ps(zbufferLine + x, mposXW); mposXW = _mm_add_ps(mposXW, mstepXW); x += 4; } posXW = _mm_cvtss_f32(mposXW); while (zbufferLine[x] <= posXW && x < xend) { if (writeDepth) zbufferLine[x] = posXW; posXW += stepXW; x++; } } else if (stencilTest) { while (stencilLine[x] == stencilTestValue && x < xend) x++; } else { x = xend; } if (x > xstart) { if (writeColor) drawfunc(y, xstart, x, args); if (writeStencil) { for (int i = xstart; i < x; i++) stencilLine[i] = stencilWriteValue; } if (!depthTest && writeDepth) { for (int i = xstart; i < x; i++) { zbufferLine[i] = posXW; posXW += stepXW; } } } if (depthTest && stencilTest) { int xendsse = x + ((xend - x) / 4); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); while ((_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0 || stencilLine[x] != stencilTestValue || stencilLine[x + 1] != stencilTestValue || stencilLine[x + 2] != stencilTestValue || stencilLine[x + 3] != stencilTestValue) && x < xendsse) { mposXW = _mm_add_ps(mposXW, mstepXW); x += 4; } posXW = _mm_cvtss_f32(mposXW); while ((zbufferLine[x] > posXW || stencilLine[x] != stencilTestValue) && x < xend) { posXW += stepXW; x++; } } else if (depthTest) { int xendsse = x + ((xend - x) / 4); __m128 mposXW = _mm_add_ps(_mm_set1_ps(posXW), mfirstStepXW); while (_mm_movemask_ps(_mm_cmple_ps(_mm_loadu_ps(zbufferLine + x), mposXW)) == 0 && x < xendsse) { mposXW = _mm_add_ps(mposXW, mstepXW); x += 4; } posXW = _mm_cvtss_f32(mposXW); while (zbufferLine[x] > posXW && x < xend) { posXW += stepXW; x++; } } else if (stencilTest) { while (stencilLine[x] != stencilTestValue && x < xend) { posXW += stepXW; x++; } } } #else while (x < xend) { int xstart = x; if (depthTest && stencilTest) { while (zbufferLine[x] <= posXW && stencilLine[x] == stencilTestValue && x < xend) { if (writeDepth) zbufferLine[x] = posXW; posXW += stepXW; x++; } } else if (depthTest) { while (zbufferLine[x] <= posXW && x < xend) { if (writeDepth) zbufferLine[x] = posXW; posXW += stepXW; x++; } } else if (stencilTest) { while (stencilLine[x] == stencilTestValue && x < xend) x++; } else { x = xend; } if (x > xstart) { if (writeColor) drawfunc(y, xstart, x, args); if (writeStencil) { for (int i = xstart; i < x; i++) stencilLine[i] = stencilWriteValue; } if (!depthTest && writeDepth) { for (int i = xstart; i < x; i++) { zbufferLine[i] = posXW; posXW += stepXW; } } } if (depthTest && stencilTest) { while ((zbufferLine[x] > posXW || stencilLine[x] != stencilTestValue) && x < xend) { posXW += stepXW; x++; } } else if (depthTest) { while (zbufferLine[x] > posXW && x < xend) { posXW += stepXW; x++; } } else if (stencilTest) { while (stencilLine[x] != stencilTestValue && x < xend) { posXW += stepXW; x++; } } } #endif } } template void DrawSpanOpt32(int y, int x0, int x1, const TriDrawTriangleArgs *args) { using namespace TriScreenDrawerModes; float v1X, v1Y, v1W, v1U, v1V, v1WorldX, v1WorldY, v1WorldZ; float startX, startY; float stepW, stepU, stepV, stepWorldX, stepWorldY, stepWorldZ; float posW, posU, posV, posWorldX, posWorldY, posWorldZ; PolyLight *lights; int num_lights; float worldnormalX, worldnormalY, worldnormalZ; uint32_t dynlightcolor; const uint32_t *texPixels, *translation; int texWidth, texHeight; uint32_t fillcolor; int alpha; uint32_t light; fixed_t shade, lightpos, lightstep; uint32_t shade_fade_r, shade_fade_g, shade_fade_b, shade_light_r, shade_light_g, shade_light_b, desaturate, inv_desaturate; int16_t dynlights_r[MAXWIDTH / 16], dynlights_g[MAXWIDTH / 16], dynlights_b[MAXWIDTH / 16]; int16_t posdynlight_r, posdynlight_g, posdynlight_b; fixed_t lightarray[MAXWIDTH / 16]; v1X = args->v1->x; v1Y = args->v1->y; v1W = args->v1->w; v1U = args->v1->u * v1W; v1V = args->v1->v * v1W; startX = x0 + (0.5f - v1X); startY = y + (0.5f - v1Y); stepW = args->gradientX.W; stepU = args->gradientX.U; stepV = args->gradientX.V; posW = v1W + stepW * startX + args->gradientY.W * startY; posU = v1U + stepU * startX + args->gradientY.U * startY; posV = v1V + stepV * startX + args->gradientY.V * startY; texPixels = (const uint32_t*)args->uniforms->TexturePixels(); translation = (const uint32_t*)args->uniforms->Translation(); texWidth = args->uniforms->TextureWidth(); texHeight = args->uniforms->TextureHeight(); fillcolor = args->uniforms->Color(); alpha = args->uniforms->Alpha(); light = args->uniforms->Light(); if (OptT::Flags & SWOPT_FixedLight) { light += light >> 7; // 255 -> 256 } else { float globVis = args->uniforms->GlobVis() * (1.0f / 32.0f); shade = (fixed_t)((2.0f - (light + 12.0f) / 128.0f) * (float)FRACUNIT); lightpos = (fixed_t)(globVis * posW * (float)FRACUNIT); lightstep = (fixed_t)(globVis * stepW * (float)FRACUNIT); int affineOffset = x0 / 16 * 16 - x0; lightpos = lightpos + lightstep * affineOffset; lightstep = lightstep * 16; fixed_t maxvis = 24 * FRACUNIT / 32; fixed_t maxlight = 31 * FRACUNIT / 32; for (int x = x0 / 16; x <= x1 / 16 + 1; x++) { lightarray[x] = (FRACUNIT - clamp(shade - MIN(maxvis, lightpos), 0, maxlight)) >> 8; lightpos += lightstep; } int offset = x0 >> 4; int t1 = x0 & 15; int t0 = 16 - t1; lightpos = (lightarray[offset] * t0 + lightarray[offset + 1] * t1); for (int x = x0 / 16; x <= x1 / 16; x++) { lightarray[x] = lightarray[x + 1] - lightarray[x]; } } if (OptT::Flags & SWOPT_DynLights) { v1WorldX = args->v1->worldX * v1W; v1WorldY = args->v1->worldY * v1W; v1WorldZ = args->v1->worldZ * v1W; stepWorldX = args->gradientX.WorldX; stepWorldY = args->gradientX.WorldY; stepWorldZ = args->gradientX.WorldZ; posWorldX = v1WorldX + stepWorldX * startX + args->gradientY.WorldX * startY; posWorldY = v1WorldY + stepWorldY * startX + args->gradientY.WorldY * startY; posWorldZ = v1WorldZ + stepWorldZ * startX + args->gradientY.WorldZ * startY; lights = args->uniforms->Lights(); num_lights = args->uniforms->NumLights(); worldnormalX = args->uniforms->Normal().X; worldnormalY = args->uniforms->Normal().Y; worldnormalZ = args->uniforms->Normal().Z; dynlightcolor = args->uniforms->DynLightColor(); // The normal vector cannot be uniform when drawing models. Calculate and use the face normal: if (worldnormalX == 0.0f && worldnormalY == 0.0f && worldnormalZ == 0.0f) { float dx1 = args->v2->worldX - args->v1->worldX; float dy1 = args->v2->worldY - args->v1->worldY; float dz1 = args->v2->worldZ - args->v1->worldZ; float dx2 = args->v3->worldX - args->v1->worldX; float dy2 = args->v3->worldY - args->v1->worldY; float dz2 = args->v3->worldZ - args->v1->worldZ; worldnormalX = dy1 * dz2 - dz1 * dy2; worldnormalY = dz1 * dx2 - dx1 * dz2; worldnormalZ = dx1 * dy2 - dy1 * dx2; float lensqr = worldnormalX * worldnormalX + worldnormalY * worldnormalY + worldnormalZ * worldnormalZ; #ifndef NO_SSE float rcplen = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(lensqr))); #else float rcplen = 1.0f / sqrt(lensqr); #endif worldnormalX *= rcplen; worldnormalY *= rcplen; worldnormalZ *= rcplen; } int affineOffset = x0 / 16 * 16 - x0; float posLightW = posW + stepW * affineOffset; posWorldX = posWorldX + stepWorldX * affineOffset; posWorldY = posWorldY + stepWorldY * affineOffset; posWorldZ = posWorldZ + stepWorldZ * affineOffset; float stepLightW = stepW * 16.0f; stepWorldX *= 16.0f; stepWorldY *= 16.0f; stepWorldZ *= 16.0f; for (int x = x0 / 16; x <= x1 / 16 + 1; x++) { uint32_t lit_r = RPART(dynlightcolor); uint32_t lit_g = GPART(dynlightcolor); uint32_t lit_b = BPART(dynlightcolor); float rcp_posW = 1.0f / posLightW; float worldposX = posWorldX * rcp_posW; float worldposY = posWorldY * rcp_posW; float worldposZ = posWorldZ * rcp_posW; for (int i = 0; i < num_lights; i++) { float lightposX = lights[i].x; float lightposY = lights[i].y; float lightposZ = lights[i].z; float light_radius = lights[i].radius; uint32_t light_color = lights[i].color; bool is_attenuated = light_radius < 0.0f; if (is_attenuated) light_radius = -light_radius; // L = light-pos // dist = sqrt(dot(L, L)) // distance_attenuation = 1 - MIN(dist * (1/radius), 1) float Lx = lightposX - worldposX; float Ly = lightposY - worldposY; float Lz = lightposZ - worldposZ; float dist2 = Lx * Lx + Ly * Ly + Lz * Lz; #ifdef NO_SSE //float rcp_dist = 1.0f / sqrt(dist2); float rcp_dist = 1.0f / (dist2 * 0.01f); #else float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(dist2))); #endif float dist = dist2 * rcp_dist; float distance_attenuation = 256.0f - MIN(dist * light_radius, 256.0f); // The simple light type float simple_attenuation = distance_attenuation; // The point light type // diffuse = max(dot(N,normalize(L)),0) * attenuation Lx *= rcp_dist; Ly *= rcp_dist; Lz *= rcp_dist; float dotNL = worldnormalX * Lx + worldnormalY * Ly + worldnormalZ * Lz; float point_attenuation = MAX(dotNL, 0.0f) * distance_attenuation; uint32_t attenuation = (uint32_t)(is_attenuated ? (int32_t)point_attenuation : (int32_t)simple_attenuation); lit_r += (RPART(light_color) * attenuation) >> 8; lit_g += (GPART(light_color) * attenuation) >> 8; lit_b += (BPART(light_color) * attenuation) >> 8; } lit_r = MIN(lit_r, 255); lit_g = MIN(lit_g, 255); lit_b = MIN(lit_b, 255); dynlights_r[x] = lit_r; dynlights_g[x] = lit_g; dynlights_b[x] = lit_b; posLightW += stepLightW; posWorldX += stepWorldX; posWorldY += stepWorldY; posWorldZ += stepWorldZ; } int offset = x0 >> 4; int t1 = x0 & 15; int t0 = 16 - t1; posdynlight_r = (dynlights_r[offset] * t0 + dynlights_r[offset + 1] * t1); posdynlight_g = (dynlights_g[offset] * t0 + dynlights_g[offset + 1] * t1); posdynlight_b = (dynlights_b[offset] * t0 + dynlights_b[offset + 1] * t1); for (int x = x0 / 16; x <= x1 / 16; x++) { dynlights_r[x] = dynlights_r[x + 1] - dynlights_r[x]; dynlights_g[x] = dynlights_g[x + 1] - dynlights_g[x]; dynlights_b[x] = dynlights_b[x + 1] - dynlights_b[x]; } } if (OptT::Flags & SWOPT_ColoredFog) { shade_fade_r = args->uniforms->ShadeFadeRed(); shade_fade_g = args->uniforms->ShadeFadeGreen(); shade_fade_b = args->uniforms->ShadeFadeBlue(); shade_light_r = args->uniforms->ShadeLightRed(); shade_light_g = args->uniforms->ShadeLightGreen(); shade_light_b = args->uniforms->ShadeLightBlue(); desaturate = args->uniforms->ShadeDesaturate(); inv_desaturate = 256 - desaturate; } fixed_t fuzzscale; int _fuzzpos; if (ModeT::BlendOp == STYLEOP_Fuzz) { fuzzscale = (200 << FRACBITS) / viewheight; _fuzzpos = swrenderer::fuzzpos; } uint32_t *dest = (uint32_t*)args->dest; uint32_t *destLine = dest + args->pitch * y; int x = x0; while (x < x1) { if (ModeT::BlendOp == STYLEOP_Fuzz) { using namespace swrenderer; float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = APART(texPixels[texelX * texHeight + texelY]); sampleshadeout += sampleshadeout >> 7; // 255 -> 256 int scaled_x = (x * fuzzscale) >> FRACBITS; int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + _fuzzpos; fixed_t fuzzcount = FUZZTABLE << FRACBITS; fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; sampleshadeout = (sampleshadeout * alpha) >> 5; uint32_t a = 256 - sampleshadeout; uint32_t dest = destLine[x]; uint32_t out_r = (RPART(dest) * a) >> 8; uint32_t out_g = (GPART(dest) * a) >> 8; uint32_t out_b = (BPART(dest) * a) >> 8; destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else if (ModeT::SWFlags & SWSTYLEF_Skycap) { float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; uint32_t fg = texPixels[texelX * texHeight + texelY]; int start_fade = 2; // How fast it should fade out int alpha_top = clamp(v >> (16 - start_fade), 0, 256); int alpha_bottom = clamp(((2 << 24) - v) >> (16 - start_fade), 0, 256); int a = MIN(alpha_top, alpha_bottom); int inv_a = 256 - a; if (a == 256) { destLine[x] = fg; } else { uint32_t r = RPART(fg); uint32_t g = GPART(fg); uint32_t b = BPART(fg); uint32_t fg_a = APART(fg); uint32_t bg_red = RPART(fillcolor); uint32_t bg_green = GPART(fillcolor); uint32_t bg_blue = BPART(fillcolor); r = (r * a + bg_red * inv_a + 127) >> 8; g = (g * a + bg_green * inv_a + 127) >> 8; b = (b * a + bg_blue * inv_a + 127) >> 8; destLine[x] = MAKEARGB(255, r, g, b); } } else if (ModeT::SWFlags & SWSTYLEF_FogBoundary) { uint32_t fg = destLine[x]; int lightshade; if (OptT::Flags & SWOPT_FixedLight) { lightshade = light; } else { lightshade = lightpos >> 4; } uint32_t shadedfg_r, shadedfg_g, shadedfg_b; if (OptT::Flags & SWOPT_ColoredFog) { uint32_t fg_r = RPART(fg); uint32_t fg_g = GPART(fg); uint32_t fg_b = BPART(fg); uint32_t intensity = ((fg_r * 77 + fg_g * 143 + fg_b * 37) >> 8) * desaturate; int inv_light = 256 - lightshade; shadedfg_r = (((shade_fade_r * inv_light + ((fg_r * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_r) >> 8; shadedfg_g = (((shade_fade_g * inv_light + ((fg_g * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_g) >> 8; shadedfg_b = (((shade_fade_b * inv_light + ((fg_b * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_b) >> 8; } else { shadedfg_r = (RPART(fg) * lightshade) >> 8; shadedfg_g = (GPART(fg) * lightshade) >> 8; shadedfg_b = (BPART(fg) * lightshade) >> 8; } destLine[x] = MAKEARGB(255, shadedfg_r, shadedfg_g, shadedfg_b); } else { uint32_t fg = 0; if (ModeT::SWFlags & SWSTYLEF_Fill) { fg = fillcolor; } else { float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; if (ModeT::SWFlags & SWSTYLEF_Translated) { fg = translation[((const uint8_t*)texPixels)[texelX * texHeight + texelY]]; } else if (ModeT::Flags & STYLEF_RedIsAlpha) { fg = ((const uint8_t*)texPixels)[texelX * texHeight + texelY]; } else { fg = texPixels[texelX * texHeight + texelY]; } } if ((ModeT::Flags & STYLEF_ColorIsFixed) && !(ModeT::SWFlags & SWSTYLEF_Fill)) { if (ModeT::Flags & STYLEF_RedIsAlpha) fg = (fg << 24) | (fillcolor & 0x00ffffff); else fg = (fg & 0xff000000) | (fillcolor & 0x00ffffff); } uint32_t fgalpha = fg >> 24; if (!(ModeT::Flags & STYLEF_Alpha1)) { fgalpha = (fgalpha * alpha) >> 8; } int lightshade; if (OptT::Flags & SWOPT_FixedLight) { lightshade = light; } else { lightshade = lightpos >> 4; } uint32_t shadedfg_r, shadedfg_g, shadedfg_b; if (OptT::Flags & SWOPT_ColoredFog) { uint32_t fg_r = RPART(fg); uint32_t fg_g = GPART(fg); uint32_t fg_b = BPART(fg); uint32_t intensity = ((fg_r * 77 + fg_g * 143 + fg_b * 37) >> 8) * desaturate; int inv_light = 256 - lightshade; shadedfg_r = (((shade_fade_r * inv_light + ((fg_r * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_r) >> 8; shadedfg_g = (((shade_fade_g * inv_light + ((fg_g * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_g) >> 8; shadedfg_b = (((shade_fade_b * inv_light + ((fg_b * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_b) >> 8; if (OptT::Flags & SWOPT_DynLights) { uint32_t lit_r = posdynlight_r >> 4; uint32_t lit_g = posdynlight_g >> 4; uint32_t lit_b = posdynlight_b >> 4; shadedfg_r = MIN(shadedfg_r + ((fg_r * lit_r) >> 8), (uint32_t)255); shadedfg_g = MIN(shadedfg_g + ((fg_g * lit_g) >> 8), (uint32_t)255); shadedfg_b = MIN(shadedfg_b + ((fg_b * lit_b) >> 8), (uint32_t)255); } } else { if (OptT::Flags & SWOPT_DynLights) { uint32_t lit_r = posdynlight_r >> 4; uint32_t lit_g = posdynlight_g >> 4; uint32_t lit_b = posdynlight_b >> 4; shadedfg_r = (RPART(fg) * MIN(lightshade + lit_r, (uint32_t)256)) >> 8; shadedfg_g = (GPART(fg) * MIN(lightshade + lit_g, (uint32_t)256)) >> 8; shadedfg_b = (BPART(fg) * MIN(lightshade + lit_b, (uint32_t)256)) >> 8; } else { shadedfg_r = (RPART(fg) * lightshade) >> 8; shadedfg_g = (GPART(fg) * lightshade) >> 8; shadedfg_b = (BPART(fg) * lightshade) >> 8; } } if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_Zero) { destLine[x] = MAKEARGB(255, shadedfg_r, shadedfg_g, shadedfg_b); } else if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_One) { uint32_t dest = destLine[x]; if (ModeT::BlendOp == STYLEOP_Add) { uint32_t out_r = MIN(RPART(dest) + shadedfg_r, 255); uint32_t out_g = MIN(GPART(dest) + shadedfg_g, 255); uint32_t out_b = MIN(BPART(dest) + shadedfg_b, 255); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else if (ModeT::BlendOp == STYLEOP_RevSub) { uint32_t out_r = MAX(RPART(dest) - shadedfg_r, 0); uint32_t out_g = MAX(GPART(dest) - shadedfg_g, 0); uint32_t out_b = MAX(BPART(dest) - shadedfg_b, 0); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else //if (ModeT::BlendOp == STYLEOP_Sub) { uint32_t out_r = MAX(shadedfg_r - RPART(dest), 0); uint32_t out_g = MAX(shadedfg_g - GPART(dest), 0); uint32_t out_b = MAX(shadedfg_b - BPART(dest), 0); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } } else if (ModeT::SWFlags & SWSTYLEF_SrcColorOneMinusSrcColor) { uint32_t dest = destLine[x]; uint32_t sfactor_r = shadedfg_r; sfactor_r += sfactor_r >> 7; // 255 -> 256 uint32_t sfactor_g = shadedfg_g; sfactor_g += sfactor_g >> 7; // 255 -> 256 uint32_t sfactor_b = shadedfg_b; sfactor_b += sfactor_b >> 7; // 255 -> 256 uint32_t sfactor_a = fgalpha; sfactor_a += sfactor_a >> 7; // 255 -> 256 uint32_t dfactor_r = 256 - sfactor_r; uint32_t dfactor_g = 256 - sfactor_g; uint32_t dfactor_b = 256 - sfactor_b; uint32_t out_r = (RPART(dest) * dfactor_r + shadedfg_r * sfactor_r + 128) >> 8; uint32_t out_g = (GPART(dest) * dfactor_g + shadedfg_g * sfactor_g + 128) >> 8; uint32_t out_b = (BPART(dest) * dfactor_b + shadedfg_b * sfactor_b + 128) >> 8; destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else if (ModeT::BlendSrc == STYLEALPHA_Src && ModeT::BlendDest == STYLEALPHA_InvSrc && fgalpha == 255) { destLine[x] = MAKEARGB(255, shadedfg_r, shadedfg_g, shadedfg_b); } else if (ModeT::BlendSrc != STYLEALPHA_Src || ModeT::BlendDest != STYLEALPHA_InvSrc || fgalpha != 0) { uint32_t dest = destLine[x]; uint32_t sfactor = fgalpha; sfactor += sfactor >> 7; // 255 -> 256 uint32_t src_r = shadedfg_r * sfactor; uint32_t src_g = shadedfg_g * sfactor; uint32_t src_b = shadedfg_b * sfactor; uint32_t dest_r = RPART(dest); uint32_t dest_g = GPART(dest); uint32_t dest_b = BPART(dest); if (ModeT::BlendDest == STYLEALPHA_One) { dest_r <<= 8; dest_g <<= 8; dest_b <<= 8; } else { uint32_t dfactor = 256 - sfactor; dest_r *= dfactor; dest_g *= dfactor; dest_b *= dfactor; } uint32_t out_r, out_g, out_b; if (ModeT::BlendOp == STYLEOP_Add) { if (ModeT::BlendDest == STYLEALPHA_One) { out_r = MIN((dest_r + src_r + 128) >> 8, 255); out_g = MIN((dest_g + src_g + 128) >> 8, 255); out_b = MIN((dest_b + src_b + 128) >> 8, 255); } else { out_r = (dest_r + src_r + 128) >> 8; out_g = (dest_g + src_g + 128) >> 8; out_b = (dest_b + src_b + 128) >> 8; } } else if (ModeT::BlendOp == STYLEOP_RevSub) { out_r = MAX(static_cast(dest_r - src_r + 128) >> 8, 0); out_g = MAX(static_cast(dest_g - src_g + 128) >> 8, 0); out_b = MAX(static_cast(dest_b - src_b + 128) >> 8, 0); } else //if (ModeT::BlendOp == STYLEOP_Sub) { out_r = MAX(static_cast(src_r - dest_r + 128) >> 8, 0); out_g = MAX(static_cast(src_g - dest_g + 128) >> 8, 0); out_b = MAX(static_cast(src_b - dest_b + 128) >> 8, 0); } destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } } posW += stepW; posU += stepU; posV += stepV; if (OptT::Flags & SWOPT_DynLights) { posdynlight_r += dynlights_r[x >> 4]; posdynlight_g += dynlights_g[x >> 4]; posdynlight_b += dynlights_b[x >> 4]; } if (!(OptT::Flags & SWOPT_FixedLight)) lightpos += lightarray[x >> 4]; x++; } } template void DrawSpan32(int y, int x0, int x1, const TriDrawTriangleArgs *args) { using namespace TriScreenDrawerModes; if (args->uniforms->NumLights() == 0 && args->uniforms->DynLightColor() == 0) { if (!args->uniforms->FixedLight()) { if (args->uniforms->SimpleShade()) DrawSpanOpt32(y, x0, x1, args); else DrawSpanOpt32(y, x0, x1, args); } else { if (args->uniforms->SimpleShade()) DrawSpanOpt32(y, x0, x1, args); else DrawSpanOpt32(y, x0, x1, args); } } else { if (!args->uniforms->FixedLight()) { if (args->uniforms->SimpleShade()) DrawSpanOpt32(y, x0, x1, args); else DrawSpanOpt32(y, x0, x1, args); } else { if (args->uniforms->SimpleShade()) DrawSpanOpt32(y, x0, x1, args); else DrawSpanOpt32(y, x0, x1, args); } } } template void DrawSpanOpt8(int y, int x0, int x1, const TriDrawTriangleArgs *args) { using namespace TriScreenDrawerModes; float v1X, v1Y, v1W, v1U, v1V, v1WorldX, v1WorldY, v1WorldZ; float startX, startY; float stepW, stepU, stepV, stepWorldX, stepWorldY, stepWorldZ; float posW, posU, posV, posWorldX, posWorldY, posWorldZ; PolyLight *lights; int num_lights; float worldnormalX, worldnormalY, worldnormalZ; uint32_t dynlightcolor; const uint8_t *colormaps, *texPixels, *translation; int texWidth, texHeight; uint32_t fillcolor, capcolor; int alpha; uint32_t light; fixed_t shade, lightpos, lightstep; int16_t dynlights_r[MAXWIDTH / 16], dynlights_g[MAXWIDTH / 16], dynlights_b[MAXWIDTH / 16]; int16_t posdynlight_r, posdynlight_g, posdynlight_b; fixed_t lightarray[MAXWIDTH / 16]; v1X = args->v1->x; v1Y = args->v1->y; v1W = args->v1->w; v1U = args->v1->u * v1W; v1V = args->v1->v * v1W; startX = x0 + (0.5f - v1X); startY = y + (0.5f - v1Y); stepW = args->gradientX.W; stepU = args->gradientX.U; stepV = args->gradientX.V; posW = v1W + stepW * startX + args->gradientY.W * startY; posU = v1U + stepU * startX + args->gradientY.U * startY; posV = v1V + stepV * startX + args->gradientY.V * startY; texPixels = args->uniforms->TexturePixels(); translation = args->uniforms->Translation(); texWidth = args->uniforms->TextureWidth(); texHeight = args->uniforms->TextureHeight(); fillcolor = args->uniforms->Color(); alpha = args->uniforms->Alpha(); colormaps = args->uniforms->BaseColormap(); light = args->uniforms->Light(); if (ModeT::SWFlags & SWSTYLEF_Skycap) capcolor = GPalette.BaseColors[fillcolor].d; if (OptT::Flags & SWOPT_FixedLight) { light += light >> 7; // 255 -> 256 light = ((256 - light) * NUMCOLORMAPS) & 0xffffff00; } else { float globVis = args->uniforms->GlobVis() * (1.0f / 32.0f); shade = (fixed_t)((2.0f - (light + 12.0f) / 128.0f) * (float)FRACUNIT); lightpos = (fixed_t)(globVis * posW * (float)FRACUNIT); lightstep = (fixed_t)(globVis * stepW * (float)FRACUNIT); int affineOffset = x0 / 16 * 16 - x0; lightpos = lightpos + lightstep * affineOffset; lightstep = lightstep * 16; fixed_t maxvis = 24 * FRACUNIT / 32; fixed_t maxlight = 31 * FRACUNIT / 32; for (int x = x0 / 16; x <= x1 / 16 + 1; x++) { lightarray[x] = (clamp(shade - MIN(maxvis, lightpos), 0, maxlight) >> 8) << 5; lightpos += lightstep; } int offset = x0 >> 4; int t1 = x0 & 15; int t0 = 16 - t1; lightpos = (lightarray[offset] * t0 + lightarray[offset + 1] * t1); for (int x = x0 / 16; x <= x1 / 16; x++) { lightarray[x] = lightarray[x + 1] - lightarray[x]; } } if (OptT::Flags & SWOPT_DynLights) { v1WorldX = args->v1->worldX * v1W; v1WorldY = args->v1->worldY * v1W; v1WorldZ = args->v1->worldZ * v1W; stepWorldX = args->gradientX.WorldX; stepWorldY = args->gradientX.WorldY; stepWorldZ = args->gradientX.WorldZ; posWorldX = v1WorldX + stepWorldX * startX + args->gradientY.WorldX * startY; posWorldY = v1WorldY + stepWorldY * startX + args->gradientY.WorldY * startY; posWorldZ = v1WorldZ + stepWorldZ * startX + args->gradientY.WorldZ * startY; lights = args->uniforms->Lights(); num_lights = args->uniforms->NumLights(); worldnormalX = args->uniforms->Normal().X; worldnormalY = args->uniforms->Normal().Y; worldnormalZ = args->uniforms->Normal().Z; dynlightcolor = args->uniforms->DynLightColor(); // The normal vector cannot be uniform when drawing models. Calculate and use the face normal: if (worldnormalX == 0.0f && worldnormalY == 0.0f && worldnormalZ == 0.0f) { float dx1 = args->v2->worldX - args->v1->worldX; float dy1 = args->v2->worldY - args->v1->worldY; float dz1 = args->v2->worldZ - args->v1->worldZ; float dx2 = args->v3->worldX - args->v1->worldX; float dy2 = args->v3->worldY - args->v1->worldY; float dz2 = args->v3->worldZ - args->v1->worldZ; worldnormalX = dy1 * dz2 - dz1 * dy2; worldnormalY = dz1 * dx2 - dx1 * dz2; worldnormalZ = dx1 * dy2 - dy1 * dx2; float lensqr = worldnormalX * worldnormalX + worldnormalY * worldnormalY + worldnormalZ * worldnormalZ; #ifndef NO_SSE float rcplen = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(lensqr))); #else float rcplen = 1.0f / sqrt(lensqr); #endif worldnormalX *= rcplen; worldnormalY *= rcplen; worldnormalZ *= rcplen; } int affineOffset = x0 / 16 * 16 - x0; float posLightW = posW + stepW * affineOffset; posWorldX = posWorldX + stepWorldX * affineOffset; posWorldY = posWorldY + stepWorldY * affineOffset; posWorldZ = posWorldZ + stepWorldZ * affineOffset; float stepLightW = stepW * 16.0f; stepWorldX *= 16.0f; stepWorldY *= 16.0f; stepWorldZ *= 16.0f; for (int x = x0 / 16; x <= x1 / 16 + 1; x++) { uint32_t lit_r = RPART(dynlightcolor); uint32_t lit_g = GPART(dynlightcolor); uint32_t lit_b = BPART(dynlightcolor); float rcp_posW = 1.0f / posLightW; float worldposX = posWorldX * rcp_posW; float worldposY = posWorldY * rcp_posW; float worldposZ = posWorldZ * rcp_posW; for (int i = 0; i < num_lights; i++) { float lightposX = lights[i].x; float lightposY = lights[i].y; float lightposZ = lights[i].z; float light_radius = lights[i].radius; uint32_t light_color = lights[i].color; bool is_attenuated = light_radius < 0.0f; if (is_attenuated) light_radius = -light_radius; // L = light-pos // dist = sqrt(dot(L, L)) // distance_attenuation = 1 - MIN(dist * (1/radius), 1) float Lx = lightposX - worldposX; float Ly = lightposY - worldposY; float Lz = lightposZ - worldposZ; float dist2 = Lx * Lx + Ly * Ly + Lz * Lz; #ifdef NO_SSE //float rcp_dist = 1.0f / sqrt(dist2); float rcp_dist = 1.0f / (dist2 * 0.01f); #else float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(dist2))); #endif float dist = dist2 * rcp_dist; float distance_attenuation = 256.0f - MIN(dist * light_radius, 256.0f); // The simple light type float simple_attenuation = distance_attenuation; // The point light type // diffuse = max(dot(N,normalize(L)),0) * attenuation Lx *= rcp_dist; Ly *= rcp_dist; Lz *= rcp_dist; float dotNL = worldnormalX * Lx + worldnormalY * Ly + worldnormalZ * Lz; float point_attenuation = MAX(dotNL, 0.0f) * distance_attenuation; uint32_t attenuation = (uint32_t)(is_attenuated ? (int32_t)point_attenuation : (int32_t)simple_attenuation); lit_r += (RPART(light_color) * attenuation) >> 8; lit_g += (GPART(light_color) * attenuation) >> 8; lit_b += (BPART(light_color) * attenuation) >> 8; } lit_r = MIN(lit_r, 255); lit_g = MIN(lit_g, 255); lit_b = MIN(lit_b, 255); dynlights_r[x] = lit_r; dynlights_g[x] = lit_g; dynlights_b[x] = lit_b; posLightW += stepLightW; posWorldX += stepWorldX; posWorldY += stepWorldY; posWorldZ += stepWorldZ; } int offset = x0 >> 4; int t1 = x0 & 15; int t0 = 16 - t1; posdynlight_r = (dynlights_r[offset] * t0 + dynlights_r[offset + 1] * t1); posdynlight_g = (dynlights_g[offset] * t0 + dynlights_g[offset + 1] * t1); posdynlight_b = (dynlights_b[offset] * t0 + dynlights_b[offset + 1] * t1); for (int x = x0 / 16; x <= x1 / 16; x++) { dynlights_r[x] = dynlights_r[x + 1] - dynlights_r[x]; dynlights_g[x] = dynlights_g[x + 1] - dynlights_g[x]; dynlights_b[x] = dynlights_b[x + 1] - dynlights_b[x]; } } fixed_t fuzzscale; int _fuzzpos; if (ModeT::BlendOp == STYLEOP_Fuzz) { fuzzscale = (200 << FRACBITS) / viewheight; _fuzzpos = swrenderer::fuzzpos; } uint8_t *dest = (uint8_t*)args->dest; uint8_t *destLine = dest + args->pitch * y; int x = x0; while (x < x1) { if (ModeT::BlendOp == STYLEOP_Fuzz) { using namespace swrenderer; float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = (texPixels[texelX * texHeight + texelY] != 0) ? 256 : 0; int scaled_x = (x * fuzzscale) >> FRACBITS; int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + _fuzzpos; fixed_t fuzzcount = FUZZTABLE << FRACBITS; fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; sampleshadeout = (sampleshadeout * alpha) >> 5; uint32_t a = 256 - sampleshadeout; uint32_t dest = GPalette.BaseColors[destLine[x]].d; uint32_t r = (RPART(dest) * a) >> 8; uint32_t g = (GPART(dest) * a) >> 8; uint32_t b = (BPART(dest) * a) >> 8; destLine[x] = RGB256k.All[((r >> 2) << 12) | ((g >> 2) << 6) | (b >> 2)]; } else if (ModeT::SWFlags & SWSTYLEF_Skycap) { float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; int fg = texPixels[texelX * texHeight + texelY]; int start_fade = 2; // How fast it should fade out int alpha_top = clamp(v >> (16 - start_fade), 0, 256); int alpha_bottom = clamp(((2 << 24) - v) >> (16 - start_fade), 0, 256); int a = MIN(alpha_top, alpha_bottom); int inv_a = 256 - a; if (a == 256) { destLine[x] = fg; } else { uint32_t texelrgb = GPalette.BaseColors[fg].d; uint32_t r = RPART(texelrgb); uint32_t g = GPART(texelrgb); uint32_t b = BPART(texelrgb); uint32_t fg_a = APART(texelrgb); uint32_t bg_red = RPART(capcolor); uint32_t bg_green = GPART(capcolor); uint32_t bg_blue = BPART(capcolor); r = (r * a + bg_red * inv_a + 127) >> 8; g = (g * a + bg_green * inv_a + 127) >> 8; b = (b * a + bg_blue * inv_a + 127) >> 8; destLine[x] = RGB256k.All[((r >> 2) << 12) | ((g >> 2) << 6) | (b >> 2)]; } } else if (ModeT::SWFlags & SWSTYLEF_FogBoundary) { int fg = destLine[x]; uint8_t shadedfg; if (OptT::Flags & SWOPT_FixedLight) { shadedfg = colormaps[light + fg]; } else { int lightshade = (lightpos >> 4) & 0xffffff00; shadedfg = colormaps[lightshade + fg]; } destLine[x] = shadedfg; } else { int fg; if (ModeT::SWFlags & SWSTYLEF_Fill) { fg = fillcolor; } else { float rcpW = 0x01000000 / posW; int32_t u = (int32_t)(posU * rcpW); int32_t v = (int32_t)(posV * rcpW); uint32_t texelX = ((((uint32_t)u << 8) >> 16) * texWidth) >> 16; uint32_t texelY = ((((uint32_t)v << 8) >> 16) * texHeight) >> 16; fg = texPixels[texelX * texHeight + texelY]; } int fgalpha = 255; if (ModeT::BlendDest == STYLEALPHA_InvSrc) { if (fg == 0) fgalpha = 0; } if ((ModeT::Flags & STYLEF_ColorIsFixed) && !(ModeT::SWFlags & SWSTYLEF_Fill)) { if (ModeT::Flags & STYLEF_RedIsAlpha) fgalpha = fg; fg = fillcolor; } if (!(ModeT::Flags & STYLEF_Alpha1)) { fgalpha = (fgalpha * alpha) >> 8; } if (ModeT::SWFlags & SWSTYLEF_Translated) fg = translation[fg]; uint8_t shadedfg; if (OptT::Flags & SWOPT_FixedLight) { shadedfg = colormaps[light + fg]; } else { int lightshade = (lightpos >> 4) & 0xffffff00; shadedfg = colormaps[lightshade + fg]; } if (OptT::Flags & SWOPT_DynLights) { if (posdynlight_r | posdynlight_g | posdynlight_b) { uint32_t lit_r = posdynlight_r >> 4; uint32_t lit_g = posdynlight_g >> 4; uint32_t lit_b = posdynlight_b >> 4; uint32_t fgrgb = GPalette.BaseColors[fg]; uint32_t shadedfgrgb = GPalette.BaseColors[shadedfg]; uint32_t out_r = MIN(((RPART(fgrgb) * lit_r) >> 8) + RPART(shadedfgrgb), (uint32_t)255); uint32_t out_g = MIN(((GPART(fgrgb) * lit_g) >> 8) + GPART(shadedfgrgb), (uint32_t)255); uint32_t out_b = MIN(((BPART(fgrgb) * lit_b) >> 8) + BPART(shadedfgrgb), (uint32_t)255); shadedfg = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } } if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_Zero) { destLine[x] = shadedfg; } else if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_One) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; if (ModeT::BlendOp == STYLEOP_Add) { uint32_t out_r = MIN(RPART(dest) + RPART(src), 255); uint32_t out_g = MIN(GPART(dest) + GPART(src), 255); uint32_t out_b = MIN(BPART(dest) + BPART(src), 255); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else if (ModeT::BlendOp == STYLEOP_RevSub) { uint32_t out_r = MAX(RPART(dest) - RPART(src), 0); uint32_t out_g = MAX(GPART(dest) - GPART(src), 0); uint32_t out_b = MAX(BPART(dest) - BPART(src), 0); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else //if (ModeT::BlendOp == STYLEOP_Sub) { uint32_t out_r = MAX(RPART(src) - RPART(dest), 0); uint32_t out_g = MAX(GPART(src) - GPART(dest), 0); uint32_t out_b = MAX(BPART(src) - BPART(dest), 0); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } } else if (ModeT::SWFlags & SWSTYLEF_SrcColorOneMinusSrcColor) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; uint32_t sfactor_r = RPART(src); sfactor_r += sfactor_r >> 7; // 255 -> 256 uint32_t sfactor_g = GPART(src); sfactor_g += sfactor_g >> 7; // 255 -> 256 uint32_t sfactor_b = BPART(src); sfactor_b += sfactor_b >> 7; // 255 -> 256 uint32_t sfactor_a = fgalpha; sfactor_a += sfactor_a >> 7; // 255 -> 256 uint32_t dfactor_r = 256 - sfactor_r; uint32_t dfactor_g = 256 - sfactor_g; uint32_t dfactor_b = 256 - sfactor_b; uint32_t out_r = (RPART(dest) * dfactor_r + RPART(src) * sfactor_r + 128) >> 8; uint32_t out_g = (GPART(dest) * dfactor_g + GPART(src) * sfactor_g + 128) >> 8; uint32_t out_b = (BPART(dest) * dfactor_b + BPART(src) * sfactor_b + 128) >> 8; destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else if (ModeT::BlendSrc == STYLEALPHA_Src && ModeT::BlendDest == STYLEALPHA_InvSrc && fgalpha == 255) { destLine[x] = shadedfg; } else if (ModeT::BlendSrc != STYLEALPHA_Src || ModeT::BlendDest != STYLEALPHA_InvSrc || fgalpha != 0) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; uint32_t sfactor = fgalpha; sfactor += sfactor >> 7; // 255 -> 256 uint32_t dfactor = 256 - sfactor; uint32_t src_r = RPART(src) * sfactor; uint32_t src_g = GPART(src) * sfactor; uint32_t src_b = BPART(src) * sfactor; uint32_t dest_r = RPART(dest); uint32_t dest_g = GPART(dest); uint32_t dest_b = BPART(dest); if (ModeT::BlendDest == STYLEALPHA_One) { dest_r <<= 8; dest_g <<= 8; dest_b <<= 8; } else { uint32_t dfactor = 256 - sfactor; dest_r *= dfactor; dest_g *= dfactor; dest_b *= dfactor; } uint32_t out_r, out_g, out_b; if (ModeT::BlendOp == STYLEOP_Add) { if (ModeT::BlendDest == STYLEALPHA_One) { out_r = MIN((dest_r + src_r + 128) >> 8, 255); out_g = MIN((dest_g + src_g + 128) >> 8, 255); out_b = MIN((dest_b + src_b + 128) >> 8, 255); } else { out_r = (dest_r + src_r + 128) >> 8; out_g = (dest_g + src_g + 128) >> 8; out_b = (dest_b + src_b + 128) >> 8; } } else if (ModeT::BlendOp == STYLEOP_RevSub) { out_r = MAX(static_cast(dest_r - src_r + 128) >> 8, 0); out_g = MAX(static_cast(dest_g - src_g + 128) >> 8, 0); out_b = MAX(static_cast(dest_b - src_b + 128) >> 8, 0); } else //if (ModeT::BlendOp == STYLEOP_Sub) { out_r = MAX(static_cast(src_r - dest_r + 128) >> 8, 0); out_g = MAX(static_cast(src_g - dest_g + 128) >> 8, 0); out_b = MAX(static_cast(src_b - dest_b + 128) >> 8, 0); } destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } } posW += stepW; posU += stepU; posV += stepV; if (OptT::Flags & SWOPT_DynLights) { posdynlight_r += dynlights_r[x >> 4]; posdynlight_g += dynlights_g[x >> 4]; posdynlight_b += dynlights_b[x >> 4]; } if (!(OptT::Flags & SWOPT_FixedLight)) lightpos += lightarray[x >> 4]; x++; } } template void DrawSpan8(int y, int x0, int x1, const TriDrawTriangleArgs *args) { using namespace TriScreenDrawerModes; if (args->uniforms->NumLights() == 0 && args->uniforms->DynLightColor() == 0) { if (!args->uniforms->FixedLight()) DrawSpanOpt8(y, x0, x1, args); else DrawSpanOpt8(y, x0, x1, args); } else { if (!args->uniforms->FixedLight()) DrawSpanOpt8(y, x0, x1, args); else DrawSpanOpt8(y, x0, x1, args); } } template void DrawRect8(const void *destOrg, int destWidth, int destHeight, int destPitch, const RectDrawArgs *args, PolyTriangleThreadData *thread) { using namespace TriScreenDrawerModes; int x0 = clamp((int)(args->X0() + 0.5f), 0, destWidth); int x1 = clamp((int)(args->X1() + 0.5f), 0, destWidth); int y0 = clamp((int)(args->Y0() + 0.5f), 0, destHeight); int y1 = clamp((int)(args->Y1() + 0.5f), 0, destHeight); if (x1 <= x0 || y1 <= y0) return; const uint8_t *colormaps, *texPixels, *translation; int texWidth, texHeight; uint32_t fillcolor; int alpha; uint32_t light; texPixels = args->TexturePixels(); translation = args->Translation(); texWidth = args->TextureWidth(); texHeight = args->TextureHeight(); fillcolor = args->Color(); alpha = args->Alpha(); colormaps = args->BaseColormap(); light = args->Light(); light += light >> 7; // 255 -> 256 light = ((256 - light) * NUMCOLORMAPS) & 0xffffff00; fixed_t fuzzscale; int _fuzzpos; if (ModeT::BlendOp == STYLEOP_Fuzz) { fuzzscale = (200 << FRACBITS) / viewheight; _fuzzpos = swrenderer::fuzzpos; } float fstepU = (args->U1() - args->U0()) / (args->X1() - args->X0()); float fstepV = (args->V1() - args->V0()) / (args->Y1() - args->Y0()); uint32_t startU = (int32_t)((args->U0() + (x0 + 0.5f - args->X0()) * fstepU) * 0x1000000); uint32_t startV = (int32_t)((args->V0() + (y0 + 0.5f - args->Y0()) * fstepV) * 0x1000000); uint32_t stepU = (int32_t)(fstepU * 0x1000000); uint32_t stepV = (int32_t)(fstepV * 0x1000000); uint32_t posV = startV; int num_cores = thread->num_cores; int skip = thread->skipped_by_thread(y0); posV += skip * stepV; stepV *= num_cores; for (int y = y0 + skip; y < y1; y += num_cores, posV += stepV) { uint8_t *destLine = ((uint8_t*)destOrg) + y * destPitch; uint32_t posU = startU; for (int x = x0; x < x1; x++) { if (ModeT::BlendOp == STYLEOP_Fuzz) { using namespace swrenderer; uint32_t texelX = (((posU << 8) >> 16) * texWidth) >> 16; uint32_t texelY = (((posV << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = (texPixels[texelX * texHeight + texelY] != 0) ? 256 : 0; int scaled_x = (x * fuzzscale) >> FRACBITS; int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + _fuzzpos; fixed_t fuzzcount = FUZZTABLE << FRACBITS; fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; sampleshadeout = (sampleshadeout * alpha) >> 5; uint32_t a = 256 - sampleshadeout; uint32_t dest = GPalette.BaseColors[destLine[x]].d; uint32_t r = (RPART(dest) * a) >> 8; uint32_t g = (GPART(dest) * a) >> 8; uint32_t b = (BPART(dest) * a) >> 8; destLine[x] = RGB256k.All[((r >> 2) << 12) | ((g >> 2) << 6) | (b >> 2)]; } else { int fg = 0; if (ModeT::SWFlags & SWSTYLEF_Fill) { fg = fillcolor; } else { uint32_t texelX = (((posU << 8) >> 16) * texWidth) >> 16; uint32_t texelY = (((posV << 8) >> 16) * texHeight) >> 16; fg = texPixels[texelX * texHeight + texelY]; } int fgalpha = 255; if (ModeT::BlendDest == STYLEALPHA_InvSrc) { if (fg == 0) fgalpha = 0; } if ((ModeT::Flags & STYLEF_ColorIsFixed) && !(ModeT::SWFlags & SWSTYLEF_Fill)) { if (ModeT::Flags & STYLEF_RedIsAlpha) fgalpha = fg; fg = fillcolor; } if (!(ModeT::Flags & STYLEF_Alpha1)) { fgalpha = (fgalpha * alpha) >> 8; } if (ModeT::SWFlags & SWSTYLEF_Translated) fg = translation[fg]; uint8_t shadedfg = colormaps[light + fg]; if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_Zero) { destLine[x] = shadedfg; } else if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_One) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; if (ModeT::BlendOp == STYLEOP_Add) { uint32_t out_r = MIN(RPART(dest) + RPART(src), 255); uint32_t out_g = MIN(GPART(dest) + GPART(src), 255); uint32_t out_b = MIN(BPART(dest) + BPART(src), 255); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else if (ModeT::BlendOp == STYLEOP_RevSub) { uint32_t out_r = MAX(RPART(dest) - RPART(src), 0); uint32_t out_g = MAX(GPART(dest) - GPART(src), 0); uint32_t out_b = MAX(BPART(dest) - BPART(src), 0); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else //if (ModeT::BlendOp == STYLEOP_Sub) { uint32_t out_r = MAX(RPART(src) - RPART(dest), 0); uint32_t out_g = MAX(GPART(src) - GPART(dest), 0); uint32_t out_b = MAX(BPART(src) - BPART(dest), 0); destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } } else if (ModeT::SWFlags & SWSTYLEF_SrcColorOneMinusSrcColor) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; uint32_t sfactor_r = RPART(src); sfactor_r += sfactor_r >> 7; // 255 -> 256 uint32_t sfactor_g = GPART(src); sfactor_g += sfactor_g >> 7; // 255 -> 256 uint32_t sfactor_b = BPART(src); sfactor_b += sfactor_b >> 7; // 255 -> 256 uint32_t sfactor_a = fgalpha; sfactor_a += sfactor_a >> 7; // 255 -> 256 uint32_t dfactor_r = 256 - sfactor_r; uint32_t dfactor_g = 256 - sfactor_g; uint32_t dfactor_b = 256 - sfactor_b; uint32_t out_r = (RPART(dest) * dfactor_r + RPART(src) * sfactor_r + 128) >> 8; uint32_t out_g = (GPART(dest) * dfactor_g + GPART(src) * sfactor_g + 128) >> 8; uint32_t out_b = (BPART(dest) * dfactor_b + BPART(src) * sfactor_b + 128) >> 8; destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } else if (ModeT::BlendSrc == STYLEALPHA_Src && ModeT::BlendDest == STYLEALPHA_InvSrc && fgalpha == 255) { destLine[x] = shadedfg; } else if (ModeT::BlendSrc != STYLEALPHA_Src || ModeT::BlendDest != STYLEALPHA_InvSrc || fgalpha != 0) { uint32_t src = GPalette.BaseColors[shadedfg]; uint32_t dest = GPalette.BaseColors[destLine[x]]; uint32_t sfactor = fgalpha; sfactor += sfactor >> 7; // 255 -> 256 uint32_t dfactor = 256 - sfactor; uint32_t src_r = RPART(src) * sfactor; uint32_t src_g = GPART(src) * sfactor; uint32_t src_b = BPART(src) * sfactor; uint32_t dest_r = RPART(dest); uint32_t dest_g = GPART(dest); uint32_t dest_b = BPART(dest); if (ModeT::BlendDest == STYLEALPHA_One) { dest_r <<= 8; dest_g <<= 8; dest_b <<= 8; } else { uint32_t dfactor = 256 - sfactor; dest_r *= dfactor; dest_g *= dfactor; dest_b *= dfactor; } uint32_t out_r, out_g, out_b; if (ModeT::BlendOp == STYLEOP_Add) { if (ModeT::BlendDest == STYLEALPHA_One) { out_r = MIN((dest_r + src_r + 128) >> 8, 255); out_g = MIN((dest_g + src_g + 128) >> 8, 255); out_b = MIN((dest_b + src_b + 128) >> 8, 255); } else { out_r = (dest_r + src_r + 128) >> 8; out_g = (dest_g + src_g + 128) >> 8; out_b = (dest_b + src_b + 128) >> 8; } } else if (ModeT::BlendOp == STYLEOP_RevSub) { out_r = MAX(static_cast(dest_r - src_r + 128) >> 8, 0); out_g = MAX(static_cast(dest_g - src_g + 128) >> 8, 0); out_b = MAX(static_cast(dest_b - src_b + 128) >> 8, 0); } else //if (ModeT::BlendOp == STYLEOP_Sub) { out_r = MAX(static_cast(src_r - dest_r + 128) >> 8, 0); out_g = MAX(static_cast(src_g - dest_g + 128) >> 8, 0); out_b = MAX(static_cast(src_b - dest_b + 128) >> 8, 0); } destLine[x] = RGB256k.All[((out_r >> 2) << 12) | ((out_g >> 2) << 6) | (out_b >> 2)]; } } posU += stepU; } } } template void DrawRectOpt32(const void *destOrg, int destWidth, int destHeight, int destPitch, const RectDrawArgs *args, PolyTriangleThreadData *thread) { using namespace TriScreenDrawerModes; int x0 = clamp((int)(args->X0() + 0.5f), 0, destWidth); int x1 = clamp((int)(args->X1() + 0.5f), 0, destWidth); int y0 = clamp((int)(args->Y0() + 0.5f), 0, destHeight); int y1 = clamp((int)(args->Y1() + 0.5f), 0, destHeight); if (x1 <= x0 || y1 <= y0) return; const uint32_t *texPixels, *translation; int texWidth, texHeight; uint32_t fillcolor; int alpha; uint32_t light; uint32_t shade_fade_r, shade_fade_g, shade_fade_b, shade_light_r, shade_light_g, shade_light_b, desaturate, inv_desaturate; texPixels = (const uint32_t*)args->TexturePixels(); translation = (const uint32_t*)args->Translation(); texWidth = args->TextureWidth(); texHeight = args->TextureHeight(); fillcolor = args->Color(); alpha = args->Alpha(); light = args->Light(); light += light >> 7; // 255 -> 256 if (OptT::Flags & SWOPT_ColoredFog) { shade_fade_r = args->ShadeFadeRed(); shade_fade_g = args->ShadeFadeGreen(); shade_fade_b = args->ShadeFadeBlue(); shade_light_r = args->ShadeLightRed(); shade_light_g = args->ShadeLightGreen(); shade_light_b = args->ShadeLightBlue(); desaturate = args->ShadeDesaturate(); inv_desaturate = 256 - desaturate; } fixed_t fuzzscale; int _fuzzpos; if (ModeT::BlendOp == STYLEOP_Fuzz) { fuzzscale = (200 << FRACBITS) / viewheight; _fuzzpos = swrenderer::fuzzpos; } float fstepU = (args->U1() - args->U0()) / (args->X1() - args->X0()); float fstepV = (args->V1() - args->V0()) / (args->Y1() - args->Y0()); uint32_t startU = (int32_t)((args->U0() + (x0 + 0.5f - args->X0()) * fstepU) * 0x1000000); uint32_t startV = (int32_t)((args->V0() + (y0 + 0.5f - args->Y0()) * fstepV) * 0x1000000); uint32_t stepU = (int32_t)(fstepU * 0x1000000); uint32_t stepV = (int32_t)(fstepV * 0x1000000); uint32_t posV = startV; int num_cores = thread->num_cores; int skip = thread->skipped_by_thread(y0); posV += skip * stepV; stepV *= num_cores; for (int y = y0 + skip; y < y1; y += num_cores, posV += stepV) { uint32_t *destLine = ((uint32_t*)destOrg) + y * destPitch; uint32_t posU = startU; for (int x = x0; x < x1; x++) { if (ModeT::BlendOp == STYLEOP_Fuzz) { using namespace swrenderer; uint32_t texelX = (((posU << 8) >> 16) * texWidth) >> 16; uint32_t texelY = (((posV << 8) >> 16) * texHeight) >> 16; unsigned int sampleshadeout = APART(texPixels[texelX * texHeight + texelY]); sampleshadeout += sampleshadeout >> 7; // 255 -> 256 int scaled_x = (x * fuzzscale) >> FRACBITS; int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + _fuzzpos; fixed_t fuzzcount = FUZZTABLE << FRACBITS; fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; unsigned int alpha = fuzzoffset[fuzz >> FRACBITS]; sampleshadeout = (sampleshadeout * alpha) >> 5; uint32_t a = 256 - sampleshadeout; uint32_t dest = destLine[x]; uint32_t out_r = (RPART(dest) * a) >> 8; uint32_t out_g = (GPART(dest) * a) >> 8; uint32_t out_b = (BPART(dest) * a) >> 8; destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else { uint32_t fg = 0; if (ModeT::SWFlags & SWSTYLEF_Fill) { fg = fillcolor; } else if (ModeT::SWFlags & SWSTYLEF_FogBoundary) { fg = destLine[x]; } else { uint32_t texelX = (((posU << 8) >> 16) * texWidth) >> 16; uint32_t texelY = (((posV << 8) >> 16) * texHeight) >> 16; if (ModeT::SWFlags & SWSTYLEF_Translated) { fg = translation[((const uint8_t*)texPixels)[texelX * texHeight + texelY]]; } else if (ModeT::Flags & STYLEF_RedIsAlpha) { fg = ((const uint8_t*)texPixels)[texelX * texHeight + texelY]; } else { fg = texPixels[texelX * texHeight + texelY]; } } if ((ModeT::Flags & STYLEF_ColorIsFixed) && !(ModeT::SWFlags & SWSTYLEF_Fill)) { if (ModeT::Flags & STYLEF_RedIsAlpha) fg = (fg << 24) | (fillcolor & 0x00ffffff); else fg = (fg & 0xff000000) | (fillcolor & 0x00ffffff); } uint32_t fgalpha = fg >> 24; if (!(ModeT::Flags & STYLEF_Alpha1)) { fgalpha = (fgalpha * alpha) >> 8; } int lightshade = light; uint32_t lit_r = 0, lit_g = 0, lit_b = 0; uint32_t shadedfg_r, shadedfg_g, shadedfg_b; if (OptT::Flags & SWOPT_ColoredFog) { uint32_t fg_r = RPART(fg); uint32_t fg_g = GPART(fg); uint32_t fg_b = BPART(fg); uint32_t intensity = ((fg_r * 77 + fg_g * 143 + fg_b * 37) >> 8) * desaturate; shadedfg_r = (((shade_fade_r + ((fg_r * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_r) >> 8; shadedfg_g = (((shade_fade_g + ((fg_g * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_g) >> 8; shadedfg_b = (((shade_fade_b + ((fg_b * inv_desaturate + intensity) >> 8) * lightshade) >> 8) * shade_light_b) >> 8; } else { shadedfg_r = (RPART(fg) * lightshade) >> 8; shadedfg_g = (GPART(fg) * lightshade) >> 8; shadedfg_b = (BPART(fg) * lightshade) >> 8; } if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_Zero) { destLine[x] = MAKEARGB(255, shadedfg_r, shadedfg_g, shadedfg_b); } else if (ModeT::BlendSrc == STYLEALPHA_One && ModeT::BlendDest == STYLEALPHA_One) { uint32_t dest = destLine[x]; if (ModeT::BlendOp == STYLEOP_Add) { uint32_t out_r = MIN(RPART(dest) + shadedfg_r, 255); uint32_t out_g = MIN(GPART(dest) + shadedfg_g, 255); uint32_t out_b = MIN(BPART(dest) + shadedfg_b, 255); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else if (ModeT::BlendOp == STYLEOP_RevSub) { uint32_t out_r = MAX(RPART(dest) - shadedfg_r, 0); uint32_t out_g = MAX(GPART(dest) - shadedfg_g, 0); uint32_t out_b = MAX(BPART(dest) - shadedfg_b, 0); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else //if (ModeT::BlendOp == STYLEOP_Sub) { uint32_t out_r = MAX(shadedfg_r - RPART(dest), 0); uint32_t out_g = MAX(shadedfg_g - GPART(dest), 0); uint32_t out_b = MAX(shadedfg_b - BPART(dest), 0); destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } } else if (ModeT::SWFlags & SWSTYLEF_SrcColorOneMinusSrcColor) { uint32_t dest = destLine[x]; uint32_t sfactor_r = shadedfg_r; sfactor_r += sfactor_r >> 7; // 255 -> 256 uint32_t sfactor_g = shadedfg_g; sfactor_g += sfactor_g >> 7; // 255 -> 256 uint32_t sfactor_b = shadedfg_b; sfactor_b += sfactor_b >> 7; // 255 -> 256 uint32_t sfactor_a = fgalpha; sfactor_a += sfactor_a >> 7; // 255 -> 256 uint32_t dfactor_r = 256 - sfactor_r; uint32_t dfactor_g = 256 - sfactor_g; uint32_t dfactor_b = 256 - sfactor_b; uint32_t out_r = (RPART(dest) * dfactor_r + shadedfg_r * sfactor_r + 128) >> 8; uint32_t out_g = (GPART(dest) * dfactor_g + shadedfg_g * sfactor_g + 128) >> 8; uint32_t out_b = (BPART(dest) * dfactor_b + shadedfg_b * sfactor_b + 128) >> 8; destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } else if (ModeT::BlendSrc == STYLEALPHA_Src && ModeT::BlendDest == STYLEALPHA_InvSrc && fgalpha == 255) { destLine[x] = MAKEARGB(255, shadedfg_r, shadedfg_g, shadedfg_b); } else if (ModeT::BlendSrc != STYLEALPHA_Src || ModeT::BlendDest != STYLEALPHA_InvSrc || fgalpha != 0) { uint32_t dest = destLine[x]; uint32_t sfactor = fgalpha; sfactor += sfactor >> 7; // 255 -> 256 uint32_t src_r = shadedfg_r * sfactor; uint32_t src_g = shadedfg_g * sfactor; uint32_t src_b = shadedfg_b * sfactor; uint32_t dest_r = RPART(dest); uint32_t dest_g = GPART(dest); uint32_t dest_b = BPART(dest); if (ModeT::BlendDest == STYLEALPHA_One) { dest_r <<= 8; dest_g <<= 8; dest_b <<= 8; } else { uint32_t dfactor = 256 - sfactor; dest_r *= dfactor; dest_g *= dfactor; dest_b *= dfactor; } uint32_t out_r, out_g, out_b; if (ModeT::BlendOp == STYLEOP_Add) { if (ModeT::BlendDest == STYLEALPHA_One) { out_r = MIN((dest_r + src_r + 128) >> 8, 255); out_g = MIN((dest_g + src_g + 128) >> 8, 255); out_b = MIN((dest_b + src_b + 128) >> 8, 255); } else { out_r = (dest_r + src_r + 128) >> 8; out_g = (dest_g + src_g + 128) >> 8; out_b = (dest_b + src_b + 128) >> 8; } } else if (ModeT::BlendOp == STYLEOP_RevSub) { out_r = MAX(static_cast(dest_r - src_r + 128) >> 8, 0); out_g = MAX(static_cast(dest_g - src_g + 128) >> 8, 0); out_b = MAX(static_cast(dest_b - src_b + 128) >> 8, 0); } else //if (ModeT::BlendOp == STYLEOP_Sub) { out_r = MAX(static_cast(src_r - dest_r + 128) >> 8, 0); out_g = MAX(static_cast(src_g - dest_g + 128) >> 8, 0); out_b = MAX(static_cast(src_b - dest_b + 128) >> 8, 0); } destLine[x] = MAKEARGB(255, out_r, out_g, out_b); } } posU += stepU; } } } template void DrawRect32(const void *destOrg, int destWidth, int destHeight, int destPitch, const RectDrawArgs *args, PolyTriangleThreadData *thread) { using namespace TriScreenDrawerModes; if (args->SimpleShade()) DrawRectOpt32(destOrg, destWidth, destHeight, destPitch, args, thread); else DrawRectOpt32(destOrg, destWidth, destHeight, destPitch, args, thread); } void(*ScreenTriangle::SpanDrawers8[])(int, int, int, const TriDrawTriangleArgs *) = { &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8, &DrawSpan8 }; void(*ScreenTriangle::SpanDrawers32[])(int, int, int, const TriDrawTriangleArgs *) = { &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32, &DrawSpan32 }; void(*ScreenTriangle::RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *) = { &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8, &DrawRect8 }; void(*ScreenTriangle::RectDrawers32[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *) = { &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32, &DrawRect32 }; int ScreenTriangle::FuzzStart = 0;