Enter Half-Life Re-imagined, our current competition!
Check out Skewing textures in Hammer, our newest tutorial!
Say hello to Bear, our newest member!

logo

Site Stuff

Reference

Maps

Community

ShoutBOX

Poll

Valve Predictions 2017

What's the most likely thing Valve will release this year?

Nothing

18

A new game

3

More overpriced hardware

8

Source 2 SDK

11

An army of evil killer cyborgs

5

The hounds

8

Onliners

31 secs

Penguinboy

3 mins

Admer456

5 mins

Victor-933

22 mins

abbadon

31 mins

Bruce

39 mins

SpannerSpammer

39 mins

platoon

Affiliates

A gaming and technology blog by TWHL admins Penguinboy and Ant. A music blog by TWHL users Ant and Hugh.

.MAP to .DXF Conversion

By Chris 'autolycus' Bokitch

The code below, sent to me by David Speyrer, is the part of the Hammer code that converts a map from standard .map format to .dxf format. It may be of use to anyone writing .map conversion programs.

struct DXFEXPINFO
{
     structint nObject;
     BOOL bVisOnly;
     CMapWorld *pWorld;
     FILE *fp;
};

void CMapDoc::OnFileExporttodxf(void)
{
     static CString str;

     if (str.IsEmpty())
     {
          int nDot;

          // Replace the extension with DXF.
          str = GetPathName();
          if ((nDot = str.ReverseFind('.')) != -1)
          {
               str = str.Left(nDot);
          }
          str += ".dxf";
     }

     CExportDlg dlg(str, "dxf", "DXF files (*.dxf)|*.dxf||");
     if(dlg.DoModal() == IDCANCEL)
          return;

     str = dlg.GetPathName();
     if(str.ReverseFind('.') == -1)
          str += ".dxf";
     
     FILE *fp = fopen(str, "wb");

     pWorld->CalcBounds(TRUE);
     BoundBox box;
     box.UpdateBounds(pWorld);

     fprintf(fp,"9\n$ACADVER\n1\nAC1008\n");
     fprintf(fp,"9\n$UCSORG\n10\n0.0\n20\n0.0\n30\n0.0\n");
     fprintf(fp,"9\n$UCSXDIR\n10\n1.0\n20\n0.0\n30\n0.0\n");
     fprintf(fp,"9\n$TILEMODE\n70\n1\n");
     fprintf(fp,"9\n$UCSYDIR\n10\n0.0\n20\n1.0\n30\n0.0\n");
     fprintf(fp,"9\n$EXTMIN\n10\n%f\n20\n%f\n30\n%f\n",
          box.bmins[0], box.bmins[1], box.bmins[2]);
     fprintf(fp,"9\n$EXTMAX\n10\n%f\n20\n%f\n30\n%f\n",
          box.bmaxs[0], box.bmaxs[1], box.bmaxs[2]);
     fprintf(fp,"0\nENDSEC\n");
     
     /* Tables section */
     fprintf(fp,"0\nSECTION\n2\nTABLES\n");
     /* Continuous line type */
     fprintf(fp,"0\nTABLE\n2\nLTYPE\n70\n1\n0\nLTYPE\n2\nCONTINUOUS"
          "\n70\n64\n3\nSolid line\n72\n65\n73\n0\n40\n0.0\n");
     fprintf(fp,"0\nENDTAB\n");
     
     /* Object names for layers */
     fprintf(fp,"0\nTABLE\n2\nLAYER\n70\n%d\n",1);
     fprintf(fp,"0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n");
     fprintf(fp,"0\nENDTAB\n");
     fprintf(fp,"0\nTABLE\n2\nSTYLE\n70\n1\n0\nSTYLE\n2\nSTANDARD\n70\n0\n"
          "40\n0.0\n41\n1.0\n50\n0.0\n71\n0\n42\n0.2\n3\ntxt\n4\n\n0\n"
          "ENDTAB\n");
     
     /* Default View? */
     
     /* UCS */
     fprintf(fp,"0\nTABLE\n2\nUCS\n70\n0\n0\nENDTAB\n");
     fprintf(fp,"0\nENDSEC\n");
     
     /* Entities section */
     fprintf(fp,"0\nSECTION\n2\nENTITIES\n");

     // export solids
     BeginWaitCursor();

     DXFEXPINFO info;
     info.bVisOnly = dlg.bVisibles;
     info.nObject = 0;
     info.pWorld = pWorld;
     info.fp = fp;

     // Enumerate all the solids in the world, calling the callback.
     pWorld->EnumChildren(ENUMMAPCHILDRENPROC(SaveDXF),
          DWORD(&info), MAPCLASS_TYPE(CMapSolid));

     EndWaitCursor();

     fprintf(fp,"0\nENDSEC\n0\nEOF\n");
     fclose(fp);
}


