Exemplo n.º 1
0
        public Mesh LoadFromStream(EndianBinaryReader reader)
        {
            MeshVertexAttributeHolder vertexData = null;
            SceneNode rootNode = new SceneNode();
            List<Texture2D> textureList = new List<Texture2D>();
            List<WEditor.Common.Nintendo.J3D.Material> materialList = null;
            List<SkeletonBone> joints = new List<SkeletonBone>();
            DrawInfo drawInfo = null;
            Envelopes envelopes = null;

            Mesh j3dMesh = new Mesh();

            // Read the Header
            int magic = reader.ReadInt32(); // J3D1, J3D2, etc
            if (magic != 1244873778)
            {
                WLog.Warning(LogCategory.ModelLoading, null, "Attempted to load model with invalid magic, ignoring!");
                return null;
            }

            int j3dType = reader.ReadInt32(); // BMD3 (models) BDL4 (models), jpa1 (particles), bck1 (animations), etc.
            int totalFileSize = reader.ReadInt32();
            int chunkCount = reader.ReadInt32();

            // Skip over an unused tag (consistent in all files) and some padding.
            reader.ReadBytes(16);

            for (int i = 0; i < chunkCount; i++)
            {
                long chunkStart = reader.BaseStream.Position;

                string tagName = reader.ReadString(4);
                int chunkSize = reader.ReadInt32();

                switch (tagName)
                {
                    // INFO - Vertex Count, Scene Hierarchy
                    case "INF1":
                        rootNode = LoadINF1FromFile(rootNode, reader, chunkStart);
                        break;
                    // VERTEX - Stores vertex arrays for pos/normal/color0/tex0 etc. Contains VertexAttributes which describe
                    // how this data is stored/laid out.
                    case "VTX1":
                        vertexData = LoadVTX1FromFile(reader, chunkStart, chunkSize);
                        break;
                    // ENVELOPES - Defines vertex weights for skinning.
                    case "EVP1":
                        envelopes = LoadEVP1FromStream(reader, chunkStart);
                        break;
                    // DRAW (Skeletal Animation Data) - Stores which matrices are weighted, and which are used directly.
                    case "DRW1":
                        drawInfo = LoadDRW1FromStream(reader, chunkStart);
                        break;
                    // JOINTS - Stores the skeletal joints (position, rotation, scale, etc.)
                    case "JNT1":
                        joints = LoadJNT1SectionFromStream(reader, chunkStart);
                        break;
                    // SHAPE - Face/Triangle information for model.
                    case "SHP1":
                        LoadSHP1SectionFromFile(vertexData, j3dMesh, reader, chunkStart);
                        break;
                    // MATERIAL - Stores materials (which describes how textures, etc. are drawn)
                    case "MAT3":
                        materialList = LoadMAT3SectionFromStream(reader, chunkStart, chunkSize);
                        break;
                    // TEXTURES - Stores binary texture images.
                    case "TEX1":
                        textureList = LoadTEX1FromFile(reader, chunkStart);
                        break;
                    // MODEL - Seems to be bypass commands for Materials and invokes GX registers directly.
                    case "MDL3":
                        break;
                }

                reader.BaseStream.Position = chunkStart + chunkSize;
            }

            // Resolve the texture indexes into actual textures now that we've loaded the TEX1 section.
            foreach (Material mat in materialList)
            {
                for (int i = 0; i < mat.TextureIndexes.Length; i++)
                {
                    short index = mat.TextureIndexes[i];
                    if (index < 0)
                        continue;

                    mat.Textures[i] = textureList[index];
                }
            }

            // loltests
            for (int i = 0; i < materialList.Count; i++)
            {
                materialList[i].VtxDesc = j3dMesh.SubMeshes[0].GetVertexDescription();
                Shader shader = TEVShaderGenerator.GenerateShader(materialList[i]);
                materialList[i].Shader = shader;
            }

            // We're going to do something a little crazy - we're going to read the scene view and apply textures to meshes (for now)
            Material curMat = null;
            AssignMaterialsToMeshRecursive(rootNode, j3dMesh, ref curMat, materialList);

            List<SkeletonBone> skeleton = new List<SkeletonBone>();
            BuildSkeletonRecursive(rootNode, skeleton, joints, 0);

            j3dMesh.Skeleton = skeleton;
            j3dMesh.BindPoses = envelopes.inverseBindPose;

            // Let's do some ugly post-processing here to see if we can't resolve all of the cross-references and turn it into
            // a normal computer-readable format that we can digest in our RenderSytem.
            {
                for (int i = 0; i < j3dMesh.SubMeshes.Count; i++)
                {
                    MeshBatch batch = j3dMesh.SubMeshes[i];
                    batch.BoneWeights = new BoneWeight[batch.Vertices.Length];

                    for (int j = 0; j < batch.PositionMatrixIndexs.Count; j++)
                    {
                        // Okay so this is where it gets more complicated. The PMI gives us an index into the MatrixTable for the packet, which we
                        // resolve and call "drawIndexes" - however we have to divide the number they give us by three for some reason, so that is
                        // already done and now our drawIndexes array should be one-index-for-every-vertex-in-batch and it should be the index into
                        // the draw section we need.
                        ushort drw1Index = batch.drawIndexes[j];

                        // The drw1Index can be set as 0xFFFF - if so, this means that you need to use the dr1Index of the previous one.
                        // until it is no longer 0xFFFF.
                        int counter = 0;
                        while (drw1Index == 0xFFFF)
                        {
                            drw1Index = batch.drawIndexes[j - counter];
                            counter++;
                        }

                        bool isWeighted = drawInfo.IsWeighted[drw1Index];
                        BoneWeight weight = new BoneWeight();

                        if (isWeighted)
                        {
                            // Something on this doesn't work for models that actually specify a PositionMatrixIndex.
                            // So... some math is off somewhere and I don't know where for the moment.
                            ushort numBonesAffecting = envelopes.numBonesAffecting[drw1Index];
                            weight.BoneIndexes = new ushort[numBonesAffecting];
                            weight.BoneWeights = new float[numBonesAffecting];

                            // "Much WTFs"
                            ushort offset = 0;
                            for (ushort e = 0; e < envelopes.indexRemap[drw1Index]; e++)
                            {
                                offset += envelopes.numBonesAffecting[e];
                            }

                            offset *= 2;
                            Matrix4 finalTransform = Matrix4.Identity;
                            for (ushort k = 0; k < numBonesAffecting; k++)
                            {
                                ushort boneIndex = envelopes.indexRemap[offset + (k * 0x2)];
                                float boneWeight = envelopes.weights[(offset / 2) + k];

                                weight.BoneIndexes[k] = boneIndex;
                                weight.BoneWeights[k] = boneWeight;

                                // This was apaprently a partial thought I never finished or got working in the old one? :S
                            }
                        }
                        else
                        {
                            // If the vertex isn't weighted, we just use the position from the bone matrix.
                            SkeletonBone joint = skeleton[drawInfo.Indexes[drw1Index]];
                            Matrix4 translation = Matrix4.CreateTranslation(joint.Translation);
                            Matrix4 rotation = Matrix4.CreateFromQuaternion(joint.Rotation);
                            Matrix4 finalMatrix = rotation * translation;

                            // Move the mesh by transforming the position by this much.

                            // I think we can just assign full weight to the first bone index and call it good.
                            weight.BoneIndexes = new[] { drawInfo.Indexes[drw1Index] };
                            weight.BoneWeights = new[] { 1f };
                        }

                        batch.BoneWeights[j] = weight;
                    }

                }
            }

            return j3dMesh;
        }