package tk.captainsplexx.Resource.MESH; import java.io.File; import java.io.FileInputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import tk.captainsplexx.Game.Core; import tk.captainsplexx.Resource.FileHandler; import tk.captainsplexx.Resource.CAS.CasDataReader; import tk.captainsplexx.Resource.TOC.ConvertedSBpart; import tk.captainsplexx.Resource.TOC.ConvertedTocFile; import tk.captainsplexx.Resource.TOC.ResourceLink; import tk.captainsplexx.Resource.TOC.TocSBLink; public class MeshChunkLoader { public byte[] MeshBytes; public byte[] ChunkBytes; private int First_Data_Block_Offset = 0; private int Mesh_Object_Count = 0; private String ObjectFullName= ""; private int ObjectFullNameOffset = 0; private int Submesh_Mat_Name_Offset; private long FaceIndiceOffset=0; private int submesh_vert_count=0; private float Model_Scale = 4f; private String Submesh_Material_Name=""; private long Submesh_Face_Indice_Count = 0; private int Submesh_Face_Indice_lastOffset = 0; public int[] VB_Offset; public int[][] VB_Data_Offset; public int[] VB_Sizes; public int VB_Datas = 30; public int Increment_For_Vert_position; public int[] Indice_Positions; public int[] Indice_Count; public int[] Indice_CountFull; public int[] Vertices_Count; public String[] SubmeshNames; public float[] minCoords; public float[] maxCoords; public boolean loadFile(byte[] mesh, ConvertedSBpart convSBPart){ this.MeshBytes = mesh; this.ChunkBytes = null; String chunkID = FileHandler.bytesToHex(readByte(MeshBytes, 0xC8, 16)); for (ResourceLink chunk : convSBPart.getChunks()){ //System.out.println(chunk.getId()); if (chunk.getId().equalsIgnoreCase(chunkID)){ this.ChunkBytes = CasDataReader.readCas(chunk.getBaseSha1(), chunk.getDeltaSha1(), chunk.getSha1(), chunk.getCasPatchType()); System.out.println("Chunk successfully found!"); break; } } if (ChunkBytes==null){ for (ConvertedTocFile commonChunk : Core.getGame().getCommonChunks()){ for (TocSBLink chunk : commonChunk.getChunks()){ if (chunk.getGuid().equalsIgnoreCase(chunkID)){ this.ChunkBytes = CasDataReader.readCas(null, null, chunk.getSha1(), 0); System.out.println("Chunk successfully found!"); break; } } if (ChunkBytes!=null){ break; } } } if (ChunkBytes==null){ System.err.println("Chunk could not be found: "+chunkID); return false; } this.ObjectFullNameOffset = readShort(MeshBytes, 0x58); // 88 Bytes this.ObjectFullName = readString(MeshBytes,ObjectFullNameOffset); this.Mesh_Object_Count = readShort(MeshBytes, 0x78); // 120 Bytes //TEST OUT =) this.Mesh_Object_Count -= 1; this.First_Data_Block_Offset = readShort(MeshBytes, 0x7C); // 124 Bytes this.FaceIndiceOffset = readLong(MeshBytes, 0xC0); // 192 Bytes System.out.println("ObjectFullNameOffset "+ ObjectFullNameOffset); System.out.println("ObjectFullName "+ ObjectFullName); System.out.println("Mesh_Object_Count "+ Mesh_Object_Count); System.out.println("First_Data_Block_Offset "+ First_Data_Block_Offset); System.out.println("FaceIndiceOffset "+ FaceIndiceOffset); Indice_Positions = new int[Mesh_Object_Count]; Indice_Count = new int[Mesh_Object_Count]; Indice_CountFull = new int[Mesh_Object_Count]; SubmeshNames = new String[Mesh_Object_Count]; Vertices_Count = new int[Mesh_Object_Count]; VB_Offset = new int[Mesh_Object_Count]; VB_Sizes = new int[Mesh_Object_Count]; Increment_For_Vert_position = 0; Submesh_Face_Indice_lastOffset = (int) FaceIndiceOffset; VB_Data_Offset = new int[Mesh_Object_Count][]; for (int submesh=0; submesh<Mesh_Object_Count; submesh++){ VB_Offset[submesh] = Increment_For_Vert_position; System.out.println(submesh+" VB_Offset "+VB_Offset[submesh]); this.Submesh_Mat_Name_Offset = readShort(MeshBytes, First_Data_Block_Offset+(0xC0*submesh)+0x08); // +192 each submesh +8 bytes System.out.println(submesh+" Submesh_Mat_Name_Offset "+ Submesh_Mat_Name_Offset); this.Submesh_Material_Name = readString(MeshBytes, Submesh_Mat_Name_Offset); SubmeshNames[submesh] = Submesh_Material_Name; System.out.println(submesh+" Submesh_Material_Name "+ Submesh_Material_Name); this.submesh_vert_count = readShort(MeshBytes, First_Data_Block_Offset+(0xC0*submesh)+0x20); // +192 each submesh +32 bytes Vertices_Count[submesh] = submesh_vert_count; System.out.println(submesh+" submesh_vert_count "+ submesh_vert_count); VB_Sizes[submesh] = readShort(MeshBytes, First_Data_Block_Offset+(0xC0*submesh)+0x70); //+192 each submesh +112 bytes System.out.println(submesh+" VB_Size "+ VB_Sizes[submesh]); VB_Data_Offset[submesh] = new int[VB_Sizes[submesh]/2]; for (int i=0; i < VB_Data_Offset[submesh].length; i++){ VB_Data_Offset[submesh][i] = readShort(MeshBytes, First_Data_Block_Offset+(0xC0*submesh)+0x2E+(i*0x02)); // +192 each submesh +44 bytes (+2bytes for VB_Blocks) System.out.println(submesh+" VB_Data_Offset"+i+" "+VB_Data_Offset[submesh][i]); } //first represents the correct count, all >= 1 need to be subtracted from previous one. this.Submesh_Face_Indice_Count = readInt(MeshBytes, First_Data_Block_Offset+(0xC0*submesh)+0xD8); // +192 each submesh +216 bytes if (submesh >= 1){ Indice_CountFull[submesh] = (int) Submesh_Face_Indice_Count; Submesh_Face_Indice_Count -= Indice_CountFull[submesh-1]; }else{ Indice_CountFull[0] = (int) Submesh_Face_Indice_Count; } System.out.println(submesh+" Submesh_Face_Indice_Count "+ Submesh_Face_Indice_Count); //current Indice Offset this.Indice_Positions[submesh] = Submesh_Face_Indice_lastOffset; this.Indice_Count[submesh] = (int) Submesh_Face_Indice_Count; System.out.println(submesh+" Indice_Positions "+ Indice_Positions[submesh]); //Calculation next Indice offset. this.Submesh_Face_Indice_lastOffset += (Submesh_Face_Indice_Count*0x02); Increment_For_Vert_position += (VB_Sizes[submesh]*submesh_vert_count); } minCoords = new float[] {10000f, 10000f, 10000f}; maxCoords = new float[] {-10000f, -10000f, -10000f}; return true; } public int getSubMeshCount(){ return Mesh_Object_Count; } public String getName(){ return ObjectFullName; } //----------------------------- public int[] getIndices(int submesh){ int[] buffer = new int[getIndiceCount(submesh)]; for (int i=0; i<buffer.length; i++){ buffer[i] = readShort(ChunkBytes, Indice_Positions[submesh]+(i*0x02)); } return buffer; } public float[] getVertexPositions(int submesh){ float[] buffer = new float[Vertices_Count[submesh]*3]; if (VB_Data_Offset[submesh][1] == 769){ //FLOAT for (int i=0; i<buffer.length; i++){ buffer[i] = readFloat(ChunkBytes, (int) ((VB_Offset[submesh]+(VB_Sizes[submesh]*Math.floor(i/3))) + ((i%3)*0x04)))*Model_Scale; } }else{ //HALF-FLOAT for (int i=0; i<buffer.length; i++){ buffer[i] = readHalfFloat(ChunkBytes, (int) ((VB_Offset[submesh]+(VB_Sizes[submesh]*Math.floor(i/3))) + ((i%3)*0x02)))*Model_Scale; } } //<--axis aligned bounding box--> for (int i=0; i<buffer.length; i++){ float vertex = buffer[i]; int mod = i%3;//xyz if (vertex > maxCoords[mod]){ maxCoords[mod] = vertex; } if (vertex < minCoords[mod]){ minCoords[mod] = vertex; } } //System.out.println("Min: "+minCoords[0]+", "+minCoords[1]+", "+minCoords[2]+" Max: "+maxCoords[0]+", "+maxCoords[1]+", "+maxCoords[2]); return buffer; } public float[] getUVCoords(int submesh) { float[] coords = new float[Vertices_Count[submesh]*2]; for (int i1=0; i1<VB_Data_Offset[submesh].length;i1++){ if (VB_Data_Offset[submesh][i1]==1569){ for (int i2=0; i2<coords.length; i2++){ coords[i2] = readHalfFloat(ChunkBytes, (int) ((VB_Offset[submesh]+(VB_Sizes[submesh]*Math.floor(i2/2))) + ((i2%2)*0x02))+(VB_Data_Offset[submesh][i1+1])); } return coords; } } return coords; } //----------------------------- public String getSubMeshName(int submesh){ return SubmeshNames[submesh]; } public int getIndicesPositions(int submesh){ return Indice_Positions[submesh]; } public int getIndiceCount(int submesh) { return Indice_Count[submesh]; } //Inputstream Operations public int readShort(byte[] fileArray, int offset){ return ByteBuffer.wrap(readByte(fileArray, offset, 2)).order(ByteOrder.LITTLE_ENDIAN).getShort(); } public int readInt(byte[] fileArray, int offset){ return ByteBuffer.wrap(readByte(fileArray, offset, 4)).order(ByteOrder.LITTLE_ENDIAN).getInt(); } public float readHalfFloat(byte[] fileArray, int offset){ return convertHalfToFloat(ByteBuffer.wrap(readByte(fileArray, offset, 2)).order(ByteOrder.LITTLE_ENDIAN).getShort()); } public float readFloat(byte[] fileArray, int offset){ return ByteBuffer.wrap(readByte(fileArray, offset, 4)).order(ByteOrder.LITTLE_ENDIAN).getFloat(); } public long readLong(byte[] fileArray, int offset){ return ByteBuffer.wrap(readByte(fileArray, offset, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); } public String readString(byte[] fileArray, int offset){ String tmp = ""; for (int i=0; i < 1000; i++){ byte[] b = readByte(fileArray, offset+i, 1); if (b[0] != 0x0){ String str; try { str = new String(b, "UTF-8"); tmp += str; } catch (UnsupportedEncodingException e) { e.printStackTrace(); i = 1500; } }else{ i = 1500; } } return tmp; } public byte[] readByte(byte[] fileArray, int offset, int len){ byte[] buffer = new byte[len]; for (int i=0; i < len; i++){ buffer[i] = fileArray[offset+i]; } return buffer; } public byte[] readFile(String filepath){ try{ File file = new File(filepath); FileInputStream fin = new FileInputStream(file); byte fileContent[] = new byte[(int)file.length()]; fin.read(fileContent); fin.close(); return fileContent; }catch (Exception e){ System.err.println("could not read file: "+filepath); return null; } } public float convertHalfToFloat(short half) { switch ((int) half) { case 0x0000: return 0f; case 0x8000: return -0f; case 0x7c00: return Float.POSITIVE_INFINITY; case 0xfc00: return Float.NEGATIVE_INFINITY; default: return Float.intBitsToFloat(((half & 0x8000) << 16) | (((half & 0x7c00) + 0x1C000) << 13) | ((half & 0x03FF) << 13)); } } public float[] getMinCoords() { return minCoords; } public float[] getMaxCoords() { return maxCoords; } }