public int MakeF3D(ROM rom) { int start = rom.offset; foreach (UInt64 cmd in header) { rom.Write64(cmd); rom.AddOffset(8); } List <TextureDescription> tds = map.Keys.ToList(); tds.Sort(new TextureDescriptionComp()); foreach (TextureDescription td in tds) { foreach (UInt64 cmd in (List <UInt64>)td) { rom.Write64(cmd); rom.AddOffset(8); } VerticesDescription vd = map[td]; vd.MakeF3D(rom); } foreach (UInt64 cmd in footer) { rom.Write64(cmd); rom.AddOffset(8); } return(rom.offset - start); }
static protected void PerformLevelScriptParse <State>(ROM rom, int offset, Parser <State>[] parser, State state) { rom.PushOffset(offset); byte curCmdIndex; do { curCmdIndex = rom.Read8(); byte curCmdSize = rom.Read8(1); if (curCmdSize == 0) { throw new ArgumentException("cmd size is 0, loop detected"); } Parser <State> func = parser[curCmdIndex]; func(rom, state); if (curCmdIndex != 0x7) { rom.AddOffset(curCmdSize); } }while (curCmdIndex != terminateCmd); rom.PopOffset(); }
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); }
public void MakeData(ROM rom) { rom.Write64(lo); rom.Write64(hi, 8); rom.AddOffset(16); }
protected static void Cmd07 <T>(ROM rom, T state) { rom.PopOffset(); rom.AddOffset(8); // diff between 0x07 length and 0x06 length }