/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2003, 2005, 2008 Sven Eberhardt * Copyright (c) 2007 Peter Wortmann * Copyright (c) 2007-2010 Günther Brammer * Copyright (c) 2010 Benjamin Herr * Copyright (c) 2010 Nicolas Hake * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de * * Portions might be copyrighted by other authors who have contributed * to OpenClonk. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * See isc_license.txt for full license and disclaimer. * * "Clonk" is a registered trademark of Matthes Bender. * See clonk_trademark_license.txt for full license. */ /* Solid areas of objects, put into the landscape */ #include #include #include #include #include #include void C4SolidMask::Put(bool fCauseInstability, C4TargetRect *pClipRect, bool fRestoreAttachment) { // If not put, put mask to background, // storing background pixels in cSolidMask. // No mask if (!pSolidMask || !pSolidMaskMatBuff) { iAttachingObjectsCount = 0; return; } // Contained if (pForObject->Contained) { iAttachingObjectsCount = 0; return; } // Mask is put if (fCauseInstability) CheckConsistency(); bool RegularPut; if (!pClipRect) { // Regular Put: Update MaskPutRect and MaskPutRotation MaskPutRotation = pForObject->r; pClipRect = &MaskPutRect; RegularPut = true; } else { // Reput by C4SolidMask::Remove // Don't change MaskPutRotation or MaskPutRect // Intersect ClipRect with the MaskPutRect if (!pClipRect->ClipBy(MaskPutRect)) return; RegularPut = false; } // Lock mask surface int iPitch = pForObject->SolidMask.Wdt; int xcnt,ycnt,iTx,iTy; // Put mask pixels BYTE byPixel; // not rotated? if (!MaskPutRotation) { // calc put rect if (RegularPut) { int ox, oy; ox = pForObject->GetX() + pForObject->Def->Shape.x + pForObject->SolidMask.tx; oy = pForObject->GetY() + pForObject->Def->Shape.y + pForObject->SolidMask.ty; MaskPutRect.x = ox; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.x = 0; } else MaskPutRect.tx = 0; MaskPutRect.y = oy; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.y = 0; } else MaskPutRect.ty = 0; MaskPutRect.Wdt = Min(ox + pForObject->SolidMask.Wdt, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min(oy + pForObject->SolidMask.Hgt, GBackHgt) - MaskPutRect.y; } // fill rect with mask for (ycnt=0; ycntHgt; ++ycnt) { BYTE *pPix=pSolidMask + (ycnt+pClipRect->ty)*pForObject->SolidMask.Wdt + pClipRect->tx; for (xcnt=0; xcntWdt; ++xcnt,++pPix) { if (*pPix) { // solid mask present here // calc position in landscape iTx=pClipRect->x+xcnt; iTy=pClipRect->y+ycnt; // is background mat to be stored? always do this in the given rect if (!MaskPut) { // get background pixel byPixel=GBackPix(iTx,iTy); // store it. If MCVehic, also store in initial put, but won't be used in restore // do not overwrite current value in re-put issued by SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[(ycnt+pClipRect->ty)*MatBuffPitch+xcnt+pClipRect->tx]=byPixel; } // and set mask _SBackPix(iTx,iTy,MCVehic); } else // no SolidMask: mark buffer as unused here if (!MaskPut) pSolidMaskMatBuff[(ycnt+pClipRect->ty)*MatBuffPitch+xcnt+pClipRect->tx]=MCVehic; } } } else { // calc matrix for given rotation C4Real Ma1 = Cos(itofix(-MaskPutRotation)), Ma2 = -Sin(itofix(-MaskPutRotation)), Mb1 = Sin(itofix(-MaskPutRotation)), Mb2 = Cos(itofix(-MaskPutRotation)); // get upper-left corner of landscape copy rect int centerx = pForObject->Def->Shape.x + pForObject->SolidMask.tx + pForObject->SolidMask.Wdt / 2; int centery = pForObject->Def->Shape.y + pForObject->SolidMask.ty + pForObject->SolidMask.Hgt / 2; int xstart = pForObject->GetX() + fixtoi(Ma1 * itofix(centerx) - Ma2 * itofix(centery)) - MatBuffPitch / 2; int ystart = pForObject->GetY() + fixtoi(-Mb1 * itofix(centerx) + Mb2 * itofix(centery)) - MatBuffPitch / 2; // store put rect if (RegularPut) { MaskPutRect.x = xstart; if (MaskPutRect.x < 0) { MaskPutRect.tx = -MaskPutRect.x; MaskPutRect.Wdt = MaskPutRect.x; MaskPutRect.x = 0; } else { MaskPutRect.tx = 0; MaskPutRect.Wdt = 0; } MaskPutRect.y = ystart; if (MaskPutRect.y < 0) { MaskPutRect.ty = -MaskPutRect.y; MaskPutRect.Hgt = MaskPutRect.y; MaskPutRect.y = 0; } else { MaskPutRect.ty = 0; MaskPutRect.Hgt = 0; } MaskPutRect.Wdt = Min(xstart + MatBuffPitch, GBackWdt) - MaskPutRect.x; MaskPutRect.Hgt = Min(ystart + MatBuffPitch, GBackHgt) - MaskPutRect.y; } // go through clipping rect const C4Real y0 = itofix(pClipRect->ty - MatBuffPitch/2); const C4Real x0 = itofix(pClipRect->tx - MatBuffPitch/2); iTy=pClipRect->y; int w = pForObject->SolidMask.Wdt; int h = pForObject->SolidMask.Hgt; C4Real ya = y0 * Ma2; C4Real yb = y0 * Mb2; for (ycnt = 0; ycnt < pClipRect->Hgt; ycnt++) { iTx=pClipRect->x; int i = (ycnt + pClipRect->ty) * MatBuffPitch + pClipRect->tx; C4Real xa = x0 * Ma1; C4Real xb = x0 * Mb1; for (xcnt = 0; xcnt < pClipRect->Wdt; xcnt++) { // calc position in solidmask buffer int iMx = fixtoi(xa + ya) + w / 2; int iMy = fixtoi(xb + yb) + h / 2; // in bounds? and solidmask? if (iMx >= 0 && iMy >= 0 && iMx < w && iMy < h && pSolidMask[iMy*iPitch+iMx]) { // is background mat to be stored? if (!MaskPut) { // get background pixel byPixel=_GBackPix(iTx,iTy); // store it. If MCVehic, also store in initial put, but won't be used in restore // do not overwrite current value in re-put issued by SolidMask-remover if (byPixel != MCVehic || RegularPut) pSolidMaskMatBuff[i + xcnt] = byPixel; } // set mask pix _SBackPix(iTx, iTy, MCVehic); } else if (!MaskPut) // mark pix as unused in buf pSolidMaskMatBuff[i+xcnt] = MCVehic; xa += Ma1; xb += Mb1; ++iTx; } ya += Ma2; yb += Mb2; ++iTy; } } // Store mask put status MaskPut=true; // restore attached object positions if moved if (fRestoreAttachment && iAttachingObjectsCount) { int32_t dx = pForObject->GetX() - MaskRemovalX; int32_t dy = pForObject->GetY() - MaskRemovalY; if (dx|dy) for (int i = 0; i < iAttachingObjectsCount; ++i) { C4Object *pObj = ppAttachingObjects[i]; if (pObj->IsMoveableBySolidMask(pForObject->GetPlane())) if (!pObj->Shape.ContactCheck(pObj->GetX()+dx, pObj->GetY()+dy)) if (pObj->iLastAttachMovementFrame != Game.FrameCounter) { pObj->iLastAttachMovementFrame = Game.FrameCounter; pObj->MovePosition(dx, dy); } } iAttachingObjectsCount = 0; } if (fCauseInstability) CheckConsistency(); } int32_t C4SolidMask::DensityProvider::GetDensity(int32_t x, int32_t y) const { // outside SolidMask: free x -= rSolidMaskData.MaskPutRect.x; y -= rSolidMaskData.MaskPutRect.y; if (!Inside(x, 0, rSolidMaskData.MaskPutRect.Wdt-1) || !Inside(y, 0, rSolidMaskData.MaskPutRect.Hgt-1)) return 0; // check put mask. Easy for unrotated BYTE *pPix; if (!rSolidMaskData.MaskPutRotation) { pPix=rSolidMaskData.pSolidMask+(y+rSolidMaskData.MaskPutRect.ty)*rSolidMaskData.pForObject->SolidMask.Wdt+rSolidMaskData.MaskPutRect.tx+x; if (*pPix == 0xff) return C4M_Solid; else return 0; } else { // Using put-buffer for rotated masks // for SolidMask-pixels not put because there was another SolidMask already, this will not return solid pPix=rSolidMaskData.pSolidMaskMatBuff+(y+rSolidMaskData.MaskPutRect.ty)*rSolidMaskData.MatBuffPitch+rSolidMaskData.MaskPutRect.tx+x; if (*pPix == MCVehic) return 0; else return C4M_Solid; } } void C4SolidMask::Remove(bool fBackupAttachment) { // If put, restore background pixels from buffer // Not put if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; CheckConsistency(); // reput background pixels for (int ycnt=0; ycntPrev) if (pSolid->MaskPut) if (pSolid->MaskPutRect.Overlap(MaskPutRect)) { // set clipping rect for all calls, since they may modify it ClipRect.Set(MaskPutRect.x, MaskPutRect.y, MaskPutRect.Wdt, MaskPutRect.Hgt, 0, 0); // doubled solidmask-pixels have just been removed in the clipped area! pSolid->MaskPut = false; // re-put the solidmask pSolid->Put(false, &ClipRect, false); } // backup attachment if desired: Backup old pos and all objects that attach to or lie on the SolidMask if (fBackupAttachment) { MaskRemovalX = pForObject->GetX(); MaskRemovalY = pForObject->GetY(); iAttachingObjectsCount = 0; C4LArea SolidArea(&::Objects.Sectors, MaskPutRect.x-1, MaskPutRect.y-1, MaskPutRect.Wdt+2, MaskPutRect.Hgt+2); C4LSector *pSct; C4Object *pObj; for (C4ObjectList *pLst=SolidArea.FirstObjectShapes(&pSct); pLst; pLst=SolidArea.NextObjectShapes(pLst, &pSct)) for (C4ObjectLink *clnk=pLst->First; clnk; clnk=clnk->Next) if ((pObj = clnk->Obj) && pObj != pForObject && pObj->IsMoveableBySolidMask(pForObject->GetPlane()) && !pObj->Shape.CheckContact(pObj->GetX(),pObj->GetY())) { // check for any contact to own SolidMask - attach-directions, bottom - "stuck" (CNAT_Center) is ignored, because that causes problems with things being stuck in basements :( int iVtx = 0; for (; iVtx < pObj->Shape.VtxNum; ++iVtx) if (pObj->Shape.GetVertexContact(iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->GetX(), pObj->GetY(), DensityProvider(pForObject, *this))) if (pObj->Shape.GetVertexContact(iVtx, pObj->Action.t_attach | CNAT_Bottom, pObj->GetX(), pObj->GetY(), DensityProvider(pForObject, *this))) break; if (iVtx == pObj->Shape.VtxNum) continue; // no contact // contact: Add object to list if (iAttachingObjectsCapacity == iAttachingObjectsCount) { iAttachingObjectsCapacity += 4; C4Object **ppNewAttachingObjects = new C4Object *[iAttachingObjectsCapacity]; if (iAttachingObjectsCount) memcpy(ppNewAttachingObjects, ppAttachingObjects, sizeof(C4Object *) * iAttachingObjectsCount); delete [] ppAttachingObjects; ppAttachingObjects = ppNewAttachingObjects; } ppAttachingObjects[iAttachingObjectsCount++] = pObj; } } CheckConsistency(); } void C4SolidMask::Draw(C4TargetFacet &cgo) { // only if put if (!MaskPut) return; // set topface facet C4Facet fct; fct.Set(pForObject->GetGraphics()->GetBitmap(), pForObject->SolidMask.x, pForObject->SolidMask.y, pForObject->SolidMask.Wdt, pForObject->SolidMask.Hgt); // draw it if (MaskPutRotation) fct.DrawXR(cgo.Surface, pForObject->GetX()+pForObject->Shape.x+cgo.X-cgo.TargetX+pForObject->SolidMask.tx, pForObject->GetY()+pForObject->Shape.y+cgo.Y-cgo.TargetY+pForObject->SolidMask.ty, fct.Wdt, fct.Hgt, 0, 0, MaskPutRotation); else fct.DrawX(cgo.Surface, pForObject->GetX()+pForObject->Shape.x+cgo.X-cgo.TargetX+pForObject->SolidMask.tx, pForObject->GetY()+pForObject->Shape.y+cgo.Y-cgo.TargetY+pForObject->SolidMask.ty, fct.Wdt, fct.Hgt, 0, 0); } void C4SolidMask::RemoveTemporary(C4Rect where) { if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; where.Intersect(MaskPutRect); // reput background pixels for (int y = where.y; y < where.y + where.Hgt; ++y) { for (int x = where.x; x < where.x + where.Wdt; ++x) { BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; // only if mask was used here if (*pPix != MCVehic) { // restore assert(GBackPix(x,y)==MCVehic); _SBackPix(x, y, *pPix); } } } } void C4SolidMask::PutTemporary(C4Rect where) { if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; where.Intersect(MaskPutRect); // reput vehicle pixels for (int y = where.y; y < where.y + where.Hgt; ++y) { for (int x = where.x; x < where.x + where.Wdt; ++x) { BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; // only if mask was used here if (*pPix != MCVehic) { // put assert(GBackPix(x,y)==*pPix); _SBackPix(x,y,MCVehic); } } } } void C4SolidMask::Repair(C4Rect where) { if (!MaskPut || !pSolidMask || !pSolidMaskMatBuff) return; where.Intersect(MaskPutRect); // reput vehicle pixels for (int y = where.y; y < where.y + where.Hgt; ++y) { for (int x = where.x; x < where.x + where.Wdt; ++x) { BYTE *pPix = pSolidMaskMatBuff + (y - MaskPutRect.y + MaskPutRect.ty) * MatBuffPitch + x - MaskPutRect.x + MaskPutRect.tx; // only if mask was used here if (*pPix != MCVehic) { // record changed landscape in MatBuff *pPix = GBackPix(x,y); // put _SBackPix(x,y,MCVehic); } } } } C4SolidMask::C4SolidMask(C4Object *pForObject) : pForObject(pForObject) { // zero fields MaskPut=false; MaskPutRotation=0; MaskRemovalX=MaskRemovalY=0; ppAttachingObjects=NULL; iAttachingObjectsCount=iAttachingObjectsCapacity=0; // Update linked list Next = 0; Prev = Last; Last = this; if (Prev) Prev->Next = this; else First = this; // copy solid mask from bitmap int iNeededBufSize = pForObject->SolidMask.Wdt * pForObject->SolidMask.Hgt; if (!(pSolidMask = new BYTE [iNeededBufSize])) return; SURFACE sfcBitmap = pForObject->GetGraphics()->GetBitmap(); if (!sfcBitmap->Lock()) return; for (int ycnt=0; ycntSolidMask.Hgt; ycnt++) for (int xcnt=0; xcntSolidMask.Wdt; xcnt++) { // Solid mask target x/y is relative to def bitmap top-left, not object center int dx = sfcBitmap->Scale*(pForObject->SolidMask.x+xcnt); int dy = sfcBitmap->Scale*(pForObject->SolidMask.y+ycnt); pSolidMask[xcnt+ycnt*pForObject->SolidMask.Wdt] = sfcBitmap->IsPixTransparent(dx,dy) ? 0x00 : 0xff; } // create mat buff to store the material replaced by the solid mask // the upper left corner is here the [objpos]+rot([shapexy]+[targetxy]+[realWH]/2)-maxWH/2 MatBuffPitch = (int) sqrt(double(pForObject->SolidMask.Wdt * pForObject->SolidMask.Wdt + pForObject->SolidMask.Hgt * pForObject->SolidMask.Hgt))+1; if (!(pSolidMaskMatBuff= new BYTE [MatBuffPitch * MatBuffPitch] )) return; memset(pSolidMaskMatBuff, 0, MatBuffPitch * MatBuffPitch); sfcBitmap->Unlock(); } C4SolidMask::~C4SolidMask() { Remove(false); // Update linked list if (Next) Next->Prev = Prev; if (Prev) Prev->Next = Next; if (First == this) First = Next; if (Last == this) Last = Prev; delete [] pSolidMask; delete [] pSolidMaskMatBuff; delete [] ppAttachingObjects; } void C4SolidMask::RemoveSolidMasks() { C4Rect SolidMaskRect(0,0,GBackWdt,GBackHgt); C4SolidMask *pSolid; for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) { pSolid->RemoveTemporary(SolidMaskRect); } } void C4SolidMask::PutSolidMasks() { C4Rect SolidMaskRect(0,0,GBackWdt,GBackHgt); C4SolidMask *pSolid; // Restore Solidmasks for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next) { pSolid->PutTemporary(SolidMaskRect); } } C4SolidMask * C4SolidMask::First = 0; C4SolidMask * C4SolidMask::Last = 0; #ifdef SOLIDMASK_DEBUG bool C4SolidMask::CheckConsistency() { C4Rect SolidMaskRect(0,0,GBackWdt,GBackHgt); C4SolidMask *pSolid; for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev) { pSolid->RemoveTemporary(SolidMaskRect); } assert(!::Landscape.MatCount[MVehic]); // Restore Solidmasks for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next) { pSolid->PutTemporary(SolidMaskRect); } return true; } #endif