]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/brushxml.h
Implement buffer operations
[xonotic/netradiant.git] / radiant / brushxml.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #if !defined( INCLUDED_BRUSHXML_H )
23 #define INCLUDED_BRUSHXML_H
24
25 #include <util/buffer.h>
26 #include "stream/stringstream.h"
27 #include "xml/xmlelement.h"
28
29 #include "brush.h"
30
31 inline void FaceTexdef_BP_importXML( FaceTexdef& texdef, const char* xmlContent ){
32         StringTokeniser content( xmlContent );
33
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() ) );
40 }
41 inline void FaceTexdef_importXML( FaceTexdef& texdef, const char* xmlContent ){
42         StringTokeniser content( xmlContent );
43
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() ) );
49
50         ASSERT_MESSAGE( texdef_sane( texdef.m_projection.m_texdef ), "FaceTexdef_importXML: bad texdef" );
51 }
52
53 inline void FacePlane_importXML( FacePlane& facePlane, const char* xmlContent ){
54         StringTokeniser content( xmlContent );
55
56         for ( int i = 0; i < 3; ++i )
57         {
58                 for ( int j = 0; j < 3; ++j )
59                 {
60                         facePlane.planePoints()[i][j] = atof( content.getToken() );
61                 }
62         }
63         facePlane.MakePlane();
64 }
65
66
67 class FaceXMLImporter
68 {
69 struct xml_state_t
70 {
71         enum EState
72         {
73                 eDefault,
74                 ePlanePts,
75                 eTexdef,
76                 eBPMatrix,
77                 eFlags,
78                 eShader,
79         };
80
81         EState m_state;
82         StringOutputStream m_content;
83
84         xml_state_t( EState state )
85                 : m_state( state )
86         {}
87
88         EState state() const {
89                 return m_state;
90         }
91         const char* content() const {
92                 return m_content.c_str();
93         }
94         std::size_t write( const char* buffer, std::size_t length ){
95                 return m_content.write( buffer, length );
96         }
97 };
98
99 std::vector<xml_state_t> m_xml_state;
100 Face& m_face;
101 public:
102 FaceXMLImporter( Face& face ) : m_face( face ){
103         m_xml_state.push_back( xml_state_t::eDefault );
104 }
105 ~FaceXMLImporter(){
106         m_face.planeChanged();
107 }
108
109 void pushElement( const XMLElement& element ){
110         ASSERT_MESSAGE( m_xml_state.back().state() == xml_state_t::eDefault, "parse error" );
111
112         if ( strcmp( element.name(), "planepts" ) == 0 ) {
113                 m_xml_state.push_back( xml_state_t::ePlanePts );
114         }
115         else if ( strcmp( element.name(), "texdef" ) == 0 ) {
116                 m_xml_state.push_back( xml_state_t::eTexdef );
117         }
118         else if ( strcmp( element.name(), "bpmatrix" ) == 0 ) {
119                 m_xml_state.push_back( xml_state_t::eBPMatrix );
120         }
121         else if ( strcmp( element.name(), "flags" ) == 0 ) {
122                 m_xml_state.push_back( xml_state_t::eFlags );
123         }
124         else if ( strcmp( element.name(), "shader" ) == 0 ) {
125                 m_xml_state.push_back( xml_state_t::eShader );
126         }
127 }
128 void popElement( const char* name ){
129         ASSERT_MESSAGE( m_xml_state.back().state() != xml_state_t::eDefault, "parse error" );
130
131         switch ( m_xml_state.back().state() )
132         {
133         case xml_state_t::ePlanePts:
134         {
135                 FacePlane_importXML( m_face.getPlane(), m_xml_state.back().content() );
136         }
137         break;
138         case xml_state_t::eTexdef:
139         {
140                 FaceTexdef_importXML( m_face.getTexdef(), m_xml_state.back().content() );
141         }
142         break;
143         case xml_state_t::eBPMatrix:
144         {
145                 FaceTexdef_BP_importXML( m_face.getTexdef(), m_xml_state.back().content() );
146         }
147         break;
148         case xml_state_t::eFlags:
149         {
150                 StringTokeniser content( m_xml_state.back().content() );
151
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() );
155         }
156         break;
157         case xml_state_t::eShader:
158         {
159                 m_face.getShader().setShader( m_xml_state.back().content() );
160         }
161         break;
162         default:
163                 break;
164         }
165
166         m_xml_state.pop_back();
167 }
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 );
171 }
172 };
173
174
175 inline void FaceTexdef_exportXML( const FaceTexdef& texdef, XMLImporter& importer ){
176         StaticElement element( "texdef" );
177         importer.pushElement( element );
178
179         ASSERT_MESSAGE( texdef_sane( texdef.m_projection.m_texdef ), "FaceTexdef_exportXML: bad texdef" );
180
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];
186
187         importer.popElement( element.name() );
188 }
189 inline void FaceTexdef_BP_exportXML( const FaceTexdef& texdef, XMLImporter& importer ){
190         StaticElement element( "texdef" );
191         importer.pushElement( element );
192
193         for ( int i = 0; i < 2; ++i )
194         {
195                 for ( int j = 0; j < 3; ++j )
196                 {
197                         importer << texdef.m_projection.m_brushprimit_texdef.coords[i][j] << ' ';
198                 }
199         }
200
201         importer.popElement( element.name() );
202 }
203 inline void FaceShader_ContentsFlagsValue_exportXML( const FaceShader& faceShader, XMLImporter& importer ){
204         StaticElement element( "flags" );
205         importer.pushElement( element );
206
207         {
208                 importer << faceShader.m_flags.m_contentFlags
209                                  << ' ' << faceShader.m_flags.m_surfaceFlags
210                                  << ' ' << faceShader.m_flags.m_value;
211         }
212
213         importer.popElement( element.name() );
214 }
215
216 inline void FacePlane_exportXML( const FacePlane& facePlane, XMLImporter& importer ){
217         StaticElement element( "planepts" );
218         importer.pushElement( element );
219
220         {
221                 // write planepts
222                 for ( int i = 0 ; i < 3 ; i++ )
223                 {
224                         for ( int j = 0 ; j < 3 ; j++ )
225                         {
226                                 importer << Face::m_quantise( facePlane.planePoints()[i][j] ) << ' ';
227                         }
228                 }
229         }
230
231         importer.popElement( element.name() );
232 }
233
234 inline void FacePolygon_exportXML( const Winding& w, const BasicVector3<double>& normal, XMLImporter& importer ){
235         DynamicElement element( "polygon" );
236
237         auto tmp = u::buffer<32>();
238
239     tmp.sprintf( "%f", normal.x() );
240         element.insertAttribute( "nx", tmp );
241
242     tmp.sprintf( "%f", normal.y() );
243         element.insertAttribute( "ny", tmp );
244
245     tmp.sprintf( "%f", normal.z() );
246         element.insertAttribute( "nz", tmp );
247
248         importer.pushElement( element );
249
250         for ( unsigned int i = 0; i < w.numpoints; ++i )
251         {
252                 DynamicElement c( "vertex" );
253
254                 tmp.sprintf( "%f", w.points[i].vertex.x() );
255                 c.insertAttribute( "x", tmp );
256
257                 tmp.sprintf( "%f", w.points[i].vertex.y() );
258                 c.insertAttribute( "y", tmp );
259
260                 tmp.sprintf( "%f", w.points[i].vertex.z() );
261                 c.insertAttribute( "z", tmp );
262
263                 tmp.sprintf( "%f", w.points[i].texcoord.x() );
264                 c.insertAttribute( "s", tmp );
265
266                 tmp.sprintf( "%f", w.points[i].texcoord.y() );
267                 c.insertAttribute( "t", tmp );
268
269                 importer.pushElement( c );
270                 importer.popElement( c.name() );
271         }
272
273         importer.popElement( element.name() );
274 }
275
276 class FaceXMLExporter
277 {
278 const Face& m_face;
279 public:
280 FaceXMLExporter( const Face& face ) : m_face( face ){
281 }
282 void exportXML( XMLImporter& importer ){
283         bool bAlternateTexdef = ( Face::m_type == eBrushTypeQuake3BP || Face::m_type == eBrushTypeDoom3 || Face::m_type == eBrushTypeQuake4 );
284
285         // write shader
286         {
287                 StaticElement element( "shader" );
288                 importer.pushElement( element );
289                 importer << m_face.getShader().getShader();
290                 importer.popElement( element.name() );
291         }
292
293         FacePolygon_exportXML( m_face.getWinding(), m_face.getPlane().plane3().normal(), importer );
294         FacePlane_exportXML( m_face.getPlane(), importer );
295
296         if ( !bAlternateTexdef ) {
297                 FaceTexdef_exportXML( m_face.getTexdef(), importer );
298         }
299         else
300         {
301                 FaceTexdef_BP_exportXML( m_face.getTexdef(), importer );
302         }
303
304         FaceShader_ContentsFlagsValue_exportXML( m_face.getShader(), importer );
305 }
306 };
307
308
309 class BrushXMLImporter : public XMLImporter
310 {
311 class xml_state_t
312 {
313 public:
314 enum EState
315 {
316         eDefault,
317         eBrush,
318         eFace,
319 };
320
321 private:
322 EState m_state;
323
324 public:
325 xml_state_t( EState state )
326         : m_state( state ){
327 }
328 EState state() const {
329         return m_state;
330 }
331 };
332
333 std::vector<xml_state_t> m_xml_state;
334 char m_faceImporter[sizeof( FaceXMLImporter )];
335 Brush& m_brush;
336
337 FaceXMLImporter& faceImporter(){
338         return *reinterpret_cast<FaceXMLImporter*>( m_faceImporter );
339 }
340
341 public:
342 BrushXMLImporter( Brush& brush ) : m_brush( brush ){
343         m_xml_state.push_back( xml_state_t::eDefault );
344 }
345 void pushElement( const XMLElement& element ){
346         switch ( m_xml_state.back().state() )
347         {
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 );
351                 break;
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();
359                 break;
360         case xml_state_t::eFace:
361                 m_xml_state.push_back( xml_state_t::eFace );
362                 faceImporter().pushElement( element );
363                 break;
364         }
365 }
366 void popElement( const char* name ){
367         ASSERT_MESSAGE( !m_xml_state.empty(), "parse error" );
368         m_xml_state.pop_back();
369
370         switch ( m_xml_state.back().state() )
371         {
372         case xml_state_t::eDefault:
373                 break;
374         case xml_state_t::eBrush:
375                 destructor( faceImporter() );
376                 break;
377         case xml_state_t::eFace:
378                 faceImporter().popElement( name );
379                 break;
380         }
381 }
382 std::size_t write( const char* data, std::size_t length ){
383         switch ( m_xml_state.back().state() )
384         {
385         case xml_state_t::eFace:
386                 return faceImporter().write( data, length );
387                 break;
388         default:
389                 break;
390         }
391         return length;
392 }
393 };
394
395 class BrushXMLExporter : public XMLExporter
396 {
397 const Brush& m_brush;
398
399 public:
400 BrushXMLExporter( const Brush& brush ) : m_brush( brush ){
401 }
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" );
405
406         const StaticElement brushElement( "brush" );
407         importer.pushElement( brushElement );
408
409         for ( Brush::const_iterator i = m_brush.begin(); i != m_brush.end(); ++i )
410         {
411                 if ( ( *i )->contributes() ) {
412                         const StaticElement element( "plane" );
413                         importer.pushElement( element );
414                         FaceXMLExporter( *( *i ) ).exportXML( importer );
415                         importer.popElement( element.name() );
416                 }
417         }
418
419         importer.popElement( brushElement.name() );
420 }
421 };
422
423
424 #endif