From 8d5c566d17fa719b8b940b5cbb468eb16a176c86 Mon Sep 17 00:00:00 2001 From: Tom Black Date: Sat, 29 Sep 2018 17:45:36 -0700 Subject: Build for WebAssembly, first pass --- README.md | 4 +- bin/ruby2d | 16 +-- ext/ruby2d/ruby2d-opal.rb | 289 ---------------------------------------------- lib/ruby2d/image.rb | 6 +- lib/ruby2d/music.rb | 6 +- lib/ruby2d/sound.rb | 6 +- lib/ruby2d/sprite.rb | 8 +- lib/ruby2d/text.rb | 6 +- lib/ruby2d/window.rb | 6 +- ruby2d.gemspec | 3 +- test/audio.rb | 6 +- test/contains.rb | 13 +-- test/sprite.rb | 6 +- test/testcard.rb | 13 +-- 14 files changed, 33 insertions(+), 355 deletions(-) delete mode 100644 ext/ruby2d/ruby2d-opal.rb diff --git a/README.md b/README.md index 909622b..f28ea4b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ rake test:int testcard # Build `test/audio.rb` natively using MRuby and run rake test:native audio -# Build `test/mouse.rb` for the web using Opal and run in the default browser +# Build `test/mouse.rb` for the web using WebAssembly and run in the default browser rake test:web mouse ``` @@ -67,7 +67,7 @@ Whether adding a feature or fixing a bug, try to do the following to ensure your - **Check if there is an existing issue, and if not, open a new one to start a discussion.** Before dedicating time and energy to an idea or fix, let's make sure it's consistent with the principles and goals of the project, and that we have a solid strategy in place to implement and test. -- **Use a subset of Ruby that works everywhere.** Ruby 2D applications are, of course, written in Ruby. Some users may choose to harness the full power of the language, standard library, and ecosystem of gems by writing interpreted apps targeting the standard implementation, [MRI](https://en.wikipedia.org/wiki/Ruby_MRI). Others may want to target the web via [Opal](http://opalrb.org), so their app can be run in any browser. And others still may want to compile their app to native code via [MRuby](http://mruby.org), so they can embed it on platforms like the [Raspberry Pi](https://www.raspberrypi.org), or run it on mobile platforms like iOS and Android, or on the big screen with Apple TV or Amazon Fire TV. Or even further, some may want to do all three! Ruby 2D aims to support all of these use cases, even with the same app codebase. Your code contribution to this gem has to support a subset of Ruby that is compatible and behaves similarly across MRI, MRuby, and Opal. Beyond reading the documentation for each Ruby implementation, you can also try out code snippets on the command line using their respective REPLs: `irb` (MRI), `mirb` (MRuby), and `opal-repl` (Opal). +- **Use a subset of Ruby that works everywhere.** Ruby 2D applications are, of course, written in Ruby. Some users may choose to harness the full power of the language, standard library, and ecosystem of gems by writing interpreted apps targeting the standard implementation, [MRI](https://en.wikipedia.org/wiki/Ruby_MRI). Others may want to target the web via [WebAssembly](https://webassembly.org), so their app can be run in any browser. And others still may want to compile their app to native code via [MRuby](http://mruby.org), so they can embed it on platforms like the [Raspberry Pi](https://www.raspberrypi.org), or run it on mobile platforms like iOS and Android, or on the big screen with Apple TV or Amazon Fire TV. Or even further, some may want to do all three! Ruby 2D aims to support all of these use cases, even with the same app codebase. Your code contribution to this gem has to support a subset of Ruby that is compatible and behaves similarly across MRI and MRuby. Beyond reading the documentation for each Ruby implementation, you can also try out code snippets on the command line using their respective REPLs: `irb` (MRI) and `mirb` (MRuby). - **Comprehensively test your change.** Unlike other Ruby libraries, not everything here can be easily covered with unit tests alone. We also need to make sure things look and sound right, inputs work as expected, and behavior is consistent across all [platforms Ruby 2D supports](http://www.ruby2d.com/platforms). diff --git a/bin/ruby2d b/bin/ruby2d index 7437a92..2acbf04 100755 --- a/bin/ruby2d +++ b/bin/ruby2d @@ -123,26 +123,17 @@ def build_web(rb_file) # Assemble the Ruby 2D library in one `.rb` file and compile to JS make_lib - `opal --compile --no-opal build/lib.rb > build/lib.js` # Read the provided Ruby source file, copy to build dir, and compile to JS - File.open('build/src.rb', 'w') { |file| file << strip_require(rb_file) } - `opal --compile --no-opal build/src.rb > build/src.js` - FileUtils.cp "#{@gem_dir}/ext/ruby2d/ruby2d-opal.rb", "build/" - `opal --compile --no-opal build/ruby2d-opal.rb > build/ruby2d-opal.js` # Combine contents of JS source files and compiled JS into one file - open('build/app.js', 'w') do |f| - f << File.read("#{@gem_dir}/assets/simple2d.js") << "\n\n" - f << File.read("#{@gem_dir}/assets/opal.js") << "\n\n" - f << File.read("build/lib.js") << "\n\n" - f << File.read("build/ruby2d-opal.js") << "\n\n" - f << File.read("build/src.js") << "\n\n" - end # Copy over HTML template FileUtils.cp "#{@gem_dir}/assets/template.html", "build/app.html" + # Build + `simple2d build --web build/app.c --flags=-lmruby` + # Clean up clean_up unless @debug @@ -206,7 +197,6 @@ end def clean_up(cmd = nil) FileUtils.rm( Dir.glob('build/{src,lib}.{rb,c,js}') + - Dir.glob('build/ruby2d-opal.{rb,js}') + Dir.glob('build/app.c') ) if cmd == :all diff --git a/ext/ruby2d/ruby2d-opal.rb b/ext/ruby2d/ruby2d-opal.rb deleted file mode 100644 index e888328..0000000 --- a/ext/ruby2d/ruby2d-opal.rb +++ /dev/null @@ -1,289 +0,0 @@ -# Web extension for Opal - -# Ruby 2D window -$R2D_WINDOW = nil - -# Simple 2D window -` -var win; - -// ruby2d.js - -function on_key(e) { - - switch (e.type) { - case S2D.KEY_DOWN: - #{type = :down}; - break; - case S2D.KEY_HELD: - #{type = :held}; - break; - case S2D.KEY_UP: - #{type = :up}; - break; - } - - #{$R2D_WINDOW.key_callback(type, `e.key`)}; -} - - -function on_mouse(e) { - - #{direction = nil} - #{button = nil} - - switch (e.type) { - case S2D.MOUSE_DOWN: - #{type = :down}; - break; - case S2D.MOUSE_UP: - #{type = :up}; - break; - case S2D.MOUSE_SCROLL: - #{type = :scroll}; - #{direction} = e.direction == S2D.MOUSE_SCROLL_NORMAL ? #{:normal} : #{:inverted}; - break; - case S2D.MOUSE_MOVE: - #{type = :move}; - break; - } - - if (e.type == S2D.MOUSE_DOWN || e.type == S2D.MOUSE_UP) { - switch (e.button) { - case S2D.MOUSE_LEFT: - #{button = :left}; - break; - case S2D.MOUSE_MIDDLE: - #{button = :middle}; - break; - case S2D.MOUSE_RIGHT: - #{button = :right}; - break; - case S2D.MOUSE_X1: - #{button = :x1}; - break; - case S2D.MOUSE_X2: - #{button = :x2}; - break; - } - } - - #{$R2D_WINDOW.mouse_callback( - type, button, direction, - `e.x`, `e.y`, `e.delta_x`, `e.delta_y` - )}; -} - - -function update() { - #{$R2D_WINDOW.mouse_x = `win.mouse.x`}; - #{$R2D_WINDOW.mouse_y = `win.mouse.y`}; - #{$R2D_WINDOW.frames = `win.frames`}; - #{$R2D_WINDOW.fps = `win.fps`}; - #{$R2D_WINDOW.update_callback}; -} - - -function render() { - - // Set background color - win.background.r = #{$R2D_WINDOW.get(:background).r}; - win.background.g = #{$R2D_WINDOW.get(:background).g}; - win.background.b = #{$R2D_WINDOW.get(:background).b}; - win.background.a = #{$R2D_WINDOW.get(:background).a}; - - var objects = #{$R2D_WINDOW.objects}; - - for (var i = 0; i < objects.length; i++) { - var el = objects[i]; - el['$ext_render'](); - } -} -` - - -module Ruby2D - class Triangle - def ext_render - `S2D.DrawTriangle( - #{self}.x1, #{self}.y1, #{self}.c1.r, #{self}.c1.g, #{self}.c1.b, #{self}.c1.a, - #{self}.x2, #{self}.y2, #{self}.c2.r, #{self}.c2.g, #{self}.c2.b, #{self}.c2.a, - #{self}.x3, #{self}.y3, #{self}.c3.r, #{self}.c3.g, #{self}.c3.b, #{self}.c3.a - );` - end - end - - class Quad - def ext_render - `S2D.DrawQuad( - #{self}.x1, #{self}.y1, #{self}.c1.r, #{self}.c1.g, #{self}.c1.b, #{self}.c1.a, - #{self}.x2, #{self}.y2, #{self}.c2.r, #{self}.c2.g, #{self}.c2.b, #{self}.c2.a, - #{self}.x3, #{self}.y3, #{self}.c3.r, #{self}.c3.g, #{self}.c3.b, #{self}.c3.a, - #{self}.x4, #{self}.y4, #{self}.c4.r, #{self}.c4.g, #{self}.c4.b, #{self}.c4.a - );` - end - end - - class Line - def ext_render - `S2D.DrawLine( - #{self}.x1, #{self}.y1, #{self}.x2, #{self}.y2, #{self}.width, - #{self}.c1.r, #{self}.c1.g, #{self}.c1.b, #{self}.c1.a, - #{self}.c2.r, #{self}.c2.g, #{self}.c2.b, #{self}.c2.a, - #{self}.c3.r, #{self}.c3.g, #{self}.c3.b, #{self}.c3.a, - #{self}.c4.r, #{self}.c4.g, #{self}.c4.b, #{self}.c4.a - );` - end - end - - class Image - def ext_init(path) - ` - #{self}.data = S2D.CreateImage(path, function() { - if (#{@width} == Opal.nil) { - #{@width} = #{self}.data.width; - } - if (#{@height} == Opal.nil) { - #{@height} = #{self}.data.height; - } - }); - ` - end - - def ext_render - ` - #{self}.data.x = #{self}.x; - #{self}.data.y = #{self}.y; - - if (#{self}.width != Opal.nil) #{self}.data.width = #{self}.width; - if (#{self}.height != Opal.nil) #{self}.data.height = #{self}.height; - - #{self}.data.color.r = #{self}.color.r; - #{self}.data.color.g = #{self}.color.g; - #{self}.data.color.b = #{self}.color.b; - #{self}.data.color.a = #{self}.color.a; - - S2D.DrawImage(#{self}.data); - ` - end - end - - class Sprite - def ext_init(path) - `#{self}.data = S2D.CreateSprite(path);` - end - - def ext_render - ` - #{self}.data.x = #{self}.x; - #{self}.data.y = #{self}.y; - - S2D.ClipSprite( - #{self}.data, - #{self}.clip_x, - #{self}.clip_y, - #{self}.clip_w, - #{self}.clip_h - ); - - S2D.DrawSprite(#{self}.data); - ` - end - end - - class Text - def ext_init - ` - #{self}.data = S2D.CreateText(#{self}.font, #{self}.text, #{self}.size); - #{@width} = #{self}.data.width; - #{@height} = #{self}.data.height; - ` - end - - def ext_set(msg) - ` - S2D.SetText(#{self}.data, #{msg}); - #{@width} = #{self}.data.width; - #{@height} = #{self}.data.height; - ` - end - - def ext_render - ` - #{self}.data.x = #{self}.x; - #{self}.data.y = #{self}.y; - - #{self}.data.color.r = #{self}.color.r; - #{self}.data.color.g = #{self}.color.g; - #{self}.data.color.b = #{self}.color.b; - #{self}.data.color.a = #{self}.color.a; - - S2D.DrawText(#{self}.data); - ` - end - end - - class Sound - def ext_init(path) - `#{self}.data = S2D.CreateSound(path);` - end - - def ext_play - `S2D.PlaySound(#{self}.data);` - end - end - - class Music - def ext_init(path) - `#{self}.data = S2D.CreateMusic(path);` - end - - def ext_play - `S2D.PlayMusic(#{self}.data, #{self}.loop);` - end - - def ext_pause - `S2D.PauseMusic();` - end - - def ext_resume - `S2D.ResumeMusic();` - end - - def ext_stop - `S2D.StopMusic();` - end - - def ext_music_fadeout(ms) - `S2D.FadeOutMusic(ms);` - end - end - - class Window - def ext_show - $R2D_WINDOW = self - - ` - var width = #{$R2D_WINDOW.get(:width)}; - var height = #{$R2D_WINDOW.get(:height)}; - - var vp_w = #{$R2D_WINDOW.get(:viewport_width)}; - var viewport_width = vp_w != Opal.nil ? vp_w : width; - - var vp_h = #{$R2D_WINDOW.get(:viewport_height)}; - var viewport_height = vp_h != Opal.nil ? vp_h : height; - - win = S2D.CreateWindow( - #{$R2D_WINDOW.get(:title)}, width, height, update, render, "ruby2d-app", {} - ); - - win.viewport.width = viewport_width; - win.viewport.height = viewport_height; - win.on_key = on_key; - win.on_mouse = on_mouse; - - S2D.Show(win); - ` - end - end -end diff --git a/lib/ruby2d/image.rb b/lib/ruby2d/image.rb index 9386f6b..2ee9ef3 100644 --- a/lib/ruby2d/image.rb +++ b/lib/ruby2d/image.rb @@ -10,10 +10,8 @@ module Ruby2D def initialize(opts = {}) @path = opts[:path] - unless RUBY_ENGINE == 'opal' - unless File.exists? @path - raise Error, "Cannot find image file `#{@path}`" - end + unless File.exists? @path + raise Error, "Cannot find image file `#{@path}`" end @x = opts[:x] || 0 diff --git a/lib/ruby2d/music.rb b/lib/ruby2d/music.rb index 41b8901..b9fa8ad 100644 --- a/lib/ruby2d/music.rb +++ b/lib/ruby2d/music.rb @@ -8,10 +8,8 @@ module Ruby2D def initialize(path) - unless RUBY_ENGINE == 'opal' - unless File.exists? path - raise Error, "Cannot find audio file `#{path}`" - end + unless File.exists? path + raise Error, "Cannot find audio file `#{path}`" end @path = path diff --git a/lib/ruby2d/sound.rb b/lib/ruby2d/sound.rb index c1d4060..07043a9 100644 --- a/lib/ruby2d/sound.rb +++ b/lib/ruby2d/sound.rb @@ -8,10 +8,8 @@ module Ruby2D def initialize(path) - unless RUBY_ENGINE == 'opal' - unless File.exists? path - raise Error, "Cannot find audio file `#{path}`" - end + unless File.exists? path + raise Error, "Cannot find audio file `#{path}`" end @path = path diff --git a/lib/ruby2d/sprite.rb b/lib/ruby2d/sprite.rb index a00617f..934fac7 100644 --- a/lib/ruby2d/sprite.rb +++ b/lib/ruby2d/sprite.rb @@ -9,11 +9,9 @@ module Ruby2D def initialize(path, opts = {}) - # Check if sprite file exists, unless running on the web - unless RUBY_ENGINE == 'opal' - unless File.exists? path - raise Error, "Cannot find sprite image file `#{path}`" - end + # Check if sprite file exists + unless File.exists? path + raise Error, "Cannot find sprite image file `#{path}`" end # Sprite image file path diff --git a/lib/ruby2d/text.rb b/lib/ruby2d/text.rb index 1b2207d..4d1d74c 100644 --- a/lib/ruby2d/text.rb +++ b/lib/ruby2d/text.rb @@ -16,10 +16,8 @@ module Ruby2D @rotate = opts[:rotate] || 0 @font = opts[:font] - unless RUBY_ENGINE == 'opal' - unless File.exists? @font - raise Error, "Cannot find font file `#{@font}`" - end + unless File.exists? @font + raise Error, "Cannot find font file `#{@font}`" end self.color = opts[:color] || 'white' diff --git a/lib/ruby2d/window.rb b/lib/ruby2d/window.rb index 5b0b7cb..c610d0f 100644 --- a/lib/ruby2d/window.rb +++ b/lib/ruby2d/window.rb @@ -320,10 +320,8 @@ module Ruby2D # Add controller mappings from file def add_controller_mappings - unless RUBY_ENGINE == 'opal' - if File.exists? @controller_mappings - ext_add_controller_mappings(@controller_mappings) - end + if File.exists? @controller_mappings + ext_add_controller_mappings(@controller_mappings) end end diff --git a/ruby2d.gemspec b/ruby2d.gemspec index fab977c..1e51f8a 100644 --- a/ruby2d.gemspec +++ b/ruby2d.gemspec @@ -11,12 +11,11 @@ Gem::Specification.new do |s| s.email = 'tom@blacktm.com' s.required_ruby_version = '>= 2.0.0' - s.add_dependency 'opal', '~> 0.11' s.add_development_dependency 'rspec', '~> 3.8' s.files = Dir.glob('lib/**/*') + Dir.glob('assets/**/*') + - Dir.glob('ext/**/*.{c,js,rb}') + Dir.glob('ext/**/*.{c,rb}') s.extensions = ['ext/ruby2d/extconf.rb'] s.executables << 'ruby2d' end diff --git a/test/audio.rb b/test/audio.rb index 7d39569..cf8ab0f 100644 --- a/test/audio.rb +++ b/test/audio.rb @@ -1,10 +1,6 @@ require 'ruby2d' -if RUBY_ENGINE == 'opal' - media = "../test/media" -else - media = "media" -end +media = "media" set width: 300, height: 200, title: "Ruby 2D — Audio" diff --git a/test/contains.rb b/test/contains.rb index c5c6686..5d97da3 100644 --- a/test/contains.rb +++ b/test/contains.rb @@ -2,13 +2,12 @@ require 'ruby2d' set title: "Ruby 2D — Contains", height: 350 -if RUBY_ENGINE == 'opal' - media = "../test/media" - font = "sans-serif" -else - media = "media" - font = "#{media}/bitstream_vera/vera.ttf" -end +# if web? +# media = "../test/media" +# font = "sans-serif" + +media = "media" +font = "#{media}/bitstream_vera/vera.ttf" objects = [] objects.push Square.new(x: 50, y: 50, size: 100) diff --git a/test/sprite.rb b/test/sprite.rb index e62488b..251e91e 100644 --- a/test/sprite.rb +++ b/test/sprite.rb @@ -1,10 +1,6 @@ require 'ruby2d' -if RUBY_ENGINE == 'opal' - media = "../test/media" -else - media = "media" -end +media = "media" set title: "Ruby 2D — Sprite", width: 400, height: 300 diff --git a/test/testcard.rb b/test/testcard.rb index c433aa4..d02831f 100644 --- a/test/testcard.rb +++ b/test/testcard.rb @@ -1,12 +1,11 @@ require 'ruby2d' -if RUBY_ENGINE == 'opal' - media = "../test/media" - font = "sans-serif" -else - media = "media" - font = "#{media}/bitstream_vera/vera.ttf" -end +# if web? +# media = "../test/media" +# font = "sans-serif" + +media = "media" +font = "#{media}/bitstream_vera/vera.ttf" set diagnostics: true -- cgit v1.2.3