public TextureAssembly AssembleTextures(TextureCompositor compositor, Dictionary <string, Material> materials) { TextureAssembly assembly = new TextureAssembly(); assembly.Images = new Dictionary <string, Image>(); assembly.MatLinks = new Dictionary <string, string>(); Bitmap uvMap = compositor.BakeTextureMap(); Rbx2Source.SetDebugImage(uvMap); ImageAttributes blankAtt = new ImageAttributes(); foreach (string materialName in materials.Keys) { Rbx2Source.Print("Building Material {0}", materialName); Material material = materials[materialName]; Image image = null; if (material.UseAvatarMap) { Limb limb; if (Enum.TryParse(materialName, out limb)) { Rectangle cropRegion = UVCrops[limb]; Size size = cropRegion.Size; int w = size.Width; int h = size.Height; Point origin = cropRegion.Location; int x = origin.X; int y = origin.Y; Bitmap newImg = new Bitmap(w, h); Graphics graphics = Graphics.FromImage(newImg); Rectangle dest = new Rectangle(Point.Empty, size); graphics.DrawImage(uvMap, dest, x, y, w, h, GraphicsUnit.Pixel, blankAtt); graphics.Dispose(); image = newImg; } } else { Asset texture = material.TextureAsset; if (texture != null) { byte[] textureData = texture.GetContent(); MemoryStream textureStream = new MemoryStream(textureData); image = Image.FromStream(textureStream); } } if (image != null) { assembly.Images.Add(materialName, image); assembly.MatLinks.Add(materialName, materialName); } else { Rbx2Source.Print("Missing Image for Material {0}?", materialName); } } return(assembly); }
public AssemblerData Assemble(object metadata) { UserAvatar avatar = metadata as UserAvatar; if (avatar == null) { throw new Exception("bad cast"); } UserInfo userInfo = avatar.UserInfo; string userName = FileUtility.MakeNameWindowsSafe(userInfo.Username); string appData = Environment.GetEnvironmentVariable("AppData"); string rbx2Source = Path.Combine(appData, "Rbx2Source"); string avatars = Path.Combine(rbx2Source, "Avatars"); string userBin = Path.Combine(avatars, userName); string modelDir = Path.Combine(userBin, "Model"); string animDir = Path.Combine(modelDir, "Animations"); string texturesDir = Path.Combine(userBin, "Textures"); string materialsDir = Path.Combine(userBin, "Materials"); FileUtility.InitiateEmptyDirectories(modelDir, animDir, texturesDir, materialsDir); AvatarType avatarType = avatar.ResolvedAvatarType; ICharacterAssembler assembler; if (avatarType == AvatarType.R15) { assembler = new R15CharacterAssembler(); } else { assembler = new R6CharacterAssembler(); } string avatarTypeName = Rbx2Source.GetEnumName(avatar.ResolvedAvatarType); Folder characterAssets = AppendCharacterAssets(avatar, avatarTypeName); Rbx2Source.ScheduleTasks("BuildCharacter", "BuildCollisionModel", "BuildAnimations", "BuildTextures", "BuildMaterials", "BuildCompilerScript"); Rbx2Source.PrintHeader("BUILDING CHARACTER MODEL"); StudioMdlWriter writer = assembler.AssembleModel(characterAssets, avatar.Scales); string studioMdl = writer.BuildFile(); string modelPath = Path.Combine(modelDir, "CharacterModel.smd"); FileUtility.WriteFile(modelPath, studioMdl); // Clear the triangles so we can build a reference pose .smd file. writer.Triangles.Clear(); string staticPose = writer.BuildFile(); string refPath = Path.Combine(modelDir, "ReferencePos.smd"); FileUtility.WriteFile(refPath, staticPose); Rbx2Source.MarkTaskCompleted("BuildCharacter"); Rbx2Source.PrintHeader("BUILDING COLLISION MODEL"); Folder lowPoly = new Folder(); SpecialMesh lowPolyHead = new SpecialMesh(); lowPolyHead.MeshId = "rbxassetid://582002794"; lowPolyHead.MeshType = MeshType.FileMesh; lowPolyHead.Scale = new Vector3(1, 1, 1); lowPolyHead.Offset = new Vector3(); lowPolyHead.Parent = lowPoly; StudioMdlWriter collisionWriter = assembler.AssembleModel(lowPoly, avatar.Scales); string collisionModel = collisionWriter.BuildFile(); string cmodelPath = Path.Combine(modelDir, "CollisionModel.smd"); FileUtility.WriteFile(cmodelPath, collisionModel); byte[] collisionJoints = assembler.CollisionModelScript; string cjointsPath = Path.Combine(modelDir, "CollisionJoints.qc"); FileUtility.WriteFile(cjointsPath, collisionJoints); Rbx2Source.MarkTaskCompleted("BuildCollisionModel"); Rbx2Source.PrintHeader("BUILDING CHARACTER ANIMATIONS"); Dictionary <string, string> animations = GatherAnimations(avatarType); if (animations.Count > 0) { foreach (string animName in animations.Keys) { Rbx2Source.Print("Building Animation {0}", animName); string localAnimPath = animations[animName]; Asset animAsset = Asset.FromResource(localAnimPath); Folder import = RBXM.LoadFromAsset(animAsset); KeyframeSequence sequence = import.FindFirstChildOfClass <KeyframeSequence>(); sequence.Name = animName; sequence.AvatarType = avatarType; string animation = AnimationAssembler.Assemble(sequence, writer.Skeleton[0].Bones); string animPath = Path.Combine(animDir, animName + ".smd"); FileUtility.WriteFile(animPath, animation); } } else { Rbx2Source.Print("No animations found :("); } Rbx2Source.MarkTaskCompleted("BuildAnimations"); Dictionary <string, Material> materials = writer.Materials; Rbx2Source.PrintHeader("BUILDING CHARACTER TEXTURES"); string compileDirectory = "roblox_avatars/" + userName; TextureCompositor texCompositor = assembler.ComposeTextureMap(characterAssets, avatar.BodyColors); TextureAssembly texAssembly = assembler.AssembleTextures(texCompositor, materials); CompositData.FreeAllocatedTextures(); texAssembly.MaterialDirectory = compileDirectory; Dictionary <string, Image> images = texAssembly.Images; foreach (string imageName in images.Keys) { Rbx2Source.Print("Writing Image {0}.png", imageName); Image image = images[imageName]; string imagePath = Path.Combine(texturesDir, imageName + ".png"); try { image.Save(imagePath, ImageFormat.Png); } catch { Rbx2Source.Print("IMAGE {0}.png FAILED TO SAVE!", imageName); } FileUtility.LockFile(imagePath); } Rbx2Source.MarkTaskCompleted("BuildTextures"); Rbx2Source.PrintHeader("BUILDING MATERIAL FILES"); Dictionary <string, string> matLinks = texAssembly.MatLinks; foreach (string mtlName in matLinks.Keys) { Rbx2Source.Print("Building VMT {0}.vmt", mtlName); string targetVtf = matLinks[mtlName]; Material mtl = materials[mtlName]; ValveMaterial vmt = new ValveMaterial(mtl); vmt.SetField("basetexture", "models/" + compileDirectory + "/" + targetVtf); string vmtPath = Path.Combine(materialsDir, mtlName + ".vmt"); string vmtContent = vmt.ToString(); FileUtility.WriteFile(vmtPath, vmtContent); } Rbx2Source.MarkTaskCompleted("BuildMaterials"); Rbx2Source.PrintHeader("WRITING COMPILER SCRIPT"); QCWriter qc = new QCWriter(); QCommand model = new QCommand("body", userName, "CharacterModel.smd"); qc.AddCommand(model); string modelNameStr = compileDirectory + ".mdl"; qc.WriteBasicCmd("modelname", modelNameStr); qc.WriteBasicCmd("upaxis", "y"); string originStr = ""; if (avatarType == AvatarType.R6) { originStr = "0 -30 0"; } else { originStr = "0 " + (-23.5 * avatar.Scales.Height).ToString(Rbx2Source.NormalParse) + " 0"; } qc.WriteBasicCmd("origin", originStr, false); qc.WriteBasicCmd("cdmaterials", "models/" + compileDirectory); qc.WriteBasicCmd("surfaceprop", "flesh"); qc.WriteBasicCmd("include", "CollisionJoints.qc"); QCommand reference = new QCommand("sequence", "reference", "ReferencePos.smd"); reference.AddParameter("fps", "1"); reference.AddParameter("loop"); qc.AddCommand(reference); foreach (string animName in animations.Keys) { QCommand sequence = new QCommand("sequence", animName.ToLower(), "Animations/" + animName + ".smd"); sequence.AddParameter("fps", AnimationAssembler.FrameRate.ToString()); sequence.AddParameter("loop"); if (avatarType == AvatarType.R6) // TODO: Find a work around so I can get rid of this. { sequence.AddParameter("delta"); } qc.AddCommand(sequence); } string qcFile = qc.BuildFile(); string qcPath = Path.Combine(modelDir, "Compile.qc"); FileUtility.WriteFile(qcPath, qcFile); Rbx2Source.MarkTaskCompleted("BuildCompilerScript"); AssemblerData data = new AssemblerData(); data.ModelData = writer; data.TextureData = texAssembly; data.CompilerScript = qcPath; data.RootDirectory = userBin; data.MaterialDirectory = materialsDir; data.TextureDirectory = texturesDir; data.CompileDirectory = compileDirectory; data.ModelName = modelNameStr; return(data); }
public AssemblerData Assemble(object metadata) { long assetId = (long)metadata; Asset asset = Asset.Get(assetId); string assetName = asset.ProductInfo.WindowsSafeName; string appData = Environment.GetEnvironmentVariable("AppData"); string rbx2Source = Path.Combine(appData, "Rbx2Source"); string items = Path.Combine(rbx2Source, "Items"); string rootDir = Path.Combine(items, assetName); string modelDir = Path.Combine(rootDir, "Model"); string texturesDir = Path.Combine(rootDir, "Textures"); string materialsDir = Path.Combine(rootDir, "Materials"); FileUtility.InitiateEmptyDirectories(modelDir, texturesDir, materialsDir); Rbx2Source.ScheduleTasks("BuildModel", "BuildTextures", "BuildMaterials", "BuildCompilerScript"); // Build Model StudioMdlWriter writer = AssembleModel(asset); string studioMdl = writer.BuildFile(); string modelPath = Path.Combine(modelDir, "Asset.smd"); FileUtility.WriteFile(modelPath, studioMdl); // Build Reference Sequence Triangle[] triangles = writer.Triangles.ToArray(); writer.Triangles.Clear(); string reference = writer.BuildFile(); string refPath = Path.Combine(modelDir, "Reference.smd"); FileUtility.WriteFile(refPath, reference); Rbx2Source.MarkTaskCompleted("BuildModel"); // Build Textures Rbx2Source.PrintHeader("BUILDING TEXTURES"); Dictionary <string, Material> materials = writer.Materials; string compileDirectory = "roblox_assets/" + assetName; TextureAssembly texAssembly = AssembleTextures(materials); texAssembly.MaterialDirectory = compileDirectory; Dictionary <string, Image> images = texAssembly.Images; foreach (string imageName in images.Keys) { Rbx2Source.Print("Writing Image {0}", imageName); Image image = images[imageName]; string imagePath = Path.Combine(texturesDir, imageName + ".png"); try { image.Save(imagePath, ImageFormat.Png); } catch { Rbx2Source.Print("IMAGE {0}.png FAILED TO SAVE!", imageName); } FileUtility.LockFile(imagePath); } Rbx2Source.MarkTaskCompleted("BuildTextures"); // Build Materials Rbx2Source.PrintHeader("BUILDING MATERIAL FILES"); string mtlDir = "models/" + compileDirectory; Dictionary <string, string> matLinks = texAssembly.MatLinks; Dictionary <string, Material> matLookup = new Dictionary <string, Material>(); foreach (string mtlName in matLinks.Keys) { Material mtl = materials[mtlName]; string vtfTarget = matLinks[mtlName]; string vmtPath = Path.Combine(materialsDir, mtlName + ".vmt"); if (!File.Exists(vmtPath)) { Rbx2Source.Print("Building VMT {0}.vmt", mtlName); ValveMaterial vmt = new ValveMaterial(mtl); vmt.SetField("basetexture", mtlDir + "/" + vtfTarget); string vmtContent = vmt.ToString(); FileUtility.WriteFile(vmtPath, vmtContent); matLookup[mtlName] = mtl; } } Rbx2Source.MarkTaskCompleted("BuildMaterials"); // Build Compiler Script Rbx2Source.PrintHeader("WRITING COMPILER SCRIPT"); QCWriter qc = new QCWriter(); QCommand model = new QCommand("body", assetName, "Asset.smd"); qc.AddCommand(model); string modelNameStr = compileDirectory + ".mdl"; qc.WriteBasicCmd("modelname", modelNameStr); qc.WriteBasicCmd("upaxis", "y"); qc.WriteBasicCmd("cdmaterials", mtlDir); QCommand collision = new QCommand("collisionjoints", "Asset.smd"); collision.AddParameter("$mass", 115.0); collision.AddParameter("$inertia", 2.00); collision.AddParameter("$damping", 0.01); collision.AddParameter("$rotdamping", 0.40); qc.AddCommand(collision); QCommand sequence = new QCommand("sequence", "reference", "Reference.smd"); sequence.AddParameter("fps", 1); sequence.AddParameter("loop"); qc.AddCommand(sequence); string qcFile = qc.BuildFile(); string qcPath = Path.Combine(modelDir, "Compile.qc"); FileUtility.WriteFile(qcPath, qcFile); Rbx2Source.MarkTaskCompleted("BuildCompilerScript"); AssemblerData data = new AssemblerData(); data.ModelData = writer; data.TextureData = texAssembly; data.CompilerScript = qcPath; data.RootDirectory = rootDir; data.MaterialDirectory = materialsDir; data.TextureDirectory = texturesDir; data.CompileDirectory = compileDirectory; data.ModelName = modelNameStr; return(data); }
public TextureAssembly AssembleTextures(TextureCompositor compositor, Dictionary <string, Material> materials) { TextureAssembly assembly = new TextureAssembly(); assembly.Images = new Dictionary <string, Image>(); assembly.MatLinks = new Dictionary <string, string>(); Bitmap core = compositor.BakeTextureMap(); Rbx2Source.SetDebugImage(core); Bitmap head = TextureCompositor.CropBitmap(core, CANVAS_HEAD); assembly.LinkDirectly("Head", head); Bitmap body = TextureCompositor.CropBitmap(core, CANVAS_BODY); Folder characterAssets = compositor.CharacterAssets; Rbx2Source.Print("Processing Package Textures..."); Rbx2Source.IncrementStack(); // Collect CharacterMeshes var packagedLimbs = new Dictionary <Limb, CharacterMesh>(); foreach (CharacterMesh mesh in characterAssets.GetChildrenOfClass <CharacterMesh>()) { packagedLimbs.Add(mesh.BodyPart, mesh); } // Compose the textures that will be used var limbOverlays = new Dictionary <Limb, long>(); var limbBitmaps = new Dictionary <long, Bitmap>() { { 0, body } }; foreach (Limb limb in LimbMatcher.Keys) { // Head is already textured, ignore it. if (limb == Limb.Head) { continue; } // Is there a CharacterMesh for this limb? if (packagedLimbs.ContainsKey(limb)) { // Check the CharacterMesh textures. CharacterMesh mesh = packagedLimbs[limb]; long baseId = mesh.BaseTextureId; long overlayId = mesh.OverlayTextureId; if (overlayId > 0) { // Use the overlay texture for this limb. limbOverlays.Add(limb, overlayId); // Compose this overlay texture with the body texture if it doesn't exist yet. if (!limbBitmaps.ContainsKey(overlayId)) { Asset overlayAsset = Asset.Get(overlayId); TextureCompositor overlayCompositor = new TextureCompositor(AvatarType.R6, CANVAS_RECT); overlayCompositor.AppendTexture(body, CANVAS_BODY); overlayCompositor.AppendTexture(overlayAsset, CANVAS_BODY, 1); overlayCompositor.SetContext("Overlay Texture " + overlayId); Bitmap overlayTex = overlayCompositor.BakeTextureMap(CANVAS_BODY); limbBitmaps.Add(overlayId, overlayTex); } continue; } else if (baseId > 0) { // Use the base texture for this limb. limbOverlays.Add(limb, baseId); // Compose the base texture if it doesn't exist yet. if (!limbBitmaps.ContainsKey(baseId)) { Asset baseAsset = Asset.Get(baseId); TextureCompositor baseCompositor = new TextureCompositor(AvatarType.R6, CANVAS_RECT); baseCompositor.AppendTexture(baseAsset, CANVAS_BODY); baseCompositor.SetContext("Base Texture " + baseId); Bitmap baseTex = baseCompositor.BakeTextureMap(CANVAS_BODY); limbBitmaps.Add(baseId, baseTex); } continue; } } // If no continue statement is reached, fallback to using the body texture. // This occurs if the limb has no package, or the package limb has no textures. limbOverlays.Add(limb, 0); } // Add the images into the texture assembly. foreach (long id in limbBitmaps.Keys) { string materialName = GetBodyMaterialName(id); Bitmap bitmap = limbBitmaps[id]; assembly.Images.Add(materialName, bitmap); } // Link the limbs to their textures. foreach (Limb limb in limbOverlays.Keys) { long id = limbOverlays[limb]; string materialName = GetBodyMaterialName(id); string limbName = Rbx2Source.GetEnumName(limb); assembly.MatLinks.Add(limbName, materialName); } // Handle the rest of the materials foreach (string materialName in materials.Keys) { if (!assembly.MatLinks.ContainsKey(materialName)) { Material material = materials[materialName]; Asset texture = material.TextureAsset; TextureCompositor matComp = new TextureCompositor(AvatarType.R6, CANVAS_ITEM); matComp.AppendTexture(texture, CANVAS_ITEM); matComp.SetContext("Accessory Texture " + materialName); Bitmap bitmap = matComp.BakeTextureMap(); assembly.LinkDirectly(materialName, bitmap); } } Rbx2Source.DecrementStack(); return(assembly); }
public static TextureAssembly AssembleTextures(Dictionary <string, Material> materials) { TextureAssembly assembly = new TextureAssembly(); Dictionary <string, Image> images = new Dictionary <string, Image>(); assembly.Images = images; Dictionary <string, string> matLinks = new Dictionary <string, string>(); assembly.MatLinks = matLinks; foreach (string mtlName in materials.Keys) { Material material = materials[mtlName]; Asset textureAsset = material.TextureAsset; if (textureAsset == null) { string bcName = "Institutional white"; int brickColor = material.LinkedTo.BrickColor; if (BrickColors.NumericalSearch.ContainsKey(brickColor)) { BrickColor color = BrickColors.NumericalSearch[brickColor]; float r = color.R / 255.0f; float g = color.G / 255.0f; float b = color.B / 255.0f; material.VertexColor = new Vector3(r, g, b); bcName = color.Name; } else { material.VertexColor = new Vector3(1, 1, 1); } if (!images.ContainsKey("BrickColor")) { byte[] rawImg = ResourceUtility.GetResource("Images/BlankWhite.png"); using (MemoryStream imgStream = new MemoryStream(rawImg)) { Image image = Image.FromStream(imgStream); images.Add("BrickColor", image); } } string matKey = serializeBrickColorMtl(material); material.UseReflectance = true; matLinks.Add(mtlName, matKey); } else { byte[] rawImg = textureAsset.GetContent(); using (MemoryStream imgStream = new MemoryStream(rawImg)) { Image image = Image.FromStream(imgStream); images.Add(mtlName, image); matLinks.Add(mtlName, mtlName); } } } return(assembly); }