Thread: New editor updates

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    New editor updates

    Check out this editor I've been toiling over forever.

    I finally got transparent blitting working in GDI.

    Pretty simple really. Link with msimg32.lib and use TransparentBlt(). It's faster than writing your own and it stretches/shrinks images to fit the destination rectangle. Nice...very nice.

  2. #2
    ---
    Join Date
    May 2004
    Posts
    1,379
    Is this what you have done with MFC?

  3. #3

    Join Date
    May 2005
    Posts
    1,042
    Looks like a very functional tile editor, keep up the good work.
    I'm not immature, I'm refined in the opposite direction.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yeppers. This is what I've done with MFC. I'm not a woman, but I'm sure this experience has been akin to childbirth.


  5. #5
    ---
    Join Date
    May 2004
    Posts
    1,379
    lol well it looks well worth it.

  6. #6

    Join Date
    May 2005
    Posts
    1,042
    Quote Originally Posted by Bubba
    Yeppers. This is what I've done with MFC. I'm not a woman, but I'm sure this experience has been akin to childbirth.

    ha, I'll have to remember that one
    I'm not immature, I'm refined in the opposite direction.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Added more support to the editor. Having a lot of problems with zooming. Anyone have any ideas on how to do this?

    As you can see really big maps are supported. Here is one with 17 layers and a map size of 1000x1000.

  8. #8
    Registered User
    Join Date
    Aug 2001
    Posts
    411
    Quote Originally Posted by Bubba
    Added more support to the editor. Having a lot of problems with zooming. Anyone have any ideas on how to do this?

    As you can see really big maps are supported. Here is one with 17 layers and a map size of 1000x1000.

    Provided that i have no idea how your code is designed, what I would try first is to create a new child window, like one of the tile selection windows, and use it to display a 50x50 grid of the main map It should not be too hard to setup methods to transfer all the data for a small grid back and forth. Give it scroll bars so you can move around, and maybe even another mouse tool so you can snap the focus to another spot by clicking on the desired spot on the main map. Make the soom window resizable, but with a fixed aspect ratio, and instead of changing the number of tiles displayed, if grows and shrinks the tiles.

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Well here is what I have. The scrolling is done in the main window. I would like to create that type of map system but the calculations are far beyond what I would have imagined - they get complicated. You can zoom in on this map until cell sizes reach 256 pixels. My idea is that I never want the map to have borders. In other words if you zoom out far enough, I never want you to be able to see off of the map. My calculations are as follows. It works fine for computing the scrolling maxX and maxY, but when you zoom in, scroll down, and then zoom out.....everything goes to um...crap.

    Code:
    void CZeldaEditorView::UpdateScrollSizes()
    {
      //Set new scroll sizes based on map size and current cellsize
      //or grid size  
      CSize size;
    
      //Get pointer to document class for map info
      CZeldaEditorDoc *pDoc=(CZeldaEditorDoc *)GetDocument();
      size.cx=pDoc->m_iMapSizeX*m_iGridSize;
      size.cy=pDoc->m_iMapSizeY*m_iGridSize;
      SetScrollSizes(MM_TEXT,size);
    
      //Retrieve width and height of client rect
      CRect rect;
      GetClientRect(&rect);
     
      int mapx=pDoc->m_iMapSizeX;
      int mapy=pDoc->m_iMapSizeY;
      int maxx=pDoc->m_iMapSizeX*m_iGridSize;
      int maxy=pDoc->m_iMapSizeY*m_iGridSize;
    
      //Set max scroll
      m_iMaxScrollX=(m_iGridSize*(mapx-1))-rect.Width();
      m_iMaxScrollY=(m_iGridSize*mapy)-rect.Height();
      
      //Don't show borders
      if (m_iMaxScrollX<0) m_iMaxScrollX=0;
      if (m_iMaxScrollY<0) m_iMaxScrollY=0;
      
      //If map size won't fit in window because map is too small
      //Re-adjust cell size so map fits perfectly in window
      if (rect.Width()>=maxx-m_iGridSize) 
      {
        m_iGridSize=rect.Width()/mapx;
        m_iScrollX=-m_iMaxScrollX;
        m_iScrollY=-m_iMaxScrollY;
      }
    
    }
    
    //Transforms mouse clicks to always click on nearest tile
    //to cursor regardless of zoom and scroll
    void CZeldaEditorView::TransformMouseXY(CPoint mouse,CRect &rect,DWORD &offset)
    {
      //Find out remainder of m_iScrollX/m_iGridSize
      int offsetx=(m_iScrollX % m_iGridSize);
      int offsety=(m_iScrollY % m_iGridSize);
    
      //Temporaries so I don't have to use mouse.x, mouse.y every
      //time
      int mx=mouse.x;
      int my=mouse.y;
    
      //Subtract mouse by offsets
      mx-=offsetx;
      my-=offsety;
    
      //Snap to grid size
      mx-=(mx % m_iGridSize);
      my-=(my % m_iGridSize);
    
      //Re-adjust on-screen rect to new grid rectangle
      rect.left=mx+offsetx;
      rect.top=my+offsety;
      rect.right=mx+m_iGridSize+offsetx;
      rect.bottom=my+m_iGridSize+offsety;
    
      //Compute actual memory location of grid clicked
      int MemoryX=(mouse.x - m_iScrollX)/m_iGridSize;
      int MemoryY=(mouse.y - m_iScrollY)/m_iGridSize;
    
      //Set offset in document
      CZeldaEditorDoc *ptrDoc=(CZeldaEditorDoc *)GetDocument();
      offset=MemoryY*ptrDoc->m_iMapSizeX+MemoryX;
    
    }

    Sorry for the mess but MFC code is a mess. Basically I'm adhering to the document/view architecture so that the document handles the info about the map and the view just draws the map according to the info. It's a real pain in the arse.

    These calculations work but zooming only zooms in on the upper left corner. I know why it does this, but how would I zoom in on an area? Do I have to draw my map relative to the center of the screen, or can I just start at the upper left corner and set m_iScrollX and m_iScrollY to different values when zooming to get the zoom in on a point effect?

    Hard to explain.

    Maybe showing the actual OnDraw() will help.

    Pardon the mess but it's a major work in progress.

    Code:
    void CZeldaEditorView::OnDraw(CDC* pDC)
    {
      //Pointer to document class
      CZeldaEditorDoc* pDoc = (CZeldaEditorDoc *)GetDocument();
      //Pointer to frame clasee
      CMainFrame *ptrFrame=(CMainFrame *)AfxGetMainWnd();
      //Number of maps (layers)
      DWORD numMaps=pDoc->m_pMapManager->GetNumberOfMaps();
    
      //Handle to destination DC
      HDC dcDest=pDC->GetSafeHdc();
        
      //Black brush for drawing
      CBrush blackbrush;
      blackbrush.CreateSolidBrush(RGB(0,0,0));
      
      //Retrieve client rect
      CRect ScreenRect;
      GetClientRect(&ScreenRect);
    
      //Fill it with black
      pDC->FillRect(&ScreenRect,&blackbrush);
    
      //Check for tile manager and map count
      //Don't draw if null or maps=0
      if (pDoc->m_pTileManager && numMaps>1)
      {
        //Tile size from bitmap
        int TileSize=pDoc->m_iTileSize;
    
        //Horizontal offset - debug info
        int iOffsetHoriz=abs(m_iScrollX/m_iGridSize);
    
        //Vertical offset - debug info
        int iOffsetVert=abs(m_iScrollY/m_iGridSize);
    
         //Function to compute relative to center of screenrect
         //Algos don't work right with this yet
        //ComputeStartGrid();
        
        //Iterate through maps
        //m_dwStartMapNum  is starting map
        //m_dwEndMapNum is ending map
        //To view one layer set start to layer and end to start+1
        for (DWORD map=m_dwStartMapNum;map<m_dwEndMapNum;map++)
        {
    
          //Get pointer to map
          CMap *ptrMap=pDoc->m_pMapManager->GetMapClass(map);
    
          //Get pointer to movement map - disabled
         //CMap *ptrMove=pDoc->m_pMapManager->GetMapClass(0);
       
          //Screen calculations for memory offset
          int CurrentY=(-m_iScrollY/m_iGridSize);
          int CurrentX=(-m_iScrollX/m_iGridSize);
    
          //Remember CurrentX before it changes
          int OriginX=CurrentX;
    
          //Compute memory offset
          DWORD offset=CurrentY*pDoc->m_iMapSizeX+CurrentX;
          int offsetx=m_iScrollX % m_iGridSize;
          int offsety=m_iScrollY % m_iGridSize;
      
          //Save starting offset
          DWORD startoffset=offset;
    
         //Rect for grid display - if enabled 
         CRect GridRect;
         
    
          //Draw the map finally
          for (int i=offsety;i<ScreenRect.bottom;i+=m_iGridSize)
          {
            for (int j=offsetx;j<ScreenRect.right;j+=m_iGridSize)
            {
              
              //Retrieve ID from map            
              DWORD ID=ptrMap->GetValueAtOffset(offset);
              
              //If this block is not empty          
              if (ID!=0xFFFFFFFF)
              {
                //Get DC of tile bitmap from tile manager
                CDC *tempDC=pDoc->m_pTileManager->GetTileDC(ID);
    
                //Get handle to tile bitmap DC
                HDC dcSource=tempDC->GetSafeHdc();
    
                //Get transparent color of tile
                UINT dwTransColor=pDoc->m_pTileManager->GetTransColor(ID);
                
              //Do a transparent blit
              ::TransparentBlt(dcDest,
                                 j,i,
                                 m_iGridSize,m_iGridSize,
                                 dcSource,
                                 0,0,
                                 TileSize,TileSize,
                                 dwTransColor);
              }
    
              //Draw the grid
              GridRect.left=j;
              GridRect.top=i;
              GridRect.right=j+m_iGridSize;
              GridRect.bottom=i+m_iGridSize;
              
              //If view->Grid checked then draw grid
              if (m_bGrid) 
              {
                          
                CBrush gridbrush;
                gridbrush.CreateSolidBrush(RGB(255,255,255));
    
                pDC->FrameRect(&GridRect,&gridbrush);
              }
    
              //Increment offset into memory                 
              offset++;
              
              //Increment cell column counter
              CurrentX++;
    
              //If column is at width of map break
              if (CurrentX>=pDoc->m_iMapSizeX-1) break;
            }
              //Set column counter to origin 
              CurrentX=OriginX;
    
              //Increment startoffset by one row
              startoffset+=pDoc->m_iMapSizeX;
    
              //Save startoffset
              offset=startoffset;
    
              //Increment row counter
              CurrentY++;
    
              //If no more rows, break
              if (CurrentY>pDoc->m_iMapSizeY-1) break;     
          }
        }
      }
    
    }
    Now the reason that the OnDraw is so extensive is because I did not want to compute the memory offset inside of the loop to render the grid. That would be slow. So I pre-compute the memory offset - which is already taking into account the scrolling values and so it is at the right spot in memory to correctly display the grid. The entire visible portion of the map can be drawn without re-computing the offset. When the column counter reaches the map size OR the X value of the render reaches the right side of the client rectangle, the row counter is incremented, column counter is reset (originX at current is always 0), starting offset is incremented by one row (m_iMapSizeX), and current offset is set to the starting offset.

    It'a a pain in the arse but it's a linear algorithm and it's quite fast.
    The array is indexed in linear fashion and the render is done in linear fashion. The Tile manager holds all of the tiles. But get this. Instead of storing bitmaps each tile is an object with a device context that has been prepared beforehand so the image data is on the device context. Then blitting the image is as simple as using TransparentBlt from the API using the handles to the source DC and the dest DC. Simple.

    It all works great. But zooming is a mess. The ComputeStartGrid() function is a function that can center the grid inside of the client rect, but then all of the LButtonDown and MouseMove() functions don't compute the mouse cursor to grid and mouse cursor to memory right.

    If you'ev ever done a tile engine I'm sure you will understand in the editor you must transform mouse clicks to align with the displayed grid or you cannot place tiles correctly.

    Also if the current tile is the selected tile and you click on the tile in the map, it erases it. The selected tile is stored in the m_pTileManager class and is set by the tile tool dialogs when you select a tile to use.

    This hasn't been easy so forgive me if the algo sucks.
    Last edited by VirtualAce; 11-05-2005 at 03:51 PM.

Popular pages Recent additions subscribe to a feed