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