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

Saturday, 4 February 2012

Using the Control Panel With Toxiclibs

Here is a recent sketch modified to use the ruby processing control panel (doesn't want to work with opengl with current release), next obvious variant to include sliders for resolution and or upper and lower boundary levels.
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export, and
# features the ruby-processing control panel (doesn't seem to work with opengl)
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'control_panel'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.volume'
include_package 'toxi.processing'

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :gfx, :is_wire_frame, :curr_zoom

def setup()
  size(720,720, P3D)
  @curr_zoom = 1.0
  control_panel do |c|
    c.title = "Implicit Function"
    c.slider :curr_zoom, 0.5..4.0, 1.0
    c.button :wire_frame_view
    c.button :smooth
    c.button :save_sketch_view
    c.button :export_to_povray
    c.button :exit!
  end
  @gfx = ToxiclibsSupport.new(self)
  vol = EvaluatingVolume.new(Vec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new()
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw()
  background(0)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x(mouse_y * 0.01)
  rotate_y(mouse_x * 0.01)
  scale(curr_zoom)
  if (is_wire_frame)
    no_fill()
    stroke(255)
  else
    fill(255)
    no_stroke()
    lights()
  end
  @gfx.mesh(mesh, true)
end

def wire_frame_view
  @is_wire_frame = !is_wire_frame
end

def smooth
  LaplacianSmooth.new().filter(mesh, 1)
end

def export_to_povray
  file_id = "implicit"
  pw = create_writer(file_id + ".inc")
  mesh.saveAsPOV(pw)
  pw.flush()
  pw.close()
  exit()
end

def save_sketch_view
  save_frame("implicit.png")
end

class EvaluatingVolume < VolumetricSpace
  include Processing::Proxy
  include_package 'toxi.math'
  attr_reader :upper_bound, :lut
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
    @lut=SinCosLUT.new()
  end

  def clear()
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      #val = lut.sin(xx * FREQ) + lut.cos(yy * FREQ) + lut.sin(zz * FREQ)
      val = lut.sin(xx * FREQ) * (xx * FREQ) + lut.sin(yy * FREQ) * (yy * FREQ) + lut.sin(zz * FREQ) * (zz * FREQ)
      #val = lut.cos(xx * FREQ) * lut.sin(yy* FREQ) + lut.cos(yy* FREQ) * lut.sin(zz* FREQ) + lut.cos(zz* FREQ)* lut.sin(xx* FREQ);
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end
Screenshot of ruby processing sketch

Using toxiclibs, volumeutils etc in Ruby-Processing

Here is a much more complicated example using toxiclibs in ruby-processing (once more relies on my custom toxiclibscore for the PovRAY export). However you can easily remove that part of the sketch if just want to experiment with using toxiclibs in ruby-processing. This example features the implementation of an abstract java class (namely VolumetricSpace) in jruby.
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Usage:
# move mouse to rotate camera
# w: toggle wireframe on/off
# -/=: zoom in/out
# l: apply laplacian mesh smooth
# 
#

# 
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export
# 
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'opengl'
include_package 'processing.opengl'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.volume'
include_package 'toxi.processing'

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :gfx, :curr_zoom, :is_wire_frame

def setup()
  size(720,720, (library_loaded?(:opengl) ? OPENGL : P3D))
  @gfx = ToxiclibsSupport.new(self)
  @curr_zoom = 1
  vol = EvaluatingVolume.new(Vec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new()
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw()
  background(0)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x(mouse_y * 0.01)
  rotate_y(mouse_x * 0.01)
  scale(curr_zoom)
  if (is_wire_frame)
    no_fill()
    stroke(255)
  else
    fill(255)
    no_stroke()
    lights()
  end
  @gfx.mesh(mesh, true)
end

def key_pressed()
  case key
  when 'w', 'W'
    @is_wire_frame = !is_wire_frame
  when '-'
    @curr_zoom -= 0.1
  when'='
    @curr_zoom += 0.1
  when 'l', 'L'
    LaplacianSmooth.new().filter(mesh, 1)
  when 'e', 'E'
    file_id = "implicit"
    pw = create_writer(file_id + ".inc")
    mesh.saveAsPOV(pw)
    pw.flush()
    pw.close()
    exit()
  when 's', 'S'
    save_frame("implicit.png")
  end
end

class EvaluatingVolume < VolumetricSpace
  include Processing::Proxy
  include_package 'toxi.math'
  attr_reader :upper_bound, :lut
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
    @lut=SinCosLUT.new()
  end

  def clear()
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      val = lut.sin(xx * FREQ) + lut.cos(yy * FREQ) + lut.sin(zz * FREQ)
      #val = Math.sin(xx * FREQ) * (xx * FREQ) + Math.sin(yy * FREQ) * (yy * FREQ) + Math.sin(zz * FREQ) * (zz * FREQ)
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end

opengl rendered implicit function

Thursday, 2 February 2012

Export toxiclibs mesh from ruby-processing to PovRAY

Here is my test ruby processing sketch (using my customised toxiclibscore.jar), that exports the toxiclibs mesh as a PovRAY mesh2 object, written to a PovRAY inc file.
#######################################################################
# FtestPOV demonstrates how to save a model as PovRAY mesh2
# format to a Java PrintWriter, called with the processing convenience 
# method create_writer.
#
# Copyright (c) 2012 Martin Prout
#
# This library example is free software you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation either version 2.1 of the License, or (at
# your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##########################################################################


load_libraries 'toxiclibscore', 'toxiclibs_p5'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.processing'

attr_reader :mesh, :gfx

def setup()
  size(200, 200, P3D)
  @gfx = ToxiclibsSupport.new(self)
  # define a "F" badly
  vert = AABB.fromMinMax(Vec3D.new(-1.0, -4.5, -1.0), Vec3D.new(1.0, 3.5, 1.0))
  box = AABB.fromMinMax(Vec3D.new(1.0, 1.5, -1.0), Vec3D.new(3.0, 3.5, 1.0))
  box2 = AABB.fromMinMax(Vec3D.new(1.0, -2.5, -1.0), Vec3D.new(3.0, -0.5, 1.0))
  @mesh = box.toMesh()
  mesh.addMesh(vert.toMesh())
  mesh.addMesh(box2.toMesh())
end

def draw()
  translate(width/2, height/2)
  scale(15)
  rotate_y(radians(-10))
  gfx.mesh(mesh)
end

def keyPressed()
  if (key == 'e')
    file_id = "FTest"
    pw = create_writer(file_id + ".inc")
    mesh.save_as_pov(pw, false)
    pw.flush()
    pw.close()
    exit()
  end
  if (key == 's')
    save_frame("Frb.png")
  end
end
Here is the pov file, that uses the geometry from the exported ruby-processing sketch, to create a PovRAY scene description file.
// Persistence Of Vision Ray Tracer Scene Description File
// File:  Simple Scene <template for povwriter>
// Vers: 3.7
// Date: January 2012
// Auth: Martin Prout 

#version 3.7;

global_settings{
  assumed_gamma 1.0
  radiosity{
    pretrace_start 0.04
    pretrace_end 0.01
    count 200
    recursion_limit 3
    nearest_count 10
    error_bound 0.5
  }
}

#include "colors.inc"
#include "skies.inc"
#include "FTest.inc"
#include "golds.inc"


//----------------declare scene Settings
#declare camera0 = camera {            // define additional cameras to suit viewing preferences
   location <-1.5, 30.0, -150.0>
   direction <0.0, 0.0, 2.0>
   up  <0.0, 1.0, 0.0>
   right <1.0, 0.0, 0.0>
   look_at <0.0, 25.0, 35.0>
}

#declare light0 = light_source { <100.0, 100.0, -200.0> colour White }

#declare ground0 = plane { <0.0, 1.0, 0.0>, 0.0  // a reflective ground plane
   pigment { NeonBlue }
   finish {reflection 0.15}
}

//------------------end of declare scene settings

// -----------------set the scene

camera { camera0 }              // ------------------------------------------
                                //  The use of declared values makes it possible to easily 
light_source{ light0 }          //  change the way the scene is rendered. Just define an
                                //  additional camera say for your template, and do the
sky_sphere{ S_Cloud3 }          //  substitution here. Retain the original definition and
                                //  you can easily backtrack. Definitions can also be saved
plane{ ground0 }                //  as included file see colors.inc for an example.
                                // ---------------------------------------------  
// -----------------end set the scene
object{
mesh2{aabb}                  // name aabb created by modified toxiclibs library
texture {T_Gold_1A}
scale<5, 5, 5>
rotate<0, 25, 0>
translate<0, 20, 40>
}

Processing sketch

PovRAY rendered

Export ruby-processing sketch to PovRAY mesh2

Updated 8 March hemesh library since 1.70 beta includes export code Here I present how to export from ruby-processing to PovRAY mesh2 format. The beauty of this approach is it does not rely on the processing rendering method, further the mesh2 object is better matched to PovRAY internals then raw triangles so rendering is quicker. However the major advantage is by using the normals calculated by the hemesh library (I'm using svn version 108 here) we get "smooth" surfaces (there is an option to exclude normals and view facets if desired). Here is the ruby processing sketch:-
#######################################################
# twin_iso.rb by Martin Prout aka monkstone
# Original sketch by Frederik Vanhoutte aka wblut
# Using svn(108) hemesh library (wblut)
# to export a ruby-processing sketch)
# to PovRAY mesh2 format (two meshes/one file)
######################################################

load_libraries 'hemesh', 'opengl'
include_package 'wblut.hemesh'
include_package 'wblut.hemesh.core'
include_package 'wblut.hemesh.creators'
include_package 'wblut.hemesh.modifiers'
include_package 'wblut.hemesh.subdividors'
include_package 'wblut.hemesh.tools'
include_package 'wblut.core.processing'

attr_accessor :render, :mesh, :inv_mesh, :display, :creator

def setup()
    size(800, 800, OPENGL)
    @display = true
    res = 20
    count = res + 1
    values=init_array(count, count, count)
    count.times do |i|
        count.times do |j|
            count.times do |k|
                values[i][j][k]=2.1*noise(0.35*i, 0.35*j, 0.35*k)
            end
        end
    end

    @creator=HEC_IsoSurface.new()
    creator.set_resolution(res, res, res)# number of cells in x,y,z direction
    creator.set_size(400.0/res, 400.0/res, 400.0/res) # cell size
    creator.set_values(values.to_java(Java::float[][]))# note cast to Javas
    # values can also be double[][][]
    creator.set_isolevel(1)# isolevel to mesh
    creator.set_invert(false)# invert mesh
    creator.set_boundary(100)# value of isoFunction outside grid
    # use creator.clearBoundary() to rest boundary values to "no value".
    # A boundary value of "no value" results in an open mesh

    @mesh=HE_Mesh.new(creator)
    mesh.modify(HEM_Smooth.new().set_iterations(20).set_auto_rescale(true))
    creator.set_invert(true)

    @inv_mesh=HE_Mesh.new(creator)
    inv_mesh.modify(HEM_Smooth.new().set_iterations(3).set_auto_rescale(true))
    @render=WB_Render.new(self)
end

def draw()
    if (display)
        screen_render()
    else
        povray_render()
    end

end

def screen_render()
    background(120)
    lights()
    translate(400, 400, 0)
    rotate_y(mouse_x*1.0/width*TWO_PI)
    rotate_x(mouse_y*1.0/height*TWO_PI)
    no_stroke()
    fill(255)
    render.draw_faces(mesh)
    fill(255, 0, 0)
    render.draw_faces(inv_mesh)
    stroke(0)
    render.draw_edges(mesh)
    stroke(255, 0, 0, 80)
    render.draw_edges(inv_mesh)
end

def povray_render()
    file_id = "mesh0"
    pw = create_writer(file_id + ".inc")
    HET_Export.saveToPOV(mesh, pw)   # (mesh, pw, false) no normals 
    HET_Export.saveToPOV(inv_mesh, pw)  # default is to use normals (smooth faces)
    pw.flush
    pw.close
    exit
end

def init_array(width, height, depth)
    Array.new(width).map!{ Array.new(height).map!{ Array.new(depth)}}
end

def key_pressed()
    case key
    when 'e', 'E'
        @display = false
    when 's', 'S'
        save_frame("twin_iso.png")
    end
end

Here is the associated "pov" file:-
// Persistence Of Vision Ray Tracer Scene Description File
// File:  Simple Scene <template for povwriter>
// Vers: 3.7
// Date: January 2012
// Auth: Martin Prout 

#version 3.7;

global_settings{
  assumed_gamma 1.0
  radiosity{
    pretrace_start 0.04
    pretrace_end 0.01
    count 200
    recursion_limit 3
    nearest_count 10
    error_bound 0.5
  }
}

#include "colors.inc"
#include "skies.inc"
#include "mesh0.inc"
#include "metals.inc"


//----------------declare scene Settings
#declare camera0 = camera {            // define additional cameras to suit viewing preferences
   location <-1.5, 30.0, -150.0>
   direction <0.0, 0.0, 2.0>
   up  <0.0, 1.0, 0.0>
   right <1.0, 0.0, 0.0>
   look_at <0.0, 25.0, 35.0>
}

#declare light0 = light_source { <100.0, 100.0, -200.0> colour White }

#declare ground0 = plane { <0.0, 1.0, 0.0>, 0.0  // a reflective ground plane
   pigment { NeonBlue }
   finish {reflection 0.15}
}

