/** \file IsoRenderer.h * Defines the IsoRenderer class, which combines several blitting optimizations into one easy-to-use package. * * @author Vovansim (aka Scorpion) * @version 1.0 * @date June 6, 2003 */ #include "IsoRenderer.h" IsoRenderer::IsoRenderer() { //Nothing to initalize } IsoRenderer::~IsoRenderer() { //Clean up the list of update rectangles delete updateList; //Clean up the update map matrix delete theMap; } void IsoRenderer::setBackBuffer(SDL_Surface* surface) { backBuffer = surface; } void IsoRenderer::setFrameBuffer(SDL_Surface* surface) { frameBuffer = surface; } void IsoRenderer::setPlotter(IsoTilePlotter* newPlotter) { tilePlotter = newPlotter; } void IsoRenderer::setWalker(IsoTileWalker* newWalker) { tileWalker = newWalker; } void IsoRenderer::setMouseMap(IsoMouseMap* newMouseMap) { mouseMap = newMouseMap; } void IsoRenderer::setScroller(IsoScroller* newScroller) { scroller = newScroller; } /** @todo delete the old array or rectangles if it exists. */ void IsoRenderer::setUpdateRectCount(int maxRects) { //Set the number of update rectangles to the new value: updateRectCount = maxRects; //TODO: delete the old array if it exists. //Allocate the array of rectangles: updateList = new RECT[maxRects]; //Set the current rectangle to 0: updateRectIndex = 0; } /** @todo Delete the old map before creating a new one if it exists. */ void IsoRenderer::setMapSize(int newMapWidth, int newMapHeight) { //TODO: Delete the old map if it exists. //Allocate the new update map of the appropriate size: theMap = new bool[newMapHeight * newMapWidth]; //Set the appropriate members: mapWidth = newMapWidth; mapHeight = newMapHeight; } void IsoRenderer::setRenderFunction(RENDERFN newRenderFunction) { renderFunction = newRenderFunction; } void IsoRenderer::setExtentRect(RECT* newExtent) { CopyRect(&extentRectangle, newExtent); } /** @todo Do something more useful than returning if we have exceeded the maximum number of update rectangles. Maybe merge some rectangles to make room or set the whole screen to update. */ void IsoRenderer::addRect(RECT* updateRect) { //TODO: Do something more useful than returning. Maybe merge some rectangles to make room or set the whole screen to update. //If there is no more room in the array of update rectangles, return: if(updateRectIndex >= updateRectCount) { return; } //Copy the relevant portion of the rectangle: RECT relevantRect; if(!IntersectRect(&relevantRect, updateRect, scroller->getScreenSpace())) { //The update rectangle is not on screen. Nothing to update: return; } //Paint the frame buffer black in the update region: SDL_FillRect(frameBuffer, GetSDLRect(&relevantRect), 0); //Add the rectangle to the list: CopyRect(&updateList[updateRectIndex], &relevantRect); updateRectIndex ++; //Increment the current index //Temporary calculation point: POINT tempPoint; //Corner points POINT upperLeftCorner; POINT upperRightCorner; POINT lowerLeftCorner; POINT lowerRightCorner; //Calculate the corner points: //Upper-left tempPoint.x = relevantRect.left; tempPoint.y = relevantRect.top; upperLeftCorner = mouseMap->mapMouse(tempPoint); //upper right tempPoint.x = relevantRect.right; tempPoint.y = relevantRect.top; upperRightCorner = mouseMap->mapMouse(tempPoint); //lower left tempPoint.x = relevantRect.left; tempPoint.y = relevantRect.bottom; lowerLeftCorner = mouseMap->mapMouse(tempPoint); //lower right tempPoint.x = relevantRect.right; tempPoint.y = relevantRect.bottom; lowerRightCorner = mouseMap->mapMouse(tempPoint); //move the left corners to the west upperLeftCorner = tileWalker->walkTile(upperLeftCorner, IDIR_WEST); lowerLeftCorner = tileWalker->walkTile(lowerLeftCorner, IDIR_WEST); //move the bottom corners to the south lowerLeftCorner = tileWalker->walkTile(lowerLeftCorner, IDIR_SOUTH); lowerRightCorner = tileWalker->walkTile(lowerRightCorner, IDIR_SOUTH); //scan through the tiles POINT startRow = upperLeftCorner; POINT endRow = upperRightCorner; POINT currentPoint; Uint32 rowCount = 0; while(true) { //Make sure the current point is at the start row: currentPoint = startRow; //Render a row of tiles: while(true) { //Make sure that we are not off the map: if(currentPoint.x >= 0 && currentPoint.x < mapWidth && currentPoint.y >= 0 && currentPoint.y < mapHeight) { //Mark the tile dirty: theMap[currentPoint.x + currentPoint.y * mapWidth] = true; } //If this is the last tile in the row, we are done with this one: if(currentPoint.x == endRow.x && currentPoint.y == endRow.y) { break; } //Walk to the east currentPoint = tileWalker->walkTile(currentPoint, IDIR_EAST); } //If we have reached the lower-left corner, we are done with the rectangle: if(startRow.x == lowerLeftCorner.x && startRow.y == lowerRightCorner.y) { break; } //Move down a row: startRow = tileWalker->walkTile(startRow, IDIR_SOUTH); endRow = tileWalker->walkTile(endRow, IDIR_SOUTH); //Increment the row counter: rowCount ++; } } void IsoRenderer::addTile(int newMapX, int newMapY) { //Put the coordinates into a point: POINT mapPoint; mapPoint.x = newMapX; mapPoint.y = newMapY; //Plot the point: POINT plotPoint = tilePlotter->plotTile(mapPoint); //Convert to screen coordinates: plotPoint = scroller->WorldToScreen(plotPoint); //Fetch the extent rectangle: RECT updateRectangle; CopyRect(&updateRectangle, &extentRectangle); //Offset by the point we have: OffsetRect(&updateRectangle, plotPoint); //Add to the update list: addRect(&updateRectangle); } void IsoRenderer::scrollFrame(int scrollX, int scrollY) { POINT oldAnchor; //Grab the current Anchor position: oldAnchor.x = scroller->getAnchor()->x; oldAnchor.y = scroller->getAnchor()->y; //Move the anchor by the values specified: scroller->moveAnchor(scrollX, scrollY); //Calculate the actual scroll values in case the anchor was wrapped: scrollX = scroller->getAnchor()->x - oldAnchor.x; scrollY = scroller->getAnchor()->y - oldAnchor.y; //Put the anchor back where it was: scroller->getAnchor()->x = oldAnchor.x; scroller->getAnchor()->y = oldAnchor.y; ////Blit rectangles: //source rectangle: RECT sourceRectangle; //Destination RECT: RECT destinationRectangle; //Update rectangles: RECT xUpdateRectangle; RECT yUpdateRectangle; //Set both the source and destination rectangles to full viewport: CopyRect(&sourceRectangle, scroller->getScreenSpace()); CopyRect(&destinationRectangle, scroller->getScreenSpace()); //Initialize the update rectangles to empty values: setRectEmpty(&xUpdateRectangle); setRectEmpty(&yUpdateRectangle); //Fill the horizontal scroll update rectangle: if(scrollX != 0) { //The user has scrolled in the horizontal direction. We got some updating to do: if(scrollX < 0) { //Scrolling to the right: //Set up an update rectangle for the left side of the screen: setRect(&xUpdateRectangle, destinationRectangle.left, destinationRectangle.top, destinationRectangle.left - scrollX, destinationRectangle.bottom); //Adjust the blitting rectangles. destinationRectangle.left -= scrollX; sourceRectangle.right += scrollX; } else { //Scrolling to the left: //Set up an update rectangle for the right side of the screen: setRect(&xUpdateRectangle, destinationRectangle.right - scrollX, destinationRectangle.top, destinationRectangle.right, destinationRectangle.bottom); //Adjust the blitting rectangles: destinationRectangle.right -= scrollX; sourceRectangle.left += scrollX; } } //Fill the vertical scroll update rectangle: if(scrollY != 0) { //The user has scrolled in the vertical direction. We got some updating to do: if(scrollY < 0) { //Scrolling down: //Set up an update rectangle for the top side of the screen: setRect(&yUpdateRectangle, destinationRectangle.left, destinationRectangle.top, destinationRectangle.right, destinationRectangle.top - scrollY); //Adjust the blitting rectangles. destinationRectangle.top -= scrollY; sourceRectangle.bottom += scrollY; } else { //Scrolling up: //Set up an update rectangle for the bottom side of the screen: setRect(&yUpdateRectangle, destinationRectangle.left, destinationRectangle.bottom - scrollY, destinationRectangle.right, destinationRectangle.bottom); //Adjust the blitting rectangles. destinationRectangle.bottom -= scrollY; sourceRectangle.top += scrollY; } } //perform the blit SDL_Rect* dest = GetSDLRect(&destinationRectangle); SDL_BlitSurface(frameBuffer, GetSDLRect(&sourceRectangle), backBuffer, dest); //perform the scroll scroller->moveAnchor(scrollX, scrollY); //add the update rects addRect(&xUpdateRectangle); addRect(&yUpdateRectangle); } void IsoRenderer::updateFrame() { //Update RECT: RECT relevantRect; //Set the initial update rectangle to the whole viewport: CopyRect(&relevantRect, scroller->getScreenSpace()); //Temporary points: POINT tempPoint; POINT plotPoint; //Corner points POINT upperLeftCorner; POINT upperRightCorner; POINT lowerLeftCorner; POINT lowerRightCorner; //Calculate the corner points: //Upper-left tempPoint.x = relevantRect.left; tempPoint.y = relevantRect.top; upperLeftCorner = mouseMap->mapMouse(tempPoint); //upper right tempPoint.x = relevantRect.right; tempPoint.y = relevantRect.top; upperRightCorner = mouseMap->mapMouse(tempPoint); //lower left tempPoint.x = relevantRect.left; tempPoint.y = relevantRect.bottom; lowerLeftCorner = mouseMap->mapMouse(tempPoint); //lower right tempPoint.x = relevantRect.right; tempPoint.y = relevantRect.bottom; lowerRightCorner = mouseMap->mapMouse(tempPoint); //move the left corners to the west upperLeftCorner = tileWalker->walkTile(upperLeftCorner, IDIR_WEST); lowerLeftCorner = tileWalker->walkTile(lowerLeftCorner, IDIR_WEST); //move the bottom corners to the south lowerLeftCorner = tileWalker->walkTile(lowerLeftCorner, IDIR_SOUTH); lowerRightCorner = tileWalker->walkTile(lowerRightCorner, IDIR_SOUTH); //scan through the tiles POINT startRow = upperLeftCorner; POINT endRow = upperRightCorner; POINT currentPoint; Uint32 rowCount = 0; while(true) { //Make sure the current point is at the start row: currentPoint = startRow; //Render a row of tiles: while(true) { //Find the tile plot coordinates: plotPoint = tilePlotter->plotTile(currentPoint); plotPoint = scroller->WorldToScreen(plotPoint); //Make sure that we are not off the map: if(currentPoint.x >= 0 && currentPoint.x < mapWidth && currentPoint.y >= 0 && currentPoint.y < mapHeight) { if(theMap[currentPoint.x + currentPoint.y * mapWidth] == true) { //The tile is marked for updating: renderFunction(frameBuffer, &relevantRect, plotPoint.x, plotPoint.y, currentPoint.x, currentPoint.y); theMap[currentPoint.x + currentPoint.y * mapWidth] = false; } } //If this is the last tile in the row, we are done with this one: if(currentPoint.x == endRow.x && currentPoint.y == endRow.y) { break; } //Walk to the east currentPoint = tileWalker->walkTile(currentPoint, IDIR_EAST); } //If we have reached the lower-left corner, we are done with the rectangle: if(startRow.x == lowerLeftCorner.x && startRow.y == lowerRightCorner.y) { break; } //Move down a row: startRow = tileWalker->walkTile(startRow, IDIR_SOUTH); endRow = tileWalker->walkTile(endRow, IDIR_SOUTH); //Increment the row counter: rowCount ++; } //render all update rects from frame buffer to back buffer for(int i=0; i