static BOOL SaveDXF(CMapSolid *pSolid, DXFEXPINFO *pInfo)
{
     if(pInfo->bVisOnly)
     {
          if(!pSolid->IsVisible(pInfo->pWorld, pInfo->pWorld->dwFilterFlags))
               return TRUE;
     }

     CSSolid *pStrucSolid = new CSSolid;
     pStrucSolid->Attach(pSolid);
     pStrucSolid->Convert();
     pStrucSolid->SerializeDXF(pInfo->fp, pInfo->nObject++);
     delete pStrucSolid;

     return TRUE;
}


void CSSolid::SerializeDXF(FILE *stream, int nObject)
{
     char szName[128];
     sprintf(szName, "OBJECT%03d", nObject);

     // count number of triangulated faces
     int nTriFaces = 0;
     for(int i = 0; i < m_nFaces; i++)
     {
          CSSFace &face = m_Faces[i];
          nTriFaces += face.nEdges-2;
     }

     fprintf(stream,"0\nPOLYLINE\n8\n%s\n66\n1\n70\n64\n71\n%u\n72\n%u\n",
                                   szName, m_nVertices, nTriFaces);

     fprintf(stream,"62\n50\n");

     for(i = 0; i < m_nVertices; i++)
     {
          float *pos = m_Vertices[i].pos;
          fprintf(stream,     "0\nVERTEX\n8\n%s\n10\n%.6f\n20\n%.6f\n30\n%.6f\n70\n192\n",
                    szName, pos[0], pos[1], pos[2]);
     }

     // triangulate each face and write
     for(i = 0; i < m_nFaces; i++)
     {
          CSSFace &face = m_Faces[i];
          PINT pVerts = CreatePointIndexList(face);

          for(int v = 0; v < face.nEdges; v++)
               pVerts[v]++;

          for(v = 0; v < face.nEdges-2; v++)
          {
               fprintf(stream, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n"
                    "0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName,
                    v == 0 ? pVerts[0] : -pVerts[0],
                    pVerts[v+1],
                    v == (face.nEdges-3) ? pVerts[v+2] : -pVerts[v+2]
                    );
          }
     }

     fprintf(stream, "0\nSEQEND\n8\n%s\n", szName);
}

User Questions

From the hlcoders mail list, Cortex writes: I'm trying to create a map to dxf converter using the code that Valve gave but I don't understand the last loop.

     // triangulate each face and write
     for(i = 0; i < m_nFaces; i++)
     {
          CSSFace &face = m_Faces[i];
          PINT pVerts = CreatePointIndexList(face);

          for(int v = 0; v < face.nEdges; v++)
               pVerts[v]++;

          for(v = 0; v < face.nEdges-2; v++)
          {
               fprintf(stream, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n"
                    "0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName,
                    v == 0 ? pVerts[0] : -pVerts[0],
                    pVerts[v+1],
                    v == (face.nEdges-3) ? pVerts[v+2] : -pVerts[v+2]
                    );
          }
     }

I only need more informations about the CSSFace class (the members), the CreatePointIndexList and the calculation of the "nEdges" member of CSSFace. Then I'll be able to understand what gets done and be able to merge it to my code.

David Speyrer responds: This code assumes that every vertex in the solid can be referred to by a unique index, so a rectangular solid would have 8 verts with indices from 0 to 7. CreatePointIndexList fills out an array with the indices of verts on the given face in clockwise winding order. GetConnectionVertex returns the vertex between the two given edges. The code for CreatePointIndexList is below.

Hope this helps!


//
// Create point list, but return indices instead of vertices.
//

PINT CSSolid::CreatePointIndexList(CSSFace &face, PINT piPoints)
{
     PINT pts;
     int nPts = 0;
     
     if (piPoints)
          pts = piPoints;
     else
          pts = new int[face.nEdges];
     
     SSHANDLEINFO hi;
     
     for (int i = 0; i < face.nEdges; i++)
     {
          // calc next edge so we can see which is the next clockwise
          point
               int iNextEdge = i+1;
          if (iNextEdge == face.nEdges)
               iNextEdge = 0;
          
          CSSEdge *edgeCur = (CSSEdge*) GetHandleData(face.Edges[i]);
          CSSEdge *edgeNext = (CSSEdge*)
               GetHandleData(face.Edges[iNextEdge]);
          
          SSHANDLE hVertex = GetConnectionVertex(edgeCur, edgeNext);
          ASSERT(hVertex);
          
          GetHandleInfo(&hi, hVertex);
          pts[i] = hi.iIndex;
     }
     
     return pts;
}