Constructive Solid Geometry Tool

calendar_today

27.03.2023

label

Modeling

mouse

Houdini 19.5

1 Introduction

In Development: A simple CSG tool which stacks smooth boolean operations such as union, subtract and intersect on solid geometries, ie. boxes and spheres.

2 Components

Solid objects: Sphere, Box, Tube, Capsule, Torus, Cone.
Boolean operations: Union, Subtract, Intersect.
Shapes: Sharp, Round, Chamfer, Columns, Stairs, Pipe.
Transformation: Size, Position, Rotation.
Repetition: None, Line, Grid, Radial, Mirror.

3 Code

Volume wrangle on a SDF volume. A loop is feeding menu input from a multiparm block into signed distance functions and combines them using boolean operations. sd and op are wrapper functions.

// SIGNED-DISTANCE FUNCTIONS
function float sd_sphere(vector xyz, pos; float r){
    float dist = length(xyz - pos) - r;
    return dist;
}

function float sd_box(vector xyz, pos, size){
    vector q = abs(xyz - pos) - size;
    float dist = length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
    return dist;
}

function float sd_capsule(vector xyz, pos, size){
    pos = xyz - pos;
    pos.y -= clamp(pos.y, 0.0, size[1]);
    return length(pos) - size[0];
}

function float sd(int type; vector xyz, pos, size){
    float dist = 0.0;
    if(type == 0)      dist = sd_sphere(xyz, pos, size[0]);
    else if(type == 1) dist = sd_box(xyz, pos, size);
    else               dist = sd_capsule(xyz, pos, size);
    return dist;
}

// BOOLEAN OPERATIONS
function float op_union(     float d1, d2) { return min(d1,  d2); }
function float op_subtract(  float d1, d2) { return max(-d1, d2); }
function float op_intersect( float d1, d2) { return max(d1,  d2); }

function float op_union_sm( float d1, d2, k ) {
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return lerp( d2, d1, h ) - k*h*(1.0-h); }

function float op_subtract_sm( float d1,  d2, k ) {
    float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
    return lerp( d2, -d1, h ) + k*h*(1.0-h); }

function float op_intersect_sm( float d1, d2, k ) {
    float h = clamp( 0.5 - 0.5*(d2-d1)/k, 0.0, 1.0 );
    return lerp( d2, d1, h ) + k*h*(1.0-h); }

function float op(int comb; float d_0, d_1, r){
    float dist = 0.0;
    if(r < 1e-3){
        if      (comb == 0) dist = op_union     (d_0, d_1);
        else if (comb == 1) dist = op_subtract  (d_0, d_1);
        else                dist = op_intersect (d_0, d_1);
    }
    else{
        if      (comb == 0) dist = op_union_sm     (d_0, d_1, r);
        else if (comb == 1) dist = op_subtract_sm  (d_0, d_1, r);
        else                dist = op_intersect_sm (d_0, d_1, r);
    }
    return dist;
}

// SCENE ASSEMBLY
float dist_scene = 0.0;
for(int i = 0; i <= chi('objects'); i++){
    string num = itoa(i);
    int type_obj = chi('type_obj' + num);
    int type_op = chi('type_op' + num);
    
    vector rot = set(chf('rx' + num), chf('ry' + num), chf('rz' + num));
    vector xyz = v@P * maketransform(0, rot);
    vector size = set(chf('sx' + num), chf('sy' + num), chf('sz' + num));
    vector pos = set(chf('px' + num), chf('py' + num), chf('pz' + num));
    
    float r_sm = chf('sm' + num);
    
    float d = sd(type_obj, xyz, pos, size);
    if(i == 0) dist_scene = d;
    else       dist_scene = op(type_op, d, dist_scene, r_sm);
}

f@d = dist_scene;

4 Sources

download

Downloads