Skip to content

Instantly share code, notes, and snippets.

@mihakrajnc
Last active February 13, 2018 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mihakrajnc/bfcae06a16a0991cb5a41601663b8c9e to your computer and use it in GitHub Desktop.
Save mihakrajnc/bfcae06a16a0991cb5a41601663b8c9e to your computer and use it in GitHub Desktop.
A snap to terrain modifier for Mapbox Unity that uses Raycasting.
using Mapbox.Unity.Map;
using Mapbox.Unity.MeshGeneration.Data;
using Mapbox.Unity.MeshGeneration.Modifiers;
using UnityEngine;
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Custom/Snap Terrain Raycast Modifier")]
public class SnapTerrainRaycastModifier : MeshModifier
{
private const int RAY_LENGTH = 50;
[SerializeField] private LayerMask terrainMask;
public override ModifierType Type
{
get { return ModifierType.Preprocess; }
}
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
{
float worldScale = FindObjectOfType<AbstractMap>().WorldRelativeScale;
if (md.Vertices.Count > 0)
{
for (int i = 0; i < md.Vertices.Count; i++)
{
var h = tile.QueryHeightData((float) ((md.Vertices[i].x + tile.Rect.Size.x / 2) / tile.Rect.Size.x),
(float) ((md.Vertices[i].z + tile.Rect.Size.y / 2) / tile.Rect.Size.y));
RaycastHit hit;
Vector3 rayCenter =
new Vector3(md.Vertices[i].x * worldScale + tile.transform.position.x,
h * worldScale + RAY_LENGTH / 2,
md.Vertices[i].z * worldScale + tile.transform.position.z);
if (Physics.Raycast(rayCenter, Vector3.down, out hit, RAY_LENGTH, terrainMask))
{
md.Vertices[i] += new Vector3(0, hit.point.y / worldScale, 0);
}
else
{
// Raycasting sometimes fails at terrain boundaries, fallback to tile height data.
md.Vertices[i] += new Vector3(0, h, 0);
}
}
}
else
{
foreach (var sub in feature.Points)
{
for (int i = 0; i < sub.Count; i++)
{
var h = tile.QueryHeightData((float) ((sub[i].x + tile.Rect.Size.x / 2) / tile.Rect.Size.x),
(float) ((sub[i].z + tile.Rect.Size.y / 2) / tile.Rect.Size.y));
RaycastHit hit;
Vector3 rayCenter =
new Vector3(sub[i].x * worldScale + tile.transform.position.x,
h * worldScale + RAY_LENGTH / 2,
sub[i].z * worldScale + tile.transform.position.z);
if (Physics.Raycast(rayCenter, Vector3.down, out hit, RAY_LENGTH, terrainMask))
{
sub[i] += new Vector3(0, hit.point.y / worldScale, 0);
}
else
{
// Raycasting sometimes fails at terrain boundaries, fallback to tile height data.
sub[i] += new Vector3(0, h, 0);
}
}
}
}
}
}
@mihakrajnc
Copy link
Author

NOTE: You need to configure your terrain to be added to a layer, and use the layer name with this modifier (default "Terrain").

@aCallum
Copy link

aCallum commented Aug 15, 2017

When making use of snap terrain modifiers such as this, the UnityTile paramter tile is always Null - where is the tile retreived from?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment