public override bool Execute(List <string> args) { var initialStringIDCount = Info.StringIDs.Strings.Count; bool isNew = false; if (args.Count == 3) { if (args[0] != "new") { return(false); } isNew = true; args.Remove("new"); } if (args.Count != 2) { return(false); } // // Verify the Blam render_model tag // var renderModelName = args[0]; CacheBase.IndexItem item = null; Console.WriteLine("Verifying Blam tag..."); foreach (var tag in BlamCache.IndexItems) { if ((tag.ClassCode == "mode" || tag.ClassCode == "sbsp") && tag.Filename == renderModelName) { item = tag; break; } } if (item == null) { Console.WriteLine("Blam tag does not exist: " + args[0]); return(false); } // // Verify the ED render_model tag // Console.WriteLine("Verifying ED tag index..."); int edRenderModelIndex; if (!int.TryParse(args[1], NumberStyles.HexNumber, null, out edRenderModelIndex) || (edRenderModelIndex >= Info.Cache.Tags.Count)) { Console.WriteLine("Invalid tag index: " + args[1]); return(false); } var edTag = Info.Cache.Tags[edRenderModelIndex]; if (edTag.Group.Name != Info.StringIDs.GetStringID("render_model")) { Console.WriteLine("Specified tag index is not a render_model: " + args[1]); return(false); } // // Deserialize the selected render_model // Console.WriteLine("Loading ED render_model tag..."); TagDefinitions.RenderModel renderModel; using (var cacheStream = Info.CacheFile.Open(FileMode.Open, FileAccess.ReadWrite)) { try { var context = new Serialization.TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, Info.Cache.Tags[(int)edRenderModelIndex]); renderModel = Info.Deserializer.Deserialize <TagDefinitions.RenderModel>(context); } catch { Console.WriteLine("Failed to deserialize selected render_model tag: " + edRenderModelIndex); return(true); } } // // Load the Blam render_model tag raw // var isBSP = item.ClassCode == "sbsp"; scenario_structure_bsp sbsp = null; render_model mode = null; if (isBSP) { sbsp = DefinitionsManager.sbsp(BlamCache, item); sbsp.LoadRaw(); } else { mode = DefinitionsManager.mode(BlamCache, item); mode.LoadRaw(); } // // Duplicate the render_model tag we're injecting over // TagInstance newTag; if (isNew) { Console.WriteLine("Duplicating selected render_model tag..."); if (!new DuplicateTagCommand(Info).Execute(new List <string> { edRenderModelIndex.ToString("X8") })) { Console.WriteLine("Failed to duplicate render_model tag: " + edRenderModelIndex); return(false); } newTag = Info.Cache.Tags[Info.Cache.Tags.Count - 1]; } else { newTag = edTag; } // // Start porting the model // RenderModelBuilder builder = new RenderModelBuilder(DefinitionSet.HaloOnline106708); var blamNodes = isBSP ? new List <render_model.Node> { new render_model.Node { Name = "default", ParentIndex = -1, FirstChildIndex = -1, NextSiblingIndex = -1, Position = new Vector(), Rotation = new Vector(0, 0, 0, -1), TransformScale = 1, TransformMatrix = new Matrix4x3( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0), DistanceFromParent = 0 } } : mode.Nodes; foreach (var node in blamNodes) { var nodeNameId = Info.StringIDs.GetStringID(node.Name); builder.AddNode( new TagDefinitions.RenderModel.Node { Name = nodeNameId.Index == 0 && node.Name != "" ? nodeNameId = Info.StringIDs.Add(node.Name) : nodeNameId, ParentNode = (short)node.ParentIndex, FirstChildNode = (short)node.FirstChildIndex, NextSiblingNode = (short)node.NextSiblingIndex, ImportNode = 0, DefaultTranslation = new Common.Vector3(node.Position.X, node.Position.Y, node.Position.Z), DefaultRotation = new Common.Vector4(node.Rotation.X, node.Rotation.Y, node.Rotation.Z, node.Rotation.W), DefaultScale = node.TransformScale, InverseForward = new Common.Vector3(node.TransformMatrix.m11, node.TransformMatrix.m12, node.TransformMatrix.m13), InverseLeft = new Common.Vector3(node.TransformMatrix.m21, node.TransformMatrix.m22, node.TransformMatrix.m23), InverseUp = new Common.Vector3(node.TransformMatrix.m31, node.TransformMatrix.m32, node.TransformMatrix.m33), InversePosition = new Common.Vector3(node.TransformMatrix.m41, node.TransformMatrix.m42, node.TransformMatrix.m43), DistanceFromParent = node.DistanceFromParent }); } // // Create empty materials for now... // var blamShaders = isBSP ? sbsp.Shaders : mode.Shaders; foreach (var shader in blamShaders) { builder.AddMaterial( new RenderMaterial { RenderMethod = Info.Cache.Tags[0x101F] }); } // // Build the model regions // if (isBSP) { builder.BeginRegion(Info.StringIDs.GetStringID("default")); builder.BeginPermutation(Info.StringIDs.GetStringID("default")); foreach (var section in sbsp.ModelSections) { if (section.Submeshes.Count == 0) { continue; } var rigidVertices = new List <RigidVertex>(); VertexValue v; if (section.Vertices != null) { foreach (var vertex in section.Vertices) { vertex.TryGetValue("position", 0, out v); var position = new Common.Vector4(v.Data.X, v.Data.Y, v.Data.Z, 1); vertex.TryGetValue("normal", 0, out v); var normal = new Common.Vector3(v.Data.I, v.Data.J, v.Data.K); vertex.TryGetValue("texcoords", 0, out v); var texcoord = new Common.Vector2(v.Data.X, v.Data.Y); vertex.TryGetValue("tangent", 0, out v); var tangent = new Common.Vector4(v.Data.X, v.Data.Y, v.Data.Z, 1); vertex.TryGetValue("binormal", 0, out v); var binormal = new Common.Vector3(v.Data.X, v.Data.Y, v.Data.Z); rigidVertices.Add( new RigidVertex { Position = position, Normal = normal, Texcoord = texcoord, Tangent = tangent, Binormal = binormal }); } } // Build the section's subparts builder.BeginMesh(); var indices = new List <ushort>(); foreach (var submesh in section.Submeshes) { builder.BeginPart((short)submesh.ShaderIndex, (ushort)submesh.FaceIndex, (ushort)submesh.FaceCount, (ushort)submesh.VertexCount); for (var j = 0; j < submesh.SubsetCount; j++) { var subpart = section.Subsets[submesh.SubsetIndex + j]; builder.DefineSubPart((ushort)subpart.FaceIndex, (ushort)subpart.FaceCount, (ushort)subpart.VertexCount); } builder.EndPart(); } builder.BindRigidVertexBuffer(rigidVertices, 0); builder.BindIndexBuffer(section.Indices.Select(index => (ushort)index), PrimitiveType.TriangleList); builder.EndMesh(); } builder.EndPermutation(); builder.EndRegion(); foreach (var instance in sbsp.GeomInstances) { var mesh = builder.Meshes[instance.SectionIndex]; if (mesh.VertexFormat == VertexBufferFormat.Rigid) { foreach (var i in mesh.RigidVertices) { i.Position = new Common.Vector4( i.Position.X + instance.TransformMatrix.m41, i.Position.Y + instance.TransformMatrix.m42, i.Position.Z + instance.TransformMatrix.m43, i.Position.W); } } else if (mesh.VertexFormat == VertexBufferFormat.World) { foreach (var i in mesh.WorldVertices) { i.Position = new Common.Vector4( i.Position.X + instance.TransformMatrix.m41, i.Position.Y + instance.TransformMatrix.m42, i.Position.Z + instance.TransformMatrix.m43, i.Position.W); } } else if (mesh.VertexFormat == VertexBufferFormat.Skinned) { foreach (var i in mesh.SkinnedVertices) { i.Position = new Common.Vector4( i.Position.X + instance.TransformMatrix.m41, i.Position.Y + instance.TransformMatrix.m42, i.Position.Z + instance.TransformMatrix.m43, i.Position.W); } } } } else { foreach (var region in mode.Regions) { var regionNameId = Info.StringIDs.GetStringID(region.Name); builder.BeginRegion(regionNameId.Index == 0 && region.Name != "" ? regionNameId = Info.StringIDs.Add(region.Name) : regionNameId); foreach (var permutation in region.Permutations) { if (permutation.PieceCount <= 0 || permutation.PieceIndex == -1) { continue; } var permutationNameId = Info.StringIDs.GetStringID(permutation.Name); builder.BeginPermutation(permutationNameId.Index == 0 && permutation.Name != "" ? permutationNameId = Info.StringIDs.Add(permutation.Name) : permutationNameId); for (var i = permutation.PieceIndex; i < permutation.PieceIndex + permutation.PieceCount; i++) { var section = mode.ModelSections[i]; if (section.Submeshes.Count == 0 || section.Vertices == null) { continue; } // // Collect the section's vertices // var skinnedVertices = new List <SkinnedVertex>(); var rigidVertices = new List <RigidVertex>(); VertexValue v; bool isSkinned = section.Vertices[0].TryGetValue("blendindices", 0, out v) && section.NodeIndex == 255; bool isBoned = section.Vertices[0].FormatName.Contains("rigid_boned"); foreach (var vertex in section.Vertices) { vertex.TryGetValue("position", 0, out v); var position = new Common.Vector4(v.Data.X, v.Data.Y, v.Data.Z, 1); vertex.TryGetValue("normal", 0, out v); var normal = new Common.Vector3(v.Data.I, v.Data.J, v.Data.K); vertex.TryGetValue("texcoords", 0, out v); var texcoord = new Common.Vector2(v.Data.X, v.Data.Y); vertex.TryGetValue("tangent", 0, out v); var tangent = new Common.Vector4(v.Data.X, v.Data.Y, v.Data.Z, 1); vertex.TryGetValue("binormal", 0, out v); var binormal = new Common.Vector3(v.Data.X, v.Data.Y, v.Data.Z); rigidVertices.Add( new RigidVertex { Position = position, Normal = normal, Texcoord = texcoord, Tangent = tangent, Binormal = binormal }); if (isBoned) { var blendIndices = new List <byte>(); vertex.TryGetValue("blendindices", 0, out v); blendIndices.Add((byte)v.Data.A); blendIndices.Add((byte)v.Data.B); blendIndices.Add((byte)v.Data.C); blendIndices.Add((byte)v.Data.D); skinnedVertices.Add(new SkinnedVertex { Position = position, Normal = normal, Texcoord = texcoord, Tangent = tangent, Binormal = binormal, BlendIndices = blendIndices.ToArray(), BlendWeights = new[] { 1.0f, 0.0f, 0.0f, 0.0f } }); } else if (isSkinned) { var blendIndices = new List <byte>(); var blendWeights = new List <float>(); vertex.TryGetValue("blendindices", 0, out v); blendIndices.Add((byte)v.Data.A); blendIndices.Add((byte)v.Data.B); blendIndices.Add((byte)v.Data.C); blendIndices.Add((byte)v.Data.D); vertex.TryGetValue("blendweight", 0, out v); blendWeights.Add(v.Data.A); blendWeights.Add(v.Data.B); blendWeights.Add(v.Data.C); blendWeights.Add(v.Data.D); skinnedVertices.Add(new SkinnedVertex { Position = position, Normal = normal, Texcoord = texcoord, Tangent = tangent, Binormal = binormal, BlendIndices = blendIndices.ToArray(), BlendWeights = blendWeights.ToArray() }); } } bool isRigid = false; if (skinnedVertices.Count == 0) { isRigid = rigidVertices.Count != 0; } // // Build the section's submeshes // builder.BeginMesh(); var indices = new List <ushort>(); foreach (var submesh in section.Submeshes) { builder.BeginPart((short)submesh.ShaderIndex, (ushort)submesh.FaceIndex, (ushort)submesh.FaceCount, (ushort)submesh.VertexCount); for (var j = 0; j < submesh.SubsetCount; j++) { var subpart = section.Subsets[submesh.SubsetIndex + j]; builder.DefineSubPart((ushort)subpart.FaceIndex, (ushort)subpart.FaceCount, (ushort)subpart.VertexCount); } builder.EndPart(); } if (isRigid) { builder.BindRigidVertexBuffer(rigidVertices, (sbyte)section.NodeIndex); } else if (isSkinned || isBoned) { builder.BindSkinnedVertexBuffer(skinnedVertices); } builder.BindIndexBuffer(section.Indices.Select(index => (ushort)index), PrimitiveType.TriangleStrip); builder.EndMesh(); } builder.EndPermutation(); } builder.EndRegion(); } } // // Finalize the new render_model tag // var resourceStream = new MemoryStream(); var newRenderModel = builder.Build(Info.Serializer, resourceStream); var renderModelNameStringID = Info.StringIDs.GetStringID(isBSP ? "default" : mode.Name); newRenderModel.Name = renderModelNameStringID.Index == -1 ? renderModelNameStringID = Info.StringIDs.Add(isBSP ? "default" : mode.Name) : renderModelNameStringID; // // Add the markers to the new render_model // newRenderModel.MarkerGroups = new List <TagDefinitions.RenderModel.MarkerGroup>(); var blamMarkerGroups = isBSP ? new List <render_model.MarkerGroup>() : mode.MarkerGroups; foreach (var markerGroup in blamMarkerGroups) { var markerGroupNameId = Info.StringIDs.GetStringID(markerGroup.Name); if (markerGroupNameId.Index == -1) { markerGroupNameId = Info.StringIDs.Add(markerGroup.Name); } newRenderModel.MarkerGroups.Add( new TagDefinitions.RenderModel.MarkerGroup { Name = markerGroupNameId, Markers = markerGroup.Markers.Select(marker => new TagDefinitions.RenderModel.MarkerGroup.Marker { RegionIndex = (sbyte)marker.RegionIndex, PermutationIndex = (sbyte)marker.PermutationIndex, NodeIndex = (sbyte)marker.NodeIndex, Unknown3 = 0, Translation = new Common.Vector3(marker.Position.X, marker.Position.Y, marker.Position.Z), Rotation = new Common.Vector4(marker.Rotation.X, marker.Rotation.Y, marker.Rotation.Z, marker.Rotation.W), Scale = marker.Scale }).ToList() }); } // // Disable rigid nodes on skinned meshes // foreach (var mesh in newRenderModel.Geometry.Meshes) { if (mesh.Type == VertexType.Skinned) { mesh.RigidNodeIndex = -1; } } // // Add a new resource for the model data // Console.WriteLine("Writing resource data..."); var resources = new ResourceDataManager(); resources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); resourceStream.Position = 0; resources.Add(newRenderModel.Geometry.Resource, ResourceLocation.Resources, resourceStream); using (var cacheStream = Info.CacheFile.Open(FileMode.Open, FileAccess.ReadWrite)) { Console.WriteLine("Writing tag data..."); newRenderModel.Geometry.Resource.Owner = newTag; var context = new Serialization.TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, newTag); Info.Serializer.Serialize(context, newRenderModel); } resourceStream.Close(); // // Save new string_ids // if (Info.StringIDs.Strings.Count != initialStringIDCount) { Console.WriteLine("Saving string_ids..."); using (var stringIdStream = Info.StringIDsFile.Open(FileMode.Open, FileAccess.ReadWrite)) Info.StringIDs.Save(stringIdStream); } // // Done! // Console.WriteLine("Ported render_model \"" + renderModelName + "\" successfully!"); return(true); }
public override bool Execute(List <string> args) { if (args.Count < 1 || args.Count > 2) { return(false); } TagInstance destination = _cache.Tags[0x3317]; if (args.Count == 2) { destination = ArgumentParser.ParseTagIndex(_cache, args[0]); if (!destination.IsInGroup("mode")) { Console.WriteLine("Specified tag is not a render_model: " + args[0]); return(false); } args = args.Skip(1).ToList(); } var builder = new RenderModelBuilder(_info.Version); // Add a root node var node = builder.AddNode(new RenderModel.Node { Name = _stringIds.GetStringId("street_cone"), ParentNode = -1, FirstChildNode = -1, NextSiblingNode = -1, DefaultRotation = new Vector4(0, 0, 0, -1), DefaultScale = 1, InverseForward = new Vector3(1, 0, 0), InverseLeft = new Vector3(0, 1, 0), InverseUp = new Vector3(0, 0, 1), }); // Begin building the default region and permutation builder.BeginRegion(_stringIds.GetStringId("default")); builder.BeginPermutation(_stringIds.GetStringId("default")); using (var importer = new AssimpContext()) { Scene model; using (var logStream = new LogStream((msg, userData) => Console.WriteLine(msg))) { logStream.Attach(); model = importer.ImportFile(args[0], PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.PreTransformVertices | PostProcessSteps.Triangulate); logStream.Detach(); } Console.WriteLine("Assembling vertices..."); // Build a multipart mesh from the model data, // with each model mesh mapping to a part of one large mesh and having its own material builder.BeginMesh(); ushort partStartVertex = 0; ushort partStartIndex = 0; var vertices = new List <RigidVertex>(); var indices = new List <ushort>(); foreach (var mesh in model.Meshes) { for (var i = 0; i < mesh.VertexCount; i++) { var position = mesh.Vertices[i]; var normal = mesh.Normals[i]; var uv = mesh.TextureCoordinateChannels[0][i]; var tangent = mesh.Tangents[i]; var bitangent = mesh.BiTangents[i]; vertices.Add(new RigidVertex { Position = new Vector4(position.X, position.Y, position.Z, 1), Normal = new Vector3(normal.X, normal.Y, normal.Z), Texcoord = new Vector2(uv.X, uv.Y), Tangent = new Vector4(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new Vector3(bitangent.X, bitangent.Y, bitangent.Z), }); } // Build the index buffer var meshIndices = mesh.GetIndices(); indices.AddRange(meshIndices.Select(i => (ushort)(i + partStartVertex))); // Define a material and part for this mesh var material = builder.AddMaterial(new RenderMaterial { RenderMethod = _cache.Tags[0x101F], }); builder.DefinePart(material, partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); // Move to the next part partStartVertex += (ushort)mesh.VertexCount; partStartIndex += (ushort)meshIndices.Length; } // Bind the vertex and index buffers builder.BindRigidVertexBuffer(vertices, node); builder.BindIndexBuffer(indices, PrimitiveType.TriangleList); builder.EndMesh(); } builder.EndPermutation(); builder.EndRegion(); Console.WriteLine("Building Blam mesh data..."); var resourceStream = new MemoryStream(); var renderModel = builder.Build(_info.Serializer, resourceStream); Console.WriteLine("Writing resource data..."); // Add a new resource for the model data var resources = new ResourceDataManager(); resources.LoadCachesFromDirectory(_fileInfo.DirectoryName); resourceStream.Position = 0; resources.Add(renderModel.Geometry.Resource, ResourceLocation.Resources, resourceStream); Console.WriteLine("Writing tag data..."); using (var cacheStream = _fileInfo.Open(FileMode.Open, FileAccess.ReadWrite)) { var tag = destination; var context = new TagSerializationContext(cacheStream, _cache, _stringIds, tag); _info.Serializer.Serialize(context, renderModel); } Console.WriteLine("Model imported successfully!"); return(true); }
public override bool Execute(List<string> args) { if (args.Count < 1 || args.Count > 3) return false; ResourceLocation location = ResourceLocation.Resources; TagInstance destination = Info.Cache.Tags[0x3317]; if (args.Count == 3) { var value = args[0]; switch (value) { case "resources": location = ResourceLocation.Resources; break; case "textures": location = ResourceLocation.Textures; break; case "textures_b": location = ResourceLocation.TexturesB; break; case "audio": location = ResourceLocation.Audio; break; case "video": location = ResourceLocation.Video; break; case "render_models": location = ResourceLocation.RenderModels; break; case "lightmaps": location = ResourceLocation.Lightmaps; break; default: Console.WriteLine("Invalid resource location: " + value); return false; } args.RemoveAt(0); } if (args.Count == 2) { destination = ArgumentParser.ParseTagIndex(Info, args[0]); if (!destination.IsInGroup("mode")) { Console.WriteLine("Specified tag is not a render_model: " + args[0]); return false; } args.RemoveAt(0); } var builder = new RenderModelBuilder(Info.Version); // Add a root node var node = builder.AddNode(new RenderModel.Node { Name = Info.StringIDs.GetStringID("street_cone"), ParentNode = -1, FirstChildNode = -1, NextSiblingNode = -1, DefaultRotation = new Vector4(0, 0, 0, -1), DefaultScale = 1, InverseForward = new Vector3(1, 0, 0), InverseLeft = new Vector3(0, 1, 0), InverseUp = new Vector3(0, 0, 1), }); // Begin building the default region and permutation builder.BeginRegion(Info.StringIDs.GetStringID("default")); builder.BeginPermutation(Info.StringIDs.GetStringID("default")); using (var importer = new AssimpContext()) { Scene model; using (var logStream = new LogStream((msg, userData) => Console.WriteLine(msg))) { logStream.Attach(); model = importer.ImportFile(args[0], PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.PreTransformVertices | PostProcessSteps.Triangulate); logStream.Detach(); } Console.WriteLine("Assembling vertices..."); // Build a multipart mesh from the model data, // with each model mesh mapping to a part of one large mesh and having its own material builder.BeginMesh(); ushort partStartVertex = 0; ushort partStartIndex = 0; var vertices = new List<RigidVertex>(); var indices = new List<ushort>(); foreach (var mesh in model.Meshes) { for (var i = 0; i < mesh.VertexCount; i++) { var position = mesh.Vertices[i]; var normal = mesh.Normals[i]; var uv = mesh.TextureCoordinateChannels[0][i]; var tangent = mesh.Tangents[i]; var bitangent = mesh.BiTangents[i]; vertices.Add(new RigidVertex { Position = new Vector4(position.X, position.Y, position.Z, 1), Normal = new Vector3(normal.X, normal.Y, normal.Z), Texcoord = new Vector2(uv.X, uv.Y), Tangent = new Vector4(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new Vector3(bitangent.X, bitangent.Y, bitangent.Z), }); } // Build the index buffer var meshIndices = mesh.GetIndices(); indices.AddRange(meshIndices.Select(i => (ushort)(i + partStartVertex))); // Define a material and part for this mesh var material = builder.AddMaterial(new RenderMaterial { RenderMethod = Info.Cache.Tags[0x101F], }); builder.BeginPart(material, partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.DefineSubPart(partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.EndPart(); // Move to the next part partStartVertex += (ushort)mesh.VertexCount; partStartIndex += (ushort)meshIndices.Length; } // Bind the vertex and index buffers builder.BindRigidVertexBuffer(vertices, node); builder.BindIndexBuffer(indices, PrimitiveType.TriangleList); builder.EndMesh(); } builder.EndPermutation(); builder.EndRegion(); Console.WriteLine("Building Blam mesh data..."); var resourceStream = new MemoryStream(); var renderModel = builder.Build(Info.Serializer, resourceStream); Console.WriteLine("Writing resource data..."); // Add a new resource for the model data var resources = new ResourceDataManager(); resources.LoadCachesFromDirectory(Info.CacheFile.DirectoryName); resourceStream.Position = 0; resources.Add(renderModel.Geometry.Resource, location, resourceStream); Console.WriteLine("Writing tag data..."); using (var cacheStream = Info.OpenCacheReadWrite()) { var tag = destination; var context = new TagSerializationContext(cacheStream, Info.Cache, Info.StringIDs, tag); Info.Serializer.Serialize(context, renderModel); } Console.WriteLine("Model imported successfully!"); return true; }
public override object Execute(List <string> args) { if (args.Count != 1) { return(false); } if (!CacheContext.TryGetTag <Shader>(@"shaders\invalid", out var defaultShaderTag)) { Console.WriteLine("WARNING: 'shaders\\invalid.shader' not found!"); Console.WriteLine("You will have to assign material shaders manually."); } var stringIdCount = CacheContext.StringIdCache.Strings.Count; var sceneFile = new FileInfo(args[0]); if (!sceneFile.Exists) { throw new FileNotFoundException(sceneFile.FullName); } if (sceneFile.Extension.ToLower() != ".dae") { throw new FormatException($"Input file is not COLLADA format: {sceneFile.FullName}"); } Scene scene; using (var importer = new AssimpContext()) { scene = importer.ImportFile(sceneFile.FullName, PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.Triangulate); } var builder = new RenderModelBuilder(CacheContext); var nodes = new Dictionary <string, sbyte>(); var materialIndices = new Dictionary <string, short>(); foreach (var oldNode in Definition.Nodes) { var name = CacheContext.GetString(oldNode.Name); nodes[name] = builder.AddNode(oldNode); } foreach (var region in Definition.Regions) { builder.BeginRegion(region.Name); var regionName = CacheContext.GetString(region.Name); foreach (var permutation in region.Permutations) { if (permutation.MeshCount > 1) { throw new NotSupportedException("multiple permutation meshes"); } if (permutation.MeshIndex == -1) { continue; } var permName = CacheContext.GetString(permutation.Name); var meshName = $"{regionName}FBXASC058{permName}Mesh"; var permMeshes = scene.Meshes.Where(i => i.Name == meshName).ToList(); if (permMeshes.Count == 0) { throw new Exception($"No mesh(es) found for region '{regionName}' permutation '{permName}'!"); } permMeshes.Sort((a, b) => a.MaterialIndex.CompareTo(b.MaterialIndex)); // Build a multipart mesh from the model data, // with each model mesh mapping to a part of one large mesh and having its own material ushort partStartVertex = 0; ushort partStartIndex = 0; var rigidVertices = new List <RigidVertex>(); var skinnedVertices = new List <SkinnedVertex>(); var indices = new List <ushort>(); var vertexType = Definition.Geometry.Meshes[permutation.MeshIndex].Type; var rigidNode = Definition.Geometry.Meshes[permutation.MeshIndex].RigidNodeIndex; builder.BeginPermutation(permutation.Name); builder.BeginMesh(); foreach (var mesh in permMeshes) { for (var i = 0; i < mesh.VertexCount; i++) { var position = mesh.Vertices[i]; var normal = mesh.Normals[i]; Vector3D uv; try { uv = mesh.TextureCoordinateChannels[0][i]; } catch { Console.WriteLine($"WARNING: Missing texture coordinate for vertex {i} in '{regionName}:{permName}'"); uv = new Vector3D(); } var tangent = mesh.Tangents.Count != 0 ? mesh.Tangents[i] : new Vector3D(); var bitangent = mesh.BiTangents.Count != 0 ? mesh.BiTangents[i] : new Vector3D(); if (vertexType == VertexType.Skinned) { var blendIndicesList = new List <byte>(); var blendWeightsList = new List <float>(); foreach (var bone in mesh.Bones) { foreach (var vertexInfo in bone.VertexWeights) { if (vertexInfo.VertexID == i) { // HAX BELOW //if (bone.Name.StartsWith("_")) //bone.Name = bone.Name.Substring(4); //if (bone.Name.EndsWith("2")) //bone.Name = bone.Name.Replace("2", "_tip"); //else if (bone.Name != "spine1" && bone.Name.EndsWith("1")) //bone.Name = bone.Name.Replace("1", "_low"); blendIndicesList.Add((byte)nodes[bone.Name]); blendWeightsList.Add(vertexInfo.Weight); } } } var blendIndices = new byte[4]; var blendWeights = new float[4]; for (int j = 0; j < blendIndicesList.Count; j++) { if (j < 4) { blendIndices[j] = blendIndicesList[j]; } } for (int j = 0; j < blendWeightsList.Count; j++) { if (j < 4) { blendWeights[j] = blendWeightsList[j]; } } skinnedVertices.Add(new SkinnedVertex { Position = new RealQuaternion(position.X * 0.01f, position.Y * 0.01f, position.Z * 0.01f, 1), Texcoord = new RealVector2d(uv.X, -uv.Y), Normal = new RealVector3d(normal.X, normal.Y, normal.Z), Tangent = new RealQuaternion(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new RealVector3d(bitangent.X, bitangent.Y, bitangent.Z), BlendIndices = blendIndices, BlendWeights = blendWeights }); } else { rigidVertices.Add(new RigidVertex { Position = new RealQuaternion(position.X * 0.01f, position.Y * 0.01f, position.Z * 0.01f, 1), Texcoord = new RealVector2d(uv.X, -uv.Y), Normal = new RealVector3d(normal.X, normal.Y, normal.Z), Tangent = new RealQuaternion(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new RealVector3d(bitangent.X, bitangent.Y, bitangent.Z), }); } } // Build the index buffer var meshIndices = mesh.GetIndices(); indices.AddRange(meshIndices.Select(i => (ushort)(i + partStartVertex))); // Define a material and part for this mesh var meshMaterial = scene.Materials[mesh.MaterialIndex]; short materialIndex = 0; if (materialIndices.ContainsKey(meshMaterial.Name)) { materialIndex = materialIndices[meshMaterial.Name]; } else { materialIndex = materialIndices[meshMaterial.Name] = builder.AddMaterial(new RenderMaterial { RenderMethod = defaultShaderTag, }); } builder.BeginPart(materialIndex, partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.DefineSubPart(partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.EndPart(); // Move to the next part partStartVertex += (ushort)mesh.VertexCount; partStartIndex += (ushort)meshIndices.Length; } // Bind the vertex and index buffers if (vertexType == VertexType.Skinned) { builder.BindSkinnedVertexBuffer(skinnedVertices); } else { builder.BindRigidVertexBuffer(rigidVertices, rigidNode); } builder.BindIndexBuffer(indices, IndexBufferFormat.TriangleList); builder.EndMesh(); builder.EndPermutation(); } builder.EndRegion(); } using (var resourceStream = new MemoryStream()) { Console.Write("Building render_geometry..."); var newDefinition = builder.Build(CacheContext.Serializer, resourceStream); Definition.Regions = newDefinition.Regions; Definition.Geometry = newDefinition.Geometry; Definition.Nodes = newDefinition.Nodes; Definition.Materials = newDefinition.Materials; resourceStream.Position = 0; Definition.Geometry.Resource.ChangeLocation(ResourceLocation.ResourcesB); CacheContext.AddResource(Definition.Geometry.Resource, resourceStream); Console.WriteLine("done."); } // // TODO: Build the new render_model and update the original render_model here... // Console.Write("Writing render_model tag data..."); using (var cacheStream = CacheContext.OpenTagCacheReadWrite()) CacheContext.Serialize(cacheStream, Tag, Definition); Console.WriteLine("done."); if (stringIdCount != CacheContext.StringIdCache.Strings.Count) { Console.Write("Saving string ids..."); using (var stream = CacheContext.OpenStringIdCacheReadWrite()) CacheContext.StringIdCache.Save(stream); Console.WriteLine("done"); } Console.WriteLine("Replaced render_geometry successfully."); return(true); }
public override object Execute(List <string> args) { if (args.Count < 1 || args.Count > 2) { return(false); } var stringIdCount = Cache.StringTable.Count; var destinationTag = Cache.TagCache.GetTag(@"objects\gear\human\industrial\street_cone\street_cone", "mode"); string vertexType = "rigid"; if (args[0] == "skinned" || args[0] == "rigid") { vertexType = args[0]; args.RemoveAt(0); } if (args.Count == 2) { if (!Cache.TryGetTag(args[0], out destinationTag) || !destinationTag.IsInGroup("mode")) { Console.WriteLine("Specified tag is not a render_model: " + args[0]); return(false); } args.RemoveAt(0); } RenderModel edMode = null; using (var cacheStream = Cache.OpenCacheReadWrite()) edMode = Cache.Deserialize <RenderModel>(cacheStream, destinationTag); // Get a list of the original nodes var nodeIndices = new Dictionary <string, int>(); foreach (var a in edMode.Nodes) { nodeIndices.Add(Cache.StringTable.GetString(a.Name), edMode.Nodes.IndexOf(a)); } // Read the custom model file if (!File.Exists(args[0])) { return(false); } Console.WriteLine($"File date: {File.GetLastWriteTime(args[0])}"); var builder = new RenderModelBuilder(Cache); using (var importer = new AssimpContext()) { Scene model; if (vertexType == "skinned") { using (var logStream = new LogStream((msg, userData) => Console.WriteLine(msg))) { logStream.Attach(); model = importer.ImportFile(args[0], PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.Triangulate); logStream.Detach(); } for (var i = 0; i < model.Meshes.Count; i++) { if (!model.Meshes[i].HasBones) { throw new Exception($"Mesh \"{model.Meshes[i].Name}\" has no bones!"); } } } else { using (var logStream = new LogStream((msg, userData) => Console.WriteLine(msg))) { logStream.Attach(); model = importer.ImportFile(args[0], PostProcessSteps.CalculateTangentSpace | PostProcessSteps.GenerateNormals | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.PreTransformVertices | PostProcessSteps.Triangulate); logStream.Detach(); } } Console.WriteLine("Assembling vertices..."); // Add nodes var rigidNode = builder.AddNode(new RenderModel.Node { Name = Cache.StringTable.GetStringId("street_cone"), ParentNode = -1, FirstChildNode = -1, NextSiblingNode = -1, DefaultRotation = new RealQuaternion(0, 0, 0, -1), DefaultScale = 1, InverseForward = new RealVector3d(1, 0, 0), InverseLeft = new RealVector3d(0, 1, 0), InverseUp = new RealVector3d(0, 0, 1), }); // Build a multipart mesh from the model data, // with each model mesh mapping to a part of one large mesh and having its own material ushort partStartVertex = 0; ushort partStartIndex = 0; var rigidVertices = new List <RigidVertex>(); var skinnedVertices = new List <SkinnedVertex>(); var indices = new List <ushort>(); Dictionary <string, int> newNodes = new Dictionary <string, int>(); foreach (var mesh in model.Meshes) { var meshIndex = model.Meshes.IndexOf(mesh); Console.Write($"Enter a region name for '{mesh.Name}' (mesh index {meshIndex}): "); var regionName = Console.ReadLine(); var regionStringId = Cache.StringTable.GetStringId(regionName); if (regionStringId == StringId.Invalid) { regionStringId = Cache.StringTable.AddString(regionName); } // Begin building the default region and permutation builder.BeginRegion(regionStringId); builder.BeginPermutation(Cache.StringTable.GetStringId("default")); builder.BeginMesh(); for (var i = 0; i < mesh.VertexCount; i++) { var position = mesh.Vertices[i]; var normal = mesh.Normals[i]; var uv = mesh.TextureCoordinateChannels[0][i]; var tangent = mesh.Tangents.Count != 0 ? mesh.Tangents[i] : new Vector3D(); var bitangent = mesh.BiTangents.Count != 0 ? mesh.BiTangents[i] : new Vector3D(); if (vertexType == "skinned") { var blendIndicesList = new List <byte>(); var blendWeightsList = new List <float>(); var bonesList = new List <string>(); foreach (var bone in mesh.Bones) { foreach (var vertexInfo in bone.VertexWeights) { if (vertexInfo.VertexID == i) { bonesList.Add(bone.Name); blendIndicesList.Add((byte)nodeIndices[bone.Name]); blendWeightsList.Add(vertexInfo.Weight); } } } var blendIndices = new byte[4]; var blendWeights = new float[4]; for (int j = 0; j < blendIndicesList.Count; j++) { if (j < 4) { blendIndices[j] = blendIndicesList[j]; } } for (int j = 0; j < blendWeightsList.Count; j++) { if (j < 4) { blendWeights[j] = blendWeightsList[j]; } } skinnedVertices.Add(new SkinnedVertex { Position = new RealQuaternion(position.X, position.Y, position.Z, 1), Texcoord = new RealVector2d(uv.X, -uv.Y), Normal = new RealVector3d(normal.X, normal.Y, normal.Z), Tangent = new RealQuaternion(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new RealVector3d(bitangent.X, bitangent.Y, bitangent.Z), BlendIndices = blendIndices, BlendWeights = blendWeights }); } else { rigidVertices.Add(new RigidVertex { Position = new RealQuaternion(position.X, position.Y, position.Z, 1), Texcoord = new RealVector2d(uv.X, -uv.Y), Normal = new RealVector3d(normal.X, normal.Y, normal.Z), Tangent = new RealQuaternion(tangent.X, tangent.Y, tangent.Z, 1), Binormal = new RealVector3d(bitangent.X, bitangent.Y, bitangent.Z), }); } } // Build the index buffer var meshIndices = mesh.GetIndices(); indices.AddRange(meshIndices.Select(i => (ushort)(i + partStartVertex))); // Define a material and part for this mesh var material = builder.AddMaterial(new RenderMaterial { RenderMethod = Cache.TagCache.GetTag(@"shaders\invalid", "rmsh"), }); builder.BeginPart(material, partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.DefineSubPart(partStartIndex, (ushort)meshIndices.Length, (ushort)mesh.VertexCount); builder.EndPart(); // Move to the next part partStartVertex += (ushort)mesh.VertexCount; partStartIndex += (ushort)meshIndices.Length; // Bind the vertex and index buffers if (vertexType == "skinned") { builder.BindSkinnedVertexBuffer(skinnedVertices); } else { builder.BindRigidVertexBuffer(rigidVertices, rigidNode); } builder.BindIndexBuffer(indices, IndexBufferFormat.TriangleList); builder.EndMesh(); builder.EndPermutation(); builder.EndRegion(); } } Console.Write("Building render_geometry..."); var resourceStream = new MemoryStream(); var renderModel = builder.Build(Cache.Serializer, resourceStream); if (vertexType == "skinned") { // Copy required data from the original render_model tag renderModel.Nodes = edMode.Nodes; renderModel.MarkerGroups = edMode.MarkerGroups; renderModel.RuntimeNodeOrientations = edMode.RuntimeNodeOrientations; } Console.WriteLine("done."); // // Serialize the new render_model tag // Console.Write("Writing render_model tag data..."); using (var cacheStream = Cache.OpenCacheReadWrite()) Cache.Serialize(cacheStream, destinationTag, renderModel); Console.WriteLine("done."); // // Save any new string ids // if (stringIdCount != Cache.StringTable.Count) { Console.Write("Saving string ids..."); Cache.SaveStrings(); Console.WriteLine("done"); } Console.WriteLine("Model imported successfully!"); return(true); }