/* 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 */ #include "DTrainDrawer.h" #include #include #include "DPoint.h" #include "DPlane.h" #include "DBrush.h" #include "DEPair.h" #include "DPatch.h" #include "DEntity.h" #include "misc.h" #include "funchandlers.h" #include "iglrender.h" #include "ientity.h" #include "math/matrix.h" #include "dialogs/dialogs-gtk.h" DTrainDrawer::DTrainDrawer() { m_bDisplay = false; BuildPaths(); constructShaders(); GlobalShaderCache().attachRenderable( *this ); } DTrainDrawer::~DTrainDrawer() { GlobalShaderCache().detachRenderable( *this ); destroyShaders(); ClearPoints(); ClearSplines(); } void DTrainDrawer::ClearSplines() { for ( auto *deadSpline : m_splineList ) { deadSpline->m_pointList.clear(); deadSpline->m_vertexList.clear(); delete deadSpline; } m_splineList.clear(); } void DTrainDrawer::ClearPoints() { for ( auto *deadPoint : m_pointList ) { delete deadPoint; } m_pointList.clear(); } void CalculateSpline_r( vec3_t* v, int count, vec3_t out, float tension ) { vec3_t dist; if ( count < 2 ) { return; } if ( count == 2 ) { VectorSubtract( v[1], v[0], dist ); VectorMA( v[0], tension, dist, out ); return; } vec3_t* v2 = new vec3_t[count - 1]; for ( int i = 0; i < count - 1; ++i ) { VectorSubtract( v[i + 1], v[i], dist ); VectorMA( v[i], tension, dist, v2[i] ); } CalculateSpline_r( v2, count - 1, out, tension ); delete[] v2; } void DTrainDrawer::render( RenderStateFlags state ) const { for ( const auto *sp : m_splineList ) { gl().glBegin( GL_LINE_STRIP ); for ( const auto& v : sp->m_vertexList ) { gl().glVertex3fv( v._pnt ); } gl().glEnd(); } } const char* DTrainDrawer_state_wireframe = "$bobtoolz/traindrawer/wireframe"; const char* DTrainDrawer_state_solid = "$bobtoolz/traindrawer/solid"; void DTrainDrawer::constructShaders(){ OpenGLState state; GlobalOpenGLStateLibrary().getDefaultState( state ); state.m_state = RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_BLEND; state.m_sort = OpenGLState::eSortOverlayFirst; state.m_linewidth = 1; state.m_colour[0] = 1; state.m_colour[1] = 0; state.m_colour[2] = 0; state.m_colour[3] = 1; state.m_linewidth = 1; GlobalOpenGLStateLibrary().insert( DTrainDrawer_state_wireframe, state ); state.m_colour[0] = 1; state.m_colour[1] = 1; state.m_colour[2] = 1; state.m_colour[3] = 1; state.m_linewidth = 2; GlobalOpenGLStateLibrary().insert( DTrainDrawer_state_solid, state ); m_shader_wireframe = GlobalShaderCache().capture( DTrainDrawer_state_wireframe ); m_shader_solid = GlobalShaderCache().capture( DTrainDrawer_state_solid ); } void DTrainDrawer::destroyShaders(){ GlobalOpenGLStateLibrary().erase( DTrainDrawer_state_wireframe ); GlobalOpenGLStateLibrary().erase( DTrainDrawer_state_solid ); GlobalShaderCache().release( DTrainDrawer_state_wireframe ); GlobalShaderCache().release( DTrainDrawer_state_solid ); } void DTrainDrawer::renderSolid( Renderer& renderer, const VolumeTest& volume ) const { if ( !m_bDisplay ) { return; } renderer.SetState( m_shader_wireframe, Renderer::eWireframeOnly ); renderer.SetState( m_shader_solid, Renderer::eFullMaterials ); renderer.addRenderable( *this, g_matrix4_identity ); } void DTrainDrawer::renderWireframe( Renderer& renderer, const VolumeTest& volume ) const { renderSolid( renderer, volume ); } void AddSplineControl( const char* control, splinePoint_t* pSP ) { controlPoint_t cp; strncpy( cp.strName, control, std::size( cp.strName ) - 1 ); cp.strName[ std::size( cp.strName ) - 1 ] = '\0'; pSP->m_pointList.push_front( cp ); } class EntityBuildPaths { mutable DEntity e; DTrainDrawer& drawer; public: EntityBuildPaths( DTrainDrawer& drawer ) : drawer( drawer ){ } void operator()( scene::Instance& instance ) const { e.ClearEPairs(); e.LoadEPairList( Node_getEntity( instance.path().top() ) ); const char* classname = e.m_Classname.c_str(); const char* target; const char* control; const char* targetname; vec3_t vOrigin; e.SpawnString( "targetname", nullptr, &targetname ); e.SpawnVector( "origin", "0 0 0", vOrigin ); if ( !strcmp( classname, "info_train_spline_main" ) ) { if ( !targetname ) { globalWarningStream() << "info_train_spline_main with no targetname"; return; } e.SpawnString( "target", nullptr, &target ); if ( !target ) { drawer.AddControlPoint( targetname, vOrigin ); } else { splinePoint_t* pSP = drawer.AddSplinePoint( targetname, target, vOrigin ); e.SpawnString( "control", nullptr, &control ); if ( control ) { AddSplineControl( control, pSP ); for ( int j = 2;; ++j ) { char buffer[32]; sprintf( buffer, "control%i", j ); e.SpawnString( buffer, nullptr, &control ); if ( !control ) { break; } AddSplineControl( control, pSP ); } } } } else if ( !strcmp( classname, "info_train_spline_control" ) ) { if ( !targetname ) { globalWarningStream() << "info_train_spline_control with no targetname"; return; } drawer.AddControlPoint( targetname, vOrigin ); } } }; void DTrainDrawer::BuildPaths() { Scene_forEachEntity( EntityBuildPaths( *this ) ); std::list::const_iterator sp; for ( auto *sp : m_splineList ) { controlPoint_t* pTarget = FindControlPoint( sp->strTarget ); if ( !pTarget ) { globalWarningStream() << "couldn't find target " << sp->strTarget; return; // continue; } sp->pTarget = pTarget; for ( auto& cp : sp->m_pointList ) { controlPoint_t* pControl = FindControlPoint( cp.strName ); if ( !pControl ) { globalWarningStream() << "couldn't find control " << cp.strName; return; } VectorCopy( pControl->vOrigin, cp.vOrigin ); } } m_bDisplay = true; for ( auto *sp : m_splineList ) { DPoint out; if ( !sp->pTarget ) { continue; } std::size_t count = sp->m_pointList.size() + 2; auto *v = new vec3_t[count]; VectorCopy( sp->point.vOrigin, v[0] ); int i = 1; for ( const auto& cp : std::ranges::reverse_view( sp->m_pointList ) ) { VectorCopy( cp.vOrigin, v[i] ); i++; } VectorCopy( sp->pTarget->vOrigin, v[i] ); for ( float tension = 0; tension <= 1; tension += 0.01f ) { CalculateSpline_r( v, static_cast( count ), out._pnt, tension ); sp->m_vertexList.push_front( out ); } delete[] v; VectorCopy( sp->pTarget->vOrigin, out._pnt ); sp->m_vertexList.push_front( out ); } SceneChangeNotify(); } void DTrainDrawer::AddControlPoint( const char* name, vec_t* origin ){ auto *pCP = new controlPoint_t; strncpy( pCP->strName, name, std::size( pCP->strName ) - 1 ); pCP->strName[ std::size( pCP->strName ) - 1 ] = '\0'; VectorCopy( origin, pCP->vOrigin ); m_pointList.push_back( pCP ); } splinePoint_t* DTrainDrawer::AddSplinePoint( const char* name, const char* target, vec_t* origin ){ auto *pSP = new splinePoint_t; strncpy( pSP->point.strName, name, std::size( pSP->point.strName ) - 1 ); pSP->point.strName[ std::size( pSP->point.strName ) - 1 ] = '\0'; strncpy( pSP->strTarget, target, std::size( pSP->strTarget ) - 1 ); pSP->strTarget[ std::size( pSP->strTarget ) - 1 ] = '\0'; VectorCopy( origin, pSP->point.vOrigin ); m_splineList.push_back( pSP ); return pSP; } controlPoint_t* DTrainDrawer::FindControlPoint( const char* name ){ for ( auto *cp : m_pointList ) { if ( string_equal( name, cp->strName ) ) { return cp; } } for ( auto *sp : m_splineList ) { if ( string_equal( name, sp->point.strName ) ) { return &sp->point; } } return nullptr; }