using System; using System.Collections.Generic; using Oni.Akira; using Oni.Motoko; namespace Oni.Physics { internal class ObjectDaeImporter { private readonly TextureImporter3 textureImporter; private readonly Dictionary properties; private readonly List nodes = new List(); public ObjectDaeImporter(TextureImporter3 textureImporter, Dictionary properties) { this.textureImporter = textureImporter; this.properties = properties; } public List Nodes { get { return nodes; } } public void Import(Dae.Scene scene) { ImportNode(scene, null, GetNodeProperties(scene)); } private void ImportNode(Dae.Node node, List parentAnimation, ObjectDaeNodeProperties parentNodeProperties) { Console.WriteLine("\t{0}", node.Id); var animation = ImportNodeAnimation(node, parentAnimation); var nodeProperties = GetNodeProperties(node); if (nodeProperties == null && parentNodeProperties != null) { nodeProperties = new ObjectDaeNodeProperties { HasPhysics = parentNodeProperties.HasPhysics, ScriptId = parentNodeProperties.ScriptId, ObjectFlags = parentNodeProperties.ObjectFlags }; // // We can't use the same anim name when we inherit the properties of the parent, // generate a name by appending "_anim" to the node name. // nodeProperties.Animations.AddRange( from a in parentNodeProperties.Animations select new ObjectAnimationClip { Name = node.Name + "_anim", Start = a.Start, Stop = a.Stop, End = a.End, Flags = a.Flags }); } if (nodeProperties != null && nodeProperties.HasPhysics) { var geometries = GeometryDaeReader.Read(node, textureImporter).ToList(); if (animation.Count > 0 || geometries.Count > 0) { nodes.Add(new ObjectNode(from g in geometries select new ObjectGeometry(g)) { Name = node.Name, FileName = node.FileName, Animations = CreateAnimations(animation, nodeProperties), ScriptId = nodeProperties.ScriptId, Flags = nodeProperties.ObjectFlags }); } } foreach (var child in node.Nodes) { ImportNode(child, animation, nodeProperties); } } private List ImportNodeAnimation(Dae.Node node, List parentAnimation) { var scale = Vector3.One; var scaleTransform = node.Transforms.OfType().FirstOrDefault(); if (scaleTransform != null) { scale.X = scaleTransform.Values[0]; scale.Y = scaleTransform.Values[1]; scale.Z = scaleTransform.Values[2]; } if (parentAnimation != null && parentAnimation.Count > 0) { scale *= parentAnimation[0].Scale; } var rotateTransforms = new List(); var angles = new List(); foreach (var rotate in node.Transforms.OfType()) { rotateTransforms.Add(rotate); var angleAnimation = rotate.AngleAnimation; if (angleAnimation != null) angles.Add(angleAnimation.Sample()); else angles.Add(new[] { rotate.Angle }); } var translateTransforms = node.Transforms.OfType().FirstOrDefault(); var positions = new List(); if (translateTransforms != null) { for (int i = 0; i < 3; i++) { var positionAnimation = translateTransforms.Animations[i]; if (positionAnimation != null) positions.Add(positionAnimation.Sample()); else positions.Add(new[] { translateTransforms.Translation[i] }); } } var frames = new List(); int frameCount = Math.Max(angles.Max(a => a.Length), positions.Max(a => a.Length)); for (int time = 0; time < frameCount; time++) { var rotation = Quaternion.Identity; for (int i = 0; i < rotateTransforms.Count; i++) { float angle; float[] values = angles[i]; if (time >= values.Length) angle = values.Last(); else angle = values[time]; rotation *= Quaternion.CreateFromAxisAngle(rotateTransforms[i].Axis, MathHelper.ToRadians(angle)); } var translation = Vector3.Zero; if (translateTransforms != null) { translation.X = positions[0][MathHelper.Clamp(time, 0, positions[0].Length - 1)]; translation.Y = positions[1][MathHelper.Clamp(time, 0, positions[1].Length - 1)]; translation.Z = positions[2][MathHelper.Clamp(time, 0, positions[2].Length - 1)]; } if (parentAnimation != null) { var parentFrame = time < parentAnimation.Count ? parentAnimation[time] : parentAnimation.LastOrDefault(); if (parentFrame != null) { rotation = parentFrame.Rotation * rotation; translation = parentFrame.Translation + Vector3.Transform(translation * parentFrame.Scale, parentFrame.Rotation); } } frames.Add(new ObjectAnimationKey { Time = time, Scale = scale, Rotation = rotation, Translation = translation }); } return frames; } private ObjectAnimation[] CreateAnimations(List allFrames, ObjectDaeNodeProperties props) { var anims = new List(); foreach (var clip in props.Animations) { int start = clip.Start; int end = clip.End != int.MaxValue ? clip.End : allFrames.Last().Time; var clipFrames = (from f in allFrames where start <= f.Time && f.Time <= end select new ObjectAnimationKey { Time = f.Time - start, Scale = f.Scale, Rotation = f.Rotation, Translation = f.Translation }).ToArray(); if (clipFrames.Length == 0) continue; anims.Add(new ObjectAnimation { Name = clip.Name, Flags = clip.Flags, Stop = clip.Stop, Length = end - start + 1, Keys = clipFrames }); } return anims.ToArray(); } private ObjectDaeNodeProperties GetNodeProperties(Dae.Node node) { AkiraDaeNodeProperties nodeProperties; if (properties == null || !properties.TryGetValue(node.Id, out nodeProperties)) return null; return nodeProperties as ObjectDaeNodeProperties; } } }