public TextureCompositor ComposeTextureMap(Folder characterAssets, WebBodyColors bodyColors) { Contract.Requires(characterAssets != null && bodyColors != null); var compositor = new TextureCompositor(AvatarType.R6, RECT_FULL) { CharacterAssets = characterAssets }; // Append BodyColors compositor.AppendColor(bodyColors.TorsoColorId, COMPOSIT_TORSO, RECT_FULL); compositor.AppendColor(bodyColors.LeftArmColorId, COMPOSIT_LEFT_ARM, RECT_FULL); compositor.AppendColor(bodyColors.LeftLegColorId, COMPOSIT_LEFT_LEG, RECT_FULL); compositor.AppendColor(bodyColors.RightArmColorId, COMPOSIT_RIGHT_ARM, RECT_FULL); compositor.AppendColor(bodyColors.RightLegColorId, COMPOSIT_RIGHT_LEG, RECT_FULL); // Append Head & Face Asset faceAsset = GetAvatarFace(characterAssets); compositor.AppendTexture(faceAsset, RECT_HEAD, 1); compositor.AppendColor(bodyColors.HeadColorId, RECT_HEAD); // Append Shirt Shirt shirt = characterAssets.FindFirstChildOfClass <Shirt>(); if (shirt != null) { Asset shirtAsset = Asset.GetByAssetId(shirt.ShirtTemplate); compositor.AppendTexture(shirtAsset, COMPOSIT_SHIRT, RECT_FULL, 2); } // Append Pants Pants pants = characterAssets.FindFirstChildOfClass <Pants>(); if (pants != null) { Asset pantsAsset = Asset.GetByAssetId(pants.PantsTemplate); compositor.AppendTexture(pantsAsset, COMPOSIT_PANTS, RECT_FULL, 1); } // Append T-Shirt ShirtGraphic tshirt = characterAssets.FindFirstChildOfClass <ShirtGraphic>(); if (tshirt != null) { Asset tshirtAsset = Asset.GetByAssetId(tshirt.Graphic); compositor.AppendTexture(tshirtAsset, RECT_TSHIRT, 3, RotateFlipType.Rotate90FlipNone); } return(compositor); }
public static TextureCompositor FromBaseObject(BaseObject baseObj) { if (baseObj == null || baseObj.NativeObject == IntPtr.Zero) { return(null); } TextureCompositor obj = baseObj as TextureCompositor; if (object.Equals(obj, null)) { obj = new TextureCompositor(CreatedWhenConstruct.CWC_NotToCreate); obj.BindNativeObject(baseObj.NativeObject, "CTextureCompositor"); obj.IncreaseCast(); } return(obj); }
public TextureBindings BindTextures(TextureCompositor compositor, Dictionary <string, ValveMaterial> materials) { Contract.Requires(compositor != null && materials != null); TextureBindings textureBinds = new TextureBindings(); Bitmap uvMap = compositor.BakeTextureMap(); Rbx2Source.SetDebugImage(uvMap); foreach (string matName in materials.Keys) { Rbx2Source.Print("Building Material {0}", matName); ValveMaterial material = materials[matName]; Image image = null; if (material.UseAvatarMap) { if (Enum.TryParse(matName, out BodyPart limb)) { Rectangle cropRegion = UVCrops[limb]; image = TextureCompositor.CropBitmap(uvMap, cropRegion); } } else { Asset texture = material.TextureAsset; if (texture != null) { byte[] textureData = texture.GetContent(); MemoryStream textureStream = new MemoryStream(textureData); image = Image.FromStream(textureStream); textureStream.Dispose(); } } textureBinds.BindTexture(matName, image); } return(textureBinds); }
public AssemblerData Assemble(UserAvatar avatar) { Contract.Requires(avatar != null); UserInfo userInfo = avatar.UserInfo; string userName = FileUtility.MakeNameWindowsSafe(userInfo.Username); string appData = Environment.GetEnvironmentVariable("LocalAppData"); string rbx2Src = Path.Combine(appData, "Rbx2Source"); string avatars = Path.Combine(rbx2Src, "Avatars"); string userBin = Path.Combine(avatars, userName); string modelDir = Path.Combine(userBin, "Model"); string anim8Dir = Path.Combine(modelDir, "Animations"); string texturesDir = Path.Combine(userBin, "Textures"); string materialsDir = Path.Combine(userBin, "Materials"); FileUtility.InitiateEmptyDirectories(modelDir, anim8Dir, texturesDir, materialsDir); AvatarType avatarType = avatar.PlayerAvatarType; ICharacterAssembler assembler; if (avatarType == AvatarType.R15) { assembler = new R15CharacterAssembler(); } else { assembler = new R6CharacterAssembler(); } string compileDir = "roblox_avatars/" + userName; string avatarTypeName = Rbx2Source.GetEnumName(avatarType); Folder characterAssets = AppendCharacterAssets(avatar, avatarTypeName); Rbx2Source.ScheduleTasks ( "BuildCharacter", "BuildCollisionModel", "BuildAnimations", "BuildTextures", "BuildMaterials", "BuildCompilerScript" ); Rbx2Source.PrintHeader("BUILDING CHARACTER MODEL"); #region Build Character Model /////////////////////////////////////////////////////////////////////////////////////////////////////// StudioMdlWriter writer = assembler.AssembleModel(characterAssets, avatar.Scales, DEBUG_RAPID_ASSEMBLY); string studioMdl = writer.BuildFile(); string modelPath = Path.Combine(modelDir, "CharacterModel.smd"); FileUtility.WriteFile(modelPath, studioMdl); string staticPose = writer.BuildFile(false); string refPath = Path.Combine(modelDir, "ReferencePos.smd"); FileUtility.WriteFile(refPath, staticPose); Rbx2Source.MarkTaskCompleted("BuildCharacter"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion Rbx2Source.PrintHeader("BUILDING COLLISION MODEL"); #region Build Character Collisions /////////////////////////////////////////////////////////////////////////////////////////////////////// Folder collisionAssets = AppendCollisionAssets(avatar, avatarTypeName); StudioMdlWriter collisionWriter = assembler.AssembleModel(collisionAssets, avatar.Scales, true); 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"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion Rbx2Source.PrintHeader("BUILDING CHARACTER ANIMATIONS"); #region Build Character Animations /////////////////////////////////////////////////////////////////////////////////////////////////////// var animIds = assembler.CollectAnimationIds(avatar); var compileAnims = new Dictionary <string, Asset>(); if (animIds.Count > 0) { Rbx2Source.Print("Collecting Animations..."); Rbx2Source.IncrementStack(); Action <string, Asset> collectAnimation = (animName, animAsset) => { if (!compileAnims.ContainsKey(animName)) { Rbx2Source.Print("Collected animation {0} with id {1}", animName, animAsset.Id); compileAnims.Add(animName, animAsset); } }; foreach (string animName in animIds.Keys) { var animId = animIds[animName]; var animAsset = animId.GetAsset(); var import = animAsset.OpenAsModel(); if (animId.AnimationType == AnimationType.R15AnimFolder) { Folder r15Anim = import.FindFirstChild <Folder>("R15Anim"); if (r15Anim != null) { foreach (Instance animDef in r15Anim.GetChildren()) { if (animDef.Name == "idle") { var anims = animDef.GetChildrenOfType <Animation>(); if (anims.Length == 2) { var getLookAnim = anims.OrderBy((anim) => { var weight = anim.FindFirstChild <NumberValue>("Weight"); if (weight != null) { return(weight.Value); } return(0.0); }); var lookAnim = getLookAnim.First(); lookAnim.Destroy(); Asset lookAsset = Asset.GetByAssetId(lookAnim.AnimationId); collectAnimation("Idle2", lookAsset); } } Animation compileAnim = animDef.FindFirstChildOfClass <Animation>(); if (compileAnim != null) { Asset compileAsset = Asset.GetByAssetId(compileAnim.AnimationId); string compileName = animName; if (animDef.Name == "pose") { compileName = "Pose"; } collectAnimation(compileName, compileAsset); } } } } else { collectAnimation(animName, animAsset); } } Rbx2Source.DecrementStack(); } else { Rbx2Source.Print("No animations found :("); } if (compileAnims.Count > 0) { Rbx2Source.Print("Assembling Animations..."); Rbx2Source.IncrementStack(); foreach (string animName in compileAnims.Keys) { Rbx2Source.Print("Building Animation {0}...", animName); Asset animAsset = compileAnims[animName]; var import = animAsset.OpenAsModel(); var sequence = import.FindFirstChildOfClass <KeyframeSequence>(); sequence.Name = animName; var avatarTypeRef = new StringValue() { Value = $"{avatarType}", Name = "AvatarType", Parent = sequence }; string animation = AnimationBuilder.Assemble(sequence, writer.Skeleton[0].Bones); string animPath = Path.Combine(anim8Dir, animName + ".smd"); FileUtility.WriteFile(animPath, animation); } Rbx2Source.DecrementStack(); } Rbx2Source.MarkTaskCompleted("BuildAnimations"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion Rbx2Source.PrintHeader("BUILDING CHARACTER TEXTURES"); #region Build Character Textures /////////////////////////////////////////////////////////////////////////////////////////////////////// var materials = writer.Materials; TextureBindings textures; if (DEBUG_RAPID_ASSEMBLY) { textures = new TextureBindings(); materials.Clear(); } else { TextureCompositor texCompositor = assembler.ComposeTextureMap(characterAssets, avatar.BodyColors); textures = assembler.BindTextures(texCompositor, materials); } var images = textures.Images; textures.MaterialDirectory = compileDir; 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); } CompositData.FreeAllocatedTextures(); Rbx2Source.MarkTaskCompleted("BuildTextures"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion Rbx2Source.PrintHeader("WRITING MATERIAL FILES"); #region Write Material Files /////////////////////////////////////////////////////////////////////////////////////////////////////// var matLinks = textures.MatLinks; foreach (string mtlName in matLinks.Keys) { Rbx2Source.Print("Building VMT {0}.vmt", mtlName); string targetVtf = matLinks[mtlName]; string vmtPath = Path.Combine(materialsDir, mtlName + ".vmt"); ValveMaterial mtl = materials[mtlName]; mtl.SetVmtField("basetexture", "models/" + compileDir + "/" + targetVtf); mtl.WriteVmtFile(vmtPath); } Rbx2Source.MarkTaskCompleted("BuildMaterials"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion Rbx2Source.PrintHeader("WRITING COMPILER SCRIPT"); #region Write Compiler Script /////////////////////////////////////////////////////////////////////////////////////////////////////// string modelName = compileDir + ".mdl"; QuakeCWriter qc = new QuakeCWriter(); qc.Add("body", userName, "CharacterModel.smd"); qc.Add("modelname", modelName); qc.Add("upaxis", "y"); // Compute the floor level of the avatar. Folder assembly = characterAssets.FindFirstChild <Folder>("ASSEMBLY"); if (assembly != null) { float floor = ComputeFloorLevel(assembly); string origin = "0 " + floor.ToInvariantString() + " 0"; qc.Add("origin", origin); } qc.Add("cdmaterials", "models/" + compileDir); qc.Add("surfaceprop", "flesh"); qc.Add("include", "CollisionJoints.qc"); QuakeCItem refAnim = qc.Add("sequence", "reference", "ReferencePos.smd"); refAnim.AddSubItem("fps", 1); refAnim.AddSubItem("loop"); foreach (string animName in compileAnims.Keys) { QuakeCItem sequence = qc.Add("sequence", animName.ToLowerInvariant(), "Animations/" + animName + ".smd"); sequence.AddSubItem("fps", AnimationBuilder.FrameRate); if (avatarType == AvatarType.R6) { sequence.AddSubItem("delta"); } sequence.AddSubItem("loop"); } string qcFile = qc.ToString(); string qcPath = Path.Combine(modelDir, "Compile.qc"); FileUtility.WriteFile(qcPath, qcFile); Rbx2Source.MarkTaskCompleted("BuildCompilerScript"); /////////////////////////////////////////////////////////////////////////////////////////////////////// #endregion AssemblerData data = new AssemblerData() { ModelData = writer, ModelName = modelName, TextureData = textures, CompilerScript = qcPath, RootDirectory = userBin, CompileDirectory = compileDir, TextureDirectory = texturesDir, MaterialDirectory = materialsDir, }; return(data); }
public TextureBindings BindTextures(TextureCompositor compositor, Dictionary <string, ValveMaterial> materials) { Contract.Requires(compositor != null && materials != null); TextureBindings textures = new TextureBindings(); Bitmap core = compositor.BakeTextureMap(); Rbx2Source.SetDebugImage(core); Bitmap head = TextureCompositor.CropBitmap(core, RECT_HEAD); textures.BindTexture("Head", head); Bitmap body = TextureCompositor.CropBitmap(core, RECT_BODY); Folder characterAssets = compositor.CharacterAssets; Rbx2Source.Print("Processing Package Textures..."); Rbx2Source.IncrementStack(); // Collect CharacterMeshes var packagedLimbs = characterAssets .GetChildrenOfType <CharacterMesh>() .ToDictionary(mesh => mesh.BodyPart); // Compose the textures that will be used var limbOverlays = new Dictionary <BodyPart, long>(); var limbBitmaps = new Dictionary <long, Bitmap>() { { 0, body } }; foreach (BodyPart limb in LimbMatcher.Keys) { // Head is already textured, ignore it. if (limb == BodyPart.Head) { continue; } // Is there a CharacterMesh for this limb? if (packagedLimbs.ContainsKey(limb)) { // Check the CharacterMesh textures. CharacterMesh mesh = packagedLimbs[limb]; if (mesh.OverlayTextureId > 0) { // Use the overlay texture for this limb. long overlayId = mesh.OverlayTextureId; 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, RECT_FULL); overlayCompositor.SetContext("Overlay Texture " + overlayId); overlayCompositor.AppendTexture(overlayAsset, RECT_BODY, 1); overlayCompositor.AppendTexture(body, RECT_BODY); Bitmap overlayTex = overlayCompositor.BakeTextureMap(RECT_BODY); limbBitmaps.Add(overlayId, overlayTex); } continue; } else if (mesh.BaseTextureId > 0) { // Use the base texture for this limb. long baseId = mesh.BaseTextureId; 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, RECT_FULL); baseCompositor.SetContext("Base Texture " + baseId); baseCompositor.AppendTexture(baseAsset, RECT_BODY); Bitmap baseTex = baseCompositor.BakeTextureMap(RECT_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) { Bitmap bitmap = limbBitmaps[id]; string matName = GetBodyMatName(id); textures.BindTexture(matName, bitmap, false); } // Link the limbs to their textures. foreach (BodyPart limb in limbOverlays.Keys) { long id = limbOverlays[limb]; string matName = GetBodyMatName(id); string limbName = Rbx2Source.GetEnumName(limb); textures.BindTextureAlias(limbName, matName); } // Handle the rest of the materials foreach (string matName in materials.Keys) { if (!textures.MatLinks.ContainsKey(matName)) { ValveMaterial material = materials[matName]; Asset texture = material.TextureAsset; TextureCompositor matComp = new TextureCompositor(AvatarType.R6, RECT_ITEM); matComp.SetContext("Accessory Texture " + matName); matComp.AppendTexture(texture, RECT_ITEM); Bitmap bitmap = matComp.BakeTextureMap(); textures.BindTexture(matName, bitmap); } } Rbx2Source.DecrementStack(); return(textures); }
public BaseObject Create() { TextureCompositor emptyInstance = new TextureCompositor(CreatedWhenConstruct.CWC_NotToCreate); return(emptyInstance); }
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 TextureCompositor ComposeTextureMap(Folder characterAssets, BodyColors bodyColors) { TextureCompositor compositor = new TextureCompositor(AvatarType.R15, 1024, 568); // Append BodyColors compositor.AppendColor(bodyColors.HeadColor, RECT_HEAD); compositor.AppendColor(bodyColors.TorsoColor, RECT_TORSO); compositor.AppendColor(bodyColors.LeftArmColor, RECT_LEFT_ARM); compositor.AppendColor(bodyColors.LeftLegColor, RECT_LEFT_LEG); compositor.AppendColor(bodyColors.RightArmColor, RECT_RIGHT_ARM); compositor.AppendColor(bodyColors.RightLegColor, RECT_RIGHT_LEG); // Append Face Asset face = GetAvatarFace(characterAssets); compositor.AppendTexture(face, RECT_HEAD, 1); // Append Shirt Shirt shirt = characterAssets.FindFirstChildOfClass <Shirt>(); if (shirt != null) { Asset shirtTemplate = Asset.GetByAssetId(shirt.ShirtTemplate); compositor.AppendTexture(shirtTemplate, COMPOSIT_TORSO, RECT_TORSO, 2); compositor.AppendTexture(shirtTemplate, COMPOSIT_LEFT_LIMB, RECT_LEFT_ARM, 1); compositor.AppendTexture(shirtTemplate, COMPOSIT_RIGHT_LIMB, RECT_RIGHT_ARM, 1); } // Append Pants Pants pants = characterAssets.FindFirstChildOfClass <Pants>(); if (pants != null) { Asset pantsTemplate = Asset.GetByAssetId(pants.PantsTemplate); compositor.AppendTexture(pantsTemplate, COMPOSIT_TORSO, RECT_TORSO, 1); compositor.AppendTexture(pantsTemplate, COMPOSIT_LEFT_LIMB, RECT_LEFT_LEG, 1); compositor.AppendTexture(pantsTemplate, COMPOSIT_RIGHT_LIMB, RECT_RIGHT_LEG, 1); } // Append T-Shirt ShirtGraphic tshirt = characterAssets.FindFirstChildOfClass <ShirtGraphic>(); if (tshirt != null) { Asset graphic = Asset.GetByAssetId(tshirt.Graphic); compositor.AppendTexture(graphic, RECT_TSHIRT, 4); } // Append Package Overlays Folder avatarParts = characterAssets.FindFirstChild <Folder>("ASSEMBLY"); List <Limb> overlainLimbs = new List <Limb>(); foreach (MeshPart part in avatarParts.GetChildrenOfClass <MeshPart>()) { Limb limb = GetLimb(part); string textureId = part.TextureID; if (textureId != null && textureId.Length > 0 && !overlainLimbs.Contains(limb)) { Asset overlay = Asset.GetByAssetId(textureId); if (limb == Limb.Torso) { compositor.AppendTexture(overlay, RECT_TORSO, 3); } else if (limb == Limb.LeftArm) { compositor.AppendTexture(overlay, RECT_LEFT_ARM, 3); } else if (limb == Limb.RightArm) { compositor.AppendTexture(overlay, RECT_RIGHT_ARM, 3); } else if (limb == Limb.LeftLeg) { compositor.AppendTexture(overlay, RECT_LEFT_LEG, 3); } else if (limb == Limb.RightLeg) { compositor.AppendTexture(overlay, RECT_RIGHT_LEG, 3); } overlainLimbs.Add(limb); } } return(compositor); }
public TextureCompositor ComposeTextureMap(Folder characterAssets, WebBodyColors bodyColors) { Contract.Requires(characterAssets != null && bodyColors != null); TextureCompositor compositor = new TextureCompositor(AvatarType.R15, 1024, 568); // Append BodyColors compositor.AppendColor(bodyColors.HeadColorId, RECT_HEAD); compositor.AppendColor(bodyColors.TorsoColorId, RECT_TORSO); compositor.AppendColor(bodyColors.LeftArmColorId, RECT_LEFT_ARM); compositor.AppendColor(bodyColors.LeftLegColorId, RECT_LEFT_LEG); compositor.AppendColor(bodyColors.RightArmColorId, RECT_RIGHT_ARM); compositor.AppendColor(bodyColors.RightLegColorId, RECT_RIGHT_LEG); // Append Face Asset face = GetAvatarFace(characterAssets); compositor.AppendTexture(face, RECT_HEAD, 1); // Append Shirt Shirt shirt = characterAssets.FindFirstChildOfClass <Shirt>(); if (shirt != null) { Asset shirtTemplate = Asset.GetByAssetId(shirt.ShirtTemplate); compositor.AppendTexture(shirtTemplate, COMPOSIT_TORSO, RECT_TORSO, 2); compositor.AppendTexture(shirtTemplate, COMPOSIT_LEFT_LIMB, RECT_LEFT_ARM, 1); compositor.AppendTexture(shirtTemplate, COMPOSIT_RIGHT_LIMB, RECT_RIGHT_ARM, 1); } // Append Pants Pants pants = characterAssets.FindFirstChildOfClass <Pants>(); if (pants != null) { Asset pantsTemplate = Asset.GetByAssetId(pants.PantsTemplate); compositor.AppendTexture(pantsTemplate, COMPOSIT_TORSO, RECT_TORSO, 1); compositor.AppendTexture(pantsTemplate, COMPOSIT_LEFT_LIMB, RECT_LEFT_LEG, 1); compositor.AppendTexture(pantsTemplate, COMPOSIT_RIGHT_LIMB, RECT_RIGHT_LEG, 1); } // Append T-Shirt ShirtGraphic tshirt = characterAssets.FindFirstChildOfClass <ShirtGraphic>(); if (tshirt != null) { Asset graphic = Asset.GetByAssetId(tshirt.Graphic); compositor.AppendTexture(graphic, RECT_TSHIRT, 4); } // Append Package Overlays Folder avatarParts = characterAssets.FindFirstChild <Folder>("ASSEMBLY"); List <BodyPart> overlainLimbs = new List <BodyPart>(); foreach (MeshPart part in avatarParts.GetChildrenOfType <MeshPart>()) { BodyPart?maybeLimb = GetLimb(part); if (!maybeLimb.HasValue) { continue; } BodyPart limb = maybeLimb.Value; string textureId = part.TextureID; if (textureId != null && textureId.Length > 0 && !overlainLimbs.Contains(limb)) { Asset overlay = Asset.GetByAssetId(textureId); Rectangle crop = UVCrops[limb]; compositor.AppendTexture(overlay, crop, 3); overlainLimbs.Add(limb); } } return(compositor); }
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); }