How to draw colored 3D metaballs with toxiclibs ?

edited April 2018 in Library Questions

Hi,

Following this thread (where I was trying to gather informations on metaballs) I decided to use the toxiclibs library in order to draw one myself.

Here is the kind of result I would like to achieve:

from Zach Lieberman (see full motion here)

from Wenzy Yang (see full motion here)

Here's what I could do so far:

see HD here

Questions:

  • How to give each metaball a specific color like in the first picture ? I tried here but to no avail. It seems toxiclibs doesn't allow to assign different colors to multiple objects within a for loop.
  • How to apply linear interpolations to the mesh vertices (with the toxiclibs library) in order to get smoother contours ?
  • How can I improve the script to make the sketch run faster ? When I draw each face individually with coordinates-based colors (see GIF above) (from line 42 to 51 below) the sketch is running very slow (8 fps vs 60 fps without coordinates-based colors).

Alternatively, what I would like to know:

  • How did Wenzy Yang draw live such smooth contours (second picture) without experiencing any lag ? With toxiclibs, drawing colored metaballs from an equally dense VolumetricSpaceArray would be too computer intensive. (Hence my question on linear interpolations).

If @amnon or @toxmeister or anyone familiar with the toxiclibs library happen to read this question, I would be immensely grateful for your help !

PS: PLEASE do not move this thread to the "Python mode" category since all the questions are about the toxiclibs library !

full code:

    add_library('toxiclibs_p5')
    add_library('toxiclibscore')
    add_library('volumeutils')
    add_library('peasycam')
    from toxi.processing import ToxiclibsSupport

    mesh = TriangleMesh("mesh")
    iso, grid, dim = .3, 70, 700
    scl = Vec3D(dim, dim, dim)
    volume = VolumetricSpaceArray(scl, grid, grid, grid)
    surface, brush = ArrayIsoSurface(volume), RoundBrush(volume, scl.x() / 2)

    def setup():
        global balls, gfx, a, cam
        size(720, 720, P3D)
        smooth(8)

        cam, gfx, a = PeasyCam(this, 700), ToxiclibsSupport(this), Attractor(0, 0, 0, 1, 100)
        balls = []    
        [balls.append(Ball()) for e in range(10)]
        noStroke()
        beginShape(TRIANGLES)


    def draw():
        background(0)
        print frameRate

        volume.clear()
        for i, b in enumerate(balls):
            force = a.attract(b)
            b.applyForce(force)
            b.update()
            b.display()
        volume.closeSides()

        surface.reset()
        surface.computeSurfaceMesh(mesh,iso)
        mesh.computeVertexNormals()
        gfx.mesh(mesh, True)

        num = mesh.getNumFaces()
        for i in range(num):
            f = mesh.faces[i]
            fill(180 + f.a.x(), 180 + f.a.y(), 180 - f.a.z())
            vertex(f.a.x(), f.a.y(), f.a.z())
            fill(180 + f.b.x(), 180 + f.b.y(), 180 - f.b.z())
            vertex(f.b.x(), f.b.y(), f.b.z())
            fill(180 + f.c.x(), 180 + f.c.y(), 180 - f.c.z())
            vertex(f.c.x(), f.c.y(), f.c.z()) 
        endShape()

        cam.rotateY(.03)


    class Ball(object):
        def __init__(self):
            self.r = random(10, 220)
            self.pos = PVector(random(-100,100), random(-100,100), random(-100, 100))
            self.vel = PVector(0, 0, 0)
            self.acceleration = PVector(0, 0, 0)
            self.mass = 1 + (self.r * .01)

        def display(self):
            v = Vec3D(self.pos.x, self.pos.y, self.pos.z)
            brush.setSize(self.r)
            brush.drawAtAbsolutePos(v, 1)       

        def applyForce(self, f):
            f = f / self.mass
            self.acceleration += f *.5

        def update(self):
            self.vel += self.acceleration
            self.pos += self.vel
            self.acceleration *= 0
            self.vel.limit(10)

    class Attractor():
        def __init__(self, x, y, z, m, g):
            self.location = PVector(x, y, z)
            self.mass = m
            self.G = g

        def attract(self, m):
            force = PVector.sub(self.location, m.pos)
            d = force.mag()
            d = constrain(d, 5, 25)
            force.normalize()
            strength = (self.G * self.mass * m.mass) / (d * d)

            if keyPressed and m.mass < 1.5:
                    force.mult(-strength*2)

        return force
Sign In or Register to comment.