commit b6b55bd2a516a509fca373fb514a21755bcb2e94 Author: Luke Hubmayer-Werner Date: Tue Jul 25 14:21:10 2023 +0930 Haphazard progress diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d808304 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Do not include any ROMs +*.sfc +*.gba diff --git a/Node2D.gd b/Node2D.gd new file mode 100644 index 0000000..1d20ce8 --- /dev/null +++ b/Node2D.gd @@ -0,0 +1,54 @@ +extends Node2D + +var PC = load('PC.tscn') +var PCs = [] +var sfx_buttons = [] + +func _ready(): + Engine.set_target_fps(60) + var strips = len(load_sprites.strip_textures) # * 4 / 5 + #var strip_divide = strips * 2 / 5 + var strip_divide = strips * 1 / 5 + for i in strips: + PCs.append(PC.instance()) + #PCs[-1].set_position(Vector2((i%strip_divide)*16, (i/strip_divide)*24*11)) + PCs[-1].set_position(Vector2((i%strip_divide)*24, (i/strip_divide)*32)) + PCs[-1].material.set_shader_param('palette', load_sprites.character_battle_sprite_palette_textures[i]) + PCs[-1].texture = load_sprites.strip_textures[i] + + add_child(PCs[-1]) +# PCs.append(PC.instance()) +# PCs[-1].set_position(Vector2(0, 2*24*11)) +# PCs[-1].material.set_shader_param('palette', load_sprites.character_battle_sprite_palette_textures[45]) +# PCs[-1].texture = load_sprites.weapon_textures['Fist'] +# add_child(PCs[-1]) + + for i in SoundLoader.INST_NUM: + var btn = Button.new() + btn.text = 'Play #%02X' % i + btn.align = Button.ALIGN_CENTER + btn.set_position(Vector2((i%8)*40, 200 + (i/8)*16)) + btn.set_scale(Vector2(0.5, 0.5)) + add_child(btn) + btn.connect('pressed', SoundLoader, 'play_sample', [i]) + sfx_buttons.append(btn) + for i in SoundLoader.SFX_NUM: + var btn = Button.new() + btn.text = 'SFX #%02X' % i + btn.align = Button.ALIGN_CENTER + btn.set_position(Vector2((i%8)*40, 290 + (i/8)*16)) + btn.set_scale(Vector2(0.5, 0.5)) + add_child(btn) + btn.connect('pressed', SoundLoader, 'play_sfx', [i]) + sfx_buttons.append(btn) + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# update() +# pass + + +func _on_OptionButton_item_selected(ID): + for pc in PCs: + pc.animation = ID diff --git a/Node2D.tscn b/Node2D.tscn new file mode 100644 index 0000000..5805a0b --- /dev/null +++ b/Node2D.tscn @@ -0,0 +1,47 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://Node2D.gd" type="Script" id=2] + +[sub_resource type="Shader" id=1] +code = "shader_type canvas_item; +//uniform usampler2D tex; +uniform sampler2D palette; + +void fragment() { + //uint color_idx = textureLod(tex, UV, 0.0).r; + uint color_idx = uint(textureLod(TEXTURE, UV, 0.0).r * 256.0); + COLOR = texelFetch(palette, ivec2(int(color_idx), 0), 0); + if (color_idx == uint(0)) + COLOR.a = 0.0; +} +" + +[sub_resource type="ShaderMaterial" id=2] +shader = SubResource( 1 ) + +[node name="Control" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="Node2D" type="Node2D" parent="."] +material = SubResource( 2 ) +position = Vector2( 16, 16 ) +scale = Vector2( 2, 2 ) +script = ExtResource( 2 ) + +[node name="OptionButton" type="OptionButton" parent="Node2D"] +anchor_bottom = 0.67 +margin_top = 180.0 +margin_right = 100.0 +margin_bottom = 170.0 +rect_scale = Vector2( 0.5, 0.5 ) +text = "Stand" +items = [ "Stand", null, false, 0, null, "Guard", null, false, 1, null, "Walk", null, false, 2, null, "Down", null, false, 3, null, "R_Swing", null, false, 4, null, "L_Swing", null, false, 5, null, "Cheer", null, false, 6, null, "Recoil", null, false, 7, null, "Chant", null, false, 8, null ] +selected = 0 + +[node name="ColorRect" type="ColorRect" parent="."] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 + +[connection signal="item_selected" from="Node2D/OptionButton" to="Node2D" method="_on_OptionButton_item_selected"] diff --git a/PC.gd b/PC.gd new file mode 100644 index 0000000..11e65b4 --- /dev/null +++ b/PC.gd @@ -0,0 +1,59 @@ +extends Node2D + +enum Battle_Frame { + STAND, + GUARD, + WALK, + DOWN, + R_SWING, + L_SWING2, + L_SWING, + CHEER, + RECOIL, + CHANT1, + CHANT2, + } + +enum Battle_Anim { + STAND, + GUARD, + WALK, + DOWN, + R_SWING, + L_SWING, + CHEER, + RECOIL, + CHANT + } + +var texture: Texture +var animation = Battle_Anim.STAND # Battle_Anim enums aren't real types in GDscript :/ + +const Animation_Frames = { + Battle_Anim.STAND: [Battle_Frame.STAND], + Battle_Anim.GUARD: [Battle_Frame.GUARD], + Battle_Anim.WALK: [Battle_Frame.WALK, Battle_Frame.STAND], + Battle_Anim.DOWN: [Battle_Frame.DOWN], + Battle_Anim.R_SWING: [Battle_Frame.R_SWING, Battle_Frame.WALK], + Battle_Anim.L_SWING: [Battle_Frame.L_SWING, Battle_Frame.L_SWING2], + Battle_Anim.CHEER: [Battle_Frame.CHEER, Battle_Frame.STAND], + Battle_Anim.RECOIL: [Battle_Frame.RECOIL], + Battle_Anim.CHANT: [Battle_Frame.CHANT1, Battle_Frame.CHANT2] + } + +func _init(): + material = load_sprites.shader_material.duplicate() + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(_delta): + update() +# pass + +func _draw(): + var frames = Animation_Frames[animation] + var frame = frames[int(globals.time*4) % len(frames)] + var y = 0 + if frame == Battle_Frame.CHEER: + y -= 1 + #draw_texture(texture, Vector2(0, 0)) + draw_texture_rect_region(texture, Rect2(0, y, 16, 24), Rect2(0, 24*frame, 16, 24)) diff --git a/PC.tscn b/PC.tscn new file mode 100644 index 0000000..34f4d56 --- /dev/null +++ b/PC.tscn @@ -0,0 +1,7 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://PC.gd" type="Script" id=1] + +[node name="Node2D" type="Node2D"] +script = ExtResource( 1 ) + diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..20207a4 --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/globals.gd b/globals.gd new file mode 100644 index 0000000..301de4d --- /dev/null +++ b/globals.gd @@ -0,0 +1,10 @@ +extends Node + +var time = 0.0 +var time_mult = 1.0 + +func _ready(): + set_process(true) + +func _process(delta): + time += delta * time_mult diff --git a/load_sprites.gd b/load_sprites.gd new file mode 100644 index 0000000..5226521 --- /dev/null +++ b/load_sprites.gd @@ -0,0 +1,264 @@ +extends Node + +var shader_material = load('res://palette_mat.tres') + +var ROM_filename := 'FF5_SCC_WepTweaks_Inus_Dash.sfc' # 'Final Fantasy V (Japan).sfc' +const offset_Character_Battle_Sprite_Tiles: int = 0x120000 +const offset_Character_Battle_Sprite_Palettes: int = 0x14A3C0 +const offset_Character_Battle_Sprite_Layouts: int = 0x14B997 +const num_Character_Battle_Sprite_Layouts: int = 11 + +const offset_Character_Battle_Sprite_Disabled_Palette: int = 0x00F867 +const offset_Character_Battle_Sprite_Stone_Palette: int = 0x00F807 + +const offset_Tiles_Fist: int = 0x11D710 # 3bpp tile + +#var character_battle_sprite_tiles = [] +#var character_battle_sprite_palette_imgs = [] +var character_battle_sprite_palette_textures = [] +var character_battle_sprite_palette_disabled_texture: ImageTexture +var character_battle_sprite_palette_stone_texture: ImageTexture +var weapon_textures = {} + +func texture_from_image(image: Image, flags: int = 0) -> ImageTexture: + var tex = ImageTexture.new() + tex.create_from_image(image, flags) + tex.flags = Texture.FLAG_CONVERT_TO_LINEAR + return tex + +func bgr555_to_color(short: int) -> Color: + var color = Color() + color.a = 1 + color.r = ((short & 0x1F) / 31.0) + color.g = (((short >> 5) & 0x1F) / 31.0) + color.b = (((short >> 10) & 0x1F) / 31.0) + return color + +func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image: + rom.seek(offset) + var img := Image.new() + img.create(length, 1, false, Image.FORMAT_RGBF) + img.lock() + for i in range(length): + var color = bgr555_to_color(rom.get_16()) + img.set_pixel(i, 0, color) + img.unlock() + return img + +func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image: + # Implicit sRGB -> linear conversion on ImageTexture creation from RGB8 Image ruins this + rom.seek(offset) + var data = ByteArray(length*3) + # img.lock() + for i in length: + var color := bgr555_to_color(rom.get_16()) + var j: int = i*3 + data[j] = color.r8 + data[j+1] = color.g8 + data[j+2] = color.b8 + var img := Image.new() + img.create_from_data(length, 1, false, Image.FORMAT_RGB8, data) + return img + +func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image: + var data = ByteArray(length*2) + rom.seek(offset) + for i in range(length): + var bgr555 := rom.get_16() + var r := bgr555 & 0x1F + var g := (bgr555 >> 5) & 0x1F + var b := (bgr555 >> 10) & 0x1F + var rgb5_a1 := (r << 11) | (g << 6) | (b << 1) | 1 + data[i*2] = rgb5_a1 & 0xFF + data[(i*2)+1] = rgb5_a1 >> 8 + var img := Image.new() + img.create_from_data(length, 1, false, Image.FORMAT_RGBA5551, data) + return img + +func generate_palette(rom: File, offset: int, length: int = 16) -> Image: + return generate_palette_rgb5_a1(rom, offset, length) + +func ByteArray(size: int) -> PoolByteArray: + var arr = PoolByteArray() + arr.resize(size) + return arr + +func gba_4bpp_to_tile(data: PoolByteArray) -> Image: + var tdata := ByteArray(64) + for i in range(32): + tdata[i*2] = data[i] % 16 + tdata[i*2+1] = data[i] / 16 + var tile := Image.new() + tile.create_from_data(8, 8, false, Image.FORMAT_R8, tdata) + return tile + +func snes_4plane_to_tile(data: PoolByteArray) -> Image: + var tdata := ByteArray(64) + for i in range(64): + var j = (i/8)*2 + var x = 7 - (i%8) + tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[j+16] >> x & 1)<<2) | ((data[j+17] >> x & 1)<<3) + var tile := Image.new() + tile.create_from_data(8, 8, false, Image.FORMAT_R8, tdata) + return tile + +func snes_3plane_to_tile(data: PoolByteArray) -> Image: + var tdata := ByteArray(64) + for i in range(64): + var j = (i/8)*2 + var x = 7 - (i%8) + tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[(i/8)+16] >> x & 1)<<2) + var tile := Image.new() + tile.create_from_data(8, 8, false, Image.FORMAT_R8, tdata) + return tile + +func snes_2plane_to_tile(data: PoolByteArray) -> Image: + var tdata := ByteArray(64) + for i in range(64): + var j = (i/8)*2 + var x = 7 - (i%8) + tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) + var tile := Image.new() + tile.create_from_data(8, 8, false, Image.FORMAT_R8, tdata) + return tile + +func snes_1plane_to_tile(data: PoolByteArray) -> Image: + var tdata := ByteArray(64) + for i in range(64): + var x = 7 - (i%8) + tdata[i] = (data[i/8] >> x & 1) + var tile := Image.new() + tile.create_from_data(8, 8, false, Image.FORMAT_R8, tdata) + return tile + +func snes_get_tile(rom: File, offset: int, length: int) -> Image: + rom.seek(offset) + var data := rom.get_buffer(length) + var planes := length / 8 + match planes: + 4: + return snes_4plane_to_tile(data) + 3: + return snes_3plane_to_tile(data) + 2: + return snes_2plane_to_tile(data) + _: + return snes_1plane_to_tile(data) + + +func load_snes_rom(filename: String): + var rom := File.new() + var _error = rom.open(filename, File.READ) + + rom.seek(offset_Character_Battle_Sprite_Layouts) + var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6) + # Character Battle Sprite Tiles + for strip in range(0, 22*5): + var tiles = [] + for i in range(0, 32*48, 32): + tiles.append(snes_get_tile(rom, offset_Character_Battle_Sprite_Tiles + (strip*32*48) + i, 32)) + var strip_image = Image.new() + strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, Image.FORMAT_R8) + for i in range(6 * num_Character_Battle_Sprite_Layouts): + strip_image.blit_rect(tiles[battle_strip_layouts[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, (i/2) * 8)) + strip_images.append(strip_image) + strip_textures.append(texture_from_image(strip_image)) + + # Character Battle Sprite Palettes + for palette in range(0, 22*5): + character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Palettes + (palette*32)))) + character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette)) + character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette)) + + weapon_textures['Fist'] = texture_from_image(snes_get_tile(rom, offset_Tiles_Fist, 24)) + + SoundLoader.load_samples(rom) + + +func gba_LZ77_decompress(rom: File, address: int) -> PoolByteArray: + rom.seek(address) + var header := rom.get_32() + assert (header & 0x10 == 0x10) + var length := header >> 8 + var output := ByteArray(length) + var ptr := 0 + while ptr < length: + var bitmap := rom.get_8() + for i in range(8): + if (bitmap >> (7-i)) & 1: + # Buffer substitution + var h1 := rom.get_8() + var h2 := rom.get_8() + var copy_len := 3 + (h1 >> 4) + var copy_ptr := ptr - 1 - (((h1 & 0x0F)<<8) + h2) + for j in range(copy_len): + output[ptr] = output[copy_ptr] + copy_ptr += 1 + ptr += 1 + if ptr >= length: + return output + else: + # Literal byte + output[ptr] = rom.get_8() + ptr += 1 + if ptr >= length: + return output + return output + + +const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB' +const gba_marker_pos_US := 0x12FE10 +const gba_marker_pos_EU := 0x131294 +const gba_offset_battle_sprite_tiles := 504 +const gba_offset_battle_sprite_palettes := 1064 +const gba_offset_battle_sprite_layouts := 9435 +func load_gba_rom(filename: String): + var rom := File.new() + var offset_marker: int + var result = rom.open(filename, File.READ) + if result != 0: + print_debug('ROM failed to open: ', result) + return + + rom.seek(gba_marker_pos_US) + if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker: + offset_marker = gba_marker_pos_US + else: + rom.seek(gba_marker_pos_EU) + if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker: + offset_marker = gba_marker_pos_EU + else: + print_debug('ROM does not match known US/EU ROMs, aborting') + return + + # Battle Sprite Tiles + rom.seek(offset_marker + gba_offset_battle_sprite_layouts) + var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6) + for strip in range(0, 26*5): + var tiles = [] + rom.seek(offset_marker + gba_offset_battle_sprite_tiles + strip*4) + var address = rom.get_32() - 0x08000000 + var tiledata = gba_LZ77_decompress(rom, address) + tiledata.append(0) # Slicing to end of array causes an engine crash. For shame! + for i in range(8, 57*32+8, 32): + tiles.append(gba_4bpp_to_tile(tiledata.subarray(i, i+32))) + var strip_image = Image.new() + strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, Image.FORMAT_R8) + for i in range(6 * num_Character_Battle_Sprite_Layouts): + strip_image.blit_rect(tiles[battle_strip_layouts[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, (i/2) * 8)) + strip_images.append(strip_image) + strip_textures.append(texture_from_image(strip_image)) + + # Character Battle Sprite Palettes + for palette in range(0, 26*5): + rom.seek(offset_marker + gba_offset_battle_sprite_palettes + palette*4) + character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, rom.get_32() - 0x08000000 + 8))) +# character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette)) +# character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette)) + + +var strip_images = [] +var strip_textures = [] +func _ready(): + load_snes_rom(ROM_filename) + #load_gba_rom('2564 - Final Fantasy V Advance (U)(Independent).gba') diff --git a/palette_mat.tres b/palette_mat.tres new file mode 100644 index 0000000..899f2dc --- /dev/null +++ b/palette_mat.tres @@ -0,0 +1,6 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=2] + +[ext_resource path="res://shaders/palette_shader.tres" type="Shader" id=1] + +[resource] +shader = ExtResource( 1 ) diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..75c6e65 --- /dev/null +++ b/project.godot @@ -0,0 +1,39 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ +} + +[application] + +config/name="FF" +run/main_scene="res://Node2D.tscn" +config/icon="res://icon.png" + +[autoload] + +globals="*res://globals.gd" +SoundLoader="*res://scripts/sound_loader.gd" +load_sprites="*res://load_sprites.gd" + +[debug] + +gdscript/warnings/unused_variable=false +gdscript/warnings/integer_division=false + +[global] + +color=false + +[rendering] + +environment/default_clear_color=Color( 0, 0, 0.517647, 1 ) +environment/default_environment="res://default_env.tres" diff --git a/scripts/sound_loader.gd b/scripts/sound_loader.gd new file mode 100644 index 0000000..e4bbc43 --- /dev/null +++ b/scripts/sound_loader.gd @@ -0,0 +1,131 @@ +extends Node + +const MAX_15B = 1 << 15 +const MAX_16B = 1 << 16 + +func unsigned16_to_signed(unsigned): + return (unsigned + MAX_15B) % MAX_16B - MAX_15B + +func process_sample(mantissa: int, exponent: int) -> int: + # For filter arithmetic the samples need to be in signed form. + # Sign-extend + if mantissa >= 8: + mantissa |= 0xFFF0 + exponent = min(exponent, 12) + var unsigned = (mantissa << exponent) & 0xFFFF + return unsigned16_to_signed(unsigned) + +func decode_brr(data: PoolByteArray): + # Decodes a single 9byte BRR packet + var exponent := data[0] >> 4 + var filter_designation := (data[0] >> 2) & 0x03 + var loop := bool(data[0] & 0x02) + var end := bool(data[0] & 0x01) + var samples := [] + for i in range(1, 9): + var b := data[i] + samples.append(process_sample(b >> 4, exponent)) + samples.append(process_sample(b & 0x0F, exponent)) + return [samples, loop, end, filter_designation] + + +func clamp_short(i: int): + return min(max(i, -0x8000), 0x7FFF) + +func make_sample(rom: File, sample_rate: int) -> AudioStreamSample: + var audio := AudioStreamSample.new() + audio.mix_rate = sample_rate + audio.stereo = false + audio.set_format(AudioStreamSample.FORMAT_16_BITS) + + var size := rom.get_16() + if (size % 9) != 0: + print_debug('Oh no! An instrument sample has an invalid size of %d! at $%06X' % [size, rom.get_position()-2]) + return audio + + var samples = [0, 0] # Two zero samples for filter purposes, strip them from the actual output + for pkt in (size / 9): + var data = decode_brr(rom.get_buffer(9)) + samples.append_array(data[0]) + #var loop = data[1] + var end = data[2] + var filter = data[3] + match filter: + 1: + for i in range(samples.size()-8, samples.size()): + samples[i] = clamp_short(samples[i] + (samples[i-1]*15)/16) + 2: + for i in range(samples.size()-8, samples.size()): + samples[i] = clamp_short(samples[i] + (samples[i-1]*61)/32 - (samples[i-2]*15)/16) + 3: + for i in range(samples.size()-8, samples.size()): + samples[i] = clamp_short(samples[i] + (samples[i-1]*115)/64 - (samples[i-2]*13)/16) + if end: + break + var audio_data = PoolByteArray() + for i in range(2, samples.size()): + var b = samples[i] + audio_data.push_back(b & 0xFF) + audio_data.push_back(b >> 8) + audio.data = audio_data + return audio + +const INST_NUM := 35 +const INST_BRR_LOOKUP := 0x043C6F +const INST_SR := 0x043CD8 +const INST_LOOP := 0x043D1E +const INST_ADSR := 0x043D64 +const SFX_NUM := 8 +const SFX_BRR_START := 0x041E3F +const SFX_ADSR := 0x041F71 +const SFX_SR := 0x041F83 +var instrument_samples = [] +var sfx_samples = [] + +func get_inst_sample_data(rom: File, id: int) -> AudioStreamSample: + rom.seek(INST_SR + (id*2)) + var sample_rate := (rom.get_16() * 32000)/4096 + + var lookup_offset := INST_BRR_LOOKUP + (id*3) + rom.seek(lookup_offset) + var brr_offset := rom.get_16() + ((rom.get_8() & 0x3F) << 16) + rom.seek(brr_offset) + var sample := make_sample(rom, sample_rate) + #print_debug('Loaded sample instrument #%02X with lookup offset $%06X, BRR data offset $%06X and length $%04X (%f packets)' % [id, lookup_offset, brr_offset, size, size/9.0]) + return sample + +func load_sfx_samples_data(rom: File): + var sample_rates = [] + rom.seek(SFX_SR) + for i in SFX_NUM: + sample_rates.append((rom.get_16() * 32000)/4096) + rom.seek(SFX_BRR_START) + for i in SFX_NUM: + print('Loading sfx sample #%X with BRR data offset $%06X' % [i, rom.get_position()]) + sfx_samples.append(make_sample(rom, sample_rates[i])) + print('size of %d samples' % sfx_samples[i].data.size()) + + +# Called when the node enters the scene tree for the first time. +func load_samples(rom: File): + for i in INST_NUM: + instrument_samples.append(get_inst_sample_data(rom, i)) + load_sfx_samples_data(rom) + +var player := AudioStreamPlayer.new() # Make one for each channel, later +func play_sample(id: int): + print('Playing sample #%02X' % id) + player.stream = instrument_samples[id] + player.play() + +func play_sfx(id: int): + print('Playing sample #%02X' % id) + player.stream = sfx_samples[id] + player.play() + +func _ready() -> void: + add_child(player) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta: float) -> void: +# pass diff --git a/shaders/palette_shader.tres b/shaders/palette_shader.tres new file mode 100644 index 0000000..5a2441d --- /dev/null +++ b/shaders/palette_shader.tres @@ -0,0 +1,15 @@ +[gd_resource type="Shader" format=2] + +[resource] +code = "shader_type canvas_item; +//uniform usampler2D tex; +uniform sampler2D palette: hint_normal; + +void fragment() { + //uint color_idx = textureLod(tex, UV, 0.0).r; + uint color_idx = uint(textureLod(TEXTURE, UV, 0.0).r * 255.0); + COLOR = texelFetch(palette, ivec2(int(color_idx), 0), 0); + if (color_idx == uint(0)) + COLOR.a = 0.0; +} +"