/** Returns the data used for the scaler shader for the given pixel. It is a 8-bit bitmask. The bits stand for the 8 neighbouring pixels in this order: 1 2 3 4 . 5 6 7 8 ... and denote whether the pixels belongs to the same group as this pixel. */ int C4LandscapeRenderGL::CalculateScalerBitmask(int x, int y, C4Rect To, C4Landscape *pSource) { int pixel = pSource->_GetPix(To.x+x, To.y+y); int placement = pSource->_GetPlacement(To.x+x, To.y+y); int neighbours[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; if(To.y+y > 0) { if(To.x+x > 0) neighbours[0] = pSource->_GetPix(To.x+x-1, To.y+y-1); neighbours[1] = pSource->_GetPix(To.x+x, To.y+y-1); if(To.x+x < iWidth-1) neighbours[2] = pSource->_GetPix(To.x+x+1, To.y+y-1); } if(To.x+x > 0) neighbours[3] = pSource->_GetPix(To.x+x-1, To.y+y); if(To.x+x < iWidth-1) neighbours[4] = pSource->_GetPix(To.x+x+1, To.y+y); if(To.y+y < iHeight-1) { if(To.x+x > 0) neighbours[5] = pSource->_GetPix(To.x+x-1, To.y+y+1); neighbours[6] = pSource->_GetPix(To.x+x, To.y+y+1); if(To.x+x < iWidth-1) neighbours[7] = pSource->_GetPix(To.x+x+1, To.y+y+1); } // Look for highest-placement material in our surroundings int maxPixel = pixel, maxPlacement = placement; for(int i = 0; i < 8; i++) { int tempPlacement = MatPlacement(PixCol2Mat(neighbours[i])); if(tempPlacement > maxPlacement || (tempPlacement == maxPlacement && neighbours[i] > maxPixel) ) { maxPixel = neighbours[i]; maxPlacement = tempPlacement; } } // Scaler calculation depends on whether this is the highest-placement material around int scaler = 0; if(maxPixel == pixel) { // If yes, we consider all other materials as "other" for(int i = 0; i < 8; i++) if(neighbours[i] == pixel) scaler += (1<<i); } else { // Otherwise, we *only* consider the highest-placement material as "other" for(int i = 0; i < 8; i++) if(neighbours[i] != maxPixel) scaler += (1<<i); } return scaler; }
void C4Landscape::UpdatePixMaps() // Copied from C4Landscape.cpp { int32_t i; for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Mat[i] = PixCol2Mat(i); for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Dens[i] = MatDensity(Pix2Mat[i]); for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Place[i] = MatValid(Pix2Mat[i]) ? ::MaterialMap.Map[Pix2Mat[i]].Placement : 0; for (i = 0; i < C4M_MaxTexIndex; i++) Pix2Light[i] = MatValid(Pix2Mat[i]) && (::MaterialMap.Map[Pix2Mat[i]].Light>0); Pix2Place[0] = 0; // clear bridge mat conversion buffers for (int32_t i = 0; i < C4M_MaxTexIndex; ++i) { delete [] BridgeMatConversion[i]; BridgeMatConversion[i] = NULL; } }
BOOL C4Shape::Attach(int32_t &cx, int32_t &cy, BYTE cnat_pos) { // Adjust given position to one pixel before contact // at vertices matching CNAT request. BOOL fAttached = FALSE; #ifdef C4ENGINE int32_t vtx, xcnt, ycnt, xcrng, ycrng, xcd, ycd; int32_t motion_x = 0; BYTE cpix; // reset attached material AttachMat = MNone; // New attachment behaviour in CE: // Before, attachment was done by searching through all vertices, // and doing attachment to any vertex with a matching CNAT. // While this worked well for normal Clonk attachment, it caused nonsense // behaviour if multiple vertices matched the same CNAT. In effect, attachment // was then done to the last vertex only, usually stucking the object sooner // or later. // For instance, the scaling procedure of regular Clonks uses two CNAT_Left- // vertices (shoulder+belly), which "block" each other in situations like // scaling up battlements of towers. That way, the 2px-overhang of the // battlement is sufficient for keeping out scaling Clonks. The drawback is // that sometimes Clonks get stuck scaling in very sharp edges or single // floating material pixels; occuring quite often in Caverace, or maps where // you blast Granite and many single pixels remain. // // Until a better solution for designing battlements is found, the old-style // behaviour will be used for Clonks. Both code variants should behave equally // for objects with only one matching vertex to cnat_pos. if (!(cnat_pos & CNAT_MultiAttach)) { // old-style attachment for (vtx = 0; vtx < VtxNum; vtx++) if (VtxCNAT[vtx] & cnat_pos) { xcd = ycd = 0; switch (cnat_pos & (~CNAT_Flags)) { case CNAT_Top: ycd = -1; break; case CNAT_Bottom: ycd = +1; break; case CNAT_Left: xcd = -1; break; case CNAT_Right: xcd = +1; break; } xcrng = AttachRange * xcd * (-1); ycrng = AttachRange * ycd * (-1); for (xcnt = xcrng, ycnt = ycrng; (xcnt != -xcrng) || (ycnt != -ycrng); xcnt += xcd, ycnt += ycd) { int32_t ax = cx + VtxX[vtx] + xcnt + xcd, ay = cy + VtxY[vtx] + ycnt + ycd; if (GBackDensity(ax, ay) >= ContactDensity && ax >= 0 && ax < GBackWdt) { cpix = GBackPix(ax, ay); AttachMat = PixCol2Mat(cpix); iAttachX = ax; iAttachY = ay; iAttachVtx = vtx; cx += xcnt; cy += ycnt; fAttached = 1; break; } } } } else // CNAT_MultiAttach { // new-style attachment // determine attachment direction xcd = ycd = 0; switch (cnat_pos & (~CNAT_Flags)) { case CNAT_Top: ycd = -1; break; case CNAT_Bottom: ycd = +1; break; case CNAT_Left: xcd = -1; break; case CNAT_Right: xcd = +1; break; } // check within attachment range xcrng = AttachRange * xcd * (-1); ycrng = AttachRange * ycd * (-1); for (xcnt = xcrng, ycnt = ycrng; (xcnt != -xcrng) || (ycnt != -ycrng); xcnt += xcd, ycnt += ycd) // check all vertices with matching CNAT for (vtx = 0; vtx < VtxNum; vtx++) if (VtxCNAT[vtx] & cnat_pos) { // get new vertex pos int32_t ax = cx + VtxX[vtx] + xcnt + xcd, ay = cy + VtxY[vtx] + ycnt + ycd; // can attach here? cpix = GBackPix(ax, ay); if (MatDensity(PixCol2Mat(cpix)) >= ContactDensity && ax >= 0 && ax < GBackWdt) { // store attachment material AttachMat = PixCol2Mat(cpix); // store absolute attachment position iAttachX = ax; iAttachY = ay; iAttachVtx = vtx; // move position here cx += xcnt; cy += ycnt; // mark attachment fAttached = 1; // break both looops xcnt = -xcrng - xcd; ycnt = -ycrng - ycd; break; } } } // both attachments: apply motion done by SolidMasks if (motion_x) cx += BoundBy<int32_t>(motion_x, -1, 1); #endif return fAttached; }