]> de.git.xonotic.org Git - voretournament/voretournament.git/blobdiff - misc/mediasource/extra/netradiant-src/radiant/brushtokens.h
Include netRadiant source in this GIT
[voretournament/voretournament.git] / misc / mediasource / extra / netradiant-src / radiant / brushtokens.h
diff --git a/misc/mediasource/extra/netradiant-src/radiant/brushtokens.h b/misc/mediasource/extra/netradiant-src/radiant/brushtokens.h
new file mode 100644 (file)
index 0000000..19d8c7b
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+Copyright (C) 2001-2006, William Joseph.
+All Rights Reserved.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#if !defined(INCLUDED_BRUSHTOKENS_H)
+#define INCLUDED_BRUSHTOKENS_H
+
+#include "stringio.h"
+#include "stream/stringstream.h"
+#include "brush.h"
+
+inline bool FaceShader_importContentsFlagsValue(FaceShader& faceShader, Tokeniser& tokeniser)
+{
+  // parse the optional contents/flags/value
+  RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, faceShader.m_flags.m_contentFlags));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, faceShader.m_flags.m_surfaceFlags));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getInteger(tokeniser, faceShader.m_flags.m_value));
+  return true;
+}
+
+inline bool FaceTexdef_importTokens(FaceTexdef& texdef, Tokeniser& tokeniser)
+{
+  // parse texdef
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.shift[0]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.shift[1]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.rotate));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.scale[0]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.scale[1]));
+
+  ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_importTokens: bad texdef");
+  return true;
+}
+
+inline bool FaceTexdef_BP_importTokens(FaceTexdef& texdef, Tokeniser& tokeniser)
+{
+  // parse alternate texdef
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
+  {
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[0][0]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[0][1]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[0][2]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
+  }
+  {
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[1][0]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[1][1]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_brushprimit_texdef.coords[1][2]));
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
+  }
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
+  return true;
+}
+
+inline bool FaceTexdef_HalfLife_importTokens(FaceTexdef& texdef, Tokeniser& tokeniser)
+{
+  // parse texdef
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "["));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_s.x()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_s.y()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_s.z()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.shift[0]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "]"));
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "["));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_t.x()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_t.y()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_basis_t.z()));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.shift[1]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "]"));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.rotate));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.scale[0]));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, texdef.m_projection.m_texdef.scale[1]));
+
+  texdef.m_projection.m_texdef.rotate = -texdef.m_projection.m_texdef.rotate;
+
+  ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_importTokens: bad texdef");
+  return true;
+}
+
+inline bool FacePlane_importTokens(FacePlane& facePlane, Tokeniser& tokeniser)
+{
+  // parse planepts
+  for(std::size_t i = 0; i<3; i++)
+  {
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
+    for(std::size_t j = 0; j < 3; ++j)
+    {
+      RETURN_FALSE_IF_FAIL(Tokeniser_getDouble(tokeniser, facePlane.planePoints()[i][j]));
+    }
+    RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
+  }
+  facePlane.MakePlane();
+  return true;
+}
+
+inline bool FacePlane_Doom3_importTokens(FacePlane& facePlane, Tokeniser& tokeniser)
+{
+  Plane3 plane;
+  // parse plane equation
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getDouble(tokeniser, plane.a));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getDouble(tokeniser, plane.b));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getDouble(tokeniser, plane.c));
+  RETURN_FALSE_IF_FAIL(Tokeniser_getDouble(tokeniser, plane.d));
+  plane.d = -plane.d;
+  RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
+
+  facePlane.setDoom3Plane(plane);
+  return true;
+}
+
+inline bool FaceShader_Doom3_importTokens(FaceShader& faceShader, Tokeniser& tokeniser)
+{
+  const char *shader = tokeniser.getToken();
+  if(shader == 0)
+  {
+    Tokeniser_unexpectedError(tokeniser, shader, "#shader-name");
+    return false;
+  }
+  if(string_equal(shader, "_emptyname"))
+  {
+    shader = texdef_name_default();
+  }
+  faceShader.setShader(shader);
+  return true;
+}
+
+inline bool FaceShader_importTokens(FaceShader& faceShader, Tokeniser& tokeniser)
+{
+  const char* texture = tokeniser.getToken();
+  if(texture == 0)
+  {
+    Tokeniser_unexpectedError(tokeniser, texture, "#texture-name");
+    return false;
+  }
+  if(string_equal(texture, "NULL"))
+  {
+    faceShader.setShader(texdef_name_default());
+  }
+  else
+  {
+    StringOutputStream shader(string_length(GlobalTexturePrefix_get()) + string_length(texture));
+    shader << GlobalTexturePrefix_get() << texture;
+    faceShader.setShader(shader.c_str());
+  }
+  return true;
+}
+
+
+
+
+class Doom3FaceTokenImporter
+{
+  Face& m_face;
+public:
+  Doom3FaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_Doom3_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_BP_importTokens(m_face.getTexdef(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_Doom3_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importContentsFlagsValue(m_face.getShader(), tokeniser));
+
+    m_face.getTexdef().m_projectionInitialised = true;
+    m_face.getTexdef().m_scaleApplied = true;
+
+    return true;
+  }
+};
+
+class Quake4FaceTokenImporter
+{
+  Face& m_face;
+public:
+  Quake4FaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_Doom3_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_BP_importTokens(m_face.getTexdef(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_Doom3_importTokens(m_face.getShader(), tokeniser));
+
+    m_face.getTexdef().m_projectionInitialised = true;
+    m_face.getTexdef().m_scaleApplied = true;
+
+    return true;
+  }
+};
+
+class Quake2FaceTokenImporter
+{
+  Face& m_face;
+public:
+  Quake2FaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_importTokens(m_face.getTexdef(), tokeniser));
+    if(Tokeniser_nextTokenIsDigit(tokeniser))
+    {
+      m_face.getShader().m_flags.m_specified = true;
+      RETURN_FALSE_IF_FAIL(FaceShader_importContentsFlagsValue(m_face.getShader(), tokeniser));
+    }
+    m_face.getTexdef().m_scaleApplied = true;
+    return true;
+  }
+};
+
+class Quake3FaceTokenImporter
+{
+  Face& m_face;
+public:
+  Quake3FaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_importTokens(m_face.getTexdef(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importContentsFlagsValue(m_face.getShader(), tokeniser));
+    m_face.getTexdef().m_scaleApplied = true;
+    return true;
+  }
+};
+
+class Quake3BPFaceTokenImporter
+{
+  Face& m_face;
+public:
+  Quake3BPFaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_BP_importTokens(m_face.getTexdef(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importContentsFlagsValue(m_face.getShader(), tokeniser));
+
+    m_face.getTexdef().m_projectionInitialised = true;
+    m_face.getTexdef().m_scaleApplied = true;
+
+    return true;
+  }
+};
+
+class QuakeFaceTokenImporter
+{
+  Face& m_face;
+public:
+  QuakeFaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_importTokens(m_face.getTexdef(), tokeniser));
+    m_face.getTexdef().m_scaleApplied = true;
+    return true;
+  }
+};
+
+class HalfLifeFaceTokenImporter
+{
+  Face& m_face;
+public:
+  HalfLifeFaceTokenImporter(Face& face) : m_face(face)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    RETURN_FALSE_IF_FAIL(FacePlane_importTokens(m_face.getPlane(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceShader_importTokens(m_face.getShader(), tokeniser));
+    RETURN_FALSE_IF_FAIL(FaceTexdef_HalfLife_importTokens(m_face.getTexdef(), tokeniser));
+    m_face.getTexdef().m_scaleApplied = true;
+    return true;
+  }
+};
+
+
+inline void FacePlane_Doom3_exportTokens(const FacePlane& facePlane, TokenWriter& writer)
+{
+  // write plane equation
+  writer.writeToken("(");
+  writer.writeFloat(facePlane.getDoom3Plane().a);
+  writer.writeFloat(facePlane.getDoom3Plane().b);
+  writer.writeFloat(facePlane.getDoom3Plane().c);
+  writer.writeFloat(-facePlane.getDoom3Plane().d);
+  writer.writeToken(")");
+}
+
+inline void FacePlane_exportTokens(const FacePlane& facePlane, TokenWriter& writer)
+{
+  // write planepts
+  for(std::size_t i=0; i<3; i++)
+  {
+    writer.writeToken("(");
+    for(std::size_t j=0; j<3; j++)
+    {
+      writer.writeFloat(Face::m_quantise(facePlane.planePoints()[i][j]));
+    }
+    writer.writeToken(")");
+  }
+}
+
+inline void FaceTexdef_BP_exportTokens(const FaceTexdef& faceTexdef, TokenWriter& writer)
+{
+  // write alternate texdef
+  writer.writeToken("(");
+  {
+    writer.writeToken("(");
+    for(std::size_t i=0;i<3;i++)
+    {
+      writer.writeFloat(faceTexdef.m_projection.m_brushprimit_texdef.coords[0][i]);
+    }
+    writer.writeToken(")");
+  }
+  {
+    writer.writeToken("(");
+    for(std::size_t i=0;i<3;i++)
+    {
+      writer.writeFloat(faceTexdef.m_projection.m_brushprimit_texdef.coords[1][i]);
+    }
+    writer.writeToken(")");
+  }
+  writer.writeToken(")");
+}
+
+inline void FaceTexdef_exportTokens(const FaceTexdef& faceTexdef, TokenWriter& writer)
+{
+  ASSERT_MESSAGE(texdef_sane(faceTexdef.m_projection.m_texdef), "FaceTexdef_exportTokens: bad texdef");
+  // write texdef
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.shift[0]);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.shift[1]);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.rotate);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.scale[0]);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.scale[1]);
+}
+
+inline void FaceTexdef_HalfLife_exportTokens(const FaceTexdef& faceTexdef, TokenWriter& writer)
+{
+  ASSERT_MESSAGE(texdef_sane(faceTexdef.m_projection.m_texdef), "FaceTexdef_exportTokens: bad texdef");
+  // write texdef
+  writer.writeToken("[");
+  writer.writeFloat(faceTexdef.m_projection.m_basis_s.x());
+  writer.writeFloat(faceTexdef.m_projection.m_basis_s.y());
+  writer.writeFloat(faceTexdef.m_projection.m_basis_s.z());
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.shift[0]);
+  writer.writeToken("]");
+  writer.writeToken("[");
+  writer.writeFloat(faceTexdef.m_projection.m_basis_t.x());
+  writer.writeFloat(faceTexdef.m_projection.m_basis_t.y());
+  writer.writeFloat(faceTexdef.m_projection.m_basis_t.z());
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.shift[1]);
+  writer.writeToken("]");
+  writer.writeFloat(-faceTexdef.m_projection.m_texdef.rotate);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.scale[0]);
+  writer.writeFloat(faceTexdef.m_projection.m_texdef.scale[1]);
+}
+
+inline void FaceShader_ContentsFlagsValue_exportTokens(const FaceShader& faceShader, TokenWriter& writer)
+{
+  // write surface flags
+  writer.writeInteger(faceShader.m_flags.m_contentFlags);
+  writer.writeInteger(faceShader.m_flags.m_surfaceFlags);
+  writer.writeInteger(faceShader.m_flags.m_value);
+}
+
+inline void FaceShader_exportTokens(const FaceShader& faceShader, TokenWriter& writer)
+{
+  // write shader name  
+  if(string_empty(shader_get_textureName(faceShader.getShader())))
+  {
+    writer.writeToken("NULL");
+  }
+  else
+  {
+    writer.writeToken(shader_get_textureName(faceShader.getShader()));
+  }
+}
+
+inline void FaceShader_Doom3_exportTokens(const FaceShader& faceShader, TokenWriter& writer)
+{
+  // write shader name  
+  if(string_empty(shader_get_textureName(faceShader.getShader())))
+  {
+    writer.writeString("_emptyname");
+  }
+  else
+  {
+    writer.writeString(faceShader.getShader());
+  }
+}
+
+class Doom3FaceTokenExporter
+{
+  const Face& m_face;
+public:
+  Doom3FaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_Doom3_exportTokens(m_face.getPlane(), writer);
+    FaceTexdef_BP_exportTokens(m_face.getTexdef(), writer);
+    FaceShader_Doom3_exportTokens(m_face.getShader(), writer);
+    FaceShader_ContentsFlagsValue_exportTokens(m_face.getShader(), writer);
+    writer.nextLine();
+  }
+};
+
+class Quake4FaceTokenExporter
+{
+  const Face& m_face;
+public:
+  Quake4FaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_Doom3_exportTokens(m_face.getPlane(), writer);
+    FaceTexdef_BP_exportTokens(m_face.getTexdef(), writer);
+    FaceShader_Doom3_exportTokens(m_face.getShader(), writer);
+    writer.nextLine();
+  }
+};
+
+class Quake2FaceTokenExporter
+{
+  const Face& m_face;
+public:
+  Quake2FaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_exportTokens(m_face.getPlane(), writer);
+    FaceShader_exportTokens(m_face.getShader(), writer);
+    FaceTexdef_exportTokens(m_face.getTexdef(), writer);
+    if(m_face.getShader().m_flags.m_specified || m_face.isDetail())
+    {
+      FaceShader_ContentsFlagsValue_exportTokens(m_face.getShader(), writer);
+    }
+    writer.nextLine();
+  }
+};
+
+class Quake3FaceTokenExporter
+{
+  const Face& m_face;
+public:
+  Quake3FaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_exportTokens(m_face.getPlane(), writer);
+    FaceShader_exportTokens(m_face.getShader(), writer);
+    FaceTexdef_exportTokens(m_face.getTexdef(), writer);
+    FaceShader_ContentsFlagsValue_exportTokens(m_face.getShader(), writer);
+    writer.nextLine();
+  }
+};
+
+class Quake3BPFaceTokenExporter
+{
+  const Face& m_face;
+public:
+  Quake3BPFaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_exportTokens(m_face.getPlane(), writer);
+    FaceTexdef_BP_exportTokens(m_face.getTexdef(), writer);
+    FaceShader_exportTokens(m_face.getShader(), writer);
+    FaceShader_ContentsFlagsValue_exportTokens(m_face.getShader(), writer);
+    writer.nextLine();
+  }
+};
+
+class QuakeFaceTokenExporter
+{
+  const Face& m_face;
+public:
+  QuakeFaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_exportTokens(m_face.getPlane(), writer);
+    FaceShader_exportTokens(m_face.getShader(), writer);
+    FaceTexdef_exportTokens(m_face.getTexdef(), writer);
+    writer.nextLine();
+  }
+};
+
+class HalfLifeFaceTokenExporter
+{
+  const Face& m_face;
+public:
+  HalfLifeFaceTokenExporter(const Face& face) : m_face(face)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    FacePlane_exportTokens(m_face.getPlane(), writer);
+    FaceShader_exportTokens(m_face.getShader(), writer);
+    FaceTexdef_HalfLife_exportTokens(m_face.getTexdef(), writer);
+    writer.nextLine();
+  }
+};
+
+
+class BrushTokenImporter : public MapImporter
+{
+  Brush& m_brush;
+
+public:
+  BrushTokenImporter(Brush& brush) : m_brush(brush)
+  {
+  }
+  bool importTokens(Tokeniser& tokeniser)
+  {
+    if(Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4)
+    {
+      tokeniser.nextLine();
+      RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "{"));
+    }
+    while(1)
+    {
+      // check for end of brush
+      tokeniser.nextLine();
+      const char* token = tokeniser.getToken();
+      if(string_equal(token, "}"))
+      {
+        break;
+      }
+
+      tokeniser.ungetToken();
+
+      m_brush.push_back(FaceSmartPointer(new Face(&m_brush)));
+
+      //!todo BP support
+      tokeniser.nextLine();
+
+      Face& face = *m_brush.back();
+
+      switch(Brush::m_type)
+      {
+      case eBrushTypeDoom3:
+        {
+          Doom3FaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeQuake4:
+        {
+          Quake4FaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeQuake2:
+        {
+          Quake2FaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeQuake3:
+        {
+          Quake3FaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeQuake3BP:
+        {
+          Quake3BPFaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeQuake:
+        {
+          QuakeFaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      case eBrushTypeHalfLife:
+        {
+          HalfLifeFaceTokenImporter importer(face);
+          RETURN_FALSE_IF_FAIL(importer.importTokens(tokeniser));
+        }
+        break;
+      }
+      face.planeChanged();
+    }
+    if(Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4)
+    {
+      tokeniser.nextLine();
+      RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "}"));
+    }
+
+    m_brush.planeChanged();
+    m_brush.shaderChanged();
+
+    return true;
+  }
+};
+
+
+class BrushTokenExporter : public MapExporter
+{
+  const Brush& m_brush;
+
+public:
+  BrushTokenExporter(const Brush& brush) : m_brush(brush)
+  {
+  }
+  void exportTokens(TokenWriter& writer) const
+  {
+    m_brush.evaluateBRep(); // ensure b-rep is up-to-date, so that non-contributing faces can be identified.
+
+    if(!m_brush.hasContributingFaces())
+    {
+      return;
+    }
+
+    writer.writeToken("{");
+    writer.nextLine();
+
+    if(Brush::m_type == eBrushTypeQuake3BP)
+    {
+      writer.writeToken("brushDef");
+      writer.nextLine();
+      writer.writeToken("{");
+      writer.nextLine();
+    }
+    
+    if(Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4)
+    {
+      writer.writeToken("brushDef3");
+      writer.nextLine();
+      writer.writeToken("{");
+      writer.nextLine();
+    }
+
+    for(Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i)
+    {
+      const Face& face = *(*i);
+
+      if(face.contributes())
+      {
+        switch(Brush::m_type)
+        {
+        case eBrushTypeDoom3:
+          {
+            Doom3FaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeQuake4:
+          {
+            Quake4FaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeQuake2:
+          {
+            Quake2FaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeQuake3:
+          {
+            Quake3FaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeQuake3BP:
+          {
+            Quake3BPFaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeQuake:
+          {
+            QuakeFaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        case eBrushTypeHalfLife:
+          {
+            HalfLifeFaceTokenExporter exporter(face);
+            exporter.exportTokens(writer);
+          }
+          break;
+        }
+      }
+    }
+
+    if(Brush::m_type == eBrushTypeQuake3BP || Brush::m_type == eBrushTypeDoom3 || Brush::m_type == eBrushTypeQuake4)
+    {
+      writer.writeToken("}");
+      writer.nextLine();
+    }
+
+    writer.writeToken("}");
+    writer.nextLine();
+  }
+};
+
+    
+#endif