Finished the code for radial distribution
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Runtime/Cloner/Distribution/RadialDistribution.cs
Normal file
13
Runtime/Cloner/Distribution/RadialDistribution.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/Cloner/Distribution/RadialDistribution.cs.meta
Normal file
2
Runtime/Cloner/Distribution/RadialDistribution.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c448aeab246b714458fee8084dd0974c
|
||||
@@ -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}");
|
||||
|
||||
9
Runtime/Cloner/Enums/PlaneDirection.cs
Normal file
9
Runtime/Cloner/Enums/PlaneDirection.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Misaki.ArtTool
|
||||
{
|
||||
public enum PlaneDirection
|
||||
{
|
||||
XY,
|
||||
ZY,
|
||||
XZ
|
||||
}
|
||||
}
|
||||
2
Runtime/Cloner/Enums/PlaneDirection.cs.meta
Normal file
2
Runtime/Cloner/Enums/PlaneDirection.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77fcb516b231dd749b3654e9e1fd1c9f
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
12
Runtime/Cloner/Helper/FormulaHelper.cs
Normal file
12
Runtime/Cloner/Helper/FormulaHelper.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Misaki.ArtTool
|
||||
{
|
||||
internal class FormulaHelper
|
||||
{
|
||||
private void Paras(NativeText inputFormula)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Runtime/Cloner/Helper/FormulaHelper.cs.meta
Normal file
2
Runtime/Cloner/Helper/FormulaHelper.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7218fdc62c3673347a29f030a4217533
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 577cc0a461b273d42be5e22ecc600d8a
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user