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

Wednesday 25 March 2015

Implicit isosurface sketch re-worked for JRubyArt

Since the release of the toxiclibs gem (example below use gem version 0.3.0.pre), it is now quite easy to work with the toxiclibs libraries in JRubyArt see sketch below. This being a bare sketch should be run with 'k9 run implicit.rb'

#
# This example implements a custom VolumetricSpace uMath.sing 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:
# drag mouse to rotate camera
# mouse wheel zoom in/out
# l: apply laplacian mesh smooth
# 
#

# 
# Copyright (c) 2010 Karsten Schmidt & ruby-procesMath.sing version Martin Prout 2013
# This sketch relies on a custom ruby-procesMath.sing mesh_to_vbo library
# 
# 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
#

require 'toxiclibs'

load_library :mesh_to_vbo

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :vbo, :curr_zoom, :implicit

def setup
  size(720,720, P3D)
  Processing::ArcBall.init(self)
  @vbo = MeshToVBO.new(self)
  @curr_zoom = 1
  vol = EvaluatingVolume.new(TVec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = Volume::HashIsoSurface.new(vol)
  @mesh = Toxi::WETriangleMesh.new
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
  no_stroke
  @implicit = vbo.meshToVBO(mesh, true)
  implicit.setFill(color(222, 222, 222))
  implicit.setAmbient(color(50, 50, 50))
  implicit.setShininess(color(10, 10, 10))
  implicit.setSpecular(color(50, 50, 50))
end

def draw
  background(0)
  lights
  define_lights
  shape(implicit)
end

def key_pressed
  case key
  when 'l', 'L'
    Toxi::LaplacianSmooth.new.filter(mesh, 1)
    @implicit = vbo.meshToVBO(mesh, true)
    # new mesh so need to set finish
    implicit.setFill(color(222, 222, 222))
    implicit.setAmbient(color(50, 50, 50))
    implicit.setShininess(color(10, 10, 10))
    implicit.setSpecular(color(50, 50, 50))
  when 's', 'S'
    save_frame("implicit.png")
  end
end

def define_lights
  ambient_light(50, 50, 50)
  point_light(30, 30, 30, 200, -150, 0)
  directional_light(0, 30, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2)
end

class EvaluatingVolume < Volume::VolumetricSpace

  attr_reader :upper_bound, :lut
  FREQ = Math::PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
  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 = Math.sin(xx * FREQ) + Math.cos(yy * FREQ) + Math.sin(zz * FREQ)
      val = Math.cos(xx * FREQ) * Math.sin(yy* FREQ) + Math.cos(yy* FREQ) * Math.sin(zz* FREQ) + Math.cos(zz* FREQ)* Math.sin(xx* FREQ)
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end


The library code:-
############################################
# mesh_to_vbo.rb
# a ruby library to convert toxi.mesh object
# to vbo (PShape) written by Martin Prout
############################################
class MeshToVBO
  PShape = Java::ProcessingCore::PShape
  attr_reader :parent

  def initialize(parent)
    @parent = parent
  end

  def meshToVBO(mesh, smth)
    retained = parent.create_shape
    retained.begin_shape(PShape::TRIANGLES)
    if smth
      mesh.compute_vertex_normals
      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.get_faces.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_shape
    retained
  end

  # variant
  # input array of meshes, output an array of shapes
  def meshToRetained(mesh, smth)
    mesh.map { |m| meshToVBO(m, smth) }
  end
end

No comments:

Post a Comment

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