static void WriteMesh(JsonWriter json, Mesh mesh, GeometryPool.VertexLayout layout) { // Unity does not import .obj verbatim. It makes these changes: // - flip x axis // - reverse winding of triangles // We undo these changes when exporting to obj. // NOTE: It's currently unknown whether this also happens for fbx files. int nVert = mesh.vertexCount; WriteArray(json, "v", AsByte(mesh.vertices, nVert, flip: true)); if (layout.bUseNormals) { WriteArray(json, "n", AsByte(mesh.normals, nVert, flip: true)); } WriteUvChannel(json, 0, layout.texcoord0.size, mesh, nVert); WriteUvChannel(json, 1, layout.texcoord1.size, mesh, nVert); if (layout.bUseColors) { WriteArray(json, "c", AsByte(mesh.colors32, nVert)); } // NOTE(b/30710462): Bubble wand lies about its tangents, so check they are really there if (layout.bUseTangents && mesh.tangents.Length > 0) { WriteArray(json, "t", AsByte(mesh.tangents, nVert, flip: true)); } var tris = mesh.GetTriangles(0); // Reverse winding, per above for (int i = 0; i < tris.Length; i += 3) { var tmp = tris[i+1]; tris[i+1] = tris[i+2]; tris[i+2] = tmp; } WriteArray(json, "tri", AsByte(tris, tris.Length)); }
public override GeometryPool.VertexLayout GetVertexLayout(BrushDescriptor desc) { // XXX: This doesn't work because blocks models may not always have the same vertex layout. // It happens to work currently. var layout = new GeometryPool.VertexLayout(); layout.bUseColors = true; layout.bUseNormals = true; layout.bUseTangents = false; layout.bUseVertexIds = false; return layout; }
// Puts the passed timestamp data into pool.texcoord2. // It's assumed that the pool does not already have anything in texcoord2. private static void AugmentWithTimestamps(GeometryPool pool, ref List <Vector3> timestamps) { if (App.UserConfig.Export.ExportStrokeTimestamp) { GeometryPool.VertexLayout withTxc2 = pool.Layout; if (withTxc2.texcoord2.size == 0 && timestamps.Count == pool.NumVerts) { withTxc2.texcoord2 = new TexcoordInfo { size = 3, semantic = Semantic.Timestamp }; pool.Layout = withTxc2; pool.m_Texcoord2.v3 = timestamps; timestamps = null; // don't let caller reuse the list } else { Debug.LogError("Internal error; cannot add timestamps"); } } }
/// Rewrite vertices and indices, welding together identical verts. /// Input topology must be as documented at the top of QuadStripBrush.cs /// Output topology is the same as FlatGeometryBrush private static void WeldSingleSidedQuadStrip( GeometryPool.VertexLayout layout, MasterBrush geometry, int numVerts, out int newNumVerts, out int newNumTris) { // Offsets to Front/Back Left/Right verts, QuadStrip-style (see ascii art at top of file) // Because of duplication, there are sometimes multiple offsets. // "S" is the stride, either 6 or 12 depending on usesDoubleSidedGeometry. // In cases where there are multiple offsets to choose from, we choose the offset // which makes it safe to reorder verts in-place. See the comment below re: overlaps const int kBrOld = 2; // also -S+5, 3 const int kBlOld = 0; // also -S+1, -S+4 const int kFrOld = 5; // also S+2, S+3 const int kFlOld = 1; // also 4, S+0 // Offsets to Front/Back Left/Right verts, FlatGeometry-style // See FlatGeometryBrush.cs:15 const int kBrNew = 0; const int kBlNew = 1; const int kFrNew = 2; const int kFlNew = 3; // End result: // 0--1 4 keep 2, 0, 5, 1 0--1 // |,' ,'| -> ignore 3, 4 -> |,'| // 2 3--5 2--5 int vertRead = 0; // vertex read index int vertWrite = 0; // vertex write index int triWrite = 0; // triangle write index var vs = geometry.m_Vertices; var ns = geometry.m_Normals; var cs = geometry.m_Colors; var ts = geometry.m_Tangents; var tris = geometry.m_Tris; var uv2s = geometry.m_UVs; var uv3s = geometry.m_UVWs.GetBackingArray(); while (vertRead < numVerts) { // Compress a single connected strip. // The first quad is treated differently from subsequent quads, // because it doesn't have a previous quad to share its first two verts with. // First quad // We write to the same buffer being read from, which is a bit dangerous. // Correctness requires we not copy from a location that's been written to. // The potential problem cases where the read area overlaps the write area are: // Quad #0: write [0, 4) read [0, 6) // Quad #1: write [4, 8) read [6, 12) // For subsequent quads the read area is ahead of, and does not overlap, the write area. vs[vertWrite + kFlNew] = vs[vertRead + kFlOld]; // 3 <- 1 7 <- 7 ns[vertWrite + kFlNew] = ns[vertRead + kFlOld]; cs[vertWrite + kFlNew] = cs[vertRead + kFlOld]; ts[vertWrite + kFlNew] = ts[vertRead + kFlOld]; vs[vertWrite + kBlNew] = vs[vertRead + kBlOld]; // 1 <- 0 5 <- 6 ns[vertWrite + kBlNew] = ns[vertRead + kBlOld]; cs[vertWrite + kBlNew] = cs[vertRead + kBlOld]; ts[vertWrite + kBlNew] = ts[vertRead + kBlOld]; vs[vertWrite + kBrNew] = vs[vertRead + kBrOld]; // 0 <- 2 4 <- 8 ns[vertWrite + kBrNew] = ns[vertRead + kBrOld]; cs[vertWrite + kBrNew] = cs[vertRead + kBrOld]; ts[vertWrite + kBrNew] = ts[vertRead + kBrOld]; vs[vertWrite + kFrNew] = vs[vertRead + kFrOld]; // 2 <- 5 6 <- 11 ns[vertWrite + kFrNew] = ns[vertRead + kFrOld]; cs[vertWrite + kFrNew] = cs[vertRead + kFrOld]; ts[vertWrite + kFrNew] = ts[vertRead + kFrOld]; if (layout.texcoord0.size == 2) { uv2s[vertWrite + kFlNew] = uv2s[vertRead + kFlOld]; uv2s[vertWrite + kBlNew] = uv2s[vertRead + kBlOld]; uv2s[vertWrite + kBrNew] = uv2s[vertRead + kBrOld]; uv2s[vertWrite + kFrNew] = uv2s[vertRead + kFrOld]; } else { uv3s[vertWrite + kFlNew] = uv3s[vertRead + kFlOld]; uv3s[vertWrite + kBlNew] = uv3s[vertRead + kBlOld]; uv3s[vertWrite + kBrNew] = uv3s[vertRead + kBrOld]; uv3s[vertWrite + kFrNew] = uv3s[vertRead + kFrOld]; } // See FlatGeometryBrush.cs:240 // SetTri(cur.iTri, cur.iVert, 0, BR, BL, FL); // SetTri(cur.iTri, cur.iVert, 1, BR, FL, FR); tris[triWrite + 0] = vertWrite + kBrNew; tris[triWrite + 1] = vertWrite + kBlNew; tris[triWrite + 2] = vertWrite + kFlNew; tris[triWrite + 3] = vertWrite + kBrNew; tris[triWrite + 4] = vertWrite + kFlNew; tris[triWrite + 5] = vertWrite + kFrNew; vertWrite += 4; // we wrote to a range of 4 verts vertRead += 6; // we read from a range of 6 verts triWrite += 6; // we wrote 6 indices // Remaining quads. // Detect strip continuation by checking a single vertex position. // To be fully correct, we should check both the left and right verts, and all the // attributes. However, as of M16 this simpler version suffices for all shipping // QuadStripBrushes. while (vertRead < numVerts && vs[vertRead + kBrOld] == vs[vertWrite - 4 + kFrNew]) { vertWrite -= 2; // Share 2 verts with the previous quad // The read range will provably never overlap with the write range. Note that // this cannot be quad 0 since there exists a previous quad. // Therefore, the closest the ranges get is: // Quad #1: write [2, 6) read [6, 12) vs[vertWrite + kFlNew] = vs[vertRead + kFlOld]; ns[vertWrite + kFlNew] = ns[vertRead + kFlOld]; cs[vertWrite + kFlNew] = cs[vertRead + kFlOld]; ts[vertWrite + kFlNew] = ts[vertRead + kFlOld]; vs[vertWrite + kFrNew] = vs[vertRead + kFrOld]; ns[vertWrite + kFrNew] = ns[vertRead + kFrOld]; cs[vertWrite + kFrNew] = cs[vertRead + kFrOld]; ts[vertWrite + kFrNew] = ts[vertRead + kFrOld]; if (layout.texcoord0.size == 2) { uv2s[vertWrite + kFlNew] = uv2s[vertRead + kFlOld]; uv2s[vertWrite + kFrNew] = uv2s[vertRead + kFrOld]; } else { uv3s[vertWrite + kFlNew] = uv3s[vertRead + kFlOld]; uv3s[vertWrite + kFrNew] = uv3s[vertRead + kFrOld]; } tris[triWrite + 0] = vertWrite + kBrNew; tris[triWrite + 1] = vertWrite + kBlNew; tris[triWrite + 2] = vertWrite + kFlNew; tris[triWrite + 3] = vertWrite + kBrNew; tris[triWrite + 4] = vertWrite + kFlNew; tris[triWrite + 5] = vertWrite + kFrNew; vertWrite += 4; vertRead += 6; triWrite += 6; } } newNumVerts = vertWrite; newNumTris = triWrite; }