public static Folder AppendCollisionAssets(UserAvatar avatar, string avatarType)
        {
            Folder collisionAssets;

            if (DEBUG_RAPID_ASSEMBLY)
            {
                collisionAssets = new Folder();
            }
            else
            {
                collisionAssets = AppendCharacterAssets(avatar, avatarType, "COLLISION");
            }

            // Replace the head mesh with a low-poly head
            DataModelMesh oldHeadMesh = collisionAssets.FindFirstChildOfClass <DataModelMesh>();

            if (oldHeadMesh != null)
            {
                oldHeadMesh.Destroy();
            }

            _ = new SpecialMesh()
            {
                MeshId   = "rbxassetid://582002794",
                MeshType = MeshType.FileMesh,
                Scale    = new Vector3(1, 1, 1),
                Offset   = new Vector3(),
                Parent   = collisionAssets
            };

            return(collisionAssets);
        }
Пример #2
0
        public static Asset ResolveHeadMeshAsset(DataModelMesh mesh)
        {
            Contract.Requires(mesh != null);
            string result = "Default";

            if (mesh is BevelMesh)
            {
                BevelMesh bevelMesh = mesh as BevelMesh;
                BevelType bevelType = BevelType.Unknown;

                if (mesh is BlockMesh)
                {
                    bevelType = BevelType.Block;
                }
                else if (mesh is CylinderMesh)
                {
                    bevelType = BevelType.Cylinder;
                }

                Head match = Lookup.Keys
                             .Where((head) => bevelType == head.BevelType)
                             .Where((head) => head.FieldsMatchWith(bevelMesh))
                             .First();

                if (match != null)
                {
                    result = Lookup[match];
                }

                mesh.Scale = new Vector3(1, 1, 1);
            }

            else if (mesh is SpecialMesh)
            {
                SpecialMesh specialMesh = mesh as SpecialMesh;

                if (specialMesh.MeshType == MeshType.Sphere)
                {
                    result            = "Perfection";
                    specialMesh.Scale = new Vector3(1, 1, 1);
                }
            }

            if (result == "Default")
            {
                mesh.Scale /= new Vector3(1.25f, 1.25f, 1.25f);
            }

            return(Asset.FromResource("Meshes/Heads/" + result + ".mesh"));
        }
        public static Asset GetAvatarFace(Folder characterAssets)
        {
            // Check if this avatar is using an Rthro head with a texture overlay.
            Contract.Requires(characterAssets != null);
            Folder assembly = characterAssets.FindFirstChild <Folder>("ASSEMBLY");

            if (assembly != null)
            {
                BasePart head = assembly.FindFirstChild <BasePart>("Head");

                if (head != null)
                {
                    SpecialMesh headMesh = head.FindFirstChildOfClass <SpecialMesh>();

                    if (headMesh != null && headMesh.TextureId != null)
                    {
                        string textureId = headMesh.TextureId;

                        if (textureId.Length > 0)
                        {
                            // One last check to make sure this is *probably* an Rthro head.
                            // The reason this check is necessary is due to the iBot Head, which has a texture and allows a face to be drawn on it.
                            // I suspect Roblox will expand this behavior later, so I need to keep an eye on it.
                            StringValue scaleType = head.FindFirstChild <StringValue>("AvatarPartScaleType");

                            if (scaleType != null && scaleType.Value != "Classic")
                            {
                                return(Asset.GetByAssetId(headMesh.TextureId));
                            }
                        }
                    }
                }
            }

            // Fall back to normal behavior.
            Decal face = characterAssets.FindFirstChild <Decal>("face");
            Asset result;

            if (face != null && face.Texture != "rbxasset://textures/face.png")
            {
                result = Asset.GetByAssetId(face.Texture);
            }
            else
            {
                result = Asset.FromResource("Images/face.png");
            }

            return(result);
        }
