Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Tuesday, 18 December 2012

New context_free.rb (cf3ruby)

Here is my "y" window sketch using my modified ruby-processing library (features CF3 syntax, ie shape replaces rule, also ruby 1.9 hash syntax).
# y.rb ruby-processing
require 'cf3'
Y_TOP = -1 / Math.sqrt(3)
Y_BOT = Math.sqrt(3) / 6

def setup_the_triangle
  @triangle = ContextFree.define do
    ########  
    shape :start do
    unit brightness: 1.0
    end

    shape :unit do
      triangle size: 1.0
      split do
        unit size: 0.5, x: 0, y: Y_TOP/2, brightness: 0.8
        rewind
        unit size: 0.5, x: -0.25, y: -Y_TOP/4, brightness: 0.8
        rewind
        unit size: 0.5, x: 0.25, y: -Y_TOP/4, brightness: 0.8
      end
    end
    ########
  end
end

def setup
  size 1024, 1024
  setup_the_triangle
  no_stroke
  color_mode RGB, 1
  smooth
  draw_it
  save_frame("y.png")
end


def draw
   # Do nothing.
end

def draw_it
  background 225, 225, 0
  @triangle.render :start, size: height, stop_size: 0.5,
  start_x: width/2, start_y: height * 0.6
end

Friday, 14 December 2012

MeshToVBO conversion ruby-processing

