2013-04-11 11:04:16 +00:00
using System.Collections.Generic ;
2015-08-08 21:56:43 +00:00
using CodeImp.DoomBuilder.Config ;
2012-09-26 00:04:17 +00:00
using CodeImp.DoomBuilder.Map ;
using CodeImp.DoomBuilder.VisualModes ;
using CodeImp.DoomBuilder.GZBuilder.Geometry ;
using CodeImp.DoomBuilder.Geometry ;
2014-12-03 23:15:26 +00:00
namespace CodeImp.DoomBuilder.GZBuilder.Data
{
public static class LinksCollector
{
2015-08-08 21:56:43 +00:00
private class SpecialThings
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
public readonly Dictionary < int , List < Thing > > PatrolPoints ; // PatrolPoint tag, list of PatrolPoints
public readonly Dictionary < int , List < PathNode > > InterpolationPoints ; // InterpolationPoint tag, list of InterpolationPoints
public readonly List < Thing > ThingsWithGoal ;
public readonly List < Thing > Cameras ;
public readonly Dictionary < int , List < Thing > > ActorMovers ; // ActorMover target tag, list of ActorMovers
public readonly List < Thing > PathFollowers ;
public readonly Dictionary < int , List < Thing > > PolyobjectAnchors ; //angle, list of PolyobjectAnchors
public readonly Dictionary < int , List < Thing > > PolyobjectStartSpots ; //angle, list of PolyobjectStartSpots
public SpecialThings ( )
{
PatrolPoints = new Dictionary < int , List < Thing > > ( ) ;
InterpolationPoints = new Dictionary < int , List < PathNode > > ( ) ;
ThingsWithGoal = new List < Thing > ( ) ;
Cameras = new List < Thing > ( ) ;
ActorMovers = new Dictionary < int , List < Thing > > ( ) ;
PathFollowers = new List < Thing > ( ) ;
PolyobjectAnchors = new Dictionary < int , List < Thing > > ( ) ;
PolyobjectStartSpots = new Dictionary < int , List < Thing > > ( ) ;
}
}
private class PathNode
{
private readonly Thing thing ;
private readonly Vector3D position ;
private readonly Dictionary < int , PathNode > nextnodes ;
private readonly Dictionary < int , PathNode > prevnodes ;
public Thing Thing { get { return thing ; } }
public Dictionary < int , PathNode > NextNodes { get { return nextnodes ; } } // Thing index, PathNode
public Dictionary < int , PathNode > PreviousNodes { get { return prevnodes ; } } // Thing index, PathNode
public Vector3D Position { get { return position ; } }
public bool IsCurved ;
public PathNode ( Thing t , bool correctzheight )
{
thing = t ;
position = new Vector3D ( t . Position , ( correctzheight ? t . Position . z + GetCorrectHeight ( t ) : t . Position . z ) ) ;
nextnodes = new Dictionary < int , PathNode > ( ) ;
prevnodes = new Dictionary < int , PathNode > ( ) ;
}
2015-08-10 18:31:27 +00:00
internal void PropagateCurvedFlag ( )
{
if ( ! IsCurved ) return ;
foreach ( PathNode node in nextnodes . Values )
{
if ( node . IsCurved ) continue ;
node . IsCurved = true ;
node . PropagateCurvedFlag ( ) ;
}
foreach ( PathNode node in prevnodes . Values )
{
if ( node . IsCurved ) continue ;
node . IsCurved = true ;
node . PropagateCurvedFlag ( ) ;
}
}
2013-09-11 09:47:53 +00:00
}
2015-08-08 21:56:43 +00:00
public static List < Line3D > GetThingLinks ( IEnumerable < VisualThing > visualthings )
2014-12-03 23:15:26 +00:00
{
2013-09-11 09:47:53 +00:00
List < Thing > things = new List < Thing > ( ) ;
2015-08-08 21:56:43 +00:00
foreach ( VisualThing vt in visualthings ) things . Add ( vt . Thing ) ;
return GetThingLinks ( GetSpecialThings ( things , true ) , true ) ;
2013-09-11 09:47:53 +00:00
}
2015-06-24 21:21:19 +00:00
2015-08-08 21:56:43 +00:00
public static List < Line3D > GetThingLinks ( IEnumerable < Thing > things )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
return GetThingLinks ( GetSpecialThings ( things , false ) , false ) ;
2013-09-11 09:47:53 +00:00
}
2015-08-08 21:56:43 +00:00
private static SpecialThings GetSpecialThings ( IEnumerable < Thing > things , bool correctheight )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
SpecialThings result = new SpecialThings ( ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process oh so special things
foreach ( Thing t in things )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
ThingTypeInfo info = General . Map . Data . GetThingInfo ( t . Type ) ;
switch ( info . ClassName . ToLowerInvariant ( ) )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
case "patrolpoint" :
if ( t . Args [ 0 ] ! = 0 )
{
if ( ! result . PatrolPoints . ContainsKey ( t . Tag ) ) result . PatrolPoints . Add ( t . Tag , new List < Thing > ( ) ) ;
result . PatrolPoints [ t . Tag ] . Add ( t ) ;
}
break ;
case "$polyanchor" :
if ( ! result . PolyobjectAnchors . ContainsKey ( t . AngleDoom ) ) result . PolyobjectAnchors [ t . AngleDoom ] = new List < Thing > ( ) ;
result . PolyobjectAnchors [ t . AngleDoom ] . Add ( t ) ;
break ;
case "$polyspawn" :
case "$polyspawncrush" :
case "$polyspawnhurt" :
if ( ! result . PolyobjectStartSpots . ContainsKey ( t . AngleDoom ) ) result . PolyobjectStartSpots [ t . AngleDoom ] = new List < Thing > ( ) ;
result . PolyobjectStartSpots [ t . AngleDoom ] . Add ( t ) ;
break ;
case "movingcamera" :
if ( t . Args [ 0 ] ! = 0 | | t . Args [ 1 ] ! = 0 ) result . Cameras . Add ( t ) ;
break ;
case "pathfollower" :
if ( t . Args [ 0 ] ! = 0 | | t . Args [ 1 ] ! = 0 ) result . PathFollowers . Add ( t ) ;
break ;
case "actormover" :
if ( ( t . Args [ 0 ] ! = 0 | | t . Args [ 1 ] ! = 0 ) & & t . Args [ 3 ] ! = 0 )
{
if ( ! result . ActorMovers . ContainsKey ( t . Args [ 3 ] ) ) result . ActorMovers . Add ( t . Args [ 3 ] , new List < Thing > ( ) ) ;
result . ActorMovers [ t . Args [ 3 ] ] . Add ( t ) ;
}
break ;
2013-09-11 09:47:53 +00:00
}
2015-03-06 19:12:12 +00:00
2015-08-08 21:56:43 +00:00
// Process Thing_SetGoal action
if ( t . Action ! = 0
& & General . Map . Config . LinedefActions . ContainsKey ( t . Action )
& & General . Map . Config . LinedefActions [ t . Action ] . Id . ToLowerInvariant ( ) = = "thing_setgoal"
& & ( t . Args [ 0 ] = = 0 | | t . Args [ 0 ] = = t . Tag )
& & t . Args [ 1 ] ! = 0 )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
result . ThingsWithGoal . Add ( t ) ;
2013-09-11 09:47:53 +00:00
}
2015-08-08 21:56:43 +00:00
}
2015-03-06 19:12:12 +00:00
2015-08-08 21:56:43 +00:00
// We may need all InterpolationPoints...
foreach ( Thing t in General . Map . Map . Things )
{
ThingTypeInfo info = General . Map . Data . GetThingInfo ( t . Type ) ;
switch ( info . ClassName . ToLowerInvariant ( ) )
2015-03-06 19:12:12 +00:00
{
2015-08-08 21:56:43 +00:00
case "interpolationpoint" :
if ( ! result . InterpolationPoints . ContainsKey ( t . Tag ) ) result . InterpolationPoints . Add ( t . Tag , new List < PathNode > ( ) ) ;
result . InterpolationPoints [ t . Tag ] . Add ( new PathNode ( t , correctheight ) ) ;
break ;
2015-03-06 19:12:12 +00:00
}
2013-09-11 09:47:53 +00:00
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
return result ;
}
private static List < Line3D > GetThingLinks ( SpecialThings result , bool correctheight )
{
List < Line3D > lines = new List < Line3D > ( ) ;
Dictionary < int , List < Thing > > actormovertargets = new Dictionary < int , List < Thing > > ( ) ;
// Get ActorMover targets
if ( result . ActorMovers . Count > 0 )
2014-12-03 23:15:26 +00:00
{
foreach ( Thing t in General . Map . Map . Things )
{
2015-08-08 21:56:43 +00:00
if ( t . Tag = = 0 | | ! result . ActorMovers . ContainsKey ( t . Tag ) ) continue ;
if ( ! actormovertargets . ContainsKey ( t . Tag ) ) actormovertargets [ t . Tag ] = new List < Thing > ( ) ;
actormovertargets [ t . Tag ] . Add ( t ) ;
2012-09-26 00:04:17 +00:00
}
}
2014-02-21 14:42:12 +00:00
Vector3D start , end ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process patrol points
foreach ( KeyValuePair < int , List < Thing > > group in result . PatrolPoints )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
foreach ( Thing t in group . Value )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
if ( ! result . PatrolPoints . ContainsKey ( t . Args [ 0 ] ) ) continue ;
start = t . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
foreach ( Thing tt in result . PatrolPoints [ t . Args [ 0 ] ] )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
end = tt . Position ;
if ( correctheight ) end . z + = GetCorrectHeight ( tt ) ;
lines . Add ( new Line3D ( start , end ) ) ;
2012-09-26 00:04:17 +00:00
}
2013-09-11 09:47:53 +00:00
}
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process things with Thing_SetGoal
foreach ( Thing t in result . ThingsWithGoal )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
if ( ! result . PatrolPoints . ContainsKey ( t . Args [ 1 ] ) ) continue ;
start = t . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
foreach ( Thing tt in result . PatrolPoints [ t . Args [ 1 ] ] )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
end = tt . Position ;
if ( correctheight ) end . z + = GetCorrectHeight ( tt ) ;
lines . Add ( new Line3D ( start , end , General . Colors . Selection ) ) ;
}
}
// Process cameras [CAN USE INTERPOLATION]
foreach ( Thing t in result . Cameras )
{
int targettag = t . Args [ 0 ] + ( t . Args [ 1 ] < < 8 ) ;
if ( targettag = = 0 | | ! result . InterpolationPoints . ContainsKey ( targettag ) ) continue ; //no target / target desn't exist
bool interpolatepath = ( ( t . Args [ 2 ] & 1 ) ! = 1 ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
start = t . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
foreach ( PathNode node in result . InterpolationPoints [ targettag ] )
{
node . IsCurved = interpolatepath ;
lines . Add ( new Line3D ( start , node . Position , General . Colors . Selection ) ) ;
2013-09-11 09:47:53 +00:00
}
}
2015-08-08 21:56:43 +00:00
//process actor movers [CAN USE INTERPOLATION]
foreach ( List < Thing > things in result . ActorMovers . Values )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
foreach ( Thing t in things )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
int targettag = t . Args [ 0 ] + ( t . Args [ 1 ] < < 8 ) ;
// Add interpolation point targets
if ( targettag ! = 0 & & result . InterpolationPoints . ContainsKey ( targettag ) )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
bool interpolatepath = ( ( t . Args [ 2 ] & 1 ) ! = 1 ) ;
start = t . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
foreach ( PathNode node in result . InterpolationPoints [ targettag ] )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
node . IsCurved = interpolatepath ;
lines . Add ( new Line3D ( start , node . Position , General . Colors . Selection ) ) ;
2012-09-26 00:04:17 +00:00
}
}
2015-08-08 21:56:43 +00:00
// Add thing-to-move targets
if ( actormovertargets . ContainsKey ( t . Args [ 3 ] ) )
2014-12-03 23:15:26 +00:00
{
2012-09-26 00:04:17 +00:00
start = t . Position ;
2015-08-08 21:56:43 +00:00
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
foreach ( Thing tt in actormovertargets [ t . Args [ 3 ] ] )
2014-12-03 23:15:26 +00:00
{
2012-09-26 00:04:17 +00:00
end = tt . Position ;
2015-08-08 21:56:43 +00:00
if ( correctheight ) end . z + = GetCorrectHeight ( tt ) ;
lines . Add ( new Line3D ( start , end , General . Colors . Selection ) ) ;
2012-09-26 00:04:17 +00:00
}
}
2013-09-11 09:47:53 +00:00
}
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process path followers [CAN USE INTERPOLATION]
foreach ( Thing t in result . PathFollowers )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
int targettag = t . Args [ 0 ] + ( t . Args [ 1 ] < < 8 ) ;
if ( targettag = = 0 | | ! result . InterpolationPoints . ContainsKey ( targettag ) ) continue ; //no target / target desn't exist
bool interpolatepath = ( t . Args [ 2 ] & 1 ) ! = 1 ;
start = t . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( t ) ;
foreach ( PathNode node in result . InterpolationPoints [ targettag ] )
2014-12-03 23:15:26 +00:00
{
2015-08-08 21:56:43 +00:00
node . IsCurved = interpolatepath ;
lines . Add ( new Line3D ( start , node . Position , General . Colors . Selection ) ) ;
}
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process polyobjects
foreach ( KeyValuePair < int , List < Thing > > group in result . PolyobjectAnchors )
{
if ( ! result . PolyobjectStartSpots . ContainsKey ( group . Key ) ) continue ;
foreach ( Thing anchor in group . Value )
{
start = anchor . Position ;
if ( correctheight ) start . z + = GetCorrectHeight ( anchor ) ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
foreach ( Thing startspot in result . PolyobjectStartSpots [ group . Key ] )
{
end = startspot . Position ;
if ( correctheight ) end . z + = GetCorrectHeight ( startspot ) ;
lines . Add ( new Line3D ( start , end , General . Colors . Selection ) ) ;
2012-09-26 00:04:17 +00:00
}
2015-08-08 21:56:43 +00:00
}
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Process interpolation points [CAN BE INTERPOLATED]
// 1. Connect PathNodes
foreach ( KeyValuePair < int , List < PathNode > > group in result . InterpolationPoints )
{
foreach ( PathNode node in group . Value )
{
int targettag = node . Thing . Args [ 3 ] + ( node . Thing . Args [ 4 ] < < 8 ) ;
if ( targettag = = 0 | | ! result . InterpolationPoints . ContainsKey ( targettag ) ) continue ;
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
foreach ( PathNode targetnode in result . InterpolationPoints [ targettag ] )
{
// Connect both ways
2015-08-10 18:31:27 +00:00
if ( ! node . NextNodes . ContainsKey ( targetnode . Thing . Index ) ) node . NextNodes . Add ( targetnode . Thing . Index , targetnode ) ;
if ( ! targetnode . PreviousNodes . ContainsKey ( node . Thing . Index ) ) targetnode . PreviousNodes . Add ( node . Thing . Index , node ) ;
2012-09-26 00:04:17 +00:00
}
}
}
2015-08-10 18:31:27 +00:00
// 2. Propagate IsCurved flag
foreach ( KeyValuePair < int , List < PathNode > > group in result . InterpolationPoints )
{
foreach ( PathNode node in group . Value ) node . PropagateCurvedFlag ( ) ;
}
// 3. Make lines
2015-08-08 21:56:43 +00:00
HashSet < int > processedindices = new HashSet < int > ( ) ;
foreach ( KeyValuePair < int , List < PathNode > > group in result . InterpolationPoints )
2015-03-06 19:12:12 +00:00
{
2015-08-08 21:56:43 +00:00
foreach ( PathNode node in group . Value )
2015-03-06 19:12:12 +00:00
{
2015-08-08 21:56:43 +00:00
// Draw as a curve?
if ( node . IsCurved & & ! processedindices . Contains ( node . Thing . Index ) & & node . NextNodes . Count > 0 & & node . PreviousNodes . Count > 0 )
2015-03-06 19:12:12 +00:00
{
2015-08-08 21:56:43 +00:00
PathNode prev = General . GetByIndex ( node . PreviousNodes , 0 ) . Value ;
PathNode next = General . GetByIndex ( node . NextNodes , 0 ) . Value ;
if ( next . NextNodes . Count > 0 )
2015-03-06 19:12:12 +00:00
{
2015-08-08 21:56:43 +00:00
PathNode nextnext = General . GetByIndex ( next . NextNodes , 0 ) . Value ;
// Generate curve points
List < Vector3D > points = new List < Vector3D > ( 11 ) ;
for ( int i = 0 ; i < 11 ; i + + )
{
float u = i * 0.1f ;
points . Add ( new Vector3D (
SplineLerp ( u , prev . Position . x , node . Position . x , next . Position . x , nextnext . Position . x ) ,
SplineLerp ( u , prev . Position . y , node . Position . y , next . Position . y , nextnext . Position . y ) ,
( ! correctheight ? 0 : SplineLerp ( u , prev . Position . z , node . Position . z , next . Position . z , nextnext . Position . z ) )
) ) ;
}
// Add line segments
for ( int i = 1 ; i < 11 ; i + + )
{
lines . Add ( new Line3D ( points [ i - 1 ] , points [ i ] , i = = 10 ) ) ;
}
continue ;
2015-03-06 19:12:12 +00:00
}
}
2015-08-08 21:56:43 +00:00
// Draw regular lines
bool startnode = ( node . IsCurved & & node . PreviousNodes . Count = = 0 ) ; // When using curves, this node won't be used by camera (the last node won't be used as well), so draw them using different color
foreach ( PathNode targetnode in node . NextNodes . Values )
{
bool isskipped = ( startnode | | ( targetnode . IsCurved & & targetnode . NextNodes . Count = = 0 ) ) ;
lines . Add ( new Line3D ( node . Position , targetnode . Position , ( isskipped ? General . Colors . Highlight : General . Colors . InfoLine ) , ! isskipped ) ) ;
}
2015-03-06 19:12:12 +00:00
}
}
2013-09-11 09:47:53 +00:00
return lines ;
}
2012-09-26 00:04:17 +00:00
2015-08-08 21:56:43 +00:00
// Taken from Xabis' "curved interpolation points paths" patch.
private static float SplineLerp ( float u , float p1 , float p2 , float p3 , float p4 )
{
float t2 = u ;
float res = 2 * p2 ;
res + = ( p3 - p1 ) * u ;
t2 * = u ;
res + = ( 2 * p1 - 5 * p2 + 4 * p3 - p4 ) * t2 ;
t2 * = u ;
res + = ( 3 * p2 - 3 * p3 + p4 - p1 ) * t2 ;
return 0.5f * res ;
}
2014-12-03 23:15:26 +00:00
private static float GetCorrectHeight ( Thing thing )
{
2015-06-03 13:20:06 +00:00
float height = thing . Height / 2f ;
2013-09-11 09:47:53 +00:00
if ( thing . Sector ! = null ) height + = thing . Sector . FloorHeight ;
return height ;
}
}
2012-09-26 00:04:17 +00:00
}