Beispiel #1
0
        public static void Hook06000036(Mesh mesh, Vector3[] vertices)
        {
            if (!bufmap.ContainsKey(vertices))
            {
                if (!resmap.ContainsKey(mesh))
                {
                    resmap.Add(mesh, new List <Vector3[]>());
                }

                VertexBuffer buf = null;

                foreach (Vector3[] v in resmap[mesh])
                {
                    if (v.Length != vertices.Length)
                    {
                        continue;
                    }
                    buf = bufmap[v];
                    for (int i = 0; i < v.Length; ++i)
                    {
                        if (v[i] != vertices[i])
                        {
                            buf = null;
                            break;
                        }
                    }
                    if (buf != null)
                    {
                        break;
                    }
                }

                if (buf == null)
                {
                    VertexBufferDescription desc = mesh.VertexBuffer.Description;
                    buf = new VertexBuffer(mesh.Device, mesh.VertexCount * 32, desc.Usage, VertexFormat.Position | VertexFormat.Texture1 | VertexFormat.Normal, desc.Pool);
                    DataStream source = mesh.VertexBuffer.Lock(0, mesh.VertexCount * 32, LockFlags.ReadOnly);
                    DataStream stream = buf.Lock(0, mesh.VertexCount * 32, LockFlags.None);
                    for (int i = 0; i < vertices.Length; i++)
                    {
                        stream.Write(vertices[i]);
                        source.Seek(12, SeekOrigin.Current);
                        stream.WriteRange(source.ReadRange <float>(5));
                    }
                    buf.Unlock();
                    mesh.VertexBuffer.Unlock();
                }

                resmap[mesh].Add(vertices);

                bufmap.Add(vertices, buf);
            }

            mesh.Device.SetStreamSource(0, bufmap[vertices], 0, 32);
            mesh.Device.Indices = mesh.IndexBuffer;
        }
Beispiel #2
0
        /// <summary>
        /// Creates a vertex buffer from IVertexData
        /// </summary>
        /// <param name="graphics">Graphics device</param>
        /// <param name="description">Vertex buffer description</param>
        /// <returns>Returns new buffer</returns>
        private static Buffer CreateVertexBuffer(Graphics graphics, VertexBufferDescription description)
        {
            string name     = description.Name;
            var    vertices = description.Data;
            bool   dynamic  = description.Dynamic;

            if (vertices?.Any() != true)
            {
                return(null);
            }

            var vertexType = vertices.First().VertexType;

            switch (vertexType)
            {
            case VertexTypes.Billboard:
                return(graphics.CreateVertexBuffer <VertexBillboard>(name, vertices, dynamic));

            case VertexTypes.CPUParticle:
                return(graphics.CreateVertexBuffer <VertexCpuParticle>(name, vertices, dynamic));

            case VertexTypes.GPUParticle:
                return(graphics.CreateVertexBuffer <VertexGpuParticle>(name, vertices, dynamic));

            case VertexTypes.Terrain:
                return(graphics.CreateVertexBuffer <VertexTerrain>(name, vertices, dynamic));

            case VertexTypes.Position:
                return(graphics.CreateVertexBuffer <VertexPosition>(name, vertices, dynamic));

            case VertexTypes.PositionColor:
                return(graphics.CreateVertexBuffer <VertexPositionColor>(name, vertices, dynamic));

            case VertexTypes.PositionTexture:
                return(graphics.CreateVertexBuffer <VertexPositionTexture>(name, vertices, dynamic));

            case VertexTypes.PositionNormalColor:
                return(graphics.CreateVertexBuffer <VertexPositionNormalColor>(name, vertices, dynamic));

            case VertexTypes.PositionNormalTexture:
                return(graphics.CreateVertexBuffer <VertexPositionNormalTexture>(name, vertices, dynamic));

            case VertexTypes.PositionNormalTextureTangent:
                return(graphics.CreateVertexBuffer <VertexPositionNormalTextureTangent>(name, vertices, dynamic));

            case VertexTypes.PositionSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPosition>(name, vertices, dynamic));

            case VertexTypes.PositionColorSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPositionColor>(name, vertices, dynamic));

            case VertexTypes.PositionTextureSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPositionTexture>(name, vertices, dynamic));

            case VertexTypes.PositionNormalColorSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPositionNormalColor>(name, vertices, dynamic));

            case VertexTypes.PositionNormalTextureSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPositionNormalTexture>(name, vertices, dynamic));

            case VertexTypes.PositionNormalTextureTangentSkinned:
                return(graphics.CreateVertexBuffer <VertexSkinnedPositionNormalTextureTangent>(name, vertices, dynamic));

            default:
                throw new EngineException(string.Format("Unknown vertex type: {0}", vertexType));
            }
        }
