diff --git a/Node2D.tscn b/Node2D.tscn index dabab40..3afd3ec 100644 --- a/Node2D.tscn +++ b/Node2D.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=5 format=2] [ext_resource path="res://palette_mat.tres" type="Material" id=1] [ext_resource path="res://Node2D.gd" type="Script" id=2] [ext_resource path="res://test/audio_system.tscn" type="PackedScene" id=3] +[ext_resource path="res://test/worldmap_system.tscn" type="PackedScene" id=4] [node name="Control" type="Control"] anchor_right = 1.0 @@ -27,4 +28,6 @@ selected = 0 [node name="audio_system" parent="." instance=ExtResource( 3 )] position = Vector2( 0, 400 ) +[node name="worldmap_system" parent="." instance=ExtResource( 4 )] + [connection signal="item_selected" from="Node2D/OptionButton" to="Node2D" method="_on_OptionButton_item_selected"] diff --git a/scripts/loaders/map_loader.gd b/scripts/loaders/map_loader.gd index a0627f0..910d343 100644 --- a/scripts/loaders/map_loader.gd +++ b/scripts/loaders/map_loader.gd @@ -76,21 +76,20 @@ class WorldMap: func get_block_pathing_flags(id: int) -> int: return block_pathing[id] - func make_tile_atlas() -> Image: + func make_tile_map() -> Image: var image := Image.new() var data := PoolByteArray() data.resize(tile_width*tile_height) - var block_tile := 0 - for y_off in range(0, tile_height*tile_width, tile_width): + var block_idx := 0 + for y_off in range(0, tile_height*tile_width, tile_width*2): for x_off in range(0, tile_width, 2): - data[y_off + x_off] = self.block_tile_ids[block_tile] - block_tile += 1 - data[y_off + x_off + 1] = self.block_tile_ids[block_tile] - block_tile += 1 - data[y_off + tile_width + x_off] = self.block_tile_ids[block_tile] - block_tile += 1 - data[y_off + tile_width + x_off + 1] = self.block_tile_ids[block_tile] - block_tile += 1 + var tile_offset = self.blockmap[block_idx] * 4 + block_idx += 1 + data[y_off + x_off] = self.block_tile_ids[tile_offset] + data[y_off + x_off + 1] = self.block_tile_ids[tile_offset+1] + data[y_off + tile_width + x_off] = self.block_tile_ids[tile_offset+2] + data[y_off + tile_width + x_off + 1] = self.block_tile_ids[tile_offset+3] + image.create_from_data(tile_width, tile_height, false, Image.FORMAT_R8, data) return image var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new()] @@ -131,22 +130,26 @@ func load_worldmaps(rom: File): # Repeated blocks along a row are run-length-encoded (RLE) # Certain blocks (mountains) expand to 1x3 var blockmap = PoolByteArray() - blockmap.resize(WorldMap.block_height * WorldMap.block_width) + # blockmap.resize(WorldMap.block_height * WorldMap.block_width) # Try this later if performance is a problem for chunk_id in range(worldmap_id*0x100, (worldmap_id+1)*0x100): rom.seek(chunk_addresses[chunk_id]) - while blockmap.size() < 256: + var chunk_size := 0 + while chunk_size < 256: var b := rom.get_8() if b >= 0xC0: # RLE var count := b-0xBF var block = rom.get_8() for i in count: blockmap.append(block) + chunk_size += count else: blockmap.append(b) + chunk_size += 1 if b == 0x0C or b == 0x1C or b == 0x2C: # Mountain blocks expand to a 1x3 blockmap.append(b+1) blockmap.append(b+2) + chunk_size += 2 worldmaps[worldmap_id].blockmap = blockmap func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array): diff --git a/scripts/loaders/sprite_loader.gd b/scripts/loaders/sprite_loader.gd index f058952..64f0d21 100644 --- a/scripts/loaders/sprite_loader.gd +++ b/scripts/loaders/sprite_loader.gd @@ -198,11 +198,11 @@ func snes_get_tile(rom: File, offset: int, length: int) -> Image: func make_tile_atlas(tile_images) -> Image: var r := Rect2(0, 0, 8, 8) var image = Image.new() - image.create(256, 256, false, Image.FORMAT_R8) + image.create(128, 128, false, Image.FORMAT_R8) var tile = 0 for y in tile_images.size()/16: for x in 16: - image.blit_rect(tile_images[tile], r, Vector2(x, y)*8) + image.blit_rect(tile_images[tile], r, Vector2(x*8, y*8)) tile += 1 return image diff --git a/shaders/worldmap_shader.gdshader b/shaders/worldmap_shader.gdshader index b9eaa59..7e34eaf 100644 --- a/shaders/worldmap_shader.gdshader +++ b/shaders/worldmap_shader.gdshader @@ -1,8 +1,8 @@ shader_type canvas_item; -uniform sampler2D tiles; -uniform sampler2D palette; +uniform sampler2D tile_atlas : hint_normal; +uniform sampler2D palette : hint_normal; // uniform float tile_width = 8.0; -uniform float tilemap_width = 256.0; // Require square tilemap for now +uniform float tilemap_width = 512.0; // Require square tilemap for now const float index_scale = 255.0 / 16.0; @@ -10,19 +10,38 @@ const float index_scale = 255.0 / 16.0; // tiles hardcoded to 16x16 tiles for now // palette hardcoded to 16x16 colors for now -void fragment() { - // GLES2 - float tile_idx = texture(TEXTURE, UV).a * index_scale; - vec2 tile_uv = vec2(fract(tile_idx), trunc(tile_idx) / 16.0); - vec2 sub_tile_uv = fract(UV / tilemap_width); +vec2 get_tile_atlas_uv(float tile_id, vec2 uv) { + float tile_idx16 = tile_id * index_scale; // Rescale from [0.0, 1.0] to [0, 255] to [0, 15.9375 (15+15/16)] + float tile_row = trunc(tile_idx16) / 16.0; + float tile_col = fract(tile_idx16); + vec2 tile_uv = vec2(tile_col, tile_row); // Convert 15.9375 to vec2(0.9375==15/16, (15)/16), this should result in integer coordinates in [0,15] scaled to [0,15/16] for UV + vec2 sub_tile_uv = fract(uv * tilemap_width); // TODO: add sea HScroll, waterfall VScroll tile UV modulation + return tile_uv + (sub_tile_uv/16.0); +} + +void fragment() { + // GLES2 + float s = texture(TEXTURE, UV).a; + // float tile_idx = texture(TEXTURE, UV).a * index_scale; // Rescale from [0.0, 1.0] to [0, 255] to [0, 15.9375 (15+15/16)] + // float tile_row = trunc(tile_idx) / 16.0; + // float tile_col = fract(tile_idx); + // vec2 tile_uv = vec2(tile_col, tile_row); // Convert 15.9375 to vec2(0.9375==15/16, (15)/16), this should result in integer coordinates in [0,15] scaled to [0,15/16] for UV + // vec2 sub_tile_uv = vec2(0.5, 0.5); //fract(UV * tilemap_width); + // TODO: move cycling palette to a sampler2DArray or sampler3D rather than rebinding - float color_idx = texture(tiles, tile_uv + (sub_tile_uv/16.0)).a * index_scale; - float palette_uv = vec2(fract(color_idx), trunc(color_idx) / 16.0); + vec2 lut_uv = get_tile_atlas_uv(s, UV); + // vec2 lut_uv = tile_uv; + float color_id = texture(tile_atlas, lut_uv).a; + float color_idx16 = color_id * index_scale; + float pal_row = trunc(color_idx16) / 16.0; + float pal_col = fract(color_idx16); + vec2 palette_uv = vec2(pal_col, pal_row); COLOR = texture(palette, palette_uv); - if (color_idx == 0.0) - COLOR.a = 0.0; + // if (color_idx16 == 0.0) + // COLOR.a = 0.0; + COLOR.a = 1.0; } diff --git a/test/worldmap_system.gd b/test/worldmap_system.gd new file mode 100644 index 0000000..8444260 --- /dev/null +++ b/test/worldmap_system.gd @@ -0,0 +1,61 @@ +extends Node2D + +var pal_shader_mat := preload('res://palette_mat.tres') +var worldmap_shader_mat := preload('res://worldmap_palette_mat.tres') + +var map_images := [] +var map_textures := [] +var map_texrects := [] + +func _create_palette_and_atlas_texrects() -> void: + for tileset in 3: + # Debug the Atlases + var texrect = TextureRect.new() + texrect.rect_position.x = tileset * 256 * 2 + texrect.rect_scale *= 16 + texrect.texture = SpriteLoader.worldmap_palette_textures[tileset] + map_texrects.append(texrect) + add_child(texrect) + texrect = TextureRect.new() + texrect.rect_position.x = tileset * 256 * 2 + texrect.rect_position.y = 256 + texrect.rect_scale *= 4 + texrect.texture = SpriteLoader.worldmap_tile_atlas_textures[tileset] + texrect.material = pal_shader_mat.duplicate() + texrect.material.set_shader_param('palette', SpriteLoader.worldmap_palette_textures[tileset]) + map_texrects.append(texrect) + add_child(texrect) + texrect = TextureRect.new() + texrect.rect_position.x = tileset * 256 * 2 + texrect.rect_position.y = 768 + texrect.rect_scale *= 4 + texrect.texture = SpriteLoader.worldmap_tile_atlas_textures[tileset] + map_texrects.append(texrect) + add_child(texrect) + +func _create_worldmap_texrects() -> void: + var map_tilesets = [0, 1, 0, 2, 2] + for i in 1: + var tileset = map_tilesets[i] + var image = MapLoader.worldmaps[i].make_tile_map() + map_images.append(image) + var tex := SpriteLoader.texture_from_image(image) + map_textures.append(tex) + var texrect = TextureRect.new() + texrect.rect_scale *= 8 #8 # Needs to have at least 1 pixel per tile pixel + texrect.texture = tex + texrect.material = worldmap_shader_mat.duplicate() + texrect.material.set_shader_param('palette', SpriteLoader.worldmap_palette_textures[tileset]) + texrect.material.set_shader_param('tile_atlas', SpriteLoader.worldmap_tile_atlas_textures[tileset]) + map_texrects.append(texrect) + add_child(texrect) + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + # Only create this after MapLoader and SpriteLoader have loaded! + _create_worldmap_texrects() + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta: float) -> void: +# pass diff --git a/test/worldmap_system.tscn b/test/worldmap_system.tscn new file mode 100644 index 0000000..05b7f2d --- /dev/null +++ b/test/worldmap_system.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://test/worldmap_system.gd" type="Script" id=1] + +[node name="worldmap_system" type="Node2D"] +script = ExtResource( 1 ) diff --git a/worldmap_palette_mat.tres b/worldmap_palette_mat.tres new file mode 100644 index 0000000..2beb8e9 --- /dev/null +++ b/worldmap_palette_mat.tres @@ -0,0 +1,7 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=2] + +[ext_resource path="res://shaders/worldmap_shader.gdshader" type="Shader" id=1] + +[resource] +shader = ExtResource( 1 ) +shader_param/tilemap_width = 256.0