diff options
| author | Amir Rajan <[email protected]> | 2020-11-13 01:29:16 -0600 |
|---|---|---|
| committer | Amir Rajan <[email protected]> | 2020-11-13 01:29:16 -0600 |
| commit | 128fa1d90cea6289605a49daf56a0cbb72e2dd28 (patch) | |
| tree | 5cfdb499d275e2b43075e4d6a076365fc58ff0f7 /samples | |
| parent | 05cbef7fb8224332795e5685be499d81d20e7d93 (diff) | |
| download | dragonruby-game-toolkit-contrib-128fa1d90cea6289605a49daf56a0cbb72e2dd28.tar.gz dragonruby-game-toolkit-contrib-128fa1d90cea6289605a49daf56a0cbb72e2dd28.zip | |
synced from DRGTK 1.27
Diffstat (limited to 'samples')
19 files changed, 666 insertions, 2 deletions
diff --git a/samples/01_rendering_basics/06_audio_mixer/app/main.rb b/samples/01_rendering_basics/06_audio_mixer/app/main.rb index a09c7f1..ce48d3f 100644 --- a/samples/01_rendering_basics/06_audio_mixer/app/main.rb +++ b/samples/01_rendering_basics/06_audio_mixer/app/main.rb @@ -93,6 +93,9 @@ def render_panel args end def spawn_new_sound args, num + input = nil + input = "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}" + # Spawn randomly in an area that won't be covered by UI. screenx = (rand * 600.0) + 200.0 screeny = (rand * 400.0) + 100.0 @@ -102,7 +105,7 @@ def spawn_new_sound args, num # you can hang anything on the audio hashes you want, so we store the # actual screen position in here for convenience. args.audio[args.state.next_sound_index] = { - filename: "sounds/#{num}.#{(num == 6) ? 'ogg' : 'wav'}", + input: input, screenx: screenx, screeny: screeny, x: ((screenx / 1279.0) * 2.0) - 1.0, # scale to -1.0 - 1.0 range @@ -155,4 +158,3 @@ def tick args render_sources args render_ui args end - diff --git a/samples/01_rendering_basics/07_sound_synthesis/app/main.rb b/samples/01_rendering_basics/07_sound_synthesis/app/main.rb new file mode 100644 index 0000000..fc58126 --- /dev/null +++ b/samples/01_rendering_basics/07_sound_synthesis/app/main.rb @@ -0,0 +1,356 @@ +def tick args + defaults args + render args + input args + process_audio_queue args +end + +def defaults args + args.state.sine_waves ||= {} + args.state.audio_queue ||= [] + args.state.buttons ||= [ + (frequency_buttons args), + (note_buttons args), + (bell_buttons args) + ].flatten +end + +def frequency_buttons args + [ + (button args, + row: 4.0, col: 0, text: "300hz", + frequency: 300, + method_to_call: :play_sine_wave), + (button args, + row: 5.0, col: 0, text: "400hz", + frequency: 400, + method_to_call: :play_sine_wave), + (button args, + row: 6.0, col: 0, text: "500hz", + frequency: 500, + method_to_call: :play_sine_wave), + ] +end + +def play_sine_wave args, sender + queue_sine_wave args, + frequency: sender[:frequency], + duration: 1.seconds, + fade_out: true +end + + +def note_buttons args + [ + (button args, + row: 1.5, col: 3, text: "C4", + note: :c, octave: 4, method_to_call: :play_note), + (button args, + row: 2.5, col: 3, text: "D4", + note: :d, octave: 4, method_to_call: :play_note), + (button args, + row: 3.5, col: 3, text: "E4", + note: :e, octave: 4, method_to_call: :play_note), + (button args, + row: 4.5, col: 3, text: "F4", + note: :f, octave: 4, method_to_call: :play_note), + (button args, + row: 5.5, col: 3, text: "G4", + note: :g, octave: 4, method_to_call: :play_note), + (button args, + row: 6.5, col: 3, text: "A5", + note: :a, octave: 5, method_to_call: :play_note), + (button args, + row: 7.5, col: 3, text: "B5", + note: :b, octave: 5, method_to_call: :play_note), + (button args, + row: 8.5, col: 3, text: "C5", + note: :c, octave: 5, method_to_call: :play_note), + ] +end + +def play_note args, sender + queue_sine_wave args, + frequency: (frequency_for note: sender[:note], + octave: sender[:octave]), + duration: 1.seconds, + fade_out: true +end + +def bell_buttons args + [ + (button args, + row: 1.5, col: 6, text: "Bell C4", + note: :c, octave: 4, method_to_call: :play_bell), + (button args, + row: 2.5, col: 6, text: "Bell D4", + note: :d, octave: 4, method_to_call: :play_bell), + (button args, + row: 3.5, col: 6, text: "Bell E4", + note: :e, octave: 4, method_to_call: :play_bell), + (button args, + row: 4.5, col: 6, text: "Bell F4", + note: :f, octave: 4, method_to_call: :play_bell), + (button args, + row: 5.5, col: 6, text: "Bell G4", + note: :g, octave: 4, method_to_call: :play_bell), + (button args, + row: 6.5, col: 6, text: "Bell A5", + note: :a, octave: 5, method_to_call: :play_bell), + (button args, + row: 7.5, col: 6, text: "Bell B5", + note: :b, octave: 5, method_to_call: :play_bell), + (button args, + row: 8.5, col: 6, text: "Bell C5", + note: :c, octave: 5, method_to_call: :play_bell), + ] +end + +def play_bell args, sender + queue_bell args, + frequency: (frequency_for note: sender[:note], + octave: sender[:octave]), + duration: 2.seconds, + fade_out: true +end + +def render args + args.outputs.borders << args.state.buttons.map { |b| b[:border] } + args.outputs.labels << args.state.buttons.map { |b| b[:label] } + args.outputs.labels << args.layout + .rect(row: 0, + col: 11.5) + .yield_self { |r| r.merge y: r.y + r.h } + .merge(text: "This is a Pro only feature. Click here to watch the YouTube video if you are on the Standard License.", + alignment_enum: 1) +end + +def input args + args.state.buttons.each do |b| + if args.inputs.mouse.click.inside_rect? b[:rect] + parameter_string = (b.slice :frequency, :note, :octave).map { |k, v| "#{k}: #{v}" }.join ", " + args.gtk.notify! "#{b[:method_to_call]} #{parameter_string}" + send b[:method_to_call], args, b + end + end + + if args.inputs.mouse.click.inside_rect? (args.layout.rect(row: 0).yield_self { |r| r.merge y: r.y + r.h.half, h: r.h.half }) + args.gtk.openurl 'https://www.youtube.com/watch?v=zEzovM5jT-k&ab_channel=AmirRajan' + end +end + +def process_audio_queue args + to_queue = args.state.audio_queue.find_all { |v| v[:queue_at] <= args.tick_count } + args.state.audio_queue -= to_queue + + to_queue.each do |a| + args.audio[a[:id]] = a + end + + args.audio.each do |k, v| + if v[:decay_rate] + v[:gain] -= v[:decay_rate] + end + end + + sounds_to_stop = args.audio.find_all do |k, v| + v[:stop_at] && args.state.tick_count >= v[:stop_at] + end + + sounds_to_stop.each do |(k, v)| + args.audio.delete k + end +end + +def graph_sine_wave args, sine_wave, frequency + if args.state.tick_count != args.state.graphed_at + args.outputs.static_lines.clear + args.outputs.static_sprites.clear + end + + r, g, b = frequency.to_i % 80, frequency.to_i % 128, frequency.to_i % 255 + center_row = args.layout.rect(row: 5, col: 9) + x_scale = 20 + y_scale = 100 + max_points = 20 + + points = sine_wave + if sine_wave.length > max_points + resolution = sine_wave.length.idiv max_points + points = sine_wave.find_all + .with_index { |y, i| i % resolution == 0 } + end + + args.outputs.static_lines << points.map_with_index do |y, x| + next_y = points[x + 1] + + if next_y + { + x: center_row.x + (x * x_scale), + y: center_row.y + center_row.h.half + y_scale * y, + x2: center_row.x + ((x + 1) * x_scale), + y2: center_row.y + center_row.h.half + y_scale * next_y, + r: r, + g: g, + b: b + } + end + end + + args.outputs.static_sprites << points.map_with_index do |y, x| + { + x: (center_row.x + (x * x_scale)) - 1, + y: (center_row.y + center_row.h.half + y_scale * y) - 1, + w: 2, + h: 2, + path: 'sprites/square-black.png' + } + end + + args.state.graphed_at = args.state.tick_count +end + +def defaults_period_sine_wave_for + { frequency: 440, sample_rate: 48000 } +end + +def sine_wave_for opts = { } + opts = defaults_period_sine_wave_for.merge opts + frequency = opts[:frequency] + sample_rate = opts[:sample_rate] + period_size = (sample_rate.fdiv frequency).ceil + period_size.map_with_index do |i| + Math::sin((2.0 * Math::PI) / (sample_rate.to_f / frequency.to_f) * i) + end.to_a +end + +def generate_audio_data sine_wave, sample_rate + sample_size = (sample_rate.fdiv (1000.fdiv 60)).ceil + copy_count = (sample_size.fdiv sine_wave.length).ceil + sine_wave * copy_count +end + +def defaults_queue_sine_wave + { frequency: 440, duration: 60, gain: 1.0, fade_out: false, queue_in: 0 } +end + +def queue_sine_wave args, opts = { } + opts = defaults_queue_sine_wave.merge opts + decay_rate = 0 + decay_rate = 1.fdiv(opts[:duration]) * opts[:gain] if opts[:fade_out] + frequency = opts[:frequency] + sample_rate = 48000 + + audio_state = { + id: (new_id! args), + frequency: frequency, + sample_rate: 48000, + stop_at: args.tick_count + opts[:queue_in] + opts[:duration], + gain: opts[:gain].to_f, + queue_at: args.state.tick_count + opts[:queue_in], + decay_rate: decay_rate, + pitch: 1.0, + looping: true, + paused: false + } + + sine_wave = sine_wave_for frequency: frequency, sample_rate: sample_rate + args.state.sine_waves[frequency] ||= sine_wave_for frequency: frequency, sample_rate: sample_rate + + proc = lambda do + generate_audio_data args.state.sine_waves[frequency], sample_rate + end + + audio_state[:input] = [1, sample_rate, proc] + graph_sine_wave args, sine_wave, frequency + args.state.audio_queue << audio_state +end + +def defaults_queue_bell + { frequency: 440, duration: 1.seconds, queue_in: 0 } +end + +def queue_bell args, opts = {} + (bell_to_sine_waves (defaults_queue_bell.merge opts)).each { |b| queue_sine_wave args, b } +end + +def bell_harmonics + [ + { frequency_ratio: 0.5, duration_ratio: 1.00 }, + { frequency_ratio: 1.0, duration_ratio: 0.80 }, + { frequency_ratio: 2.0, duration_ratio: 0.60 }, + { frequency_ratio: 3.0, duration_ratio: 0.40 }, + { frequency_ratio: 4.2, duration_ratio: 0.25 }, + { frequency_ratio: 5.4, duration_ratio: 0.20 }, + { frequency_ratio: 6.8, duration_ratio: 0.15 } + ] +end + +def bell_to_sine_waves opts + bell_harmonics.map do |b| + { + frequency: opts[:frequency] * b[:frequency_ratio], + duration: opts[:duration] * b[:duration_ratio], + queue_in: opts[:queue_in], + gain: (1.fdiv bell_harmonics.length), + fade_out: true + } + end +end + +def defaults_frequency_for + { note: :a, octave: 5, sharp: false, flat: false } +end + +def frequency_for opts = {} + opts = defaults_frequency_for.merge opts + octave_offset_multiplier = opts[:octave] - 5 + note = note_frequencies_octave_5[opts[:note]] + if octave_offset_multiplier < 0 + note = note * 1 / (octave_offset_multiplier.abs + 1) + elsif octave_offset_multiplier > 0 + note = note * (octave_offset_multiplier.abs + 1) / 1 + end + note +end + +def note_frequencies_octave_5 + { + a: 440.0, + a_sharp: 466.16, b_flat: 466.16, + b: 493.88, + c: 523.25, + c_sharp: 554.37, d_flat: 587.33, + d: 587.33, + d_sharp: 622.25, e_flat: 659.25, + e: 659.25, + f: 698.25, + f_sharp: 739.99, g_flat: 739.99, + g: 783.99, + g_sharp: 830.61, a_flat: 830.61 + } +end + +def new_id! args + args.state.audio_id ||= 0 + args.state.audio_id += 1 +end + +def button args, opts + button_def = opts.merge rect: (args.layout.rect (opts.merge w: 2, h: 1)) + + button_def[:border] = button_def[:rect].merge r: 0, g: 0, b: 0 + + font_size_enum = args.layout.font_relative_size_enum 0 + label_offset_x = 4 + label_offset_y = button_def[:rect].h.half + button_def[:rect].h.idiv(4) + + button_def[:label] = button_def[:rect].merge text: opts[:text], + size_enum: font_size_enum, + x: button_def[:rect].x + label_offset_x, + y: button_def[:rect].y + label_offset_y + + button_def +end + +$gtk.reset diff --git a/samples/01_rendering_basics/07_sound_synthesis/license-for-sample.txt b/samples/01_rendering_basics/07_sound_synthesis/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/01_rendering_basics/07_sound_synthesis/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/01_rendering_basics/07_sound_synthesis/metadata/game_metadata.txt b/samples/01_rendering_basics/07_sound_synthesis/metadata/game_metadata.txt new file mode 100644 index 0000000..16cef1d --- /dev/null +++ b/samples/01_rendering_basics/07_sound_synthesis/metadata/game_metadata.txt @@ -0,0 +1,6 @@ +devid=amirrajan +devtitle=Amir Rajan +gameid=hello-world +gametitle=Hello World +version=1.0 +icon=metadata/icon.png diff --git a/samples/01_rendering_basics/07_sound_synthesis/metadata/icon.png b/samples/01_rendering_basics/07_sound_synthesis/metadata/icon.png Binary files differnew file mode 100644 index 0000000..e20e8c2 --- /dev/null +++ b/samples/01_rendering_basics/07_sound_synthesis/metadata/icon.png diff --git a/samples/01_rendering_basics/07_sound_synthesis/sprites/square-black.png b/samples/01_rendering_basics/07_sound_synthesis/sprites/square-black.png Binary files differnew file mode 100644 index 0000000..cea7bd7 --- /dev/null +++ b/samples/01_rendering_basics/07_sound_synthesis/sprites/square-black.png diff --git a/samples/02_input_basics/06_touch/app/main.rb b/samples/02_input_basics/06_touch/app/main.rb new file mode 100644 index 0000000..8b006c7 --- /dev/null +++ b/samples/02_input_basics/06_touch/app/main.rb @@ -0,0 +1,42 @@ +def tick args + args.outputs.background_color = [ 0, 0, 0 ] + args.outputs.primitives << [640, 700, "Touch your screen.", 5, 1, 255, 255, 255].label + + # If you don't want to get fancy, you can just look for finger_one + # (and _two, if you like), which are assigned in the order new touches hit + # the screen. If not nil, they are touching right now, and are just + # references to specific items in the args.input.touch hash. + # If finger_one lifts off, it will become nil, but finger_two, if it was + # touching, remains until it also lifts off. When all fingers lift off, the + # the next new touch will be finger_one again, but until then, new touches + # don't fill in earlier slots. + if !args.inputs.finger_one.nil? + args.outputs.primitives << [640, 650, "Finger #1 is touching at (#{args.inputs.finger_one.x}, #{args.inputs.finger_one.y}).", 5, 1, 255, 255, 255].label + end + if !args.inputs.finger_two.nil? + args.outputs.primitives << [640, 600, "Finger #2 is touching at (#{args.inputs.finger_two.x}, #{args.inputs.finger_two.y}).", 5, 1, 255, 255, 255].label + end + + # Here's the more flexible interface: this will report as many simultaneous + # touches as the system can handle, but it's a little more effort to track + # them. Each item in the args.input.touch hash has a unique key (an + # incrementing integer) that exists until the finger lifts off. You can + # tell which order the touches happened globally by the key value, or + # by the touch[id].touch_order field, which resets to zero each time all + # touches have lifted. + + args.state.colors ||= [ + 0xFF0000, 0x00FF00, 0x1010FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF + ] + + size = 100 + args.inputs.touch.each { |k,v| + color = args.state.colors[v.touch_order % 7] + r = (color & 0xFF0000) >> 16 + g = (color & 0x00FF00) >> 8 + b = (color & 0x0000FF) + args.outputs.primitives << [v.x - (size / 2), v.y + (size / 2), size, size, r, g, b, 255].solid + args.outputs.primitives << [v.x, v.y + size, k.to_s, 0, 1, 0, 0, 0].label + } +end + diff --git a/samples/02_input_basics/06_touch/license-for-sample.txt b/samples/02_input_basics/06_touch/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/02_input_basics/06_touch/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/02_input_basics/06_touch/metadata/game_metadata.txt b/samples/02_input_basics/06_touch/metadata/game_metadata.txt new file mode 100644 index 0000000..88e7d96 --- /dev/null +++ b/samples/02_input_basics/06_touch/metadata/game_metadata.txt @@ -0,0 +1,6 @@ +devid=dragonruby +devtitle=DragonRuby LLC +gameid=touchexample +gametitle=DragonRuby GTK Touch Example +version=0.1 +icon=metadata/icon.png diff --git a/samples/02_input_basics/06_touch/metadata/icon.png b/samples/02_input_basics/06_touch/metadata/icon.png Binary files differnew file mode 100644 index 0000000..57254fe --- /dev/null +++ b/samples/02_input_basics/06_touch/metadata/icon.png diff --git a/samples/07_advanced_rendering/06_pixel_arrays/app/main.rb b/samples/07_advanced_rendering/06_pixel_arrays/app/main.rb new file mode 100644 index 0000000..12291c4 --- /dev/null +++ b/samples/07_advanced_rendering/06_pixel_arrays/app/main.rb @@ -0,0 +1,41 @@ +$gtk.reset + +def tick args + args.state.posinc ||= 1 + args.state.pos ||= 0 + args.state.rotation ||= 0 + + dimension = 10 # keep it small and let the GPU scale it when rendering the sprite. + + # Set up our "scanner" pixel array and fill it with black pixels. + args.pixel_array(:scanner).width = dimension + args.pixel_array(:scanner).height = dimension + args.pixel_array(:scanner).pixels.fill(0xFF000000, 0, dimension * dimension) # black, full alpha + + # Draw a green line that bounces up and down the sprite. + args.pixel_array(:scanner).pixels.fill(0xFF00FF00, dimension * args.state.pos, dimension) # green, full alpha + + # Adjust position for next frame. + args.state.pos += args.state.posinc + if args.state.posinc > 0 && args.state.pos >= dimension + args.state.posinc = -1 + args.state.pos = dimension - 1 + elsif args.state.posinc < 0 && args.state.pos < 0 + args.state.posinc = 1 + args.state.pos = 1 + end + + # New/changed pixel arrays get uploaded to the GPU before we render + # anything. At that point, they can be scaled, rotated, and otherwise + # used like any other sprite. + w = 100 + h = 100 + x = (1280 - w) / 2 + y = (720 - h) / 2 + args.outputs.background_color = [64, 0, 128] + args.outputs.primitives << [x, y, w, h, :scanner, args.state.rotation].sprite + args.state.rotation += 1 + + args.outputs.primitives << args.gtk.current_framerate_primitives +end + diff --git a/samples/12_c_extensions/03_native_pixel_arrays/README.md b/samples/12_c_extensions/03_native_pixel_arrays/README.md new file mode 100644 index 0000000..f7cd12b --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/README.md @@ -0,0 +1,13 @@ +Please see samples/12_c_extensions/01_basics for the overview of C extensions. + +This sample reproduces the same program from +samples/07_advanced_rendering/06_pixel_arrays, but moves the creation of the +pixel array to C code. + +This particular use-case doesn't need heavier processing power, so you are +only risking problems and portability loss by moving into native code, but +for more computationally demanding jobs, this can be quite helpful: not only +can C crunch numbers and access memory faster, but you can also hand your +pixel array to the renderer without having to convert it to a Ruby array +first (which the engine would then just convert it right back again anyhow). + diff --git a/samples/12_c_extensions/03_native_pixel_arrays/app/ext.c b/samples/12_c_extensions/03_native_pixel_arrays/app/ext.c new file mode 100644 index 0000000..49d0e40 --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/app/ext.c @@ -0,0 +1,54 @@ +#ifndef NULL +#define NULL 0 +#endif +typedef unsigned int Uint32; + +extern void *(*drb_symbol_lookup)(const char *sym); +typedef void (*drb_upload_pixel_array_fn)(const char *name, const int w, const int h, const Uint32 *pixels); + +void update_scanner_texture(void) +{ + #define dimension 10 + + static drb_upload_pixel_array_fn drb_upload_pixel_array = NULL; + static int pos = 0; + static int posinc = 1; + + if (!drb_upload_pixel_array) { + drb_upload_pixel_array = drb_symbol_lookup("drb_upload_pixel_array"); + if (!drb_upload_pixel_array) { + return; // oh well. + } + } + + + // Set up our "scanner" pixel array and fill it with black pixels. + + // You could make this faster by making this array static (which will + // initialize it all to zero at startup), and then blanking the previous + // line and drawing the next, and not touching the rest. + Uint32 pixels[dimension * dimension]; + for (int i = 0; i < (dimension * dimension); i++) { + pixels[i] = 0xFF000000; // full alpha, full black + } + + // Draw a green line that bounces up and down the sprite. + Uint32 *line = pixels + (pos * dimension); + for (int i = 0; i < dimension; i++) { + line[i] = 0xFF00FF00; // full alpha, full green + } + + // Adjust position for next frame. + pos += posinc; + if ((posinc > 0) && (pos >= dimension)) { + posinc = -1; + pos = dimension - 1; + } else if ((posinc < 0) && (pos < 0)) { + posinc = 1; + pos = 1; + } + + // Send it to the renderer to create/update a sprite. + drb_upload_pixel_array("scanner", dimension, dimension, pixels); +} + diff --git a/samples/12_c_extensions/03_native_pixel_arrays/app/main.rb b/samples/12_c_extensions/03_native_pixel_arrays/app/main.rb new file mode 100644 index 0000000..1dbc716 --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/app/main.rb @@ -0,0 +1,22 @@ +$gtk.ffi_misc.gtk_dlopen("ext") +include FFI::CExt + +def tick args + args.state.rotation ||= 0 + + update_scanner_texture # this calls into a C extension! + + # New/changed pixel arrays get uploaded to the GPU before we render + # anything. At that point, they can be scaled, rotated, and otherwise + # used like any other sprite. + w = 100 + h = 100 + x = (1280 - w) / 2 + y = (720 - h) / 2 + args.outputs.background_color = [64, 0, 128] + args.outputs.primitives << [x, y, w, h, :scanner, args.state.rotation].sprite + args.state.rotation += 1 + + args.outputs.primitives << args.gtk.current_framerate_primitives +end + diff --git a/samples/12_c_extensions/03_native_pixel_arrays/license-for-sample.txt b/samples/12_c_extensions/03_native_pixel_arrays/license-for-sample.txt new file mode 100644 index 0000000..100dcec --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/license-for-sample.txt @@ -0,0 +1,9 @@ +Copyright 2019 DragonRuby LLC + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/12_c_extensions/03_native_pixel_arrays/metadata/game_metadata.txt b/samples/12_c_extensions/03_native_pixel_arrays/metadata/game_metadata.txt new file mode 100644 index 0000000..a8772d0 --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/metadata/game_metadata.txt @@ -0,0 +1,5 @@ +devid=dragonruby +devtitle=DragonRuby LLC +gameid=cbasics +gametitle=C Basics +version=1.0 diff --git a/samples/12_c_extensions/03_native_pixel_arrays/native/ext-bindings.c b/samples/12_c_extensions/03_native_pixel_arrays/native/ext-bindings.c new file mode 100644 index 0000000..c0a923c --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/native/ext-bindings.c @@ -0,0 +1,66 @@ +#include <mruby.h> +#include <string.h> +#include <assert.h> +#include <mruby/string.h> +#include <mruby/data.h> +#include <dragonruby.h> +#include "app/ext.c" + +// MRuby `typedef`s mrb_int in the mruby/value.h +// Then `#define`s mrb_int in mruby.h +// We need to undo the macro and avoid it's usage +// FIXME: I'm surely doing something wrong +#ifdef mrb_int +#undef mrb_int +#endif + +void *(*drb_symbol_lookup)(const char *sym) = NULL; + +static void (*drb_free_foreign_object_f)(mrb_state *, void *); +static struct RClass *(*mrb_module_get_f)(mrb_state *, const char *); +static mrb_int (*mrb_get_args_f)(mrb_state *, mrb_args_format, ...); +static struct RClass *(*mrb_module_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_class_get_under_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_define_module_under_f)(mrb_state *, struct RClass *, const char *); +static void (*mrb_define_module_function_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RClass *(*mrb_define_class_under_f)(mrb_state *, struct RClass *, const char *, struct RClass *); +static void (*mrb_define_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static void (*mrb_define_class_method_f)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); +static struct RData *(*mrb_data_object_alloc_f)(mrb_state *, struct RClass *, void *, const mrb_data_type *); +static mrb_value (*mrb_str_new_cstr_f)(mrb_state *, const char *); +static void (*mrb_raise_f)(mrb_state *, struct RClass *, const char *); +static struct RClass *(*mrb_exc_get_f)(mrb_state *, const char *); +static void drb_free_foreign_object_indirect(mrb_state *state, void *pointer) { + drb_free_foreign_object_f(state, pointer); +} +static mrb_value drb_ffi_update_scanner_texture_Binding(mrb_state *state, mrb_value value) { + update_scanner_texture(); + return mrb_nil_value(); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *)); +DRB_FFI_EXPORT +void drb_register_c_extensions(void *(*lookup)(const char *), mrb_state *state, struct RClass *FFI) { + if (drb_ffi_init_indirect_functions(lookup)) + return; + struct RClass *module = mrb_define_module_under_f(state, FFI, "CExt"); + struct RClass *object_class = state->object_class; + mrb_define_module_function_f(state, module, "update_scanner_texture", drb_ffi_update_scanner_texture_Binding, MRB_ARGS_REQ(0)); +} +static int drb_ffi_init_indirect_functions(void *(*lookup)(const char *fnname)) { + drb_symbol_lookup = lookup; + if (!(mrb_exc_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_exc_get"))) return -1; + if (!(mrb_raise_f = (void (*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_raise"))) return -1; + if (!(mrb_class_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_class_get_under"))) return -1; + if (!(mrb_module_get_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_module_get_under"))) return -1; + if (!(drb_free_foreign_object_f = (void (*)(mrb_state *, void *)) lookup("drb_free_foreign_object"))) return -1; + if (!(mrb_module_get_f = (struct RClass *(*)(mrb_state *, const char *)) lookup("mrb_module_get"))) return -1; + if (!(mrb_define_module_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *)) lookup("mrb_define_module_under"))) return -1; + if (!(mrb_data_object_alloc_f = (struct RData *(*)(mrb_state *, struct RClass *, void *, const mrb_data_type *)) lookup("mrb_data_object_alloc"))) return -1; + if (!(mrb_define_module_function_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_module_function"))) return -1; + if (!(mrb_get_args_f = (mrb_int (*)(mrb_state *, mrb_args_format, ...)) lookup("mrb_get_args"))) return -1; + if (!(mrb_define_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_method"))) return -1; + if (!(mrb_define_class_method_f = (void (*)(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec)) lookup("mrb_define_class_method"))) return -1; + if (!(mrb_str_new_cstr_f = (mrb_value (*)(mrb_state *, const char *)) lookup("mrb_str_new_cstr"))) return -1; + if (!(mrb_define_class_under_f = (struct RClass *(*)(mrb_state *, struct RClass *, const char *, struct RClass *)) lookup("mrb_define_class_under"))) return -1; + return 0; +} diff --git a/samples/12_c_extensions/03_native_pixel_arrays/pre.bat b/samples/12_c_extensions/03_native_pixel_arrays/pre.bat new file mode 100644 index 0000000..16befa3 --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/pre.bat @@ -0,0 +1,5 @@ +set DRB_ROOT=..\..\..\ +md native +md native\windows-amd64 +%DRB_ROOT%\dragonruby-bind.exe --output=native\ext-bind.c app\ext.c +clang -shared .\native\ext-bind.c --sysroot=C:\mingw-w64\mingw64 --target=x86_64-w64-mingw32 -fuse-ld=lld -isystem %DRB_ROOT%\include -I. -o native\windows-amd64\ext.dll diff --git a/samples/12_c_extensions/03_native_pixel_arrays/pre.sh b/samples/12_c_extensions/03_native_pixel_arrays/pre.sh new file mode 100755 index 0000000..07965a5 --- /dev/null +++ b/samples/12_c_extensions/03_native_pixel_arrays/pre.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +OSTYPE=`uname -s` +if [ "x$OSTYPE" = "xDarwin" ]; then + PLATFORM=macos + DLLEXT=dylib +else + PLATFORM=linux-amd64 + DLLEXT=so +fi + +DRB_ROOT=../../.. +mkdir -p native/$PLATFORM + +$DRB_ROOT/dragonruby-bind --output=native/ext-bindings.c app/ext.c +clang \ + -isystem $DRB_ROOT/include -I. \ + -fPIC -shared native/ext-bindings.c -o native/$PLATFORM/ext.$DLLEXT + |
