diff options
Diffstat (limited to 'extractor/gnd_format.gd')
-rw-r--r-- | extractor/gnd_format.gd | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/extractor/gnd_format.gd b/extractor/gnd_format.gd new file mode 100644 index 0000000..c49bce6 --- /dev/null +++ b/extractor/gnd_format.gd @@ -0,0 +1,431 @@ +class_name GNDFormat + + +const MAP_TILE_SIZE := 10.0 + + +## Byte Length: 4 [br] +## GRAT +var signature: String = "GRGN" + +## Byte Type: u8 [br] +## Byte Length: 2 +var version: Version + +## Byte Type: i32 [br] +## Byte Length: 4 +var width: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var height: int + +## Byte Type: f32 [br] +## Byte Length: 4 [br] +## Always 10. +var scale: float + +## Byte Type: i32 [br] +## Byte Length: 4 +var texture_count: int + +## Byte Type: i32 [br] +## Byte Length: 4 +## Always 80. +var texture_path_length: int + +## Byte Length: [member texture_count] * [member texture_path_length] [br] +var texture_paths: Array + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_slice_count: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_width: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_slice_height: int + +## Byte Type: i32 [br] +## Byte Length: 4 +var light_map_cells_per_grid: int + +## Length: [member light_map_slice_count] +var light_map_slices: Array + +## Byte Type: i32 [br] +## Byte Length: 4 +var surface_count: int + +## Length: [member surface_count] +var surfaces: Array[Surface] + +## Length: [member width] * [member height] +var ground_mesh_cubes: Array[GroundMeshCube] + + +static func from_bytes(bytes: ByteStream) -> GNDFormat: + var gnd_format = GNDFormat.new() + + bytes.advance(gnd_format.signature.length()) + + @warning_ignore("shadowed_variable") + var version = Version.new() + version.major = bytes.decode_u8() + version.minor = bytes.decode_u8() + gnd_format.version = version + + if version.minor > 7: + print(version) + return gnd_format + + gnd_format.width = bytes.decode_s32() + gnd_format.height = bytes.decode_s32() + gnd_format.scale = bytes.decode_float() + gnd_format.texture_count = bytes.decode_s32() + gnd_format.texture_path_length = bytes.decode_s32() + + gnd_format.texture_paths = [] + for _n in gnd_format.texture_count: + gnd_format.texture_paths.append( + bytes.get_string_from_ro(gnd_format.texture_path_length) + ) + + gnd_format.light_map_slice_count = bytes.decode_s32() + gnd_format.light_map_width = bytes.decode_s32() + gnd_format.light_map_slice_height = bytes.decode_s32() + gnd_format.light_map_cells_per_grid = bytes.decode_s32() + + #gnd_format.light_map_slices = [] + #for _n in gnd_format.light_map_slice_count: + #gnd_format.light_map_slices.append(LightMapSlice.from_bytes(bytes)) + bytes.advance( + gnd_format.light_map_slice_count * + gnd_format.light_map_width * + gnd_format.light_map_slice_height * + 4 + ) + + gnd_format.surface_count = bytes.decode_s32() + + gnd_format.surfaces = [] as Array[Surface] + for _n in gnd_format.surface_count: + gnd_format.surfaces.append(Surface.from_bytes(bytes)) + + gnd_format.ground_mesh_cubes = [] as Array[GroundMeshCube] + for _n in (gnd_format.width * gnd_format.height): + gnd_format.ground_mesh_cubes.append(GroundMeshCube.from_bytes(bytes)) + + #print(inst_to_dict(gnd_format)) + return gnd_format + + +func get_cubes() -> Array: + # TODO: return custom type with helper functions for getting neighboring cubes and building a full cube + + var cubes := [] + for cube in ground_mesh_cubes: + cubes.append({ + "cube": cube, + Vector3(0, 1, 0): { + "surface": surfaces[cube.upwards_facing_surface_index], + "mesh": surfaces[cube.upwards_facing_surface_index].get_mesh({ + Vector2(0, 0): Vector3(0, cube.top_left_height, 0), + Vector2(1, 0): Vector3(MAP_TILE_SIZE, cube.top_right_height, 0), + Vector2(1, 1): Vector3(MAP_TILE_SIZE, cube.bottom_right_height, MAP_TILE_SIZE), + Vector2(0, 1): Vector3(0, cube.bottom_left_height, MAP_TILE_SIZE), + }), + }, + }) + + return cubes + + +func convert(data_path: String) -> GridMap: + var grid_map := GridMap.new() + grid_map.cell_size = Vector3(GNDFormat.MAP_TILE_SIZE, GNDFormat.MAP_TILE_SIZE, GNDFormat.MAP_TILE_SIZE) + grid_map.cell_center_x = false + grid_map.cell_center_y = false + grid_map.cell_center_z = false + + grid_map.position = Vector3( + (width * MAP_TILE_SIZE) / 2.0, + 0, + -(height * MAP_TILE_SIZE) / 2.0, + ) + + var library := MeshLibrary.new() + grid_map.mesh_library = library + + var cubes := get_cubes() + + var cache := {} + # TODO: use texture_index and surface uvs as key + # TODO: for deduplication of cell items (as long as other sides aren't accounted for..) + + for x in width: + for y in height: + var cube = cubes[x + y * width] + + # TODO: get all sides of a cube and add that mesh to the library + # TODO: so a single mesh per cube + for surface_type in [Vector3(0, 1, 0)]: + var mesh: ArrayMesh = cube[surface_type].mesh + var material := StandardMaterial3D.new() + material.albedo_texture = load( + "%s/data/texture/%s" % [ + data_path, + texture_paths[cube[surface_type].surface.texture_index] + ] + ) + + mesh.surface_set_material(0, material) + + var id := library.get_last_unused_item_id() + library.create_item(id) + library.set_item_mesh(id, mesh) + + grid_map.set_cell_item(Vector3(-x, 0, y), id) + + return grid_map + + +class LightMapSlice: + ## Byte Type: i32 [br] + ## Byte Length: 4 + var pixel_format: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var width: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var height: int + + ## Byte Length: [member width] * [member height] + var shadow_map_pixels: ByteStream + + ## Byte Length: [member width] * [member height] + var light_map_pixels: ByteStream + + + static func from_bytes(bytes: ByteStream) -> LightMapSlice: + var slice = LightMapSlice.new() + + slice.pixel_format = bytes.decode_s32() + slice.width = bytes.decode_s32() + slice.height = bytes.decode_s32() + slice.shadow_map_pixels = bytes.get_buffer(slice.width * slice.height) + slice.light_map_pixels = bytes.get_buffer(slice.width * slice.height) + + #print(inst_to_dict(slice)) + return slice + + +class Surface: + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_bottom_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_bottom_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_top_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var u_top_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_bottom_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_bottom_right: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_top_left: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var v_top_right: float + + ## Byte Type: i16 [br] + ## Byte Length: 2 + var texture_index: int + + ## Byte Type: i16 [br] + ## Byte Length: 2 + var light_map_index: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_blue: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_green: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_red: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var vertex_color_alpha: int + + + func get_uvs() -> Dictionary[Vector2, Vector2]: + return { + Vector2(0, 0): Vector2(u_top_left, v_top_left), + Vector2(1, 0): Vector2(u_top_right, v_top_right), + Vector2(0, 1): Vector2(u_bottom_left, v_bottom_left), + Vector2(1, 1): Vector2(u_bottom_right, v_bottom_right), + } + + + func get_vertex_color() -> Color: + return Color8( + vertex_color_red, + vertex_color_green, + vertex_color_blue, + vertex_color_alpha + ) + + + func get_mesh(vertices: Dictionary[Vector2, Vector3]) -> ArrayMesh: + var surface_tool := SurfaceTool.new() + surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES) + + var uvs = get_uvs() + var color = get_vertex_color() + + surface_tool.add_triangle_fan( + PackedVector3Array([ + vertices[Vector2(0, 0)], + vertices[Vector2(1, 0)], + vertices[Vector2(1, 1)], + ]), + PackedVector2Array([ + uvs[Vector2(0, 0)], + uvs[Vector2(1, 0)], + uvs[Vector2(1, 1)], + ]), + PackedColorArray([ + color, color, color, + ]) + ) + surface_tool.add_triangle_fan( + PackedVector3Array([ + vertices[Vector2(0, 0)], + vertices[Vector2(1, 1)], + vertices[Vector2(0, 1)], + ]), + PackedVector2Array([ + uvs[Vector2(0, 0)], + uvs[Vector2(1, 1)], + uvs[Vector2(0, 1)], + ]), + PackedColorArray([ + color, color, color, + ]) + ) + + return surface_tool.commit() + + + func get_uv_id() -> String: + # TODO: add more data? + return ( + "%d %d %d %d %d %d %d %d" % [ + u_top_left, v_top_left, + u_top_right, v_top_right, + u_bottom_right, v_bottom_right, + u_bottom_left, v_bottom_left + ] + ).md5_text() + + + static func from_bytes(bytes: ByteStream) -> Surface: + var surface = Surface.new() + + surface.u_bottom_left = bytes.decode_float() + surface.u_bottom_right = bytes.decode_float() + surface.u_top_left = bytes.decode_float() + surface.u_top_right = bytes.decode_float() + surface.v_bottom_left = bytes.decode_float() + surface.v_bottom_right = bytes.decode_float() + surface.v_top_left = bytes.decode_float() + surface.v_top_right = bytes.decode_float() + surface.texture_index = bytes.decode_s16() + surface.light_map_index = bytes.decode_s16() + surface.vertex_color_blue = bytes.decode_u8() + surface.vertex_color_green = bytes.decode_u8() + surface.vertex_color_red = bytes.decode_u8() + surface.vertex_color_alpha = bytes.decode_u8() + + #print(inst_to_dict(surface)) + return surface + + +class GroundMeshCube: + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## upper left? + var bottom_left_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## upper right? + var bottom_right_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## lower left? + var top_left_height: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + ## lower right? + var top_right_height: float + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## top surface index? + var upwards_facing_surface_index: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## front surface index? + var northern_wall_surface_index: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + ## right surface index? + var eastern_wall_surface_index: int + + + static func from_bytes(bytes: ByteStream) -> GroundMeshCube: + var mesh = GroundMeshCube.new() + + mesh.bottom_left_height = bytes.decode_float() + mesh.bottom_right_height = bytes.decode_float() + mesh.top_left_height = bytes.decode_float() + mesh.top_right_height = bytes.decode_float() + mesh.upwards_facing_surface_index = bytes.decode_s32() + mesh.northern_wall_surface_index = bytes.decode_s32() + mesh.eastern_wall_surface_index = bytes.decode_s32() + + #print(inst_to_dict(mesh)) + return mesh + + # TODO: convert function to build cube mesh? or at least with neighbor input build full cube struct |