Пример #4
0
        public static Mesh BakePart(Part part, Material material = null)
        {
            Mesh result = null;

            Asset meshAsset    = null;
            Asset textureAsset = null;

            Vector3 scale  = null;
            CFrame  offset = null;

            if (material != null)
            {
                material.LinkedTo     = part;
                material.Transparency = part.Transparency;
                material.Reflectance  = part.Reflectance;
            }

            if (part.Transparency < 1)
            {
                if (part.IsA("MeshPart"))
                {
                    MeshPart meshPart = (MeshPart)part;
                    if (meshPart.MeshID == null)
                    {
                        string partName = meshPart.Name;
                        if (StandardLimbs.ContainsKey(partName))
                        {
                            meshAsset = StandardLimbs[partName];
                        }
                    }
                    else
                    {
                        meshAsset = Asset.GetByAssetId(meshPart.MeshID);
                    }

                    if (meshPart.TextureID != null)
                    {
                        textureAsset = Asset.GetByAssetId(meshPart.TextureID);
                    }

                    scale  = meshPart.Size / meshPart.InitialSize;
                    offset = part.CFrame;
                }
                else
                {
                    offset = part.CFrame;

                    SpecialMesh specialMesh = part.FindFirstChildOfClass <SpecialMesh>();
                    if (specialMesh != null && specialMesh.MeshType == MeshType.FileMesh)
                    {
                        meshAsset = Asset.GetByAssetId(specialMesh.MeshId);
                        scale     = specialMesh.Scale;
                        offset   *= new CFrame(specialMesh.Offset);
                        if (material != null)
                        {
                            textureAsset         = Asset.GetByAssetId(specialMesh.TextureId);
                            material.VertexColor = specialMesh.VertexColor;
                        }
                    }
                    else
                    {
                        DataModelMesh legacy = part.FindFirstChildOfClass <DataModelMesh>();
                        if (legacy != null)
                        {
                            meshAsset = Head.ResolveHeadMeshAsset(legacy);
                            scale     = legacy.Scale;
                            offset   *= new CFrame(legacy.Offset);
                        }
                    }
                }
            }
            else
            {
                // Just give it a blank mesh to eat for now.
                result = new Mesh();
            }

            if (meshAsset != null)
            {
                if (material != null)
                {
                    material.TextureAsset = textureAsset;
                }

                result = FromAsset(meshAsset);
                result.BakeGeometry(scale, offset);
            }

            return(result);
        }
Пример #5
0
        public static Mesh BakePart(BasePart part, ValveMaterial material = null)
        {
            Contract.Requires(part != null);
            Mesh result = null;

            Asset meshAsset    = null;
            Asset textureAsset = null;

            Vector3 scale  = null;
            CFrame  offset = null;

            if (material != null)
            {
                material.LinkedTo     = part;
                material.Reflectance  = part.Reflectance;
                material.Transparency = part.Transparency;
            }

            if (part.Transparency < 1)
            {
                if (part is MeshPart meshPart)
                {
                    string meshId = meshPart.MeshId;

                    if (meshId != null && meshId.Length > 0)
                    {
                        meshAsset = Asset.GetByAssetId(meshId);
                    }
                    else
                    {
                        string partName = meshPart.Name;
                        StandardLimbs.TryGetValue(partName, out meshAsset);
                    }

                    if (meshPart.TextureID != null)
                    {
                        textureAsset = Asset.GetByAssetId(meshPart.TextureID);
                    }

                    scale  = meshPart.Size / meshPart.InitialSize;
                    offset = part.CFrame;
                }
                else
                {
                    SpecialMesh specialMesh = part.FindFirstChildOfClass <SpecialMesh>();
                    offset = part.CFrame;

                    if (specialMesh != null && specialMesh.MeshType == MeshType.FileMesh)
                    {
                        meshAsset = Asset.GetByAssetId(specialMesh.MeshId);
                        offset   *= new CFrame(specialMesh.Offset);
                        scale     = specialMesh.Scale;

                        if (material != null)
                        {
                            textureAsset         = Asset.GetByAssetId(specialMesh.TextureId);
                            material.VertexColor = specialMesh.VertexColor;
                        }
                    }
                    else
                    {
                        DataModelMesh legacy = part.FindFirstChildOfClass <DataModelMesh>();

                        if (legacy != null)
                        {
                            meshAsset = Head.ResolveHeadMeshAsset(legacy);
                            offset   *= new CFrame(legacy.Offset);
                            scale     = legacy.Scale;
                        }
                    }
                }
            }
            else
            {
                // Just give it a blank mesh to eat for now.
                result = new Mesh();
            }

            if (meshAsset != null)
            {
                if (material != null)
                {
                    material.TextureAsset = textureAsset;
                }

                result = FromAsset(meshAsset);
                result.BakeGeometry(scale, offset);
            }

            return(result);
        }
Пример #6
0
        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);
        }