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);
        }
Example #4
0
        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);
        }