diff --git a/source/build/include/build.h b/source/build/include/build.h index 56a5669d0..b00bf1be8 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -92,6 +92,7 @@ enum { RS_ALIGN_MASK = 768, RS_STRETCH = 1024, + ROTATESPRITE_FULL16 = 2048, RS_MODELSUBST= 4096, // ROTATESPRITE_MAX-1 is the mask of all externally available orientation bits ROTATESPRITE_MAX = 8192, @@ -497,9 +498,22 @@ void videoInit(); void videoClearViewableArea(int32_t dacol); void videoClearScreen(int32_t dacol); void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang); +void rotatesprite_(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, + int8_t dashade, uint8_t dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend, + int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2, FGameTexture *pic = nullptr, int basepal = 0); class F2DDrawer; +void twod_rotatesprite(F2DDrawer* twod, int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, + int8_t dashade, uint8_t dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend, + int32_t clipx1, int32_t clipy1, int32_t clipx2, int32_t clipy2, FGameTexture* pic = nullptr, int basepal = 0); +////////// specialized rotatesprite wrappers for (very) often used cases ////////// +static FORCE_INLINE void rotatesprite(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, + int8_t dashade, uint8_t dapalnum, int32_t dastat, + int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2, FGameTexture* pic = nullptr, int basepal = 0) +{ + rotatesprite_(sx, sy, z, a, picnum, dashade, dapalnum, dastat, 0, 0, cx1, cy1, cx2, cy2, pic, basepal); +} void getzrange(const vec3_t *pos, int16_t sectnum, int32_t *ceilz, int32_t *ceilhit, int32_t *florz, int32_t *florhit, int32_t walldist, uint32_t cliptype) ATTRIBUTE((nonnull(1,3,4,5,6))); diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index b36c67ada..2b5f5de72 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -1660,6 +1660,268 @@ void FillPolygon(int* rx1, int* ry1, int* xb1, int32_t npoints, int picnum, int #include "build.h" #include "../src/engine_priv.h" +//sx,sy center of sprite; screen coords*65536 +//z zoom*65536. > is zoomed in +//a angle (0 is default) +//dastat&1 1:translucence +//dastat&2 1:auto-scale mode (use 320*200 coordinates) +//dastat&4 1:y-flip +//dastat&8 1:don't clip to startumost/startdmost +//dastat&16 1:force point passed to be top-left corner, 0:Editart center +//dastat&32 1:reverse translucence +//dastat&64 1:non-masked, 0:masked +//dastat&128 1:draw all pages (permanent - no longer used) +//cx1,... clip window (actual screen coords) + +//========================================================================== +// +// INTERNAL helper function for classic/polymost dorotatesprite +// sxptr, sxptr, z: in/out +// ret_yxaspect, ret_xyaspect: out +// +//========================================================================== + +static int32_t dorotspr_handle_bit2(int32_t* sxptr, int32_t* syptr, int32_t* z, int32_t dastat, int32_t cx1_plus_cx2, int32_t cy1_plus_cy2) +{ + if ((dastat & RS_AUTO) == 0) + { + if (!(dastat & RS_STRETCH) && 4 * ydim <= 3 * xdim) + { + return (10 << 16) / 12; + } + else + { + return xyaspect; + } + } + else + { + // dastat&2: Auto window size scaling + const int32_t oxdim = xdim; + const int32_t oydim = ydim; + int32_t xdim = oxdim; // SHADOWS global + int32_t ydim = oydim; + + int32_t zoomsc, sx = *sxptr, sy = *syptr; + int32_t ouryxaspect = yxaspect, ourxyaspect = xyaspect; + + if (!(dastat & RS_STRETCH) && 4 * ydim <= 3 * xdim) + { + if ((dastat & RS_ALIGN_MASK) && (dastat & RS_ALIGN_MASK) != RS_ALIGN_MASK) + sx += (scale(120 << 16, xdim, ydim) - (160 << 16)) * ((!(dastat & RS_ALIGN_R))?-1:1); + + if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK) + ydim = scale(xdim, 3, 4); + else + xdim = scale(ydim, 4, 3); + + ouryxaspect = (12 << 16) / 10; + ourxyaspect = (10 << 16) / 12; + } + + // screen center to s[xy], 320<<16 coords. + const int32_t normxofs = sx - (320 << 15), normyofs = sy - (200 << 15); + + // nasty hacks go here + if (!(dastat & RS_NOCLIP)) + { + const int32_t twice_midcx = cx1_plus_cx2 + 2; + + // screen x center to sx1, scaled to viewport + const int32_t scaledxofs = scale(normxofs, scale(xdimen, xdim, oxdim), 320); + + sx = ((twice_midcx) << 15) + scaledxofs; + + zoomsc = xdimenscale; //= scale(xdimen,yxaspect,320); + + if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK) + zoomsc = scale(zoomsc, ydim, oydim); + + sy = ((cy1_plus_cy2 + 2) << 15) + mulscale16(normyofs, zoomsc); + } + else + { + //If not clipping to startmosts, & auto-scaling on, as a + //hard-coded bonus, scale to full screen instead + + sx = (xdim << 15) + 32768 + scale(normxofs, xdim, 320); + + zoomsc = scale(xdim, ouryxaspect, 320); + sy = (ydim << 15) + 32768 + mulscale16(normyofs, zoomsc); + + if ((dastat & RS_ALIGN_MASK) == RS_ALIGN_MASK) + sy += (oydim - ydim) << 15; + else + sx += (oxdim - xdim) << 15; + + if (dastat & RS_CENTERORIGIN) + sx += oxdim << 15; + } + + *sxptr = sx; + *syptr = sy; + *z = mulscale16(*z, zoomsc); + + return ourxyaspect; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void twod_rotatesprite(F2DDrawer *twod, int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, + int8_t dashade, uint8_t dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend, + int32_t clipx1, int32_t clipy1, int32_t clipx2, int32_t clipy2, FGameTexture* pic, int basepal) +{ + // todo: re-add +#if 0 + if (!tex && (dastat & RS_MODELSUBST)) + { + tileUpdatePicnum(&picnum, (int16_t)0xc000); + if ((tileWidth(picnum) <= 0) || (tileHeight(picnum) <= 0)) return; + if (hw_models && tile2model[picnum].hudmem[(dastat & 4) >> 2]) + { + polymost_dorotatespritemodel(sx, sy, z, a, picnum, dashade, dapalnum, dastat, daalpha, dablend, guniqhudid); + return; + } + } + else +#endif + if (!pic) tileUpdatePicnum(&picnum, 0); + + + + F2DDrawer::RenderCommand dg = {}; + int method = 0; + + dg.mType = F2DDrawer::DrawTypeTriangles; + if (clipx1 > 0 || clipy1 > 0 || clipx2 < screen->GetWidth() - 1 || clipy2 < screen->GetHeight() - 1) + { + dg.mScissor[0] = clipx1; + dg.mScissor[1] = clipy1; + dg.mScissor[2] = clipx2 + 1; + dg.mScissor[3] = clipy2 + 1; + dg.mFlags |= F2DDrawer::DTF_Scissor; + } + + if (!(dastat & RS_NOMASK)) + { + if (dastat & RS_TRANS1) + method |= (dastat & RS_TRANS2) ? DAMETH_TRANS2 : DAMETH_TRANS1; + else + method |= DAMETH_MASK; + + dg.mRenderStyle = GetRenderStyle(dablend, (dastat & RS_TRANS2) ? 1 : 0); + } + else + { + dg.mRenderStyle = LegacyRenderStyles[STYLE_Normal]; + } + + dg.mTexture = pic ? pic : tileGetTexture(picnum); + if (!dg.mTexture || !dg.mTexture->isValid()) return; // empty tile. + + // todo: check for hires replacements. + + // The weapon drawer needs to use the global base palette. + dg.mTranslationId = TRANSLATION(Translation_Remap + basepal, dapalnum); + + dg.mVertCount = 4; + dg.mVertIndex = (int)twod->mVertices.Reserve(4); + auto ptr = &twod->mVertices[dg.mVertIndex]; + float drawpoly_alpha = daalpha * (1.0f / 255.0f); + float alpha = GetAlphaFromBlend(method, dablend) * (1.f - drawpoly_alpha); // Hmmm... + PalEntry p; + + if (!hw_useindexedcolortextures) + { + int light = clamp(scale((numshades - dashade), 255, numshades), 0, 255); + p = PalEntry((uint8_t)(alpha * 255), light, light, light); + } + else + { + p = PalEntry((uint8_t)(alpha * 255), 255, 255, 255); + dg.mLightLevel = clamp(dashade, 0, numshades); + } + + + vec2_t const siz = { (int)dg.mTexture->GetDisplayWidth(), (int)dg.mTexture->GetDisplayHeight() }; + vec2_16_t ofs = { 0, 0 }; + + if (!(dastat & RS_TOPLEFT)) + { + if (!pic && !(dastat & RS_CENTER)) + { + ofs = { int16_t(tileLeftOffset(picnum) + (siz.x >> 1)), + int16_t(tileTopOffset(picnum) + (siz.y >> 1)) }; + } + else + { + ofs = { int16_t((siz.x >> 1)), + int16_t((siz.y >> 1)) }; + } + } + + if (dastat & RS_YFLIP) + ofs.y = siz.y - ofs.y; + + int32_t aspectcorrect = dorotspr_handle_bit2(&sx, &sy, &z, dastat, clipx1 + clipx2, clipy1 + clipy2); + + int32_t cosang = mulscale14(sintable[(a + 512) & 2047], z); + int32_t cosang2 = cosang; + int32_t sinang = mulscale14(sintable[a & 2047], z); + int32_t sinang2 = sinang; + + if ((dastat & RS_AUTO) || (!(dastat & RS_NOCLIP))) // Don't aspect unscaled perms + { + cosang2 = mulscale16(cosang2, aspectcorrect); + sinang2 = mulscale16(sinang2, aspectcorrect); + } + + int cx0 = sx - ofs.x * cosang2 + ofs.y * sinang2; + int cy0 = sy - ofs.x * sinang - ofs.y * cosang; + + int cx1 = cx0 + siz.x * cosang2; + int cy1 = cy0 + siz.x * sinang; + + int cx3 = cx0 - siz.y * sinang2; + int cy3 = cy0 + siz.y * cosang; + + int cx2 = cx1 + cx3 - cx0; + int cy2 = cy1 + cy3 - cy0; + + float y = (dastat & RS_YFLIP) ? 1.f : 0.f; + + ptr->Set(cx0 / 65536.f, cy0 / 65536.f, 0.f, 0.f, y, p); ptr++; + ptr->Set(cx1 / 65536.f, cy1 / 65536.f, 0.f, 1.f, y, p); ptr++; + ptr->Set(cx2 / 65536.f, cy2 / 65536.f, 0.f, 1.f, 1.f - y, p); ptr++; + ptr->Set(cx3 / 65536.f, cy3 / 65536.f, 0.f, 0.f, 1.f - y, p); ptr++; + dg.mIndexIndex = twod->mIndices.Size(); + dg.mIndexCount += 6; + twod->AddIndices(dg.mVertIndex, 6, 0, 1, 2, 0, 2, 3); + twod->AddCommand(&dg); + +} + +void rotatesprite_(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum, + int8_t dashade, uint8_t dapalnum, int32_t dastat, uint8_t daalpha, uint8_t dablend, + int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2, FGameTexture* tex, int basepal) +{ + if (!tex && (unsigned)picnum >= MAXTILES) + return; + + if ((cx1 > cx2) || (cy1 > cy2)) return; + if (z <= 16) return; + + // We must store all calls in the 2D drawer so that the backend can operate on a clean 3D view. + twod_rotatesprite(twod, sx, sy, z, a, picnum, dashade, dapalnum, dastat, daalpha, dablend, cx1, cy1, cx2, cy2, tex, basepal); +} + + + // // fillpolygon (internal)