//------------------end of declare scene settings

// -----------------set the scene

camera { camera0 }              // ------------------------------------------
                                //  The use of declared values makes it possible to easily 
light_source{ light0 }          //  change the way the scene is rendered. Just define an
                                //  additional camera say for your template, and do the
sky_sphere{ S_Cloud3 }          //  substitution here. Retain the original definition and
                                //  you can easily backtrack. Definitions can also be saved
plane{ ground0 }                //  as included file see colors.inc for an example.
                                // ---------------------------------------------  
// -----------------end set the scene
object{
mesh2{ obj0 }                  // name obj0 created by modified Hemesh library
texture {T_Silver_5E}
scale<0.3, 0.3, 0.3>
rotate<0, 15, 0>
translate<0, 50, 400>
}

object{
mesh2{ obj2 }                  // name obj2 created by modified Hemesh library (why not 1?)
texture {T_Copper_1A}
scale<0.3, 0.3, 0.3>
rotate<0, 15, 0>
translate<0, 50, 400>
}
PovRAY rendered


Opengl Rendered

Thursday, 1 December 2011

GLGraphics in ruby-processing post processing (GLSL Fish Eye shader)




# This example applies a post-processing texture filter to the offscreen canvas
# in order to generate an angular fish-eye effect.
# Based on the following discussion thread in the Processing forum:
# http://forum.processing.org/topic/angular-fisheye
# Some other resources about fish-eye projections:
# 1) Excellent article from Paul Bourke on the math behind the angular fish-eye mapping  
# http://paulbourke.net/miscellaneous/domefisheye/fisheye/
# 2) Fish-eye effect implemented as a GLSL vertex shader:
# http://pixelsorcery.wordpress.com/2010/07/13/fisheye-vertex-shader/
# 3) Another fish-eye GLSL shader (didn't test it though):
# http://pages.cpsc.ucalgary.ca/~brosz/wiki/pmwiki.php/CSharp/08022008

load_libraries 'opengl', 'GLGraphics'
include_package "codeanticode.glgraphics"


attr_reader :canvas, :fisheye, :tex

def setup()
  size(300, 300, GLConstants.GLGRAPHICS)
  # If your video card isn't up to it use following (no antialias)
  #@canvas = GLGraphicsOffScreen.new(self, width, height)    
  @canvas = GLGraphicsOffScreen.new(self, width, height, true, 4)

  # Destination texture to store the fisheye image.
  @tex = GLTexture.new(self)

  @fisheye = GLTextureFilter.new(self, "FishEye.xml")
  # The aperture angle is specified in degrees. Values
  # greater than 180 can be specified, but the result won't
  # be consistent since the mapping is not one-to-one in
  # that case.
  fisheye.set_parameter_value("aperture", 180.0)
end

def draw()
  # Generating offscreen rendering:
  canvas.begin_draw
  canvas.background(0)
  canvas.lights
  canvas.stroke(255, 0, 0)
  (0...height).step(10) {|i| canvas.line(i, 0, i, height)}
  (0...width).step(10) {|j| canvas.line(0, j, width, j)}
  canvas.no_stroke
  canvas.translate(mouse_x, mouse_y, 100)
  canvas.rotate_x(frame_count * 0.01)
  canvas.rotate_y(frame_count * 0.01)
  canvas.box(50)
  canvas.end_draw
  fisheye.apply(canvas.get_texture(), tex)
  image(tex, 0, 0, width, height)
