Finished the code for radial distribution

This commit is contained in:
Misaki
2024-09-24 21:35:53 +09:00
parent df0194ff9c
commit a9095d9636
24 changed files with 552 additions and 245 deletions

View File

@@ -9,10 +9,8 @@ using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using UnityEngine.Splines;
using Object = UnityEngine.Object;
using Random = Unity.Mathematics.Random;
namespace Misaki.ArtTool
@@ -30,26 +28,38 @@ namespace Misaki.ArtTool
public MeshFilter inputMeshFilter;
public ObjectDistributionSetting objectDistributionSetting;
public SplineContainer inputSplineContainer;
public SplineDistributionSetting splineDistributionSetting;
public LinearDistributionSetting linearDistributionSetting;
public GridDistributionSetting gridDistributionSetting;
public List<EffectorData> effectors;
public RadialDistributionSetting radialDistributionSetting;
public List<EffectorData> effectors = new();
public DebugMode debugMode;
public float gizmosSize = 0.25f;
private static readonly ArrayPool<PointData> _pointPool = ArrayPool<PointData>.Shared;
private static readonly ArrayPool<int> _intPool = ArrayPool<int>.Shared;
private int _pointSize;
public int pointSize;
private PointData[] _points;
private int[] _instantiateObjects;
private List<GameObject> _instantiateObjects = new();
private readonly Dictionary<int, List<Matrix4x4>> _objectGroup = new();
private bool _isPointsDirty = false;
private const float GIZMOS_BASE_SIZE = 0.25f;
[MenuItem("GameObject/Cloner/Cloner Object")]
public static void CreateCloner()
{
var clonerObject = new GameObject("Cloner");
GameObjectUtility.EnsureUniqueNameForSibling(clonerObject);
clonerObject.AddComponent<Cloner>();
}
private void OnEnable()
{
@@ -63,11 +73,11 @@ namespace Misaki.ArtTool
item.effector.propertyChanged += OnPropertyChanged;
}
if (transform.childCount > 0 && _instantiateObjects.Length == 0)
if (transform.childCount > 0 && _instantiateObjects.Count == 0)
{
for (var i = 0; i < transform.childCount; i++)
{
_instantiateObjects[i] = transform.GetChild(i).GetInstanceID();
_instantiateObjects[i] = transform.GetChild(i).gameObject;
}
}
}
@@ -127,14 +137,12 @@ namespace Misaki.ArtTool
{
shadowCastingMode = ShadowCastingMode.On
};
Graphics.RenderMeshInstanced(renderParams, objectData.Mesh, 0, item.Value, _pointSize);
Graphics.RenderMeshInstanced(renderParams, objectData.Mesh, 0, item.Value, pointSize);
}
}
public void GeneratePoints()
{
Profiler.BeginSample("GeneratePoints");
Clear();
switch (distributionMode)
@@ -147,7 +155,7 @@ namespace Misaki.ArtTool
}
objectDistributionSetting.meshData = new(inputMeshFilter, Allocator.TempJob);
_pointSize = objectDistributionSetting.DistributionCount;
pointSize = objectDistributionSetting.DistributionCount;
break;
case DistributionMode.Spline:
@@ -161,18 +169,19 @@ namespace Misaki.ArtTool
splineDistributionSetting.nativeSpline = new(inputSplineContainer.Spline, Allocator.TempJob);
splineDistributionSetting.splineLength = splineDistributionSetting.nativeSpline.CalculateLength(splineDistributionSetting.splineWorldMatrix);
_pointSize = splineDistributionSetting.DistributionCount;
pointSize = splineDistributionSetting.DistributionCount;
break;
case DistributionMode.Linear:
_pointSize = (int)linearDistributionSetting.count;
pointSize = (int)linearDistributionSetting.count;
break;
case DistributionMode.Grid:
_pointSize = gridDistributionSetting.DistributionCount;
pointSize = gridDistributionSetting.DistributionCount;
break;
case DistributionMode.Radial:
pointSize = (int)radialDistributionSetting.count;
break;
case DistributionMode.Honeycomb:
break;
@@ -183,12 +192,12 @@ namespace Misaki.ArtTool
// Allocate a empty native collection to avoid job error
EnsureNativeCollectionValid();
if (_pointSize == 0)
if (pointSize == 0)
{
return;
}
_points = _pointPool.Rent(_pointSize);
_points = _pointPool.Rent(pointSize);
foreach (var effectorData in effectors)
{
@@ -202,7 +211,7 @@ namespace Misaki.ArtTool
var pointsGenerationJob = new PointsGenerationJob()
{
worldMatrix = worldMatrix,
pointSize = _pointSize,
pointSize = pointSize,
distributionMode = distributionMode,
@@ -210,11 +219,12 @@ namespace Misaki.ArtTool
splineDistributionSetting = splineDistributionSetting,
linearDistributionSetting = linearDistributionSetting,
gridDistributionSetting = gridDistributionSetting,
radialDistributionSetting = radialDistributionSetting,
points = pointsArray
};
var handle = pointsGenerationJob.Schedule(_pointSize, 64);
var handle = pointsGenerationJob.Schedule(pointSize, 64);
handle.Complete();
pointsArray.CopyTo(_points);
@@ -224,16 +234,11 @@ namespace Misaki.ArtTool
objectDistributionSetting.meshData.Dispose();
// Switch to managed thread for effectors because of interface
Parallel.For(0, _pointSize, i =>
Parallel.For(0, pointSize, i =>
{
for (var e = effectors.Count - 1; e >= 0; e--)
{
if (!effectors[e].enable)
{
continue;
}
if (effectors[e].effector == null)
if (!effectors[e].enable || effectors[e].effector == null)
{
continue;
}
@@ -248,8 +253,6 @@ namespace Misaki.ArtTool
});
_isPointsDirty = false;
Profiler.EndSample();
}
private void EnsureNativeCollectionValid()
@@ -267,7 +270,7 @@ namespace Misaki.ArtTool
public void InstantiateGameObjectOnPoints()
{
if (_points == null && _pointSize <= 0)
if (_points == null && pointSize <= 0)
{
return;
}
@@ -278,7 +281,7 @@ namespace Misaki.ArtTool
{
// Use Fisher-Yates Shuffle algorithm to swap value
var random = new Random(seed);
for (var i = _pointSize - 1; i > 0; i--)
for (var i = pointSize - 1; i > 0; i--)
{
var k = random.NextInt(0, i + 1);
(_points[i], _points[k]) = (_points[k], _points[i]);
@@ -292,7 +295,7 @@ namespace Misaki.ArtTool
var currentIndex = 0;
var objectIndex = 0;
while (currentIndex < _pointSize)
while (currentIndex < pointSize)
{
if (!_points[currentIndex].isValid)
{
@@ -318,7 +321,7 @@ namespace Misaki.ArtTool
_objectGroup[objectIndex].Add(_points[currentIndex].matrix);
currentIndex++;
if (currentIndex >= _pointSize)
if (currentIndex >= pointSize)
{
break;
}
@@ -339,22 +342,17 @@ namespace Misaki.ArtTool
currentIndex = 0;
if (!isRenderInstancing)
{
if (_instantiateObjects != null)
{
_intPool.Return(_instantiateObjects, true);
}
_instantiateObjects.Capacity = pointSize;
_instantiateObjects = _intPool.Rent(_pointSize);
using var transformAccess = new TransformAccessArray(_pointSize);
using var pointsList = new NativeList<float4x4>(_pointSize, Allocator.TempJob);
var transformAccess = new TransformAccessArray(pointSize);
var pointsList = new NativeList<float4x4>(pointSize, Allocator.TempJob);
foreach (var item in _objectGroup)
{
var instantiateCount = item.Value.Count;
using var instanceIDs = new NativeArray<int>(instantiateCount, Allocator.TempJob);
using var transformIDs = new NativeArray<int>(instantiateCount, Allocator.TempJob);
var instanceIDs = new NativeArray<int>(instantiateCount, Allocator.TempJob);
var transformIDs = new NativeArray<int>(instantiateCount, Allocator.TempJob);
GameObject.InstantiateGameObjects(inputObjects[item.Key].gameObject.GetInstanceID(), instantiateCount, instanceIDs, transformIDs);
@@ -363,9 +361,15 @@ namespace Misaki.ArtTool
transformAccess.Add(transformIDs[i]);
pointsList.Add(item.Value[i]);
_instantiateObjects[currentIndex] = instanceIDs[i];
var instantiateObject = (GameObject)Resources.InstanceIDToObject(instanceIDs[i]);
instantiateObject.transform.parent = this.transform;
_instantiateObjects.Add(instantiateObject);
currentIndex++;
}
instanceIDs.Dispose();
transformIDs.Dispose();
}
var transformAccessJob = new TransformAccessJob()
@@ -375,6 +379,9 @@ namespace Misaki.ArtTool
var handle = transformAccessJob.Schedule(transformAccess);
handle.Complete();
transformAccess.Dispose();
pointsList.Dispose();
}
}
@@ -383,45 +390,38 @@ namespace Misaki.ArtTool
if (isClearPoints && _points != null)
{
_pointPool.Return(_points, true);
_pointSize = 0;
pointSize = 0;
}
if (isClearObjects)
{
_objectGroup.Clear();
if (_instantiateObjects == null || _instantiateObjects.Length <= 0 || _pointSize <= 0)
if (_instantiateObjects == null || _instantiateObjects.Count <= 0)
{
return;
}
var objectList = ListPool<Object>.Get();
using var idArray = new NativeArray<int>(_instantiateObjects.Length, Allocator.TempJob);
idArray.CopyFrom(_instantiateObjects);
Resources.InstanceIDToObjectList(idArray, objectList);
for (var i = 0; i < objectList.Count; i++)
for (var i = 0; i < _instantiateObjects.Count; i++)
{
#if UNITY_EDITOR
DestroyImmediate(objectList[i]);
DestroyImmediate(_instantiateObjects[i]);
#else
Destroy(child.gameObject);
Destroy(_instantiateObjects[i]);
#endif
}
ListPool<Object>.Release(objectList);
_intPool.Return(_instantiateObjects, true);
_instantiateObjects.Clear();
_objectGroup.Clear();
}
}
private void OnDrawGizmosSelected()
{
if (_pointSize <= 0 || _points.Length < _pointSize)
if (pointSize <= 0 || _points == null || _points.Length < pointSize)
{
return;
}
for (var i = 0; i < _pointSize; i++)
for (var i = 0; i < pointSize; i++)
{
var point = _points[i];
@@ -440,15 +440,15 @@ namespace Misaki.ArtTool
// Draw the x-axis in red
Gizmos.color = Color.red;
Gizmos.DrawLine(Vector3.zero, Vector3.right * scale * GIZMOS_BASE_SIZE);
Gizmos.DrawLine(Vector3.zero, Vector3.right * scale * gizmosSize);
// Draw the y-axis in green
Gizmos.color = Color.green;
Gizmos.DrawLine(Vector3.zero, Vector3.up * scale * GIZMOS_BASE_SIZE);
Gizmos.DrawLine(Vector3.zero, Vector3.up * scale * gizmosSize);
// Draw the z-axis in blue
Gizmos.color = Color.blue;
Gizmos.DrawLine(Vector3.zero, Vector3.forward * scale * GIZMOS_BASE_SIZE);
Gizmos.DrawLine(Vector3.zero, Vector3.forward * scale * gizmosSize);
break;
case DebugMode.Index:
@@ -472,7 +472,7 @@ namespace Misaki.ArtTool
Gizmos.color = Color.red;
}
Gizmos.DrawSphere(point.matrix.c3.xyz, GIZMOS_BASE_SIZE / 2.0f);
Gizmos.DrawSphere(point.matrix.GetPosition(), gizmosSize / 2.0f);
break;
default:

View File

@@ -17,14 +17,11 @@ namespace Misaki.ArtTool
break;
case GridShape.Sphere:
var isInsideSphere = ShapeHelper.IsPointInsideSphere(localPosition, 0.0f, setting.count * setting.spacing);
isValid = isInsideSphere;
isValid = ShapeHelper.IsPointInsideSphere(localPosition, 0.0f, setting.count * setting.spacing);
break;
case GridShape.Cylinder:
var isInsideCylinder = ShapeHelper.IsPointInsideCylinder(localPosition, 0.0f, setting.count * setting.spacing);
isValid = isInsideCylinder;
isValid = ShapeHelper.IsPointInsideCylinder(localPosition, 0.0f, setting.count * setting.spacing);
break;
default:
isValid = false;

View File

@@ -1,80 +1,39 @@
using Unity.Mathematics;
using Random = Unity.Mathematics.Random;
namespace Misaki.ArtTool
{
public static partial class Distribution
{
public static void ObjectDistribution(int index, ObjectDistributionSetting setting, out float4x4 localMatrix, out bool isValid)
public static void ObjectDistribution(int index, ref ObjectDistributionSetting setting, out float4x4 localMatrix, out bool isValid)
{
var random = new Random();
if (index > uint.MaxValue - setting.seed)
{
random = Random.CreateFromIndex(setting.seed - (uint)index);
}
else
{
random = Random.CreateFromIndex(setting.seed + (uint)index);
}
var position = float3.zero;
var forwardDirection = new float3(0.0f, 0.0f, 1.0f);
var upDirection = new float3(0.0f, 1.0f, 0.0f);
isValid = false;
localMatrix = float4x4.identity;
switch (setting.mode)
{
case ObjectDistributionMode.Surface:
isValid = ShapeHelper.TryGetMatrixOnMeshSurface(index, setting.seed, setting.isAlignNormal, ref setting.meshData, out localMatrix);
break;
case ObjectDistributionMode.Volume:
var meshScale = setting.meshData.worldMatrix.GetScale();
var meshPosition = setting.meshData.worldMatrix.c3.xyz;
position = random.NextFloat3(-setting.meshData.bounds.extents * meshScale + meshPosition, setting.meshData.bounds.extents * meshScale + meshPosition);
var isInsideMesh = ShapeHelper.IsPointInsideMesh(position, ref setting.meshData);
while (!isInsideMesh)
{
position = random.NextFloat3(-setting.meshData.bounds.extents * meshScale + meshPosition, setting.meshData.bounds.extents * meshScale + meshPosition);
isInsideMesh = ShapeHelper.IsPointInsideMesh(position, ref setting.meshData);
}
isValid = true;
isValid = ShapeHelper.TryGetPositionInMeshVolume(index, setting.seed, ref setting.meshData, out localMatrix);
break;
case ObjectDistributionMode.Vertex:
if (ShapeHelper.GetMeshVertexPosition(index, ref setting.meshData, out var vertexPosition))
{
position = vertexPosition;
isValid = true;
}
isValid = ShapeHelper.TryGetMeshVertexMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
break;
case ObjectDistributionMode.Edge:
if (ShapeHelper.GetMeshEdgePosition(index, ref setting.meshData, out var edgePosition, out var edgeForward, out var edgeUp))
{
position = edgePosition;
forwardDirection = edgeForward;
upDirection = edgeUp;
isValid = true;
}
isValid = ShapeHelper.TryGetMeshEdgeMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
break;
case ObjectDistributionMode.PolygonCenter:
if (ShapeHelper.GetMeshPolygonPosition(index, ref setting.meshData, out var polygonPosition))
{
position = polygonPosition;
isValid = true;
}
isValid = ShapeHelper.TryGetMeshPolygonMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
break;
default:
break;
}
var rotation = quaternion.LookRotationSafe(forwardDirection, upDirection);
localMatrix = float4x4.TRS(position, rotation, new float3(1.0f));
}
}
}

View File

@@ -0,0 +1,13 @@
using Unity.Mathematics;
namespace Misaki.ArtTool
{
public static partial class Distribution
{
public static void RadialDistribution(int index, RadialDistributionSetting setting, out float4x4 localMatrix, out bool isValid)
{
localMatrix = ShapeHelper.GetRadialMatrix(index, setting);
isValid = true;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c448aeab246b714458fee8084dd0974c

View File

@@ -14,7 +14,7 @@ namespace Misaki.ArtTool
{
var currentPoint = points[index];
var weight = CalculateFieldsWeight(currentPoint.matrix.c3.xyz);
var weight = CalculateFieldsWeight(currentPoint.matrix.GetPosition());
if (weight > 0)
{
for (var i = 0; i < iteration; i++)
@@ -28,10 +28,10 @@ namespace Misaki.ArtTool
var targetPoint = points[p];
var distance = math.distance(currentPoint.matrix.c3.xyz, targetPoint.matrix.c3.xyz);
var distance = math.distance(currentPoint.matrix.GetPosition(), targetPoint.matrix.GetPosition());
if (distance < radius)
{
var direction = math.normalizesafe(currentPoint.matrix.c3.xyz - targetPoint.matrix.c3.xyz);
var direction = math.normalizesafe(currentPoint.matrix.GetPosition() - targetPoint.matrix.GetPosition());
currentPoint.matrix.c3.xyz += (radius - distance) * weight * direction;
//Debug.Log($"Push at index {index} with distance {radius - distance} and direction {direction}");

View File

@@ -0,0 +1,9 @@
namespace Misaki.ArtTool
{
public enum PlaneDirection
{
XY,
ZY,
XZ
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 77fcb516b231dd749b3654e9e1fd1c9f

View File

@@ -4,7 +4,7 @@ namespace Misaki.ArtTool
{
internal class FieldHelper
{
public static float BlendField(float a, float b, float t, BlendingMode blendingMode)
internal static float BlendField(float a, float b, float t, BlendingMode blendingMode)
{
var result = 0.0f;
switch (blendingMode)
@@ -53,7 +53,7 @@ namespace Misaki.ArtTool
return result;
}
public static float ApplyRemapping(float weight, ref RemappingSetting remappingSetting)
internal static float ApplyRemapping(float weight, ref RemappingSetting remappingSetting)
{
if (!remappingSetting.enable)
{

View File

@@ -0,0 +1,12 @@
using Unity.Collections;
namespace Misaki.ArtTool
{
internal class FormulaHelper
{
private void Paras(NativeText inputFormula)
{
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7218fdc62c3673347a29f030a4217533

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
using UnityEngine;
@@ -98,6 +99,20 @@ namespace Misaki.ArtTool
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float3 GetPosition(this float4x4 matrix)
{
return matrix.c3.xyz;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static quaternion GetRotation(this float4x4 matrix)
{
var scale = new float3(math.length(matrix.c0.xyz), math.length(matrix.c1.xyz), math.length(matrix.c2.xyz));
return Quaternion.LookRotation(matrix.c2.xyz / scale.z, matrix.c1.xyz / scale.y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static float3 GetScale(this float4x4 matrix)
{
return new float3(math.length(matrix.c0.xyz), math.length(matrix.c1.xyz), math.length(matrix.c2.xyz));

View File

@@ -12,14 +12,62 @@ namespace Misaki.ArtTool
var zIndex = remain / size.x;
var xIndex = remain % size.x;
localPosition = new float3(xIndex, yIndex, zIndex);
localPosition = new float3((int)xIndex, (int)yIndex, (int)zIndex);
localPosition -= (size - 1) * 0.5f;
return localPosition;
}
internal static bool GetMeshVertexPosition(int index, ref MeshData meshData, out float3 position)
internal static float4x4 GetRadialMatrix(int index, RadialDistributionSetting setting)
{
position = float3.zero;
var staringAngle = math.radians(setting.angleMinMax.x);
var totalAngle = math.TAU / (360.0f / setting.angleMinMax.y) - staringAngle;
var angleStep = totalAngle / setting.count;
var angle = index * angleStep + staringAngle;
var x = math.cos(-angle) * setting.radius;
var y = math.sin(-angle) * setting.radius;
var position = float3.zero;
switch (setting.plane)
{
case PlaneDirection.XY:
position = new float3(x, y, 0);
break;
case PlaneDirection.ZY:
position = new float3(0, x, y);
break;
case PlaneDirection.XZ:
position = new float3(x, 0, y);
break;
default:
break;
}
var rotation = quaternion.identity;
if (setting.align)
{
switch (setting.plane)
{
case PlaneDirection.XY:
rotation = quaternion.EulerXYZ(0.0f, 0.0f, angle);
break;
case PlaneDirection.ZY:
rotation = quaternion.EulerXYZ(angle, 0.0f, 0.0f);
break;
case PlaneDirection.XZ:
rotation = quaternion.EulerXYZ(0.0f, angle, 0.0f);
break;
default:
break;
}
}
return float4x4.TRS(position, rotation, new float3(1.0f));
}
internal static bool TryGetMeshVertexMatrix(int index, bool alignNormal, ref MeshData meshData, out float4x4 outMatrix)
{
outMatrix = float4x4.identity;
if (!meshData.vertices.IsCreated || meshData.vertices.Length <= index)
{
@@ -27,22 +75,32 @@ namespace Misaki.ArtTool
}
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz;
var meshPosition = meshData.worldMatrix.GetPosition();
if (index >= meshData.vertices.Length)
{
return false;
}
position = meshData.vertices[index] * meshScale + meshPosition;
var position = meshData.vertices[index] * meshScale + meshPosition;
var rotation = quaternion.identity;
if (alignNormal)
{
var upDirection = meshData.normals[index];
var forwardDirection = math.normalize(math.cross(upDirection, new float3(-1.0f, 0.0f, 0.0f)));
rotation = quaternion.LookRotation(forwardDirection, upDirection);
}
outMatrix = float4x4.TRS(position, rotation, new float3(1.0f));
return true;
}
internal static bool GetMeshEdgePosition(int index, ref MeshData meshData, out float3 position, out float3 forwardDirection, out float3 upDirection)
internal static bool TryGetMeshEdgeMatrix(int index, bool alignNormal, ref MeshData meshData, out float4x4 outMatrix)
{
position = float3.zero;
forwardDirection = new float3(0.0f, 0.0f, 1.0f);
upDirection = new float3(0.0f, 1.0f, 0.0f);
outMatrix = float4x4.identity;
if (!meshData.edges.IsCreated || index >= meshData.edges.Length)
{
@@ -55,38 +113,38 @@ namespace Misaki.ArtTool
return false;
}
var a = meshData.vertices[edge.x];
var b = meshData.vertices[edge.y];
var position = (a + b) / 2.0f;
var forwardDirection = math.normalize(a - b);
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz;
var meshPosition = meshData.worldMatrix.GetPosition();
position = position * meshScale + meshPosition;
var a = meshData.vertices[edge.x] * meshScale + meshPosition;
var b = meshData.vertices[edge.y] * meshScale + meshPosition;
position = (a + b) / 2.0f;
forwardDirection = a - b;
var interpNormal = math.normalize(meshData.normals[edge.x] + meshData.normals[edge.y]);
upDirection = math.normalize(math.cross(interpNormal, new float3(1.0f, 0.0f, 0.0f)));
if (math.all(upDirection <= float3.zero))
var rotation = quaternion.identity;
if (alignNormal)
{
upDirection = math.normalize(math.cross(interpNormal, new float3(0.0f, 0.0f, 1.0f)));
var upDirection = math.normalize(meshData.normals[edge.x] + meshData.normals[edge.y]);
rotation = quaternion.LookRotation(forwardDirection, upDirection);
}
outMatrix = float4x4.TRS(position, rotation, new float3(1.0f));
return true;
}
internal static bool GetMeshPolygonPosition(int index, ref MeshData meshData, out float3 position)
//TODO : interpolate normal based on distance
internal static bool TryGetMeshPolygonMatrix(int index, bool alignNormal, ref MeshData meshData, out float4x4 outMatrix)
{
position = float3.zero;
outMatrix = float4x4.identity;
if (!meshData.edges.IsCreated || meshData.edges.Length <= index)
{
return false;
}
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz;
var triangleIndex = index * 3;
if (triangleIndex >= meshData.triangles.Length - 2)
@@ -94,15 +152,110 @@ namespace Misaki.ArtTool
return false;
}
var pointIndexA = meshData.triangles[triangleIndex];
var pointIndexB = meshData.triangles[triangleIndex + 1];
var pointIndexC = meshData.triangles[triangleIndex + 2];
var vertexIndexA = meshData.triangles[triangleIndex];
var vertexIndexB = meshData.triangles[triangleIndex + 1];
var vertexIndexC = meshData.triangles[triangleIndex + 2];
var a = meshData.vertices[pointIndexA] * meshScale + meshPosition;
var b = meshData.vertices[pointIndexB] * meshScale + meshPosition;
var c = meshData.vertices[pointIndexC] * meshScale + meshPosition;
var a = meshData.vertices[vertexIndexA];
var b = meshData.vertices[vertexIndexB];
var c = meshData.vertices[vertexIndexC];
position = (a + b + c) / 3.0f;
var position = (a + b + c) / 3.0f;
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.GetPosition();
position = position * meshScale + meshPosition;
var rotation = quaternion.identity;
if (alignNormal)
{
var upDirection = math.normalize(meshData.normals[vertexIndexA] + meshData.normals[vertexIndexB] + meshData.normals[vertexIndexC]);
var forwardDirection = math.normalize(math.cross(upDirection, new float3(-1.0f, 0.0f, 0.0f)));
rotation = quaternion.LookRotation(forwardDirection, upDirection);
}
outMatrix = float4x4.TRS(position, rotation, new float3(1.0f));
return true;
}
//TODO : interpolate normal based on distance
internal static bool TryGetMatrixOnMeshSurface(int index, uint seed, bool alignNormal, ref MeshData meshData, out float4x4 outMatrix)
{
outMatrix = float4x4.identity;
if (!meshData.areas.IsCreated || !meshData.vertices.IsCreated || !meshData.triangles.IsCreated)
{
return false;
}
var random = Random.CreateFromIndex(seed + (uint)index);
var randomValue = random.NextFloat(meshData.totalArea);
var triangleIndex = -1;
for (var j = 0; j < meshData.areas.Length; j++)
{
if (randomValue <= meshData.areas[j])
{
triangleIndex = j * 3;
break;
}
randomValue -= meshData.areas[j];
}
if (triangleIndex >= meshData.triangles.Length - 2)
{
return false;
}
var vertexIndexA = meshData.triangles[triangleIndex];
var vertexIndexB = meshData.triangles[triangleIndex + 1];
var vertexIndexC = meshData.triangles[triangleIndex + 2];
var a = meshData.vertices[vertexIndexA];
var b = meshData.vertices[vertexIndexB];
var c = meshData.vertices[vertexIndexC];
var r1 = math.sqrt(random.NextFloat());
var r2 = random.NextFloat();
var position = (1 - r1) * a + (r1 * (1 - r2)) * b + (r1 * r2) * c;
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.GetPosition();
position = position * meshScale + meshPosition;
var rotation = quaternion.identity;
if (alignNormal)
{
var upDirection = math.normalize(meshData.normals[vertexIndexA] + meshData.normals[vertexIndexB] + meshData.normals[vertexIndexC]);
var forwardDirection = math.normalize(math.cross(upDirection, new float3(-1.0f, 0.0f, 0.0f)));
rotation = quaternion.LookRotation(forwardDirection, upDirection);
}
outMatrix = float4x4.TRS(position, rotation, new float3(1.0f));
return true;
}
internal static bool TryGetPositionInMeshVolume(int index, uint seed, ref MeshData meshData, out float4x4 outMatrix)
{
var random = Random.CreateFromIndex(seed + (uint)index);
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.GetPosition();
var volumePosition = random.NextFloat3(-meshData.bounds.extents * meshScale + meshPosition, meshData.bounds.extents * meshScale + meshPosition);
var isInsideMesh = IsPointInsideMesh(volumePosition, ref meshData);
while (!isInsideMesh)
{
volumePosition = random.NextFloat3(-meshData.bounds.extents * meshScale + meshPosition, meshData.bounds.extents * meshScale + meshPosition);
isInsideMesh = IsPointInsideMesh(volumePosition, ref meshData);
}
outMatrix = float4x4.TRS(volumePosition, quaternion.identity, new float3(1.0f));
return true;
}
}

View File

@@ -45,7 +45,7 @@ namespace Misaki.ArtTool
{
var windingNumber = 0;
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz;
var meshPosition = meshData.worldMatrix.GetPosition();
for (var i = 0; i < meshData.triangles.Length; i += 3)
{

View File

@@ -17,6 +17,7 @@ namespace Misaki.ArtTool
public SplineDistributionSetting splineDistributionSetting;
public LinearDistributionSetting linearDistributionSetting;
public GridDistributionSetting gridDistributionSetting;
public RadialDistributionSetting radialDistributionSetting;
[WriteOnly]
public NativeArray<PointData> points;
@@ -30,7 +31,7 @@ namespace Misaki.ArtTool
switch (distributionMode)
{
case DistributionMode.Object:
Distribution.ObjectDistribution(i, objectDistributionSetting, out pointMatrix, out isValid);
Distribution.ObjectDistribution(i, ref objectDistributionSetting, out pointMatrix, out isValid);
break;
case DistributionMode.Spline:
Distribution.SplineDistribution(i, pointSize, splineDistributionSetting, out pointMatrix, out isValid);
@@ -42,6 +43,7 @@ namespace Misaki.ArtTool
Distribution.GridDistribution(i, gridDistributionSetting, out pointMatrix, out isValid);
break;
case DistributionMode.Radial:
Distribution.RadialDistribution(i, radialDistributionSetting, out pointMatrix, out isValid);
break;
case DistributionMode.Honeycomb:
break;

View File

@@ -9,7 +9,7 @@ namespace Misaki.ArtTool
public ObjectDistributionMode mode;
public uint count;
public uint seed;
public bool alignNormal;
public bool isAlignNormal;
public int DistributionCount
{

View File

@@ -0,0 +1,15 @@
using System;
using Unity.Mathematics;
namespace Misaki.ArtTool
{
[Serializable]
public struct RadialDistributionSetting
{
public uint count;
public float radius;
public PlaneDirection plane;
public float2 angleMinMax;
public bool align;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 577cc0a461b273d42be5e22ecc600d8a

View File

@@ -17,6 +17,10 @@ namespace Misaki.ArtTool
public NativeArray<float3> vertices;
[ReadOnly]
public NativeList<int2> edges;
[ReadOnly]
public NativeArray<float> areas;
public float totalArea;
public int vertexCount;
@@ -31,6 +35,9 @@ namespace Misaki.ArtTool
vertices = new(0, allocator);
edges = new(0, allocator);
areas = new(0, allocator);
totalArea = 0.0f;
vertexCount = 0;
worldMatrix = float4x4.identity;
}
@@ -69,17 +76,37 @@ namespace Misaki.ArtTool
AddEdge(edges, triangles[i + 2], triangles[i]);
}
areas = new(mesh.triangles.Length / 3, allocator);
totalArea = 0.0f;
for (var i = 0; i < triangles.Length; i += 3)
{
Vector3 v0 = vertices[triangles[i]];
Vector3 v1 = vertices[triangles[i + 1]];
Vector3 v2 = vertices[triangles[i + 2]];
var area = Vector3.Cross(v1 - v0, v2 - v0).magnitude * 0.5f;
areas[i / 3] = area;
totalArea += area;
}
worldMatrix = meshFilter.transform.localToWorldMatrix;
static void AddEdge(NativeList<int2> edges, int a, int b)
{
if (a < b)
{
edges.Add(new int2(a, b));
var edge = new int2(a, b);
if (!edges.Contains(edge))
{
edges.Add(edge);
}
}
else
{
edges.Add(new int2(b, a));
var edge = new int2(b, a);
if (!edges.Contains(edge))
{
edges.Add(edge);
}
}
}
}
@@ -90,6 +117,7 @@ namespace Misaki.ArtTool
normals.Dispose();
vertices.Dispose();
edges.Dispose();
areas.Dispose();
}
}
}

View File

@@ -4,38 +4,9 @@ using Unity.Mathematics;
namespace Misaki.ArtTool
{
[Serializable]
public struct PointData : IEquatable<PointData>
public struct PointData
{
public bool isValid;
public float4x4 matrix;
public bool Equals(PointData other)
{
return isValid == other.isValid && Equals(matrix, other.matrix);
}
public override bool Equals(object obj)
{
if (obj is PointData other)
{
return Equals(other);
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(isValid, matrix);
}
public static bool operator ==(PointData left, PointData right)
{
return left.Equals(right);
}
public static bool operator !=(PointData left, PointData right)
{
return !(left == right);
}
}
}