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); }
private void WriteFace(ValveBspFile bsp, int faceIndex, GeometryPage page, List <Face> outFaces) { const SurfFlags ignoreFlags = SurfFlags.NODRAW | SurfFlags.LIGHT | SurfFlags.SKY | SurfFlags.SKY2D; var faceInfo = bsp.Faces[faceIndex]; var texInfo = bsp.TextureInfos[faceInfo.TexInfo]; if ((texInfo.Flags & ignoreFlags) != 0 || texInfo.TexData < 0) { return; } var texData = bsp.TextureData[texInfo.TexData]; var matPath = bsp.GetTextureString(texData.NameStringTableId); var meshData = GetOrCreateMeshData(bsp, page, matPath); if (Skip) { return; } MeshElement elem; Face face; var leafFaceIndex = outFaces.FindIndex(x => x.Material == meshData.MaterialIndex); if (leafFaceIndex != -1) { face = outFaces[leafFaceIndex]; elem = meshData.Elements[face.Element]; } else { elem = new MeshElement { Mode = PrimitiveType.Triangles, Material = 0, IndexOffset = meshData.Indices.Count }; face = new Face { Material = meshData.MaterialIndex, Element = meshData.Elements.Count }; outFaces.Add(face); meshData.Elements.Add(elem); } var texScale = new Vector2(1f / Math.Max(texData.Width, 1), 1f / Math.Max(texData.Height, 1)); Vector2 lmMin, lmSize; bsp.LightmapLayout.GetUvs(faceIndex, out lmMin, out lmSize); if (faceInfo.DispInfo != -1) { var disp = bsp.DisplacementManager[faceInfo.DispInfo]; SourceUtils.Vector3 c0, c1, c2, c3; disp.GetCorners(out c0, out c1, out c2, out c3); var uv00 = GetUv(c0, texInfo.TextureUAxis, texInfo.TextureVAxis) * texScale; var uv10 = GetUv(c3, texInfo.TextureUAxis, texInfo.TextureVAxis) * texScale; var uv01 = GetUv(c1, texInfo.TextureUAxis, texInfo.TextureVAxis) * texScale; var uv11 = GetUv(c2, texInfo.TextureUAxis, texInfo.TextureVAxis) * texScale; var subDivMul = 1f / disp.Subdivisions; for (var y = 0; y < disp.Subdivisions; ++y) { meshData.BeginPrimitive(); var v0 = (y + 0) * subDivMul; var v1 = (y + 1) * subDivMul; for (var x = 0; x < disp.Size; ++x) { var u = x * subDivMul; meshData.VertexAttribute(VertexAttribute.Position, disp.GetPosition(x, y + 0)); meshData.VertexAttribute(VertexAttribute.Normal, disp.GetNormal(x, y + 0)); meshData.VertexAttribute(VertexAttribute.Uv, (uv00 * (1f - u) + uv10 * u) * (1f - v0) + (uv01 * (1f - u) + uv11 * u) * v0); meshData.VertexAttribute(VertexAttribute.Uv2, new Vector2(u, v0) * lmSize + lmMin); meshData.VertexAttribute(VertexAttribute.Alpha, disp.GetAlpha(x, y + 0)); meshData.CommitVertex(); meshData.VertexAttribute(VertexAttribute.Position, disp.GetPosition(x, y + 1)); meshData.VertexAttribute(VertexAttribute.Normal, disp.GetNormal(x, y + 1)); meshData.VertexAttribute(VertexAttribute.Uv, (uv00 * (1f - u) + uv10 * u) * (1f - v1) + (uv01 * (1f - u) + uv11 * u) * v1); meshData.VertexAttribute(VertexAttribute.Uv2, new Vector2(u, v1) * lmSize + lmMin); meshData.VertexAttribute(VertexAttribute.Alpha, disp.GetAlpha(x, y + 1)); meshData.CommitVertex(); } meshData.CommitPrimitive(PrimitiveType.TriangleStrip); } } else { meshData.BeginPrimitive(); var plane = bsp.Planes[faceInfo.PlaneNum]; for (int k = faceInfo.FirstEdge, kEnd = faceInfo.FirstEdge + faceInfo.NumEdges; k < kEnd; ++k) { var vert = bsp.GetVertexFromSurfEdgeId(k); var uv = GetUv(vert, texInfo.TextureUAxis, texInfo.TextureVAxis); var uv2 = GetUv(vert, texInfo.LightmapUAxis, texInfo.LightmapVAxis); uv2.X -= faceInfo.LightMapOffsetX; uv2.Y -= faceInfo.LightMapOffsetY; uv2.X /= Math.Max(faceInfo.LightMapSizeX, 1); uv2.Y /= Math.Max(faceInfo.LightMapSizeY, 1); uv2 *= lmSize; uv2 += lmMin; meshData.VertexAttribute(VertexAttribute.Position, vert); meshData.VertexAttribute(VertexAttribute.Normal, plane.Normal); meshData.VertexAttribute(VertexAttribute.Uv, uv * texScale); meshData.VertexAttribute(VertexAttribute.Uv2, uv2); meshData.CommitVertex(); } var numPrimitives = faceInfo.NumPrimitives & 0x7fff; if (numPrimitives == 0) { meshData.CommitPrimitive(PrimitiveType.TriangleFan); } else { if (_sIndexBuffer == null) { _sIndexBuffer = new List <int>(); } else { _sIndexBuffer.Clear(); } var indices = _sIndexBuffer; for (int k = faceInfo.FirstPrimitive, kEnd = faceInfo.FirstPrimitive + numPrimitives; k < kEnd; ++k) { var primitive = bsp.Primitives[k]; for (int l = primitive.FirstIndex, lEnd = primitive.FirstIndex + primitive.IndexCount; l < lEnd; ++l) { indices.Add(bsp.PrimitiveIndices[l]); } PrimitiveType mode; switch (primitive.Type) { case ValveBsp.PrimitiveType.TriangleStrip: mode = PrimitiveType.TriangleStrip; break; case ValveBsp.PrimitiveType.TriangleFan: mode = PrimitiveType.TriangleFan; break; case ValveBsp.PrimitiveType.TriangleList: mode = PrimitiveType.Triangles; break; default: throw new NotImplementedException(); } meshData.CommitPrimitive(mode, indices); indices.Clear(); } } } elem.IndexCount = meshData.Indices.Count - elem.IndexOffset; }