Пример #1
0
        public static List<Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset)
        {
            var chunks = new List<Chunk>();
            Chunk currentChunk = new Chunk();
            Chunk previousChunk = null;
            while (offset < endOffset) {
                int vifCommand = fileData[offset + 3] & 0x7f;
                int numCommand = fileData[offset + 2] & 0xff;
                int immCommand = DataUtil.getLEShort(fileData, offset);
                switch (vifCommand) {
                    case NOP_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("NOP");
                        offset += 4;
                        break;
                    case STCYCL_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("STCYCL: WL: " + (immCommand >> 8) + " CL: " + (immCommand & 0xFF));
                        offset += 4;
                        break;
                    case ITOP_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("ITOP: " + immCommand);
                        offset += 4;
                        break;
                    case STMOD_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("STMOD: " + immCommand);
                        offset += 4;
                        break;
                    case MSCAL_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("MSCAL: " + immCommand);
                        if (immCommand != 66 && immCommand != 68 && immCommand != 70)
                        {
                            DebugWriteLine("**** Microcode " + immCommand + " not supported");
                        }
                        currentChunk.mscalID = immCommand;
                        chunks.Add(currentChunk);
                        previousChunk = currentChunk;
                        currentChunk = new Chunk();

                        offset += 4;
                        break;
                    case STMASK_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        offset += 4;
                        int stmask = DataUtil.getLEInt(fileData, offset);
                        DebugWriteLine("STMASK: " + stmask);
                        offset += 4;
                        break;
                    case FLUSH_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("FLUSH");
                        offset += 4;
                        break;
                    case DIRECT_CMD:
                        DebugWrite(HexUtil.formatHex(offset) + " ");
                        DebugWriteLine("DIRECT, " + immCommand*16 + " bytes");

                        GIFTag[] tags = new GIFTag[immCommand];

                        for (int i = 0; i < immCommand; i++)
                        {
                            tags[i] = new GIFTag();
                            tags[i].parse(fileData, offset + 4 + i*16);
                        }
                        currentChunk.DIRECTGifTags.AddRange(tags);

                        offset += 4;
                        offset += immCommand * 16;
                        break;
                    default:
                        if ((vifCommand & 0x60) == 0x60) {
                            // unpack command
                            bool mask = ((vifCommand & 0x10) == 0x10);
                            int vn = (vifCommand >> 2) & 3;
                            int vl = vifCommand & 3;
                            int addr = immCommand & 0x1ff;
                            bool flag = (immCommand & 0x8000) == 0x8000;
                            bool usn = (immCommand & 0x4000) == 0x4000;

                            DebugWrite(HexUtil.formatHex(offset) + " ");
                            String debugMsg = "UNPACK: vn: " + vn + ", vl: " + vl + ", Addr: " + addr + ", num: " + numCommand;

                            if (flag) {
                                debugMsg += ", Flag";
                            }
                            if (usn) {
                                debugMsg += ", Unsigned";
                            }
                            if (mask) {
                                debugMsg += ", Mask";
                            }
                            DebugWriteLine(debugMsg);
                            offset += 4;
                            if (vn == 1 && vl == 1) {
                                // v2-16
                                // I don't know why but the UVs come after the MSCAL instruction.
                                if (previousChunk != null) {
                                    for (int uvnum = 0; uvnum < numCommand; ++uvnum) {
                                        short u = DataUtil.getLEShort(fileData, offset);
                                        short v = DataUtil.getLEShort(fileData, offset + 2);
                                        previousChunk.uvs.Add(new UV(u, v));
                                        offset += 4;
                                    }
                                } else {
                                    int numBytes = numCommand * 4;
                                    offset += numBytes;
                                }
                            } else if (vn == 2 && vl == 1) {
                                // v3-16
                                // each vertex is 128 bits, so num is the number of vertices
                                for (int vnum = 0; vnum < numCommand; ++vnum) {
                                    if (!usn) {
                                        short x = DataUtil.getLEShort(fileData, offset);
                                        short y = DataUtil.getLEShort(fileData, offset + 2);
                                        short z = DataUtil.getLEShort(fileData, offset + 4);
                                        offset += 6;

                                        Vertex vertex = new Vertex();
                                        vertex.x = x;
                                        vertex.y = y;
                                        vertex.z = z;
                                        currentChunk.vertices.Add(vertex);
                                    } else {
                                        int x = DataUtil.getLEUShort(fileData, offset);
                                        int y = DataUtil.getLEUShort(fileData, offset + 2);
                                        int z = DataUtil.getLEUShort(fileData, offset + 4);
                                        offset += 6;

                                        VLoc vloc = new VLoc();
                                        vloc.v1 = x;
                                        vloc.v2 = y;
                                        vloc.v3 = z;
                                        currentChunk.vlocs.Add(vloc);
                                    }
                                }
                                offset = (offset + 3) & ~3;
                            } else if (vn == 2 && vl == 2) {
                                // v3-8
                                int idx = offset;
                                for (int vnum = 0; vnum < numCommand; ++vnum) {
                                    SByteVector vec = new SByteVector();
                                    vec.x = (sbyte)fileData[idx++];
                                    vec.y = (sbyte)fileData[idx++];
                                    vec.z = (sbyte)fileData[idx++];
                                    currentChunk.normals.Add(vec);
                                }
                                int numBytes = ((numCommand * 3) + 3) & ~3;
                                offset += numBytes;
                            } else if (vn == 3 && vl == 0) {
                                // v4-32
                                log.LogLine("v4-32 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                                if (1 == numCommand) {
                                    currentChunk.gifTag0 = new GIFTag();
                                    currentChunk.gifTag0.parse(fileData, offset);
                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag: " + currentChunk.gifTag0.ToString());
                                } else if (2 == numCommand) {
                                    currentChunk.gifTag0 = new GIFTag();
                                    currentChunk.gifTag0.parse(fileData, offset);
                                    currentChunk.gifTag1 = new GIFTag();
                                    currentChunk.gifTag1.parse(fileData, offset + 16);

                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag0: " + currentChunk.gifTag0.ToString());
                                    DebugWrite(HexUtil.formatHex(offset) + " ");
                                    DebugWriteLine("GifTag1: " + currentChunk.gifTag1.ToString());
                                } else {
                                    log.LogLine("unknown number of gif commands.");
                                }
                                int numBytes = numCommand * 16;
                                offset += numBytes;
                            } else if (vn == 3 && vl == 1) {
                                // v4-16
                                log.LogLine("v4-16 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr);
                                int numShorts = numCommand * 4;
                                if (usn) {
                                    currentChunk.extraVlocs = new ushort[numShorts];
                                    for (int i = 0; i < numCommand; ++i) {
                                        currentChunk.extraVlocs[i*4] = DataUtil.getLEUShort(fileData, offset + i * 8);
                                        currentChunk.extraVlocs[i * 4 + 1] = DataUtil.getLEUShort(fileData, offset + i * 8 + 2);
                                        currentChunk.extraVlocs[i * 4 + 2] = DataUtil.getLEUShort(fileData, offset + i * 8 + 4);
                                        currentChunk.extraVlocs[i * 4 + 3] = DataUtil.getLEUShort(fileData, offset + i * 8 + 6);
                                    }
                                } else {
                                    log.LogLine("Unsupported tag");
                                }
                                offset += numShorts * 2;
                            } else if (vn == 3 && vl == 2) {
                                // v4-8
                                int numBytes = numCommand * 4;
                                currentChunk.vertexWeights = new List<VertexWeight>();
                                int curVertex=0;
                                for (int i = 0; i < numCommand; ++i) {
                                    VertexWeight vw = new VertexWeight();
                                    vw.startVertex = curVertex;
                                    vw.bone1 = fileData[offset++] / 4;
                                    vw.boneWeight1 = fileData[offset++];
                                    vw.bone2 = fileData[offset++];
                                    if (vw.bone2 == 0xFF) {
                                        // Single bone
                                        vw.boneWeight2 = 0;
                                        int count = fileData[offset++];
                                        curVertex += count;
                                    } else {
                                        vw.bone2 /= 4;
                                        vw.boneWeight2 = fileData[offset++];
                                        ++curVertex;

                                        if (vw.boneWeight1 + vw.boneWeight2 < 255)
                                        {
                                            ++i;
                                            vw.bone3 = fileData[offset++] / 4;
                                            vw.boneWeight3 = fileData[offset++];
                                            vw.bone4 = fileData[offset++];
                                            int bw4 = fileData[offset++];
                                            if (vw.bone4 != 255)
                                            {
                                                vw.bone4 /= 4;
                                                vw.boneWeight4 = bw4;
                                            }
                                        }

                                    }
                                    vw.endVertex = curVertex - 1;
                                    currentChunk.vertexWeights.Add(vw);
                                }

                            } else {
                                DebugWriteLine("Unknown vnvl combination: vn=" + vn + ", vl=" + vl);
                                offset = endOffset;
                            }
                        } else {
                            DebugWriteLine("Unknown command: " + vifCommand);
                            offset = endOffset;
                        }
                        break;
                }
            }
            return chunks;
        }
Пример #2
0
        public static Model3D CreateModel3D(List<Mesh> meshGroups, BitmapSource texture, AnimData pose, int frame)
        {
            GeometryModel3D model = new GeometryModel3D();
            var mesh3D = new MeshGeometry3D();

            int numVertices = 0;
            foreach (var meshGroup in meshGroups)
            {
                numVertices += meshGroup.Positions.Count;
            }
            var triangleIndices = new Int32Collection();
            var positions = new Point3DCollection(numVertices);
            var normals = new Vector3DCollection(numVertices);
            var uvCoords = new PointCollection(numVertices);
            int vstart = 0;

            foreach (var meshGroup in meshGroups)
            {
                Boolean hasVertexWeights = meshGroup.vertexWeights.Count > 0;
                int vwNum = 0;
                VertexWeight vw = new VertexWeight();
                if (meshGroup.vertexWeights.Count > 0)
                {
                    vw = meshGroup.vertexWeights[vwNum];
                }
                int vnum = 0;
                foreach (var vertex in meshGroup.Positions)
                {
                    var point = vertex;
                    if (frame >= 0 && pose != null) {
                        if (vw.endVertex < vnum)
                        {
                            ++vwNum;
                            vw = meshGroup.vertexWeights[vwNum];
                            if (vnum < vw.startVertex || vnum > vw.endVertex)
                            {
                                Debug.Fail("Vertex " + vnum + " out of range of bone weights " + vw.startVertex + " -> " + vw.endVertex);
                            }
                        }
                        int bone1No = vw.bone1;
                        Point3D bindingPos1 = pose.bindingPose[bone1No];
                        AnimMeshPose bone1Pose = pose.perFrameFKPoses[frame, bone1No];
                        var joint1Pos = bone1Pose.Position;
                        if (vw.bone2 == 0xFF) {
                            if (bone1No == 1)
                            {
                                bone1No = 1;
                            }
                            Matrix3D m = Matrix3D.Identity;
                            m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z));   // Inverse binding matrix
                            m.Rotate(bone1Pose.Rotation);
                            m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z));
                            point = m.Transform(point);
                        } else {
                            // multi-bone
                            int bone2No = vw.bone2;
                            Point3D bindingPos2 = pose.bindingPose[bone2No];
                            AnimMeshPose bone2Pose = pose.perFrameFKPoses[frame, bone2No];
                            double boneSum = vw.boneWeight1 + vw.boneWeight2;
                            double bone1Coeff = vw.boneWeight1 / boneSum;
                            double bone2Coeff = vw.boneWeight2 / boneSum;

                            Matrix3D m = Matrix3D.Identity;
                            m.Translate(new Vector3D(-bindingPos1.X, -bindingPos1.Y, -bindingPos1.Z));   // Inverse binding matrix
                            m.Rotate(bone1Pose.Rotation);
                            m.Translate(new Vector3D(bone1Pose.Position.X, bone1Pose.Position.Y, bone1Pose.Position.Z));
                            var point1 = m.Transform(point);

                            // Now rotate
                            Matrix3D m2 = Matrix3D.Identity;
                            m2.Translate(new Vector3D(-bindingPos2.X, -bindingPos2.Y, -bindingPos2.Z));   // Inverse binding matrix
                            m2.Rotate(bone2Pose.Rotation);
                            m2.Translate(new Vector3D(bone2Pose.Position.X, bone2Pose.Position.Y, bone2Pose.Position.Z));
                            var point2 = m2.Transform(point);

                            point = new Point3D(point1.X * bone1Coeff + point2.X * bone2Coeff, point1.Y * bone1Coeff + point2.Y * bone2Coeff, point1.Z * bone1Coeff + point2.Z * bone2Coeff);
                        }
                    }
                    positions.Add(point);
                    ++vnum;
                }
                foreach (var normal in meshGroup.Normals)
                {
                    normals.Add(normal);
                }
                foreach (var ti in meshGroup.TriangleIndices) {
                    triangleIndices.Add(ti+vstart);
                }
                foreach (var uv in meshGroup.TextureCoordinates) {
                    uvCoords.Add(uv);
                }
                vstart += meshGroup.Positions.Count;
            }
            mesh3D.TriangleIndices = triangleIndices;
            mesh3D.Positions = positions;
            mesh3D.TextureCoordinates = uvCoords;
            mesh3D.Normals = normals;
            model.Geometry = mesh3D;
            var dm = new DiffuseMaterial();
            if (texture != null && texture.Width > 0 && texture.Height > 0)
            {
                var ib = new ImageBrush(texture);
                ib.ViewportUnits = BrushMappingMode.Absolute;
                // May be needed at a later point
                //ib.TileMode = TileMode.Tile;
                dm.Brush = ib;
            }
            else
            {
                var dg = new DrawingGroup();
                // Background
                dg.Children.Add(new GeometryDrawing()
                {
                    Brush = new SolidColorBrush(Colors.Black),
                    Geometry = new RectangleGeometry(new Rect(0, 0, 2, 2))
                });

                // Tiles
                dg.Children.Add(new GeometryDrawing()
                {
                    Brush = new SolidColorBrush(Colors.Violet),
                    Geometry = new RectangleGeometry(new Rect(0, 0, 1, 1))
                });
                dg.Children.Add(new GeometryDrawing()
                {
                    Brush = new SolidColorBrush(Colors.Violet),
                    Geometry = new RectangleGeometry(new Rect(1, 1, 1, 1))
                });

                dm.Brush = new DrawingBrush(dg){ TileMode = TileMode.Tile, Transform = new ScaleTransform(0.1,0.1)};
            }
            model.Material = dm;
            return model;
        }