private void UPinit() { if (!_up) { _up = true; _l = new Kn5RenderableList(Kn5Node.CreateBaseNode("L"), null) { LocalMatrix = Matrix.Translation(_swRadius, 0, _swOffset), HighlightDummy = true }; _r = new Kn5RenderableList(Kn5Node.CreateBaseNode("R"), null) { LocalMatrix = Matrix.Translation(-_swRadius, 0, _swOffset), HighlightDummy = true }; _dbg0 = new Kn5RenderableList(Kn5Node.CreateBaseNode("debug"), null) { HighlightDummy = true }; _dbg1 = new Kn5RenderableList(Kn5Node.CreateBaseNode("debug"), null) { HighlightDummy = true }; //_steer.Add(_l); //_steer.Add(_r); _la = new Arm(_driver, true); _ra = new Arm(_driver, false); } }
public static Mat4x4 CalculateTransformRelativeToParent(this Kn5Node child, Kn5Node root) { var mat = new Mat4x4(); if (!TraverseDown(root, Mat4x4.Identity, ref mat)) { throw new Exception("Failed to traverse down"); } return(mat); bool TraverseDown(Kn5Node node, Mat4x4 m, ref Mat4x4 r) { foreach (var c in node.Children) { if (c == child) { r = m; return(true); } if (c.NodeClass == Kn5NodeClass.Base) { if (TraverseDown(c, c.Transform * m, ref r)) { return(true); } } } return(false); } }
private static bool FixLrHrNodes_HasChild(Kn5Node node, Kn5Node child) { if (node.NodeClass != Kn5NodeClass.Base) { return(false); } return(node.Children.Contains(child) || node.Children.Any(subNode => FixLrHrNodes_HasChild(subNode, child))); }
public Kn5RenderableDepthOnlyObject(Kn5Node node, bool forceVisible = false) : base(node.Name, Convert(node.Vertices), Convert(node.Indices)) { OriginalNode = node; if (IsEnabled && (!node.Active || !forceVisible && (!node.IsVisible || !node.IsRenderable))) { IsEnabled = false; } }
public int MergeGroup(Kn5Node node, double priority) { if (_mergeAsBlack?.Any(x => x.Test(node)) == true) { node.MaterialId = uint.MaxValue; return(priority.GetHashCode()); } return((((int)node.MaterialId * 397) | (node.IsTransparent ? 1 << 31 : 0) | (node.CastShadows ? 1 << 30 : 0)) ^ priority.GetHashCode()); }
public static Aabb3 CalculateAabb3(this Kn5Node mesh, Kn5Node relativeTo) { var transform = mesh.CalculateTransformRelativeToParent(relativeTo); var aabb = Aabb3.CreateNew(); foreach (var v in mesh.Vertices) { aabb.Extend(Vec3.Transform(v.Position, transform)); } return(aabb); }
public bool HasParentWithSameName([NotNull] Kn5Node node) { for (var parent = _filterContext.GetParent(node); parent != null; parent = _filterContext.GetParent(parent)) { if (parent.Name == node.Name) { return(true); } } return(false); }
public Kn5RenderableList(Kn5Node node, Func <Kn5Node, IRenderableObject> convert) : base(node.Name, node.Transform.ToMatrix(), node.Children.Count == 0 ? new IRenderableObject[0] : node.Children.Select(convert)) { OriginalNode = node; if (IsEnabled && (!OriginalNode.Active || OriginalNode.Name == "CINTURE_ON" || OriginalNode.Name.StartsWith("DAMAGE_GLASS"))) { IsEnabled = false; } if (node.Name.StartsWith("DIR_")) { _dirNode = node.Name.Substring(4); } }
private static void FixSuspensionNodes(Kn5 kn5) { foreach (var name in SuspensionNodes.Where(name => kn5.FirstByName(name) == null)) { var node = Kn5Node.CreateBaseNode(name); var wheel = kn5.FirstByName(name.Replace("SUSP", "WHEEL"))?.Transform; if (wheel != null) { node.Transform = wheel; } kn5.RootNode.Children.Add(node); } }
public static IRenderableObject Convert(Kn5Node node) { switch (node.NodeClass) { case Kn5NodeClass.Base: return(new Kn5RenderableList(node, Convert)); case Kn5NodeClass.Mesh: case Kn5NodeClass.SkinnedMesh: return(new Kn5RenderableDepthOnlyObject(node)); default: throw new ArgumentOutOfRangeException(); } }
public static Kn5Node FindFirst(this Kn5Node parent, string name) { foreach (var child in parent.Children) { if (child.Name == name) { return(child); } var ret = child.FindFirst(name); if (ret != null) { return(ret); } } return(null); }
public Kn5RenderableObject(Kn5Node node) : base(node.Name, Convert(node.Vertices), Convert(node.Indices)) { OriginalNode = node; IsCastingShadows = node.CastShadows; if (IsEnabled && (!OriginalNode.Active || !OriginalNode.IsVisible || !OriginalNode.IsRenderable)) { IsEnabled = false; } if (OriginalNode.IsTransparent || OriginalNode.Layer == 1 /* WHAT? WHAT DOES IT DO? BUT KUNOS PREVIEWS SHOWROOM WORKS THIS WAY, SO… */) { IsReflectable = false; } _isTransparent = OriginalNode.IsTransparent; _distanceFromSqr = OriginalNode.LodIn.Pow(2f); _distanceToSqr = OriginalNode.LodOut.Pow(2f); }
private static void FixVertices(Kn5Node node, Matrix parentMatrix) { if (node.NodeClass == Kn5NodeClass.Base) { var localMatrix = node.Transform.ToMatrix() * parentMatrix; foreach (var child in node.Children) { FixVertices(child, localMatrix); } } else { var normal = Vector3.TransformNormal(Vector3.UnitY, Matrix.Invert(parentMatrix)); for (var i = 0; i < node.Vertices.Length; i++) { FixVertice(ref node.Vertices[i], normal); } } }
private static void MergeMeshes(Kn5Node root, List <Tuple <Kn5Node, double, Mat4x4> > children, CarLodGeneratorMergeRules mergeRules, CarLodGeneratorStageParams stage) { if (children.Count == 0) { return; } var mesh = children[0].Item1; var priority = children[0].Item2; var considerDetails = mesh.MaterialId != uint.MaxValue; var builder = new Kn5MeshBuilder(considerDetails, considerDetails); // AcToolsLogging.Write($"Merging together: {children.Select(x => $"{x.Item1.Name} [{x.Item2}]").JoinToString(", ")}"); var extraCounter = 0; foreach (var child in children) { var transform = child.Item3 * Mat4x4.CreateScale(new Vec3((float)priority)) * Mat4x4.CreateTranslation(MoveAsideDistance(priority, stage)); var offset = mergeRules.GetOffsetAlongNormal(child.Item1); for (var i = 0; i < child.Item1.Indices.Length; ++i) { builder.AddVertex(child.Item1.Vertices[child.Item1.Indices[i]].Transform(transform, offset)); if (i % 3 == 2 && builder.IsCloseToLimit) { builder.SetTo(mesh); root.Children.Add(mesh); mesh.Tag = priority; builder.Clear(); mesh = Kn5MeshUtils.Create(children[0].Item1.Name + $"___$extra:{extraCounter}", children[0].Item1.MaterialId); ++extraCounter; } } } if (builder.Count > 0) { builder.SetTo(mesh); root.Children.Add(mesh); mesh.Tag = priority; } }
protected static IRenderableObject Convert(Kn5Node node, bool allowSkinnedObjects) { switch (node.NodeClass) { case Kn5NodeClass.Base: return(new Kn5RenderableList(node, n => Convert(n, allowSkinnedObjects))); case Kn5NodeClass.Mesh: return(new Kn5RenderableObject(node)); case Kn5NodeClass.SkinnedMesh: if (allowSkinnedObjects) { return(new Kn5SkinnedObject(node)); } return(new Kn5RenderableObject(node)); default: throw new ArgumentOutOfRangeException(); } }
static void PrintNode(Kn5Node node, int spaces = 0) { for (var i = -1; i < spaces; i++) { Console.Write(@" "); } if (node.NodeClass == Kn5NodeClass.Base) { Console.WriteLine(@"{0}:", node.Name); foreach (var child in node.Children) { PrintNode(child, spaces + 1); } } else { Console.WriteLine(@"{0}", node.Name); } }
public static bool FixSuspension(string acRoot, string carName) { var kn5File = FileUtils.GetMainCarFilename(acRoot, carName); var kn5 = Kn5.FromFile(kn5File); var added = 0; foreach (var name in new [] { "SUSP_LF", "SUSP_RF", "SUSP_LR", "SUSP_RR" }.Where(name => kn5.FirstByName(name) == null)) { kn5.RootNode.Children.Add(Kn5Node.CreateBaseNode(name)); added++; } if (added == 0) { return(false); } kn5.SaveRecyclingOriginal(kn5File); return(true); }
private static bool FixLrHrNodes_HasParentWithName(Kn5Node rootNode, Kn5Node node, string name) { if (rootNode.NodeClass != Kn5NodeClass.Base) { return(false); } foreach (var subNode in rootNode.Children) { if (subNode.Name == name) { return(FixLrHrNodes_HasChild(subNode, node)); } else if (FixLrHrNodes_HasParentWithName(subNode, node, name)) { return(true); } } return(false); }
public static void RecalculateTangents(this Kn5Node mesh) { var vertexCount = mesh.Vertices.Length; var triangleCount = mesh.Indices.Length / 3; var tan1 = new Vec3[vertexCount]; var tan2 = new Vec3[vertexCount]; for (long a = 0; a < triangleCount; a++) { var i1 = mesh.Indices[a * 3]; var i2 = mesh.Indices[a * 3 + 1]; var i3 = mesh.Indices[a * 3 + 2]; var v1 = mesh.Vertices[i1]; var v2 = mesh.Vertices[i2]; var v3 = mesh.Vertices[i3]; var x1 = v2.Position.X - v1.Position.X; var x2 = v3.Position.X - v1.Position.X; var y1 = v2.Position.Y - v1.Position.Y; var y2 = v3.Position.Y - v1.Position.Y; var z1 = v2.Position.Z - v1.Position.Z; var z2 = v3.Position.Z - v1.Position.Z; var s1 = v2.Tex.X - v1.Tex.X; var s2 = v3.Tex.X - v1.Tex.X; var t1 = v2.Tex.Y - v1.Tex.Y; var t2 = v3.Tex.Y - v1.Tex.Y; var r = 1f / (s1 * t2 - s2 * t1); var sdir = new Vec3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); var tdir = new Vec3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (long a = 0; a < vertexCount; a++) { mesh.Vertices[a].Tangent = Vec3.Normalize(tan1[a] - mesh.Vertices[a].Normal * Vec3.Dot(mesh.Vertices[a].Normal, tan1[a])); } }
private void GetObject(Kn5Node node, Matrix matrix) { if ((!node.Active || node.Name == "CINTURE_ON") && _mode != VisualMode.TRACK_MAP) { return; } if (node.NodeClass == Kn5NodeClass.Base) { matrix = ToMatrix(node.Transform) * matrix; foreach (var child in node.Children) { GetObject(child, matrix); } switch (node.Name) { case "WHEEL_LF": _wheelLfPos = new Vector3(matrix.M41, matrix.M42, matrix.M43); break; case "WHEEL_RF": _wheelRfPos = new Vector3(matrix.M41, matrix.M42, matrix.M43); break; case "WHEEL_LR": _wheelLrPos = new Vector3(matrix.M41, matrix.M42, matrix.M43); break; case "WHEEL_RR": _wheelRrPos = new Vector3(matrix.M41, matrix.M42, matrix.M43); break; } } else if (_mode == VisualMode.TRACK_MAP || node.IsRenderable) { GetMeshObject(node, matrix); } }
public Kn5SkinnedObject(Kn5Node node) : base(node.Name, Convert(node.Vertices, node.VerticeWeights), Convert(node.Indices)) { OriginalNode = node; IsCastingShadows = node.CastShadows; if (IsEnabled && (!OriginalNode.Active || !OriginalNode.IsVisible || !OriginalNode.IsRenderable)) { IsEnabled = false; } if (OriginalNode.IsTransparent || OriginalNode.Layer == 1 /* WHAT? WHAT DOES IT DO? BUT KUNOS PREVIEWS SHOWROOM WORKS THIS WAY, SO… */) { IsReflectable = false; } _bonesTransform = node.Bones.Select(x => x.Transform.ToMatrix()).ToArray(); _bones = _bonesTransform.ToArray(); _isTransparent = OriginalNode.IsTransparent; _distanceFromSqr = OriginalNode.LodIn.Pow(2f); _distanceToSqr = OriginalNode.LodOut.Pow(2f); }
public static IEnumerable <Kn5Node> AllChildren([CanBeNull] this Kn5Node parent) { var queue = new Queue <Kn5Node>(); if (parent != null) { queue.Enqueue(parent); } while (queue.Count > 0) { var next = queue.Dequeue(); if (next.NodeClass == Kn5NodeClass.Base) { foreach (var child in next.Children) { queue.Enqueue(child); } } yield return(next); } }
private static IEnumerable <Kn5Node> FilterNodes(IKn5 kn5, IFilter <string> filter, Kn5Node node) { if (node.NodeClass == Kn5NodeClass.Base) { return(node.Children.SelectMany(x => FilterNodes(kn5, filter, x))); } if (!filter.Test(node.Name) || kn5.GetMaterial(node.MaterialId)?.TextureMappings.Any(x => x.Name == "txNormal" || x.Name == "txNormalDetail") != false) { return(new Kn5Node[0]); } return(new[] { node }); }
private static async Task PrepareForGenerationAsync(Kn5NodeFilterContext filterContext, IKn5 kn5, CarLodGeneratorStageParams stage, bool printNodes) { var mergeRules = new CarLodGeneratorMergeRules(filterContext, stage); var toMerge = new Dictionary <Kn5Node, List <Tuple <Kn5Node, double, Mat4x4> > >(); var nodeIndices = new Dictionary <Kn5Node, int>(); MergeNode(kn5.RootNode, kn5.RootNode, 1d); foreach (var pair in toMerge) { var mergeData = pair.Value.GroupBy(x => mergeRules.MergeGroup(x.Item1, x.Item2)) .OrderBy(v => mergeRules.GroupOrder(kn5, v, nodeIndices)).Select(v => v.ToList()).ToList(); await Task.Run(() => { foreach (var group in mergeData) { MergeMeshes(pair.Key, group, mergeRules, stage); } }); } foreach (var node in kn5.Nodes.Where(x => x.Children?.Count > 1).ToList()) { var meshesList = node.Children .Where(x => x.NodeClass != Kn5NodeClass.Base) .Select((x, i) => new { x, i = OrderIndex(x, i) }) .OrderBy(x => x.i) .Select(x => x.x).ToList(); if (meshesList.Count > 0) { if (node.Children.Any(x => x.NodeClass == Kn5NodeClass.Base)) { node.Children = node.Children.Where(x => x.NodeClass == Kn5NodeClass.Base) .Prepend(Kn5Node.CreateBaseNode($"__{meshesList[0].Name}_wrap_", meshesList, true)) .Select((x, i) => new { x, i = OrderIndex(x, i) }) .OrderBy(x => x.i) .Select(x => x.x).ToList(); } else { node.Children = meshesList; } } } if (printNodes) { PrintNode(kn5.RootNode, 0, 0); } mergeRules.FinalizeKn5(kn5); var duplicateNames = kn5.Nodes.GroupBy(x => $"{x.NodeClass}/{x.Name}") .Select(x => x.ToList()).Where(x => x.Count > 1).ToList(); foreach (var group in duplicateNames) { AcToolsLogging.Write($"Duplicate name: {group[0].Name} ({group[0].NodeClass})"); foreach (var toRename in group.Skip(1).Select((x, i) => new { x, i })) { toRename.x.Name = $"{toRename.x.Name}___$unique:{toRename.i}"; } } int OrderIndex(Kn5Node node, int index) { return(index + (AnyTransparent(node) ? 1 << 10 : 0)); } void PrintNode(Kn5Node node, int level, int index) { var postfix = node.NodeClass == Kn5NodeClass.Base ? "" : $"{(node.IsTransparent ? ", transparent" : "")}, material: {kn5.GetMaterial(node.MaterialId)?.Name}"; AcToolsLogging.Write( $"{new string('\t', level)}{node.Name} [{node.NodeClass}{postfix}, index: {OrderIndex(node, index)}, aabb: {filterContext.GetAabb3(node)}]"); if (node.NodeClass == Kn5NodeClass.Base) { for (var i = 0; i < node.Children.Count; i++) { PrintNode(node.Children[i], level + 1, i); } } } bool AnyTransparent(Kn5Node node) { return(node.NodeClass == Kn5NodeClass.Base ? node.Children.Any(AnyTransparent) : node.IsTransparent || kn5.GetMaterial(node.MaterialId)?.BlendMode == Kn5MaterialBlendMode.AlphaBlend); } void ApplyPriority(Kn5Node mesh, double priority) { var offset = MoveAsideDistance(priority, stage); for (var i = 0; i < mesh.Vertices.Length; i++) { Apply(ref mesh.Vertices[i]); } mesh.Tag = priority; void Apply(ref Kn5Node.Vertex v) { v.Position[0] = v.Position[0] * (float)priority + offset.X; v.Position[1] = v.Position[1] * (float)priority + offset.Y; v.Position[2] = v.Position[2] * (float)priority + offset.Z; } } bool MergeNode(Kn5Node node, Kn5Node mergeRoot, double priority) { nodeIndices[node] = nodeIndices.Count; if (node != mergeRoot) { if (mergeRules.CanSkipNode(node)) { if (node.NodeClass == Kn5NodeClass.Base && !mergeRules.HasParentWithSameName(node) && !mergeRules.CanRemoveEmptyNode(node)) { node.Children.Clear(); return(true); } return(false); } var priorityAdjustment = mergeRules.CalculateReductionPriority(node); if (priorityAdjustment != 1d && (priorityAdjustment < priority || priority == 1d || node.NodeClass == Kn5NodeClass.Mesh)) { priority = priorityAdjustment; } if (node.NodeClass == Kn5NodeClass.Mesh) { if (mergeRoot != null && mergeRules.CanMerge(node)) { if (!toMerge.ContainsKey(mergeRoot)) { toMerge[mergeRoot] = new List <Tuple <Kn5Node, double, Mat4x4> >(); } toMerge[mergeRoot].Add(Tuple.Create(node, priority, node.CalculateTransformRelativeToParent(mergeRoot))); return(false); } if (priority != 1d) { ApplyPriority(node, priority); } return(true); } if (node.NodeClass == Kn5NodeClass.SkinnedMesh) { return(true); } if (mergeRoot != null) { if (!mergeRules.CanMerge(node)) { mergeRoot = null; } else if (node.Name != mergeRoot.Name && mergeRules.IsNodeMergeRoot(node)) { mergeRoot = node; } } } for (var i = 0; i < node.Children.Count; ++i) { if (!MergeNode(node.Children[i], mergeRoot, priority)) { node.Children.RemoveAt(i); --i; } } return(node.Children.Count > 0 || mergeRoot == node || !mergeRules.CanRemoveEmptyNode(node)); } }
public bool CanRemoveEmptyNode(Kn5Node node) { return(_emptyNodesToKeep?.Any(x => x.Test(node)) != true); }
public bool IsNodeMergeRoot(Kn5Node node) { return(_mergeParents?.Any(x => x.Test(node)) == true); }
public bool CanMerge(Kn5Node node) { return(_mergeExceptions?.Any(x => x.Test(node)) != true); }
public bool CanSkipNode(Kn5Node node) { return(node.NodeClass == Kn5NodeClass.SkinnedMesh || node.NodeClass == Kn5NodeClass.Mesh && !node.IsRenderable || _elementToRemove?.Any(x => x.Test(node)) == true); }
public double GetOffsetAlongNormal(Kn5Node node) { return(_offsetsAlongNormal?.FirstOrDefault(x => x.Item1.Test(node))?.Item2 ?? 0d); }
public double CalculateReductionPriority(Kn5Node node) { return(_elementPriorities?.FirstOrDefault(x => x.Item1.Test(node))?.Item2 ?? 1d); }
private static void Save_Node(Kn5Writer writer, Kn5Node node) { writer.Write(node); foreach (var t in node.Children) { Save_Node(writer, t); } }