Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 03/25/2018 in all areas

  1. Irup

    .TDF Terrain format

    I could not find any documentation for it, so I took Xir's suggestion and I've mapped out the basics of the format, but most of it is still a mystery to me. Here's a testing script with my findings for anyone curious, or wanting to do more research. If anyone has any findings that could go into an editor, please post them. Overview: The terrain is divided into 1024 (32x32) tiles. There are 4 levels of detail, and each tile's surface consists of a grid of 17x17, 9x9, 5x5 and 3x3 vertices, with each grid popping in and out of view depending on how far you are from it. They can be edited separately. Each vertex consists of only a height, and 4 bytes, which determine things like lighting and texture blending. from struct import unpack infilepath =\ r'C:\Program Files (x86)\LEGO Media\LEGO Racers 2\game data\EDITOR GEN\TERRAIN\SANDY ISLAND\TERRDATA.TDF backup' outfilepath =\ r'C:\Program Files (x86)\LEGO Media\LEGO Racers 2\game data\EDITOR GEN\TERRAIN\SANDY ISLAND\TERRDATA.TDF' HEADER_BYTESPER = 0x4 HEADER_OFFSET = 0x0 HEADER_SIZE = 0x20 # 0 1 2 3 4 5 6 7 # ffffffff bb bb bb bb # | | | | | # height | | | texture blending? # | | light level????? # | flags # unknown????????????? #flags: # 0x01 0b00000001 = pass-through (all LOD meshes have this checked) # 0x02 0b00000010 # 0x04 0b00000100 # 0x08 0b00001000 # 0x10 0b00010000 # 0x20 0b00100000 # 0x40 0b01000000 # 0x80 0b10000000 VERTEX_LODS = 4 VERTEX_BYTESPER = 0x8 VERTEX_OFFSET = (0x20, 0x242020, 0x2e4020, 0x316020) VERTEX_SIZE = (0x242000, 0xa2000, 0x32000, 0x12000) # [VERTEX_BYTESPER * lod**2 * WORLD_TILE_LINE**2 for lod in TILE_VERTEX_LINE] VERTEX_LOD_BYTESPER = [x // 32**2 for x in VERTEX_SIZE] # unknown #UNKDA0_BYTESPER = 0xF8 # UNKDA0_SIZE / WORLD_TILE_TOTAL UNKDA0_OFFSET = 0x328020 # UNKDA0_SIZE = 0x3E000 # # unknown UNKDA1_BYTESPER = 0x120 # UNKDA1_SIZE / WORLD_TILE_TOTAL UNKDA1_OFFSET = 0x366020 # UNKDA1_SIZE = 0x48000 # # all integers, offsets into tiles from UNKDA1_OFFSET? the numbers are divisible by UNKDA1_BYTESPER UNKDA2_BYTESPER = 0x4 # UNKDA2_SIZE / WORLD_TILE_TOTAL UNKDA2_OFFSET = 0x3AE020 # UNKDA2_SIZE = 0x1000 # # all floats UNKDA3_BYTESPER = 0xC8 # UNKDA3_SIZE / WORLD_TILE_TOTAL UNKDA3_OFFSET = 0x3AF020 # UNKDA3_SIZE = 0x32000 # TILE_VERTEX_LINE = (17, 9, 5, 3) TILE_LENGTH = [x**2 * VERTEX_BYTESPER for x in TILE_VERTEX_LINE] WORLD_TILE_LINE = 32 WORLD_VERTEX_TOTAL = [x**2 * WORLD_TILE_LINE**2 for x in TILE_VERTEX_LINE] DISTANCE = 0.2 def blender_import(): #imports 300000 vertices, but doesn't work too well import bpy, bmesh f = open(infilepath, 'rb') T = TILE_VERTEX_LINE[0] terrain_bmesh = bmesh.new() for v in range(WORLD_VERTEX_TOTAL[0]): terrain_bmesh.verts.new(( #alien code, not safe for human brains DISTANCE * ((v % T) + T * ((v // (T**2)) % WORLD_TILE_LINE)), DISTANCE * (((v // T) % T) + T * (v // (T**2 * WORLD_TILE_LINE))), unpack('f', f.read(4))[0], )) f.read(4) terrain_mesh = bpy.data.meshes.new('terraintest mesh') terrain_bmesh.to_mesh(terrain_mesh) terrain_bmesh.free() terrain_object = bpy.data.objects.new('terraintest obj', terrain_mesh) bpy.context.scene.objects.link(terrain_object) def action(): def showheader(): f = open(infilepath, 'rb') f.seek(HEADER_OFFSET) signature = f.read(4).decode('ansi') print('Signature:',signature) header = [f.read(4) for num in range((HEADER_SIZE-4) // 4)] labels = ( 'Unknown 0 ', 'Unknown 1 ', 'Unknown 2 ', 'Add height', 'Unknown 3 ', 'Unknown 4 ', 'Unknown 5 ', ) for label, number in zip(labels, header): print('%s: %-10i %-11i %-8x' % ( label, unpack('I', number)[0], unpack('i', number)[0], unpack('I', number)[0] ), unpack('f', number)[0] ) f.close() def findpassthroughvert(): f = open(infilepath, 'rb') o = open(outfilepath, 'wb') f.seek(HEADER_OFFSET) o.write(f.read(HEADER_SIZE)) for lod in range(VERTEX_LODS): for tile in range(32**2): passthroughvertex = False for vertex in range(TILE_VERTEX_LINE[lod]**2): co = f.read(4) unk, flags, light, texblend = f.read(4) if not lod and flags & 1: passthroughvertex = (f.tell()-8, flags) o.write(co + bytes((0, 0 if passthroughvertex else flags, light, texblend))) if passthroughvertex: print('Found pass-through vertex in tile %i, flags: %2x offset: %x' % (tile, passthroughvertex[1], passthroughvertex[0])) o.write(f.read()) f.close() o.close() def clearflagseveryfifthtile(): f = open(infilepath, 'rb') o = open(outfilepath, 'wb') f.seek(HEADER_OFFSET) o.write(f.read(HEADER_SIZE)) for lod in range(VERTEX_LODS): for tile in range(32**2): if not tile % 5: for vertex in range(TILE_VERTEX_LINE[lod]**2): co = f.read(4) unk, flags, light, texblend = f.read(4) o.write(co + bytes((unk, 0, light, texblend))) else: o.write(f.read(TILE_LENGTH[lod])) f.close() o.close() def makeunkda1homogenous(every = None, copytile = 32*4+16): #can make tiles that crash the game if crossed, tiles that flicker textures, the results are not very predictable f = open(infilepath, 'rb') o = open(outfilepath, 'wb') f.seek(HEADER_OFFSET) o.write(f.read(HEADER_SIZE + sum(VERTEX_SIZE) + UNKDA0_SIZE)) f.seek(UNKDA1_OFFSET + UNKDA1_BYTESPER*copytile) tilesample = f.read(UNKDA1_BYTESPER) for tile in range(32**2): if every != None and not tile % every: o.write(tilesample) else: f.seek(UNKDA1_OFFSET + UNKDA1_BYTESPER*tile) o.write(f.read(UNKDA1_BYTESPER)) f.seek(UNKDA2_OFFSET) o.write(f.read()) f.close() o.close() makeunkda1homogenous(every = 5) input('Finished') action() Here I set all the vertex heights in every fifth tile to zero: Here's the very broken, but almost recognizable blender import: This weird thing happened by making every tile info structure (presumably, UNKDA1) homogenous to tile info 144. I've updated the script to be nicer and have more functions. All the invisible ground was solid.
    2 points
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.