ported bobtoolz
[xonotic/netradiant.git] / contrib / bobtoolz / DEntity.cpp
1 /*
2 BobToolz plugin for GtkRadiant
3 Copyright (C) 2001 Gordon Biggans
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 // DEntity.cpp: implementation of the DEntity class.
21 //
22 //////////////////////////////////////////////////////////////////////
23
24 #include "DEntity.h"
25
26 #ifdef WIN32
27 #pragma warning(disable : 4786)
28 #endif
29
30 #include <list>
31 #include "str.h"
32
33 #include "DPoint.h"
34 #include "DPlane.h"
35 #include "DBrush.h"
36 #include "DEPair.h"
37 #include "DPatch.h"
38
39 #include "dialogs/dialogs-gtk.h"
40 #include "misc.h"
41 #include "CPortals.h"
42
43 #include "iundo.h"
44 #include "ientity.h"
45 #include "ieclass.h"
46
47 #include "generic/referencecounted.h"
48
49 #include <vector>
50 #include <list>
51 #include <map>
52 #include <algorithm>
53
54 #include "scenelib.h"
55
56
57 const char* brushEntityList[] = {
58         "worldspawn",
59         "trigger_always",
60         "trigger_hurt",
61         "trigger_multiple",
62         "trigger_push",
63         "trigger_teleport",
64         "func_bobbing",
65         "func_button",
66         "func_door",
67         "func_group",
68         "func_pendulum",
69         "func_plat",
70         "func_rotating",
71         "func_static",
72         "func_timer",
73         "func_train",
74         0
75 };
76
77 //////////////////////////////////////////////////////////////////////
78 // Construction/Destruction
79 //////////////////////////////////////////////////////////////////////
80
81 DEntity::DEntity(const char *classname, int ID)
82 {
83         SetClassname(classname);
84         m_nID = ID;
85         QER_Entity = NULL;
86 }
87
88 DEntity::~DEntity()
89 {
90         ClearPatches();
91         ClearBrushes();
92         ClearEPairs();
93 }
94
95 //////////////////////////////////////////////////////////////////////
96 // Implementation
97 //////////////////////////////////////////////////////////////////////
98
99 void DEntity::ClearBrushes()
100 {
101         for(std::list<DBrush *>::const_iterator deadBrush=brushList.begin(); deadBrush!=brushList.end(); deadBrush++)
102         {
103                 delete *deadBrush;
104         }
105         brushList.clear();
106 }
107
108 void DEntity::ClearPatches()
109 {
110         for(std::list<DPatch *>::const_iterator deadPatch=patchList.begin(); deadPatch!=patchList.end(); deadPatch++)
111         {
112                 delete *deadPatch;
113         }
114         patchList.clear();
115 }
116
117 DPatch* DEntity::NewPatch()
118 {
119         DPatch* newPatch = new DPatch;
120
121         patchList.push_back(newPatch);
122
123         return newPatch;
124 }
125
126 DBrush* DEntity::NewBrush(int ID)
127 {
128         DBrush* newBrush = new DBrush(ID);
129
130         brushList.push_back(newBrush);
131
132         return newBrush;
133 }
134
135 char* getNextBracket(char* s)
136 {
137         char* p = s;
138         while(*p)
139         {
140                 p++;
141                 if(*p == '(')
142                         break;
143         }
144
145         return p;
146 }
147
148 bool DEntity::LoadFromPrt(char *filename)
149 {
150         CPortals portals;
151         strcpy(portals.fn, filename);
152         portals.Load();
153
154         if(portals.node_count == 0)
155                 return false;
156
157         ClearBrushes();
158         ClearEPairs();
159         
160   bool build = false;
161         for(unsigned int i = 0; i < portals.node_count; i++)
162         {
163     build = false;
164                 DBrush* brush = NewBrush();
165
166                 for(unsigned int j = 0; j < portals.node[i].portal_count; j++)
167                 {
168       for(unsigned int k = 0; k < portals.node[i].portal[j].point_count-2; k++) 
169       {
170               vec3_t v1, v2, normal, n;
171               VectorSubtract(portals.node[i].portal[j].point[k+2].p, portals.node[i].portal[j].point[k+1].p, v1);
172               VectorSubtract(portals.node[i].portal[j].point[k].p, portals.node[i].portal[j].point[k+1].p, v2);
173               CrossProduct(v1, v2, n);
174         VectorNormalize(n, v2);
175
176         if(k == 0) 
177         {
178           VectorCopy(v2, normal);
179         }
180         else
181         {
182           VectorSubtract(v2, normal, v1);
183           if(VectorLength(v1) > 0.01)
184           {
185             build = true;
186             break;
187           }
188         }
189       }
190
191       if(!build)
192                           brush->AddFace(portals.node[i].portal[j].point[2].p, portals.node[i].portal[j].point[1].p, portals.node[i].portal[j].point[0].p, "textures/common/caulk", false);
193       else
194                           brush->AddFace(portals.node[i].portal[j].point[0].p, portals.node[i].portal[j].point[1].p, portals.node[i].portal[j].point[2].p, "textures/common/caulk", false);
195                 }
196     if(build)
197       brush->BuildInRadiant(false, NULL);
198         }
199
200         return true;
201 }
202
203 DPlane* DEntity::AddFaceToBrush(vec3_t va, vec3_t vb, vec3_t vc, _QERFaceData* faceData, int ID)
204 {
205         DBrush* buildBrush = GetBrushForID(ID);
206         return buildBrush->AddFace(va, vb, vc, faceData);
207         // slow, dont use much
208 }
209
210 DBrush* DEntity::GetBrushForID(int ID)
211 {
212         DBrush* buildBrush = NULL;
213
214         for(std::list<DBrush *>::const_iterator chkBrush=brushList.begin(); chkBrush!=brushList.end(); chkBrush++)
215         {
216                 if((*chkBrush)->m_nBrushID == ID)
217                 {
218                         buildBrush = (*chkBrush);
219                         break;
220                 }
221         }
222
223         if(!buildBrush)
224                 buildBrush = NewBrush(ID);
225
226         return buildBrush;
227 }
228
229 template<typename Functor>
230 class BrushSelectedVisitor : public SelectionSystem::Visitor
231 {
232   const Functor& m_functor;
233 public:
234   BrushSelectedVisitor(const Functor& functor) : m_functor(functor)
235   {
236   }
237   void visit(scene::Instance& instance) const
238   {
239     if(Node_isBrush(instance.path().top()))
240     {
241       m_functor(instance);
242     }
243   }
244 };
245
246 template<typename Functor>
247 inline const Functor& Scene_forEachSelectedBrush(const Functor& functor)
248 {
249   GlobalSelectionSystem().foreachSelected(BrushSelectedVisitor<Functor>(functor));
250   return functor;
251 }
252
253 void DEntity_loadBrush(DEntity& entity, scene::Instance& brush)
254 {
255   DBrush* loadBrush = entity.NewBrush(entity.brushList.size());
256         loadBrush->LoadFromBrush(brush, true);
257 }
258 typedef ReferenceCaller1<DEntity, scene::Instance&, DEntity_loadBrush> DEntityLoadBrushCaller;
259
260 void DEntity::LoadSelectedBrushes()
261 {
262         ClearBrushes();
263         ClearEPairs();
264
265   Scene_forEachSelectedBrush(DEntityLoadBrushCaller(*this));
266 }
267
268 template<typename Functor>
269 class PatchSelectedVisitor : public SelectionSystem::Visitor
270 {
271   const Functor& m_functor;
272 public:
273   PatchSelectedVisitor(const Functor& functor) : m_functor(functor)
274   {
275   }
276   void visit(scene::Instance& instance) const
277   {
278     if(Node_isPatch(instance.path().top()))
279     {
280       m_functor(instance);
281     }
282   }
283 };
284
285 template<typename Functor>
286 inline const Functor& Scene_forEachSelectedPatch(const Functor& functor)
287 {
288   GlobalSelectionSystem().foreachSelected(PatchSelectedVisitor<Functor>(functor));
289   return functor;
290 }
291
292 void DEntity_loadPatch(DEntity& entity, scene::Instance& patch)
293 {
294   DPatch* loadPatch = entity.NewPatch();
295         loadPatch->LoadFromPatch(patch);
296 }
297 typedef ReferenceCaller1<DEntity, scene::Instance&, DEntity_loadPatch> DEntityLoadPatchCaller;
298
299 void DEntity::LoadSelectedPatches()
300 {
301         ClearPatches();
302         ClearEPairs();
303
304   Scene_forEachSelectedPatch(DEntityLoadPatchCaller(*this));
305 }
306
307 bool* DEntity::BuildIntersectList()
308 {
309         int max = GetIDMax();
310         if(max == 0)
311                 return NULL;
312
313         bool* pbIntList = new bool[max];
314         memset(pbIntList, 0, sizeof(bool)*(max));
315
316         for(std::list<DBrush *>::const_iterator pB1=brushList.begin(); pB1!=brushList.end(); pB1++)
317         {
318                 std::list<DBrush *>::const_iterator pB2=pB1;
319                 for(pB2++; pB2!=brushList.end(); pB2++)
320                 {
321                         if((*pB1)->IntersectsWith((*pB2)))
322                         {
323                                 pbIntList[(*pB1)->m_nBrushID] = true;
324                                 pbIntList[(*pB2)->m_nBrushID] = true;
325                         }
326                 }
327         }
328
329         return pbIntList;
330 }
331
332 bool* DEntity::BuildDuplicateList()
333 {
334         int max = GetIDMax();
335         if(max == 0)
336                 return NULL;
337
338         bool* pbDupList = new bool[max];
339         memset(pbDupList, 0, sizeof(bool)*(max));
340
341         for(std::list<DBrush *>::const_iterator pB1=brushList.begin(); pB1!=brushList.end(); pB1++)
342         {
343                 std::list<DBrush *>::const_iterator pB2=pB1;
344                 for(pB2++; pB2!=brushList.end(); pB2++)
345                 {
346                         if(**pB1 == *pB2)
347                         {
348                                 pbDupList[(*pB1)->m_nBrushID] = true;
349                                 pbDupList[(*pB2)->m_nBrushID] = true;
350                         }
351                 }
352         }
353
354         return pbDupList;
355 }
356
357 void DEntity::SelectBrushes(bool *selectList)
358 {
359         if(selectList == NULL)
360                 return;
361
362   GlobalSelectionSystem().setSelectedAll(false);
363
364   scene::Path path(NodeReference(GlobalSceneGraph().root()));
365   path.push(NodeReference(*QER_Entity));
366
367         for(std::list<DBrush *>::const_iterator pBrush=brushList.begin(); pBrush!=brushList.end(); pBrush++)
368         {
369                 if(selectList[(*pBrush)->m_nBrushID])
370     {
371       path.push(NodeReference(*(*pBrush)->QER_brush));
372       Instance_getSelectable(*GlobalSceneGraph().find(path))->setSelected(true);
373       path.pop();
374     }
375         }
376 }
377
378 bool DEntity::LoadFromEntity(scene::Node& ent, bool bLoadPatches) {
379         ClearPatches();
380         ClearBrushes();
381         ClearEPairs();
382
383         QER_Entity = &ent;
384
385         LoadEPairList(Node_getEntity(ent));
386
387         bool keep = false;
388         int i;
389         for(i = 0; brushEntityList[i]; i++)
390         {
391                 if(string_equal_nocase(brushEntityList[i], m_Classname))
392                 {
393                         keep = true;
394                         break;
395                 }
396         }
397
398         if(!keep)
399                 return false;
400
401   if(Node_getTraversable(ent))
402   {
403     class load_brushes_t : public scene::Traversable::Walker
404     {
405       DEntity* m_entity;
406       mutable int m_count;
407     public:
408       load_brushes_t(DEntity* entity)
409         : m_entity(entity), m_count(0)
410       {
411       }
412       bool pre(scene::Node& node) const
413       {
414         scene::Path path(NodeReference(GlobalSceneGraph().root()));
415         path.push(NodeReference(*m_entity->QER_Entity));
416         path.push(NodeReference(node));
417         scene::Instance* instance = GlobalSceneGraph().find(path);
418         ASSERT_MESSAGE(instance != 0, "");
419
420         if(Node_isPatch(node))
421         {
422           DPatch* loadPatch = m_entity->NewPatch();
423                                   loadPatch->LoadFromPatch(*instance);
424         }
425         else if(Node_isBrush(node))
426         {
427                             DBrush* loadBrush = m_entity->NewBrush(m_count++);
428                             loadBrush->LoadFromBrush(*instance, true);
429         }
430         return false;
431       }
432     } load_brushes(this);
433
434     Node_getTraversable(ent)->traverse(load_brushes);
435   }
436
437         return true;
438 }
439
440 void DEntity::RemoveNonCheckBrushes(std::list<Str>* exclusionList, bool useDetail)
441 {
442         std::list<DBrush *>::iterator chkBrush=brushList.begin();
443
444         while( chkBrush!=brushList.end() )
445         {
446                 if(!useDetail)
447                 {
448                         if((*chkBrush)->IsDetail())
449                         {
450                                 delete *chkBrush;
451                                 chkBrush = brushList.erase(chkBrush);
452                                 continue;
453                         }
454                 }
455
456                 std::list<Str>::iterator eTexture;
457
458                 for( eTexture=exclusionList->begin(); eTexture!=exclusionList->end(); eTexture++ )
459                 {
460                         if((*chkBrush)->HasTexture((*eTexture).GetBuffer()))
461                         {
462                                 delete *chkBrush;
463                                 chkBrush = brushList.erase(chkBrush);
464                                 break;
465                         }
466                 }
467
468                 if( eTexture == exclusionList->end() )
469                         chkBrush++;
470         }
471 }
472
473 void DEntity::ResetChecks(std::list<Str>* exclusionList)
474 {
475         for(std::list<DBrush *>::const_iterator resetBrush=brushList.begin(); resetBrush!=brushList.end(); resetBrush++)
476         {
477                 (*resetBrush)->ResetChecks(exclusionList);
478         }
479 }
480
481 int DEntity::FixBrushes()
482 {
483         int count = 0;
484
485         for(std::list<DBrush *>::const_iterator fixBrush=brushList.begin(); fixBrush!=brushList.end(); fixBrush++)
486         {
487     count += (*fixBrush)->RemoveRedundantPlanes();
488         }
489
490         return count;
491 }
492
493 void DEntity::BuildInRadiant(bool allowDestruction)
494 {
495         bool makeEntity = strcmp(m_Classname, "worldspawn") ? true : false;
496
497         if(makeEntity)
498         {
499     NodeSmartReference node(GlobalEntityCreator().createEntity(GlobalEntityClassManager().findOrInsert(m_Classname.GetBuffer(), !brushList.empty() || !patchList.empty())));
500
501                 for(std::list<DEPair* >::const_iterator buildEPair=epairList.begin(); buildEPair!=epairList.end(); buildEPair++)
502                 {
503       Node_getEntity(node)->setKeyValue((*buildEPair)->key, (*buildEPair)->value);
504                 }
505
506                 Node_getTraversable(GlobalSceneGraph().root())->insert(node);
507
508                 for(std::list<DBrush *>::const_iterator buildBrush=brushList.begin(); buildBrush!=brushList.end(); buildBrush++)
509                         (*buildBrush)->BuildInRadiant(allowDestruction, NULL, node.get_pointer());
510
511                 for(std::list<DPatch *>::const_iterator buildPatch=patchList.begin(); buildPatch!=patchList.end(); buildPatch++)
512                         (*buildPatch)->BuildInRadiant(node.get_pointer());
513
514                 QER_Entity = node.get_pointer();
515         }
516         else
517         {
518                 for(std::list<DBrush *>::const_iterator buildBrush=brushList.begin(); buildBrush!=brushList.end(); buildBrush++)
519                         (*buildBrush)->BuildInRadiant(allowDestruction, NULL);
520
521                 for(std::list<DPatch *>::const_iterator buildPatch=patchList.begin(); buildPatch!=patchList.end(); buildPatch++)
522                         (*buildPatch)->BuildInRadiant();
523         }
524 }
525
526
527
528 int DEntity::GetIDMax( void ) {
529         int max = -1;
530         for(std::list<DBrush *>::const_iterator cntBrush=brushList.begin(); cntBrush!=brushList.end(); cntBrush++) {
531                 if((*cntBrush)->m_nBrushID > max)
532                         max = (*cntBrush)->m_nBrushID;
533         }
534         return max+1;
535 }
536
537 void DEntity::SetClassname( const char *classname ) {
538         m_Classname = classname;
539 }
540
541 void DEntity::SaveToFile(FILE *pFile)
542 {
543         fprintf(pFile, "{\n");
544
545         fprintf(pFile, "\"classname\" \"%s\"\n", (const char *)m_Classname);
546
547         for(std::list<DEPair *>::const_iterator ep=epairList.begin(); ep!=epairList.end(); ep++)
548         {
549                 fprintf(pFile, "\"%s\" \"%s\"\n", (const char *)(*ep)->key, (const char *)(*ep)->value);
550         }
551
552         for(std::list<DBrush *>::const_iterator bp=brushList.begin(); bp!=brushList.end(); bp++)
553         {
554                 (*bp)->SaveToFile(pFile);
555         }
556
557         fprintf(pFile, "}\n");
558 }
559
560 void DEntity::ClearEPairs()
561 {
562         for(std::list<DEPair *>::const_iterator deadEPair=epairList.begin(); deadEPair!=epairList.end(); deadEPair++)
563         {
564                 delete (*deadEPair);
565         }
566         epairList.clear();
567 }
568
569 void DEntity::AddEPair(const char *key, const char *value) {    
570         DEPair* newEPair; 
571         newEPair = FindEPairByKey( key );
572         if(!newEPair) {
573                 newEPair = new DEPair;
574                 newEPair->Build(key, value);
575                 epairList.push_back(newEPair);
576         } else {
577                 newEPair->Build(key, value);
578         }
579 }
580
581 void DEntity::LoadEPairList(Entity *epl)
582 {
583   class load_epairs_t : public Entity::Visitor
584   {
585     DEntity* m_entity;
586   public:
587     load_epairs_t(DEntity* entity)
588       : m_entity(entity)
589     {
590     }
591     void visit(const char* key, const char* value)
592     {
593       if(strcmp(key, "classname") == 0)
594         m_entity->SetClassname(value);
595       else      
596                           m_entity->AddEPair(key, value);
597     }
598
599   } load_epairs(this);
600
601   epl->forEachKeyValue(load_epairs);
602 }
603
604 bool DEntity::ResetTextures(const char* textureName, float fScale[2],     float fShift[2],    int rotation, const char* newTextureName, 
605                             int bResetTextureName,    int bResetScale[2], int bResetShift[2], int bResetRotation, bool rebuild)
606 {
607         bool reset = false;
608
609         for(std::list<DBrush *>::const_iterator resetBrush=brushList.begin(); resetBrush!=brushList.end(); resetBrush++)
610         {
611                 bool tmp = (*resetBrush)->ResetTextures(textureName,        fScale,       fShift,       rotation, newTextureName, 
612                                             bResetTextureName,  bResetScale,  bResetShift,  bResetRotation);
613
614                 if(tmp)
615                 {
616                         reset = true;
617                         if(rebuild)
618                         {
619         Node_getTraversable(*(*resetBrush)->QER_entity)->erase(*(*resetBrush)->QER_brush);      
620         (*resetBrush)->BuildInRadiant(false, NULL, (*resetBrush)->QER_entity);
621                         }
622                 }
623         }
624
625   if(bResetTextureName)
626   {
627           for(std::list<DPatch *>::const_iterator resetPatch=patchList.begin(); resetPatch!=patchList.end(); resetPatch++)
628           {
629                   bool tmp = (*resetPatch)->ResetTextures(textureName, newTextureName);
630
631                   if(tmp)
632                   {
633                           reset = true;
634                           if(rebuild)
635                           {
636           Node_getTraversable(*(*resetPatch)->QER_entity)->erase(*(*resetPatch)->QER_brush);      
637           (*resetPatch)->BuildInRadiant((*resetPatch)->QER_entity);
638                           }
639                   }
640           }
641   }
642
643         return reset;
644 }
645
646 DEPair* DEntity::FindEPairByKey(const char* keyname)
647 {
648         for(std::list<DEPair *>::const_iterator ep=epairList.begin(); ep!=epairList.end(); ep++)
649         {
650                 char* c = (*ep)->key;
651                 if(!strcmp(c, keyname))
652                         return *ep;
653         }
654         return NULL;
655 }
656
657 void DEntity::RemoveFromRadiant()
658 {
659         Node_getTraversable(GlobalSceneGraph().root())->erase(*QER_Entity);
660
661         QER_Entity = NULL;
662 }
663
664 void DEntity::SpawnString(const char* key, const char* defaultstring, const char** out)
665 {
666         DEPair* pEP = FindEPairByKey(key);
667         if(pEP) {
668                 *out = pEP->value;
669         } else {
670                 *out = defaultstring;
671         }
672 }
673
674 void DEntity::SpawnInt(const char* key, const char* defaultstring, int* out)
675 {
676         DEPair* pEP = FindEPairByKey(key);
677         if(pEP) {
678                 *out = atoi(pEP->value);
679         } else {
680                 *out = atoi(defaultstring);
681         }
682 }
683
684 void DEntity::SpawnFloat(const char* key, const char* defaultstring, float* out)
685 {
686         DEPair* pEP = FindEPairByKey(key);
687         if(pEP) {
688                 *out = static_cast<float>(atof(pEP->value));
689         } else {
690                 *out = static_cast<float>(atof(defaultstring));
691         }
692 }
693
694 void DEntity::SpawnVector(const char* key, const char* defaultstring, vec_t* out)
695 {
696         DEPair* pEP = FindEPairByKey(key);
697         if(pEP) {
698                 sscanf(pEP->value, "%f %f %f", &out[0], &out[1], &out[2]);
699         } else {
700                 sscanf(defaultstring, "%f %f %f", &out[0], &out[1], &out[2]);
701         }
702 }
703
704 int DEntity::GetBrushCount( void ) {
705         return brushList.size();
706 }
707
708 DBrush* DEntity::FindBrushByPointer( scene::Node& brush ) {
709         for(std::list<DBrush *>::const_iterator listBrush = brushList.begin(); listBrush != brushList.end(); listBrush++) {
710                 DBrush* pBrush = (*listBrush);
711                 if(pBrush->QER_brush == &brush) {
712                         return pBrush;
713                 }
714         }
715         return NULL;
716 }