2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #if !defined( INCLUDED_BRUSHXML_H )
23 #define INCLUDED_BRUSHXML_H
25 #include <util/buffer.h>
26 #include "stream/stringstream.h"
27 #include "xml/xmlelement.h"
31 inline void FaceTexdef_BP_importXML( FaceTexdef& texdef, const char* xmlContent ){
32 StringTokeniser content( xmlContent );
34 texdef.m_projection.m_brushprimit_texdef.coords[0][0] = static_cast<float>( atof( content.getToken() ) );
35 texdef.m_projection.m_brushprimit_texdef.coords[0][1] = static_cast<float>( atof( content.getToken() ) );
36 texdef.m_projection.m_brushprimit_texdef.coords[0][2] = static_cast<float>( atof( content.getToken() ) );
37 texdef.m_projection.m_brushprimit_texdef.coords[1][0] = static_cast<float>( atof( content.getToken() ) );
38 texdef.m_projection.m_brushprimit_texdef.coords[1][1] = static_cast<float>( atof( content.getToken() ) );
39 texdef.m_projection.m_brushprimit_texdef.coords[1][2] = static_cast<float>( atof( content.getToken() ) );
41 inline void FaceTexdef_importXML( FaceTexdef& texdef, const char* xmlContent ){
42 StringTokeniser content( xmlContent );
44 texdef.m_projection.m_texdef.shift[0] = static_cast<float>( atof( content.getToken() ) );
45 texdef.m_projection.m_texdef.shift[1] = static_cast<float>( atof( content.getToken() ) );
46 texdef.m_projection.m_texdef.rotate = static_cast<float>( atof( content.getToken() ) );
47 texdef.m_projection.m_texdef.scale[0] = static_cast<float>( atof( content.getToken() ) );
48 texdef.m_projection.m_texdef.scale[1] = static_cast<float>( atof( content.getToken() ) );
50 ASSERT_MESSAGE( texdef_sane( texdef.m_projection.m_texdef ), "FaceTexdef_importXML: bad texdef" );
53 inline void FacePlane_importXML( FacePlane& facePlane, const char* xmlContent ){
54 StringTokeniser content( xmlContent );
56 for ( int i = 0; i < 3; ++i )
58 for ( int j = 0; j < 3; ++j )
60 facePlane.planePoints()[i][j] = atof( content.getToken() );
63 facePlane.MakePlane();
82 StringOutputStream m_content;
84 xml_state_t( EState state )
88 EState state() const {
91 const char* content() const {
92 return m_content.c_str();
94 std::size_t write( const char* buffer, std::size_t length ){
95 return m_content.write( buffer, length );
99 std::vector<xml_state_t> m_xml_state;
102 FaceXMLImporter( Face& face ) : m_face( face ){
103 m_xml_state.push_back( xml_state_t::eDefault );
106 m_face.planeChanged();
109 void pushElement( const XMLElement& element ){
110 ASSERT_MESSAGE( m_xml_state.back().state() == xml_state_t::eDefault, "parse error" );
112 if ( strcmp( element.name(), "planepts" ) == 0 ) {
113 m_xml_state.push_back( xml_state_t::ePlanePts );
115 else if ( strcmp( element.name(), "texdef" ) == 0 ) {
116 m_xml_state.push_back( xml_state_t::eTexdef );
118 else if ( strcmp( element.name(), "bpmatrix" ) == 0 ) {
119 m_xml_state.push_back( xml_state_t::eBPMatrix );
121 else if ( strcmp( element.name(), "flags" ) == 0 ) {
122 m_xml_state.push_back( xml_state_t::eFlags );
124 else if ( strcmp( element.name(), "shader" ) == 0 ) {
125 m_xml_state.push_back( xml_state_t::eShader );
128 void popElement( const char* name ){
129 ASSERT_MESSAGE( m_xml_state.back().state() != xml_state_t::eDefault, "parse error" );
131 switch ( m_xml_state.back().state() )
133 case xml_state_t::ePlanePts:
135 FacePlane_importXML( m_face.getPlane(), m_xml_state.back().content() );
138 case xml_state_t::eTexdef:
140 FaceTexdef_importXML( m_face.getTexdef(), m_xml_state.back().content() );
143 case xml_state_t::eBPMatrix:
145 FaceTexdef_BP_importXML( m_face.getTexdef(), m_xml_state.back().content() );
148 case xml_state_t::eFlags:
150 StringTokeniser content( m_xml_state.back().content() );
152 m_face.getShader().m_flags.m_contentFlags = atoi( content.getToken() );
153 m_face.getShader().m_flags.m_surfaceFlags = atoi( content.getToken() );
154 m_face.getShader().m_flags.m_value = atoi( content.getToken() );
157 case xml_state_t::eShader:
159 m_face.getShader().setShader( m_xml_state.back().content() );
166 m_xml_state.pop_back();
168 std::size_t write( const char* data, std::size_t length ){
169 ASSERT_MESSAGE( !m_xml_state.empty(), "parse error" );
170 return m_xml_state.back().write( data, length );
175 inline void FaceTexdef_exportXML( const FaceTexdef& texdef, XMLImporter& importer ){
176 StaticElement element( "texdef" );
177 importer.pushElement( element );
179 ASSERT_MESSAGE( texdef_sane( texdef.m_projection.m_texdef ), "FaceTexdef_exportXML: bad texdef" );
181 importer << texdef.m_projection.m_texdef.shift[0]
182 << ' ' << texdef.m_projection.m_texdef.shift[1]
183 << ' ' << texdef.m_projection.m_texdef.rotate
184 << ' ' << texdef.m_projection.m_texdef.scale[0]
185 << ' ' << texdef.m_projection.m_texdef.scale[1];
187 importer.popElement( element.name() );
189 inline void FaceTexdef_BP_exportXML( const FaceTexdef& texdef, XMLImporter& importer ){
190 StaticElement element( "texdef" );
191 importer.pushElement( element );
193 for ( int i = 0; i < 2; ++i )
195 for ( int j = 0; j < 3; ++j )
197 importer << texdef.m_projection.m_brushprimit_texdef.coords[i][j] << ' ';
201 importer.popElement( element.name() );
203 inline void FaceShader_ContentsFlagsValue_exportXML( const FaceShader& faceShader, XMLImporter& importer ){
204 StaticElement element( "flags" );
205 importer.pushElement( element );
208 importer << faceShader.m_flags.m_contentFlags
209 << ' ' << faceShader.m_flags.m_surfaceFlags
210 << ' ' << faceShader.m_flags.m_value;
213 importer.popElement( element.name() );
216 inline void FacePlane_exportXML( const FacePlane& facePlane, XMLImporter& importer ){
217 StaticElement element( "planepts" );
218 importer.pushElement( element );
222 for ( int i = 0 ; i < 3 ; i++ )
224 for ( int j = 0 ; j < 3 ; j++ )
226 importer << Face::m_quantise( facePlane.planePoints()[i][j] ) << ' ';
231 importer.popElement( element.name() );
234 inline void FacePolygon_exportXML( const Winding& w, const BasicVector3<double>& normal, XMLImporter& importer ){
235 DynamicElement element( "polygon" );
237 auto tmp = u::buffer<32>();
239 sprintf( tmp, "%f", normal.x() );
240 element.insertAttribute( "nx", tmp );
242 sprintf( tmp, "%f", normal.y() );
243 element.insertAttribute( "ny", tmp );
245 sprintf( tmp, "%f", normal.z() );
246 element.insertAttribute( "nz", tmp );
248 importer.pushElement( element );
250 for ( unsigned int i = 0; i < w.numpoints; ++i )
252 DynamicElement c( "vertex" );
254 sprintf( tmp, "%f", w.points[i].vertex.x() );
255 c.insertAttribute( "x", tmp );
257 sprintf( tmp, "%f", w.points[i].vertex.y() );
258 c.insertAttribute( "y", tmp );
260 sprintf( tmp, "%f", w.points[i].vertex.z() );
261 c.insertAttribute( "z", tmp );
263 sprintf( tmp, "%f", w.points[i].texcoord.x() );
264 c.insertAttribute( "s", tmp );
266 sprintf( tmp, "%f", w.points[i].texcoord.y() );
267 c.insertAttribute( "t", tmp );
269 importer.pushElement( c );
270 importer.popElement( c.name() );
273 importer.popElement( element.name() );
276 class FaceXMLExporter
280 FaceXMLExporter( const Face& face ) : m_face( face ){
282 void exportXML( XMLImporter& importer ){
283 bool bAlternateTexdef = ( Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeDoom3 || Face::m_type == eBrushTypeQuake4 );
287 StaticElement element( "shader" );
288 importer.pushElement( element );
289 importer << m_face.getShader().getShader();
290 importer.popElement( element.name() );
293 FacePolygon_exportXML( m_face.getWinding(), m_face.getPlane().plane3().normal(), importer );
294 FacePlane_exportXML( m_face.getPlane(), importer );
296 if ( !bAlternateTexdef ) {
297 FaceTexdef_exportXML( m_face.getTexdef(), importer );
301 FaceTexdef_BP_exportXML( m_face.getTexdef(), importer );
304 FaceShader_ContentsFlagsValue_exportXML( m_face.getShader(), importer );
309 class BrushXMLImporter : public XMLImporter
325 xml_state_t( EState state )
328 EState state() const {
333 std::vector<xml_state_t> m_xml_state;
334 char m_faceImporter[sizeof( FaceXMLImporter )];
337 FaceXMLImporter& faceImporter(){
338 return *reinterpret_cast<FaceXMLImporter*>( m_faceImporter );
342 BrushXMLImporter( Brush& brush ) : m_brush( brush ){
343 m_xml_state.push_back( xml_state_t::eDefault );
345 void pushElement( const XMLElement& element ){
346 switch ( m_xml_state.back().state() )
348 case xml_state_t::eDefault:
349 ASSERT_MESSAGE( strcmp( element.name(), "brush" ) == 0, "parse error" );
350 m_xml_state.push_back( xml_state_t::eBrush );
352 case xml_state_t::eBrush:
353 ASSERT_MESSAGE( strcmp( element.name(), "plane" ) == 0, "parse error" );
354 m_xml_state.push_back( xml_state_t::eFace );
355 m_brush.push_back( FaceSmartPointer( new Face( &m_brush ) ) );
356 constructor( faceImporter(), makeReference( *m_brush.back() ) );
357 m_brush.planeChanged();
358 m_brush.shaderChanged();
360 case xml_state_t::eFace:
361 m_xml_state.push_back( xml_state_t::eFace );
362 faceImporter().pushElement( element );
366 void popElement( const char* name ){
367 ASSERT_MESSAGE( !m_xml_state.empty(), "parse error" );
368 m_xml_state.pop_back();
370 switch ( m_xml_state.back().state() )
372 case xml_state_t::eDefault:
374 case xml_state_t::eBrush:
375 destructor( faceImporter() );
377 case xml_state_t::eFace:
378 faceImporter().popElement( name );
382 std::size_t write( const char* data, std::size_t length ){
383 switch ( m_xml_state.back().state() )
385 case xml_state_t::eFace:
386 return faceImporter().write( data, length );
395 class BrushXMLExporter : public XMLExporter
397 const Brush& m_brush;
400 BrushXMLExporter( const Brush& brush ) : m_brush( brush ){
402 void exportXML( XMLImporter& importer ){
403 m_brush.evaluateBRep(); // ensure b-rep is up-to-date, so that non-contributing faces can be identified.
404 ASSERT_MESSAGE( m_brush.hasContributingFaces(), "exporting an empty brush" );
406 const StaticElement brushElement( "brush" );
407 importer.pushElement( brushElement );
409 for ( Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i )
411 if ( ( *i )->contributes() ) {
412 const StaticElement element( "plane" );
413 importer.pushElement( element );
414 FaceXMLExporter( *( *i ) ).exportXML( importer );
415 importer.popElement( element.name() );
419 importer.popElement( brushElement.name() );