end

This is is an example included with vanilla processing GLGraphics library be sure to copy the shader files from the GLGraphics/Examples/Output/FishEye/data into a data folder (where fish_eye.rb is saved). See also faux fish eye here.

Saturday, 26 November 2011

Further Prototyping of my Povwriter Library in ruby-processing

Update 22 December I have gone on to modify my java povwriter library, incorporating some of ideas developed here. This sketch is incompatible with that new library, however old library is still available here.

It really is much easier to prototype in ruby/ruby processing vs java/vanilla processing. Recently I had the idea of splitting up the povray files as follows:-
  1. pov.ini to hold size (resolution) and quality (ray-trace parameter
  2. pov.pov to hold template (camera/background etc)
  3. pov.inc to hold mesh data etc from the processing sketch
In this iteration of prototyping I get to do 1 and 2, as 3 will involve a rewrite of my povwriter library, which I'm putting off until is clear whether beginRaw/endRaw will work in processing 2.0 (scheduled for release in Spring 2012?). I have decided less is definetly more for my gui, there are now only 3 quality options and the raytrace button now also initiates the recording of the PovRAY files as well as starting PovRAY. Further I've split the ini writer/povray runner into a new class as follows:-



# Demonstrates how you can use my povwriter libary
# in Ruby-Processing, to export to PovRAY

load_libraries 'povwriter', 'control_panel'
import 'povexport'
require 'ini_writer'

DATA = [-1, 0, 1]
attr_reader :exporter, :precord, :pquality, :type, :pview, :povfile

def setup
  size 1000, 1000, P3D
  @povfile = "balls.pov"
  @precord = false
  @pview = false
  @exporter = PovExporter.new self
  control_panel do |c|
    c.title = "Render as PovRAY"
    c.menu(:quality, ['instant', 'medium', 'high'], 'instant') {|m| load_quality_choice(m) }
    c.button :choose_template
    c.button :raytrace
    c.button :exit!
  end
  # uncomment next enter the path to povray executable on mac/windows/linux (or possibly jedit linux)  
  exporter.set_povray_path('/usr/local/bin/povray') # choice is saved in '.povwriter/povwriter.properties'
  # uncomment following line to adjust degenerate triangle cut off (area squared)
  # export.set_epsilon("0.001")
  exporter.store_settings    # uncomment this line to save settings   
  no_stroke
  sphere_detail(18)
end

def draw
  lights          # this needs to be outside the record loop
  if (precord)
    no_lights     # let PovRAY do the lighting
    no_loop       # don't loop while recording sketch
    begin_raw("povexport.RawPovray", data_path(povfile))
  end
  if pview
    pov_image = load_image(data_path(povfile.gsub("pov", "png")))
    update_pixels
    background(255) # clear the screen   
    image(pov_image, 0, 0)
  else
    render
  end
  if (precord)
    end_raw
    @precord = false
    puts "done recording"
    trace = IniWriter.new(data_path(povfile), width, height, pquality)
    trace.raytrace exporter.get_povray_path
    @pview = true
  end
end

def exit!
  exit
end

def render()
  background(0)
  translate(width*0.7, height/2, -width)
  DATA.each do |y|
    DATA.each do |x|
      DATA.each do |z|
        push_matrix
        translate(120*x, 120*y, 120*z)
        fill(rand * 255, rand * 255, rand * 255) # a nice test for my colorFactory class
        exporter.sphere(60) # use a better sphere primitive for ray tracing
        pop_matrix
      end
    end
  end
end



################################### Independent of sketch ############################

def raytrace
  @precord = true
end

def choose_template
  exporter.choose_template
end

def load_quality_choice(item)
  @pquality = item
end



Here is the ini writer class..................

require 'pathname'

INI = {'instant' => 'quick.ini', 'medium' => 'medium.ini', 'high' => 'quality.ini'}
QUALITY = {'instant' => 4, 'medium' => 6, 'high' => 11}

class IniWriter

  attr_reader :name, :width, :height, :qual, :base, :ini
 def initialize name, width, height, qual
   @name, @width, @height, @qual = name, width, height, qual
   init
 end

 def init
   @base = File.dirname(name)
   if (QUALITY[qual] < 6 && width > 300)
     @height *= 300.0/width
     @width = 300
   end
 end

 def write
   @ini = File.join(base, INI[qual])
   file = open(ini, 'w')
   file.puts("; #{ini}")
   file.puts("Input_File_Name='#{name}'")
   file.puts("width=#{width}")
   file.puts("height=#{height}")
   file.puts("quality=#{QUALITY[qual]}")
   file.puts("Antialias=true") unless QUALITY[qual] < 10
   file.puts("Output_File_Type=N8")
   file.puts("Output_File_Name='#{name.gsub('pov', 'png')}'")
   file.close
 end

 def raytrace povray_path
   write
   system([povray_path, ini].join(" "))
 end
end


Wednesday, 16 November 2011

Exporting Sketches from Ruby-Processing to PovRAY, features control panel

It has taken a lot longer that it should have done, but finally I got round to exploring my vanilla processing povwriter library in ruby processing. Thanks to the latest version of ruby-processing, we can use the vanilla processing library from the sketchbook folder. This works fine and dandy at present, but I can foresee potential conflicts with libraries that have been modified to run with processing-2.0, but then lots of libraries are probably going to get screwed along the way to the new version (my povwriter library for one does not work with processing-2.0). Here's one of my povwriter example files translated to ruby:-



# Demonstrates how you can use my povwriter libary
# in Ruby-Processing, to export to PovRAY

load_libraries 'povwriter', 'control_panel'
import 'povexport'

DATA = [-1, 0, 1]
attr_reader :exporter, :precord, :pquality, :type, :pview, :memory, :povfile

def setup
  size 300, 300, P3D
  @povfile = "balls.pov"
  @precord = false
  @exporter = PovExporter.new self
  control_panel do |c|
    c.title = "Render as PovRAY"
    c.menu(:quality, ['low', 'medium', 'high', 'highest'], 'high') {|m| load_quality_choice(m) }
    c.menu(:image_type, ['png', 'jpeg', 'tga'], 'png') {|m| load_image_type(m) }
    c.menu(:memory, ['low', 'default', 'high', 'none'], 'default') {|m| load_memory_choice(m) }
    c.checkbox :antialias
    c.button :choose_template
    c.button :record
    c.button :raytrace
    c.button :view
    c.button :exit!
  end
  # uncomment next enter the path to povray executable on mac/windows/linux (or possibly jedit linux)  
  exporter.set_povray_path('/usr/local/bin/povray') # choice is saved in '.povwriter/povwriter.properties'
  # uncomment following line to adjust degenerate triangle cut off (area squared)
  # export.set_epsilon("0.001")
  exporter.store_settings    # uncomment this line to save settings   
  no_stroke
  sphere_detail(18)
end

def draw
  lights          # this needs to be outside the record loop
  if (precord)
    no_lights   # let PovRAY do the lighting
    no_loop       # don't loop while recording sketch
    begin_raw("povexport.RawPovray", data_path(povfile))
  end
  if pview
    background(0) # clear the screen
    image(load_image(data_path("balls.png")), 0, 0)
    no_loop
  else
    render
  end
  if (precord)
    end_raw
    @precord = false
    puts "done recording"
    loop
  end
end

def exit!
  exit
end

def render()
  background(0)
  translate(width*0.7, height/2, -width)
  DATA.each do |y|
    DATA.each do |x|
      DATA.each do |z|
        push_matrix
        translate(120*x, 120*y, 120*z)
        fill(rand * 255, rand * 255, rand * 255) # a nice test for my colorFactory class
        exporter.sphere(60) # use a better sphere primitive for ray tracing
        pop_matrix
      end
    end
  end
end

################################### Independent of sketch ############################
def record
  @precord = true
end

def raytrace
  Kernel.system([exporter.get_povray_path, povray_options, povray_scene].join(" "))
end

def choose_template
  exporter.choose_template
end

def view
  @pview = true
end

def povray_scene
  return "+I#{data_path(povfile)}"
end

def povray_options
  aa = @antialias ? "+A" : ""
  return "+IM#{memory} +W#{width} +H#{height} +Q#{pquality} #{aa} +F#{type}"
end


def load_quality_choice(item)
  map = {'low' => 0, 'medium' => 5, 'high' => 9, 'highest' => 11}
  @pquality = map[item]
end

def load_memory_choice(item)
  map = {'low' => 56, 'default' => 128, 'high' => 1024, 'none' => 0}
  @memory = map[item]
end

def load_image_type(item)
  map = {'png' => 'N', 'jpeg' => 'J', 'tga' => 'C'}
  @type = map[item]
end

Sketch now modified to use the excellent control panel of ruby-processing. Here I am using a custom sphere for raytracing, ideally I would just call the PovRAY sphere primitive, but that is a bit more complicated than it appears at first sight (see my vanilla processing blog). Presently everything is exported as a simple triangle mesh (ideally this would be a PovRAY mesh2 object). Make life easy for yourself and wrap drawing logic in a function such as render. Click on images below to see full size....

Live Display of PovRAY rendering

Viewing Rendered image in processing sketch



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