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

@@ -0,0 +1,87 @@
using UnityEngine;
using UnityEngine.UIElements;
namespace Misaki.ArtToolEditor
{
public enum HeaderSize
{
Small,
Medium,
Large
}
[UxmlElement]
public partial class PropertyGroup : VisualElement
{
private string _headerText = "Property Header";
[UxmlAttribute]
public string HeaderText
{
get => _headerText;
set
{
_headerText = value;
header.text = value;
}
}
private HeaderSize _headerSize = HeaderSize.Medium;
[UxmlAttribute]
public HeaderSize HeaderSize
{
get => _headerSize;
set
{
_headerSize = value;
SetHeaderStyle(value);
}
}
private readonly Label header;
private readonly VisualElement propertyContainer;
public PropertyGroup()
{
header = new Label(_headerText) { name = "header" };
SetHeaderStyle(_headerSize);
propertyContainer = new VisualElement() { name = "property-content" };
propertyContainer.style.marginLeft = 8;
hierarchy.Add(header);
hierarchy.Add(propertyContainer);
}
public override VisualElement contentContainer => propertyContainer;
private void SetHeaderStyle(HeaderSize size)
{
switch (size)
{
case HeaderSize.Small:
header.style.marginTop = 6;
header.style.marginBottom = 4;
header.style.fontSize = 11;
break;
case HeaderSize.Medium:
header.style.marginTop = 8;
header.style.marginBottom = 6;
header.style.fontSize = 12;
break;
case HeaderSize.Large:
header.style.marginTop = 16;
header.style.marginBottom = 12;
header.style.fontSize = 16;
break;
default:
break;
}
header.style.unityFontStyleAndWeight = FontStyle.Bold;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 61c279d7ffcfe1f4e8aa19345cd14e32

View File

@@ -8,7 +8,7 @@
<engine:Label text="Object" enable-rich-text="false" style="flex-grow: 1; margin-left: 28px; font-size: 9px; -unity-text-align: middle-left;" /> <engine:Label text="Object" enable-rich-text="false" style="flex-grow: 1; margin-left: 28px; font-size: 9px; -unity-text-align: middle-left;" />
<engine:Label text="Frequncy" enable-rich-text="false" name="Label" style="width: 50px; font-size: 9px; -unity-text-align: middle-left;" /> <engine:Label text="Frequncy" enable-rich-text="false" name="Label" style="width: 50px; font-size: 9px; -unity-text-align: middle-left;" />
</engine:VisualElement> </engine:VisualElement>
<engine:ListView reorderable="true" reorder-mode="Animated" show-border="true" show-add-remove-footer="true" item-template="project://database/Packages/com.misaki.art-tools/Editor/Cloner/InputObjectItemView.uxml?fileID=9197481963319205126&amp;guid=af925948194d82c44a565c55d8ce0de8&amp;type=3#InputObjectItemView" name="input-objects-listview" binding-source-selection-mode="AutoAssign"> <engine:ListView reorderable="true" reorder-mode="Animated" show-border="true" show-add-remove-footer="true" name="input-objects-listview" binding-source-selection-mode="AutoAssign" item-template="project://database/Packages/com.misaki.art-tools/Editor/Cloner/View/ItemTemplate/InputObjectItemTemplate.uxml?fileID=9197481963319205126&amp;guid=af925948194d82c44a565c55d8ce0de8&amp;type=3#InputObjectItemTemplate">
<Bindings> <Bindings>
<engine:DataBinding property="itemsSource" data-source-path="inputObjects" binding-mode="TwoWay" /> <engine:DataBinding property="itemsSource" data-source-path="inputObjects" binding-mode="TwoWay" />
</Bindings> </Bindings>
@@ -39,7 +39,7 @@
<engine:DataBinding property="index" data-source-path="autoGenerate" binding-mode="TwoWay" /> <engine:DataBinding property="index" data-source-path="autoGenerate" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:DropdownField> </engine:DropdownField>
<engine:DropdownField label="Render Mode" choices="Gameobject,Instancing" index="0" name="render-mode-dropdown"> <engine:DropdownField label="Instantiate Mode" choices="GameObject,Instancing" index="0" name="render-mode-dropdown">
<Bindings> <Bindings>
<engine:DataBinding property="index" data-source-path="isRenderInstancing" binding-mode="TwoWay" /> <engine:DataBinding property="index" data-source-path="isRenderInstancing" binding-mode="TwoWay" />
</Bindings> </Bindings>
@@ -72,7 +72,7 @@
</engine:UnsignedIntegerField> </engine:UnsignedIntegerField>
<engine:DropdownField label="Align Normal" choices="Disable,Enable" index="0"> <engine:DropdownField label="Align Normal" choices="Disable,Enable" index="0">
<Bindings> <Bindings>
<engine:DataBinding property="index" data-source-path="objectDistributionSetting.alignNormal" binding-mode="TwoWay" /> <engine:DataBinding property="index" data-source-path="objectDistributionSetting.isAlignNormal" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:DropdownField> </engine:DropdownField>
<Bindings> <Bindings>
@@ -124,22 +124,22 @@
</engine:IntegerField> </engine:IntegerField>
<engine:Vector3Field label="Position Spacing"> <engine:Vector3Field label="Position Spacing">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="linearDistributionSetting.positionSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="linearDistributionSetting.positionSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" ui-to-source-converters="Float3ToVector3Converter" />
</Bindings> </Bindings>
</engine:Vector3Field> </engine:Vector3Field>
<engine:Vector3Field label="Rotation Spacing"> <engine:Vector3Field label="Rotation Spacing">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="linearDistributionSetting.scaleSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="linearDistributionSetting.scaleSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" ui-to-source-converters="Float3ToVector3Converter" />
</Bindings> </Bindings>
</engine:Vector3Field> </engine:Vector3Field>
<engine:Vector3Field label="Scale Spacing"> <engine:Vector3Field label="Scale Spacing">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="linearDistributionSetting.scaleSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="linearDistributionSetting.scaleSpacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" ui-to-source-converters="Float3ToVector3Converter" />
</Bindings> </Bindings>
</engine:Vector3Field> </engine:Vector3Field>
<engine:Vector3Field label="Step Rotation"> <engine:Vector3Field label="Step Rotation">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="linearDistributionSetting.stepRotation" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="linearDistributionSetting.stepRotation" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" ui-to-source-converters="Float3ToVector3Converter" />
</Bindings> </Bindings>
</engine:Vector3Field> </engine:Vector3Field>
<Bindings> <Bindings>
@@ -149,12 +149,12 @@
<engine:VisualElement name="grid-parameter" style="flex-grow: 1;"> <engine:VisualElement name="grid-parameter" style="flex-grow: 1;">
<engine:Vector3IntField label="Count"> <engine:Vector3IntField label="Count">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="gridDistributionSetting.count" binding-mode="TwoWay" source-to-ui-converters="int3ToVector3IntConverter" /> <engine:DataBinding property="value" data-source-path="gridDistributionSetting.count" binding-mode="TwoWay" source-to-ui-converters="int3ToVector3IntConverter" ui-to-source-converters="int3ToVector3IntConverter" />
</Bindings> </Bindings>
</engine:Vector3IntField> </engine:Vector3IntField>
<engine:Vector3Field label="Spacing"> <engine:Vector3Field label="Spacing">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="gridDistributionSetting.spacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="gridDistributionSetting.spacing" source-to-ui-converters="Float3ToVector3Converter" binding-mode="TwoWay" ui-to-source-converters="Float3ToVector3Converter" />
</Bindings> </Bindings>
</engine:Vector3Field> </engine:Vector3Field>
<engine:EnumField label="Shape" value="Center" type="Misaki.ArtTool.GridShape, Misaki.ArtTool"> <engine:EnumField label="Shape" value="Center" type="Misaki.ArtTool.GridShape, Misaki.ArtTool">
@@ -171,12 +171,47 @@
<engine:DataBinding property="style.display" data-source-path="distributionMode" binding-mode="ToTarget" source-to-ui-converters="GridModeToDisplayStyleConverter" /> <engine:DataBinding property="style.display" data-source-path="distributionMode" binding-mode="ToTarget" source-to-ui-converters="GridModeToDisplayStyleConverter" />
</Bindings> </Bindings>
</engine:VisualElement> </engine:VisualElement>
<engine:VisualElement name="radial-parameter" style="flex-grow: 1;">
<engine:UnsignedIntegerField label="Count" value="5">
<Bindings>
<engine:DataBinding property="value" data-source-path="radialDistributionSetting.count" binding-mode="TwoWay" />
</Bindings>
</engine:UnsignedIntegerField>
<engine:FloatField label="Radius" value="5">
<Bindings>
<engine:DataBinding property="value" data-source-path="radialDistributionSetting.radius" binding-mode="TwoWay" />
</Bindings>
</engine:FloatField>
<engine:EnumField label="Plane" value="XZ" type="Misaki.ArtTool.PlaneDirection, Misaki.ArtTool">
<Bindings>
<engine:DataBinding property="value" data-source-path="radialDistributionSetting.plane" binding-mode="TwoWay" />
</Bindings>
</engine:EnumField>
<engine:FloatField label="Start Angle" value="0">
<Bindings>
<engine:DataBinding property="value" data-source-path="radialDistributionSetting.angleMinMax.x" binding-mode="TwoWay" />
</Bindings>
</engine:FloatField>
<engine:FloatField label="End Angle" value="360">
<Bindings>
<engine:DataBinding property="value" data-source-path="radialDistributionSetting.angleMinMax.y" binding-mode="TwoWay" />
</Bindings>
</engine:FloatField>
<engine:DropdownField label="Align" choices="Disable,Enable" index="0">
<Bindings>
<engine:DataBinding property="index" data-source-path="radialDistributionSetting.align" binding-mode="TwoWay" />
</Bindings>
</engine:DropdownField>
<Bindings>
<engine:DataBinding property="style.display" data-source-path="distributionMode" binding-mode="ToTarget" source-to-ui-converters="RadialModeToDisplayStyleConverter" />
</Bindings>
</engine:VisualElement>
</engine:VisualElement> </engine:VisualElement>
</engine:VisualElement> </engine:VisualElement>
<engine:VisualElement> <engine:VisualElement>
<engine:Label text="Effectors" class="SubHeader" /> <engine:Label text="Effectors" class="SubHeader" />
<engine:VisualElement class="PropertyContainer"> <engine:VisualElement class="PropertyContainer">
<engine:ListView reorderable="true" reorder-mode="Animated" show-border="true" show-add-remove-footer="true" item-template="project://database/Packages/com.misaki.art-tools/Editor/Cloner/EffectorItemTemplate.uxml?fileID=9197481963319205126&amp;guid=9a72cc6a25b7e104991d25d5c2458b81&amp;type=3#EffectorItemTemplate" name="effectors-listview" binding-source-selection-mode="AutoAssign"> <engine:ListView reorderable="true" reorder-mode="Animated" show-border="true" show-add-remove-footer="true" name="effectors-listview" binding-source-selection-mode="AutoAssign" item-template="project://database/Packages/com.misaki.art-tools/Editor/Cloner/View/ItemTemplate/EffectorItemTemplate.uxml?fileID=9197481963319205126&amp;guid=9a72cc6a25b7e104991d25d5c2458b81&amp;type=3#EffectorItemTemplate">
<Bindings> <Bindings>
<engine:DataBinding property="itemsSource" data-source-path="effectors" binding-mode="TwoWay" /> <engine:DataBinding property="itemsSource" data-source-path="effectors" binding-mode="TwoWay" />
</Bindings> </Bindings>
@@ -191,6 +226,16 @@
<engine:DataBinding property="value" data-source-path="debugMode" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="debugMode" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:EnumField> </engine:EnumField>
<engine:FloatField label="Debug Size" value="0.5">
<Bindings>
<engine:DataBinding property="value" data-source-path="gizmosSize" binding-mode="TwoWay" />
</Bindings>
</engine:FloatField>
<engine:IntegerField label="Point Count" value="42" enabled="false">
<Bindings>
<engine:DataBinding property="value" data-source-path="pointSize" binding-mode="ToTarget" />
</Bindings>
</engine:IntegerField>
</engine:VisualElement> </engine:VisualElement>
</engine:VisualElement> </engine:VisualElement>
<engine:VisualElement> <engine:VisualElement>

View File

@@ -1,19 +1,14 @@
<engine:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:engine="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True"> <engine:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:engine="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<Style src="project://database/Packages/com.misaki.art-tools/Editor/Cloner/View/ClonerEditorStyle.uss?fileID=7433441132597879392&amp;guid=216b892cd94cc624da01dc4947facdcb&amp;type=3#ClonerEditorStyle" /> <Style src="project://database/Packages/com.misaki.art-tools/Editor/Cloner/View/ClonerEditorStyle.uss?fileID=7433441132597879392&amp;guid=216b892cd94cc624da01dc4947facdcb&amp;type=3#ClonerEditorStyle" />
<engine:VisualElement data-source-type="Misaki.ArtTool.PushApartEffector, Misaki.ArtTool" style="flex-grow: 1;"> <engine:VisualElement data-source-type="Misaki.ArtTool.PushApartEffector, Misaki.ArtTool" style="flex-grow: 1;">
<engine:VisualElement> <Misaki.ArtToolEditor.PropertyGroup header-text="Effector">
<engine:Label text="Effector" class="SubHeader" />
<engine:VisualElement class="PropertyContainer">
<engine:Slider label="Strength" value="42" high-value="1" fill="true" show-input-field="true"> <engine:Slider label="Strength" value="42" high-value="1" fill="true" show-input-field="true">
<Bindings> <Bindings>
<engine:DataBinding property="value" data-source-path="strength" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="strength" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:Slider> </engine:Slider>
</engine:VisualElement> </Misaki.ArtToolEditor.PropertyGroup>
</engine:VisualElement> <Misaki.ArtToolEditor.PropertyGroup header-text="Parameter" name="PropertyGroup">
<engine:VisualElement>
<engine:Label text="Parameter" class="SubHeader" />
<engine:VisualElement class="PropertyContainer">
<engine:DropdownField label="Mode" choices="Push,Hide" index="0"> <engine:DropdownField label="Mode" choices="Push,Hide" index="0">
<Bindings> <Bindings>
<engine:DataBinding property="index" data-source-path="isHideMode" binding-mode="TwoWay" /> <engine:DataBinding property="index" data-source-path="isHideMode" binding-mode="TwoWay" />
@@ -29,11 +24,8 @@
<engine:DataBinding property="value" data-source-path="iteration" binding-mode="TwoWay" /> <engine:DataBinding property="value" data-source-path="iteration" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:UnsignedIntegerField> </engine:UnsignedIntegerField>
</engine:VisualElement> </Misaki.ArtToolEditor.PropertyGroup>
</engine:VisualElement> <Misaki.ArtToolEditor.PropertyGroup header-text="Fields">
<engine:VisualElement>
<engine:Label text="Fields" class="SubHeader" />
<engine:VisualElement class="PropertyContainer" style="flex-grow: 1;">
<engine:VisualElement style="flex-grow: 1; flex-direction: row; margin-top: 4px; margin-right: 4px; margin-bottom: 8px; margin-left: 4px;"> <engine:VisualElement style="flex-grow: 1; flex-direction: row; margin-top: 4px; margin-right: 4px; margin-bottom: 8px; margin-left: 4px;">
<engine:Label text="Enable" enable-rich-text="false" style="font-size: 9px; -unity-text-align: middle-left; margin-left: 4px;" /> <engine:Label text="Enable" enable-rich-text="false" style="font-size: 9px; -unity-text-align: middle-left; margin-left: 4px;" />
<engine:Label text="Field" enable-rich-text="false" style="flex-grow: 1; margin-left: 12px; font-size: 9px; -unity-text-align: middle-left;" /> <engine:Label text="Field" enable-rich-text="false" style="flex-grow: 1; margin-left: 12px; font-size: 9px; -unity-text-align: middle-left;" />
@@ -44,7 +36,6 @@
<engine:DataBinding property="itemsSource" data-source-path="fieldDataList" binding-mode="TwoWay" /> <engine:DataBinding property="itemsSource" data-source-path="fieldDataList" binding-mode="TwoWay" />
</Bindings> </Bindings>
</engine:ListView> </engine:ListView>
</engine:VisualElement> </Misaki.ArtToolEditor.PropertyGroup>
</engine:VisualElement>
</engine:VisualElement> </engine:VisualElement>
</engine:UXML> </engine:UXML>

View File

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

View File

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

View File

@@ -1,80 +1,39 @@
using Unity.Mathematics; using Unity.Mathematics;
using Random = Unity.Mathematics.Random;
namespace Misaki.ArtTool namespace Misaki.ArtTool
{ {
public static partial class Distribution 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; isValid = false;
localMatrix = float4x4.identity;
switch (setting.mode) switch (setting.mode)
{ {
case ObjectDistributionMode.Surface: case ObjectDistributionMode.Surface:
isValid = ShapeHelper.TryGetMatrixOnMeshSurface(index, setting.seed, setting.isAlignNormal, ref setting.meshData, out localMatrix);
break; break;
case ObjectDistributionMode.Volume: case ObjectDistributionMode.Volume:
isValid = ShapeHelper.TryGetPositionInMeshVolume(index, setting.seed, ref setting.meshData, out localMatrix);
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;
break; break;
case ObjectDistributionMode.Vertex: case ObjectDistributionMode.Vertex:
if (ShapeHelper.GetMeshVertexPosition(index, ref setting.meshData, out var vertexPosition)) isValid = ShapeHelper.TryGetMeshVertexMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
{
position = vertexPosition;
isValid = true;
}
break; break;
case ObjectDistributionMode.Edge: case ObjectDistributionMode.Edge:
if (ShapeHelper.GetMeshEdgePosition(index, ref setting.meshData, out var edgePosition, out var edgeForward, out var edgeUp)) isValid = ShapeHelper.TryGetMeshEdgeMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
{
position = edgePosition;
forwardDirection = edgeForward;
upDirection = edgeUp;
isValid = true;
}
break; break;
case ObjectDistributionMode.PolygonCenter: case ObjectDistributionMode.PolygonCenter:
if (ShapeHelper.GetMeshPolygonPosition(index, ref setting.meshData, out var polygonPosition)) isValid = ShapeHelper.TryGetMeshPolygonMatrix(index, setting.isAlignNormal, ref setting.meshData, out localMatrix);
{
position = polygonPosition;
isValid = true;
}
break; break;
default: default:
break; 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 currentPoint = points[index];
var weight = CalculateFieldsWeight(currentPoint.matrix.c3.xyz); var weight = CalculateFieldsWeight(currentPoint.matrix.GetPosition());
if (weight > 0) if (weight > 0)
{ {
for (var i = 0; i < iteration; i++) for (var i = 0; i < iteration; i++)
@@ -28,10 +28,10 @@ namespace Misaki.ArtTool
var targetPoint = points[p]; 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) 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; currentPoint.matrix.c3.xyz += (radius - distance) * weight * direction;
//Debug.Log($"Push at index {index} with distance {radius - distance} and direction {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 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; var result = 0.0f;
switch (blendingMode) switch (blendingMode)
@@ -53,7 +53,7 @@ namespace Misaki.ArtTool
return result; return result;
} }
public static float ApplyRemapping(float weight, ref RemappingSetting remappingSetting) internal static float ApplyRemapping(float weight, ref RemappingSetting remappingSetting)
{ {
if (!remappingSetting.enable) 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Mathematics; using Unity.Mathematics;
using UnityEngine; 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) 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)); 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 zIndex = remain / size.x;
var xIndex = 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; localPosition -= (size - 1) * 0.5f;
return localPosition; 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) if (!meshData.vertices.IsCreated || meshData.vertices.Length <= index)
{ {
@@ -27,22 +75,32 @@ namespace Misaki.ArtTool
} }
var meshScale = meshData.worldMatrix.GetScale(); var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz; var meshPosition = meshData.worldMatrix.GetPosition();
if (index >= meshData.vertices.Length) if (index >= meshData.vertices.Length)
{ {
return false; 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; 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; outMatrix = float4x4.identity;
forwardDirection = new float3(0.0f, 0.0f, 1.0f);
upDirection = new float3(0.0f, 1.0f, 0.0f);
if (!meshData.edges.IsCreated || index >= meshData.edges.Length) if (!meshData.edges.IsCreated || index >= meshData.edges.Length)
{ {
@@ -55,38 +113,38 @@ namespace Misaki.ArtTool
return false; 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 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 rotation = quaternion.identity;
var b = meshData.vertices[edge.y] * meshScale + meshPosition; if (alignNormal)
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))
{ {
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; 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) if (!meshData.edges.IsCreated || meshData.edges.Length <= index)
{ {
return false; return false;
} }
var meshScale = meshData.worldMatrix.GetScale();
var meshPosition = meshData.worldMatrix.c3.xyz;
var triangleIndex = index * 3; var triangleIndex = index * 3;
if (triangleIndex >= meshData.triangles.Length - 2) if (triangleIndex >= meshData.triangles.Length - 2)
@@ -94,15 +152,110 @@ namespace Misaki.ArtTool
return false; return false;
} }
var pointIndexA = meshData.triangles[triangleIndex]; var vertexIndexA = meshData.triangles[triangleIndex];
var pointIndexB = meshData.triangles[triangleIndex + 1]; var vertexIndexB = meshData.triangles[triangleIndex + 1];
var pointIndexC = meshData.triangles[triangleIndex + 2]; var vertexIndexC = meshData.triangles[triangleIndex + 2];
var a = meshData.vertices[pointIndexA] * meshScale + meshPosition; var a = meshData.vertices[vertexIndexA];
var b = meshData.vertices[pointIndexB] * meshScale + meshPosition; var b = meshData.vertices[vertexIndexB];
var c = meshData.vertices[pointIndexC] * meshScale + meshPosition; 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; return true;
} }
} }

View File

@@ -45,7 +45,7 @@ namespace Misaki.ArtTool
{ {
var windingNumber = 0; var windingNumber = 0;
var meshScale = meshData.worldMatrix.GetScale(); 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) for (var i = 0; i < meshData.triangles.Length; i += 3)
{ {

View File

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

View File

@@ -9,7 +9,7 @@ namespace Misaki.ArtTool
public ObjectDistributionMode mode; public ObjectDistributionMode mode;
public uint count; public uint count;
public uint seed; public uint seed;
public bool alignNormal; public bool isAlignNormal;
public int DistributionCount 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; public NativeArray<float3> vertices;
[ReadOnly] [ReadOnly]
public NativeList<int2> edges; public NativeList<int2> edges;
[ReadOnly]
public NativeArray<float> areas;
public float totalArea;
public int vertexCount; public int vertexCount;
@@ -31,6 +35,9 @@ namespace Misaki.ArtTool
vertices = new(0, allocator); vertices = new(0, allocator);
edges = new(0, allocator); edges = new(0, allocator);
areas = new(0, allocator);
totalArea = 0.0f;
vertexCount = 0; vertexCount = 0;
worldMatrix = float4x4.identity; worldMatrix = float4x4.identity;
} }
@@ -69,17 +76,37 @@ namespace Misaki.ArtTool
AddEdge(edges, triangles[i + 2], triangles[i]); 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; worldMatrix = meshFilter.transform.localToWorldMatrix;
static void AddEdge(NativeList<int2> edges, int a, int b) static void AddEdge(NativeList<int2> edges, int a, int b)
{ {
if (a < b) if (a < b)
{ {
edges.Add(new int2(a, b)); var edge = new int2(a, b);
if (!edges.Contains(edge))
{
edges.Add(edge);
}
} }
else 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(); normals.Dispose();
vertices.Dispose(); vertices.Dispose();
edges.Dispose(); edges.Dispose();
areas.Dispose();
} }
} }
} }

View File

@@ -4,38 +4,9 @@ using Unity.Mathematics;
namespace Misaki.ArtTool namespace Misaki.ArtTool
{ {
[Serializable] [Serializable]
public struct PointData : IEquatable<PointData> public struct PointData
{ {
public bool isValid; public bool isValid;
public float4x4 matrix; 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);
}
} }
} }