Exemplo n.º 1
0
        public Transform BuildModel(Boolean GenerateUV2 = false)
        {
            GameObject ModelObject = new GameObject(MDL_Header.Name);

            #region Bones
            Transform[] Bones = new Transform[MDL_Header.bone_count];
            Dictionary <Int32, String> bonePathDict = new Dictionary <Int32, String>();
            for (Int32 boneID = 0; boneID < MDL_Header.bone_count; boneID++)
            {
                GameObject BoneObject = new GameObject(MDL_BoneNames[boneID]);

                Bones[boneID] = BoneObject.transform;//MDL_Bones.Add(BoneObject.transform);

                Vector3 pos = MDL_StudioBones[boneID].pos * uLoader.UnitScale;
                Vector3 rot = MDL_StudioBones[boneID].rot * Mathf.Rad2Deg;

                //Invert x for convert right-handed to left-handed
                pos.x = -pos.x;

                if (MDL_StudioBones[boneID].parent >= 0)
                {
                    Bones[boneID].parent = Bones[MDL_StudioBones[boneID].parent];
                }
                else
                {
                    //Swap Z & Y and invert Y (ex: X Z -Y)
                    //only for parents, cuz parents used different order vectors
                    float temp = pos.y;
                    pos.y = pos.z;
                    pos.z = -temp;

                    Bones[boneID].parent = ModelObject.transform;
                }

                bonePathDict.Add(boneID, Bones[boneID].GetTransformPath(ModelObject.transform));

                Bones[boneID].localPosition = pos;
                //Bones[i].localRotation = MDL_StudioBones[i].quat;

                if (MDL_StudioBones[boneID].parent == -1)
                {
                    //Fix up parents
                    Bones[boneID].localRotation = Quaternion.Euler(-90, 90, -90) * MathLibrary.AngleQuaternion(rot);
                }
                else
                {
                    Bones[boneID].localRotation = MathLibrary.AngleQuaternion(rot);
                }
            }

            if (uLoader.DrawArmature)
            {
                MDLArmatureInfo DebugArmature = ModelObject.AddComponent <MDLArmatureInfo>();
                DebugArmature.boneNodes = Bones;
            }
            #endregion

            #region Hitboxes
            if (MDL_Hitboxsets != null)
            {
                for (Int32 HitboxsetID = 0; HitboxsetID < MDL_Header.hitbox_count; HitboxsetID++)
                {
                    for (Int32 HitboxID = 0; HitboxID < MDL_Hitboxsets[HitboxsetID].numhitboxes; HitboxID++)
                    {
                        mstudiobbox_t Hitbox = Hitboxes[HitboxsetID][HitboxID].BBox;
                        BoxCollider   BBox   = new GameObject(String.Format("Hitbox_{0}", Bones[Hitbox.bone].name)).AddComponent <BoxCollider>();

                        BBox.size   = MathLibrary.NegateX(Hitbox.bbmax - Hitbox.bbmin) * uLoader.UnitScale;
                        BBox.center = (MathLibrary.NegateX(Hitbox.bbmax + Hitbox.bbmin) / 2) * uLoader.UnitScale;

                        BBox.transform.parent        = Bones[Hitbox.bone];
                        BBox.transform.localPosition = Vector3.zero;
                        BBox.transform.localRotation = Quaternion.identity;

                        //bbox.transform.tag = HitTagType(MDL_BBoxes[i].group);
                    }
                }
            }
            #endregion

            #region BodyParts
            if (BuildMesh)
            {
                for (Int32 BodypartID = 0; BodypartID < MDL_Header.bodypart_count; BodypartID++)
                {
                    StudioBodyPart BodyPart = MDL_Bodyparts[BodypartID];

                    for (Int32 ModelID = 0; ModelID < BodyPart.Models.Length; ModelID++)
                    {
                        StudioModel Model = BodyPart.Models[ModelID];

                        //Skip if model is blank
                        if (Model.isBlank)
                        {
                            continue;
                        }

                        #region TODO: Remove this code after find way to strip unused vertexes for lod's (VTXStripGroup / VTXStrip)
                        mstudiovertex_t[] Vertexes = Model.VerticesPerLod[0];

                        BoneWeight[] BoneWeight = new BoneWeight[Vertexes.Length];
                        Vector3[]    Vertices   = new Vector3[Vertexes.Length];
                        Vector3[]    Normals    = new Vector3[Vertexes.Length];
                        Vector2[]    UvBuffer   = new Vector2[Vertexes.Length];

                        for (Int32 i = 0; i < Vertexes.Length; i++)
                        {
                            Vertices[i] = MathLibrary.SwapZY(Vertexes[i].m_vecPosition * uLoader.UnitScale);
                            Normals[i]  = MathLibrary.SwapZY(Vertexes[i].m_vecNormal);

                            Vector2 UV = Vertexes[i].m_vecTexCoord;
                            if (uLoader.SaveAssetsToUnity && uLoader.ExportTextureAsPNG)
                            {
                                UV.y = -UV.y;
                            }

                            UvBuffer[i]   = UV;
                            BoneWeight[i] = GetBoneWeight(Vertexes[i].m_BoneWeights);
                        }
                        #endregion

                        #region LOD Support
                        Boolean DetailModeEnabled =
                            uLoader.DetailMode == DetailMode.Lowest ||
                            uLoader.DetailMode == DetailMode.Low ||
                            uLoader.DetailMode == DetailMode.Medium ||
                            uLoader.DetailMode == DetailMode.High;

                        Boolean   LODExist       = uLoader.EnableLODParsing && !DetailModeEnabled && Model.NumLODs > 1;
                        Transform FirstLODObject = null;
                        LODGroup  LODGroup       = null;
                        LOD[]     LODs           = null;
                        Single    MaxSwitchPoint = 100;
                        Int32     StartLODIndex  = 0;

                        if (LODExist)
                        {
                            LODs = new LOD[Model.NumLODs];

                            for (Int32 LODID = 1; LODID < 3; LODID++)
                            {
                                Int32            LastID = Model.NumLODs - LODID;
                                ModelLODHeader_t LOD    = Model.LODData[LastID];

                                //ignore $shadowlod
                                if (LOD.switchPoint != -1)
                                {
                                    if (LOD.switchPoint > 0)
                                    {
                                        MaxSwitchPoint = LOD.switchPoint;
                                    }

                                    //Set switchPoint from MaxSwitchPoint (if switchPoint is zero or negative)
                                    if (LODID == 2 || LOD.switchPoint == 0)
                                    {
                                        MaxSwitchPoint += MaxSwitchPoint * uLoader.NegativeAddLODPrecent;
                                        Model.LODData[LOD.switchPoint == 0 ? LastID : LastID + 1].switchPoint = MaxSwitchPoint;
                                    }

                                    // + Threshold used to avoid errors with LODGroup
                                    MaxSwitchPoint += uLoader.ThresholdMaxSwitch;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (!uLoader.EnableLODParsing)
                            {
                                Model.NumLODs = 1;
                            }
                        }

                        if (uLoader.EnableLODParsing && DetailModeEnabled)
                        {
                            StartLODIndex =
                                uLoader.DetailMode == DetailMode.Lowest ? (Model.NumLODs - 1) :
                                uLoader.DetailMode == DetailMode.Low ? (Int32)(Model.NumLODs / 1.5f) :
                                uLoader.DetailMode == DetailMode.Medium ? (Model.NumLODs / 2) : (Int32)(Model.NumLODs / 2.5f);
                        }
                        #endregion

                        #region Build Meshes
                        for (Int32 LODID = StartLODIndex; LODID < Model.NumLODs; LODID++)
                        {
                            #region TODO: Uncomment after find way to strip unused vertexes for lod's (VTXStripGroup / VTXStrip)

                            /*mstudiovertex_t[] Vertexes = Model.VerticesPerLod[LODID];
                             *
                             * BoneWeight[] BoneWeight = new BoneWeight[Vertexes.Length];
                             * Vector3[] Vertices = new Vector3[Vertexes.Length];
                             * Vector3[] Normals = new Vector3[Vertexes.Length];
                             * Vector2[] UvBuffer = new Vector2[Vertexes.Length];
                             *
                             * for (Int32 i = 0; i < Vertexes.Length; i++)
                             * {
                             *  Vertices[i] = MathLibrary.SwapZY(Vertexes[i].m_vecPosition * uLoader.UnitScale);
                             *  Normals[i] = MathLibrary.SwapZY(Vertexes[i].m_vecNormal);
                             *
                             *  Vector2 UV = Vertexes[i].m_vecTexCoord;
                             *  if (uLoader.SaveAssetsToUnity && uLoader.ExportTextureAsPNG)
                             *      UV.y = -UV.y;
                             *
                             *  UvBuffer[i] = UV;
                             *  BoneWeight[i] = GetBoneWeight(Vertexes[i].m_BoneWeights);
                             * }*/
                            #endregion

                            #region LOD
                            ModelLODHeader_t ModelLOD = Model.LODData[LODID];

                            if (LODExist)
                            {
                                if (ModelLOD.switchPoint == 0)
                                {
                                    ModelLOD.switchPoint = MaxSwitchPoint;
                                }
                                else
                                {
                                    ModelLOD.switchPoint = MaxSwitchPoint - ModelLOD.switchPoint;
                                }

                                ModelLOD.switchPoint -= ModelLOD.switchPoint * uLoader.SubstractLODPrecent;
                            }
                            #endregion

                            #region Mesh
                            //Create empty object for mesh
                            GameObject MeshObject = new GameObject(Model.Model.Name);
                            MeshObject.name            += "_vLOD" + LODID;
                            MeshObject.transform.parent = ModelObject.transform;

                            //Create empty mesh and fill parsed data
                            Mesh Mesh = new Mesh();
                            Mesh.name = MeshObject.name;

                            Mesh.subMeshCount = Model.Model.nummeshes;
                            Mesh.vertices     = Vertices;
                            //Make sure if mesh exist any vertexes
                            if (Mesh.vertexCount <= 0)
                            {
                                Debug.LogWarning(String.Format("Mesh: \"{0}\" has no vertexes, skip building... (MDL Version: {1})", Mesh.name, MDL_Header.version));
                                continue;
                            }

                            Mesh.normals = Normals;
                            Mesh.uv      = UvBuffer;
                            #endregion

                            #region Renderers
                            Renderer Renderer;
                            //SkinnedMeshRenderer (Models with "animated" bones & skin data)
                            if (!MDL_Header.flags.HasFlag(StudioHDRFlags.STUDIOHDR_FLAGS_STATIC_PROP))
                            {
                                //Bind poses & bone weights
                                SkinnedMeshRenderer SkinnedRenderer = MeshObject.AddComponent <SkinnedMeshRenderer>();
                                Renderer = SkinnedRenderer;
                                Matrix4x4[] BindPoses = new Matrix4x4[Bones.Length];

                                for (Int32 i = 0; i < BindPoses.Length; i++)
                                {
                                    BindPoses[i] = Bones[i].worldToLocalMatrix * MeshObject.transform.localToWorldMatrix;
                                }

                                Mesh.boneWeights = BoneWeight;
                                Mesh.bindposes   = BindPoses;

                                SkinnedRenderer.sharedMesh = Mesh;

                                SkinnedRenderer.bones = Bones;
                                SkinnedRenderer.updateWhenOffscreen = true;
                            }
                            //MeshRenderer (models with "STUDIOHDR_FLAGS_STATIC_PROP" flag or with generic "static_prop" bone)
                            else
                            {
                                MeshFilter MeshFilter = MeshObject.AddComponent <MeshFilter>();
                                Renderer = MeshObject.AddComponent <MeshRenderer>();
                                MeshFilter.sharedMesh = Mesh;
                            }

                            Renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.TwoSided;
                            #endregion

                            #region Debug Stuff
                            #if UNITY_EDITOR
                            DebugMaterial DebugMat = null;
                            if (uLoader.DebugMaterials)
                            {
                                DebugMat = MeshObject.AddComponent <DebugMaterial>();
                            }
                            #endif
                            #endregion

                            #region Triangles and Materials
                            Material[] Materials = new Material[Mesh.subMeshCount];

                            for (Int32 MeshID = 0; MeshID < Model.Model.nummeshes; MeshID++)
                            {
                                //Set triangles per lod & submeshes
                                Mesh.SetTriangles(Model.IndicesPerLod[LODID][MeshID], MeshID);

                                //Find & parse materials
                                String MaterialPath;
                                Int32  LastDirID = MDL_TDirectories.Length - 1;
                                for (Int32 DirID = 0; DirID < MDL_TDirectories.Length; DirID++)
                                {
                                    MaterialPath = MDL_TDirectories[DirID] + MDL_Textures[Model.Meshes[MeshID].material];

                                    //If material exist
                                    if (uResourceManager.ContainsFile(MaterialPath, uResourceManager.MaterialsSubFolder, uResourceManager.MaterialsExtension[0]))
                                    {
                                        VMTFile VMT = uResourceManager.LoadMaterial(MaterialPath);

                                        #region Debug Stuff
                                        #if UNITY_EDITOR
                                        if (uLoader.DebugMaterials)
                                        {
                                            DebugMat.Init(VMT);
                                        }
                                        #endif
                                        #endregion

                                        //Set material
                                        Materials[MeshID] = VMT.Material;
                                        break;
                                    }
                                    else if (DirID == LastDirID)
                                    {
                                        Materials[MeshID] = uResourceManager.LoadMaterial(String.Empty).Material;
                                    }
                                }
                            }

                            Renderer.sharedMaterials = Materials;
                            #endregion

                            #region UV2
                            #if UNITY_EDITOR
                            if (GenerateUV2)
                            {
                                UnityEditor.SerializedObject so = new UnityEditor.SerializedObject(Renderer);
                                so.FindProperty("m_ScaleInLightmap").floatValue = uLoader.ModelsLightmapSize;
                                so.ApplyModifiedProperties();

                                MeshObject.isStatic = GenerateUV2;
                                uResourceManager.UV2GenerateCache.Add(Mesh);
                            }
                            #endif
                            #endregion

                            #region Set LOD's
                            if (LODExist)
                            {
                                if (LODID == 0)
                                {
                                    LODGroup       = MeshObject.AddComponent <LODGroup>();
                                    FirstLODObject = MeshObject.transform;
                                }
                                else if (FirstLODObject != null)
                                {
                                    MeshObject.transform.parent = FirstLODObject;
                                }

                                LODs[LODID] = new LOD(ModelLOD.switchPoint / MaxSwitchPoint, new Renderer[] { Renderer });
                            }

                            if (uLoader.EnableLODParsing && DetailModeEnabled)
                            {
                                break;
                            }
                            #endregion
                        }//lod's per model

                        //Init all parsed lod's into LODGroup
                        if (LODGroup != null)
                        {
                            LODGroup.SetLODs(LODs);
                        }
                        #endregion
                    } //models in bodypart
                }     //Bodypart
            }
            #endregion

            #region Animations
            if (MDL_SeqDescriptions != null)
            {
                var AnimationComponent = ModelObject.AddComponent <Animation>();
                for (Int32 seqID = 0; seqID < MDL_SeqDescriptions.Length; seqID++)
                {
                    SeqInfo Sequence  = Sequences[seqID];
                    AniInfo Animation = Sequence.ani;

                    //Creating "AnimationCurve" for animation "paths" (aka frames where stored position (XYZ) & rotation (XYZW))
                    AnimationCurve[] posX = new AnimationCurve[MDL_Header.bone_count];    //X
                    AnimationCurve[] posY = new AnimationCurve[MDL_Header.bone_count];    //Y
                    AnimationCurve[] posZ = new AnimationCurve[MDL_Header.bone_count];    //Z

                    AnimationCurve[] rotX = new AnimationCurve[MDL_Header.bone_count];    //X
                    AnimationCurve[] rotY = new AnimationCurve[MDL_Header.bone_count];    //Y
                    AnimationCurve[] rotZ = new AnimationCurve[MDL_Header.bone_count];    //Z
                    AnimationCurve[] rotW = new AnimationCurve[MDL_Header.bone_count];    //W

                    //Fill "AnimationCurve" arrays
                    for (Int32 boneIndex = 0; boneIndex < MDL_Header.bone_count; boneIndex++)
                    {
                        posX[boneIndex] = new AnimationCurve();
                        posY[boneIndex] = new AnimationCurve();
                        posZ[boneIndex] = new AnimationCurve();

                        rotX[boneIndex] = new AnimationCurve();
                        rotY[boneIndex] = new AnimationCurve();
                        rotZ[boneIndex] = new AnimationCurve();
                        rotW[boneIndex] = new AnimationCurve();
                    }

                    Int32 numFrames = Animation.studioAnim.numframes;

                    //Used to avoid "Assertion failed" key count in Unity (if frames less than 2)
                    if (numFrames < 2)
                    {
                        numFrames += 1;
                    }

                    //Create animation clip
                    AnimationClip clip = new AnimationClip();
                    //Make it for legacy animation system (for now, but it possible to rework for Mecanim)
                    clip.legacy = true;
                    //Set animation clip name
                    clip.name = Animation.name;

                    //To avoid problems with "obfuscators" / "protectors" for models, make sure if model have name in sequence
                    if (String.IsNullOrEmpty(clip.name))
                    {
                        clip.name = "(empty)" + seqID;
                    }

                    for (Int32 frameIndex = 0; frameIndex < numFrames; frameIndex++)
                    {
                        //Get current frame from blend (meaning from "Animation") by index
                        //AnimationFrame frame = Animation.Frames[frameIndex];

                        //Set keys (position / rotation) from current frame
                        for (Int32 boneIndex = 0; boneIndex < Bones.Length; boneIndex++)
                        {
                            posX[boneIndex].AddKey(Animation.PosX[frameIndex][boneIndex]);
                            posY[boneIndex].AddKey(Animation.PosY[frameIndex][boneIndex]);
                            posZ[boneIndex].AddKey(Animation.PosZ[frameIndex][boneIndex]);

                            rotX[boneIndex].AddKey(Animation.RotX[frameIndex][boneIndex]);
                            rotY[boneIndex].AddKey(Animation.RotY[frameIndex][boneIndex]);
                            rotZ[boneIndex].AddKey(Animation.RotZ[frameIndex][boneIndex]);
                            rotW[boneIndex].AddKey(Animation.RotW[frameIndex][boneIndex]);

                            //Set default pose from the first animation
                            if (seqID == 0 && frameIndex == 0)
                            {
                                Bones[boneIndex].localPosition = new Vector3
                                                                 (
                                    Animation.PosX[0][boneIndex].value,
                                    Animation.PosY[0][boneIndex].value,
                                    Animation.PosZ[0][boneIndex].value
                                                                 );

                                Bones[boneIndex].localRotation = new Quaternion
                                                                 (
                                    Animation.RotX[0][boneIndex].value,
                                    Animation.RotY[0][boneIndex].value,
                                    Animation.RotZ[0][boneIndex].value,
                                    Animation.RotW[0][boneIndex].value
                                                                 );
                            }
                        }
                    }

                    //Apply animation paths (Position / Rotation) to clip
                    for (Int32 boneIndex = 0; boneIndex < MDL_Header.bone_count; boneIndex++)
                    {
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localPosition.x", posX[boneIndex]);
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localPosition.y", posY[boneIndex]);
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localPosition.z", posZ[boneIndex]);

                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localRotation.x", rotX[boneIndex]);
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localRotation.y", rotY[boneIndex]);
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localRotation.z", rotZ[boneIndex]);
                        clip.SetCurve(bonePathDict[boneIndex], typeof(Transform), "localRotation.w", rotW[boneIndex]);
                    }

                    if (Animation.studioAnim.fps > 0.0f)
                    {
                        clip.frameRate = Animation.studioAnim.fps;
                    }

                    //This ensures a smooth interpolation (corrects the problem of "jitter" after 180~270 degrees rotation path)
                    //can be "comment" if have idea how to replace this
                    clip.EnsureQuaternionContinuity();
                    AnimationComponent.AddClip(clip, clip.name);
                }
            }
            #endregion

            //If model has compiled flag "$staticprop"
            //then rotate this model by 90 degrees (Y)
            //https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/public/studio.h#L1965
            //Big thanks for this tip:
            //ShadelessFox
            //REDxEYE
            if (MDL_Header.flags.HasFlag(StudioHDRFlags.STUDIOHDR_FLAGS_STATIC_PROP))
            {
                ModelObject.transform.eulerAngles = new Vector3(0, 90, 0);
            }

            return(ModelObject.transform);
        }
        public static Transform Load(String ModelName)
        {
            Clear();
            //return new GameObject(ModelName).transform;
            if (ModelsInRAM == null)
            {
                ModelsInRAM = new Dictionary <string, Transform>();
            }

            String OpenPath = String.Empty;

            ModelName = ModelName
                        .Replace(".mdl", "")
                        .Replace("models/", "");

            if (ModelsInRAM.ContainsKey(ModelName))
            {
                return(UnityEngine.Object.Instantiate(ModelsInRAM[ModelName]));
            }

            if (File.Exists(Path.Combine(ConfigLoader._PakPath, ConfigLoader.LevelName + "_pakFile/models/" + ModelName + ".mdl")))
            {
                OpenPath = Path.Combine(ConfigLoader._PakPath, ConfigLoader.LevelName + "_pakFile/models/" + ModelName);
            }
            else
            {
                for (Int32 i = 0; i < ConfigLoader.ModFolders.Length; i++)
                {
                    if (File.Exists(ConfigLoader.GamePath + "/" + ConfigLoader.ModFolders[i] + "/models/" + ModelName + ".mdl"))
                    {
                        OpenPath = ConfigLoader.GamePath + "/" + ConfigLoader.ModFolders[i] + "/models/" + ModelName;
                    }
                }
            }

            ModelObject = new GameObject(ModelName);

            if (!File.Exists(OpenPath + ".mdl"))
            {
                Debug.Log(String.Format("{0}: File not found", ModelName + ".mdl"));
                return(Load("error"));
                //return ModelObject.transform;
            }

            if (!File.Exists(OpenPath + ".vvd"))
            {
                Debug.Log(String.Format("{0}: File not found", ModelName + ".vvd"));
                return(ModelObject.transform);
            }

            if (!File.Exists(OpenPath + ".dx90.vtx"))
            {
                Debug.Log(String.Format("{0}: File not found", ModelName + ".dx90.vtx"));
                return(ModelObject.transform);
            }

            try
            {
                ModelFileLoader = new MemUtils(File.OpenRead(OpenPath + ".mdl"));
                ModelFileLoader.ReadType(ref MDL_Header);
                ParseMdlFile();

                ModelFileLoader = new MemUtils(File.OpenRead(OpenPath + ".vvd"));
                ModelFileLoader.ReadType(ref VVD_Header);
                ParseVvdFile();

                ModelFileLoader = new MemUtils(File.OpenRead(OpenPath + ".dx90.vtx"));
                ModelFileLoader.ReadType(ref VTX_Header);
                ParseVtxFile();
            }
            catch (Exception ErrorInfo)
            {
                Debug.LogError(ErrorInfo.ToString());
                return(ModelObject.transform);
            }

            if (ConfigLoader.DrawArmature)
            {
                BonesInfo = ModelObject.AddComponent <MDLArmatureInfo>();
                DrawArmature();
            }

            ModelsInRAM.Add(ModelName, ModelObject.transform);
            ModelFileLoader.BaseStream.Dispose();
            return(ModelObject.transform);
        }