Skip to content

Instantly share code, notes, and snippets.

@monkstone
Created April 6, 2015 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monkstone/41c8566b064dc187246c to your computer and use it in GitHub Desktop.
Save monkstone/41c8566b064dc187246c to your computer and use it in GitHub Desktop.
Work in progress
require 'toxiclibs'
#
# <p>BoxFLuid demo combining 3D physics particles with the IsoSurface class to
# create an animated mesh with a fluid behaviour. The mesh is optionally created
# within a boundary sphere, but other forms can be created using a custom
# ParticleConstraint class.</p>
#
# <p>Dependencies:</p>
# <ul>
# <li>toxiclibscore-0015 or newer package from <a href="http://toxiclibs.org">toxiclibs.org</a></li>
# <li>verletphysics-0004 or newer package from <a href="http://toxiclibs.org">toxiclibs.org</a></li>
# <li>volumeutils-0002 or newer package from <a href="http://toxiclibs.org">toxiclibs.org</a></li>
# <li>controlP5 GUI library from <a href="http://sojamo.de">sojamo.de</a></li>
# </ul>
#
# <p>Key controls:</p>
# <ul>
# <li>w : wireframe on/off</li>
# <li>c : close sides on/off</li>
# <li>p : show particles only on/off</li>
# <li>b : turn bounding sphere on/off</li>
# <li>r : reset particles</li>
# <li>s : save current mesh as OBJ & STL format</li>
# <li>- / = : decrease/increase surface threshold/tightness</li>
# </ul>
#/
#
# Copyright (c) 2009 Karsten Schmidt
#
# This demo & 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
#
NUM_PARTICLES = 100
REST_LENGTH=183.0
DIM=100
GRID=9
VS =2.0 * DIM / GRID
SCALE=TVec3D.new(DIM,DIM,DIM).scale(2.0)
SphereConstraint = Java::ToxiPhysics3dConstraints::SphereConstraint
attr_reader :ui, :iso_threshold, :physics, :bounding_sphere, :mesh
attr_reader :surface, :show_physics, :is_wire_frame, :is_closed, :use_boundary
attr_reader :volume, :gravity, :col_amp, :num_p
def setup
size(640, 360, P3D)
smooth
init_physics
@volume=Volume::VolumetricSpaceArray.new(SCALE,GRID,GRID,GRID)
@surface=Volume::ArrayIsoSurface.new(volume)
@iso_threshold=1.5
@mesh=TriangleMesh.new('fluid')
@show_physics=false
@is_wire_frame=false
@is_closed=true
@use_boundary=false
@col_amp=TVec3D.new(200, 100, 100)
text_font(create_font('SansSerif', 12))
end
def draw
background(224)
hint(DISABLE_DEPTH_TEST)
fill(0)
text(format('faces: %d', mesh.getNumFaces), 20, 290)
text(format('vertices: %d', mesh.getNumVertices), 20, 305)
text(format('particles: %d', physics.particles.size), 20, 320)
text(format('springs: %d', physics.springs.size), 20, 335)
text(format('fps: %d', frame_rate), 20, 350)
hint(ENABLE_DEPTH_TEST)
update_particles
compute_volume
translate(width * 0.5, height * 0.5, 0)
rotate_x(mouse_y * 0.01)
rotate_y(mouse_x * 0.01)
no_fill
stroke(255, 192)
stroke_weight(1)
box(physics.get_world_bounds.get_extent.x * 2)
if show_physics
stroke_weight(4)
stroke(0)
physics.particles.each do |p|
col = p.add(col_amp).scale_self(1.0)
stroke(col.x, col.y, col.z)
point(p.x, p.y, p.z)
end
else
ambient_light(216, 216, 216)
directional_light(255, 255, 255, 0, 1, 0)
directional_light(96, 96, 96, 1, 1, -1)
if is_wire_frame
stroke(255)
no_fill
else
no_stroke
fill(224,0,51)
end
begin_shape(TRIANGLES)
is_wire_frame ? draw_wire_mesh : draw_filled_mesh
end_shape
end
end
def keyPressed
case key
when 'r'
init_physics
when 'w'
@is_wire_frame=!is_wire_frame
when 'p'
@show_physics=!show_physics
when 'c'
@is_closed=!is_closed
when 'b'
toggle_boundary
when '-', '_'
@iso_threshold -= 0.001
when '=', '+'
@iso_threshold += 0.001
when 's'
mesh.saveAsOBJ(sketchPath(format('%s.obj', mesh.name)))
mesh.saveAsSTL(sketchPath(format('%s.stl', mesh.name)))
end
end
def toggle_boundary
@use_boundary = !use_boundary
init_physics
end
def compute_volume
cell_size = DIM.to_f * 2 / GRID
pos = TVec3D.new
offset = physics.get_world_bounds.get_min
volume_data = volume.get_data
index = 0
GRID.times do |z|
pos.set_z(z * cell_size + offset.z)
GRID.times do |y|
pos.set_y(y * cell_size + offset.y)
GRID.times do |x|
pos.set_x(x * cell_size + offset.x)
val=0
physics.particles.each do |p|
mag = pos.distanceToSquared(p) + 0.00001
val += 1.0 / mag
end
volume_data[index] = val
index += 1
end
end
end
volume.close_sides if is_closed
surface.reset
surface.compute_surface_mesh(mesh, iso_threshold * 0.001)
end
def draw_filled_mesh
mesh.compute_vertex_normals
mesh.faces.each do |f|
col = f.a.add(col_amp).scale_self(1.0)
fill(col.x,col.y,col.z)
normal_v(f.a.normal)
vertex_v(f.a)
col=f.b.add(col_amp).scale_self(1.0)
fill(col.x,col.y,col.z)
normal_v(f.b.normal)
vertex_v(f.b)
col=f.c.add(col_amp).scale_self(1.0)
fill(col.x,col.y,col.z)
normal_v(f.c.normal)
vertex_v(f.c)
end
end
def draw_wire_mesh
no_fill
mesh.faces.each do |f|
col=f.a.add(col_amp).scale_self(1.0)
stroke(col.x,col.y,col.z)
vertex_v(f.a)
col=f.b.add(col_amp).scale_self(1.0)
stroke(col.x,col.y,col.z)
vertex_v(f.b)
col=f.c.add(col_amp).scale_self(1.0)
stroke(col.x,col.y,col.z)
vertex_v(f.c)
end
end
def normal_v(v)
normal(v.x, v.y, v.z)
end
def vertex_v(v)
vertex(v.x, v.y, v.z)
end
def init_physics
@physics=Physics::VerletPhysics3D.new
physics.set_world_bounds(AABB.new(TVec3D.new, TVec3D.new(DIM,DIM,DIM)))
unless surface.nil?
surface.reset
mesh.clear
end
@bounding_sphere=SphereConstraint.new(Toxi::Sphere.new(TVec3D.new,DIM),SphereConstraint::INSIDE)
@gravity=Physics::GravityBehavior3D.new(TVec3D.new(0, 0.6, 0))
physics.add_behavior(gravity)
end
def update_particles
grav = TVec3D::Y_AXIS.copy
grav.rotate_x(mouse_y * 0.01)
grav.rotate_y(mouse_x * 0.01)
gravity.setForce(grav.scale_self(2))
@num_p = physics.particles.size
if (rand < 0.8 && num_p < NUM_PARTICLES)
p = Physics::VerletParticle3D.new(TVec3D.new(random(-1,1)*10), -DIM, random(-1,1)*10)))
p.add_constraint(bounding_sphere) if use_boundary
physics.add_particle(p)
end
if (num_p > 5) && (physics.springs.size < 700)
30.times do
if rand < 0.04
q = physics.particles.get(rand(0...num_p))
r = q
Kernel.loop do
break unless q == r
r = physics.particles.get(rand(0...num_p))
physics.add_spring(Physics::VerletSpring3D.new(q, r, REST_LENGTH, 0.0001))
end
end
end
end
len = num_p.to_f / NUM_PARTICLES * REST_LENGTH
physics.springs.map { |s| s.set_rest_length(rand(0.9..1.1) * len) }
physics.update
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment