Freeform Zone Generation

Modified zone generator to create multiple fixed points with areas of influence tangent to each other and the zone boundary
Added weapon range icons
Modified gravity potential shader to fade out at maximum depth
This commit is contained in:
Reinier Vens 2021-03-08 22:58:37 +00:00
commit cb4cb243e4
19 changed files with 541 additions and 112 deletions

BIN
Asset Sources/clouds.psd (Stored with Git LFS)

Binary file not shown.

BIN
Asset Sources/range.psd (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -86,7 +86,7 @@ Material:
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Scale: 1.2393435
- _Scale: 4.933006
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1

BIN
Assets/Resources/Settings.asset (Stored with Git LFS)

Binary file not shown.

View file

@ -54,6 +54,7 @@ distance by Anthony Ledoux
Cross by shashank singh
Worried Fire Flame by Akshar Pathak
Boiling Thermometer by KEN MURRAY
wifi by i cons
Other

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 85df8a21e7cff9c44817677a6bf316c5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/Icons/Weapons/Long Range.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: c67d4f4a1937edd45b177b6ca7f086b3
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/Icons/Weapons/Medium Range.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 8116eba48a70bee45a4e8b86ed4ade50
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/Icons/Weapons/Melee Range.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 9099d9d8147be6647bef58bc86f6cce8
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/Resources/Sprites/Icons/Weapons/Short Range.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,108 @@
fileFormatVersion: 2
guid: 3605fce693e46684db487553aaf9b74d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 64
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -4010,101 +4010,6 @@ Camera:
m_OcclusionCulling: 0
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!1 &328187702
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 328187706}
- component: {fileID: 328187705}
- component: {fileID: 328187704}
- component: {fileID: 328187703}
m_Layer: 0
m_Name: Sphere
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!135 &328187703
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 328187702}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &328187704
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 328187702}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 5acb4b3490cbda04c85ad7cca3b2b325, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &328187705
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 328187702}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &328187706
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 328187702}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -1.03, y: -0.16, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 9
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1001 &342600466
PrefabInstance:
m_ObjectHideFlags: 0
@ -18504,7 +18409,7 @@ PrefabInstance:
- target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 55.675
value: 0
objectReference: {fileID: 0}
- target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e,
type: 3}
@ -19534,7 +19439,7 @@ PrefabInstance:
- target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e,
type: 3}
propertyPath: m_AnchoredPosition.x
value: 60.98
value: 0
objectReference: {fileID: 0}
- target: {fileID: 878382121558056004, guid: 2758dfa2744d7ad4d8877545189d116e,
type: 3}

View file

@ -144,7 +144,10 @@ public class Zone
}
}
Orbits[orbitID].Position = GetOrbitPosition(orbit.Data.Parent) + pos;
var parentPosition = Orbits[orbitID].Data.Parent == Guid.Empty
? Orbits[orbitID].Data.FixedPosition :
GetOrbitPosition(orbit.Data.Parent);
Orbits[orbitID].Position = parentPosition + pos;
_updatedOrbits.Add(orbitID);
}

View file

@ -10,6 +10,7 @@ using Newtonsoft.Json;
using UniRx;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
using float3 = Unity.Mathematics.float3;
[RethinkTable("Galaxy"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
@ -226,6 +227,9 @@ public class OrbitData : DatabaseEntry
[JsonProperty("phase"), Key(3)]
public float Phase;
[JsonProperty("phase"), Key(3)]
public float2 FixedPosition = float2.zero;
// [JsonProperty("period"), Key(4)]
// public float Period;

View file

@ -11,6 +11,7 @@ using MessagePack;
using UniRx;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using float2 = Unity.Mathematics.float2;
using Random = Unity.Mathematics.Random;
public class ZoneGenerator
@ -40,9 +41,27 @@ public class ZoneGenerator
var position = float2(0f);
var random = new Random(Convert.ToUInt32(abs(name.GetHashCode())));
return GenerateZone(settings, name, position, mass, radius, random);
return GenerateZone(settings, name, position, mass, radius, random, 6);
}
private class Circle
{
public float2 Center;
public float Radius;
public float Area => PI * Radius * Radius;
public Circle(float2 center, float radius)
{
Center = center;
Radius = radius;
}
public float DistanceTo(float2 point) => length(point - Center) - Radius;
public float DistanceTo(Circle other) => length(other.Center - Center) - Radius - other.Radius;
}
private const int MaximumPlacementSamples = 32;
private static ZonePack GenerateZone(
ZoneGenerationSettings settings,
// Dictionary<string,GalaxyMapLayerData> mapLayers,
@ -51,7 +70,8 @@ public class ZoneGenerator
float2 position,
float mass,
float radius,
Random random)
Random random,
int targetSubzoneCount)
{
var zone = new ZoneData
{
@ -68,8 +88,45 @@ public class ZoneGenerator
zone.Mass = mass;
//Debug.Log($"Generating zone at position {zone.Position} with radius {zoneRadius} and mass {zoneMass}");
var planets = GenerateEntities(settings, ref random, zone);
var planets = new List<GeneratorPlanet>();
if (targetSubzoneCount > 1)
{
var zoneBoundary = new Circle(float2.zero, radius);
float boundaryTangentRadius(float2 point) => -zoneBoundary.DistanceTo(point);
var occupiedAreas = new List<Circle>();
float tangentRadius(float2 point) => min(boundaryTangentRadius(point), occupiedAreas.Min(circle => circle.DistanceTo(point)));
var startPosition = random.NextFloat(radius * .25f, radius * .75f) * random.NextFloat2Direction();
occupiedAreas.Add(new Circle(startPosition, boundaryTangentRadius(startPosition)));
int samples = 0;
while (occupiedAreas.Count < targetSubzoneCount && samples < MaximumPlacementSamples)
{
samples = 0;
for (int i = 0; i < MaximumPlacementSamples; i++)
{
var samplePos = random.NextFloat2(-radius, radius);
var rad = tangentRadius(samplePos);
if (rad > 0)
{
occupiedAreas.Add(new Circle(samplePos, rad));
break;
}
samples++;
}
}
var totalArea = occupiedAreas.Sum(c => c.Area);
foreach (var c in occupiedAreas)
{
planets.AddRange(GenerateEntities(settings, ref random, c.Area / totalArea * zone.Mass, c.Radius, c.Center));
}
}
else
planets.AddRange(GenerateEntities(settings, ref random, zone.Mass, zone.Radius, float2.zero));
// Create collections to map between zone generator output and database entries
var orbitMap = new Dictionary<GeneratorPlanet, OrbitData>();
@ -80,6 +137,7 @@ public class ZoneGenerator
{
var data = new OrbitData
{
FixedPosition = planet.FixedPosition,
Distance = new ReactiveProperty<float>(planet.Distance),
//Period = planet.Period,
Phase = planet.Phase
@ -209,14 +267,15 @@ public class ZoneGenerator
// 1 / lerp(settings.ResourceDensityMinimum, settings.ResourceDensityMaximum, density));
// }
public static GeneratorPlanet[] GenerateEntities(ZoneGenerationSettings settings, ref Random random, ZoneData zone)
public static GeneratorPlanet[] GenerateEntities(ZoneGenerationSettings settings, ref Random random, float mass, float radius, float2 fixedPosition)
{
var root = new GeneratorPlanet
{
FixedPosition = fixedPosition,
Settings = settings,
Mass = zone.Mass,
ChildDistanceMaximum = zone.Radius * .75f,
ChildDistanceMinimum = settings.PlanetSafetyRadius.Evaluate(zone.Mass)
Mass = mass,
ChildDistanceMaximum = radius * .75f,
ChildDistanceMinimum = settings.PlanetSafetyRadius.Evaluate(mass)
};
// There is some chance of generating a rosette or binary system
@ -350,6 +409,7 @@ public class GeneratorPlanet
public bool Belt = false;
public List<GeneratorPlanet> Children = new List<GeneratorPlanet>(); // Planets orbiting this one are referred to as children
public GeneratorPlanet Parent;
public float2 FixedPosition;
// Recursively gather all planets in the hierarchy
public IEnumerable<GeneratorPlanet> AllPlanets()

View file

@ -193,6 +193,7 @@ Shader "UI/Isolines"
for (float ia = 0.5; ia < 13; ia++) {
float isoline = abs(angle - ia / 6.0);
float l = 1-smoothstep(_AngleWidth, _AngleWidth * _AngleFade, isoline);
l *= smoothstep(-_StartDepth - _DepthRange, -_StartDepth, h);
col += l * angcol;
// col += (1-smoothstep(_Angle2Width, _Angle2Width * _Angle2Fade, isoline / angmag)) * _AngleColor * blend; // Isoline
}