/* 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_BRUSHXML_H ) #define INCLUDED_BRUSHXML_H #include "stream/stringstream.h" #include "xml/xmlelement.h" #include "brush.h" inline void FaceTexdef_BP_importXML(FaceTexdef &texdef, const char *xmlContent) { StringTokeniser content(xmlContent); texdef.m_projection.m_brushprimit_texdef.coords[0][0] = static_cast( atof(content.getToken())); texdef.m_projection.m_brushprimit_texdef.coords[0][1] = static_cast( atof(content.getToken())); texdef.m_projection.m_brushprimit_texdef.coords[0][2] = static_cast( atof(content.getToken())); texdef.m_projection.m_brushprimit_texdef.coords[1][0] = static_cast( atof(content.getToken())); texdef.m_projection.m_brushprimit_texdef.coords[1][1] = static_cast( atof(content.getToken())); texdef.m_projection.m_brushprimit_texdef.coords[1][2] = static_cast( atof(content.getToken())); } inline void FaceTexdef_importXML(FaceTexdef &texdef, const char *xmlContent) { StringTokeniser content(xmlContent); texdef.m_projection.m_texdef.shift[0] = static_cast( atof(content.getToken())); texdef.m_projection.m_texdef.shift[1] = static_cast( atof(content.getToken())); texdef.m_projection.m_texdef.rotate = static_cast( atof(content.getToken())); texdef.m_projection.m_texdef.scale[0] = static_cast( atof(content.getToken())); texdef.m_projection.m_texdef.scale[1] = static_cast( atof(content.getToken())); ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_importXML: bad texdef"); } inline void FacePlane_importXML(FacePlane &facePlane, const char *xmlContent) { StringTokeniser content(xmlContent); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { facePlane.planePoints()[i][j] = atof(content.getToken()); } } facePlane.MakePlane(); } class FaceXMLImporter { struct xml_state_t { enum EState { eDefault, ePlanePts, eTexdef, eBPMatrix, eFlags, eShader, }; EState m_state; StringOutputStream m_content; xml_state_t(EState state) : m_state(state) {} EState state() const { return m_state; } const char *content() const { return m_content.c_str(); } std::size_t write(const char *buffer, std::size_t length) { return m_content.write(buffer, length); } }; std::vector m_xml_state; Face &m_face; public: FaceXMLImporter(Face &face) : m_face(face) { m_xml_state.push_back(xml_state_t::eDefault); } ~FaceXMLImporter() { m_face.planeChanged(); } void pushElement(const XMLElement &element) { ASSERT_MESSAGE(m_xml_state.back().state() == xml_state_t::eDefault, "parse error"); if (strcmp(element.name(), "planepts") == 0) { m_xml_state.push_back(xml_state_t::ePlanePts); } else if (strcmp(element.name(), "texdef") == 0) { m_xml_state.push_back(xml_state_t::eTexdef); } else if (strcmp(element.name(), "bpmatrix") == 0) { m_xml_state.push_back(xml_state_t::eBPMatrix); } else if (strcmp(element.name(), "flags") == 0) { m_xml_state.push_back(xml_state_t::eFlags); } else if (strcmp(element.name(), "shader") == 0) { m_xml_state.push_back(xml_state_t::eShader); } } void popElement(const char *name) { ASSERT_MESSAGE(m_xml_state.back().state() != xml_state_t::eDefault, "parse error"); switch (m_xml_state.back().state()) { case xml_state_t::ePlanePts: { FacePlane_importXML(m_face.getPlane(), m_xml_state.back().content()); } break; case xml_state_t::eTexdef: { FaceTexdef_importXML(m_face.getTexdef(), m_xml_state.back().content()); } break; case xml_state_t::eBPMatrix: { FaceTexdef_BP_importXML(m_face.getTexdef(), m_xml_state.back().content()); } break; case xml_state_t::eFlags: { StringTokeniser content(m_xml_state.back().content()); m_face.getShader().m_flags.m_contentFlags = atoi(content.getToken()); m_face.getShader().m_flags.m_surfaceFlags = atoi(content.getToken()); m_face.getShader().m_flags.m_value = atoi(content.getToken()); } break; case xml_state_t::eShader: { m_face.getShader().setShader(m_xml_state.back().content()); } break; default: break; } m_xml_state.pop_back(); } std::size_t write(const char *data, std::size_t length) { ASSERT_MESSAGE(!m_xml_state.empty(), "parse error"); return m_xml_state.back().write(data, length); } }; inline void FaceTexdef_exportXML(const FaceTexdef &texdef, XMLImporter &importer) { StaticElement element("texdef"); importer.pushElement(element); ASSERT_MESSAGE(texdef_sane(texdef.m_projection.m_texdef), "FaceTexdef_exportXML: bad texdef"); importer << texdef.m_projection.m_texdef.shift[0] << ' ' << texdef.m_projection.m_texdef.shift[1] << ' ' << texdef.m_projection.m_texdef.rotate << ' ' << texdef.m_projection.m_texdef.scale[0] << ' ' << texdef.m_projection.m_texdef.scale[1]; importer.popElement(element.name()); } inline void FaceTexdef_BP_exportXML(const FaceTexdef &texdef, XMLImporter &importer) { StaticElement element("texdef"); importer.pushElement(element); for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; ++j) { importer << texdef.m_projection.m_brushprimit_texdef.coords[i][j] << ' '; } } importer.popElement(element.name()); } inline void FaceShader_ContentsFlagsValue_exportXML(const FaceShader &faceShader, XMLImporter &importer) { StaticElement element("flags"); importer.pushElement(element); { importer << faceShader.m_flags.m_contentFlags << ' ' << faceShader.m_flags.m_surfaceFlags << ' ' << faceShader.m_flags.m_value; } importer.popElement(element.name()); } inline void FacePlane_exportXML(const FacePlane &facePlane, XMLImporter &importer) { StaticElement element("planepts"); importer.pushElement(element); { // write planepts for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { importer << Face::m_quantise(facePlane.planePoints()[i][j]) << ' '; } } } importer.popElement(element.name()); } inline void FacePolygon_exportXML(const Winding &w, const BasicVector3 &normal, XMLImporter &importer) { DynamicElement element("polygon"); char tmp[32]; sprintf(tmp, "%f", normal.x()); element.insertAttribute("nx", tmp); sprintf(tmp, "%f", normal.y()); element.insertAttribute("ny", tmp); sprintf(tmp, "%f", normal.z()); element.insertAttribute("nz", tmp); importer.pushElement(element); for (unsigned int i = 0; i < w.numpoints; ++i) { DynamicElement c("vertex"); sprintf(tmp, "%f", w.points[i].vertex.x()); c.insertAttribute("x", tmp); sprintf(tmp, "%f", w.points[i].vertex.y()); c.insertAttribute("y", tmp); sprintf(tmp, "%f", w.points[i].vertex.z()); c.insertAttribute("z", tmp); sprintf(tmp, "%f", w.points[i].texcoord.x()); c.insertAttribute("s", tmp); sprintf(tmp, "%f", w.points[i].texcoord.y()); c.insertAttribute("t", tmp); importer.pushElement(c); importer.popElement(c.name()); } importer.popElement(element.name()); } class FaceXMLExporter { const Face &m_face; public: FaceXMLExporter(const Face &face) : m_face(face) { } void exportXML(XMLImporter &importer) { bool bAlternateTexdef = (Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeDoom3 || Face::m_type == eBrushTypeQuake4); // write shader { StaticElement element("shader"); importer.pushElement(element); importer << m_face.getShader().getShader(); importer.popElement(element.name()); } FacePolygon_exportXML(m_face.getWinding(), m_face.getPlane().plane3().normal(), importer); FacePlane_exportXML(m_face.getPlane(), importer); if (!bAlternateTexdef) { FaceTexdef_exportXML(m_face.getTexdef(), importer); } else { FaceTexdef_BP_exportXML(m_face.getTexdef(), importer); } FaceShader_ContentsFlagsValue_exportXML(m_face.getShader(), importer); } }; class BrushXMLImporter : public XMLImporter { class xml_state_t { public: enum EState { eDefault, eBrush, eFace, }; private: EState m_state; public: xml_state_t(EState state) : m_state(state) { } EState state() const { return m_state; } }; std::vector m_xml_state; char m_faceImporter[sizeof(FaceXMLImporter)]; Brush &m_brush; FaceXMLImporter &faceImporter() { return *reinterpret_cast( m_faceImporter ); } public: BrushXMLImporter(Brush &brush) : m_brush(brush) { m_xml_state.push_back(xml_state_t::eDefault); } void pushElement(const XMLElement &element) { switch (m_xml_state.back().state()) { case xml_state_t::eDefault: ASSERT_MESSAGE(strcmp(element.name(), "brush") == 0, "parse error"); m_xml_state.push_back(xml_state_t::eBrush); break; case xml_state_t::eBrush: ASSERT_MESSAGE(strcmp(element.name(), "plane") == 0, "parse error"); m_xml_state.push_back(xml_state_t::eFace); m_brush.push_back(FaceSmartPointer(new Face(&m_brush))); constructor(faceImporter(), makeReference(*m_brush.back())); m_brush.planeChanged(); m_brush.shaderChanged(); break; case xml_state_t::eFace: m_xml_state.push_back(xml_state_t::eFace); faceImporter().pushElement(element); break; } } void popElement(const char *name) { ASSERT_MESSAGE(!m_xml_state.empty(), "parse error"); m_xml_state.pop_back(); switch (m_xml_state.back().state()) { case xml_state_t::eDefault: break; case xml_state_t::eBrush: destructor(faceImporter()); break; case xml_state_t::eFace: faceImporter().popElement(name); break; } } std::size_t write(const char *data, std::size_t length) { switch (m_xml_state.back().state()) { case xml_state_t::eFace: return faceImporter().write(data, length); break; default: break; } return length; } }; class BrushXMLExporter : public XMLExporter { const Brush &m_brush; public: BrushXMLExporter(const Brush &brush) : m_brush(brush) { } void exportXML(XMLImporter &importer) { m_brush.evaluateBRep(); // ensure b-rep is up-to-date, so that non-contributing faces can be identified. ASSERT_MESSAGE(m_brush.hasContributingFaces(), "exporting an empty brush"); const StaticElement brushElement("brush"); importer.pushElement(brushElement); for (Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i) { if ((*i)->contributes()) { const StaticElement element("plane"); importer.pushElement(element); FaceXMLExporter(*(*i)).exportXML(importer); importer.popElement(element.name()); } } importer.popElement(brushElement.name()); } }; #endif