Exemplo n.º 1
0
        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;
            }
        }
Exemplo n.º 2
0
        private static IKn5 LodFbxToKn5(IKn5 preparedKn5, string fbxFilename, CarLodGeneratorStageParams stage)
        {
            var fbx        = FbxIO.Read(fbxFilename);
            var geometries = fbx.GetGeometryIds()
                             .Select(id => new { id, name = fbx.GetNode("Model", fbx.GetConnection(id)).GetName("Model") })
                             .GroupBy(v => v.name).ToDictionary(x => x.Key, x => x.Select(v => v.id).ToList());

            MergeNode(preparedKn5.RootNode);
            foreach (var geometry in geometries.Where(g => g.Value.Count > 0))
            {
                var parent = preparedKn5.FirstByName(geometry.Key);
                if (parent == null)
                {
                    AcToolsLogging.Write($"Error: parent {geometry.Key} is missing");
                    continue;
                }

                var mesh = Kn5MeshUtils.Create($"{geometry.Key}__mesh_", parent.Children[0].MaterialId);
                if (parent.Children.Count != 1)
                {
                    throw new Exception("Unexpected arrangement");
                }
                MergeMeshWith(parent, mesh, geometry.Value.Select(x => Tuple.Create(fbx.GetGeometry(x), 1d)));
                parent.Children.Add(mesh);
            }
            RemoveEmpty(preparedKn5.RootNode);
            return(preparedKn5);

            void MergeMeshWith(Kn5Node parent, Kn5Node mesh, IEnumerable <Tuple <FbxNode, double> > geometriesList)
            {
                var builder    = new Kn5MeshBuilder();
                var subCounter = 1;

                foreach (var geometry in geometriesList)
                {
                    var fbxIndices = geometry?.Item1?.GetRelative("PolygonVertexIndex")?.Value?.GetAsIntArray();
                    if (fbxIndices == null)
                    {
                        continue;
                    }

                    var fbxVertices = geometry.Item1.GetRelative("Vertices").Value.GetAsFloatArray();
                    var fbxNormals  = geometry.Item1.GetRelative("LayerElementNormal").GetRelative("Normals").Value.GetAsFloatArray();
                    var fbxUvs      = geometry.Item1.GetRelative("LayerElementUV").GetRelative("UV").Value.GetAsFloatArray();
                    var offset      = MoveAsideDistance(geometry.Item2, stage);
                    var scale       = (float)(1d / geometry.Item2);

                    for (var i = 0; i < fbxIndices.Length; ++i)
                    {
                        if (i % 3 == 0 && builder.IsCloseToLimit)
                        {
                            builder.SetTo(mesh);
                            mesh.RecalculateTangents();

                            builder.Clear();
                            var oldIndex = parent.Children.IndexOf(mesh);
                            mesh = Kn5MeshUtils.Create(mesh.Name + $"___$sub:{subCounter}", mesh.MaterialId);
                            ++subCounter;
                            if (oldIndex != -1 && oldIndex < parent.Children.Count - 1)
                            {
                                parent.Children.Insert(oldIndex + 1, mesh);
                            }
                            else
                            {
                                parent.Children.Add(mesh);
                            }
                        }

                        var index = fbxIndices[i] < 0 ? -fbxIndices[i] - 1 : fbxIndices[i];
                        builder.AddVertex(new Kn5Node.Vertex {
                            Position = new Vec3((fbxVertices[index * 3] - offset.X) * scale, (fbxVertices[index * 3 + 1] - offset.Y) * scale,
                                                (fbxVertices[index * 3 + 2] - offset.Z) * scale),
                            Normal = new Vec3(fbxNormals[i * 3], fbxNormals[i * 3 + 1], fbxNormals[i * 3 + 2]),
                            Tex    = new Vec2(fbxUvs[i * 2], 1f - fbxUvs[i * 2 + 1])
                        });
                    }
                }
                builder.SetTo(mesh);
                mesh.RecalculateTangents();
            }

            void MergeMesh(Kn5Node parent, Kn5Node node, IEnumerable <Kn5Node> merges)
            {
                if (Regex.IsMatch(node.Name, @"___\$extra:\d+$"))
                {
                    node.Vertices = new Kn5Node.Vertex[0];
                    return;
                }

                MergeMeshWith(parent, node, Enumerable.Range(-1, 100).Select(i => GetGeometry(node, i)).TakeWhile(i => i != null)
                              .Concat(merges.Select(n => GetGeometry(n))));
            }

            Tuple <FbxNode, double> GetGeometry(Kn5Node node, int extraBit = -1)
            {
                var name = extraBit < 0 ? node.Name : $"{node.Name}___$extra:{extraBit}";

                if (geometries.TryGetValue(name, out var list))
                {
                    if (list.Count == 0)
                    {
                        return(null);
                    }
                    var geometryId = list[0];
                    list.RemoveAt(0);
                    return(Tuple.Create(fbx.GetGeometry(geometryId), node.Tag is double priority ? priority : 1d));
                }
                return(null);
            }

            void MergeNode(Kn5Node node)
            {
                foreach (var child in node.Children.ToList())
                {
                    if (child.NodeClass == Kn5NodeClass.Base)
                    {
                        MergeNode(child);
                    }
                    else if (child.NodeClass == Kn5NodeClass.Mesh && child.Vertices.Length > 0)
                    {
                        var mergeKey = MergeKey(child);
                        var merge    = node.Children.ApartFrom(child).Where(c => c.NodeClass == Kn5NodeClass.Mesh && MergeKey(c) == mergeKey).ToList();
                        MergeMesh(node, child, merge);
                        merge.ForEach(m => m.Vertices = new Kn5Node.Vertex[0]);
                    }
                }
            }

            int MergeKey(Kn5Node node)
            {
                return((int)(node.MaterialId * 397) | (node.IsTransparent ? 1 << 31 : 0) | (node.CastShadows ? 1 << 30 : 0));
            }

            bool RemoveEmpty(Kn5Node node)
            {
                if (node.NodeClass == Kn5NodeClass.Mesh)
                {
                    if (Regex.IsMatch(node.Name, @"___\$extra:\d+$"))
                    {
                        return(false);
                    }
                    return(node.Vertices.Length > 0);
                }
                if (node.NodeClass == Kn5NodeClass.SkinnedMesh)
                {
                    return(false);
                }
                var found = node.Name.IndexOf("___$unique:", StringComparison.Ordinal);

                if (found != -1)
                {
                    node.Name = node.Name.Substring(0, found);
                }
                for (var i = 0; i < node.Children.Count; ++i)
                {
                    if (!RemoveEmpty(node.Children[i]))
                    {
                        node.Children.RemoveAt(i);
                        --i;
                    }
                }
                return(node.Children.Count > 0);
            }
        }