[NUI] Public open MotionData
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Scene3D / src / public / ModelMotion / MotionData.cs
1 /*
2  * Copyright(c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Runtime.InteropServices;
20 using System.ComponentModel;
21 using System.Collections.Generic;
22
23 namespace Tizen.NUI.Scene3D
24 {
25     /// <summary>
26     /// List of model motion definitions.
27     /// Each motion has pair of <see cref="MotionIndex"/> and <see cref="MotionValue"/>.
28     /// MotionIndex is abstract class that specify the target of motion.
29     /// MotionValue is destination value of target for the motion. It can be expressed with <see cref="Tizen.NUI.PropertyValue"/> or <see cref="Tizen.NUI.KeyFrames"/>.
30     /// </summary>
31     /// <remarks>
32     /// We don't check duplicated MotionIndex internally.
33     /// We don't check MotionValue type is matched with MotionIndex.
34     /// </remarks>
35     /// <example>
36     /// We can generate list of motions by MotionIndex and MotionValue classes.
37     ///
38     /// <code>
39     /// MotionData motionData = new MotionData(3.0f);
40     ///
41     /// // Make MotionIndex with MotionPropertyIndex
42     /// // Make MotionValue with PropertyValue
43     /// motionData.Add(new MotionPropertyIndex(new PropertyKey("nodeName"), new PropertyKey("color")), new MotionValue(new PropertyValue(Color.Red)));
44     ///
45     /// // Make MotionIndex with MotionTransformIndex
46     /// // Make MotionValue with Dali::KeyFrames
47     /// KeyFrames keyFrames = new KeyFrames();
48     /// keyFrames.Add(0.0f, 0.0f);
49     /// keyFrames.Add(0.0f, 1.0f);
50     /// motionData.Add(new MotionTransformIndex(new PropertyKey("nodeName"), MotionTransformIndex.TransformType.PositionX), new MotionValue(keyFrames));
51     ///
52     /// // Make MotionIndex with BlendShapeIndex
53     /// motionData.Add(new BlendShapeIndex(new PropertyKey("nodeName"), new PropertyKey("blendShapeName")), motionData.GetValue(1u));
54     /// </code>
55     /// </example>
56     /// <example>
57     /// We can request to load MotionData from file or buffer asynchronously.
58     /// If load completed, <see cref="LoadCompleted"/> event will be invoked.
59     /// If we try to load before LoadCompleted event invoked, previous load request cancel and only latest request loaded.
60     ///
61     /// <code>
62     /// MotionData motionData = new MotionData();
63     /// motionData.LoadCompleted += OnLoadCompleted;
64     /// motionData.LoadBvh("bvhFilename.bvh", Vector3.One);
65     ///
66     /// ...
67     ///
68     /// void OnLoadCompleted(object o, event e)
69     /// {
70     ///     MotionData motionData = o as MotionData;
71     ///     /// Do something.
72     /// }
73     /// </code>
74     /// </example>
75     /// <example>
76     /// We can generate animation of Scene3D.Model from MotionData class.
77     /// Or, just set values.
78     ///
79     /// <code>
80     /// // Generate animation from loaded Model
81     /// Animation animation = model.GenerateMotionDataAnimation(motionData);
82     /// animation.Play();
83     ///
84     /// // Set values from loaded Model.
85     /// model2.SetMotionData(motionData);
86     /// </code>
87     /// </example>
88     /// <since_tizen> 11 </since_tizen>
89     public class MotionData : BaseHandle
90     {
91         private EventHandler loadCompletedEventHandler;
92         private LoadCompletedCallbackType loadCompletedCallback;
93         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
94         private delegate void LoadCompletedCallbackType(IntPtr motionData);
95
96         /// <summary>
97         /// Create an initialized, empty motion data.
98         /// </summary>
99         /// <since_tizen> 11 </since_tizen>
100         public MotionData() : this(Interop.MotionData.MotionDataNew(), true)
101         {
102             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
103         }
104
105         /// <summary>
106         /// Create an initialized motion data with duration.
107         /// </summary>
108         /// <param name="durationMilliseconds">Duration of an Animation generated from this motion data, in milliseconds.</param>
109         /// <since_tizen> 11 </since_tizen>
110         public MotionData(int durationMilliseconds) : this(Interop.MotionData.MotionDataNew(MillisecondsToSeconds(durationMilliseconds)), true)
111         {
112             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
113         }
114
115         /// <summary>
116         /// Copy constructor.
117         /// </summary>
118         /// <param name="motionData">Source object to copy.</param>
119         /// <since_tizen> 11 </since_tizen>
120         public MotionData(MotionData motionData) : this(Interop.MotionData.NewMotionData(MotionData.getCPtr(motionData)), true)
121         {
122             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
123         }
124
125         /// <summary>
126         /// Assignment operator.
127         /// </summary>
128         /// <param name="motionData">Source object to be assigned.</param>
129         /// <returns>Reference to this.</returns>
130         internal MotionData Assign(MotionData motionData)
131         {
132             MotionData ret = new MotionData(Interop.MotionData.MotionDataAssign(SwigCPtr, MotionData.getCPtr(motionData)), false);
133             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
134             return ret;
135         }
136
137         internal MotionData(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
138         {
139         }
140
141         /// <summary>
142         /// Get or set the duration of this motion data in milliseconds.
143         /// </summary>
144         /// <since_tizen> 11 </since_tizen>
145         public int Duration
146         {
147             get
148             {
149                 return SecondsToMilliseconds(GetDuration());
150             }
151             set
152             {
153                 SetDuration(MillisecondsToSeconds(value));
154             }
155         }
156
157         /// <summary>
158         /// Get the number of contained MotionIndex / MotionValue pair what this hold.
159         /// </summary>
160         /// <returns>The number of contained motions.</returns>
161         /// <since_tizen> 11 </since_tizen>
162         public uint GetMotionCount()
163         {
164             uint ret = Interop.MotionData.GetMotionCount(SwigCPtr);
165             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
166             return ret;
167         }
168
169         /// <summary>
170         /// Append pair of MotionIndex and MotionValue to the list.
171         /// </summary>
172         /// <param name="index">MotionIndex to be added</param>
173         /// <param name="value">MotionValue to be added</param>
174         /// <since_tizen> 11 </since_tizen>
175         public void Add(MotionIndex index, MotionValue value)
176         {
177             Interop.MotionData.Add(SwigCPtr, MotionIndex.getCPtr(index), MotionValue.getCPtr(value));
178             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
179         }
180
181         /// <summary>
182         /// Get MotionIndex at position, or null if invalid index was given.
183         /// </summary>
184         /// <param name="index">The index of motion data list</param>
185         /// <returns>The MotionIndex at position. Or null.</returns>
186         /// <since_tizen> 11 </since_tizen>
187         public MotionIndex GetIndex(uint index)
188         {
189             IntPtr cPtr = Interop.MotionData.GetIndex(SwigCPtr, index);
190             MotionIndex ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as MotionIndex;
191             if (ret == null)
192             {
193                 // Register new animation into Registry.
194                 ret = new MotionIndex(cPtr, true);
195             }
196             else
197             {
198                 // We found matched NUI animation. Reduce cPtr reference count.
199                 HandleRef handle = new HandleRef(this, cPtr);
200                 Interop.MotionIndex.DeleteMotionIndex(handle);
201                 handle = new HandleRef(null, IntPtr.Zero);
202             }
203             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
204
205             if (!ret.HasBody())
206             {
207                 ret.Dispose();
208                 ret = null;
209             }
210             return ret;
211         }
212
213         /// <summary>
214         /// Get MotionValue at position, or null if invalid index was given.
215         /// </summary>
216         /// <param name="index">The index of motion data list</param>
217         /// <returns>The MotionValue at position. Or null.</returns>
218         /// <since_tizen> 11 </since_tizen>
219         public MotionValue GetValue(uint index)
220         {
221             IntPtr cPtr = Interop.MotionData.GetValue(SwigCPtr, index);
222             MotionValue ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as MotionValue;
223             if (ret == null)
224             {
225                 // Register new animation into Registry.
226                 ret = new MotionValue(cPtr, true);
227             }
228             else
229             {
230                 // We found matched NUI animation. Reduce cPtr reference count.
231                 HandleRef handle = new HandleRef(this, cPtr);
232                 Interop.MotionValue.DeleteMotionValue(handle);
233                 handle = new HandleRef(null, IntPtr.Zero);
234             }
235             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
236
237             if (!ret.HasBody())
238             {
239                 ret.Dispose();
240                 ret = null;
241             }
242             return ret;
243         }
244
245         /// <summary>
246         /// Load motion capture animation.
247         /// We support bvh format.
248         /// After load completes, <see cref="LoadCompleted"/> event will be invoked.
249         /// </summary>
250         /// <remarks>
251         /// Scale is additional scale factor of motion capture animation. It is possible that
252         /// Model's scale may not match with motion capture animation scale.
253         /// If scale is null, default value will be used: <cref name="Vector3.ONE"/>
254         /// </remarks>
255         /// <param name="motionCaptureFilename">Name of motion capture format file.</param>
256         /// <param name="scale">Scale value of motion capture animation match with model.</param>
257         /// <param name="synchronousLoad">Load synchronously or not. Default is async load.</param>
258         /// <since_tizen> 11 </since_tizen>
259         public void LoadMotionCaptureAnimation(string motionCaptureFilename, Vector3 scale = null, bool synchronousLoad = false)
260         {
261             Interop.MotionData.LoadMotionCaptureAnimation(SwigCPtr, motionCaptureFilename, Vector3.getCPtr(scale), synchronousLoad);
262             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
263         }
264
265         /// <summary>
266         /// Load motion capture animation from string.
267         /// We support bvh format.
268         /// After load completes, <see cref="LoadCompleted"/> event will be invoked.
269         /// </summary>
270         /// <remarks>
271         /// Scale is additional scale factor of motion capture animation. It is possible that
272         /// Model's scale may not match with motion capture animation scale.
273         /// If scale is null, default value will be used: <cref name="Vector3.ONE"/>
274         /// </remarks>
275         /// <param name="motionCaptureBuffer">Contents of motion capture format string.</param>
276         /// <param name="scale">Scale value of motion capture animation match with model.</param>
277         /// <param name="synchronousLoad">Load synchronously or not. Default is async load.</param>
278         /// <since_tizen> 11 </since_tizen>
279         public void LoadMotionCaptureAnimationFromBuffer(string motionCaptureBuffer, Vector3 scale = null, bool synchronousLoad = false)
280         {
281             Interop.MotionData.LoadMotionCaptureAnimationFromBuffer(SwigCPtr, motionCaptureBuffer, motionCaptureBuffer.Length, Vector3.getCPtr(scale), synchronousLoad);
282             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
283         }
284
285         /// <summary>
286         /// Load blendshape animation from json file.
287         /// After load completed, <see cref="LoadCompleted"/> event will be invoked.
288         /// </summary>
289         /// <param name="blendShapeFilename">Name of json format file what we predefined.</param>
290         /// <param name="synchronousLoad">Load synchronously or not. Default is async load.</param>
291         /// <since_tizen> 11 </since_tizen>
292         public void LoadBlendShapeAnimation(string blendShapeFilename, bool synchronousLoad = false)
293         {
294             Interop.MotionData.LoadBlendShapeAnimation(SwigCPtr, blendShapeFilename, synchronousLoad);
295             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
296         }
297
298         /// <summary>
299         /// Load morphing animation from json string.
300         /// After load completed, <see cref="LoadCompleted"/> event will be invoked.
301         /// </summary>
302         /// <param name="blendShapeBuffer">Contents of json format file what we predefined.</param>
303         /// <param name="synchronousLoad">Load synchronously or not. Default is async load.</param>
304         /// <since_tizen> 11 </since_tizen>
305         public void LoadBlendShapeAnimationFromBuffer(string blendShapeBuffer, bool synchronousLoad = false)
306         {
307             Interop.MotionData.LoadBlendShapeAnimationFromBuffer(SwigCPtr, blendShapeBuffer, blendShapeBuffer.Length, synchronousLoad);
308             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
309         }
310
311         /// <summary>
312         /// LoadCompleted event.
313         /// It will be invoked only for latest load request.
314         /// </summary>
315         /// <since_tizen> 11 </since_tizen>
316         public event EventHandler LoadCompleted
317         {
318             add
319             {
320                 if (loadCompletedEventHandler == null)
321                 {
322                     loadCompletedCallback = OnLoadCompleted;
323                     Interop.MotionData.LoadCompletedConnect(SwigCPtr, loadCompletedCallback.ToHandleRef(this));
324                     NDalicPINVOKE.ThrowExceptionIfExists();
325                 }
326                 loadCompletedEventHandler += value;
327             }
328             remove
329             {
330                 loadCompletedEventHandler -= value;
331                 if (loadCompletedEventHandler == null && loadCompletedCallback != null)
332                 {
333                     Interop.MotionData.LoadCompletedDisconnect(SwigCPtr, loadCompletedCallback.ToHandleRef(this));
334                     NDalicPINVOKE.ThrowExceptionIfExists();
335                     loadCompletedCallback = null;
336                 }
337             }
338         }
339
340         private void OnLoadCompleted(IntPtr motionData)
341         {
342             if (loadCompletedEventHandler != null)
343             {
344                 loadCompletedEventHandler(this, null);
345             }
346         }
347
348         /// <summary>
349         /// Removes all stored motions.
350         /// </summary>
351         /// <since_tizen> 11 </since_tizen>
352         public void Clear()
353         {
354             Interop.MotionData.Clear(SwigCPtr);
355             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
356         }
357
358         internal void SetDuration(float durationSeconds)
359         {
360             Interop.MotionData.SetDuration(SwigCPtr, durationSeconds);
361             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
362         }
363
364         internal float GetDuration()
365         {
366             float ret = Interop.MotionData.GetDuration(SwigCPtr);
367             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
368             return ret;
369         }
370
371         private static float MillisecondsToSeconds(int millisec)
372         {
373             return (float)millisec / 1000.0f;
374         }
375
376         private static int SecondsToMilliseconds(float sec)
377         {
378             return (int)(sec * 1000);
379         }
380
381         /// <summary>
382         /// you can override it to clean-up your own resources.
383         /// </summary>
384         /// <param name="type">DisposeTypes</param>
385         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
386         [EditorBrowsable(EditorBrowsableState.Never)]
387         protected override void Dispose(DisposeTypes type)
388         {
389             if (disposed)
390             {
391                 return;
392             }
393
394             if (type == DisposeTypes.Explicit)
395             {
396                 //Called by User
397                 //Release your own managed resources here.
398                 //You should release all of your own disposable objects here.
399             }
400
401             //Release your own unmanaged resources here.
402             //You should not access any managed member here except static instance.
403             //because the execution order of Finalizes is non-deterministic.
404
405             if (loadCompletedCallback != null)
406             {
407                 NUILog.Debug($"[Dispose] loadCompletedCallback");
408
409                 Interop.MotionData.LoadCompletedDisconnect(SwigCPtr, loadCompletedCallback.ToHandleRef(this));
410                 NDalicPINVOKE.ThrowExceptionIfExists();
411                 loadCompletedCallback = null;
412             }
413
414             base.Dispose(type);
415         }
416
417         /// <summary>
418         /// Release swigCPtr.
419         /// </summary>
420         // This will be public opened.
421         [EditorBrowsable(EditorBrowsableState.Never)]
422         protected override void ReleaseSwigCPtr(global::System.Runtime.InteropServices.HandleRef swigCPtr)
423         {
424             Interop.MotionData.DeleteMotionData(swigCPtr);
425         }
426     }
427 }