public void GetTotalDuration() { var animation = new AnimationClip<float> { Animation = new SingleFromToByAnimation { Duration = TimeSpan.FromSeconds(1.0) }, Delay = TimeSpan.FromSeconds(10), Duration = null, Speed = 1.0f, }; // Default duration is 1 second. Assert.AreEqual(TimeSpan.FromSeconds(11.0), animation.GetTotalDuration()); animation.Duration = TimeSpan.FromSeconds(5.0); Assert.AreEqual(TimeSpan.FromSeconds(15.0), animation.GetTotalDuration()); animation.Speed = 2.0f; Assert.AreEqual(TimeSpan.FromSeconds(12.5), animation.GetTotalDuration()); animation.Delay = TimeSpan.FromSeconds(-1.0); Assert.AreEqual(TimeSpan.FromSeconds(1.5), animation.GetTotalDuration()); animation.Speed = 0.0f; Assert.AreEqual(TimeSpan.MaxValue, animation.GetTotalDuration()); }
private void RestartAnimations() { // Start original animation on one model. var loopingAnimation = new AnimationClip<SkeletonPose>(_animation) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)_meshNodeUncompressed.SkeletonPose); // Clone the original animation. var animationCompressed = _animation.Clone(); // Compress animation. This removes key frames that can be computed from neighboring frames. // This animation is lossy and the parameters define the allowed error. _removedKeyFrames = animationCompressed.Compress(0.01f, _rotationCompressionThreshold, 0.001f); // Finalize the SkeletonKeyFrameAnimation. // (This must be called to optimize the internal data structures.) animationCompressed.Freeze(); // Start compressed animation on the other model. var loopingAnimationCompressed = new AnimationClip<SkeletonPose>(animationCompressed) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; AnimationService.StartAnimation(loopingAnimationCompressed, (IAnimatableProperty)_meshNodeCompressed.SkeletonPose); }
public ModelContainer(Model modelpass, Pose posepass, SkeletonPose skeletonpass) { _model = modelpass; _pose = posepass; _skeleton = skeletonpass; var additionalData = (Dictionary<string,object>)_model.Tag; var animations = (Dictionary<string, SkeletonKeyFrameAnimation>)additionalData["Animations"]; int index = 0; _animations = new ITimeline[animations.Count]; _animations[0] = new AnimationClip<SkeletonPose>(animations["First"]) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue }; index++; _animations[1] = new AnimationClip<SkeletonPose>(animations["Second"]); _animations[2] = new AnimationClip<SkeletonPose>(animations["Second"]) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue }; }
public void TestAnimationClip() { var clip = new AnimationClip { Duration = TimeSpan.FromSeconds(2.0f), RepeatMode = AnimationRepeatMode.LoopInfinite }; var testCurve = new AnimationCurve<float>(); clip.AddCurve("posx[TestNode]", testCurve); testCurve.InterpolationType = AnimationCurveInterpolationType.Linear; var time = CompressedTimeSpan.FromSeconds(0.0f); var value = 0.0f; var frame0 = new KeyFrameData<float>(time, value); testCurve.KeyFrames.Add(frame0); time = CompressedTimeSpan.FromSeconds(1.0f); value = 1.0f; var frame1 = new KeyFrameData<float>(time, value); testCurve.KeyFrames.Add(frame1); clip.Optimize(); //we should have 3 frames at this point. the last one will be added by the optimization process... Assert.That(clip.OptimizedCurvesFloat.AnimationSortedValueCount, Is.EqualTo(1)); //And 2 initial frames Assert.That(clip.OptimizedCurvesFloat.AnimationInitialValues[0].Value1, Is.EqualTo(frame0)); Assert.That(clip.OptimizedCurvesFloat.AnimationInitialValues[0].Value2, Is.EqualTo(frame1)); Assert.That(clip.OptimizedCurvesFloat.AnimationSortedValues.Length, Is.EqualTo(1)); Assert.That(clip.OptimizedCurvesFloat.AnimationSortedValues[0].Length, Is.EqualTo(1)); Assert.That(clip.OptimizedCurvesFloat.AnimationSortedValues[0][0].Value, Is.EqualTo(frame1)); }
public BoneJiggleSample(Microsoft.Xna.Framework.Game game) : base(game) { var modelNode = ContentManager.Load<ModelNode>("Dude/Dude"); _meshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); _meshNode.PoseLocal = new Pose(new Vector3F(0, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi)); SampleHelper.EnablePerPixelLighting(_meshNode); GraphicsScreen.Scene.Children.Add(_meshNode); var animations = _meshNode.Mesh.Animations; var walkAnimation = new AnimationClip<SkeletonPose>(animations.Values.First()) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; _walkAnimationController = AnimationService.StartAnimation(walkAnimation, (IAnimatableProperty)_meshNode.SkeletonPose); _walkAnimationController.AutoRecycle(); // Create a BoneJiggler instance for the head bone (bone index 7). _boneJiggler = new BoneJiggler(_meshNode.SkeletonPose, 7, new Vector3F(1.1f, 0, 0)) { Spring = 100, Damping = 3, }; }
public AttachmentSample(Microsoft.Xna.Framework.Game game) : base(game) { var modelNode = ContentManager.Load<ModelNode>("Marine/PlayerMarine"); _meshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); SampleHelper.EnablePerPixelLighting(_meshNode); GraphicsScreen.Scene.Children.Add(_meshNode); // Play a looping 'Idle' animation. var animations = _meshNode.Mesh.Animations; var idleAnimation = animations["Idle"]; var loopingAnimation = new AnimationClip<SkeletonPose>(idleAnimation) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; var animationController = AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)_meshNode.SkeletonPose); animationController.UpdateAndApply(); animationController.AutoRecycle(); // Add weapon model to the scene graph under the node of the marine mesh. _weaponModelNode = ContentManager.Load<ModelNode>("Marine/Weapon/WeaponMachineGun").Clone(); _meshNode.Children = new SceneNodeCollection(); _meshNode.Children.Add(_weaponModelNode); }
public void CyclicLoopBehavior() { var keyFrame0 = new KeyFrame<QuaternionF>(TimeSpan.FromSeconds(0.0), _random.NextQuaternionF()); var keyFrame1 = new KeyFrame<QuaternionF>(TimeSpan.FromSeconds(1.0), _random.NextQuaternionF()); var keyFrame2 = new KeyFrame<QuaternionF>(TimeSpan.FromSeconds(2.0), _random.NextQuaternionF()); var animation = new QuaternionFKeyFrameAnimation(); animation.KeyFrames.Add(keyFrame0); animation.KeyFrames.Add(keyFrame1); animation.KeyFrames.Add(keyFrame2); var animationClip = new AnimationClip<QuaternionF> { Animation = animation }; animationClip.LoopBehavior = LoopBehavior.Cycle; animationClip.Duration = TimeSpan.MaxValue; animationClip.ClipOffset = TimeSpan.FromSeconds(-1); var defaultSource = _random.NextQuaternionF(); var defaultTarget = _random.NextQuaternionF(); // Pre loop var expected = InterpolationHelper.Lerp(keyFrame1.Value, keyFrame2.Value, 0.25f); Assert.AreEqual(expected, animationClip.GetValue(TimeSpan.FromSeconds(0.25), defaultSource, defaultTarget)); // Post loop expected = InterpolationHelper.Lerp(keyFrame1.Value, keyFrame2.Value, 0.75f); Assert.AreEqual(expected, animationClip.GetValue(TimeSpan.FromSeconds(4.75), defaultSource, defaultTarget)); }
public StringAnimationSample(Microsoft.Xna.Framework.Game game) : base(game) { // A key-frame animation of string values. // Note: The key frame animation plays all strings and then holds the last key frame // because the default KeyFrameAnimation.FillBehavior is "Hold". StringKeyFrameAnimation keyFrameAnimation = new StringKeyFrameAnimation(); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(0), "The")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(1), "quick")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(2), "brown")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(3), "fox")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(4), "jumps")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(5), "over")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(6), "the")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(7), "lazy")); keyFrameAnimation.KeyFrames.Add(new KeyFrame<string>(TimeSpan.FromSeconds(8), "dog.")); // Use an AnimationClip to loop the first 9 seconds of the key-frame animation // forever. The animation speed is set to 2 to make the animation play faster. AnimationClip<string> timelineClip = new AnimationClip<string>(keyFrameAnimation) { LoopBehavior = LoopBehavior.Cycle, ClipStart = TimeSpan.FromSeconds(0), ClipEnd = TimeSpan.FromSeconds(9), Duration = TimeSpan.MaxValue, Speed = 2, }; // Start the animation. AnimationService.StartAnimation(timelineClip, _animatableString); }
public void GetDefaultDurationTest() { var timelineGroup = new TimelineGroup(); var animation = new Vector2FAnimation(); var animation2 = new AnimationClip<float> { Animation = new SingleFromToByAnimation(), Delay = TimeSpan.FromSeconds(10), }; var animation3 = new AnimationClip<float> { Animation = new SingleFromToByAnimation(), Delay = TimeSpan.FromSeconds(8), Duration = TimeSpan.FromSeconds(4), }; Assert.AreEqual(TimeSpan.FromSeconds(0.0), timelineGroup.GetTotalDuration()); timelineGroup.Add(animation); Assert.AreEqual(TimeSpan.FromSeconds(0.0), timelineGroup.GetTotalDuration()); timelineGroup.Add(animation2); Assert.AreEqual(TimeSpan.FromSeconds(11.0), timelineGroup.GetTotalDuration()); timelineGroup.Add(animation3); Assert.AreEqual(TimeSpan.FromSeconds(12.0), timelineGroup.GetTotalDuration()); }
protected override void LoadContent() { // Get dude model and start animation on the dude. _dudeModel = Game.Content.Load<Model>("Dude"); var additionalData = (Dictionary<string, object>)_dudeModel.Tag; var skeleton = (Skeleton)additionalData["Skeleton"]; _dudeSkeletonPose = SkeletonPose.Create(skeleton); var animations = (Dictionary<string, SkeletonKeyFrameAnimation>)additionalData["Animations"]; var loopingAnimation = new AnimationClip<SkeletonPose>(animations.Values.First()) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)_dudeSkeletonPose); // Get marine model - do not start any animations on the marine model. _marineModel = Game.Content.Load<Model>("PlayerMarine"); additionalData = (Dictionary<string, object>)_marineModel.Tag; skeleton = (Skeleton)additionalData["Skeleton"]; _marineSkeletonPose = SkeletonPose.Create(skeleton); CreateSkeletonMapper(); base.LoadContent(); }
public SkeletonMappingSample(Microsoft.Xna.Framework.Game game) : base(game) { // Get dude model and start animation on the dude. var modelNode = ContentManager.Load<ModelNode>("Dude/Dude"); _dudeMeshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); _dudeMeshNode.PoseLocal = new Pose(new Vector3F(-0.5f, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi)); SampleHelper.EnablePerPixelLighting(_dudeMeshNode); GraphicsScreen.Scene.Children.Add(_dudeMeshNode); var animations = _dudeMeshNode.Mesh.Animations; var loopingAnimation = new AnimationClip<SkeletonPose>(animations.Values.First()) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)_dudeMeshNode.SkeletonPose); // Get marine model - do not start any animations on the marine model. modelNode = ContentManager.Load<ModelNode>("Marine/PlayerMarine"); _marineMeshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); _marineMeshNode.PoseLocal = new Pose(new Vector3F(0.5f, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi)); SampleHelper.EnablePerPixelLighting(_marineMeshNode); GraphicsScreen.Scene.Children.Add(_marineMeshNode); CreateSkeletonMapper(); }
internal void Initialize(AnimationClip clip, List<AnimationBlender.Channel> channels) { this.BlenderChannels = channels; this.clip = clip; clip.Freeze(); // If there are optimized curve data, instantiate appropriate evaluators if (clip.OptimizedCurvesFloat != null) { if (curveEvaluatorOptimizedFloat == null) curveEvaluatorOptimizedFloat = new AnimationCurveEvaluatorOptimizedFloatGroup(); curveEvaluatorOptimizedFloat.Initialize(clip.OptimizedCurvesFloat); } if (clip.OptimizedCurvesVector3 != null) { if (curveEvaluatorOptimizedVector3 == null) curveEvaluatorOptimizedVector3 = new AnimationCurveEvaluatorOptimizedVector3Group(); curveEvaluatorOptimizedVector3.Initialize(clip.OptimizedCurvesVector3); } if (clip.OptimizedCurvesQuaternion != null) { if (curveEvaluatorOptimizedQuaternion == null) curveEvaluatorOptimizedQuaternion = new AnimationCurveEvaluatorOptimizedQuaternionGroup(); curveEvaluatorOptimizedQuaternion.Initialize(clip.OptimizedCurvesQuaternion); } // Add already existing channels for (int index = 0; index < channels.Count; index++) { var channel = channels[index]; AddChannel(ref channel); } }
public BasicAnimationSample(Microsoft.Xna.Framework.Game game) : base(game) { var titleSafeArea = GraphicsService.GraphicsDevice.Viewport.TitleSafeArea; float minX = titleSafeArea.Left + 100; float maxX = titleSafeArea.Right - 100; // A from/to/by animation that animates a float value from minX to maxX over 2 seconds // using an easing function smooth movement. SingleFromToByAnimation xAnimation = new SingleFromToByAnimation { From = minX, To = maxX, Duration = TimeSpan.FromSeconds(2), EasingFunction = new CubicEase { Mode = EasingMode.EaseInOut }, }; // From-To-By animations do not loop by default. // Use an AnimationClip to turn the 2 second xAnimation into an animation that // oscillates forever. _oscillatingXAnimation = new AnimationClip<float>(xAnimation) { LoopBehavior = LoopBehavior.Oscillate, Duration = TimeSpan.MaxValue, }; // A color key-frame animation. ColorKeyFrameAnimation colorAnimation = new ColorKeyFrameAnimation { //EnableInterpolation = true, // Interpolation is enabled by default. }; // Add the key-frames. colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0.0), Color.Red)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0.5), Color.Orange)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(1.0), Color.Yellow)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(1.5), Color.Green)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(2.0), Color.Blue)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(2.5), Color.Indigo)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(3.0), Color.Violet)); // The last-key frame defines the length of the animation (3.5 seconds). This is a // "loop frame", which means the value is the same as in the first key to create a looping // animation. colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(3.5), Color.Red)); // If the key-frames are not sorted, call Sort(). //colorAnimation.KeyFrames.Sort(); // Use an AnimationClip to turn the 3.5 second colorAnimation into an animation that // loops forever. _loopedColorAnimation = new AnimationClip<Color>(colorAnimation) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; StartAnimations(); }
public void Test_Indexer() { var anim = new AnimationController (); var clip = new AnimationClip (100, "Clip"); anim.AddClip (clip); Assert.AreEqual (clip, anim["Clip"]); }
public void Test_Play() { var clip = new AnimationClip (100, "ClipName"); Assert.AreEqual (false, clip.IsPlaying); clip.Play (); Assert.AreEqual (true, clip.IsPlaying); }
/// <summary> /// <para>Automatically creates an AnimationClipPlayable for each supplied AnimationClip, then sets them as inputs to the mixer.</para> /// </summary> /// <param name="clips">AnimationClips to be used as inputs.</param> /// <returns> /// <para>Returns false if the creation of the AnimationClipPlayables failed, or if the connection failed.</para> /// </returns> public bool SetInputs(AnimationClip[] clips) { if (clips == null) throw new NullReferenceException("Parameter clips was null. You need to pass in a valid array of clips."); AnimationPlayable[] animationPlayableArray = new AnimationPlayable[clips.Length]; for (int index = 0; index < clips.Length; ++index) animationPlayableArray[index] = (AnimationPlayable) new AnimationClipPlayable(clips[index]); return this.SetInputs((IEnumerable<AnimationPlayable>) animationPlayableArray); }
public void GetTotalDurationTest() { var animation = new AnimationClip<float> { Animation = new SingleFromToByAnimation { From = 100, To = 200, Duration = TimeSpan.FromSeconds(6.0), }, Delay = TimeSpan.FromSeconds(10), Speed = 2, FillBehavior = FillBehavior.Hold, }; var animation2 = new AnimationClip<float> { Animation = new SingleFromToByAnimation { From = 10, To = 20, Duration = TimeSpan.FromSeconds(5.0), }, Delay = TimeSpan.Zero, Speed = 1, FillBehavior = FillBehavior.Hold, }; var animationEx = new Vector4FAnimation(); Assert.AreEqual(TimeSpan.FromSeconds(0.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.W = animation; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.X = animation; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.Y = animation; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.Z = animation; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.W = animation; animationEx.X = animation2; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); animationEx = new Vector4FAnimation(); animationEx.Y = animation2; animationEx.Z = animation; Assert.AreEqual(TimeSpan.FromSeconds(13.0), animationEx.GetTotalDuration()); }
internal void Cleanup() { curveEvaluatorOptimizedFloat?.Cleanup(); curveEvaluatorOptimizedVector3?.Cleanup(); curveEvaluatorOptimizedQuaternion?.Cleanup(); Channels.Clear(); BlenderChannels = null; clip = null; }
public void ItWillNotBeEnabledIfOtherClipsArePlaying() { string name = "MyClip"; AnimationClip clip = new AnimationClip(); animation.AddClip(clip, name); animation.Play(name); AnimationState state = animation.PlayQueued(name); Assert.That(state.enabled, Is.False); }
public void CheckDefaultValues() { var animationClip = new AnimationClip<float>(); Assert.IsNull(animationClip.Traits); Assert.IsNull(animationClip.Animation); Assert.IsFalse(animationClip.ClipStart.HasValue); Assert.IsFalse(animationClip.ClipEnd.HasValue); Assert.AreEqual(TimeSpan.FromSeconds(0.0), animationClip.ClipOffset); Assert.AreEqual(LoopBehavior.Constant, animationClip.LoopBehavior); }
public static Node Create(Vector3 pos) { var cmp = new MyFireExplosion (); var spr = new Sprite (100, 100); spr.AddTexture (new Texture ("media/FireExplosion.png")); var track = new AnimationTrack ("TextureOffset", InterpolationType.Step); track.AddKeyframe (0, new Vector2 (0, 0)); track.AddKeyframe (100, new Vector2 (100, 0)); track.AddKeyframe (200, new Vector2 (200, 0)); track.AddKeyframe (300, new Vector2 (300, 0)); track.AddKeyframe (400, new Vector2 (400, 0)); track.AddKeyframe (500, new Vector2 (500, 0)); track.AddKeyframe (600, new Vector2 (600, 0)); track.AddKeyframe (700, new Vector2 (700, 0)); track.AddKeyframe (800, new Vector2 (800, 0)); track.AddKeyframe (900, new Vector2 (900, 0)); track.AddKeyframe (1000, new Vector2 (0, 100)); track.AddKeyframe (1100, new Vector2 (100, 100)); track.AddKeyframe (1200, new Vector2 (200, 100)); track.AddKeyframe (1300, new Vector2 (300, 100)); track.AddKeyframe (1400, new Vector2 (400, 100)); track.AddKeyframe (1500, new Vector2 (500, 100)); track.AddKeyframe (1600, new Vector2 (600, 100)); track.AddKeyframe (1700, new Vector2 (700, 100)); track.AddKeyframe (1800, new Vector2 (800, 100)); track.AddKeyframe (1900, new Vector2 (900, 100)); track.AddKeyframe (2000, new Vector2 (0, 200)); track.AddKeyframe (2100, new Vector2 (100, 200)); track.AddKeyframe (2200, new Vector2 (200, 200)); track.AddKeyframe (2300, new Vector2 (300, 200)); track.AddKeyframe (2400, new Vector2 (400, 200)); track.AddKeyframe (2500, new Vector2 (500, 200)); track.AddKeyframe (2600, new Vector2 (600, 200)); track.AddKeyframe (2700, new Vector2 (700, 200)); track.AddKeyframe (2800, new Vector2 (800, 200)); track.AddKeyframe (2900, new Vector2 (900, 200)); var clip = new AnimationClip (3000, "FireExplosion"); clip.AddTrack (spr, track); clip.WrapMode = WrapMode.Loop; var anim = new AnimationController (); anim.AddClip (clip); var node = new Node (); node.Attach (cmp); node.Attach (spr); node.Attach (anim); node.Translation = pos; return node; }
public MixingSample(Microsoft.Xna.Framework.Game game) : base(game) { var modelNode = ContentManager.Load<ModelNode>("Marine/PlayerMarine"); _meshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); SampleHelper.EnablePerPixelLighting(_meshNode); GraphicsScreen.Scene.Children.Add(_meshNode); var animations = _meshNode.Mesh.Animations; _runAnimation = new AnimationClip<SkeletonPose>(animations["Run"]) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; _idleAnimation = new AnimationClip<SkeletonPose>(animations["Idle"]) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; // Create a 'Shoot' animation that only affects the upper body. var shootAnimation = animations["Shoot"]; // The SkeletonKeyFrameAnimations allows to set a weight for each bone channel. // For the 'Shoot' animation, we set the weight to 0 for all bones that are // not descendants of the second spine bone (bone index 2). That means, the // animation affects only the upper body bones and is disabled on the lower // body bones. for (int i = 0; i < _meshNode.Mesh.Skeleton.NumberOfBones; i++) { if (!SkeletonHelper.IsAncestorOrSelf(_meshNode.SkeletonPose, 2, i)) shootAnimation.SetWeight(i, 0); } var loopedShootingAnimation = new AnimationClip<SkeletonPose>(shootAnimation) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; // Start 'Idle' animation. _idleAnimationController = AnimationService.StartAnimation(_idleAnimation, (IAnimatableProperty)_meshNode.SkeletonPose); _idleAnimationController.AutoRecycle(); // Start looping the 'Shoot' animation. We use a Compose transition. This will add the // 'Shoot' animation to the animation composition chain and keeping all other playing // animations. // The 'Idle' animation animates the whole skeleton. The 'Shoot' animation replaces // the 'Idle' animation on the bones of the upper body. AnimationService.StartAnimation(loopedShootingAnimation, (IAnimatableProperty)_meshNode.SkeletonPose, AnimationTransitions.Compose() ).AutoRecycle(); }
internal void Cleanup() { if (curveEvaluatorOptimizedVector3 != null) curveEvaluatorOptimizedVector3.Cleanup(); if (curveEvaluatorOptimizedQuaternion != null) curveEvaluatorOptimizedQuaternion.Cleanup(); Channels.Clear(); BlenderChannels = null; clip = null; }
public void ItWillBeEnabledImmidiatelyIfNoOtherClipsArePlaying() { string name = "MyClip"; AnimationClip clip = new AnimationClip(); animation.AddClip(clip, name); AnimationState state = animation.PlayQueued(name); Assert.That(state.enabled, Is.True); Assert.That(state.playQueuedReference, Is.True); Assert.That(state, Is.Not.SameAs(animation[name])); Assert.That(animation[name].playQueuedReference, Is.False); }
public static Node Create(Vector3 pos) { var cmp = new MyFireExplosion (); var spr = new Sprite (100, 100); spr.AddTexture(Resource.GetTexture ("media/FireExplosion.png")); var node = new Node ("FireExplosion"); node.Attach (spr); node.Attach (cmp); node.Translation = pos - new Vector3(34, 34, 0); // 爆発の中心を地球にあわせる var track = new AnimationTrack ("TextureOffset", InterpolationType.Step); track.AddKeyframe (0, new Vector2 (0, 0)); track.AddKeyframe (100, new Vector2 (100, 0)); track.AddKeyframe (200, new Vector2 (200, 0)); track.AddKeyframe (300, new Vector2 (300, 0)); track.AddKeyframe (400, new Vector2 (400, 0)); track.AddKeyframe (500, new Vector2 (500, 0)); track.AddKeyframe (600, new Vector2 (600, 0)); track.AddKeyframe (700, new Vector2 (700, 0)); track.AddKeyframe (800, new Vector2 (800, 0)); track.AddKeyframe (900, new Vector2 (900, 0)); track.AddKeyframe (1000, new Vector2 (0, 100)); track.AddKeyframe (1100, new Vector2 (100, 100)); track.AddKeyframe (1200, new Vector2 (200, 100)); track.AddKeyframe (1300, new Vector2 (300, 100)); track.AddKeyframe (1400, new Vector2 (400, 100)); track.AddKeyframe (1500, new Vector2 (500, 100)); track.AddKeyframe (1600, new Vector2 (600, 100)); track.AddKeyframe (1700, new Vector2 (700, 100)); track.AddKeyframe (1800, new Vector2 (800, 100)); track.AddKeyframe (1900, new Vector2 (900, 100)); track.AddKeyframe (2000, new Vector2 (0, 200)); track.AddKeyframe (2100, new Vector2 (100, 200)); track.AddKeyframe (2200, new Vector2 (200, 200)); track.AddKeyframe (2300, new Vector2 (300, 200)); track.AddKeyframe (2400, new Vector2 (400, 200)); track.AddKeyframe (2500, new Vector2 (500, 200)); track.AddKeyframe (2600, new Vector2 (600, 200)); track.AddKeyframe (2700, new Vector2 (700, 200)); track.AddKeyframe (2800, new Vector2 (800, 200)); track.AddKeyframe (2900, new Vector2 (900, 200)); var clip = new AnimationClip (3000, "Explosion"); clip.AddTrack (spr, track); clip.WrapMode = WrapMode.Once; node.UserData.Add (clip.Name, clip); return node; }
public void Test_New() { var clip = new AnimationClip (100, "ClipName"); Assert.AreEqual (100, clip.Duration); Assert.AreEqual ("ClipName", clip.Name); Assert.AreEqual (1, clip.Speed); Assert.AreEqual (WrapMode.Loop, clip.WrapMode); Assert.AreEqual (false, clip.IsPlaying); Assert.AreEqual (0, clip.TrackCount); Assert.AreEqual (0, clip.EventCount); }
public CollisionDetectionOnlyRagdollSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 1, 6), 0, 0); // Add a game object which allows to shoot balls. _ballShooterObject = new BallShooterObject(Services); GameObjectService.Objects.Add(_ballShooterObject); var modelNode = ContentManager.Load<ModelNode>("Dude/Dude"); _meshNode = modelNode.GetSubtree().OfType<MeshNode>().First().Clone(); _meshNode.PoseLocal = new Pose(new Vector3F(0, 0, 0), Matrix33F.CreateRotationY(ConstantsF.Pi)); SampleHelper.EnablePerPixelLighting(_meshNode); GraphicsScreen.Scene.Children.Add(_meshNode); var animations = _meshNode.Mesh.Animations; var loopingAnimation = new AnimationClip<SkeletonPose>(animations.Values.First()) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; AnimationService.StartAnimation(loopingAnimation, (IAnimatableProperty)_meshNode.SkeletonPose); // Create a ragdoll for the Dude model. _ragdoll = new Ragdoll(); DudeRagdollCreator.Create(_meshNode.SkeletonPose, _ragdoll, Simulation, 0.571f); // Set the world space pose of the whole ragdoll. _ragdoll.Pose = _meshNode.PoseWorld; // And copy the bone poses of the current skeleton pose. _ragdoll.UpdateBodiesFromSkeleton(_meshNode.SkeletonPose); foreach (var body in _ragdoll.Bodies) { if (body != null) { // Set all bodies to kinematic - they should not be affected by forces. body.MotionType = MotionType.Kinematic; // Disable collision response. body.CollisionResponseEnabled = false; } } // In this sample, we do not need joints, limits or motors. _ragdoll.DisableJoints(); _ragdoll.DisableLimits(); _ragdoll.DisableMotors(); // Add ragdoll rigid bodies to the simulation. _ragdoll.AddToSimulation(Simulation); }
public void GetValueTest() { var animation = new AnimationClip<float> { Animation = new SingleFromToByAnimation { From = 100, To = 200, Duration = TimeSpan.FromSeconds(6.0), }, Delay = TimeSpan.FromSeconds(10), Speed = 2, FillBehavior = FillBehavior.Hold, }; var animation2 = new AnimationClip<float> { Animation = new SingleFromToByAnimation { From = 10, To = 20, Duration = TimeSpan.FromSeconds(5.0), }, Delay = TimeSpan.Zero, Speed = 1, FillBehavior = FillBehavior.Hold, }; var animationEx = new Vector2FAnimation { X = animation, Y = animation2, }; var defaultSource = new Vector2F(1, 2); var defaultTarget = new Vector2F(5, 6); var result = animationEx.GetValue(TimeSpan.FromSeconds(0.0), defaultSource, defaultTarget); Assert.AreEqual(defaultSource.X, result.X); // animation has not started. Assert.AreEqual(10.0f, result.Y); // animation2 has started. result = animationEx.GetValue(TimeSpan.FromSeconds(5.0), defaultSource, defaultTarget); Assert.AreEqual(defaultSource.X, result.X); // animation has not started. Assert.AreEqual(20.0f, result.Y); // animation2 has ended. result = animationEx.GetValue(TimeSpan.FromSeconds(5.0), defaultSource, defaultTarget); Assert.AreEqual(defaultSource.X, result.X); // animation has not started. Assert.AreEqual(20.0f, result.Y); // animation2 has ended. result = animationEx.GetValue(TimeSpan.FromSeconds(13.0), defaultSource, defaultTarget); Assert.AreEqual(200, result.X); // animation has ended. Assert.AreEqual(20.0f, result.Y); // animation2 is filling. }
public void GetTotalDurationTest() { var animationClip = new AnimationClip<float>(); Assert.AreEqual(TimeSpan.FromSeconds(0.0), animationClip.GetTotalDuration()); animationClip.Animation = new AnimationClip<float> { Animation = new SingleFromToByAnimation(), Delay = TimeSpan.FromSeconds(10), Duration = TimeSpan.FromSeconds(4.0), Speed = 2.0f, }; Assert.AreEqual(TimeSpan.FromSeconds(12.0), animationClip.GetTotalDuration()); }
public CrossFadeSample(Microsoft.Xna.Framework.Game game) : base(game) { Rectangle bounds = GraphicsService.GraphicsDevice.Viewport.TitleSafeArea; // Set the base value of the _animatedPosition. When the animations are stopped, // _animatedPosition will return to this value. _animatedPosition.Value = new Vector2(bounds.Center.X, bounds.Center.Y); // Create an oscillating horizontal animation. Vector2FromToByAnimation fromToAnimation = new Vector2FromToByAnimation { From = new Vector2(200, bounds.Center.Y), To = new Vector2(bounds.Right - 200, bounds.Center.Y), Duration = TimeSpan.FromSeconds(2), EasingFunction = new CubicEase { Mode = EasingMode.EaseInOut }, }; _horizontalAnimation = new AnimationClip<Vector2>(fromToAnimation) { LoopBehavior = LoopBehavior.Oscillate, Duration = TimeSpan.MaxValue, }; // Create an oscillating vertical animation. fromToAnimation = new Vector2FromToByAnimation { From = new Vector2(bounds.Center.X, 100), To = new Vector2(bounds.Center.X, bounds.Bottom - 100), Duration = TimeSpan.FromSeconds(2), EasingFunction = new CubicEase { Mode = EasingMode.EaseInOut }, }; _verticalAnimation = new AnimationClip<Vector2>(fromToAnimation) { LoopBehavior = LoopBehavior.Oscillate, Duration = TimeSpan.MaxValue, }; // Start the horizontal movement. AnimationService.StartAnimation() returns an // AnimationController. We keep this AnimationController because it is needed to fade-out // the animation. _state = 1; _currentAnimationController = AnimationService.StartAnimation(_horizontalAnimation, _animatedPosition); _currentAnimationController.UpdateAndApply(); // When the animation is stopped, all intermediate animation objects should be recycled. _currentAnimationController.AutoRecycle(); }
public static extern void SetAnimationEvents(AnimationClip clip, AnimationEvent[] events);
public static void SetAnimationType(AnimationClip clip, ModelImporterAnimationType type) { }
internal static extern void SetGenerateMotionCurves(AnimationClip clip, bool value);
public static extern AnimationClipSettings GetAnimationClipSettings(AnimationClip clip);
public static extern AnimationEvent[] GetAnimationEvents(AnimationClip clip);
public static extern AnimationCurve GetEditorCurve(AnimationClip clip, EditorCurveBinding binding);
internal static extern bool GetGenerateMotionCurves(AnimationClip clip);
//GameObject o = null; void OnGUI() { if (_labelstyle == null) { _labelstyle = new GUIStyle(EditorStyles.boldLabel); _labelstyle.fontSize = 11; } scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false); EditorGUILayout.Space(); GUILayout.Label("CutScene Editor:", _labelstyle); EditorGUILayout.Space(); _name = EditorGUILayout.TextField("Name", _name); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Scene", _scene); if (GUILayout.Button("Browser", GUILayout.MaxWidth(80))) { string file = EditorUtility.OpenFilePanel("Select unity scene file", XEditorLibrary.Sce, "unity"); if (file.Length != 0) { file = file.Remove(file.LastIndexOf(".")); _scene = file.Remove(0, file.IndexOf(XEditorLibrary.Sce)); Scene scene = EditorSceneManager.GetActiveScene(); if (scene.name.Length == 0 || !EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { EditorSceneManager.OpenScene(_scene + ".unity"); } } } EditorGUILayout.EndHorizontal(); _auto_end = EditorGUILayout.Toggle("Auto End", _auto_end); EditorGUILayout.Space(); _general_show = EditorGUILayout.Toggle("General Clip", _general_show); if (_general_show) { _general_bigguy = EditorGUILayout.Toggle("General Big Guy", _general_bigguy); if (_general_bigguy) { _camera = Resources.Load("Animation/Main_Camera/Cut_Scene/cutscene_generalshow_bigguy", typeof(AnimationClip)) as AnimationClip; } else { _camera = Resources.Load("Animation/Main_Camera/Cut_Scene/cutscene_generalshow", typeof(AnimationClip)) as AnimationClip; } EditorGUILayout.ObjectField("Camera Clip", _camera, typeof(AnimationClip), true); } else { _camera = EditorGUILayout.ObjectField("Camera Clip", _camera, typeof(AnimationClip), true) as AnimationClip; } if (_camera != null) { _length = _camera.length; EditorGUILayout.LabelField("CutScene Length", _length.ToString("F3") + "s" + "\t" + (_length * XEditorLibrary.FPS).ToString("F1") + "(frame)"); } EditorGUILayout.Space(); _type_mask = (int)(EntitySpecies)EditorGUILayout.EnumMaskField("Type Mask", (EntitySpecies)_type_mask); _mourningborder = EditorGUILayout.Toggle("Mourningborder", _mourningborder); EditorGUILayout.Space(); _fov = EditorGUILayout.FloatField("FieldOfVeiw", _fov); _trigger = (CameraTrigger)EditorGUILayout.EnumPopup("Trigger", _trigger); EditorGUILayout.Space(); _override_bgm = EditorGUILayout.Toggle("Override BGM", _override_bgm); EditorGUILayout.Space(); UpdateScript(); GUILayout.Label("TimeLine:", _labelstyle); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); _type = (XClipType)EditorGUILayout.EnumPopup("Add Clip", _type); if (GUILayout.Button(_content_add, GUILayout.MaxWidth(25), GUILayout.MaxHeight(15))) { if (_camera != null && _name != null && _name.Length > 0) { XCutSceneTimelineWindow window = EditorWindow.GetWindow <XCutSceneTimelineWindow>(@"Timeline At:"); window._total_frame = _camera.length * XEditorLibrary.FPS; window._clip = null; } else { EditorUtility.DisplayDialog("Confirm your selection.", "Please select camera clip or name the cutscene", "Ok"); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); ActorList.Clear(); ActorList.Add("None"); foreach (XClip clip in _clips.Values) { if (clip.Valid) { if (_camera != null) { clip.TimeLineTotal = _camera.length * XEditorLibrary.FPS; clip.OnGUI(GetCurrentData()); if (clip.ClipType == XClipType.Actor || clip.ClipType == XClipType.Player) { int all = ActorList.FindAll(s => s == clip.Name).Count; if (all > 0) { ActorList.Add(clip.Name + " " + (all + 1)); } else { ActorList.Add(clip.Name); } } } } } EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Run")) { if (_name != null && _name.Length > 0 && _camera != null && !EditorApplication.isPlaying) { EditorApplication.ExecuteMenuItem("Edit/Play"); GameObject _cameraObject = GameObject.Find(@"Main Camera"); GameEntrance entrance = _cameraObject.GetComponent <GameEntrance>(); entrance.enabled = false; XCutSceneRunner runner = _cameraObject.AddComponent <XCutSceneRunner>(); _run_data = GetCurrentData(); runner.is_start_by_editor = true; runner.cut_scene_data = _run_data; } } if (GUILayout.Button("Pause")) { if (EditorApplication.isPlaying) { EditorApplication.isPaused = !EditorApplication.isPaused; } } if (GUILayout.Button("Quit")) { if (EditorApplication.isPlaying) { EditorApplication.isPlaying = false; } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Save")) { Save(); } if (GUILayout.Button("Open")) { _file = EditorUtility.OpenFilePanel("Select cutscene file", XEditorLibrary.Cts, "txt"); if (_file.Length != 0) { _open_scene = true; Load(XDataIO <XCutSceneData> .singleton.DeserializeData(_file.Substring(_file.IndexOf("Assets/")))); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndScrollView(); }
void InnerLoad(XCutSceneData data) { _run_data = data; _name = data.Name; _script = data.Script; _scene = data.Scene; _camera = Resources.Load(data.CameraClip, typeof(AnimationClip)) as AnimationClip; _type_mask = data.TypeMask; _auto_end = data.AutoEnd; _general_show = data.GeneralShow; _general_bigguy = data.GeneralBigGuy; _override_bgm = data.OverrideBGM; _mourningborder = data.Mourningborder; _fov = data.FieldOfView; _length = data.Length; _trigger = (CameraTrigger)Enum.Parse(typeof(CameraTrigger), data.Trigger); _clips.Clear(); ActorList.Clear(); ActorList.Add("None"); foreach (XActorDataClip clip in data.Actors) { TimeChecker(clip, data); XClip xclip = AddClip(clip); ActorList.Add(xclip.Name); } foreach (XPlayerDataClip clip in data.Player) { TimeChecker(clip, data); XClip xclip = AddClip(clip); ActorList.Add(xclip.Name); } foreach (XFxDataClip clip in data.Fxs) { TimeChecker(clip, data); AddClip(clip); } foreach (XAudioDataClip clip in data.Audios) { TimeChecker(clip, data); AddClip(clip); } foreach (XSubTitleDataClip clip in data.SubTitle) { TimeChecker(clip, data); AddClip(clip); } foreach (XSlashDataClip clip in data.Slash) { TimeChecker(clip, data); AddClip(clip); } if (_open_scene && _scene != null && _scene.Length != 0) { Scene scene = EditorSceneManager.GetActiveScene(); if (scene.name.Length > 0 && !EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { return; } else { EditorSceneManager.OpenScene(_scene + ".unity"); } _open_scene = false; } }
void DrawAnimation() { if (mData.AnimationSkillList == null) { return; } for (int i = 0; i < mData.AnimationSkillList.Count; i++) { EditorGUILayout.Space(); AnimationSkillStruct _ass = mData.AnimationSkillList[i]; if (_ass.IsFoldout) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.LabelField(_ass.Type.ToString()); if (GUILayout.Button("Preview")) { mASD.PlayAnimaState(_ass.Type); } _ass.AnimationClip = (AnimationClip)EditorGUILayout.ObjectField("AnimaitonClip:", _ass.AnimationClip, typeof(AnimationClip), true); EditorGUILayout.BeginHorizontal(); if (_ass.AnimationClip == null) { _ass.fTime = 1f; } else { _ass.fTime = _ass.AnimationClip.length; } EditorGUILayout.LabelField("NextAnima After:", _ass.fTime.ToString()); _ass.NextType = (NFAnimaStateType)EditorGUILayout.EnumPopup(_ass.NextType); EditorGUILayout.EndHorizontal(); AnimatorController ctl = (AnimatorController)mASD.GetComponent <Animator> ().runtimeAnimatorController; if (ctl != null) { AnimatorStateMachine state_machine = ctl.layers[0].stateMachine; for (int j = 0; j < state_machine.states.Length; ++j) { if (state_machine.states [j].state.name == _ass.Type.ToString()) { String strPath = AssetDatabase.GetAssetPath(_ass.AnimationClip); AnimationClip anim = AssetDatabase.LoadAssetAtPath(strPath, typeof(AnimationClip)) as AnimationClip; state_machine.states [j].state.motion = anim; break; } } } mData.AnimationSkillList[i] = _ass; //添加特效与删除片断 EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("ADD EFFECT")) { EffectStruct _es = new EffectStruct(); _es.LifeTime = 3; _es.isFoldout = true; mData.AnimationSkillList[i].EffectStructList.Add(_es); } if (GUILayout.Button("ADD Audio")) { AudioStruct _es = new AudioStruct(); _es.LifeTime = 3; _es.isFoldout = true; mData.AnimationSkillList[i].AudioStructList.Add(_es); } if (GUILayout.Button("ADD Bullet")) { BulletStruct _es = new BulletStruct(); _es.isFoldout = true; mData.AnimationSkillList[i].BulletStructList.Add(_es); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("ADD Damage")) { DamageStruct _es = new DamageStruct(); _es.isFoldout = true; mData.AnimationSkillList[i].DamageStructList.Add(_es); } if (GUILayout.Button("ADD Movement")) { MovementStruct _es = new MovementStruct(); _es.isFoldout = true; mData.AnimationSkillList[i].MovementStructList.Add(_es); } if (GUILayout.Button("ADD Camera")) { CameraStruct _es = new CameraStruct(); _es.isFoldout = true; mData.AnimationSkillList[i].CameraStructList.Add(_es); } EditorGUILayout.EndHorizontal(); if (mData.AnimationSkillList.Count > 0) { DrawEffect(i); DrawAudio(i); DrawMovement(i); DrawDamage(i); DrawBullet(i); DrawCamera(i); } EditorGUILayout.EndVertical(); } } }
/// <summary> /// 替换动画控制器中使用的Clip /// </summary> public static void replaceStateMotion(AnimatorController ctrl, AnimationClip oldClip, AnimationClip newClip, bool replaceAll = false) { var e = ctrl.layers.GetEnumerator(); while (e.MoveNext()) { AnimatorControllerLayer layer = (AnimatorControllerLayer)e.Current; var child = layer.stateMachine.states.GetEnumerator(); while (child.MoveNext()) { ChildAnimatorState childState = (ChildAnimatorState)child.Current; if (childState.state.motion == oldClip) { ctrl.SetStateEffectiveMotion(childState.state, newClip); if (!replaceAll) { return; } } } } }
public bool RemoveClip(AnimationClip clip) { InvalidateStates(); return(m_States.RemoveClip(clip)); }
public static void SaveAnimationClip(AnimationClip clip, string path) { AssetDatabase.CreateAsset(clip, path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); }
public static void SetClipToAnimatorControllerState(AnimationClip clip, string controllerPath, int layerIdx, string stateName) { var animatorState = GetAnimatorState(controllerPath, stateName, layerIdx); animatorState.motion = clip; }
private static bool GetEditableClip(IEnumerable <TimelineClip> clips, out TimelineClip clip, out AnimationClip animClip) { clip = null; animClip = null; if (clips.Count() != 1) { return(false); } clip = clips.FirstOrDefault(); if (clip == null) { return(false); } if (clip.animationClip != null) { animClip = clip.animationClip; } else if (clip.curves != null && !clip.curves.empty) { animClip = clip.curves; } return(animClip != null); }
/// <summary> /// Generates a clip from this SpriteAnim /// </summary> /// <param name="savePath">Save path.</param> /// <param name="filenamePrefix">Filename prefix.</param> public void GenerateClip(string savePath, string filenamePrefix) { // Output nothing if there is no clip name if (string.IsNullOrEmpty(this.clipName)) { return; } // Output nothing if no frames are defined if (this.animationKeyframes == null || this.animationKeyframes.Length == 0) { return; } // Get the clip to add our Sprite Animation into, or create a new one. AnimationClip builtClip; bool clipIsNew; if (this.savedClip == null) { builtClip = new AnimationClip(); clipIsNew = true; } else { builtClip = this.savedClip; clipIsNew = false; } builtClip.name = this.clipName; builtClip.frameRate = this.Samples; // Set the Looping status of the clip AnimationClipSettings clipSettings = new AnimationClipSettings(); clipSettings.loopTime = this.isLooping; AnimationUtility.SetAnimationClipSettings(builtClip, clipSettings); // Clear ALL existing sprite bindings in the clip EditorCurveBinding[] existingObjectBinding = AnimationUtility.GetObjectReferenceCurveBindings(builtClip); for (int i = 0; i < existingObjectBinding.Length; i++) { EditorCurveBinding currentBinding = existingObjectBinding[i]; if (currentBinding.type == typeof(SpriteRenderer)) { AnimationUtility.SetObjectReferenceCurve(builtClip, currentBinding, null); } } // Clear existing Scale since it will be replaced EditorCurveBinding[] existingValueBindings = AnimationUtility.GetCurveBindings(builtClip); for (int i = 0; i < existingValueBindings.Length; i++) { EditorCurveBinding currentBinding = existingValueBindings[i]; if (currentBinding.type == typeof(Transform) && currentBinding.propertyName == "m_LocalScale.x") { builtClip.SetCurve(currentBinding.path, typeof(Transform), "m_LocalScale", null); break; } } // Initialize the curve property EditorCurveBinding curveBinding = new EditorCurveBinding(); curveBinding.propertyName = "m_Sprite"; curveBinding.path = this.PathToSpriteRenderer; curveBinding.type = typeof(SpriteRenderer); // Build keyframes for the property Sprite[] sprites = AssetDatabaseUtility.LoadSpritesInTextureSorted(this.SourceTexture); ObjectReferenceKeyframe[] keys = this.CreateKeysForKeyframeRanges(sprites, this.animationKeyframes, this.Samples); // Build the clip if valid if (keys != null && keys.Length > 0) { // Set the keyframes to the animation AnimationUtility.SetObjectReferenceCurve(builtClip, curveBinding, keys); // Add scaling to mirror sprites // Need to also restore scale in case a clip was previously mirrored and then unflagged AnimationCurve normalCurve = AnimationCurve.Linear(0.0f, 1.0f, builtClip.length, 1.0f); AnimationCurve mirrorCurve = AnimationCurve.Linear(0.0f, -1.0f, builtClip.length, -1.0f); AnimationCurve xCurve = this.isMirroredX ? mirrorCurve : normalCurve; AnimationCurve yCurve = this.isMirroredY ? mirrorCurve : normalCurve; builtClip.SetCurve(this.PathToSpriteRenderer, typeof(Transform), "localScale.x", xCurve); builtClip.SetCurve(this.PathToSpriteRenderer, typeof(Transform), "localScale.y", yCurve); builtClip.SetCurve(this.PathToSpriteRenderer, typeof(Transform), "localScale.z", normalCurve); // Create or replace the file string filenameSansExtension = filenamePrefix + "_" + this.clipName; if (clipIsNew) { string filename = filenameSansExtension + ".anim"; string fullpath = savePath + filename; AssetDatabase.CreateAsset(builtClip, fullpath); } else { string pathToAsset = AssetDatabase.GetAssetPath(this.savedClip); // renaming file doesn't expect extension for some reason AssetDatabase.RenameAsset(pathToAsset, filenameSansExtension); } // Store reference to created clip to allow overwriting / renaming this.savedClip = builtClip; } else { if (keys == null) { Debug.LogWarning("Skipping clip due to no keys found: " + this.clipName); } else { Debug.LogWarning("Encountered invalid clip. Not enough keys. Skipping clip: " + this.clipName); } } }
public static AnimationCurve GetEditorCurve(AnimationClip clip, string relativePath, Type type, string propertyName) { return(GetEditorCurve(clip, EditorCurveBinding.FloatCurve(relativePath, type, propertyName))); }
static BlobAssetReference <CurvesInfo> ConvertFloatCurves([NotNull] AnimationClip clip) { var bindings = AnimationUtility .GetCurveBindings(clip) .OrderBy(b => b.path) // Ensures we are processing one target at a time .ThenBy(b => b.propertyName) // Ensures we are processing vector properties together .ToArray(); var numBindings = bindings.Length; var blobAssetRef = BlobAssetReference <CurvesInfo> .Null; if (numBindings == 0) { return(blobAssetRef); } var targetPaths = new List <FixedString512>(numBindings); var animationBindingsConvertedNames = new List <FixedString512>(numBindings); var keyframeCurves = new List <KeyframeCurve>(numBindings); var scaleRequired = false; var numKeys = 0; var numBindingsAfterConversion = numBindings; for (var i = 0; i < numBindings; i++) { var binding = bindings[i]; var rotationMode = TinyAnimationEditorBridge.GetRotationMode(binding); // Handle non quaternion rotation. if (rotationMode != TinyAnimationEditorBridge.RotationMode.Undefined && rotationMode != TinyAnimationEditorBridge.RotationMode.RawQuaternions) { // TODO: Handle other modes when/if they show up if (rotationMode == TinyAnimationEditorBridge.RotationMode.RawEuler) { var currentPath = binding.path; var xBinding = default(EditorCurveBinding); var yBinding = default(EditorCurveBinding); var zBinding = default(EditorCurveBinding); var rotationBindingOffset = 0; while (rotationBindingOffset < 3 && i + rotationBindingOffset < numBindings) { var nextBinding = bindings[i + rotationBindingOffset]; if (!string.Equals(currentPath, nextBinding.path, StringComparison.Ordinal) || TinyAnimationEditorBridge.GetRotationMode(nextBinding) != TinyAnimationEditorBridge.RotationMode.RawEuler) { // Binding is either for a different target or not a rotation: skip! break; } if (nextBinding.propertyName.EndsWith(".x", StringComparison.Ordinal)) { xBinding = nextBinding; } else if (nextBinding.propertyName.EndsWith(".y", StringComparison.Ordinal)) { yBinding = nextBinding; } else { zBinding = nextBinding; } rotationBindingOffset++; } var xCurveOriginal = xBinding != default ? AnimationUtility.GetEditorCurve(clip, xBinding) : new AnimationCurve(new UnityEngine.Keyframe(0.0f, 0.0f), new UnityEngine.Keyframe(clip.length, 0.0f)); var yCurveOriginal = yBinding != default ? AnimationUtility.GetEditorCurve(clip, yBinding) : new AnimationCurve(new UnityEngine.Keyframe(0.0f, 0.0f), new UnityEngine.Keyframe(clip.length, 0.0f)); var zCurveOriginal = zBinding != default ? AnimationUtility.GetEditorCurve(clip, zBinding) : new AnimationCurve(new UnityEngine.Keyframe(0.0f, 0.0f), new UnityEngine.Keyframe(clip.length, 0.0f)); var xCurveNew = new AnimationCurve(); var yCurveNew = new AnimationCurve(); var zCurveNew = new AnimationCurve(); var wCurveNew = new AnimationCurve(); // We *need* to resample at the framerate when converting from Euler to Quaternion var step = clip.length / clip.frameRate; var time = 0.0f; do { EvaluateAndConvert(time); time += step; }while (time <= clip.length - step); // Setting the last frame explicitly to avoid precision errors EvaluateAndConvert(clip.length); void EvaluateAndConvert(float t) { var euler = new float3(xCurveOriginal.Evaluate(t), yCurveOriginal.Evaluate(t), zCurveOriginal.Evaluate(t)); var quat = quaternion.Euler(math.radians(euler)); xCurveNew.AddKey(new UnityEngine.Keyframe(t, quat.value.x, Mathf.Infinity, Mathf.Infinity)); yCurveNew.AddKey(new UnityEngine.Keyframe(t, quat.value.y, Mathf.Infinity, Mathf.Infinity)); zCurveNew.AddKey(new UnityEngine.Keyframe(t, quat.value.z, Mathf.Infinity, Mathf.Infinity)); wCurveNew.AddKey(new UnityEngine.Keyframe(t, quat.value.w, Mathf.Infinity, Mathf.Infinity)); } var resampledKeysCount = xCurveNew.length; var xPropName = GetConvertedName(binding.type, TinyAnimationEditorBridge.CreateRawQuaternionsBindingName("x")); var yPropName = GetConvertedName(binding.type, TinyAnimationEditorBridge.CreateRawQuaternionsBindingName("y")); var zPropName = GetConvertedName(binding.type, TinyAnimationEditorBridge.CreateRawQuaternionsBindingName("z")); var wPropName = GetConvertedName(binding.type, TinyAnimationEditorBridge.CreateRawQuaternionsBindingName("w")); animationBindingsConvertedNames.Add(new FixedString512(xPropName)); animationBindingsConvertedNames.Add(new FixedString512(yPropName)); animationBindingsConvertedNames.Add(new FixedString512(zPropName)); animationBindingsConvertedNames.Add(new FixedString512(wPropName)); keyframeCurves.Add(xCurveNew.ToKeyframeCurve()); keyframeCurves.Add(yCurveNew.ToKeyframeCurve()); keyframeCurves.Add(zCurveNew.ToKeyframeCurve()); keyframeCurves.Add(wCurveNew.ToKeyframeCurve()); numKeys += resampledKeysCount * 4; var targetPath = string.IsNullOrEmpty(binding.path) ? string.Empty : binding.path; targetPaths.Add(targetPath); targetPaths.Add(targetPath); targetPaths.Add(targetPath); targetPaths.Add(targetPath); // How many new bindings were added by this conversion? // e.g.: // Euler bindings to [x,y,z] converts to quaternion bindings [x,y,z,w], so we added 1 new binding // Euler bindings to [y] converts to quaternion bindings [x,y,z,w], so we added 3 new binding numBindingsAfterConversion += 4 - rotationBindingOffset; // Skip already processed bindings i += rotationBindingOffset - 1; } else { throw new InvalidOperationException($"Rotation mode: {rotationMode} is not supported."); } } else { // Note: Empty string maps to self in Transform.Find() targetPaths.Add(string.IsNullOrEmpty(binding.path) ? string.Empty : binding.path); var bindingPropertyName = binding.propertyName; var convertedName = GetConvertedName(binding.type, bindingPropertyName); animationBindingsConvertedNames.Add(new FixedString512(convertedName)); var animationCurve = AnimationUtility.GetEditorCurve(clip, binding); var curve = animationCurve.ToKeyframeCurve(); keyframeCurves.Add(curve); numKeys += curve.Length; if (!scaleRequired && binding.type == typeof(Transform) && bindingPropertyName.StartsWith("m_LocalScale.", StringComparison.Ordinal)) { scaleRequired = true; } } } using (var blobBuilder = new BlobBuilder(Allocator.Temp)) { ref var builderRoot = ref blobBuilder.ConstructRoot <CurvesInfo>(); var keyframesBuilder = blobBuilder.Allocate(ref builderRoot.Keyframes, numKeys); var curveOffsetsBuilder = blobBuilder.Allocate(ref builderRoot.CurveOffsets, numBindingsAfterConversion); var bindingNamesBuilder = blobBuilder.Allocate(ref builderRoot.BindingNames, numBindingsAfterConversion); var targetPathsBuilder = blobBuilder.Allocate(ref builderRoot.TargetGameObjectPaths, numBindingsAfterConversion); // We don't care about that field in this case blobBuilder.Allocate(ref builderRoot.AnimatedAssetGroupings, 0); for (int bindingIndex = 0, keyIndex = 0, curveOffset = 0; bindingIndex < numBindingsAfterConversion; ++bindingIndex) { var keyframeCurve = keyframeCurves[bindingIndex]; for (var i = 0; i < keyframeCurve.Length; ++i) { keyframesBuilder[keyIndex++] = keyframeCurve[i]; } curveOffsetsBuilder[bindingIndex] = curveOffset; curveOffset += keyframeCurve.Length; bindingNamesBuilder[bindingIndex] = animationBindingsConvertedNames[bindingIndex]; targetPathsBuilder[bindingIndex] = targetPaths[bindingIndex]; } builderRoot.ConversionActions = scaleRequired ? RequiredConversionActions.PatchScale : RequiredConversionActions.None; blobAssetRef = blobBuilder.CreateBlobAssetReference <CurvesInfo>(Allocator.Persistent); }
public static extern EditorCurveBinding[] GetCurveBindings(AnimationClip clip);
void ProcessClipPosition(AnimationClip clip, string objectPathName) { // Access the position curves for the given object EditorCurveBinding bindX = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalPosition.x"); EditorCurveBinding bindY = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalPosition.y"); EditorCurveBinding bindZ = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalPosition.z"); AnimationCurve curveX = AnimationUtility.GetEditorCurve(clip, bindX); AnimationCurve curveY = AnimationUtility.GetEditorCurve(clip, bindY); AnimationCurve curveZ = AnimationUtility.GetEditorCurve(clip, bindZ); if (curveX == null || curveY == null || curveZ == null) { LogInfo("WARNING: animated object [" + objectPathName + "] doesn't have correct position curves"); return; } int keyframes = curveX.length; if (curveY.length != keyframes || curveZ.length != keyframes) { LogInfo("WARNING: animated object [" + objectPathName + "] doesn't have the same keyframes for XYZ position curves. " + "x: " + curveX.length + " y:" + curveY.length + " z:" + curveZ.length); return; } if (keyframes == 0) { return; } bool isFirstLevel = objectPathName.IndexOf('/') < 0; // Retrieve the keyframes of the position curves Keyframe[] keysX = curveX.keys; Keyframe[] keysY = curveY.keys; Keyframe[] keysZ = curveZ.keys; for (int k = 0; k < keyframes; k++) { Vector3 pos = new Vector3(keysX[k].value, keysY[k].value, keysZ[k].value); if (isFirstLevel) { if (m_zReverse) { // Turn around the local position pos = Quaternion.AngleAxis(180.0f, Vector3.up) * pos; // Tangents X and Z must be adjusted to match the new orientation InvertTangents(ref keysX[k]); InvertTangents(ref keysZ[k]); } } else { // Position in the 2nd level objects must compensante the X+90 rotation that // has been applied to 1st level. Quaternion q = Quaternion.AngleAxis(-90.0f, Vector3.right); if (m_zReverse) { q = Quaternion.AngleAxis(180.0f, Vector3.up) * q; } pos = q * pos; // Adjust the tangents of the Y and Z curves as result of the rotation X-90 float inT = keysZ[k].inTangent; float outT = keysZ[k].outTangent; keysZ[k].inTangent = -keysY[k].inTangent; keysZ[k].outTangent = -keysY[k].outTangent; keysY[k].inTangent = inT; keysY[k].outTangent = outT; // If the model has been turned around (180º) the tangents for X and Z position // curves must be inverted as well. if (m_zReverse) { InvertTangents(ref keysX[k]); InvertTangents(ref keysZ[k]); } } if (m_floatFix) { pos = FixVector3(pos, m_floatFixThreshold); } keysX[k].value = pos.x; keysY[k].value = pos.y; keysZ[k].value = pos.z; } // Assign the fixed keyframes back to the position curves curveX.keys = keysX; curveY.keys = keysY; curveZ.keys = keysZ; AnimationUtility.SetEditorCurve(clip, bindX, curveX); AnimationUtility.SetEditorCurve(clip, bindY, curveY); AnimationUtility.SetEditorCurve(clip, bindZ, curveZ); }
internal static extern AnimationClipStats GetAnimationClipStats(AnimationClip clip);
// Animation manipulation functions ------------------------------------------------------------ void ProcessClipRotation(AnimationClip clip, string objectPathName) { // Access the rotation curves for the given object EditorCurveBinding bindX = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalRotation.x"); EditorCurveBinding bindY = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalRotation.y"); EditorCurveBinding bindZ = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalRotation.z"); EditorCurveBinding bindW = EditorCurveBinding.FloatCurve(objectPathName, typeof(Transform), "m_LocalRotation.w"); AnimationCurve curveX = AnimationUtility.GetEditorCurve(clip, bindX); AnimationCurve curveY = AnimationUtility.GetEditorCurve(clip, bindY); AnimationCurve curveZ = AnimationUtility.GetEditorCurve(clip, bindZ); AnimationCurve curveW = AnimationUtility.GetEditorCurve(clip, bindW); if (curveX == null || curveY == null || curveZ == null || curveW == null) { LogInfo("WARNING: animated object [" + objectPathName + "] doesn't have correct rotation curves"); return; } int keyframes = curveX.length; if (curveY.length != keyframes || curveZ.length != keyframes || curveW.length != keyframes) { LogInfo("WARNING: animated object [" + objectPathName + "] doesn't have the same keyframes for XYZW rotation curves. " + "x: " + curveX.length + " y:" + curveY.length + " z:" + curveZ.length + " w:" + curveW.length); return; } if (keyframes == 0) { return; } bool isFirstLevel = objectPathName.IndexOf('/') < 0; // Retrieve the keyframes of the rotation curves Keyframe[] keysX = curveX.keys; Keyframe[] keysY = curveY.keys; Keyframe[] keysZ = curveZ.keys; Keyframe[] keysW = curveW.keys; for (int k = 0; k < keyframes; k++) { Quaternion rot = new Quaternion(keysX[k].value, keysY[k].value, keysZ[k].value, keysW[k].value); // Convert rotation from Blender (Right-handed) to Unity (Left-handed). // Must have in mind the -90 rotation in the first level objects. if (isFirstLevel) { // This is the same as rot = rot * Quaternion.AngleAxis(90.0f, Vector3.right); // Idea courtesy of apatriarca (www.gamedev.net/topic/654682-quaternions-convert-between-left-right-handed-without-using-euler/) float s45 = Mathf.Sin(Mathf.PI / 4.0f); keysX[k].value = (rot.w + rot.x) * s45; keysY[k].value = (rot.y + rot.z) * s45; keysZ[k].value = (rot.z - rot.y) * s45; keysW[k].value = (rot.w - rot.x) * s45; // Perform the same operation with tangents float inTx = (keysW[k].inTangent + keysX[k].inTangent) * s45; float inTy = (keysY[k].inTangent + keysZ[k].inTangent) * s45; float inTz = (keysZ[k].inTangent - keysY[k].inTangent) * s45; float inTw = (keysW[k].inTangent - keysX[k].inTangent) * s45; float outTx = (keysW[k].outTangent + keysX[k].outTangent) * s45; float outTy = (keysY[k].outTangent + keysZ[k].outTangent) * s45; float outTz = (keysZ[k].outTangent - keysY[k].outTangent) * s45; float outTw = (keysW[k].outTangent - keysX[k].outTangent) * s45; keysX[k].inTangent = inTx; keysY[k].inTangent = inTy; keysZ[k].inTangent = inTz; keysW[k].inTangent = inTw; keysX[k].outTangent = outTx; keysY[k].outTangent = outTy; keysZ[k].outTangent = outTz; keysW[k].outTangent = outTw; } else { // Convert the rotation from Blender to Unity keysX[k].value = -rot.x; keysY[k].value = -rot.z; keysZ[k].value = rot.y; keysW[k].value = -rot.w; // Adjust and exchange tangents accordingly InvertTangents(ref keysX[k]); InvertTangents(ref keysW[k]); float inT = keysY[k].inTangent; float outT = keysY[k].outTangent; keysY[k].inTangent = -keysZ[k].inTangent; keysY[k].outTangent = -keysZ[k].outTangent; keysZ[k].inTangent = inT; keysZ[k].outTangent = outT; } // Turn around the forward axis if required if (m_zReverse) { keysX[k].value = -keysX[k].value; keysZ[k].value = -keysZ[k].value; InvertTangents(ref keysX[k]); InvertTangents(ref keysZ[k]); } } // Assign the fixed keyframes back to the rotation curves curveX.keys = keysX; curveY.keys = keysY; curveZ.keys = keysZ; curveW.keys = keysW; AnimationUtility.SetEditorCurve(clip, bindX, curveX); AnimationUtility.SetEditorCurve(clip, bindY, curveY); AnimationUtility.SetEditorCurve(clip, bindZ, curveZ); AnimationUtility.SetEditorCurve(clip, bindW, curveW); }
public static AnimationClipCurveData[] GetAllCurves(AnimationClip clip) { bool includeCurveData = true; return(GetAllCurves(clip, includeCurveData)); }
void Start() { Animation anim = gameObject.AddComponent <Animation>(); // Assign a simple waving animation to the bottom bone gameObject.AddComponent <SkinnedMeshRenderer>(); SkinnedMeshRenderer rend = GetComponent <SkinnedMeshRenderer>(); // Build basic mesh Mesh mesh = new Mesh(); mesh.vertices = new Vector3[] { new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(-1, 5, 0), new Vector3(1, 5, 0) }; mesh.uv = new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1) }; mesh.triangles = new int[] { 0, 1, 2, 1, 3, 2 }; mesh.RecalculateNormals(); // Assign mesh to mesh filter & renderer rend.material = new Material(Shader.Find("Diffuse")); // Assign bone weights to mesh // We use 2 bones. One for the lower vertices, one for the upper vertices. BoneWeight[] weights = new BoneWeight[4]; weights[0].boneIndex0 = 0; weights[0].weight0 = 0.3f; weights[1].boneIndex0 = 0; weights[1].weight0 = 1; weights[2].boneIndex0 = 1; weights[2].weight0 = 0.8f; weights[3].boneIndex0 = 1; weights[3].weight0 = 1; // A BoneWeights array (weights) was just created and the boneIndex and weight assigned. // The weights array will now be assigned to the boneWeights array in the Mesh. mesh.boneWeights = weights; // Create Bone Transforms and Bind poses // One bone at the bottom and one at the top Transform[] bones = new Transform[2]; Matrix4x4[] bindPoses = new Matrix4x4[2]; bones[0] = new GameObject("Lower").transform; bones[0].parent = transform; // Set the position relative to the parent bones[0].localRotation = Quaternion.identity; bones[0].localPosition = Vector3.zero; // The bind pose is bone's inverse transformation matrix // In this case the matrix we also make this matrix relative to the root // So that we can move the root game object around freely bindPoses[0] = bones[0].worldToLocalMatrix * transform.localToWorldMatrix; bones[1] = new GameObject("Upper").transform; bones[1].parent = transform; // Set the position relative to the parent bones[1].localRotation = Quaternion.identity; bones[1].localPosition = new Vector3(0, 5, 0); // The bind pose is bone's inverse transformation matrix // In this case the matrix we also make this matrix relative to the root // So that we can move the root game object around freely bindPoses[1] = bones[1].worldToLocalMatrix * transform.localToWorldMatrix; // assign the bindPoses array to the bindposes array which is part of the mesh. mesh.bindposes = bindPoses; // Assign bones and bind poses rend.bones = bones; rend.sharedMesh = mesh; AnimationCurve curve = new AnimationCurve(); curve.keys = new Keyframe[] { new Keyframe(0, 0, 0, 0), new Keyframe(3, 60, 0, 0), new Keyframe(6, 0.0F, 0, 0) }; // Create the clip with the curve AnimationClip clip = new AnimationClip(); clip.legacy = true; clip.wrapMode = WrapMode.Loop; //clip.SetCurve("Upper", typeof(Transform), "localPosition.z", curve); clip.SetCurve("Upper", typeof(Transform), "localEulerAngles.x", curve); // Add and play the clip anim.AddClip(clip, "test"); anim.Play("test"); }
public static void SetEditorCurve(AnimationClip clip, string relativePath, Type type, string propertyName, AnimationCurve curve) { SetEditorCurve(clip, EditorCurveBinding.FloatCurve(relativePath, type, propertyName), curve); }
private static void readMesh(string path, string filename, string texturepath) { string importingAssetsDir; if (File.Exists(path + "/" + filename)) { Assimp.PostProcessSteps flags = ( // Assimp.PostProcessSteps.MakeLeftHanded | Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.CalculateTangentSpace | Assimp.PostProcessSteps.GenerateUVCoords | Assimp.PostProcessSteps.GenerateSmoothNormals | Assimp.PostProcessSteps.RemoveComponent | Assimp.PostProcessSteps.JoinIdenticalVertices); IntPtr config = Assimp.aiCreatePropertyStore(); Assimp.aiSetImportPropertyFloat(config, Assimp.AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, 60.0f); Assimp.aiSetImportPropertyInteger(config, Assimp.AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4); // IntPtr scene = Assimp.aiImportFile(path + "/" + filename, (uint)flags); IntPtr scene = Assimp.aiImportFileWithProperties(path + "/" + filename, (uint)flags, config); Assimp.aiReleasePropertyStore(config); if (scene == null) { Debug.LogWarning("failed to read file: " + path + "/" + filename); return; } else { string nm = Path.GetFileNameWithoutExtension(filename); importingAssetsDir = "Assets/Prefabs/" + nm + "/"; if (saveAssets) { if (!Directory.Exists(importingAssetsDir)) { Directory.CreateDirectory(importingAssetsDir); } AssetDatabase.Refresh(); } GameObject ObjectRoot = new GameObject(nm); GameObject meshContainer = new GameObject(nm + "_Mesh"); meshContainer.transform.parent = ObjectRoot.transform; // List<CombineInstance> combineInstances = new List<CombineInstance>(); List <Material> materials = new List <Material>(); List <AssimpMesh> MeshList = new List <AssimpMesh>(); for (int i = 0; i < Assimp.aiScene_GetNumMaterials(scene); i++) { string matName = Assimp.aiMaterial_GetName(scene, i); matName = nm + "_mat" + i; // string fname = Path.GetFileNameWithoutExtension(Assimp.aiMaterial_GetTexture(scene, i, (int)Assimp.TextureType.Diffuse)); string fname = Path.GetFileName(Assimp.aiMaterial_GetTexture(scene, i, (int)Assimp.TextureType.Diffuse)); Debug.Log("texture " + fname + "Material :" + matName); Color ambient = Assimp.aiMaterial_GetAmbient(scene, i); Color diffuse = Assimp.aiMaterial_GetDiffuse(scene, i); Color specular = Assimp.aiMaterial_GetSpecular(scene, i); Color emissive = Assimp.aiMaterial_GetEmissive(scene, i); Material mat = new Material(Shader.Find("Diffuse")); mat.name = matName; mat.color = diffuse; // Debug.Log(ambient.ToString()); // Debug.Log(diffuse.ToString()); // Debug.Log(specular.ToString()); // Debug.Log(emissive.ToString()); string texturename = path + "/" + fname; Texture2D tex = null; if (File.Exists(texturename)) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename, typeof(Texture2D)); } else if (File.Exists(texturename + ".PNG")) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename + ".PNG", typeof(Texture2D)); } else if (File.Exists(texturename + ".JPG")) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename + ".JPG", typeof(Texture2D)); } else if (File.Exists(texturename + ".BMP")) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename + ".BMP", typeof(Texture2D)); } else if (File.Exists(texturename + ".TGA")) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename + ".TGA", typeof(Texture2D)); } else if (File.Exists(texturename + ".DDS")) { tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturename + ".DDS", typeof(Texture2D)); } if (tex != null) { Debug.Log("LOAD (" + texturename + ") texture"); mat.SetTexture("_MainTex", tex); } else { Debug.LogError("Fail LOAD (" + texturename + ") error"); } if (saveAssets) { string materialAssetPath = AssetDatabase.GenerateUniqueAssetPath(importingAssetsDir + mat.name + ".asset"); AssetDatabase.CreateAsset(mat, materialAssetPath); } materials.Add(mat); } AssetDatabase.Refresh(); if (Assimp.aiScene_GetRootNode(scene) != null) { ObjectRoot.transform.position = Assimp.aiNode_GetPosition(Assimp.aiScene_GetRootNode(scene)); //assimp quaternion is w,x,y,z and unity x,y,z,w bu int this lib i fix this for unity Quaternion assQuad = Assimp.aiNode_GetRotation(Assimp.aiScene_GetRootNode(scene)); ObjectRoot.transform.rotation = assQuad; GameObject skeleton = new GameObject("Skeleton"); skeleton.transform.parent = ObjectRoot.transform; processNodes(scene, Assimp.aiScene_GetRootNode(scene), ref listJoints); for (int i = 0; i < listJoints.Count; i++) { AssimpJoint joint = listJoints[i]; Transform bone = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform; // bone.transform.localScale.Set(2,2,2); // Transform bone = new GameObject(joint.Name).transform; // DebugBone debug = (DebugBone)bone.gameObject.AddComponent(typeof(DebugBone)); bone.name = joint.Name; bone.parent = skeleton.transform; if (getBoneByName(joint.parentName) != null) { int index = findBoneByName(joint.parentName); bone.parent = joint.parent.transform; } bone.localPosition = joint.Position; bone.localRotation = joint.Orientation; joint.transform = bone; } } if (Assimp.aiScene_HasMeshes(scene)) { for (int i = 0; i < Assimp.aiScene_GetNumMeshes(scene); i++) { string name = "Mesh_"; name += i.ToString(); bool HasNormals = Assimp.aiMesh_HasNormals(scene, i); bool HasTexCoord = Assimp.aiMesh_HasTextureCoords(scene, i, 0); bool HasFaces = Assimp.aiMesh_HasFaces(scene, i); AssimpMesh mesh = new AssimpMesh(meshContainer, ObjectRoot, name); mesh.setmaterial(materials[Assimp.aiMesh_GetMaterialIndex(scene, i)]); MeshList.Add(mesh); for (int v = 0; v < Assimp.aiMesh_GetNumVertices(scene, i); v++) { Vector3 vertex = Assimp.aiMesh_Vertex(scene, i, v); Vector3 n = Assimp.aiMesh_Normal(scene, i, v); float x = Assimp.aiMesh_TextureCoordX(scene, i, v, 0); float y = Assimp.aiMesh_TextureCoordY(scene, i, v, 0); Vector3 binormalf = Assimp.aiMesh_Bitangent(scene, i, v); Vector3 tangentf = Assimp.aiMesh_Tangent(scene, i, v); Vector4 outputTangent = new Vector4(tangentf.x, tangentf.y, tangentf.z, 0.0F); float dp = Vector3.Dot(Vector3.Cross(n, tangentf), binormalf); if (dp > 0.0F) { outputTangent.w = 1.0F; } else { outputTangent.w = -1.0F; } mesh.addVertex(vertex, n, outputTangent, new Vector2(x, y)); } for (int f = 0; f < Assimp.aiMesh_GetNumFaces(scene, i); f++) { int a = Assimp.aiMesh_Indice(scene, i, f, 0); int b = Assimp.aiMesh_Indice(scene, i, f, 1); int c = Assimp.aiMesh_Indice(scene, i, f, 2); mesh.addFace(a, b, c); } //**** int numBone = Assimp.aiMesh_GetNumBones(scene, i); for (int b = 0; b < numBone; b++) { string bname = Assimp.aiMesh_GetBoneName(scene, i, b); AssimpJoint joint = getBoneByName(bname); int boneID = findBoneByName(bname); int numWeights = Assimp.aiMesh_GetNumBoneWeights(scene, i, b); for (int w = 0; w < numWeights; w++) { float Weight = Assimp.aiMesh_GetBoneWeight(scene, i, b, w); int VertexId = Assimp.aiMesh_GetBoneVertexId(scene, i, b, w); mesh.addBone(VertexId, boneID, Weight); } } for (int j = 0; j < listJoints.Count; j++) { AssimpJoint joint = listJoints[j]; mesh.addJoint(joint); } //********** mesh.BuilSkin(); mesh.build(); if (saveAssets) { string meshAssetPath = AssetDatabase.GenerateUniqueAssetPath(importingAssetsDir + mesh.name + ".asset"); AssetDatabase.CreateAsset(mesh.geometry, meshAssetPath); } mesh.dispose(); } } //create key frames if (Assimp.aiScene_HasAnimation(scene)) { Animation anim = (UnityEngine.Animation)ObjectRoot.AddComponent(typeof(Animation)); int numAnimation = Assimp.aiScene_GetNumAnimations(scene); Debug.Log("count animation :" + numAnimation); for (int a = 0; a < numAnimation; a++) { AnimationClip clip = new AnimationClip(); string anima = Assimp.aiAnim_GetName(scene, a); clip.name = nm + "_" + anima + "_" + a; clip.legacy = true; clip.wrapMode = WrapMode.Loop; float tinks = (float)Assimp.aiAnim_GetTicksPerSecond(scene, a); if (tinks <= 1f) { tinks = 1f; } float fps = tinks; clip.frameRate = tinks; Debug.Log("animation fps :" + fps); int numchannels = Assimp.aiAnim_GetNumChannels(scene, a); for (int i = 0; i < numchannels; i++) { string name = Assimp.aiAnim_GetChannelName(scene, a, i); AssimpJoint joint = getBoneByName(name); // Debug.Log(String.Format("anim channel {0} bone name {1} poskeys {2} rotkeys{2}", i, name, Assimp.aiAnim_GetNumPositionKeys(scene, 0, i), Assimp.aiAnim_GetNumRotationKeys(scene, 0, i))); //public static bool ignoreRotations = true; // public static bool ignorePositions = true; // public static bool ignoreScalling = true; if (!ignoreScalling) { if (Assimp.aiAnim_GetNumScalingKeys(scene, a, i) != 0) { AnimationCurve scaleXcurve = new AnimationCurve(); AnimationCurve scaleYcurve = new AnimationCurve(); AnimationCurve scaleZcurve = new AnimationCurve(); for (int j = 0; j < Assimp.aiAnim_GetNumScalingKeys(scene, a, i); j++) { float time = (float)Assimp.aiAnim_GetScalingFrame(scene, a, i, j);// / fps; Vector3 scale = Assimp.aiAnim_GetScalingKey(scene, a, i, j); scaleXcurve.AddKey(time, scale.x); scaleYcurve.AddKey(time, scale.y); scaleZcurve.AddKey(time, scale.z); } clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "m_LocalScale.x", scaleXcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "m_LocalScale.y", scaleYcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "m_LocalScale.z", scaleZcurve); } } if (!ignorePositions) { if (Assimp.aiAnim_GetNumPositionKeys(scene, a, i) != 0) { AnimationCurve posXcurve = new AnimationCurve(); AnimationCurve posYcurve = new AnimationCurve(); AnimationCurve posZcurve = new AnimationCurve(); for (int j = 0; j < Assimp.aiAnim_GetNumPositionKeys(scene, a, i); j++) { float time = (float)Assimp.aiAnim_GetPositionFrame(scene, a, i, j);// / fps; Vector3 pos = Assimp.aiAnim_GetPositionKey(scene, a, i, j); posXcurve.AddKey(time, pos.x); posYcurve.AddKey(time, pos.y); posZcurve.AddKey(time, pos.z); } clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localPosition.x", posXcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localPosition.y", posYcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localPosition.z", posZcurve); } } if (!ignoreRotations) { if (Assimp.aiAnim_GetNumRotationKeys(scene, a, i) != 0) { AnimationCurve rotXcurve = new AnimationCurve(); AnimationCurve rotYcurve = new AnimationCurve(); AnimationCurve rotZcurve = new AnimationCurve(); AnimationCurve rotWcurve = new AnimationCurve(); for (int j = 0; j < Assimp.aiAnim_GetNumRotationKeys(scene, a, i); j++) { float time = (float)Assimp.aiAnim_GetRotationFrame(scene, a, i, j);// / fps; Quaternion rotation = Assimp.aiAnim_GetRotationKey(scene, a, i, j); rotXcurve.AddKey(time, rotation.x); rotYcurve.AddKey(time, rotation.y); rotZcurve.AddKey(time, rotation.z); rotWcurve.AddKey(time, rotation.w); } clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localRotation.x", rotXcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localRotation.y", rotYcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localRotation.z", rotZcurve); clip.SetCurve("Skeleton/" + joint.Path, typeof(Transform), "localRotation.w", rotWcurve); } } } clip.EnsureQuaternionContinuity(); anim.AddClip(clip, clip.name); anim.clip = clip; string clipAssetPath = AssetDatabase.GenerateUniqueAssetPath(importingAssetsDir + clip.name + ".asset"); AssetDatabase.CreateAsset(clip, clipAssetPath); // AssetDatabase.CreateAsset(clip, "Assets/Models/" + nm +"_"+a+ ".anim"); // AssetDatabase.SaveAssets(); } } if (saveAssets) { string prefabPath = AssetDatabase.GenerateUniqueAssetPath(importingAssetsDir + filename + ".prefab"); var prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); PrefabUtility.ReplacePrefab(ObjectRoot, prefab, ReplacePrefabOptions.ConnectToPrefab); AssetDatabase.Refresh(); } MeshList.Clear(); } listJoints.Clear(); Assimp.aiReleaseImport(scene); Debug.LogWarning(path + "/" + filename + " Imported ;) "); } }
public static void RebuildUnitAnimController() { // Warn about controller overwrite. // Gather all clips from folder. // Gather all anims from anim decl and build those with clips. string clipsAssetPath = "Assets/Animations/Unit"; string[] assetGUIDs = AssetDatabase.FindAssets("", new string[] { clipsAssetPath }); Dictionary <string, AnimationClip> clips = new Dictionary <string, AnimationClip>(); foreach (string str in assetGUIDs) { string assetPath = AssetDatabase.GUIDToAssetPath(str); if (assetPath.EndsWith(".fbx")) { UnityEngine.Object[] allAssets = AssetDatabase.LoadAllAssetsAtPath(assetPath); foreach (UnityEngine.Object obj in allAssets) { if (obj.GetType() == typeof(AnimationClip)) { AnimationClip clip = obj as AnimationClip; clips[clip.name] = clip; } } } } AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath("Assets/Animations/unitAnimController.controller"); AnimatorStateMachine rootState = controller.layers[0].stateMachine; for (int i = 0; i < Animation.FBXEntries.Length; ++i) { AnimationClip clip = null; if (clips.TryGetValue(Animation.FBXEntries[i].mName, out clip)) { Debug.Log("Match for: " + clip.name); AnimatorState state = rootState.AddState(clip.name); state.motion = clip; state.speed = Animation.FBXEntries[i].mSpeed; } } string defaultState = Animation.GetDefaultAnimation(); for (int i = 0; i < rootState.states.Length; ++i) { if (rootState.states[i].state.name == defaultState) { rootState.defaultState = rootState.states[i].state; break; } } //AnimatorStateMachine rootState = controller.layers[0].stateMachine; //AnimatorState state = rootState.AddState("run"); //state.add = newClips[0]; //controller //AnimationClip a = new AnimationClip(); //controller.AddMotion(a); //AnimationClip[] clip = AssetDatabase.LoadAllAssetsAtPath("Assets/Models/new_char_rig.fbx/", typeof(AnimationClip)) as AnimationClip[]; //controller.AddMotion(clip); }
void OnPostprocessAnimation(GameObject root, AnimationClip clip) { }
private static IEnumerator LoadAsync(string json, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, Action <GameObject, AnimationClip[]> onFinished, Action <float> onProgress = null) { // Threaded deserialization Task <GLTFObject> deserializeTask = new Task <GLTFObject>(() => JsonConvert.DeserializeObject <GLTFObject>(json)); deserializeTask.Start(); while (!deserializeTask.IsCompleted) { yield return(null); } GLTFObject gltfObject = deserializeTask.Result; CheckExtensions(gltfObject); // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = filepath != null?Directory.GetParent(filepath).ToString() + "/" : null; importSettings.shaderOverrides.CacheDefaultShaders(); // Setup import tasks List <ImportTask> importTasks = new List <ImportTask>(); GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart); importTasks.Add(bufferTask); GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); importTasks.Add(bufferViewTask); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); importTasks.Add(accessorTask); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); importTasks.Add(imageTask); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); importTasks.Add(textureTask); GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); importTasks.Add(materialTask); GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, bufferViewTask, materialTask, importSettings); importTasks.Add(meshTask); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); importTasks.Add(skinTask); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); importTasks.Add(nodeTask); // Ignite for (int i = 0; i < importTasks.Count; i++) { TaskSupervisor(importTasks[i], onProgress).RunCoroutine(); } // Wait for all tasks to finish while (!importTasks.All(x => x.IsCompleted)) { yield return(null); } // Fire onFinished when all tasks have completed GameObject root = nodeTask.Result.GetRoot(); GLTFAnimation.ImportResult[] animationResult = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings); AnimationClip[] animations = new AnimationClip[0]; if (animationResult != null) { animations = animationResult.Select(x => x.clip).ToArray(); } if (onFinished != null) { onFinished(nodeTask.Result.GetRoot(), animations); } // Close file streams foreach (var item in bufferTask.Result) { item.Dispose(); } }
public void GenerateInventory() { string generatedDirPath = $"Assets/Merlin/Inventory/_generated/{descriptorGUID}"; if (!Directory.Exists(generatedDirPath)) { Directory.CreateDirectory(generatedDirPath); } // Generate the stage parameters for the inventory toggles VRCExpressionParameters inventoryStageParams; string stageParameterPath = $"{generatedDirPath}/customStageParams.asset"; if (basisStageParameters != null) { AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisStageParameters), stageParameterPath); inventoryStageParams = AssetDatabase.LoadAssetAtPath <VRCExpressionParameters>(stageParameterPath); } else { inventoryStageParams = ScriptableObject.CreateInstance <VRCExpressionParameters>(); AssetDatabase.CreateAsset(inventoryStageParams, $"{generatedDirPath}/customStageParams.asset"); } List <VRCExpressionParameters.Parameter> originalParams = new List <VRCExpressionParameters.Parameter>(); if (inventoryStageParams.parameters != null) { foreach (VRCExpressionParameters.Parameter param in inventoryStageParams.parameters) { if (!string.IsNullOrEmpty(param.name)) { originalParams.Add(new VRCExpressionParameters.Parameter() { name = param.name, valueType = param.valueType }); } } } if (inventorySlots.Length + originalParams.Count > 16) { Debug.LogError($"Cannot have more than {16 - originalParams.Count} inventory slots"); return; } VRCExpressionParameters.Parameter[] basisParameters = inventoryStageParams.parameters; inventoryStageParams.parameters = new VRCExpressionParameters.Parameter[16]; for (int i = 0; i < originalParams.Count; ++i) { inventoryStageParams.parameters[i] = originalParams[i]; } for (int i = originalParams.Count; i < inventorySlots.Length + originalParams.Count; ++i) { inventoryStageParams.parameters[i] = new VRCExpressionParameters.Parameter() { name = $"GenInventorySlot{i - originalParams.Count}", valueType = VRCExpressionParameters.ValueType.Int } } ; for (int i = originalParams.Count + inventorySlots.Length; i < 16; ++i) // Clear out empty params { inventoryStageParams.parameters[i] = new VRCExpressionParameters.Parameter() { name = "", valueType = VRCExpressionParameters.ValueType.Float } } ; // Generate menu asset VRCExpressionsMenu menuAsset; string menuPath = $"{generatedDirPath}/expressionMenu.asset"; if (basisMenu) { AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisMenu), menuPath); menuAsset = AssetDatabase.LoadAssetAtPath <VRCExpressionsMenu>(menuPath); } else { menuAsset = ScriptableObject.CreateInstance <VRCExpressionsMenu>(); AssetDatabase.CreateAsset(menuAsset, menuPath); } for (int i = 0; i < inventorySlots.Length; ++i) { menuAsset.controls.Add(new VRCExpressionsMenu.Control() { icon = inventorySlots[i].slotIcon, name = inventorySlots[i].slotName, parameter = new VRCExpressionsMenu.Control.Parameter() { name = $"GenInventorySlot{i}" }, type = VRCExpressionsMenu.Control.ControlType.Toggle, value = 1, }); } // Generate controller AnimatorController controller; string controllerPath = $"{generatedDirPath}/inventoryController.controller"; if (basisAnimator) { AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(basisAnimator), controllerPath); controller = AssetDatabase.LoadAssetAtPath <AnimatorController>(controllerPath); } else { controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath); } AnimationClip[] inventoryClips = new AnimationClip[inventorySlots.Length]; // Generate layer mask AvatarMask maskEverything = new AvatarMask(); for (int i = 0; i < (int)AvatarMaskBodyPart.LastBodyPart; ++i) { maskEverything.SetHumanoidBodyPartActive((AvatarMaskBodyPart)i, false); } maskEverything.name = "maskEverythingMask"; AssetDatabase.AddObjectToAsset(maskEverything, controller); // Generate animation clips for (int i = 0; i < inventorySlots.Length; ++i) { InventorySlot slot = inventorySlots[i]; // Set initial object state foreach (GameObject toggleObject in slot.slotToggleItems) { if (toggleObject) { toggleObject.SetActive(slot.startEnabled); } } string animationClipPath = $"{generatedDirPath}/Animations/_toggle{i}.anim"; AnimationClip toggleClip = GenerateToggleClip(slot.slotToggleItems, !slot.startEnabled); //AssetDatabase.CreateAsset(toggleClip, animationClipPath); inventoryClips[i] = toggleClip; toggleClip.name = $"toggleAnim{i}"; AssetDatabase.AddObjectToAsset(toggleClip, controller); } // Generate controller layers for (int i = 0; i < inventorySlots.Length; ++i) { string paramName = $"GenInventorySlot{i}"; controller.AddParameter(paramName, AnimatorControllerParameterType.Int); string layerName = $"GenToggleLayer{i}"; AnimatorControllerLayer toggleLayer = new AnimatorControllerLayer(); toggleLayer.name = layerName; toggleLayer.defaultWeight = 1f; toggleLayer.stateMachine = new AnimatorStateMachine(); toggleLayer.stateMachine.name = toggleLayer.name; toggleLayer.stateMachine.hideFlags = HideFlags.HideInHierarchy; toggleLayer.avatarMask = maskEverything; if (AssetDatabase.GetAssetPath(controller) != "") { AssetDatabase.AddObjectToAsset(toggleLayer.stateMachine, AssetDatabase.GetAssetPath(controller)); } AnimatorStateMachine stateMachine = toggleLayer.stateMachine; AnimatorState nullState = stateMachine.AddState("Null State", stateMachine.entryPosition + new Vector3(200f, 0f)); AnimatorState toggleState = stateMachine.AddState("Toggle Triggered", stateMachine.entryPosition + new Vector3(500f, 0f)); toggleState.motion = inventoryClips[i]; AnimatorStateTransition toToggle = nullState.AddTransition(toggleState); toToggle.exitTime = 0f; toToggle.hasExitTime = false; toToggle.hasFixedDuration = true; toToggle.duration = 0f; AnimatorStateTransition toNull = toggleState.AddTransition(nullState); toNull.exitTime = 0f; toNull.hasExitTime = false; toNull.hasFixedDuration = true; toNull.duration = 0f; toToggle.AddCondition(AnimatorConditionMode.Greater, 0f, paramName); toNull.AddCondition(AnimatorConditionMode.Equals, 0f, paramName); controller.AddLayer(toggleLayer); } // Setup layers on the avatar descriptor VRCAvatarDescriptor descriptor = GetComponent <VRCAvatarDescriptor>(); descriptor.expressionsMenu = menuAsset; descriptor.expressionParameters = inventoryStageParams; VRCAvatarDescriptor.CustomAnimLayer layer = new VRCAvatarDescriptor.CustomAnimLayer(); layer.isDefault = false; layer.animatorController = controller; layer.type = inventoryAnimLayer; for (int i = 0; i < descriptor.baseAnimationLayers.Length; ++i) { if (descriptor.baseAnimationLayers[i].type == inventoryAnimLayer) { descriptor.baseAnimationLayers[i] = layer; break; } } AssetDatabase.SaveAssets(); }