summaryrefslogtreecommitdiff
path: root/extractor/gnd_format.gd
diff options
context:
space:
mode:
Diffstat (limited to 'extractor/gnd_format.gd')
-rw-r--r--extractor/gnd_format.gd431
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