Since processing-2.0 there is the possibility of converting 3D meshes to vertical buffer objects (vbo) as instances of PShape. Here I explore a toxiclibs example sketch that instead of using toxis gfx package to render the shapes converts the shapes to vbo using a ruby library (albeit with a Processing::Proxy mixin. Here is the library:-
############################################
# mesh_to_vbo.rb
# a ruby library to convert toxi.mesh object
# to vbo (PShape) written by Martin Prout
# make use of Processing::Proxy mixin
############################################
class MeshToVBO
    include Processing::Proxy

    attr_reader :parent

    def initialize(parent)
        @parent = parent
    end

    def meshToVBO(mesh, smth)
        retained = parent.createShape(PShape::TRIANGLES)
        retained.enableStyle()
        retained.fill(222, 222, 222)
        retained.ambient(50)
        retained.shininess(10)
        retained.specular(50)
        if (smth)
            mesh.computeVertexNormals()
            mesh.getFaces.each do |f|
                retained.normal(f.a.normal.x, f.a.normal.y, f.a.normal.z)
                retained.vertex(f.a.x, f.a.y, f.a.z)
                retained.normal(f.b.normal.x, f.b.normal.y, f.b.normal.z)
                retained.vertex(f.b.x, f.b.y, f.b.z)
                retained.normal(f.c.normal.x, f.c.normal.y, f.c.normal.z)
                retained.vertex(f.c.x, f.c.y, f.c.z)
            end
        else
            mesh.getFaces.each do |f|
                retained.normal(f.normal.x, f.normal.y, f.normal.z)
                retained.vertex(f.a.x, f.a.y, f.a.z)
                retained.vertex(f.b.x, f.b.y, f.b.z)
                retained.vertex(f.c.x, f.c.y, f.c.z)
            end
        end
        retained.end()
        return retained
    end

    # variant
    # input array of meshes, output an array of shapes
    def meshToRetained(mesh, smth)
        rshapes = []
        (0 ... mesh.length).each do |i|
            rshapes.push(meshToVBO(mesh[i], smth))
        end
        return rshapes
    end
end



Here is the example sketch:-
# This example demonstrates how to rotate a number of meshes
# so that each points towards a common & user controlled focal point.
#
# Requires toxiclibs-0020 or newer
#
# (c) 2012 Karsten Schmidt / LGPL2 licensed
#
load_libraries 'toxiclibscore', 'vbo'

include_package 'toxi.geom'
include_package 'toxi.geom.mesh'

# container for mesh positions

attr_reader :positions, :vbo

def setup
  size(640,480,P3D)
  @vbo = MeshToVBO.new(self)
  @positions = []
  # compute mesh positions on circle in XZ plane
  (Circle.new(200).toPolygon2D(8)).each do |p|
    positions.push(p.to3DXZ)
  end
end

def draw
  background(51)
  lights
  no_stroke
  translate(width/2,height/2,0)
  rotate_x(-PI/6)
  # create manual focal point in XY plane
  focus = Vec3D.new((mouse_x - width/2), (mouse_y - height/2), 0)
  # create mesh prototype to draw at all generated positions
  # the mesh is a simple box placed at the world origin
  m = AABB.new(25).to_mesh
  # draw focus
  shape(vbo.meshToVBO(AABB.new(focus, 5).to_mesh, false))
  positions.each do |p|
    # align the positive z-axis of mesh to point at focus
    # mesh needs to be located at world origin for it to work correctly
    # only once rotated, move it to actual position
    shape(vbo.meshToVBO(m.copy.pointTowards(focus.sub(p), Vec3D::Z_AXIS).translate(p), false))
  end
  # draw connections from mesh centers to focal point
  stroke(0,255,255)
  positions.each do |p|
    line(p.x, p.y, p.z, focus.x, focus.y, focus.z)
  end
end

Wednesday, 12 December 2012

Fix for ruby-processing context free

The adoption of jruby-1.7.0 for the latest version of processing has required changes to the context_free.rb script. See the change on my Pembrokeshire Branch. My dragon sketch and city sketches working fine, I haven't checked any other. Here is my dragon sketch updated to ruby-1.9 syntax (NB: continues work with 1.8 syntax)



# contributed by monkstone

load_library 'context_free'

def setup_the_dragon
  @dragon = ContextFree.define do

    rule :start do
      dragon alpha: 1
    end

    rule :dragon do
      square hue: 0, brightness: 0, saturation: 1, alpha: 0.02
      split do
        dragon size: 1/Math.sqrt(2), rotation: -45, x: 0.25, y: 0.25
        rewind
        dragon size: 1/Math.sqrt(2), rotation: 135, x: 0.25, y: 0.25
        rewind
      end
    end

  end
end

def setup
  size 800, 500
  setup_the_dragon
  smooth
  draw_it
end

def draw
  # Do nothing.
end

def draw_it
  background 255
  @dragon.render :start, size: width*0.8,  stop_size: 2,
                         start_x: width/3, start_y: height/3.5
end




















Here's a sketch with a simple custom shape of an isosceles triangle
load_library 'context_free'

def setup_the_spiral
  @spiral= ContextFree.define do
    ############ Begin defining custom terminal, an isoceles triangle     
    class << self
      define_method(:isoceles) do |some_options| # isoceles triangle
        size, options = *self.get_shape_values(some_options)
        rot = options[:rotation]
        rotate(rot) if rot
        $app.triangle(-0.5 * size, -0.5 * size, -0.5 * size, 0.5 * size, 0.5 * size, 0.5 * size)
        rotate(-rot) if rot
      end
    end
    ########### End definition of custom terminal 'isoceles'
    rule :spiral do
      isoceles brightness: -1, rotation: 90
      spiral rotation: 135, size: 1/sqrt(2), x: 1/sqrt(2)
    end
  end
end

def setup
  size 800, 500
  setup_the_spiral
  draw_it
end

def draw
  # Do nothing.
end

def draw_it
  background 255
  @spiral.render :spiral, size: height, start_x: width/3, start_y: height/2
end

Wednesday, 5 December 2012

What works what does not and may not

I have just forked ruby-processing and updated my fork to match work in progress on my linux box to get ruby-processing running with the latest jruby-1.7.1 and latest processing-2.0b7 release. https://github.com/monkstone/ruby-processing clone it if you like. All samples should work, with the exception of the peasycam sketch (does work with an unofficially updated version of peasycam). Examples that rely on external "contributed" libraries probably will not work with the probable exception of toxiclibs (which is implementation agnostic). I don't think control panel likes working the latest version of processing (it was ok with processing-2.0b6).
Since web applets are no-longer supported with vanilla processing I don't think there's much call for it in ruby processing. For web presence with processing you are encouraged to write processing.js sketches (there is even preliminary support for coffeescript in processing ide). I have requested that my modified fork is pulled into core, which will make it easier to use but in the meantime you can clone it from github or fork it if you've a burning desire to implement something new.

Retained Shape (FBO) in Ruby Processing

Now we are cooking, this example demonstrated running ruby-processing compiled with a pre-released version of processing-2.0b7. Further it demonstrates the use of the FBO to vastly improve performance. All sketches run with this ruby-processing hack are very noisy (complaining about ambigous overloaded java methods eg background and fill).

BOX_SIZE = 20
MARGIN = BOX_SIZE * 2
DEPTH = 400
FINT = 3

attr_reader :box_fill, :grid, :fcount, :lastm, :frate

def setup
  size(640, 360, P3D)
  frame_rate(60)
  @fcount = 0
  @lastm = 0
  no_smooth()
  no_stroke()
  @grid = create_shape(GROUP)
  # Build grid using multiple translations 
  (-(DEPTH/2 + MARGIN) ... (DEPTH/2 - MARGIN)).step(BOX_SIZE) do |i|
    (-(height + MARGIN) ... (height - MARGIN)).step(BOX_SIZE) do |j|
      (-(width + MARGIN) ... (width - MARGIN)).step(BOX_SIZE) do |k|
        # Base fill color on counter values, abs function 
        # ensures values stay within legal range
        @box_fill = color(i.abs.to_i, j.abs.to_i, k.abs.to_i, 50)
        cube = create_shape(BOX, BOX_SIZE.to_f, BOX_SIZE.to_f, BOX_SIZE.to_f)
        cube.fill(box_fill)
        cube.translate(k, j, i)
        grid.add_child(cube)
      end
    end
  end
end

def draw
  background(255)
  hint(DISABLE_DEPTH_TEST)
  # Center and spin grid
  push_matrix()
  translate(width/2, height/2, -DEPTH)
  rotate_y(frame_count * 0.01)
  rotate_x(frame_count * 0.01)
  shape(grid)
  pop_matrix()
  hint(ENABLE_DEPTH_TEST)
  @fcount += 1
  m = millis()
  if (m - lastm > 1000 * FINT)
    @frate = fcount / FINT
    @fcount = 0
    @lastm = m
    puts("fps: #{frate}")
  end
  fill(0)
  text("fps: #{frate}", 10, 20)
end

Creating a Mock Vanilla Processing Distribution

It is possible to create a distribution version of processing from the git version. I wanted a mock version to use in my development version of ruby-processing.
These are the steps for a linux64 distribution:-
  1. Build the git version from the build folder
  2. cd linux/work
  3. mkdir processing-2.1.1
  4. mkdir processing-2.1.1/modes
  5. cp -rf modes/java processing-2.1.1/modes
  6. cp -rf core processing-2.1.1
  7. tar czvf processing-2.1.1-linux64.tgz processing-2.1.1
I do this so that I can test the latest development version of processing in ruby-processing.

Clone processing from github here:-

https://github.com/processing/processing
Updated 6 November 2013, to match github and most recent pre-version

Tuesday, 4 December 2012

Landscape shader in ruby-processing

Feels like I'm on a roll, here is the processing-2.0 landscape shader example running with ruby-processing.
##
# Landscape
# Simple raymarching shader with camera, originally by Paulo Falcão
# Ported from the webGL version in GLSL Sandbox:
# http://glsl.heroku.com/e//3213.0
#
##

attr_reader :landscape, :pg

def setup
  size(640, 360, P2D)

  # This effect can be too demanding on older GPUs, 
  # so we can render it on a smaller res offscreen surface
  # @pg = create_graphics(320, 180, P2D)
  @pg = create_graphics(640, 360, P2D)
  pg.no_stroke()
  @landscape = load_shader("landscape.glsl")
  landscape.set("resolution", width.to_f, height.to_f)
end

def draw
  landscape.set("time", millis() / 1000.0)
  landscape.set("mouse", mouse_x.to_f, height - mouse_y.to_f)

  # This kind of raymarching effects are entirely implemented in the
  # fragment shader, they only need a quad covering the entire view 
  # area so every pixel is pushed through the shader.  
  pg.begin_draw()
  pg.shader(landscape)
  pg.rect(0, 0, width, height)
  pg.end_draw()

  # Scaling up offscreen surface to cover entire screen (if reduced).
  image(pg, 0, 0, width, height)
end

Followers

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2