public void SmoothNodeTriangles(RsmNode node, List <RsmTriangle> triangles)
        {
            //var maxSmoothGroup = node.Faces.Max(f => f.SmoothGroup);

            //we can assume all faces in a given triangle has the same normal at this point
            var faceNormals = new List <Vector3>();

            //var faceNormals = triangles.Select(t => new[] {t.Normals[0], t.Normals[1], t.Normals[2]}).ToList();
            foreach (var t in triangles)
            {
                faceNormals.Add(VectorHelper.CalcNormal(t.Vertices[0], t.Vertices[1], t.Vertices[2]));
            }

            for (var i = 0; i < node.Faces.Count; i++)             //loop through the first set of faces
            {
                var face1     = node.Faces[i];
                var f1Normals = new[] { faceNormals[i], faceNormals[i], faceNormals[i] };
                var f1Counts  = new[] { 1, 1, 1 };

                for (var j = 0; j < node.Faces.Count; j++)                 //loop through the second set of faces
                {
                    var face2 = node.Faces[j];

                    if (i == j || face1.SmoothGroup != face2.SmoothGroup)                     //they must be in the same smooth group and not the same face
                    {
                        continue;
                    }

                    for (var k = 0; k < 3; k++)                           //loop through each vertex in face1
                    {
                        for (var l = 0; l < 3; l++)                       //loop through each vertex in face2
                        {
                            if (face1.VertexIds[k] == face2.VertexIds[l]) //if the ids match, add their normal to the total
                            {
                                f1Normals[k] += faceNormals[j];
                                f1Counts[k]++;
                            }
                        }
                    }
                }

                for (var k = 0; k < 3; k++)
                {
                    var normal = (f1Normals[k] / f1Counts[k]).normalized;
                    if (normal.magnitude > 0.1)                     //discard normals that don't normalize, as that means it's not pointing any direction
                    {
                        triangles[i].Normals[k] = normal;
                    }
                }
            }
        }
        private RsmNode LoadNode()
        {
            var node = new RsmNode();

            node.Name       = br.ReadKoreanString(40);
            node.ParentName = br.ReadKoreanString(40);

            if (string.IsNullOrWhiteSpace(node.Name))
            {
                node.Name = $"Object {nameId}";
                nameId++;
            }

            var textureCount = br.ReadInt32();

            node.TextureIds = new List <int>();

            for (var i = 0; i < textureCount; i++)
            {
                node.TextureIds.Add(br.ReadInt32());
            }

            var forward = br.ReadVector3();
            var up      = br.ReadVector3();
            var right   = br.ReadVector3();

            node.OffsetMatrix  = new Matrix4x4(forward, up, right, new Vector4(0, 0, 0, 1));
            node.Offset        = br.ReadVector3();
            node.Position      = br.ReadVector3();
            node.RotationAngle = br.ReadSingle();
            node.RotationAxis  = br.ReadVector3();
            node.Scale         = br.ReadVector3();

            var vertCount = br.ReadInt32();

            for (var i = 0; i < vertCount; i++)
            {
                node.Vertices.Add(node.OffsetMatrix * br.ReadVector3());
            }

            var uvCount = br.ReadInt32();

            for (var i = 0; i < uvCount; i++)
            {
                node.Colors.Add(model.Version >= 12 ? br.ReadByteColor() : Color.white);

                var uv = br.ReadVector2();
                uv.x = Mathf.Clamp(uv.x, 0, 1);
                uv.y = Mathf.Clamp(1 - uv.y, 0, 1);                 //1 - (uv.y * 0.98f + 0.01f); //wut

                node.UVs.Add(uv);
            }

            var faceCount = br.ReadInt32();

            for (var i = 0; i < faceCount; i++)
            {
                var face = new RsmFace();

                face.VertexIds[0] = br.ReadUInt16();
                face.VertexIds[1] = br.ReadUInt16();
                face.VertexIds[2] = br.ReadUInt16();

                face.UVIds[0] = br.ReadUInt16();
                face.UVIds[1] = br.ReadUInt16();
                face.UVIds[2] = br.ReadUInt16();

                face.TextureId = br.ReadUInt16();
                face.Padding   = br.ReadUInt16();
                face.TwoSided  = br.ReadInt32() == 1;
                if (model.Version >= 12)
                {
                    face.SmoothGroup = br.ReadInt32();
                }

                node.Faces.Add(face);
            }

            if (model.Version >= 15)
            {
                var posKeyFrames = br.ReadInt32();
                for (var i = 0; i < posKeyFrames; i++)
                {
                    node.PosKeyFrames.Add(new RsmPosKeyframe()
                    {
                        Frame = br.ReadInt32(), Position = br.ReadRoPosition()
                    });
                }
            }

            var rotKeyFrames = br.ReadInt32();

            for (var i = 0; i < rotKeyFrames; i++)
            {
                var keyframe = new RsmRotKeyFrame()
                {
                    Frame = br.ReadInt32(), Rotation = br.ReadQuaternion().FlipY()
                };
                node.RotationKeyFrames.Add(keyframe);
                //Debug.Log(keyframe.Rotation.eulerAngles);
            }

            node.Matrix = Matrix4x4.identity;
            node.Bounds = new Bounds();

            return(node);
        }
        public GameObject CompileNode(GameObject parentGameObject, RsmNode node, Material mat)
        {
            var go = new GameObject(node.Name);
            var mf = go.AddComponent <MeshFilter>();
            var mr = go.AddComponent <MeshRenderer>();
            var mc = go.AddComponent <MeshCollider>();

            go.layer             = LayerMask.NameToLayer("Object");
            go.isStatic          = true;
            mr.material          = mat;
            mr.receiveGI         = ReceiveGI.Lightmaps;
            mr.shadowCastingMode = ShadowCastingMode.TwoSided;
            mc.cookingOptions    = MeshColliderCookingOptions.CookForFasterSimulation |
                                   MeshColliderCookingOptions.EnableMeshCleaning |
                                   MeshColliderCookingOptions.WeldColocatedVertices |
                                   MeshColliderCookingOptions.UseFastMidphase;

            go.transform.parent = parentGameObject.transform;

            var position = node.Position;
            var rotation = Quaternion.AngleAxis(node.RotationAngle * Mathf.Rad2Deg, node.RotationAxis).FlipY();
            var scale    = node.Scale;
            var offset   = node.Offset;

            if (node.PosKeyFrames.Count > 0)
            {
                position = node.PosKeyFrames[0].Position;
            }
            if (node.RotationKeyFrames.Count > 0)
            {
                rotation = node.RotationKeyFrames[0].Rotation;
            }

            go.transform.localPosition = position.FlipY();
            go.transform.localRotation = rotation;
            go.transform.localScale    = scale;

            var vTrans = new List <Vector3>();
            var tris   = new List <RsmTriangle>();

            foreach (var f in node.Faces)
            {
                var tri = new RsmTriangle()
                {
                    TwoSided = f.TwoSided
                };

                var realTexId = node.TextureIds[f.TextureId];

                for (var i = 0; i < 3; i++)
                {
                    var v = node.Vertices[f.VertexIds[i]].FlipY() + offset.FlipY();
                    vTrans.Add(go.transform.TransformPoint(v));
                    tri.Vertices[i] = v;
                    tri.UVs[i]      = VectorHelper.RemapUV(node.UVs[f.UVIds[i]], atlasRects[realTexId]);
                    tri.Colors[i]   = node.Colors[f.UVIds[i]];
                }

                tri.CalcNormals();
                tris.Add(tri);
            }

            if (model.ShadingType == RsmShadingType.Smooth)
            {
                SmoothNodeTriangles(node, tris);
            }

            if (vTrans.Count > 0)
            {
                node.Bounds = GeometryUtility.CalculateBounds(vTrans.ToArray(), Matrix4x4.identity);
            }
            else
            {
                node.Bounds = new Bounds(Vector3.zero, Vector3.zero);
            }

            var mb = new MeshBuilder();

            foreach (var t in tris)
            {
                mb.AddFullTriangle(t.Vertices, t.FlippedNormals, t.UVs, t.Colors, new[] { 2, 1, 0 });

                if (t.TwoSided)
                {
                    mb.AddFullTriangle(t.Vertices, t.Normals, t.UVs, t.Colors, new[] { 0, 1, 2 });
                }
            }

            var mesh = mb.Build(node.Name, true);

            mf.sharedMesh = mesh;
            mc.sharedMesh = mesh;
            if (tris.Any(t => t.TwoSided))
            {
                mr.shadowCastingMode = ShadowCastingMode.On;                 //disable two sided shadows if the model has any two sided faces
            }
            if (node.Children.Count > 0)
            {
                foreach (var child in node.Children)
                {
                    CompileNode(go, child, mat);
                }
            }

            if (node.RotationKeyFrames.Count != 0 || node.PosKeyFrames.Count != 0)
            {
                go.ChangeStaticRecursive(false);
            }

            if (string.IsNullOrWhiteSpace(node.ParentName))
            {
                var bounds = node.Bounds;
                foreach (var n in model.RsmNodes)
                {
                    bounds.Encapsulate(n.Bounds);
                }

                var centering = new Vector3(bounds.center.x, bounds.min.y, bounds.center.z);
                //Debug.Log(centering);
                go.transform.localPosition -= centering;
                go.transform.localPosition *= 0.2f;
                go.transform.localScale    *= 0.2f;
            }

            if (node.RotationKeyFrames.Count > 0)
            {
                var r         = go.AddComponent <RoKeyframeRotator>();
                var keyframes = new List <float>();
                var rotations = new List <Quaternion>();
                foreach (var k in node.RotationKeyFrames)
                {
                    keyframes.Add(k.Frame / 1000f);
                    rotations.Add(k.Rotation);
                }

                r.Keyframes = keyframes.ToArray();
                r.Rotations = rotations.ToArray();
            }

            //save Mesh
            var meshPath = AssetHelper.GetAssetPath(Path.Combine("Assets/models/mesh", savePath, baseName), Path.GetFileNameWithoutExtension(node.Name) + ".asset");

            AssetDatabase.CreateAsset(mesh, meshPath);

            return(go);
        }