netradiant-custom/contrib/bobtoolz/DEntity.cpp
Garux dea7800d3c bobToolz: load all group entities, was hardcoded list of them
fix crash in DEntity::SelectBrushes after DEntity::LoadSelectedBrushes (which may load brushes from other entities)
generalize loading options; fix ignored loadPatches option; use only visible brushes in Find Duplicates
2023-09-04 06:38:59 +06:00

578 lines
14 KiB
C++

/*
BobToolz plugin for GtkRadiant
Copyright (C) 2001 Gordon Biggans
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// DEntity.cpp: implementation of the DEntity class.
//
//////////////////////////////////////////////////////////////////////
#include "DEntity.h"
#include <list>
#include <utility>
#include "str.h"
#include "DPoint.h"
#include "DPlane.h"
#include "DBrush.h"
#include "DEPair.h"
#include "DPatch.h"
#include "dialogs/dialogs-gtk.h"
#include "misc.h"
#include "CPortals.h"
#include "iundo.h"
#include "ientity.h"
#include "ieclass.h"
#include "generic/referencecounted.h"
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include "scenelib.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
DEntity::DEntity( const char *classname, int ID ){
SetClassname( classname );
m_nID = ID;
QER_Entity = NULL;
}
DEntity::~DEntity(){
ClearPatches();
ClearBrushes();
ClearEPairs();
}
//////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////
void DEntity::ClearBrushes(){
for ( DBrush *brush : brushList )
{
delete brush;
}
brushList.clear();
}
void DEntity::ClearPatches(){
for ( DPatch *patch : patchList )
{
delete patch;
}
patchList.clear();
}
DPatch* DEntity::NewPatch(){
return patchList.emplace_back( new DPatch );
}
DBrush* DEntity::NewBrush(){
return brushList.emplace_back( new DBrush );
}
char* getNextBracket( char* s ){
char* p = s;
while ( *p )
{
p++;
if ( *p == '(' ) {
break;
}
}
return p;
}
bool DEntity::LoadFromPrt( char *filename ){
CPortals portals;
strcpy( portals.fn, filename );
portals.Load();
if ( portals.node_count == 0 ) {
return false;
}
ClearBrushes();
ClearEPairs();
bool build = false;
for ( unsigned int i = 0; i < portals.node_count; i++ )
{
build = false;
DBrush* brush = NewBrush();
for ( unsigned int j = 0; j < portals.node[i].portal_count; j++ )
{
for ( unsigned int k = 0; k < portals.node[i].portal[j].point_count - 2; k++ )
{
vec3_t v1, v2, normal, n;
VectorSubtract( portals.node[i].portal[j].point[k + 2].p, portals.node[i].portal[j].point[k + 1].p, v1 );
VectorSubtract( portals.node[i].portal[j].point[k].p, portals.node[i].portal[j].point[k + 1].p, v2 );
CrossProduct( v1, v2, n );
VectorNormalize( n, v2 );
if ( k == 0 ) {
VectorCopy( v2, normal );
}
else
{
VectorSubtract( v2, normal, v1 );
if ( VectorLength( v1 ) > 0.01 ) {
build = true;
break;
}
}
}
if ( !build ) {
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 );
}
else{
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 );
}
}
if ( build ) {
brush->BuildInRadiant( false, NULL );
}
}
return true;
}
template<typename Functor>
class BrushSelectedVisitor : public SelectionSystem::Visitor
{
const Functor& m_functor;
public:
BrushSelectedVisitor( const Functor& functor ) : m_functor( functor ){
}
void visit( scene::Instance& instance ) const {
if ( Node_isBrush( instance.path().top() ) ) {
m_functor( instance );
}
}
};
template<typename Functor>
inline const Functor& Scene_forEachSelectedBrush( const Functor& functor ){
GlobalSelectionSystem().foreachSelected( BrushSelectedVisitor<Functor>( functor ) );
return functor;
}
void DEntity_loadBrush( DEntity& entity, scene::Instance& brush ){
entity.NewBrush()->LoadFromBrush( brush, true );
}
typedef ReferenceCaller1<DEntity, scene::Instance&, DEntity_loadBrush> DEntityLoadBrushCaller;
void DEntity::LoadSelectedBrushes(){
ClearBrushes();
ClearEPairs();
Scene_forEachSelectedBrush( DEntityLoadBrushCaller( *this ) );
}
template<typename Functor>
class PatchSelectedVisitor : public SelectionSystem::Visitor
{
const Functor& m_functor;
public:
PatchSelectedVisitor( const Functor& functor ) : m_functor( functor ){
}
void visit( scene::Instance& instance ) const {
if ( Node_isPatch( instance.path().top() ) ) {
m_functor( instance );
}
}
};
template<typename Functor>
inline const Functor& Scene_forEachSelectedPatch( const Functor& functor ){
GlobalSelectionSystem().foreachSelected( PatchSelectedVisitor<Functor>( functor ) );
return functor;
}
void DEntity_loadPatch( DEntity& entity, scene::Instance& patch ){
DPatch* loadPatch = entity.NewPatch();
loadPatch->LoadFromPatch( patch );
}
typedef ReferenceCaller1<DEntity, scene::Instance&, DEntity_loadPatch> DEntityLoadPatchCaller;
void DEntity::LoadSelectedPatches(){
ClearPatches();
ClearEPairs();
Scene_forEachSelectedPatch( DEntityLoadPatchCaller( *this ) );
}
bool* DEntity::BuildIntersectList(){
if ( brushList.empty() ) {
return nullptr;
}
bool* pbIntList = new bool[brushList.size()] (); // () zero initialize
for ( size_t i = 0; i < brushList.size(); ++i )
{
for ( size_t j = i + 1; j < brushList.size(); ++j )
{
if ( brushList[i]->IntersectsWith( brushList[j] ) ) {
pbIntList[i] = true;
pbIntList[j] = true;
}
}
}
return pbIntList;
}
bool* DEntity::BuildDuplicateList(){
if ( brushList.empty() ) {
return nullptr;
}
bool* pbDupList = new bool[brushList.size()] (); // () zero initialize
for ( size_t i = 0; i < brushList.size(); ++i )
{
for ( size_t j = i + 1; j < brushList.size(); ++j )
{
if ( brushList[i]->operator==( brushList[j] ) ) {
pbDupList[i] = true;
pbDupList[j] = true;
}
}
}
return pbDupList;
}
void DEntity::SelectBrushes( bool *selectList ){
if ( selectList == NULL ) {
return;
}
GlobalSelectionSystem().setSelectedAll( false );
for ( size_t i = 0; i < brushList.size(); ++i )
{
if ( selectList[i] ) {
brushList[i]->selectInRadiant();
}
}
}
void select_primitive( scene::Node *primitive, scene::Node *entity ){
scene::Path path( NodeReference( GlobalSceneGraph().root() ) );
path.push( NodeReference( *entity ) );
path.push( NodeReference( *primitive ) );
Instance_getSelectable( *GlobalSceneGraph().find( path ) )->setSelected( true );
}
bool DEntity::LoadFromEntity( scene::Node& ent, const LoadOptions options ) {
ClearPatches();
ClearBrushes();
ClearEPairs();
QER_Entity = &ent;
LoadEPairList( Node_getEntity( ent ) );
if ( !node_is_group( ent ) ) {
return false;
}
if ( Node_getTraversable( ent ) ) {
class load_brushes_t : public scene::Traversable::Walker
{
DEntity* m_entity;
const LoadOptions m_options;
public:
load_brushes_t( DEntity* entity, const LoadOptions options )
: m_entity( entity ), m_options( options ){
}
bool pre( scene::Node& node ) const {
if( !( m_options.loadVisibleOnly && !node.visible() ) ){
scene::Path path( NodeReference( GlobalSceneGraph().root() ) );
path.push( NodeReference( *m_entity->QER_Entity ) );
path.push( NodeReference( node ) );
scene::Instance* instance = GlobalSceneGraph().find( path );
ASSERT_MESSAGE( instance != 0, "" );
if( !( m_options.loadSelectedOnly && Instance_isSelected( *instance ) ) ){
if ( Node_isPatch( node ) ) {
if( m_options.loadPatches )
m_entity->NewPatch()->LoadFromPatch( *instance );
}
else if ( Node_isBrush( node ) ) {
m_entity->NewBrush()->LoadFromBrush( *instance, true );
if( !m_options.loadDetail && m_entity->brushList.back()->IsDetail() ){
delete m_entity->brushList.back();
m_entity->brushList.pop_back();
}
}
}
}
return false;
}
} load_brushes( this, options );
Node_getTraversable( ent )->traverse( load_brushes );
}
return true;
}
void DEntity::RemoveNonCheckBrushes( std::list<Str>* exclusionList ){
brushList.erase( std::remove_if( brushList.begin(), brushList.end(), [&]( DBrush *brush ){
if ( std::any_of( exclusionList->cbegin(), exclusionList->cend(), [brush]( const Str& tex ){ return brush->HasTexture( tex.GetBuffer() ); } ) ) {
delete brush;
return true;
}
return false;
} ), brushList.end() );
}
void DEntity::ResetChecks( std::list<Str>* exclusionList ){
for ( DBrush *brush : brushList )
{
brush->ResetChecks( exclusionList );
}
}
int DEntity::FixBrushes(){
int count = 0;
for ( DBrush *brush : brushList )
{
count += brush->RemoveRedundantPlanes();
}
return count;
}
void DEntity::BuildInRadiant( bool allowDestruction ){
bool makeEntity = strcmp( m_Classname, "worldspawn" ) ? true : false;
if ( makeEntity ) {
NodeSmartReference node( GlobalEntityCreator().createEntity( GlobalEntityClassManager().findOrInsert( m_Classname.GetBuffer(), !brushList.empty() || !patchList.empty() ) ) );
for ( const DEPair *epair : epairList )
{
Node_getEntity( node )->setKeyValue( epair->key, epair->value );
}
Node_getTraversable( GlobalSceneGraph().root() )->insert( node );
for ( DBrush *brush : brushList )
brush->BuildInRadiant( allowDestruction, NULL, node.get_pointer() );
for ( DPatch *patch : patchList )
patch->BuildInRadiant( node.get_pointer() );
QER_Entity = node.get_pointer();
}
else
{
for ( DBrush *brush : brushList )
brush->BuildInRadiant( allowDestruction, NULL );
for ( DPatch *patch : patchList )
patch->BuildInRadiant();
}
}
void DEntity::SetClassname( const char *classname ) {
m_Classname = classname;
}
void DEntity::SaveToFile( FILE *pFile ){
fprintf( pFile, "{\n" );
fprintf( pFile, "\"classname\" \"%s\"\n", (const char *)m_Classname );
for ( const DEPair *ep : epairList )
{
fprintf( pFile, "\"%s\" \"%s\"\n", (const char *)ep->key, (const char *)ep->value );
}
for ( DBrush *brush : brushList )
{
brush->SaveToFile( pFile );
}
fprintf( pFile, "}\n" );
}
void DEntity::ClearEPairs(){
for ( DEPair *epair : epairList )
{
delete epair;
}
epairList.clear();
}
void DEntity::AddEPair( const char *key, const char *value ) {
DEPair* newEPair;
newEPair = FindEPairByKey( key );
if ( !newEPair ) {
newEPair = new DEPair;
newEPair->Build( key, value );
epairList.push_back( newEPair );
}
else {
newEPair->Build( key, value );
}
}
void DEntity::LoadEPairList( Entity *epl ){
class load_epairs_t : public Entity::Visitor
{
DEntity* m_entity;
public:
load_epairs_t( DEntity* entity )
: m_entity( entity ){
}
void visit( const char* key, const char* value ){
if ( strcmp( key, "classname" ) == 0 ) {
m_entity->SetClassname( value );
}
else{
m_entity->AddEPair( key, value );
}
}
} load_epairs( this );
epl->forEachKeyValue( load_epairs );
}
bool DEntity::ResetTextures( const char* textureName, float fScale[2], float fShift[2], int rotation, const char* newTextureName,
bool bResetTextureName, bool bResetScale[2], bool bResetShift[2], bool bResetRotation, bool rebuild ){
bool reset = false;
for ( DBrush *brush : brushList )
{
const bool tmp = brush->ResetTextures( textureName, fScale, fShift, rotation, newTextureName,
bResetTextureName, bResetScale, bResetShift, bResetRotation );
if ( tmp ) {
reset = true;
if ( rebuild ) {
Node_getTraversable( *brush->QER_entity )->erase( *brush->QER_brush );
brush->BuildInRadiant( false, NULL, brush->QER_entity );
}
}
}
if ( bResetTextureName ) {
for ( DPatch *patch : patchList )
{
if ( patch->ResetTextures( textureName, newTextureName ) ) {
reset = true;
if ( rebuild ) {
Node_getTraversable( *patch->QER_entity )->erase( *patch->QER_brush );
patch->BuildInRadiant( patch->QER_entity );
}
}
}
}
return reset;
}
DEPair* DEntity::FindEPairByKey( const char* keyname ){
for ( DEPair *ep : epairList )
{
if ( !strcmp( ep->key, keyname ) ) {
return ep;
}
}
return NULL;
}
void DEntity::RemoveFromRadiant(){
Node_getTraversable( GlobalSceneGraph().root() )->erase( *QER_Entity );
QER_Entity = NULL;
}
void DEntity::SpawnString( const char* key, const char* defaultstring, const char** out ){
DEPair* pEP = FindEPairByKey( key );
if ( pEP ) {
*out = pEP->value;
}
else {
*out = defaultstring;
}
}
void DEntity::SpawnInt( const char* key, const char* defaultstring, int* out ){
DEPair* pEP = FindEPairByKey( key );
if ( pEP ) {
*out = atoi( pEP->value );
}
else {
*out = atoi( defaultstring );
}
}
void DEntity::SpawnFloat( const char* key, const char* defaultstring, float* out ){
DEPair* pEP = FindEPairByKey( key );
if ( pEP ) {
*out = static_cast<float>( atof( pEP->value ) );
}
else {
*out = static_cast<float>( atof( defaultstring ) );
}
}
void DEntity::SpawnVector( const char* key, const char* defaultstring, vec_t* out ){
DEPair* pEP = FindEPairByKey( key );
if ( pEP ) {
sscanf( pEP->value, "%f %f %f", &out[0], &out[1], &out[2] );
}
else {
sscanf( defaultstring, "%f %f %f", &out[0], &out[1], &out[2] );
}
}
int DEntity::GetBrushCount( void ) {
return static_cast<int>( brushList.size() );
}
DBrush* DEntity::FindBrushByPointer( scene::Node& brush ) {
for ( DBrush* pBrush : brushList ) {
if ( pBrush->QER_brush == &brush ) {
return pBrush;
}
}
return NULL;
}