Created
April 6, 2015 06:52
-
-
Save monkstone/41c8566b064dc187246c to your computer and use it in GitHub Desktop.
Work in progress
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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