[WIP] Worldmap viewing - interesting aesthetic with missing tiles

Looks a bit like the worldmap shrink
This commit is contained in:
Luke Hubmayer-Werner 2023-07-27 20:25:26 +09:30
parent a7b3377df6
commit 36dc124edb
7 changed files with 127 additions and 28 deletions

View File

@ -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://palette_mat.tres" type="Material" id=1]
[ext_resource path="res://Node2D.gd" type="Script" id=2] [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/audio_system.tscn" type="PackedScene" id=3]
[ext_resource path="res://test/worldmap_system.tscn" type="PackedScene" id=4]
[node name="Control" type="Control"] [node name="Control" type="Control"]
anchor_right = 1.0 anchor_right = 1.0
@ -27,4 +28,6 @@ selected = 0
[node name="audio_system" parent="." instance=ExtResource( 3 )] [node name="audio_system" parent="." instance=ExtResource( 3 )]
position = Vector2( 0, 400 ) 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"] [connection signal="item_selected" from="Node2D/OptionButton" to="Node2D" method="_on_OptionButton_item_selected"]

View File

@ -76,21 +76,20 @@ class WorldMap:
func get_block_pathing_flags(id: int) -> int: func get_block_pathing_flags(id: int) -> int:
return block_pathing[id] return block_pathing[id]
func make_tile_atlas() -> Image: func make_tile_map() -> Image:
var image := Image.new() var image := Image.new()
var data := PoolByteArray() var data := PoolByteArray()
data.resize(tile_width*tile_height) data.resize(tile_width*tile_height)
var block_tile := 0 var block_idx := 0
for y_off in range(0, tile_height*tile_width, tile_width): for y_off in range(0, tile_height*tile_width, tile_width*2):
for x_off in range(0, tile_width, 2): for x_off in range(0, tile_width, 2):
data[y_off + x_off] = self.block_tile_ids[block_tile] var tile_offset = self.blockmap[block_idx] * 4
block_tile += 1 block_idx += 1
data[y_off + x_off + 1] = self.block_tile_ids[block_tile] data[y_off + x_off] = self.block_tile_ids[tile_offset]
block_tile += 1 data[y_off + x_off + 1] = self.block_tile_ids[tile_offset+1]
data[y_off + tile_width + x_off] = self.block_tile_ids[block_tile] data[y_off + tile_width + x_off] = self.block_tile_ids[tile_offset+2]
block_tile += 1 data[y_off + tile_width + x_off + 1] = self.block_tile_ids[tile_offset+3]
data[y_off + tile_width + x_off + 1] = self.block_tile_ids[block_tile] image.create_from_data(tile_width, tile_height, false, Image.FORMAT_R8, data)
block_tile += 1
return image return image
var worldmaps = [WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new(), WorldMap.new()] 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) # Repeated blocks along a row are run-length-encoded (RLE)
# Certain blocks (mountains) expand to 1x3 # Certain blocks (mountains) expand to 1x3
var blockmap = PoolByteArray() 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): for chunk_id in range(worldmap_id*0x100, (worldmap_id+1)*0x100):
rom.seek(chunk_addresses[chunk_id]) rom.seek(chunk_addresses[chunk_id])
while blockmap.size() < 256: var chunk_size := 0
while chunk_size < 256:
var b := rom.get_8() var b := rom.get_8()
if b >= 0xC0: # RLE if b >= 0xC0: # RLE
var count := b-0xBF var count := b-0xBF
var block = rom.get_8() var block = rom.get_8()
for i in count: for i in count:
blockmap.append(block) blockmap.append(block)
chunk_size += count
else: else:
blockmap.append(b) blockmap.append(b)
chunk_size += 1
if b == 0x0C or b == 0x1C or b == 0x2C: if b == 0x0C or b == 0x1C or b == 0x2C:
# Mountain blocks expand to a 1x3 # Mountain blocks expand to a 1x3
blockmap.append(b+1) blockmap.append(b+1)
blockmap.append(b+2) blockmap.append(b+2)
chunk_size += 2
worldmaps[worldmap_id].blockmap = blockmap worldmaps[worldmap_id].blockmap = blockmap
func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array): func update_worldmap_block_tile_ids(worldmap_block_tile_ids: Array):

View File

@ -198,11 +198,11 @@ func snes_get_tile(rom: File, offset: int, length: int) -> Image:
func make_tile_atlas(tile_images) -> Image: func make_tile_atlas(tile_images) -> Image:
var r := Rect2(0, 0, 8, 8) var r := Rect2(0, 0, 8, 8)
var image = Image.new() var image = Image.new()
image.create(256, 256, false, Image.FORMAT_R8) image.create(128, 128, false, Image.FORMAT_R8)
var tile = 0 var tile = 0
for y in tile_images.size()/16: for y in tile_images.size()/16:
for x in 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 tile += 1
return image return image

View File

@ -1,8 +1,8 @@
shader_type canvas_item; shader_type canvas_item;
uniform sampler2D tiles; uniform sampler2D tile_atlas : hint_normal;
uniform sampler2D palette; uniform sampler2D palette : hint_normal;
// uniform float tile_width = 8.0; // 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; 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 // tiles hardcoded to 16x16 tiles for now
// palette hardcoded to 16x16 colors for now // palette hardcoded to 16x16 colors for now
void fragment() { vec2 get_tile_atlas_uv(float tile_id, vec2 uv) {
// GLES2 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_idx = texture(TEXTURE, UV).a * index_scale; float tile_row = trunc(tile_idx16) / 16.0;
vec2 tile_uv = vec2(fract(tile_idx), trunc(tile_idx) / 16.0); float tile_col = fract(tile_idx16);
vec2 sub_tile_uv = fract(UV / tilemap_width); 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 // 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 // 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; vec2 lut_uv = get_tile_atlas_uv(s, UV);
float palette_uv = vec2(fract(color_idx), trunc(color_idx) / 16.0); // 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); COLOR = texture(palette, palette_uv);
if (color_idx == 0.0) // if (color_idx16 == 0.0)
COLOR.a = 0.0; // COLOR.a = 0.0;
COLOR.a = 1.0;
} }

61
test/worldmap_system.gd Normal file
View File

@ -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

View File

@ -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 )

View File

@ -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