/// <summary> /// Create a new TMD by reading from a binary stream. /// </summary> /// <param name="b">The binary stream.</param> public TMD(BinaryReader b) { Header = new TMDHeader(b); NumberOfVertices = 0; ObjectTable = new TMDObject[Header.NumObjects]; uint objTableTop = (uint)b.BaseStream.Position; for (int i = 0; i < Header.NumObjects; i++) { ObjectTable[i] = new TMDObject(b, Header.FixP, objTableTop); NumberOfVertices += ObjectTable[i].NumVertices; } }
private FastMesh createTileMesh(LBDTile tile, TMD tilesTmd, Dictionary <TMDObject, FastMesh> tileCache) { TMDObject tileObj = tilesTmd.ObjectTable[tile.TileType]; if (tileCache.ContainsKey(tileObj)) { return(tileCache[tileObj]); } Mesh m = LibLSDUnity.MeshFromTMDObject(tileObj); FastMesh fm = new FastMesh(m, new[] { LBDDiffuse, LBDAlpha }); tileCache[tileObj] = fm; return(fm); }
private static IRenderable createSingleLBDTileMesh(LBDTile tile, int x, int y, TMD tilesTmd, Shader shader, ITexture2D vram, bool headless) { TMDObject tileObj = tilesTmd.ObjectTable[tile.TileType]; IRenderable tileMesh = MeshFromTMDObject(tileObj, shader, headless); switch (tile.TileDirection) { case LBDTile.TileDirections.Deg90: { tileMesh.Transform.Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(90)); break; } case LBDTile.TileDirections.Deg180: { tileMesh.Transform.Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(180)); break; } case LBDTile.TileDirections.Deg270: { tileMesh.Transform.Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathHelper.DegreesToRadians(270)); break; } } tileMesh.Transform.Position = new Vector3(x, tile.TileHeight, y); tileMesh.Textures.Add(vram); return(tileMesh); }
public Mesh CreateTMDObjectMesh(TMDObject obj) { clearCachedLists(); foreach (var prim in obj.Primitives) { // currently only polygon primitives are supported if (prim.Type != TMDPrimitivePacket.Types.POLYGON) { continue; } // figure out which index list to use based on whether or not this primitive is alpha blended List <int> indicesList = (prim.Options & TMDPrimitivePacket.OptionsFlags.AlphaBlended) != 0 ? _alphaBlendIndices : _indices; // get interfaces for different packet types from LibLSD IPrimitivePacket primitivePacket = prim.PacketData; ITexturedPrimitivePacket texturedPrimitivePacket = prim.PacketData as ITexturedPrimitivePacket; IColoredPrimitivePacket coloredPrimitivePacket = prim.PacketData as IColoredPrimitivePacket; ILitPrimitivePacket litPrimitivePacket = prim.PacketData as ILitPrimitivePacket; for (int i = 0; i < primitivePacket.Vertices.Length; i++) { // get index into vertices array int vertIndex = primitivePacket.Vertices[i]; _packetIndices[i] = _verts.Count; // create variables for each of the vertex types Vec3 vertPos = obj.Vertices[vertIndex]; Vector3 vec3VertPos = new Vector3(vertPos.X, -vertPos.Y, vertPos.Z) / 2048f; Color32 vertCol = Color.white; Vector3 vertNorm = Vector3.zero; Vector2 vertUV = Vector2.one; // handle packet colour if (coloredPrimitivePacket != null) { Vec3 packetVertCol = coloredPrimitivePacket.Colors[coloredPrimitivePacket.Colors.Length > 1 ? i : 0]; vertCol = new Color(packetVertCol.X, packetVertCol.Y, packetVertCol.Z); if (vertCol.r > 0 && vertCol.g > 0 && vertCol.b > 0 && (prim.Options & TMDPrimitivePacket.OptionsFlags.Textured) == 0 && (prim.Options & TMDPrimitivePacket.OptionsFlags.AlphaBlended) != 0) { vertCol.a = 127; } } // handle packet normals if (litPrimitivePacket != null) { TMDNormal packetVertNorm = obj.Normals[litPrimitivePacket.Normals[litPrimitivePacket.Normals.Length > 1 ? i : 0]]; vertNorm = new Vector3(packetVertNorm.X, packetVertNorm.Y, packetVertNorm.Z); } // handle packet UVs if (texturedPrimitivePacket != null) { // calculate which texture page we're on int texPage = texturedPrimitivePacket.Texture.TexturePageNumber; int texPageXPos = ((texPage % 16) * 128) - 640; int texPageYPos = texPage < 16 ? 256 : 0; // derive UV coords from the texture page int uvIndex = i * 2; int vramXPos = texPageXPos + texturedPrimitivePacket.UVs[uvIndex]; int vramYPos = texPageYPos + (256 - texturedPrimitivePacket.UVs[uvIndex + 1]); float uCoord = vramXPos / (float)PsxVram.VRAM_WIDTH; float vCoord = vramYPos / (float)PsxVram.VRAM_HEIGHT; vertUV = new Vector2(uCoord, vCoord); // check for overlapping UVs and fix them slightly foreach (var uv in _uvs) { if (uv.Equals(vertUV)) { vertUV += new Vector2(0.0001f, 0.0001f); } } } // add all computed aspects of vertex to lists _verts.Add(vec3VertPos); _normals.Add(vertNorm); _colors.Add(vertCol); _uvs.Add(vertUV); } // we want to add extra indices if this primitive is a quad (to triangulate) bool isQuad = (prim.Options & TMDPrimitivePacket.OptionsFlags.Quad) != 0; _polyIndices.Add(_packetIndices[0]); _polyIndices.Add(_packetIndices[1]); _polyIndices.Add(_packetIndices[2]); if (isQuad) { _polyIndices.Add(_packetIndices[2]); _polyIndices.Add(_packetIndices[1]); _polyIndices.Add(_packetIndices[3]); } // if this primitive is double sided we want to add more vertices with opposite winding order if ((prim.Flags & TMDPrimitivePacket.PrimitiveFlags.DoubleSided) != 0) { _polyIndices.Add(_packetIndices[1]); _polyIndices.Add(_packetIndices[0]); _polyIndices.Add(_packetIndices[2]); if (isQuad) { _polyIndices.Add(_packetIndices[1]); _polyIndices.Add(_packetIndices[2]); _polyIndices.Add(_packetIndices[3]); } } // add the indices to the list indicesList.AddRange(_polyIndices); _polyIndices.Clear(); } Mesh result = new Mesh(); result.SetVertices(_verts); result.SetNormals(_normals); result.SetColors(_colors); result.SetUVs(0, _uvs); // regular mesh if (_indices.Count >= 3) { result.SetTriangles(_indices, 0, false, 0); } // alpha blended mesh if (_alphaBlendIndices.Count >= 3) { result.subMeshCount = 2; result.SetTriangles(_alphaBlendIndices, 1, false, 0); } return(result); }
public static IRenderable MeshFromTMDObject(TMDObject obj, Shader shader, bool headless) { Vec3[] verts = new Vec3[obj.NumVertices]; List <Vertex> vertList = new List <Vertex>(); List <int> indices = new List <int>(); for (int i = 0; i < obj.NumVertices; i++) { verts[i] = obj.Vertices[i] / 2048f; } foreach (var prim in obj.Primitives) { if (prim.Type != TMDPrimitivePacket.Types.POLYGON) { continue; } ITMDPrimitivePacket primitivePacket = prim.PacketData; ITMDTexturedPrimitivePacket texPrimitivePacket = prim.PacketData as ITMDTexturedPrimitivePacket; ITMDColoredPrimitivePacket colPrimitivePacket = prim.PacketData as ITMDColoredPrimitivePacket; ITMDLitPrimitivePacket litPrimitivePacket = prim.PacketData as ITMDLitPrimitivePacket; List <int> polyIndices = new List <int>(); int[] packetIndices = new int[primitivePacket.Vertices.Length]; for (int i = 0; i < primitivePacket.Vertices.Length; i++) { int vertIndex = primitivePacket.Vertices[i]; packetIndices[i] = vertList.Count; Vector3 vertPos = new Vector3(verts[vertIndex].X, verts[vertIndex].Y, verts[vertIndex].Z); Vector4 vertCol = Vector4.One; Vector3 vertNorm = Vector3.Zero; Vector2 vertUV = Vector2.One; // handle packet colour if (colPrimitivePacket != null) { Vec3 packetVertCol = colPrimitivePacket.Colors[colPrimitivePacket.Colors.Length > 1 ? i : 0]; vertCol = new Vector4(packetVertCol.X, packetVertCol.Y, packetVertCol.Z, 1f); } // handle packet normals if (litPrimitivePacket != null) { TMDNormal packetVertNorm = obj.Normals[ litPrimitivePacket.Normals[litPrimitivePacket.Normals.Length > 1 ? i : 0]]; vertNorm = new Vector3(packetVertNorm.X, packetVertNorm.Y, packetVertNorm.Z); } // handle packet UVs if (texPrimitivePacket != null) { int texPage = texPrimitivePacket.Texture.TexturePageNumber; // the PSX VRAM is split into 32 texture pages, 16 on top and 16 on bottom // a texture page is 128x256 pixels large // pages 0-4 and 16-20 are used as a double buffer, with a width of 640 (5*128) // the X position of the texture page is it's row index multiplied by the width // we're also subtracting the width of the double buffer as we don't want to include // it - we're using modern graphics so this will be on the video card! int texPageXPos = ((texPage % TEX_PAGE_PER_ROW) * TEX_PAGE_WIDTH) - DOUBLE_BUFFER_WIDTH; // more simply, the Y position of the texture page is 0 or the height of a texture page, based // on if the texture page number is on the 2nd row of pages or not int texPageYPos = texPage <= TEX_PAGE_PER_ROW ? TEX_PAGE_HEIGHT : 0; int uvIndex = i * 2; // 2 UVs per vertex // the UV information we get from the TMD model is UVs into a specific texture page, using // only that texture page's coordinate system // here, we're adding the texture page position offsets (into VRAM) to the TMD UVs, transforming // them into the VRAM coordinate space int vramXPos = texPageXPos + texPrimitivePacket.UVs[uvIndex]; // we're subtracting from the texture page height for the Y position as OpenGL uses flipped // Y coordinates for textures compared with the PSX int vramYPos = texPageYPos + (TEX_PAGE_HEIGHT - texPrimitivePacket.UVs[uvIndex + 1]); // finally, we're normalizing the UVs from pixels float uCoord = vramXPos / (float)VRAM_WIDTH; float vCoord = vramYPos / (float)VRAM_HEIGHT; vertUV = new Vector2(uCoord, vCoord); } vertList.Add(new Vertex(vertPos, vertNorm, vertUV, vertCol)); } bool isQuad = (prim.Options & TMDPrimitivePacket.OptionsFlags.Quad) != 0; polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[3]); } // if primitive is double sided poly we need to add other side with reverse winding if ((prim.Flags & TMDPrimitivePacket.PrimitiveFlags.DoubleSided) != 0) { polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[3]); } } indices.AddRange(polyIndices); } return(headless ? (IRenderable) new HeadlessMesh(vertList.ToArray(), indices.ToArray()) : (IRenderable) new Mesh(vertList.ToArray(), indices.ToArray(), shader)); }
// create a single LBD tile GameObject (not including extra tiles) private GameObject createSingleLBDTile(LBDTile tile, int x, int y, TMD tilesTmd, List <CombineInstance> meshesCreated, Dictionary <TMDObject, Mesh> cache) { // rotate the tile based on its direction Quaternion tileRot = Quaternion.identity; switch (tile.TileDirection) { case LBDTile.TileDirections.Deg90: { tileRot = Quaternion.AngleAxis(90, Vector3.up); break; } case LBDTile.TileDirections.Deg180: { tileRot = Quaternion.AngleAxis(180, Vector3.up); break; } case LBDTile.TileDirections.Deg270: { tileRot = Quaternion.AngleAxis(270, Vector3.up); break; } } // create the GameObject and add/setup necessary components GameObject lbdTile = LBDTilePool.Summon(new Vector3(x, -tile.TileHeight, y), tileRot); MeshFilter mf = lbdTile.GetComponent <MeshFilter>(); MeshRenderer mr = lbdTile.GetComponent <MeshRenderer>(); TMDObject tileObj = tilesTmd.ObjectTable[tile.TileType]; Mesh tileMesh; if (cache.ContainsKey(tileObj)) { tileMesh = cache[tileObj]; } else { tileMesh = LibLSDUnity.MeshFromTMDObject(tileObj); cache[tileObj] = tileMesh; } mf.sharedMesh = tileMesh; // the renderer needs to use virtual PSX Vram as its materials mr.sharedMaterials = new[] { LBDDiffuse, LBDAlpha }; // set the tile's height lbdTile.transform.position = new Vector3(x, -tile.TileHeight, y); // make a CombineInstance for combining all tiles into one mesh later on var localToWorldMatrix = lbdTile.transform.localToWorldMatrix; CombineInstance combine = new CombineInstance() { mesh = tileMesh, transform = localToWorldMatrix, subMeshIndex = 0 }; meshesCreated.Add(combine); // if tile has transparent part, do the same for the transparent mesh if (tileMesh.subMeshCount > 1) { CombineInstance combineTrans = new CombineInstance() { mesh = tileMesh, transform = localToWorldMatrix, subMeshIndex = 1 }; meshesCreated.Add(combineTrans); } return(lbdTile); }
public static IRenderable MeshFromTMDObject(TMDObject obj, Shader shader) { Vec3[] verts = new Vec3[obj.NumVertices]; List <Vertex> vertList = new List <Vertex>(); List <int> indices = new List <int>(); for (int i = 0; i < obj.NumVertices; i++) { verts[i] = obj.Vertices[i] / 2048f; } foreach (var prim in obj.Primitives) { if (prim.Type != TMDPrimitivePacket.Types.POLYGON) { continue; } ITMDPrimitivePacket primitivePacket = prim.PacketData; ITMDTexturedPrimitivePacket texPrimitivePacket = prim.PacketData as ITMDTexturedPrimitivePacket; ITMDColoredPrimitivePacket colPrimitivePacket = prim.PacketData as ITMDColoredPrimitivePacket; ITMDLitPrimitivePacket litPrimitivePacket = prim.PacketData as ITMDLitPrimitivePacket; List <int> polyIndices = new List <int>(); int[] packetIndices = new int[primitivePacket.Vertices.Length]; for (int i = 0; i < primitivePacket.Vertices.Length; i++) { int vertIndex = primitivePacket.Vertices[i]; packetIndices[i] = vertList.Count; Vector3 vertPos = new Vector3(verts[vertIndex].X, verts[vertIndex].Y, verts[vertIndex].Z); Vector4 vertCol = Vector4.One; Vector3 vertNorm = Vector3.Zero; Vector2 vertUV = Vector2.One; // handle packet colour if (colPrimitivePacket != null) { Vec3 packetVertCol = colPrimitivePacket.Colors[colPrimitivePacket.Colors.Length > 1 ? i : 0]; vertCol = new Vector4(packetVertCol.X, packetVertCol.Y, packetVertCol.Z, 1f); } // handle packet normals if (litPrimitivePacket != null) { TMDNormal packetVertNorm = obj.Normals[ litPrimitivePacket.Normals[litPrimitivePacket.Normals.Length > 1 ? i : 0]]; vertNorm = new Vector3(packetVertNorm.X, packetVertNorm.Y, packetVertNorm.Z); } // handle packet UVs if (texPrimitivePacket != null) { int texPage = texPrimitivePacket.Texture.TexturePageNumber; int texPageXPos = ((texPage % 16) * 128) - 640; int texPageYPos = texPage < 16 ? 256 : 0; int uvIndex = i * 2; int vramXPos = texPageXPos + texPrimitivePacket.UVs[uvIndex]; int vramYPos = texPageYPos + (256 - texPrimitivePacket.UVs[uvIndex + 1]); float uCoord = vramXPos / (float)VRAMController.VRAM_WIDTH; float vCoord = vramYPos / (float)VRAMController.VRAM_HEIGHT; vertUV = new Vector2(uCoord, vCoord); } vertList.Add(new Vertex(vertPos, vertNorm, vertUV, vertCol)); } bool isQuad = (prim.Options & TMDPrimitivePacket.OptionsFlags.Quad) != 0; polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[3]); } // if primitive is double sided poly we need to add other side with reverse winding if ((prim.Flags & TMDPrimitivePacket.PrimitiveFlags.DoubleSided) != 0) { polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[3]); } } indices.AddRange(polyIndices); } Mesh toReturn = new Mesh(vertList.ToArray(), indices.ToArray(), shader); return(toReturn); }
/// <summary> /// Create a mesh from an object stored inside a TMD model file. /// </summary> /// <param name="obj">The TMD object to create a mesh from.</param> /// <returns>The Mesh created from the object.</returns> public static Mesh MeshFromTMDObject(TMDObject obj) { Profiler.BeginSample("MeshFromTMDObject"); // create the mesh, and lists of vertices, normals, colors, uvs, and indices Mesh result = new Mesh(); List <Vector3> verts = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <Color32> colors = new List <Color32>(); List <Vector2> uvs = new List <Vector2>(); List <int> indices = new List <int>(); List <int> alphaBlendIndices = new List <int>(); // alpha blended polygons are stored in a submesh // TMD objects are built from 'primitives' foreach (var prim in obj.Primitives) { // currently only polygon primitives are supported if (prim.Type != TMDPrimitivePacket.Types.POLYGON) { continue; } // check which index list to use based on whether this primitive is alpha blended or not List <int> indicesList = (prim.Options & TMDPrimitivePacket.OptionsFlags.AlphaBlended) != 0 ? alphaBlendIndices : indices; // get interfaces for different packet types from LibLSD IPrimitivePacket primitivePacket = prim.PacketData; ITexturedPrimitivePacket texturedPrimitivePacket = prim.PacketData as ITexturedPrimitivePacket; IColoredPrimitivePacket coloredPrimitivePacket = prim.PacketData as IColoredPrimitivePacket; ILitPrimitivePacket litPrimitivePacket = prim.PacketData as ILitPrimitivePacket; // for each vertex in the primitive List <int> polyIndices = new List <int>(); int[] packetIndices = new int[primitivePacket.Vertices.Length]; for (int i = 0; i < primitivePacket.Vertices.Length; i++) { // get its index into the vertices array int vertIndex = primitivePacket.Vertices[i]; packetIndices[i] = verts.Count; // create variables for each of the vertex types Vec3 vertPos = obj.Vertices[vertIndex]; Vector3 vec3VertPos = new Vector3(vertPos.X, -vertPos.Y, vertPos.Z) / 2048f; Color32 vertCol = Color.white; Vector3 vertNorm = Vector3.zero; Vector2 vertUV = Vector2.one; // handle packet colour if (coloredPrimitivePacket != null) { Vec3 packetVertCol = coloredPrimitivePacket.Colors[coloredPrimitivePacket.Colors.Length > 1 ? i : 0]; vertCol = new Color(packetVertCol.X, packetVertCol.Y, packetVertCol.Z); if (vertCol.r > 0 && vertCol.g > 0 && vertCol.b > 0 && (prim.Options & TMDPrimitivePacket.OptionsFlags.Textured) == 0 && (prim.Options & TMDPrimitivePacket.OptionsFlags.AlphaBlended) != 0) { vertCol.a = 127; } } // handle packet normals if (litPrimitivePacket != null) { TMDNormal packetVertNorm = obj.Normals[litPrimitivePacket.Normals[litPrimitivePacket.Normals.Length > 1 ? i : 0]]; vertNorm = new Vector3(packetVertNorm.X, packetVertNorm.Y, packetVertNorm.Z); } // handle packet UVs if (texturedPrimitivePacket != null) { // calculate which texture page we're on int texPage = texturedPrimitivePacket.Texture.TexturePageNumber; int texPageXPos = ((texPage % 16) * 128) - 640; int texPageYPos = texPage < 16 ? 256 : 0; // derive UV coords from the texture page int uvIndex = i * 2; int vramXPos = texPageXPos + texturedPrimitivePacket.UVs[uvIndex]; int vramYPos = texPageYPos + (256 - texturedPrimitivePacket.UVs[uvIndex + 1]); float uCoord = vramXPos / (float)PsxVram.VRAM_WIDTH; float vCoord = vramYPos / (float)PsxVram.VRAM_HEIGHT; vertUV = new Vector2(uCoord, vCoord); // check for overlapping UVs and fix them slightly foreach (var uv in uvs) { if (uv.Equals(vertUV)) { vertUV += new Vector2(0.0001f, 0.0001f); } } } // add all computed aspects of vertex to lists verts.Add(vec3VertPos); normals.Add(vertNorm); colors.Add(vertCol); uvs.Add(vertUV); } // we want to add extra indices if this primitive is a quad (to triangulate) bool isQuad = (prim.Options & TMDPrimitivePacket.OptionsFlags.Quad) != 0; polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[3]); } // if this primitive is double sided we want to add more vertices with opposite winding order if ((prim.Flags & TMDPrimitivePacket.PrimitiveFlags.DoubleSided) != 0) { polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[0]); polyIndices.Add(packetIndices[2]); if (isQuad) { polyIndices.Add(packetIndices[1]); polyIndices.Add(packetIndices[2]); polyIndices.Add(packetIndices[3]); } } // add the indices to the list indicesList.AddRange(polyIndices); } // set the mesh arrays result.vertices = verts.ToArray(); result.normals = normals.ToArray(); result.colors32 = colors.ToArray(); result.uv = uvs.ToArray(); // regular mesh if (indices.Count >= 3) { result.SetTriangles(indices, 0, false, 0); } // alpha blended mesh if (alphaBlendIndices.Count >= 3) { result.subMeshCount = 2; result.SetTriangles(alphaBlendIndices, 1, false, 0); } Profiler.EndSample(); return(result); }
// create a single LBD tile GameObject (not including extra tiles) private static GameObject createSingleLBDTile(LBDTile tile, int x, int y, TMD tilesTmd, List <CombineInstance> meshesCreated) { // create the GameObject and add/setup necessary components GameObject lbdTile = new GameObject($"Tile {tile.TileType}"); MeshFilter mf = lbdTile.AddComponent <MeshFilter>(); MeshRenderer mr = lbdTile.AddComponent <MeshRenderer>(); lbdTile.AddComponent <CullMeshOnDistance>(); lbdTile.AddComponent <MeshFog>(); TMDObject tileObj = tilesTmd.ObjectTable[tile.TileType]; Mesh tileMesh = MeshFromTMDObject(tileObj); mf.mesh = tileMesh; // the renderer needs to use virtual PSX Vram as its materials mr.sharedMaterials = new[] { PsxVram.VramMaterial, PsxVram.VramAlphaBlendMaterial }; // rotate the tile based on its direction switch (tile.TileDirection) { case LBDTile.TileDirections.Deg90: { lbdTile.transform.Rotate(Vector3.up, 90); break; } case LBDTile.TileDirections.Deg180: { lbdTile.transform.Rotate(Vector3.up, 180); break; } case LBDTile.TileDirections.Deg270: { lbdTile.transform.Rotate(Vector3.up, 270); break; } } // set the tile's height lbdTile.transform.position = new Vector3(x, -tile.TileHeight, y); // make a CombineInstance for combining all tiles into one mesh later on var localToWorldMatrix = lbdTile.transform.localToWorldMatrix; CombineInstance combine = new CombineInstance() { mesh = tileMesh, transform = localToWorldMatrix, subMeshIndex = 0 }; meshesCreated.Add(combine); // if tile has transparent part, do the same for the transparent mesh if (tileMesh.subMeshCount > 1) { CombineInstance combineTrans = new CombineInstance() { mesh = tileMesh, transform = localToWorldMatrix, subMeshIndex = 1 }; meshesCreated.Add(combineTrans); } return(lbdTile); }