Beispiel #3
0
        public int MakeF3D(ROM rom, SortedRegionList vertexBytes, ScrollFactory factory)
        {
            int start = rom.offset;

            foreach (UInt64 cmd in header)
            {
                rom.Write64(cmd);
                rom.AddOffset(8);
            }

            // TODO: This is kinda stupid assumption but we believe there are more triangles in the beginning available...
            List <ScrollingTextureDescription> tds = map.Keys.ToList();

            tds.Sort(new ScrollingTextureDescriptionComp());

            foreach (ScrollingTextureDescription td in tds)
            {
                td.MakeF3D(rom);

                // Time to do optimization magic
                List <Triangle> textureTris = map[td];

                // Create reverse map for vertices, vertices with more triangles will be used first
                Dictionary <Vertex, List <Triangle> > vertex2triMap = new Dictionary <Vertex, List <Triangle> >();
                foreach (Triangle tri in textureTris)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        Vertex v = tri.vertices[i];
                        if (!vertex2triMap.TryGetValue(v, out List <Triangle> weight))
                        {
                            vertex2triMap[v] = new List <Triangle>();
                        }
                        vertex2triMap[v].Add(tri);
                    }
                }

                // You quite literally can't eat more than that
                int vertexLength = 0x30 * textureTris.Count();
                vertexBytes.CutContigRegion(vertexLength, out int vertexPosition);

                int allocVertexStart     = vertexPosition;
                int allocVertexEnd       = allocVertexStart + vertexLength;
                int writtenVerticesCount = 0;

                // Check if there are still vertices that needs to be worked with
                // Also we must make sure all lists in v2tm are not empty!
                while (vertex2triMap.Count != 0)
                {
                    int segmentedVertexStart = rom.GetSegmentedAddress(vertexPosition);

                    // vbd is main structure that holds information about used vertices space
                    VertexBufferDescription vbd = new VertexBufferDescription((UInt32)segmentedVertexStart, 0xF);

                    // Potential triangles also have weight depending on connection to other triangles and to added vertices
                    // Weight +1 for each tri that shares vertice; +vertexPresentBonus for each vbd present vertex
                    // It is recommended to have vertexPresentBonus be more than any weight that shared vertices can give
                    // This way triangle that have the most weight will have the most vertices and potentially help others
                    Dictionary <Triangle, int> potentialTris = new Dictionary <Triangle, int>();

                    // Add triangles to vbd till it won't be able to fit in the whole triangle
                    while (vbd.freeCount != 0 && vertex2triMap.Count != 0)
                    {
                        // Right now no vertices are in vbd, pick one with the heighest weight and add it to vbd
                        if (potentialTris.Count == 0)
                        {
                            // We need at least 3 vertices to draw not present tri
                            if (vbd.freeCount < 3)
                            {
                                break;
                            }

                            KeyValuePair <Vertex, List <Triangle> > pair;
                            // Let's ask for at least 5 vertices for optimal stuff, otherwise just put the worst triangle and call it
                            if (vbd.freeCount < 5)
                            {
                                pair = vertex2triMap.Aggregate((prev, cur) => prev.Value.Count < cur.Value.Count ? prev : cur);
                            }
                            else
                            {
                                pair = vertex2triMap.Aggregate((prev, cur) => prev.Value.Count > cur.Value.Count ? prev : cur);
                            }

                            Vertex          v    = pair.Key;
                            List <Triangle> tris = pair.Value;
                            if (tris.Count == 0)
                            {
                                throw new ArithmeticException("Tris count in dict can't be 0");
                            }

                            // Put current vertex and use found tris as potential next tri to add
                            vbd.AddVertex(v);

                            // Setup all potential triangles to could be added to vbd, the ones that have this vertex in common

                            foreach (Triangle tri in tris)
                            {
                                potentialTris[tri] = 0;
                            }

                            // If vertex is in common between tris, give 1 each
                            var tripairs = tris.Zip(tris.Skip(1), (a, b) => Tuple.Create(a, b));
                            foreach (Tuple <Triangle, Triangle> tripair in tripairs)
                            {
                                Triangle tri1   = tripair.Item1;
                                Triangle tri2   = tripair.Item2;
                                int      common = tri1.CountCommon(tri2);

                                potentialTris[tri1] += common;
                                potentialTris[tri2] += common;
                            }
                        }


                        // Find tri with biggest weight
                        Triangle biggestTri = potentialTris.Aggregate((prev, cur) => prev.Value > cur.Value ? prev : cur).Key;

                        // Add all triangles in vbd
                        int[] indices = new int[3];

                        for (int i = 0; i < 3; i++)
                        {
                            Vertex v     = biggestTri.vertices[i];
                            int    index = vbd.AddVertex(v, out bool isNew);
                            indices[i] = index;

                            // Fix weights of tris if a new vertex was added to vbd (not already in vbd it is)
                            if (isNew)
                            {
                                foreach (Triangle ptri in potentialTris.Keys.ToList())
                                {
                                    if (ptri.Contains(v))
                                    {
                                        potentialTris[ptri] += vertexPresentBonus;
                                    }
                                }
                            }
                        }

                        // Draw the triangle, all vertices are guaranteed to be in
                        vbd.DrawTriangle(indices);

                        // As triangle was drawn, it needs to be removed from structs that had it
                        // vertex2map & potentialTris
                        // This way triangle won't be able to appear again
                        foreach (Vertex v in biggestTri.vertices)
                        {
                            vertex2triMap[v].Remove(biggestTri);
                            if (vertex2triMap[v].Count == 0)
                            {
                                vertex2triMap.Remove(v);
                            }
                        }

                        potentialTris.Remove(biggestTri);

                        // Setup new triangles to potentialTris
                        HashSet <Triangle> newTris = new HashSet <Triangle>();
                        foreach (Vertex v in biggestTri.vertices)
                        {
                            if (vertex2triMap.TryGetValue(v, out List <Triangle> tris))
                            {
                                foreach (Triangle tri in tris)
                                {
                                    newTris.Add(tri);
                                }
                            }
                        }

                        // Also do not take into account tris that were added already
                        // If we will take them into account, bad things will happen
                        newTris.RemoveWhere(t => potentialTris.Keys.Contains(t));

                        // As new triangles appear, proceed to fix weights
                        // First of all, initialize world: create new potential tris that will be merged later with potential tris
                        // Initial value is -vertexPresentBonus as one vertex will be counted anyways
                        Dictionary <Triangle, int> newPotentialTris = new Dictionary <Triangle, int>();
                        foreach (Triangle ntri in newTris)
                        {
                            newPotentialTris[ntri] = -vertexPresentBonus;
                        }

                        // Calculate weights for vertices that were present
                        foreach (Triangle ntri in newTris)
                        {
                            foreach (Vertex v in vbd.vbuf)
                            {
                                if (ntri.Contains(v))
                                {
                                    newPotentialTris[ntri] += vertexPresentBonus;
                                }
                            }
                        }

                        // Check between triangles new/new
                        var newNewTripairs = newTris.Zip(newTris.Skip(1), (a, b) => Tuple.Create(a, b));
                        foreach (Tuple <Triangle, Triangle> tripair in newNewTripairs)
                        {
                            Triangle tri1   = tripair.Item1;
                            Triangle tri2   = tripair.Item2;
                            int      common = tri1.CountCommon(tri2);

                            newPotentialTris[tri1] += common;
                            newPotentialTris[tri2] += common;
                        }

                        // Check between triangles new/old
                        List <Triangle> oldTris        = potentialTris.Keys.ToList();
                        var             newOldTripairs = newTris.Zip(oldTris, (a, b) => Tuple.Create(a, b));
                        foreach (Tuple <Triangle, Triangle> tripair in newOldTripairs)
                        {
                            Triangle newTri = tripair.Item1;
                            Triangle oldTri = tripair.Item2;

                            int common = newTri.CountCommon(oldTri);

                            newPotentialTris[newTri] += common;
                            potentialTris[oldTri]    += common;
                        }

                        // Merge together old and new
                        foreach (KeyValuePair <Triangle, int> pair in newPotentialTris)
                        {
                            potentialTris.Add(pair.Key, pair.Value);
                        }

                        // Check if there are triangles that have all vertexes inside the vbd
                        // All such vertices could be initially drawn so just draw them
                        // Triangles with weight over 2*vertexPresetBonus have 2+1 vertices in buffer so just draw them
                        var drawnPairs = potentialTris.Where((t, w) => w >= 2 * vertexPresentBonus);
                        foreach (KeyValuePair <Triangle, int> pair in drawnPairs)
                        {
                            Triangle ftri = pair.Key;
                            for (int i = 0; i < 3; i++)
                            {
                                Vertex v     = ftri.vertices[i];
                                int    index = vbd.AddVertex(v, out bool isNew);
                                indices[i] = index;

                                // Fix weights of tris if a new vertex was added to vbd (not already in vbd it is)
                                if (isNew)
                                {
                                    throw new ArithmeticException("Triangles with high weight has new vertices!");
                                }
                            }

                            // Draw the triangle, all vertices are guaranteed to be in
                            vbd.DrawTriangle(indices);
                            potentialTris.Remove(biggestTri);
                        }

                        // Check if there are less then 2 vertices, which are required to be able to draw any tri
                        // If there is a vertex that has vertexPresentBonus weight, we good, otherwise retreat
                        if (vbd.freeCount == 1)
                        {
                            if (potentialTris.Values.ToList().FindIndex(w => w >= vertexPresentBonus) == -1)
                            {
                                break;
                            }
                        }
                    }

                    rom.PushOffset(vertexPosition);
                    vbd.MakeData(rom);
                    writtenVerticesCount += (rom.offset - vertexPosition) / 0x10;
                    vertexPosition        = rom.offset;
                    rom.PopOffset();

                    vbd.MakeF3D(rom);
                }

                if (td.scroll != null)
                {
                    // Vertices must be rounded by 3 because skelux scrolls work like that

                    /*
                     * if (writtenVerticesCount % 3 != 0)
                     * {
                     *  int leftToRoundVertices = 3 - (writtenVerticesCount % 3);
                     *  writtenVerticesCount += leftToRoundVertices;
                     *  vertexPosition += leftToRoundVertices * 0x10;
                     * }
                     */

                    int segmentedAddress        = rom.GetSegmentedAddress(allocVertexStart);
                    List <ScrollObject> scrolls = factory.GetScrolls(writtenVerticesCount, segmentedAddress, td);
                    foreach (ScrollObject scroll in scrolls)
                    {
                        scroll.WriteScroll(rom);
                    }
                }

                vertexBytes.AddRegion(vertexPosition, allocVertexEnd - vertexPosition);
            }
            foreach (UInt64 cmd in footer)
            {
                rom.Write64(cmd);
                rom.AddOffset(8);
            }
            return(rom.offset - start);
        }