private IImage ExtractEmbeddedDXT5A(byte[] bytes, HWBinaryResourceChunk chunk) { // Get raw embedded DXT5 texture from resource file var width = (int)Math.Sqrt(chunk.Size * 2); var height = width; // For some godforsaken reason, every pair of bytes is flipped so we need // to fix it here. This was really annoying to figure out, haha. for (var i = 0; i < chunk.Size; i += 2) { var offset = (int)chunk.Offset + i; var byte0 = bytes[offset + 0]; var byte1 = bytes[offset + 1]; bytes[offset + 0] = byte1; bytes[offset + 1] = byte0; } return(DxtDecoder.DecompressDxt5a(bytes, (int)chunk.Offset, width, height)); }
private IImage ExtractEmbeddedDXT1(byte[] bytes, HWBinaryResourceChunk chunk) { // Decompress DXT1 texture and turn it into a Bitmap var width = BinaryUtils.ReadInt32BigEndian(bytes, (int)chunk.Offset + 4); var height = BinaryUtils.ReadInt32BigEndian(bytes, (int)chunk.Offset + 8); return(DxtDecoder.DecompressDXT1(bytes, (int)chunk.Offset + 16, width, height)); }
protected override void Load(byte[] bytes) { uint headerSize = BinaryUtils.ReadUInt32BigEndian(bytes, 4); ushort numChunks = BinaryUtils.ReadUInt16BigEndian(bytes, 16); int chunkHeaderSize = 24; var chunks = new HWBinaryResourceChunk[numChunks]; for (int i = 0; i < chunks.Length; i++) { int offset = (int)headerSize + i * chunkHeaderSize; chunks[i] = new HWBinaryResourceChunk() { Type = ParseChunkType( BinaryUtils.ReadUInt64BigEndian(bytes, offset)), Offset = BinaryUtils.ReadUInt32BigEndian(bytes, offset + 8), Size = BinaryUtils.ReadUInt32BigEndian(bytes, offset + 12) }; } this.Chunks = chunks; }
private IModel ImportMesh(byte[] bytes) { int stride = 1; MeshNormalExportMode shadingMode = MeshNormalExportMode.Unchanged; HWBinaryResourceChunk headerChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_XTDHeader); float tileScale = BinaryUtils.ReadFloatBigEndian(bytes, (int)headerChunk.Offset + 12); HWBinaryResourceChunk atlasChunk = GetFirstChunkOfType(HWBinaryResourceChunkType.XTD_AtlasChunk); int gridSize = (int)Math.Round(Math.Sqrt((atlasChunk.Size - 32) / 8)); // Subtract the min/range vector sizes, divide by position + normal size, and sqrt for grid size int positionOffset = (int)atlasChunk.Offset + 32; int normalOffset = positionOffset + gridSize * gridSize * 4; if (gridSize % stride != 0) { throw new Exception( $"Grid size {gridSize} is not evenly divisible by stride {stride} - choose a different stride value."); } // These are stored as ZYX, 4 bytes per component Vector3 PosCompMin = BinaryUtils .ReadVector3BigEndian( bytes, (int)atlasChunk.Offset) .ReverseComponents(); Vector3 PosCompRange = BinaryUtils .ReadVector3BigEndian(bytes, (int)atlasChunk.Offset + 16) .ReverseComponents(); var finModel = new ModelImpl(gridSize * gridSize); var finMesh = finModel.Skin.AddMesh(); var finVertices = new IVertex[gridSize * gridSize]; // Read vertex offsets/normals and add them to the mesh for (int x = 0; x < gridSize; x += stride) { for (int z = 0; z < gridSize; z += stride) { int index = ConvertGridPositionToIndex(new Tuple <int, int>(x, z), gridSize); int offset = index * 4; // Get offset position and normal for this vertex Vector3 position = ReadVector3Compressed(bytes, positionOffset + offset) * PosCompRange - PosCompMin; // Positions are relative to the terrain grid, so shift them by the grid position position += new Vector3(x, 0, z) * tileScale; Vector3 normal = ConvertDirectionVector( Vector3.Normalize( ReadVector3Compressed(bytes, normalOffset + offset) * 2.0f - Vector3.One)); // Simple UV based on original, non-warped terrain grid Vector3 texCoord = new Vector3(x / ((float)gridSize - 1), z / ((float)gridSize - 1), 0); var finVertex = finModel.Skin .AddVertex(position.X, position.Y, position.Z) .SetLocalNormal(normal.X, normal.Y, normal.Z) .SetUv(texCoord.X, texCoord.Y); finVertices[GetVertexIndex(x, z, gridSize)] = finVertex; } } // Generate faces based on terrain grid for (int x = 0; x < gridSize - stride; x += stride) { var triangles = new List <(IVertex, IVertex, IVertex)>(); for (int z = 0; z < gridSize - stride; z += stride) { var a = finVertices[GetVertexIndex(x, z, gridSize)]; var b = finVertices[GetVertexIndex(x + stride, z, gridSize)]; var c = finVertices[GetVertexIndex(x, z + stride, gridSize)]; var d = finVertices[GetVertexIndex(x + stride, z + stride, gridSize)]; triangles.Add((a, b, c)); triangles.Add((d, c, b)); } finMesh.AddTriangles(triangles.ToArray()); triangles.Clear(); } return(finModel); }