@ -1,4 +1,5 @@
using System.IO ;
using System.Runtime.InteropServices ;
public partial class Terrain
{
@ -8,11 +9,15 @@ public partial class Terrain
// Set by the terrain editing plugin.
public EditorUndoRedoManager EditorUndoRedo { get ; set ; }
// Dummy value to satisfy the overly careful compiler.
static bool _d ummy = false ;
Material _ editToolMaterial ;
public override void _ EnterTree ( )
{
_ editToolMaterial = new StandardMaterial3D {
AlbedoColor = Colors . Blue ,
VertexColorUseAsAlbedo = true ,
BlendMode = BaseMaterial3D . BlendModeEnum . Mix ,
ShadingMode = BaseMaterial3D . ShadingModeEnum . Unshaded ,
DepthDrawMode = BaseMaterial3D . DepthDrawModeEnum . Disabled ,
NoDepthTest = true ,
@ -64,46 +69,60 @@ public partial class Terrain
center . DistanceSquaredTo ( tile . ToCenter ( ) ) < distanceSqr ) ;
}
var tiles = ( toolShape switch {
ToolShape . Corner = > [ hover . Position ] ,
ToolShape . Circle = > GetTilesInRadius ( ) ,
ToolShape . Square = > GetTilesInSquare ( ) ,
_ = > throw new InvalidOperationException ( ) ,
} ) . ToHashSet ( ) ;
// TODO: Handle different tool modes, such as painting.
// TODO: Finally allow editing single corners.
// TODO: Allow click-dragging which doesn't affect already changed tiles / corners.
// TODO: Make mesh generation generate vertical walls between disconnected corners.
// TODO: Use ArrayMesh instead of ImmediateMesh.
// TODO: Dynamically expand terrain instead of having it be a set size.
// Raise / lower the terrain when left mouse button is pressed.
if ( ev is InputEventMouseButton { ButtonIndex : MouseButton . Left , Pressed : true } ) {
prevent_default = true ;
// Holds onto all the tiles and which of their corners corners will be affected by this edit operation.
var tilesToChange = new Dictionary < TilePos , Corners < bool > > ( ) ;
ref Corners < bool > GetTileToChange ( TilePos position )
// Don't look at this black magic. The Dictionary type should have this by default I swear!
= > ref CollectionsMarshal . GetValueRefOrAddDefault ( tilesToChange , position , out _d ummy ) ;
var cornersToChange = new HashSet < ( TilePos Position , Corner Corner ) > ( ) ;
if ( toolShape = = ToolShape . Corner ) {
// Modify selected corner itself.
GetTileToChange ( hover . Position ) [ hover . Corner ] = true ;
// Raise selected tiles themselves.
if ( isConnected ) {
var height = GetTile ( hover . Position ) . Height [ hover . Corner ] ;
foreach ( var neighbor in GetNeighbors ( hover . Position , hover . Corner ) ) {
var neighborHeight = GetTile ( neighbor . Position ) . Height [ neighbor . Corner ] ;
if ( neighborHeight ! = height ) continue ;
GetTileToChange ( neighbor . Position ) [ neighbor . Corner ] = true ;
}
}
} else {
var tiles = ( toolShape switch {
ToolShape . Circle = > GetTilesInRadius ( ) ,
ToolShape . Square = > GetTilesInSquare ( ) ,
_ = > throw new InvalidOperationException ( ) ,
} ) . ToHashSet ( ) ;
// Modify selected tiles themselves.
foreach ( var pos in tiles )
foreach ( var corner2 in Enum . GetValues < Corner > ( ) )
cornersToChange . Add ( ( pos , corner2 ) ) ;
GetTileToChange ( pos ) = new ( true ) ;
if ( isConnected ) {
// If the 'connected_toggle' button is active, move "connected" corners.
// Connected corners are the ones that are at the same height as ones already being moved.
foreach ( var pos in tiles ) {
var tile = GetTile ( pos ) ;
foreach ( var corner in Enum . GetValues < Corner > ( ) ) {
var height = tile . Height [ corner ] ;
foreach ( var ( neighborPos , neighborCorner ) in GetNeighbors ( pos , corner ) ) {
if ( tiles . Contains ( neighborPos ) ) continue ;
var neighborHeight = GetTile ( neighborPos ) . Height [ neighborCorner ] ;
if ( neighborHeight = = height ) cornersToChange . Add ( ( neighborPos , neighborCorner ) ) ;
}
// If the 'connected_toggle' button is active, move "connected" corners.
// Connected corners are the ones that are at the same height as ones already being moved.
if ( isConnected ) foreach ( var pos in tiles ) {
var tile = GetTile ( pos ) ;
foreach ( var corner in Enum . GetValues < Corner > ( ) ) {
var height = tile . Height [ corner ] ;
foreach ( var neighbor in GetNeighbors ( pos , corner ) ) {
if ( tiles . Contains ( neighbor . Position ) ) continue ;
var neighborHeight = GetTile ( neighbor . Position ) . Height [ neighbor . Corner ] ;
if ( neighborHeight ! = height ) continue ;
GetTileToChange ( neighbor . Position ) [ neighbor . Corner ] = true ;
}
}
}
}
// Raise / lower the terrain when left mouse button is pressed.
if ( ev is InputEventMouseButton { ButtonIndex : MouseButton . Left , Pressed : true } ) {
prevent_default = true ;
const float AdjustHeight = 0.5f ;
var amount = isFlatten ? GetTile ( hover . Position ) . Height [ hover . Corner ]
@ -111,16 +130,15 @@ public partial class Terrain
var tilesPrevious = new List < ( TilePos , Corners < float > ) > ( ) ;
var tilesChanged = new List < ( TilePos , Corners < float > ) > ( ) ;
foreach ( var group in cornersToChange . GroupBy ( c = > c . Position , c = > c . Corner ) ) {
var pos = group . Key ;
foreach ( var ( pos , corners ) in tilesToChange ) {
var tile = GetTile ( pos ) ;
tilesPrevious . Add ( ( pos , tile . Height ) ) ;
var newHeight = tile . Height ;
foreach ( var corner in group ) {
if ( isFlatten ) newHeight [ corner ] = amount ;
else newHeight [ corner ] + = amount ;
}
if ( corners . TopLeft ) newHeight . TopLeft = isFlatten ? amount : newHeight . TopLeft + amount ;
if ( corners . TopRight ) newHeight . TopRight = isFlatten ? amount : newHeight . TopRight + amount ;
if ( corners . BottomRight ) newHeight . BottomRight = isFlatten ? amount : newHeight . BottomRight + amount ;
if ( corners . BottomLeft ) newHeight . BottomLeft = isFlatten ? amount : newHeight . BottomLeft + amount ;
tilesChanged . Add ( ( pos , newHeight ) ) ;
}
@ -140,7 +158,7 @@ public partial class Terrain
}
}
UpdateEditToolMesh ( tiles ) ;
UpdateEditToolMesh ( tilesToChange ) ;
return prevent_default ;
}
@ -157,29 +175,28 @@ public partial class Terrain
}
void UpdateEditToolMesh ( IEnumerable < TilePos > tiles )
void UpdateEditToolMesh ( Dictionary < TilePos , Corners < bool > > tiles )
{
var mesh = GetOrCreateMesh ( "EditToolMesh" ) ;
mesh . ClearSurfaces ( ) ;
mesh . SurfaceBegin ( Mesh . PrimitiveType . Lines ) ;
void AddLine ( Vector3 start , Vector3 end ) {
mesh . SurfaceAddVertex ( start ) ;
mesh . SurfaceAddVertex ( end ) ;
void AddLine ( ( Vector3 Position , bool Visible ) start ,
( Vector3 Position , bool Visible ) end ) {
mesh . SurfaceSetColor ( start . Visible ? Colors . Blue : Colors . Transparent ) ;
mesh . SurfaceAddVertex ( start . Position ) ;
mesh . SurfaceSetColor ( end . Visible ? Colors . Blue : Colors . Transparent ) ;
mesh . SurfaceAddVertex ( end . Position ) ;
}
void AddQuad ( Vector3 topLeft , Vector3 topRight ,
Vector3 bottomRight , Vector3 bottomLeft ) {
AddLine ( topLeft , topRight ) ;
AddLine ( topRight , bottomRight ) ;
AddLine ( bottomRight , bottomLeft ) ;
AddLine ( bottomLeft , topLeft ) ;
}
foreach ( var tile in tiles ) {
var ( topLeft , topRight , bottomRight , bottomLeft )
= GetTileCornerPositions ( tile ) ;
AddQuad ( topLeft , topRight , bottomRight , bottomLeft ) ;
foreach ( var ( tile , visible ) in tiles ) {
var positions = GetTileCornerPositions ( tile ) ;
foreach ( var side in Enum . GetValues < Side > ( ) ) {
var ( corner1 , corner2 ) = side . GetCorners ( ) ;
if ( ! visible [ corner1 ] & & ! visible [ corner2 ] ) continue ;
AddLine ( ( positions [ corner1 ] , visible [ corner1 ] ) ,
( positions [ corner2 ] , visible [ corner2 ] ) ) ;
}
}
mesh . SurfaceEnd ( ) ;
@ -211,7 +228,7 @@ public partial class Terrain
[Corner.BottomRight] = [ ( + 1 , + 1 , Corner . TopLeft ) , ( + 1 , 0 , Corner . BottomLeft ) , ( 0 , + 1 , Corner . TopRight ) ] ,
[Corner.BottomLeft ] = [ ( - 1 , + 1 , Corner . TopRight ) , ( - 1 , 0 , Corner . BottomRight ) , ( 0 , + 1 , Corner . TopLeft ) ] ,
} ;
static IEnumerable < ( TilePos , Corner ) > GetNeighbors ( TilePos pos , Corner corner )
static IEnumerable < ( TilePos Position , Corner Corner ) > GetNeighbors ( TilePos pos , Corner corner )
= > _ offsetLookup [ corner ] . Select ( e = > ( new TilePos ( pos . X + e . X , pos . Y + e . Y ) , e . Opposite ) ) ;
static byte [ ] Pack ( IEnumerable < ( TilePos Position , Corners < float > Corners ) > data )