public static void AssimpPRMConvert(string initialFilePath, string finalFilePath)
        {
            Assimp.AssimpContext context = new Assimp.AssimpContext();
            context.SetConfig(new Assimp.Configs.FBXPreservePivotsConfig(false));
            Assimp.Scene aiScene = context.ImportFile(initialFilePath, Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.JoinIdenticalVertices | Assimp.PostProcessSteps.FlipUVs);

            PRMModel prm = new PRMModel();

            int totalVerts = 0;

            //Iterate through and combine meshes. PRMs can only have a single mesh
            IterateAiNodesPRM(prm, ref totalVerts, aiScene, aiScene.RootNode, Matrix4x4.Transpose(GetMat4FromAssimpMat4(aiScene.RootNode.Transform)));
            AquaUtil.WritePRMToFile(prm, finalFilePath, 4);
        }
Пример #2
0
        public static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                DisplayUsage();
            }
            else
            {
                foreach (var arg in args)
                {
                    var aqua = new AquaUtil();
                    switch (Path.GetExtension(arg))
                    {
                    case ".aqp":
                    case ".aqo":
                    case ".trp":
                    case ".tro":
                        string backup = Path.ChangeExtension(arg, ".org.aqp");
                        if (File.Exists(backup))
                        {
                            File.Delete(backup);
                        }
                        File.Copy(arg, backup);
                        var aqpName = arg;
                        aqua.ReadModel(aqpName);
                        LegacyObj.LegacyObjIO.ExportObj(Path.ChangeExtension(aqpName, ".obj"), aqua.aquaModels[0].models[0]);
                        break;

                    case ".obj":
                        aqua.ReadModel(Path.ChangeExtension(arg, ".org.aqp"));
                        aqua.aquaModels[0].models[0] = LegacyObj.LegacyObjIO.ImportObj(arg, aqua.aquaModels[0].models[0]);
                        string outName = Path.ChangeExtension(arg, ".aqp");
                        if (aqua.aquaModels[0].models[0].objc.type >= 0xC32)
                        {
                            aqua.WriteNGSNIFLModel(outName, outName);
                        }
                        else
                        {
                            aqua.WriteClassicNIFLModel(outName, outName);
                        }
                        break;

                    default:
                        DisplayUsage();
                        break;
                    }
                }
            }
        }
        //Takes in an Assimp model and generates a full PSO2 model and skeleton from it.
        public static AquaObject AssimpAquaConvertFull(string initialFilePath, float scaleFactor, bool preAssignNodeIds, bool isNGS)
        {
            AquaUtil aquaUtil  = new AquaUtil();
            float    baseScale = 1f / 100f * scaleFactor; //We assume that this will be 100x the true scale because 1 unit to 1 meter isn't the norm

            Assimp.AssimpContext context = new Assimp.AssimpContext();
            context.SetConfig(new Assimp.Configs.FBXPreservePivotsConfig(false));
            Assimp.Scene aiScene = context.ImportFile(initialFilePath, Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.JoinIdenticalVertices | Assimp.PostProcessSteps.FlipUVs);

            AquaObject aqp;
            AquaNode   aqn = new AquaNode();

            if (isNGS)
            {
                aqp = new NGSAquaObject();
            }
            else
            {
                aqp = new ClassicAquaObject();
            }

            //Construct Materials
            Dictionary <string, int> matNameTracker = new Dictionary <string, int>();

            foreach (var aiMat in aiScene.Materials)
            {
                string name;
                if (matNameTracker.ContainsKey(aiMat.Name))
                {
                    name = $"{aiMat.Name} ({matNameTracker[aiMat.Name]})";
                    matNameTracker[aiMat.Name] += 1;
                }
                else
                {
                    name = aiMat.Name;
                    matNameTracker.Add(aiMat.Name, 1);
                }

                AquaObject.GenericMaterial genMat = new AquaObject.GenericMaterial();
                List <string> shaderList          = new List <string>();
                AquaObjectMethods.GetMaterialNameData(ref name, shaderList, out string alphaType, out string playerFlag);
                genMat.matName     = name;
                genMat.shaderNames = shaderList;
                genMat.blendType   = alphaType;
                genMat.specialType = playerFlag;
                genMat.texNames    = new List <string>();
                genMat.texUVSets   = new List <int>();

                //Texture assignments. Since we can't rely on these to export properly, we dummy them or just put diffuse if a playerFlag isn't defined.
                //We'll have the user set these later if needed.
                if (genMat.specialType != null)
                {
                    AquaObjectMethods.GenerateSpecialMaterialParameters(genMat);
                }
                else if (aiMat.TextureDiffuse.FilePath != null)
                {
                    genMat.texNames.Add(Path.GetFileName(aiMat.TextureDiffuse.FilePath));
                }
                else
                {
                    genMat.texNames.Add("tex0_d.dds");
                }
                genMat.texUVSets.Add(0);

                AquaObjectMethods.GenerateMaterial(aqp, genMat, true);
            }

            //Default to this so ids can be assigned by order if needed
            Dictionary <string, int> boneDict = new Dictionary <string, int>();

            if (aiScene.RootNode.Name == null || !aiScene.RootNode.Name.Contains("(") || preAssignNodeIds == true)
            {
                int nodeCounter = 0;
                BuildAiNodeDictionary(aiScene.RootNode, ref nodeCounter, boneDict);
            }

            IterateAiNodesAQP(aqp, aqn, aiScene, aiScene.RootNode, Matrix4x4.Transpose(GetMat4FromAssimpMat4(aiScene.RootNode.Transform)), baseScale);

            //Assimp data is gathered, proceed to processing model data for PSO2
            AquaUtil.ModelSet set = new AquaUtil.ModelSet();
            set.models.Add(aqp);
            aquaUtil.aquaModels.Add(set);
            aquaUtil.ConvertToNGSPSO2Mesh(false, false, false, true, false, false, true);

            //AQPs created this way will require more processing to finish.
            //-Texture lists in particular, MUST be generated as what exists is not valid without serious errors
            return(aqp);
        }
        public static void AssimpAQMConvert(string initialFilePath, bool playerExport, bool useScaleFrames, float scaleFactor)
        {
            float baseScale = 1f / 100f * scaleFactor; //We assume that this will be 100x the true scale because 1 unit to 1 meter isn't the norm

            Assimp.AssimpContext context = new Assimp.AssimpContext();
            context.SetConfig(new Assimp.Configs.FBXPreservePivotsConfig(false));
            Assimp.Scene aiScene = context.ImportFile(initialFilePath, Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.JoinIdenticalVertices | Assimp.PostProcessSteps.FlipUVs);

            string                        inputFilename = Path.GetFileNameWithoutExtension(initialFilePath);
            List <string>                 aqmNames      = new List <string>(); //Leave off extensions in case we want this to be .trm later
            List <AquaMotion>             aqmList       = new List <AquaMotion>();
            Dictionary <int, Assimp.Node> aiNodes       = GetAnimatedNodes(aiScene);
            var nodeKeys = aiNodes.Keys.ToList();

            nodeKeys.Sort();
            int      animatedNodeCount = nodeKeys.Last() + 1;
            AquaUtil aqua = new AquaUtil();

            for (int i = 0; i < aiScene.Animations.Count; i++)
            {
                if (aiScene.Animations[i] == null)
                {
                    continue;
                }
                AquaMotion aqm          = new AquaMotion();
                var        anim         = aiScene.Animations[i];
                int        animEndFrame = 0; //We'll fill this later. Assumes frame 0 to be the start

                if (anim.Name != null && anim.Name != "")
                {
                    //Make sure we're not overwriting anims that somehow have duplicate names
                    if (aqmNames.Contains(anim.Name))
                    {
                        aqmNames.Add($"Anim_{i}_" + anim.Name + ".aqm");
                    }
                    else
                    {
                        aqmNames.Add(anim.Name + ".aqm");
                    }
                }
                else
                {
                    aqmNames.Add($"Anim_{i}_" + inputFilename + ".aqm");
                }

                aqm.moHeader = new AquaMotion.MOHeader();

                //Get anim fps
                if (anim.TicksPerSecond == 0)
                {
                    //Default to 30
                    aqm.moHeader.frameSpeed = 30;
                }
                else
                {
                    aqm.moHeader.frameSpeed = (float)anim.TicksPerSecond;
                }

                aqm.moHeader.unkInt0 = 2;   //Always, always 2 for NIFL
                aqm.moHeader.variant = 0x2; //These are flags for the animation to tell the game what type it is. Since this is a skeletal animation, we always put 2 here.
                //If it's a player one specifically, the game generally adds 0x10 to this.
                aqm.moHeader.nodeCount = animatedNodeCount;
                if (playerExport)
                {
                    aqm.moHeader.variant += 0x10;
                    aqm.moHeader.nodeCount++; //Add an extra for the nodeTreeFlag 'node'
                }
                aqm.moHeader.testString.SetString("test");

                //Set this ahead of time in case these are out of order
                aqm.motionKeys = new List <AquaMotion.KeyData>(new AquaMotion.KeyData[aqm.moHeader.nodeCount]);

                //Nodes
                foreach (var animNode in anim.NodeAnimationChannels)
                {
                    if (animNode == null)
                    {
                        continue;
                    }

                    int id = GetNodeNumber(animNode.NodeName);

                    var node = aqm.motionKeys[id] = new AquaMotion.KeyData();

                    node.mseg.nodeName.SetString(animNode.NodeName);
                    node.mseg.nodeId        = id;
                    node.mseg.nodeType      = 2;
                    node.mseg.nodeDataCount = useScaleFrames ? 3 : 2;

                    if (animNode.HasPositionKeys)
                    {
                        AquaMotion.MKEY posKeys = new AquaMotion.MKEY();
                        posKeys.keyType  = 1;
                        posKeys.dataType = 1;
                        var first = true;
                        foreach (var pos in animNode.PositionKeys)
                        {
                            posKeys.vector4Keys.Add(new Vector4(pos.Value.X * baseScale, pos.Value.Y * baseScale, pos.Value.Z * baseScale, 0));

                            //Account for first frame difference
                            if (first)
                            {
                                posKeys.frameTimings.Add(1);
                                first = false;
                            }
                            else
                            {
                                posKeys.frameTimings.Add((ushort)(pos.Time * 0x10));
                            }
                            posKeys.keyCount++;
                        }
                        posKeys.frameTimings[posKeys.keyCount - 1] += 2; //Account for final frame bitflags

                        animEndFrame = Math.Max(animEndFrame, posKeys.keyCount);
                        node.keyData.Add(posKeys);
                    }

                    if (animNode.HasRotationKeys)
                    {
                        AquaMotion.MKEY rotKeys = new AquaMotion.MKEY();
                        rotKeys.keyType  = 2;
                        rotKeys.dataType = 3;
                        var first = true;
                        foreach (var rot in animNode.RotationKeys)
                        {
                            rotKeys.vector4Keys.Add(new Vector4(rot.Value.X, rot.Value.Y, rot.Value.Z, rot.Value.W));

                            //Account for first frame difference
                            if (first)
                            {
                                rotKeys.frameTimings.Add(1);
                                first = false;
                            }
                            else
                            {
                                rotKeys.frameTimings.Add((ushort)(rot.Time * 0x10));
                            }
                            rotKeys.keyCount++;
                        }
                        rotKeys.frameTimings[rotKeys.keyCount - 1] += 2; //Account for final frame bitflags

                        animEndFrame = Math.Max(animEndFrame, rotKeys.keyCount);
                        node.keyData.Add(rotKeys);
                    }

                    if (animNode.HasScalingKeys)
                    {
                        AquaMotion.MKEY sclKeys = new AquaMotion.MKEY();
                        sclKeys.keyType  = 2;
                        sclKeys.dataType = 3;
                        var first = true;
                        foreach (var scl in animNode.ScalingKeys)
                        {
                            sclKeys.vector4Keys.Add(new Vector4(scl.Value.X, scl.Value.Y, scl.Value.Z, 0));

                            //Account for first frame difference
                            if (first)
                            {
                                sclKeys.frameTimings.Add(1);
                                first = false;
                            }
                            else
                            {
                                sclKeys.frameTimings.Add((ushort)(scl.Time * 0x10));
                            }
                            sclKeys.keyCount++;
                        }
                        sclKeys.frameTimings[sclKeys.keyCount - 1] += 2; //Account for final frame bitflags

                        animEndFrame = Math.Max(animEndFrame, sclKeys.keyCount);
                        node.keyData.Add(sclKeys);
                    }
                }

                //NodeTreeFlag
                if (playerExport)
                {
                    var node = aqm.motionKeys[aqm.motionKeys.Count - 1] = new AquaMotion.KeyData();
                    node.mseg.nodeName.SetString("__NodeTreeFlag__");
                    node.mseg.nodeId        = aqm.motionKeys.Count - 1;
                    node.mseg.nodeType      = 0x10;
                    node.mseg.nodeDataCount = useScaleFrames ? 3 : 2;

                    //Position
                    AquaMotion.MKEY posKeys = new AquaMotion.MKEY();
                    posKeys.keyType  = 0x10;
                    posKeys.dataType = 5;
                    posKeys.keyCount = animEndFrame + 1;
                    for (int frame = 0; frame < posKeys.keyCount; frame++)
                    {
                        if (frame == 0)
                        {
                            posKeys.frameTimings.Add(0x9);
                        }
                        else if (frame == posKeys.keyCount - 1)
                        {
                            posKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA));
                        }
                        else
                        {
                            posKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8));
                        }
                        posKeys.intKeys.Add(0x31);
                    }
                    node.keyData.Add(posKeys);

                    //Rotation
                    AquaMotion.MKEY rotKeys = new AquaMotion.MKEY();
                    rotKeys.keyType  = 0x11;
                    rotKeys.dataType = 5;
                    rotKeys.keyCount = animEndFrame + 1;
                    for (int frame = 0; frame < rotKeys.keyCount; frame++)
                    {
                        if (frame == 0)
                        {
                            rotKeys.frameTimings.Add(0x9);
                        }
                        else if (frame == rotKeys.keyCount - 1)
                        {
                            rotKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA));
                        }
                        else
                        {
                            rotKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8));
                        }
                        rotKeys.intKeys.Add(0x31);
                    }
                    node.keyData.Add(rotKeys);

                    //Scale
                    if (useScaleFrames)
                    {
                        AquaMotion.MKEY sclKeys = new AquaMotion.MKEY();
                        sclKeys.keyType  = 0x12;
                        sclKeys.dataType = 5;
                        sclKeys.keyCount = animEndFrame + 1;
                        for (int frame = 0; frame < sclKeys.keyCount; frame++)
                        {
                            if (frame == 0)
                            {
                                sclKeys.frameTimings.Add(0x9);
                            }
                            else if (frame == sclKeys.keyCount - 1)
                            {
                                sclKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA));
                            }
                            else
                            {
                                sclKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8));
                            }
                            sclKeys.intKeys.Add(0x31);
                        }
                        node.keyData.Add(sclKeys);
                    }
                }

                //Sanity check
                foreach (var aiPair in aiNodes)
                {
                    var node   = aqm.motionKeys[aiPair.Key];
                    var aiNode = aiPair.Value;
                    if (node == null)
                    {
                        node = aqm.motionKeys[aiPair.Key] = new AquaMotion.KeyData();

                        node.mseg.nodeName.SetString(aiNode.Name);
                        node.mseg.nodeId        = aiPair.Key;
                        node.mseg.nodeType      = 2;
                        node.mseg.nodeDataCount = useScaleFrames ? 3 : 2;

                        //Position
                        AddOnePosFrame(node, aiNode, baseScale);

                        //Rotation
                        AddOneRotFrame(node, aiNode);

                        //Scale
                        AddOneScaleFrame(useScaleFrames, node);
                    }
                    else
                    {
                        if (node.keyData[0].vector4Keys.Count < 1)
                        {
                            AddOnePosFrame(node, aiNode, baseScale);
                        }

                        if (node.keyData[1].vector4Keys.Count < 1)
                        {
                            AddOneRotFrame(node, aiNode);
                        }

                        if (useScaleFrames && node.keyData[2].vector4Keys.Count < 1)
                        {
                            AddOneScaleFrame(useScaleFrames, node, aiNode);
                        }
                    }
                }

                aqmList.Add(aqm);
            }

            for (int i = 0; i < aqmList.Count; i++)
            {
                var aqm = aqmList[i];
                AquaUtil.AnimSet set = new AquaUtil.AnimSet();
                set.anims.Add(aqm);
                aqua.aquaMotions.Add(set);
                aqua.WriteNIFLMotion(initialFilePath + "_" + aqmNames[i]);

                aqua.aquaMotions.Clear();
            }
        }