protected override IEnumerable <string> OnFindResourcePaths(ValveBspFile bsp) { var items = Enumerable.Range(0, bsp.StaticProps.ModelCount) .Select(x => bsp.StaticProps.GetModelName(x)) .Select(x => { var mdl = StudioModelFile.FromProvider(x, bsp.PakFile, Program.Resources); if (mdl == null) { return(null); } return(new { Path = x, VertexCount = mdl.TotalVertices, FirstMaterialIndex = MaterialDictionary.GetResourceIndex(bsp, mdl.GetMaterialName(0, bsp.PakFile, Program.Resources)) }); }) .Where(x => x != null) .GroupBy(x => x.FirstMaterialIndex) .OrderByDescending(x => x.Count()) .SelectMany(x => x) .ToArray(); foreach (var item in items) { yield return(item.Path); var index = GetResourceIndex(item.Path); if (index == _vertexCounts.Count) { _vertexCounts.Add(item.VertexCount); } } }
protected override IEnumerable <string> OnFindResourcePaths(ValveBspFile bsp) { for (var i = 0; i < bsp.TextureStringTable.Length; ++i) { yield return(bsp.GetTextureString(i)); } for (var i = 0; i < bsp.StaticProps.ModelCount; ++i) { var modelName = bsp.StaticProps.GetModelName(i); var mdl = StudioModelFile.FromProvider(modelName, bsp.PakFile, Program.Resources); if (mdl == null) { continue; } for (var j = 0; j < mdl.MaterialCount; ++j) { yield return(mdl.GetMaterialName(j, bsp.PakFile, Program.Resources)); } } foreach (var entity in bsp.Entities) { switch (entity.ClassName) { case "move_rope": case "keyframe_rope": yield return(entity["RopeMaterial"]); break; } } }
/*IEnumerable<StudioVertex[]> LoadMdl(IResourceProvider Res) { * for (int BodyPartIdx = 0; BodyPartIdx < Mdl.BodyPartCount; BodyPartIdx++) { * StudioModelFile.StudioModel[] Models = Mdl.GetModels(BodyPartIdx).ToArray(); * * for (int ModelIndex = 0; ModelIndex < Models.Length; ModelIndex++) { * StudioModelFile.StudioModel Model = Models[ModelIndex]; * StudioModelFile.StudioMesh[] Meshes = Mdl.GetMeshes(ref Model).ToArray(); * * for (int MeshIndex = 0; MeshIndex < Meshes.Length; MeshIndex++) { * StudioModelFile.StudioMesh Mesh = Meshes[MeshIndex]; * * StudioVertex[] StudioVerts = new StudioVertex[Tris.GetVertexCount(BodyPartIdx, ModelIndex, 0, MeshIndex)]; * Tris.GetVertices(BodyPartIdx, ModelIndex, 0, MeshIndex, StudioVerts); * * int[] Indices = new int[Tris.GetIndexCount(BodyPartIdx, ModelIndex, 0, MeshIndex)]; * Tris.GetIndices(BodyPartIdx, ModelIndex, 0, MeshIndex, Indices); * * List<StudioVertex> Vts = new List<StudioVertex>(); * for (int i = 0; i < Indices.Length; i++) * Vts.Add(StudioVerts[Indices[i]]); * * yield return Vts.ToArray(); * } * } * } * }*/ public static SourceMdl FromFile(string FilePath, IResourceProvider Res) { FilePath = FilePath.Substring(0, FilePath.Length - Path.GetExtension(FilePath).Length); SourceMdl Model = new SourceMdl(); Model.Mdl = StudioModelFile.FromProvider(FilePath + ".mdl", Res); Model.Verts = ValveVertexFile.FromProvider(FilePath + ".vvd", Res); Model.Tris = ValveTriangleFile.FromProvider(FilePath + ".dx90.vtx", Model.Mdl, Model.Verts, Res); return(Model); }
static int ModelExtract(MaterialExtractOptions args) { SetBaseOptions(args); var mdl = StudioModelFile.FromProvider(args.InputPath, Resources); if (mdl == null) { Console.Error.WriteLine($"Unable to find model at path '{args.InputPath}'!"); return(1); } for (var i = 0; i < mdl.MaterialCount; ++i) { var srcPath = mdl.GetMaterialName(i, Resources); if (!Resources.ContainsFile(srcPath)) { continue; } Console.WriteLine(srcPath); if (args.OutputPath != null) { var dstPath = Path.Combine(args.OutputPath, srcPath); var dstDir = Path.GetDirectoryName(dstPath); if (dstDir != null && !Directory.Exists(dstDir)) { Directory.CreateDirectory(dstDir); } using (var src = Resources.OpenFile(srcPath)) using (var dst = File.Create(dstPath)) { src.CopyTo(dst); } } } return(0); }
static int ModelPatch(ModelPatchOptions args) { SetBaseOptions(args); var mdl = StudioModelFile.FromProvider(args.InputPath, Resources); if (mdl == null) { Console.Error.WriteLine($"Unable to find model at path '{args.InputPath}'!"); return(1); } using (var outStream = new MemoryStream()) { using (var inStream = Resources.OpenFile(args.InputPath)) { inStream.CopyTo(outStream); } var empty = new List <Range> { new Range(mdl.FileHeader.Length, int.MaxValue) }; var commands = new List <ReplacementCommand>(); var shouldOverrideFlags = false; StudioModelFile.Flags overrideFlags = 0; if (args.Replace.Any() || args.Flags != null) { WriteVerboseHeader("Commands"); foreach (var replaceStr in args.Replace) { ReplacementCommand cmd; if (!ReplacementCommand.TryParse(replaceStr, out cmd)) { Console.Error.WriteLine($"Unable to parse replacement command '{replaceStr}'."); return(1); } WriteVerbose($"Replacing {cmd.Type}[{cmd.Index}] with \"{cmd.Value}\""); commands.Add(cmd); } WriteSeparator(); if (args.Flags != null) { shouldOverrideFlags = true; foreach (var flagStr in args.Flags.Split(',', ';', '|')) { var trimmedString = flagStr.TrimStart(); int flagInt; StudioModelFile.Flags flagValue; var numberStyle = NumberStyles.Integer; if (trimmedString.StartsWith("0x")) { numberStyle = NumberStyles.HexNumber; trimmedString = trimmedString.Substring("0x".Length); } if (int.TryParse(trimmedString, numberStyle, CultureInfo.InvariantCulture, out flagInt)) { overrideFlags |= (StudioModelFile.Flags)flagInt; } else if (Enum.TryParse(trimmedString, true, out flagValue)) { overrideFlags |= flagValue; } else { Console.Error.WriteLine($"Unexpected model flag value \"{trimmedString}\"."); return(1); } } WriteVerbose($"Override model flags: {overrideFlags:x}:"); foreach (StudioModelFile.Flags value in Enum.GetValues(typeof(StudioModelFile.Flags))) { if ((overrideFlags & value) != 0) { WriteVerbose($" - {value}"); } } WriteSeparator(); } } WriteVerboseHeader("Input Model"); WriteVerbose($"Existing model flags: {mdl.FileHeader.Flags:x}:"); foreach (StudioModelFile.Flags value in Enum.GetValues(typeof(StudioModelFile.Flags))) { if ((mdl.FileHeader.Flags & value) != 0) { WriteVerbose($" - {value}"); } } WriteSeparator(); WriteVerbose($"Model has {mdl.TextureDirectories.Count} material directories:"); var i = 0; foreach (var texDir in mdl.TextureDirectories) { var indexOffset = mdl.FileHeader.CdTextureIndex + sizeof(int) * i; var index = ReadInt32(outStream, indexOffset); ClearRange(outStream, new Range(index, index + texDir.Length + 1), empty); if (!commands.Any(x => x.Type == ReplacementType.Directory && (x.Wildcard || x.Index == i))) { commands.Add(new ReplacementCommand(ReplacementType.Directory, i, texDir)); } WriteVerbose($" [{i}]: {texDir}"); ++i; } WriteSeparator(); WriteVerbose($"Model has {mdl.TextureNames.Count} material names:"); i = 0; foreach (var texName in mdl.TextureNames) { var texOffset = mdl.FileHeader.TextureIndex + Marshal.SizeOf <StudioModelFile.StudioTexture>() * i; var index = texOffset + mdl.Textures[i].NameIndex; ClearRange(outStream, new Range(index, index + texName.Length + 1), empty); if (!commands.Any(x => x.Type == ReplacementType.Name && (x.Wildcard || x.Index == i))) { commands.Add(new ReplacementCommand(ReplacementType.Name, i, texName)); } WriteVerbose($" [{i}]: {texName}"); ++i; } if (args.Replace.Any() || shouldOverrideFlags) { WriteVerboseHeader("Applying Commands"); if (args.Replace.Any()) { commands.Sort((a, b) => a.Type != b.Type ? a.Type.CompareTo(b.Type) : a.Index - b.Index); foreach (var cmd in commands) { switch (cmd.Type) { case ReplacementType.Directory: { var startIndex = cmd.Wildcard ? 0 : cmd.Index; var endIndex = cmd.Wildcard ? mdl.TextureDirectories.Count : cmd.Index + 1; for (var index = startIndex; index < endIndex; ++index) { var indexOffset = mdl.FileHeader.CdTextureIndex + sizeof(int) * index; var original = mdl.TextureDirectories[index]; var newIndex = AppendString(outStream, cmd.GetFormattedValue(index, original), empty); WriteVerbose($" - Writing directory index: 0x{newIndex:x8}, at: 0x{indexOffset:x8}."); WriteInt32(outStream, indexOffset, newIndex); } break; } case ReplacementType.Name: { var startIndex = cmd.Wildcard ? 0 : cmd.Index; var endIndex = cmd.Wildcard ? mdl.TextureNames.Count : cmd.Index + 1; for (var index = startIndex; index < endIndex; ++index) { var texOffset = mdl.FileHeader.TextureIndex + Marshal.SizeOf <StudioModelFile.StudioTexture>() * index; var indexOffset = texOffset + StudioModelFile.StudioTexture.NameIndexOffset; var original = mdl.TextureNames[index]; var newIndex = AppendString(outStream, cmd.GetFormattedValue(index, original), empty); WriteVerbose($" - Writing name index: 0x{newIndex:x8}, at: 0x{indexOffset:x8}."); WriteInt32(outStream, indexOffset, newIndex - texOffset); } break; } } } var length = empty.Last().Min; outStream.SetLength(length); WriteVerbose($" - Writing file length: 0x{length:x8}, at: 0x{StudioModelFile.Header.LengthOffset:x8}."); WriteInt32(outStream, StudioModelFile.Header.LengthOffset, length); } if (shouldOverrideFlags) { WriteVerbose($" - Writing model flags: 0x{overrideFlags:x}, at: 0x{StudioModelFile.Header.FlagsOffset:x8}."); WriteInt32(outStream, StudioModelFile.Header.FlagsOffset, (int)overrideFlags); } } if (!string.IsNullOrEmpty(args.OutputPath)) { WriteVerboseHeader("Output"); var fullOutPath = new FileInfo(args.OutputPath).FullName; WriteVerbose($"Writing output to \"{fullOutPath}\"..."); outStream.Seek(0, SeekOrigin.Begin); var dir = Path.GetDirectoryName(fullOutPath); if (dir != null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } using (var fileStream = File.Create(fullOutPath)) { outStream.CopyTo(fileStream); } } } return(0); }
public StudioModelPage GetStudioModelPage([Url] string map, [Url] int index) { var bsp = Program.GetMap(map); var info = IndexController.GetPageLayout(bsp, StudioModelDictionary.GetResourceCount(bsp), StudioModelPage.VerticesPerPage, null, i => StudioModelDictionary.GetVertexCount(bsp, i)).Skip(index).FirstOrDefault(); var first = info?.First ?? StudioModelDictionary.GetResourceCount(bsp); var count = info?.Count ?? 0; var page = new StudioModelPage(); StudioVertex[] vertices = null; int[] indices = null; for (var i = 0; i < count; ++i) { var mdlPath = StudioModelDictionary.GetResourcePath(bsp, first + i); var vvdPath = mdlPath.Replace(".mdl", ".vvd"); var vtxPath = mdlPath.Replace(".mdl", ".dx90.vtx"); var mdlFile = StudioModelFile.FromProvider(mdlPath, bsp.PakFile, Program.Resources); var vvdFile = ValveVertexFile.FromProvider(vvdPath, bsp.PakFile, Program.Resources); var vtxFile = ValveTriangleFile.FromProvider(vtxPath, mdlFile, vvdFile, bsp.PakFile, Program.Resources); StudioModel mdl; page.Models.Add(mdl = new StudioModel()); for (var j = 0; j < mdlFile.BodyPartCount; ++j) { SmdBodyPart smdBodyPart; mdl.BodyParts.Add(smdBodyPart = new SmdBodyPart { Name = mdlFile.GetBodyPartName(j) }); smdBodyPart.Models.AddRange(mdlFile.GetModels(j).Select((model, modelIndex) => { var smdModel = new SmdModel(); smdModel.Meshes.AddRange(mdlFile.GetMeshes(ref model).Select((mesh, meshIndex) => { var vertexCount = vtxFile.GetVertexCount(j, modelIndex, 0, meshIndex); if (vertices == null || vertices.Length < vertexCount) { vertices = new StudioVertex[MathHelper.NextPowerOfTwo(vertexCount)]; } var indexCount = vtxFile.GetIndexCount(j, modelIndex, 0, meshIndex); if (indices == null || indices.Length < indexCount) { indices = new int[MathHelper.NextPowerOfTwo(indexCount)]; } vtxFile.GetVertices(j, modelIndex, 0, meshIndex, vertices); vtxFile.GetIndices(j, modelIndex, 0, meshIndex, indices); var meshData = GetOrCreateMeshData(bsp, page, mdlFile.GetMaterialName(mesh.Material, bsp.PakFile, Program.Resources), false); var meshElem = new MeshElement { Mode = PrimitiveType.Triangles, IndexOffset = meshData.Indices.Count, VertexOffset = meshData.Vertices.Count }; var smdMesh = new SmdMesh { MeshId = mesh.MeshId, Material = meshData.MaterialIndex, Element = meshData.Elements.Count }; meshData.BeginPrimitive(); for (var k = 0; k < vertexCount; ++k) { var vertex = vertices[k]; meshData.VertexAttribute(VertexAttribute.Position, vertex.Position); meshData.VertexAttribute(VertexAttribute.Normal, vertex.Normal); meshData.VertexAttribute(VertexAttribute.Uv, new Vector2(vertex.TexCoordX, vertex.TexCoordY)); meshData.CommitVertex(); } meshData.CommitPrimitive(PrimitiveType.Triangles, indices.Take(indexCount)); meshElem.IndexCount = meshData.Indices.Count - meshElem.IndexOffset; meshElem.VertexCount = meshData.Vertices.Count - meshElem.VertexOffset; meshData.Elements.Add(meshElem); return(smdMesh); })); return(smdModel); })); } } return(page); }
static FoamModel LoadMdl(string FilePath) { FilePath = FilePath.Substring(0, FilePath.Length - Path.GetExtension(FilePath).Length); StudioModelFile Mdl = StudioModelFile.FromProvider(FilePath + ".mdl", VFS); if (Mdl == null) { throw new FileNotFoundException("File not found", FilePath + ".mdl"); } ValveVertexFile Verts = ValveVertexFile.FromProvider(FilePath + ".vvd", VFS); ValveTriangleFile Tris = ValveTriangleFile.FromProvider(FilePath + ".dx90.vtx", Mdl, Verts, VFS); // Foam stuff List <FoamMaterial> FoamMaterials = new List <FoamMaterial>(); string[] TexNames = Mdl.TextureNames.ToArray(); for (int i = 0; i < Mdl.MaterialCount; i++) { string MatName = Mdl.GetMaterialName(i, VFS); string ShortMatName = Path.GetFileNameWithoutExtension(MatName); ValveMaterialFile VMF = ValveMaterialFile.FromProvider(MatName, VFS); FoamMaterials.Add(new FoamMaterial(ShortMatName, new FoamTexture[] { new FoamTexture(TexNames[i], FoamTextureType.Diffuse) })); } List <FoamMesh> FoamMeshes = new List <FoamMesh>(); List <FoamBone> FoamBones = new List <FoamBone>(); StudioModelFile.StudioBone[] Bones = Mdl.GetBones(); for (int i = 0; i < Bones.Length; i++) { string BoneName = Mdl.GetBoneName(i); FoamBones.Add(new FoamBone(BoneName, Bones[i].Parent, Matrix4x4.Identity)); } // BODIES for (int BodyPartIdx = 0; BodyPartIdx < Mdl.BodyPartCount; BodyPartIdx++) { StudioModelFile.StudioModel[] StudioModels = Mdl.GetModels(BodyPartIdx).ToArray(); // MODELS for (int ModelIdx = 0; ModelIdx < StudioModels.Length; ModelIdx++) { ref StudioModelFile.StudioModel StudioModel = ref StudioModels[ModelIdx]; StudioModelFile.StudioMesh[] StudioMeshes = Mdl.GetMeshes(ref StudioModel).ToArray(); // MESHES for (int MeshIdx = 0; MeshIdx < StudioMeshes.Length; MeshIdx++) { ref StudioModelFile.StudioMesh StudioMesh = ref StudioMeshes[MeshIdx]; StudioVertex[] StudioVerts = new StudioVertex[Tris.GetVertexCount(BodyPartIdx, ModelIdx, 0, MeshIdx)]; Tris.GetVertices(BodyPartIdx, ModelIdx, 0, MeshIdx, StudioVerts); int[] Indices = new int[Tris.GetIndexCount(BodyPartIdx, ModelIdx, 0, MeshIdx)]; Tris.GetIndices(BodyPartIdx, ModelIdx, 0, MeshIdx, Indices); // Foam converted List <FoamVertex3> FoamVerts = new List <FoamVertex3>(StudioVerts.Select(V => { // TODO: CCW nVector2 UV = new nVector2(V.TexCoordX, V.TexCoordY); return(new FoamVertex3(Conv(V.Position), UV, nVector2.Zero, Conv(V.Normal), nVector3.Zero, FoamColor.White)); })); List <FoamBoneInfo> FoamInfo = new List <FoamBoneInfo>(StudioVerts.Select(V => { FoamBoneInfo Info = new FoamBoneInfo(); Info.Bone1 = V.BoneWeights.Bone0; Info.Bone2 = V.BoneWeights.Bone1; Info.Bone3 = V.BoneWeights.Bone1; Info.Weight1 = V.BoneWeights.Weight0; Info.Weight2 = V.BoneWeights.Weight1; Info.Weight3 = V.BoneWeights.Weight2; return(Info); })); List <ushort> FoamInds = new List <ushort>(Indices.Select(I => (ushort)I)); if (ConvertToCCW) { FoamInds.Reverse(); } FoamMeshes.Add(new FoamMesh(FoamVerts.ToArray(), FoamInds.ToArray(), FoamInfo.ToArray(), StudioModel.Name + ";" + MeshIdx, StudioMesh.Material)); /*List<FoamVertex3> Vts = new List<FoamVertex3>(); * for (int i = 0; i < Indices.Length; i++) { * ref StudioVertex V = ref StudioVerts[Indices[i]]; * Vts.Add(new Vertex3(new Vector3(V.Position.X, V.Position.Y, V.Position.Z), new Vector2(V.TexCoordX, 1.0f - V.TexCoordY), Color.White)); * }*/ /*string MatName = MaterialNames[StudioMesh.Material]; * Material Mat = Engine.GetMaterial(MatName); * * if (Mat == Engine.GetMaterial("error")) { * Mat = ValveMaterial.CreateMaterial(MatName); * * if (Mat != Engine.GetMaterial("error")) * Engine.RegisterMaterial(Mat); * } * * libTechMesh Msh = new libTechMesh(Vts.ToArray(), Mat); * Msh.Name = StudioModel.Name; * Model.AddMesh(Msh);*/ } }
public GameObject GenerateMesh(BspToUnity bspToUnity, string modelName, string vvdPath, string vtxPath, IResourceProvider resourceProvider) { Mdl = StudioModelFile.FromProvider(modelName, resourceProvider); Vvd = ValveVertexFile.FromProvider(vvdPath, resourceProvider); Vtx = ValveTriangleFile.FromProvider(vtxPath, Mdl, Vvd, resourceProvider); var parent = new GameObject(); for (int b = 0; b < Mdl.BodyPartCount; b++) { var models = Mdl.GetModels(b); var e = models.GetEnumerator(); var modelIndex = 0; while (e.MoveNext()) { var model = e.Current; var meshes = Mdl.GetMeshes(ref model); var meshIndex = 0; foreach (var m in meshes) { try { var vertexCount = Vtx.GetVertexCount(b, modelIndex, 0, meshIndex); var sv = new StudioVertex[vertexCount]; var indexCount = Vtx.GetIndexCount(b, modelIndex, 0, meshIndex); var indices = new int[indexCount]; Vtx.GetVertices(b, modelIndex, 0, meshIndex, sv); Vtx.GetIndices(b, modelIndex, 0, meshIndex, indices); var go = new GameObject(); go.transform.SetParent(parent.transform); go.transform.localPosition = UnityEngine.Vector3.zero; go.transform.localRotation = Quaternion.identity; go.transform.localScale = UnityEngine.Vector3.one; var mr = go.AddComponent <MeshRenderer>(); var mf = go.AddComponent <MeshFilter>(); var modelMesh = new Mesh(); var vertices = new List <UnityEngine.Vector3>(); var normals = new List <UnityEngine.Vector3>(); var uvs = new List <UnityEngine.Vector2>(); var tris = new List <int>(); mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false; // verts, normals, uvs for (int j = 0; j < sv.Length; j++) { vertices.Add(sv[j].Position.ToUVector() * bspToUnity.Options.WorldScale); normals.Add(sv[j].Normal.ToUVector()); uvs.Add(new UnityEngine.Vector2(sv[j].TexCoordX, sv[j].TexCoordY)); } // tris tris.AddRange(indices); // build mesh modelMesh.SetVertices(vertices); modelMesh.SetUVs(0, uvs); modelMesh.SetNormals(normals); modelMesh.SetTriangles(tris, 0); modelMesh.RecalculateNormals(); mf.mesh = modelMesh; var matName = Mdl.GetMaterialName(m.Material, resourceProvider); var mat = bspToUnity.ApplyMaterial(mr, matName); go.name = matName; } catch { Debug.Log("NOPE: " + Vtx.NumLods); continue; } meshIndex++; } modelIndex++; } } return(parent); }