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