1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using Elements . Assets ;
5+ using Elements . Core ;
6+ using Elements . Data ;
7+ using FrooxEngine ;
8+ using Obsidian . Components . Utility ;
9+
10+ namespace Obsidian . Assets . ProceduralMeshes ;
11+
12+ [ OldTypeName ( "Obsidian.Components.Tools.EditableMesh" ) ]
13+ [ Category ( "Obsidian/Assets/Procedural Meshes" ) ]
14+ public class EditableMesh : ProceduralMesh
15+ {
16+ private const float EPSILON = 0.001f ;
17+
18+ protected readonly AssetRef < Mesh > _sourceMesh ;
19+ protected readonly SyncRef < Slot > _controlPointsSlot ;
20+ protected readonly SyncRef < MeshCollider > _newCollider ;
21+ protected readonly SyncRef < ICollider > _originalCollider ;
22+ protected readonly SyncRef < UnlitMaterial > _controlPointMaterial ;
23+
24+ private MeshX storedMeshX ;
25+ private List < MergedVertexData > mergedVertexData ;
26+
27+ private class MergedVertexData
28+ {
29+ public float3 pos ;
30+ public HashSet < Vertex > vertices = new ( ) ;
31+ }
32+
33+ private void DebugLog ( string msg )
34+ {
35+ UniLog . Log ( $ "[EditableMesh { this . ReferenceID . ToString ( ) } ] { msg } ") ;
36+ }
37+
38+ protected override void OnDestroy ( )
39+ {
40+ base . OnDestroy ( ) ;
41+
42+ _controlPointsSlot . Target ? . Destroy ( ) ;
43+
44+ _controlPointMaterial . Target ? . Destroy ( ) ;
45+
46+ // If this ProceduralMesh was baked, nothing should be referencing it anymore
47+
48+ DebugLog ( $ "OnDestroy. AssetReferenceCount: { AssetReferenceCount } ") ;
49+
50+ foreach ( var reference in References )
51+ {
52+ DebugLog ( $ "Reference: { reference . Parent . Name } ") ;
53+ }
54+
55+ if ( AssetReferenceCount != 0 )
56+ {
57+ // EditableMesh wasn't baked.
58+
59+ DebugLog ( $ "Wasn't baked.") ;
60+
61+ _newCollider . Target ? . Destroy ( ) ;
62+
63+ foreach ( var reference in References )
64+ {
65+ reference . Target = _sourceMesh . Target ;
66+ }
67+
68+ if ( _originalCollider . Target != null )
69+ {
70+ _originalCollider . Target . Enabled = true ;
71+ }
72+ }
73+ else
74+ {
75+ // EditableMesh has most likely been baked because nothing references it
76+
77+ DebugLog ( $ "Baked.") ;
78+
79+ _originalCollider . Target ? . Destroy ( ) ;
80+
81+ if ( _sourceMesh . Target != null )
82+ {
83+ var assetSlot = World . AssetsSlot . AddSlot ( "EditableMesh Original Mesh" ) ;
84+ assetSlot . MoveComponent ( ( Component ) _sourceMesh . Target ) ;
85+ }
86+ }
87+ }
88+
89+ protected override void OnStart ( )
90+ {
91+ //_sourceMesh.OnTargetChange += (syncRef) =>
92+ //{
93+ // Handle mesh changes in here?
94+ // Should regen control points and clear mesh data
95+ //};
96+
97+ //if (_sourceMesh.Asset != null && _controlPointsSlot.Target is null)
98+ //{
99+ //InitControlPoints();
100+ //}
101+ }
102+
103+ public void Setup ( IAssetProvider < Mesh > meshProvider )
104+ {
105+ if ( meshProvider == null )
106+ {
107+ throw new ArgumentNullException ( nameof ( meshProvider ) ) ;
108+ }
109+ if ( meshProvider . Asset == null )
110+ {
111+ throw new ArgumentException ( "Provided mesh asset is null!" ) ;
112+ }
113+ _sourceMesh . Target = meshProvider ;
114+ _controlPointMaterial . Target = Slot . AttachComponent < UnlitMaterial > ( ) ;
115+ _controlPointMaterial . Target . TintColor . Value = colorX . Green ;
116+ InitControlPoints ( ) ;
117+ }
118+
119+ public void SetColliders ( ICollider originalCollider , MeshCollider newCollider )
120+ {
121+ if ( originalCollider == null )
122+ {
123+ throw new ArgumentNullException ( $ "{ nameof ( originalCollider ) } is null!") ;
124+ }
125+ if ( newCollider == null )
126+ {
127+ throw new ArgumentNullException ( $ "{ nameof ( newCollider ) } is null!") ;
128+ }
129+ if ( newCollider . Mesh . Target != this )
130+ {
131+ throw new ArgumentException ( $ "{ nameof ( newCollider ) } doesn't target this EditableMesh!") ;
132+ }
133+ _originalCollider . Target = originalCollider ;
134+ _newCollider . Target = newCollider ;
135+ }
136+
137+ private void InitControlPoints ( )
138+ {
139+ var mergedVertexData = CollectMergedVertexData ( _sourceMesh . Asset . Data ) ;
140+
141+ _controlPointsSlot . Target = Slot . AddSlot ( "EditableMesh Control Points" ) ;
142+
143+ for ( int i = 0 ; i < mergedVertexData . Count ; i ++ )
144+ {
145+ var data = mergedVertexData [ i ] ;
146+
147+ var vertSlot = _controlPointsSlot . Target . AddSlot ( $ "ControlPoint{ i } ") ;
148+ vertSlot . ChildIndex = i ;
149+ vertSlot . LocalPosition = data . pos ;
150+ vertSlot . AttachSphere ( 0.01f , _controlPointMaterial . Target ) ;
151+ vertSlot . AttachComponent < Slider > ( ) ;
152+ var controlPointComp = vertSlot . AttachComponent < EditableMeshControlPoint > ( ) ;
153+ controlPointComp . Setup ( this ) ;
154+ }
155+ }
156+
157+ protected override void ClearMeshData ( )
158+ {
159+ storedMeshX ? . Clear ( ) ;
160+ storedMeshX = null ;
161+ mergedVertexData ? . Clear ( ) ;
162+ mergedVertexData = null ;
163+ }
164+
165+ private static bool ShouldMerge ( float3 p1 , float3 p2 )
166+ {
167+ return MathX . Approximately ( p1 , p2 , EPSILON ) ;
168+ }
169+
170+ private static List < MergedVertexData > CollectMergedVertexData ( MeshX mesh )
171+ {
172+ var result = new List < MergedVertexData > ( ) ;
173+ for ( int i = 0 ; i < mesh . VertexCount ; i ++ )
174+ {
175+ var vert = mesh . GetVertex ( i ) ;
176+ MergedVertexData data = result . FirstOrDefault ( d => ShouldMerge ( d . pos , vert . Position ) ) ;
177+ if ( data == null )
178+ {
179+ data = new MergedVertexData ( ) ;
180+ data . pos = vert . Position ;
181+ result . Add ( data ) ;
182+ }
183+ else
184+ {
185+ continue ;
186+ }
187+ data . vertices . Add ( vert ) ;
188+ for ( int j = 0 ; j < mesh . VertexCount ; j ++ )
189+ {
190+ var vert2 = mesh . GetVertex ( j ) ;
191+ if ( vert . Index == vert2 . Index ) continue ;
192+ if ( ShouldMerge ( vert . Position , vert2 . Position ) )
193+ {
194+ data . vertices . Add ( vert2 ) ;
195+ }
196+ }
197+ }
198+ return result ;
199+ }
200+
201+ protected override void UpdateMeshData ( MeshX meshx )
202+ {
203+ if ( _sourceMesh . Asset ? . Data is null )
204+ {
205+ meshx . Clear ( ) ;
206+ _sourceMesh . ListenToAssetUpdates = true ;
207+ DebugLog ( $ "{ nameof ( _sourceMesh ) } Asset null. Listening for updates...") ;
208+ return ;
209+ }
210+
211+ if ( _sourceMesh . ListenToAssetUpdates )
212+ {
213+ DebugLog ( $ "{ nameof ( _sourceMesh ) } Asset loaded. No longer listening for updates.") ;
214+ _sourceMesh . ListenToAssetUpdates = false ;
215+ }
216+
217+ if ( storedMeshX != meshx )
218+ {
219+ DebugLog ( $ "Storing new MeshX data.") ;
220+ storedMeshX = meshx ;
221+ storedMeshX . Copy ( _sourceMesh . Asset . Data ) ;
222+ mergedVertexData = CollectMergedVertexData ( storedMeshX ) ;
223+ }
224+
225+ for ( int i = 0 ; i < mergedVertexData . Count ; i ++ )
226+ {
227+ var data = mergedVertexData [ i ] ;
228+
229+ Slot vertSlot = _controlPointsSlot . Target [ i ] ; // Could throw null ref exception but it's probably okay
230+
231+ foreach ( var vert in data . vertices )
232+ {
233+ storedMeshX . SetVertex ( vert . Index , vertSlot . LocalPosition ) ;
234+ }
235+ }
236+ }
237+ }
0 commit comments