public AutoSplatHeightAngleRange AddHeight(long heightMM, AutoSplatHeightAngleRange slopesTemplate) { AutoSplatHeightAngleRange range = new AutoSplatHeightAngleRange(heightMM, slopesTemplate); InternalAddHeightAngleRange(range); return(range); }
protected void FireHeightRangeMoved(AutoSplatHeightAngleRange range) { if (null != HeightRangeMoved) { HeightRangeMoved(this, range); } }
public AutoSplatHeightAngleRange AddHeight(long heightMM, int textureIndex) { AutoSplatHeightAngleRange range = new AutoSplatHeightAngleRange(heightMM, textureIndex, textureIndex); InternalAddHeightAngleRange(range); return(range); }
public void MoveHeight(Guid id, long newHeightMM) { if (m_IdToRange.ContainsKey(id)) { AutoSplatHeightAngleRange targetRange = m_IdToRange[id]; if (newHeightMM.Equals(targetRange.HeightMM)) { // This move isn't going to change anything, so get out early. return; } int index = m_RangeList.IndexOf(targetRange); AutoSplatHeightAngleRange splitRange = null; if (0 == index || (m_RangeList.Count - 1) == index) { // This move alters a range at an extreme, which implicitly splits the // range as we must always keep a range at each extreme. splitRange = new AutoSplatHeightAngleRange(targetRange.HeightMM, targetRange); } else { // Guaranteed to be neither the first nor last AutoSplatHeightAngleRange prev = m_RangeList[index - 1]; AutoSplatHeightAngleRange next = m_RangeList[index + 1]; if (newHeightMM < prev.HeightMM || next.HeightMM < newHeightMM) { throw new ConstraintException( "MoveHeight would change ordering of HeightAngleRange list;" + " only Add and Remove should change the sort ordering."); } // If the move would make the height coincide with one of its // neighbors, back it off slightly to make things sort nicely. if (newHeightMM.Equals(prev.HeightMM)) { ++newHeightMM; } else if (newHeightMM.Equals(next.HeightMM)) { --newHeightMM; } } targetRange.HeightMM = newHeightMM; FireHeightRangeMoved(targetRange); if (null != splitRange) { // Add the result of the split InternalAddHeightAngleRange(splitRange); } } }
private void ConvertConfigToRules(AutoSplatConfig autoSplatConfig) { // Initialize textures layerTextureNames[SAND_INDEX] = autoSplatConfig.SandTextureName; layerTextureNames[GRASS_INDEX] = autoSplatConfig.GrassTextureName; layerTextureNames[ROCK_INDEX] = autoSplatConfig.RockTextureName; layerTextureNames[SNOW_INDEX] = autoSplatConfig.SnowTextureName; // Create height angle ranges bottom-up; in all cases we use rock for // the vertical -- the steeper the rockier. Actually, more accurately, // we don't use full vertical; in the auto splat normal rules, we only // go up to one radian. So each of these is actually a triplet, where // we use the same texture for up to 57.2 degrees, then start adding rock. AutoSplatHeightAngleRange range; float angleStart = 0f; float angleMid = 90f - 57.2957795f; float angleEnd = 90f; // Min height - Sand range = new AutoSplatHeightAngleRange(Math.Min(MinHeightMM, 0)); range.AddAngleTexturePair(angleStart, SAND_INDEX); range.AddAngleTexturePair(angleMid, SAND_INDEX); range.AddAngleTexturePair(angleEnd, ROCK_INDEX); InternalAddHeightAngleRange(range); // Grass - blend inflection point for grass range = new AutoSplatHeightAngleRange((long)autoSplatConfig.SandToGrassHeight); range.AddAngleTexturePair(angleStart, GRASS_INDEX); range.AddAngleTexturePair(angleMid, GRASS_INDEX); range.AddAngleTexturePair(angleEnd, ROCK_INDEX); InternalAddHeightAngleRange(range); // Rock - blend inflection point for rock range = new AutoSplatHeightAngleRange((long)autoSplatConfig.GrassToRockHeight); range.AddAngleTexturePair(angleStart, ROCK_INDEX); range.AddAngleTexturePair(angleEnd, ROCK_INDEX); InternalAddHeightAngleRange(range); // Snow - blend inflection point for snow range = new AutoSplatHeightAngleRange((long)autoSplatConfig.RockToSnowHeight); range.AddAngleTexturePair(angleStart, SNOW_INDEX); range.AddAngleTexturePair(angleMid, SNOW_INDEX); range.AddAngleTexturePair(angleEnd, ROCK_INDEX); InternalAddHeightAngleRange(range); // Max height - Snow! if (MaxHeightMM > autoSplatConfig.RockToSnowHeight) { // Max height - Snow! range = new AutoSplatHeightAngleRange(MaxHeightMM); range.AddAngleTexturePair(angleStart, SNOW_INDEX); range.AddAngleTexturePair(angleEnd, SNOW_INDEX); InternalAddHeightAngleRange(range); } }
private void InternalAddHeightAngleRange(AutoSplatHeightAngleRange range) { if (range == null) { throw new NullReferenceException(); } // Make sure the height angle range to be added is not duplicated foreach (AutoSplatHeightAngleRange angleRange in m_RangeList) { if (angleRange.HeightMM == range.HeightMM) { throw new ArgumentException("Duplicate heights are not allowed." + range); } } #if REJECT_OUT_OF_BOUNDS_RANGES // A properly configured world will never have a range outside of the // height limits, but it is possible that a legacy world will not have // saved the AutoSplatConfig, so it uses the default--and the default // config might have ranges outside the world's limits. Rather than // try to guess how to fit it, we'll just discard it, and leave it to // the user to set up a configuration that they want. if (range.HeightMM <= MaxHeightMM && range.HeightMM >= MinHeightMM) { m_RangeList.Add(range); m_RangeList.Sort(); // Keep sorted by height m_IdToRange.Add(range.Id, range); FireHeightRangeAdded(range); } #else m_RangeList.Add(range); m_RangeList.Sort(); // Keep sorted by height m_IdToRange.Add(range.Id, range); // TODO: This is not right yet, but useful in the short term // For legacy worlds, we may have ranges outside the min/max // that occurs in the world. We'll stretch the bounds for // auto-splatting, but now they are out of sync with the terrain. AdjustMinMax(); FireHeightRangeAdded(range); #endif }
private void InternalRemoveHeightAngleRange(Guid rangeId) { int index = m_RangeList.IndexOf(m_IdToRange[rangeId]); // Design constraints require that you always have a min and max // range, so we are not allowed to remove the extremes. Otherwise, // go crazy! if (0 != index && (m_RangeList.Count - 1) != index) { AutoSplatHeightAngleRange range = m_IdToRange[rangeId]; m_RangeList.Remove(range); m_IdToRange.Remove(rangeId); FireHeightRangeRemoved(range); } }
public float[] GetAutoSplatSampleNormalized(long heightMM, Vector3 normal) { AutoSplatHeightAngleRange lowerRange = null; AutoSplatHeightAngleRange higherRange = null; // We assume the ranges are sorted in increasing order by height foreach (AutoSplatHeightAngleRange range in m_RangeList) { if (range.HeightMM == heightMM) { lowerRange = range; higherRange = range; break; } if (range.HeightMM < heightMM) { lowerRange = range; continue; } if (range.HeightMM > heightMM) { higherRange = range; // We should have both the lower & upper bounds now, so break; break; } } if (lowerRange == null) { lowerRange = m_RangeList[0]; // allows us to continue } if (higherRange == null) { higherRange = m_RangeList[m_RangeList.Count - 1]; // allows us to continue } // We want the angle of the normal relative to the XZ plane, so // we first use the dot product to get the angle to the Y-axis // which is perpendicular to the XZ plane, convert it to degress, // and then subtract it from 90. float angleRadians = normal.Dot(Vector3.UnitY); float angleDegrees = Convert.ToSingle(90 - RadiansToDegrees(angleRadians)); if (lowerRange == higherRange) { // No need to do any weighting since we at the exact height return(lowerRange.GetAutoSplatSampleNormalized(angleDegrees)); } // Compute the gradiant weighting for the lower & higher angled textures float lowerWeight; float higherWeight; long heightDiff = higherRange.HeightMM - lowerRange.HeightMM; if (heightDiff == 0) { // Give equal weighting to both samples. // This covers the case when we have two ranges at the // same height....this really shouldn't happen due to the // way we choose the lower/higher ranges. lowerWeight = 0.5f; higherWeight = 0.5f; } else { // How close is the angle to the higher/lower angle? Normalize // that distance from 0..1 and use that as the gradient weights higherWeight = ((float)(heightMM - lowerRange.HeightMM)) / heightDiff; lowerWeight = 1f - higherWeight; } float[] lowerNormalizedSample = lowerRange.GetAutoSplatSampleNormalized(angleDegrees); float[] higherNormalizedSample = higherRange.GetAutoSplatSampleNormalized(angleDegrees); float[] normalizedSample = new float[AlphaSplatTerrainConfig.MAX_LAYER_TEXTURES]; for (int i = 0; i < AlphaSplatTerrainConfig.MAX_LAYER_TEXTURES; i++) { normalizedSample[i] = lowerNormalizedSample[i] * lowerWeight + higherNormalizedSample[i] * higherWeight; } return(normalizedSample); }
public void UndoRemoveHeightRange(AutoSplatHeightAngleRange range) { InternalAddHeightAngleRange(range); }