marching
Version:
Marching.js is a JavaScript library that compiles GLSL ray marchers.
1,712 lines (1,417 loc) • 1.87 MB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
const SceneNode = require( './sceneNode.js' )
const { param_wrap, MaterialID } = require( './utils.js' )
const { Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen, int_var_gen } = require( './var.js' )
const ops = {
Onion: {
func( sdf,thickness ) { return `vec2( opOnion( ${sdf}.x, ${thickness} ), ${sdf}.y )` },
variables:[['thickness', 'float', .03]],
def:`
float opOnion( in float sdf, in float thickness ){
return abs(sdf)-thickness;
}
vec2 opOnion( vec2 sdf, float thickness ) {
float x = 0.;
sdf.x = opOnion( sdf.x, thickness );
return sdf;
}
`
},
Halve: {
func( sdf, direction ) { return `vec2( opHalve( ${sdf}.x, p, ${direction} ), ${sdf}.y )` },
variables:[['direction','int',0]],
def:`
float opHalve( in float sdf, vec4 p, in int dir ){
float _out = 0.;
switch( dir ) {
case 0:
_out = max( sdf, p.y );
break;
case 1:
_out = max( sdf, -p.y );
break;
case 2:
_out = max( sdf, p.x );
break;
case 3:
_out = max( sdf, -p.x );
break;
}
return _out;
}
vec2 opHalve( vec2 sdf, vec4 p, int dir ) {
float x = 0.;
x = opHalve( sdf.x, p, dir );
sdf.x = x;
return sdf;
}
`
},
Round: {
func( sdf, amount ) { return `vec2( ${sdf}.x - ${amount}, ${sdf}.y )` },
variables:[['amount','float',.1]],
def:`
`
}
}
//vec4 opElongate( in vec3 p, in vec3 h ) {
// //return vec4( p-clamp(p,-h,h), 0.0 ); // faster, but produces zero in the interior elongated box
// vec3 q = abs(p)-h;
// return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
//}
const pushString = function( name ) {
const glslobj = ops[ name ].def
// some definitions are a single string, and not split into
// separate float and opOut functions
if( typeof glslobj === 'string' ) {
if( Alterations.__glsl.indexOf( glslobj ) === -1 ) {
Alterations.__glsl.push( glslobj )
}
}
}
const Alterations = {
__glsl:[],
__getGLSL() {
return this.__glsl.join('\n')
},
__clear() { this.__glsl.length = 0 }
}
for( let name in ops ) {
// get codegen function
let op = ops[ name ]
// create constructor
Alterations[ name ] = function( sdf, ...args ) {
const __op = Object.create( Alterations[ name ].prototype )
__op.sdf = sdf
__op.variables = []
__op.__desc = { parameters:[] }
for( let i = 0; i < op.variables.length; i++ ) {
const propArray = op.variables[ i ]
const propName = propArray[ 0 ]
const propType = propArray[ 1 ]
const propValue = args[ i ] === undefined ? propArray[ 2 ] : args[ i ]
__op.__desc.parameters.push({ name:propName, value:propValue })
let param
switch( propType ) {
case 'int':
param = int_var_gen( propValue )()
break;
default:
param = float_var_gen( propValue )()
break;
}
Object.defineProperty( __op, propName, {
get() { return param },
set(v) { param.set( v ) }
})
__op.variables.push( param )
}
__op.matId = MaterialID.alloc()
return __op
}
Alterations[ name ].prototype = SceneNode()
Alterations[ name ].prototype.emit = function ( __name ) {
const emitterA = this.sdf.emit( __name )
pushString( name )
//const emitterB = this.b.emit()
const output = {
out: op.func( emitterA.out, ...this.variables.map( v => v.emit() ) ),
preface: (emitterA.preface || '')
}
return output
}
Alterations[name].prototype.emit_decl = function () {
let str = this.sdf.emit_decl()
for( let v of this.variables ) {
str += v.emit_decl()
}
return str
};
Alterations[name].prototype.update_location = function(gl, program) {
this.sdf.update_location( gl, program )
for( let v of this.variables ) v.update_location( gl, program )
}
Alterations[name].prototype.upload_data = function(gl) {
this.sdf.upload_data( gl )
for( let v of this.variables ) v.upload_data( gl )
}
}
Alterations.Halve.UP = 0
Alterations.Halve.DOWN = 1
Alterations.Halve.LEFT = 3
Alterations.Halve.RIGHT = 2
module.exports = Alterations
},{"./sceneNode.js":23,"./utils.js":28,"./var.js":29}],2:[function(require,module,exports){
const Audio = {
__hasInput: false,
ctx: null,
bins:null,
start( bins=null ) {
if( Audio.__hasInput === false ) {
Audio.ctx = new AudioContext()
Audio.createInput().then( input => {
if( bins !== null ) Audio.bins = bins
Audio.createFFT()
input.connect( Audio.FFT )
Audio.interval = setInterval( Audio.fftCallback, 1000/60 )
//window.FFT = Audio.FFT
})
Audio.__hasInput = true
}else{
if( bins !== null ) Audio.bins = bins
}
},
createInput() {
console.log( 'connecting audio input...' )
const p = new Promise( resolve => {
console.log( 'start?' )
navigator.mediaDevices.getUserMedia({ audio:true, video:false })
.then( stream => {
console.log( 'audio input connected' )
Audio.input = Audio.ctx.createMediaStreamSource( stream )
//Audio.mediaStreamSource.connect( Gibberish.node )
Audio.__hasInput = true
resolve( Audio.input )
})
.catch( err => {
console.log( 'error opening audio input:', err )
})
})
return p
},
createFFT() {
Audio.FFT = Audio.ctx.createAnalyser()
let __windowSize = 512
Object.defineProperty( Audio, 'windowSize', {
configurable:true,
get() { return __windowSize },
set(v){
__windowSize = v
Audio.FFT.fftSize = v
Audio.FFT.values = new Uint8Array( Audio.FFT.frequencyBinCount )
}
})
Audio.windowSize = 512
},
fftCallback() {
Audio.FFT.getByteFrequencyData( Audio.FFT.values )
let lowSum, midSum, highSum, lowCount, midCount, highCount
lowSum = midSum = highSum = lowCount = midCount = highCount = 0
let frequencyCounter = 0
// does this start at 0Hz? ack... can't remember... does it include DC offset?
const hzPerBin = (Audio.ctx.sampleRate / 2) / Audio.FFT.frequencyBinCount
const lowRange = 150, midRange = 1400, highRange = Audio.ctx.sampleRate / 2
if( Audio.bins === null ) {
for( let i = 1; i < Audio.FFT.frequencyBinCount; i++ ) {
if( frequencyCounter < lowRange ) {
lowSum += Audio.FFT.values[ i ]
lowCount++
}else if( frequencyCounter < midRange ) {
midSum += Audio.FFT.values[ i ]
midCount++
}else{
highSum += Audio.FFT.values[ i ]
highCount++
}
frequencyCounter += hzPerBin
}
Audio.low = (lowSum / lowCount) / 255
Audio.mid = (midSum / midCount) / 255 || 0
Audio.high = (highSum / highCount) / 255
}else{
const sums = {}
for( let bin = 0; bin < Audio.bins.length; bin++ ) {
const frequency = Audio.bins[ bin ]
sums[ bin ] = { count: 0, value: 0 }
for( let i = 1; i < Audio.FFT.frequencyBinCount; i++ ) {
if( frequencyCounter < frequency ) {
sums[ bin ].value += Audio.FFT.values[i]
sums[ bin ].count++
frequencyCounter += hzPerBin
}else{
break
}
}
Audio[ bin ] = (sums[ bin ].value / sums[ bin ].count) / 255
}
}
}
}
module.exports = Audio
},{}],3:[function(require,module,exports){
const SceneNode = require( './sceneNode.js' ),
{ param_wrap, MaterialID } = require( './utils.js' ),
{ Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen } = require( './var.js' )
const { Vec2, Vec3, Vec4 } = require( './vec.js' )
const BG = function( Scene, SDF ) {
const Background = function( color ) {
if( SDF.memo.background === undefined ) {
const bg = Object.create( Background.prototype )
if( color !== undefined && color.type === 'vec3' ) color = Vec4( color.x, color.y, color.z, 1 )
const __color = param_wrap( Vec4( color ), vec4_var_gen( 0,0,0,1, 'bg' ), 'bg' )
Object.defineProperty( bg, 'color', {
get() { return __color },
set( v ) {
__color.var.set( v )
}
})
// this refers to the current scene via implicit binding in scene.js
//this.postprocessing.push( bg )
bg.__backgroundColor = color
this.__background = bg
SDF.memo.background = true
}
return this
}
Background.prototype = SceneNode()
Object.assign( Background.prototype, {
emit() {
return ''// this.color.emit()
},
emit_decl() {
//let str = this.color.emit_decl()
//SDF.memo.background = true
const out = this.__backgroundColor === undefined
? 'vec4 bg = vec4(0.,0.,0.,1.);'
: `vec4 bg = vec4(${ this.__backgroundColor.x }, ${this.__backgroundColor.y}, ${this.__backgroundColor.z}, 1.);`
return out
},
update_location( gl, program ) {
this.color.update_location( gl, program )
},
upload_data( gl ) {
this.color.upload_data( gl )
}
})
return Background
}
module.exports = BG
},{"./sceneNode.js":23,"./utils.js":28,"./var.js":29,"./vec.js":30}],4:[function(require,module,exports){
const vec3 = require('gl-vec3')
const mat4 = require('gl-mat4')
// camera adapted from https://github.com/shama/first-person-camera
function FirstPersonCamera(opts) {
if (!(this instanceof FirstPersonCamera)) return new FirstPersonCamera(opts)
opts = opts || {}
this.position = opts.position || vec3.create()
this.rotation = opts.rotation || vec3.create()
this.positionSpeed = opts.positionSpeed || -.5
this.rotationSpeed = opts.rotationSpeed || .01
}
module.exports = FirstPersonCamera
FirstPersonCamera.prototype.view = function(out) {
if (!out) out = mat4.create()
// altered x/y ordering from original
mat4.rotateY(out, out, this.rotation[1])
mat4.rotateX(out, out, this.rotation[0])
mat4.rotateZ(out, out, this.rotation[2] - Math.PI)
mat4.translate(out, out, [-this.position[0], -this.position[1], -this.position[2]])
return out
}
FirstPersonCamera.prototype.control = function(dt, move, mouse, prevMouse) {
var speed = (this.positionSpeed / 1000) * dt
var dir = [0,0,0]
if (move[0]) dir[2] -= speed * (Marching.keys.Alt ? 4 : 1 )
else if (move[1]) dir[2] += speed * (Marching.keys.Alt ? 4 : 1 )
if (move[2]) dir[0] += speed * (Marching.keys.Alt ? 4 : 1 )
else if (move[3]) dir[0] -= speed * (Marching.keys.Alt ? 4 : 1 )
if (move[4]) dir[1] -= speed * (Marching.keys.Alt ? 4 : 1 )
else if (move[5]) dir[1] += speed * (Marching.keys.Alt ? 4 : 1 )
this.move(dir)
// just use arrow keys instead of mouse
// this.pointer(mouse, prevMouse)
}
FirstPersonCamera.prototype.move = function(dir) {
if (dir[0] !== 0 || dir[1] !== 0 || dir[2] !== 0) {
var cam = mat4.create()
mat4.rotateY(cam, cam, this.rotation[1])
mat4.rotateX(cam, cam, this.rotation[0])
vec3.transformMat4(dir, dir, cam)
vec3.add(this.position, this.position, dir)
this.parent.pos.dirty = true
}
}
//FirstPersonCamera.prototype.pointer = function(da, db) {
// var dt = [da[0] - db[0], da[1]- db[1]]
// var rot = this.rotation
// rot[1] -= dt[0] * this.rotationSpeed
// if (rot[1] < 0) rot[1] += Math.PI * 2
// if (rot[1] >= Math.PI * 2) rot[1] -= Math.PI * 2
// rot[0] -= dt[1] * this.rotationSpeed
// if (rot[0] < -Math.PI * .5) rot[0] = -Math.PI*0.5
// if (rot[0] > Math.PI * .5) rot[0] = Math.PI*0.5
//}
const Camera = {
init( gl, program, handler ) {
const camera = FirstPersonCamera({
fov: 190,
near:.01,
far:10,
direction:[0,0,1],
viewport:[1,1,1,-1]
})
camera.rotation = [0,Math.PI,Math.PI]
Camera.__camera = camera
camera.parent = this
const camera_pos = gl.getUniformLocation( program, 'camera_pos' )
const camera_normal = gl.getUniformLocation( program, 'camera_normal' )
const camera_rot = gl.getUniformLocation( program, 'camera_rot' )
const ucamera = gl.getUniformLocation( program, 'camera' )
this.pos = { dirty:false }
this.dir = { dirty:true }
this.__rot = { dirty:true, value:0 }
Object.defineProperty( this, 'rotation', {
configurable:true,
get() { return this.__rot.value },
set(v) {
this.__rot.value = v
this.__rot.dirty = true
}
})
let px = 0, py =0, pz = 5, nx = 0, ny = 0, nz = 0
Object.defineProperties( this.pos, {
x: {
get() { return px },
set(v) { px = camera.position[0] = v;this.dirty = true; }
},
y: {
get() { return py },
set(v) { py = camera.position[1] = v; this.dirty = true; }
},
z: {
get() { return pz },
set(v) { pz = camera.position[2] = v; this.dirty = true; }
},
})
Object.defineProperties( this.dir, {
x: {
get() { return nx },
set(v) { nx = camera.rotation[0] = v; this.dirty = true; }
},
y: {
get() { return ny },
set(v) { ny = camera.rotation[1] = v; this.dirty = true; }
},
z: {
get() { return nz },
set(v) { nz = camera.rotation[2] = v; this.dirty = true; }
},
})
let init = false
gl.uniform3f( camera_normal, this.dir.x, this.dir.y, this.dir.z )
camera.position = [this.pos.x, this.pos.y, this.pos.z ]
//camera.update()
gl.uniform3f( camera_pos, this.pos.x, this.pos.y, this.pos.z )
gl.uniformMatrix4fv( ucamera, false, camera.view() )
gl.uniform1f( camera_rot, this.rot )
Camera.move = (x,y,z) => {
// XXX does this need to update property values?
camera.move([x,y,z])
Camera.update()
}
Camera.moveTo = (x,y,z) => {
Camera.pos.x = x
Camera.pos.y = y
Camera.pos.z = z
}
Camera.update = ()=> {
const pos = camera.position
gl.uniform3f( camera_pos, pos[0], pos[1], pos[2] )
gl.uniformMatrix4fv( ucamera, false, camera.view() )
}
// determine an offset from the current camera position based
// on the current camera rotation e.g. to always position a light
// behind the camera.
Camera.offset = (amt=[0,0,3]) => {
const cam = mat4.create()
mat4.rotateY(cam, cam, camera.rotation[1])
mat4.rotateX(cam, cam, camera.rotation[0])
vec3.transformMat4(amt, amt, cam)
return amt
}
let prvx = 0, prvy = 0, x = 0, y = 0
Camera.__mousemovefnc = e => {
prvx = x
prvy = y
x = e.pageX
y = e.pageY
}
let prevTime = 0
let k = Marching.keys
Camera.__framefnc = t => {
if( k.ArrowLeft ) camera.rotation[1] += camera.rotationSpeed
if( k.ArrowRight ) camera.rotation[1] -= camera.rotationSpeed
if( k.ArrowUp && !k.Shift ) camera.rotation[0] -= camera.rotationSpeed
if( k.ArrowDown && !k.Shift) camera.rotation[0] += camera.rotationSpeed
if( Marching.cameraEnabled ) {
camera.control(
t*1000 - prevTime,
[k.w,k.s,k.d,k.a,k.ArrowUp && k.Shift, k.ArrowDown && k.Shift],
[x,y], [prvx,prvy]
)
Camera.update()
prvx = x
prvy = y
prevTime = t*1000
}
}
Camera.__mousemove = null
Camera.on = ()=> {
if( Camera.__mousemove === null ) {
window.addEventListener( 'mousemove', Camera.__mousemovefnc )
Camera.__mousemove = true
}
if( Marching.callbacks.indexOf( Camera.__framefnc ) === -1 ) {
Marching.callbacks.push( Camera.__framefnc )
}
}
handler( ()=> {
if( this.pos.dirty === true ) {
//camera.position = [this.pos.x, this.pos.y, this.pos.z ]
//camera.position = [this.pos.x, this.pos.y, this.pos.z ]
//camera.update()
const pos = camera.position
gl.uniform3f( camera_pos, pos[0], pos[1], pos[2] )
gl.uniformMatrix4fv( ucamera, false, camera.view() )
this.pos.dirty = false
}
// XXX this is broken and needs to be fixed
if( this.dir.dirty === true ) {
gl.uniform3f( camera_normal, this.dir.x, this.dir.y, this.dir.z )
gl.uniformMatrix4fv( ucamera, false, camera.view() )
this.dir.dirty = false
}
if( this.__rot.dirty === true ) {
gl.uniform1f( camera_rot, this.__rot.value )
this.__rot.dirty = false
}
if( typeof this.onmove === 'function' ) {
this.onmove( this )
}
})
}
}
module.exports = Camera
},{"gl-mat4":115,"gl-vec3":149}],5:[function(require,module,exports){
const SceneNode = require( './sceneNode.js' )
const { param_wrap, MaterialID } = require( './utils.js' )
const { Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen, int_var_gen, VarAlloc } = require( './var.js' )
const Transform = require( './transform.js' )
const ops = {
// this needs to create an opOut, not return a vec2
Displace( __name ) {
let name = __name === undefined ? 'p' : __name
const sdf = this.sdf.emit( name );
const sdfStr = `float d1${this.id} = ${sdf.out}.x;\n`
let displaceString = `float d2${this.id} = sin( ${this.amount.emit()}.x * ${name}.x ) * `
displaceString += `sin( ${this.amount.emit()}.y * ${name}.y ) * `
displaceString += `sin( ${this.amount.emit()}.z * ${name}.z );\n`
displaceString += `${sdf.out}.x = (d1${this.id} + d2${this.id}*${this.size.emit()})*.5;\n`
const output = {
out: `${sdf.out}`,
preface: sdf.preface + sdfStr + displaceString
}
return output
},
Halve( __name ) {
let name = __name === undefined ? 'p' : __name
// XXX why?
this.transform.invert()
const bumpString = ` vec4 transformBump${this.id} = ${name} * ${this.transform.emit()};\n`
const pointString = ` vec4 transformBump2${this.id} = (transformBump${this.id} * ${this.sdf.transform.emit()});\n`
const sdf = this.sdf.emit( `transformBump2${this.id}` );
let displaceString = `${sdf.out}.x = opHalve( ${sdf.out}.x, transformBump2${this.id}, int(${this.amount.emit()}) );`
const output = {
out: `${sdf.out}`,
preface: bumpString + pointString + sdf.preface + displaceString
}
return output
},
Bend( __name ) {
let name = __name === undefined ? 'p' : __name
const sdf = this.sdf.emit( 'q'+this.id );
let preface=` float c${this.id} = cos( ${this.amount.emit()}.x * ${name}.x );
float s${this.id} = sin( ${this.amount.emit()}.x * ${name}.x );
mat2 m${this.id} = mat2( c${this.id},-s${this.id},s${this.id},c${this.id} );
vec4 q${this.id} = vec4( m${this.id} * ${name}.xy, ${name}.z, 1. );\n`
if( typeof sdf.preface === 'string' ) {
preface += sdf.preface
}
return { preface, out:sdf.out }
},
Twist( __name ) {
let name = __name === undefined ? 'p' : __name
const sdf = this.sdf.emit( 'q'+this.id );
let preface=` float c${this.id} = cos( ${this.amount.emit()}.x * ${name}.y );
float s${this.id} = sin( ${this.amount.emit()}.x * ${name}.y );
mat2 m${this.id} = mat2( c${this.id},-s${this.id},s${this.id},c${this.id} );
vec4 q${this.id} = vec4( m${this.id} * ${name}.xz, ${name}.y, 1. );\n`
if( typeof sdf.preface === 'string' ) {
preface += sdf.preface
}
return { preface, out:sdf.out }
},
__Bump( __name ) {
let name = __name === undefined ? 'p' : __name
const bumpString = ` vec4 transformBump${this.id} = ${name} * ${this.transform.emit()};\n`
const tex = this.amount.emit( name )
const pointString = `(transformBump${this.id} * ${this.sdf.transform.emit()})`
const sdf = this.sdf.emit( pointString, this.transform, `tex${this.id}` )
Marching.textures.addTexture( this.amount.value )
let preface=` vec3 tex${this.id} = getTexture( ${this.amount.value.id}, ${pointString}.xyz ) * ${this.size.emit()};\n
//vec4 displaceBump${this.id} = vec4((${pointString} - tex${this.id}), 1.);
`
//${sdf.out}.x = (tex${this.id}.x + tex${this.id}.y + tex${this.id}.z ) / 3. * .5 + ${sdf.out}.x;\n`
//vec4 ${'p'+this.id} = vec4(${pointString} + tex${this.id}, 1.);\n`
//sdf.preface += `\n
// ${sdf.out}.x -= min(tex${this.id}.x, min(tex${this.id}.y, tex${this.id}.z));\n`
if( typeof sdf.preface === 'string' ) {
preface = preface + sdf.preface
}
preface = bumpString + preface
return { preface, out:sdf.out }
},
// XXX todo: something like https://www.shadertoy.com/view/ldSGzR
// https://www.dropbox.com/s/l1yl164jb3rhomq/mm_sfgrad_bump.pdf?dl=0
Bump( __name ) {
let name = __name === undefined ? 'p' : __name
const bumpString = ` vec4 transformBump${this.id} = ${name} * ${this.transform.emit()};\n`
const tex = this.amount.emit( name )
const pointString = `(transformBump${this.id} * ${this.sdf.transform.emit()}).xyz`
const sdf = this.sdf.emit( `transformBump${this.id}`, this.transform )
Marching.textures.addTexture( this.amount.value )
let preface=` vec3 tex${this.id} = getTexture( ${this.amount.value.id}, ${pointString}) * ${this.size.emit()};
${sdf.out}.x = (tex${this.id}.x + tex${this.id}.y + tex${this.id}.z)/3. + ${sdf.out}.x;\n`
if( typeof sdf.preface === 'string' ) {
preface = sdf.preface + preface
}
preface = bumpString + preface
return { preface, out:sdf.out }
},
}
const DistanceOps = {}
for( let name in ops ) {
// get codegen function
let __op = ops[ name ]
// create constructor
DistanceOps[ name ] = function( a,b,c ) {
const op = Object.create( DistanceOps[ name ].prototype )
op.sdf = a
op.amount = b
op.emit = __op
op.name = name
op.transform = Transform()
const defaultValues = [.5,.5,.5]
op.id = VarAlloc.alloc()
const isArray = true
if( name === 'Halve' ) {
op.amount = param_wrap( b, float_var_gen( b ) )
}else{
if( typeof b === 'number' ) {
b = [b,b,b]
b.type = 'vec3'
}
}
if( name !== 'Bumpz' ) {
let __var = param_wrap(
b,
vec3_var_gen( ...defaultValues )
)
// for assigning entire new vectors to property
Object.defineProperty( op, 'amount', {
get() { return __var },
set(v) {
if( typeof v === 'object' ) {
__var.set( v )
}else{
__var.value.x = v
__var.value.y = v
__var.value.z = v
__var.value.w = v
__var.dirty = true
}
}
})
op.params = [{ name:'amount' }]
}else{
op.params = []
op.emit_decl = function() {}
op.emit = function() {}
op.update_data= function() {}
op.upload_location = function() {}
}
op.__setMaterial = function(mat) {
if( typeof mat === 'string' ) mat = Marching.Material[ mat ]
this.__material = this.mat = Marching.materials.addMaterial( mat )
op.sdf.material( this.__material )
}
if( name === 'Displace' || name === 'Bump' ) {
let __var2 = param_wrap(
c,
float_var_gen( .03 )
)
Object.defineProperty( op, 'size', {
get() { return __var2 },
set(v) {
__var2.set( v )
__var2.dirty = true
}
})
op.params.push({ name:'size' })
}
op.__desc = { parameters:op.params }
return op
}
DistanceOps[ name ].prototype = SceneNode()
DistanceOps[name].prototype.emit_decl = function () {
let str = this.sdf.emit_decl() + (this.name !== 'Bump' ? this.amount.emit_decl() : '')
str += this.transform.emit_decl()
if( this.name === 'Displace' || this.name === 'Bump' ) str += this.size.emit_decl()
return str
};
DistanceOps[name].prototype.update_location = function(gl, program) {
this.sdf.update_location( gl, program )
if( this.name !== 'Bump' ) this.amount.update_location( gl, program )
if( this.name === 'Displace' || this.name === 'Bump') this.size.update_location( gl, program )
this.transform.update_location( gl, program )
}
DistanceOps[name].prototype.upload_data = function(gl) {
this.sdf.upload_data( gl )
if( this.name !== 'Bump' ) this.amount.upload_data( gl )
if( this.name === 'Displace' || this.name === 'Bump' ) this.size.upload_data( gl )
this.transform.upload_data( gl )
}
}
DistanceOps.Halve.UP = 0
DistanceOps.Halve.DOWN = 1
DistanceOps.Halve.LEFT = 3
DistanceOps.Halve.RIGHT = 2
module.exports = DistanceOps
},{"./sceneNode.js":23,"./transform.js":27,"./utils.js":28,"./var.js":29}],6:[function(require,module,exports){
const SceneNode = require( './sceneNode.js' )
const { param_wrap, MaterialID } = require( './utils.js' )
const { Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen, int_var_gen, VarAlloc } = require( './var.js' )
const Transform = require( './transform.js' )
const glslops = require( './distanceOperationsGLSL.js' )
const opslen = {
Union:2,
Intersection:2,
Difference:2,
StairsUnion:4,
StairsIntersection:4,
StairsDifference:4,
RoundUnion:3,
RoundDifference:3,
RoundIntersection:3,
ChamferUnion:3,
ChamferDifference:3,
ChamferIntersection:3,
Pipe:3,
Engrave:3,
Groove:4,
Tongue:4,
// these two do not currently have support for transforms or repeats...
Onion:2,
Switch:2
}
const ops = {
Union( ...args ) { return `opU( ${args.join(',')} )` },
SmoothUnion( ...args ) { return `opSmoothUnion( ${args.join(',')} )` },
Intersection( ...args ) { return `opI( ${args.join(',')} )` },
SmoothIntersection( ...args ) { return `opSmoothIntersection( ${args.join(',')} )` },
Difference( ...args ) { return `opS( ${args.join(',')} )` },
SmoothDifference( ...args ) { return `opSmoothSubtraction( ${args.join(',')} )` },
StairsUnion( ...args ) { return `fOpUnionStairs( ${args.join(',')} )` },
StairsIntersection( ...args ) { return `fOpIntersectionStairs( ${args.join(',')} )` },
StairsDifference( ...args ) { return `fOpSubstractionStairs( ${args.join(',')} )` },
RoundUnion( ...args ) { return `fOpUnionRound( ${args.join(',')} )` },
RoundDifference( ...args ) { return `fOpDifferenceRound( ${args.join(',')} )` },
RoundIntersection( ...args ) { return `fOpIntersectionRound( ${args.join(',')} )` },
ChamferUnion( ...args ) { return `fOpUnionChamfer( ${args.join(',')} )` },
ChamferDifference( ...args ) { return `fOpDifferenceChamfer( ${args.join(',')} )` },
ChamferIntersection( ...args ) { return `fOpIntersectionChamfer( ${args.join(',')} )` },
Pipe( ...args ) { return `fOpPipe( ${args.join(',')} )` },
Engrave( ...args ) { return `fOpEngrave( ${args.join(',')} )` },
Groove( ...args ) { return `fOpGroove( ${args.join(',')} )` },
Tongue( ...args ) { return `fOpTongue( ${args.join(',')} )` },
// these two do not currently have support for transforms or repeats...
Onion( ...args ) { return `opOnion( ${args.join(',')} )` },
Switch( a,b,c,d,e,f ) { return `opSwitch( ${a}, ${b}, ${c} )` }
}
const emit_float = function( a ) {
if (a % 1 === 0)
return a.toFixed( 1 )
else
return a
}
const DistanceOps = {
__glsl:[],
__getGLSL() {
return this.__glsl.join('\n')
},
__clear() { this.__glsl.length = 0 }
}
const oneops = ['Onion','Halve']
for( let name in ops ) {
// get codegen function
let op = ops[ name ]
const name2 = name + '2'
// create constructor
DistanceOps[ name ] = function( a,b,c,d ) {
const op = Object.create( DistanceOps[ name ].prototype )
op.a = a
op.b = oneops.indexOf( name ) === -1 ? b : param_wrap( b, float_var_gen(.2 ) )
op.transform = Transform( false )
op.id = VarAlloc.alloc()
op.type = 'distance_op'
op.name = name
let __c = param_wrap( c, float_var_gen(.3) )
op.__len = opslen[ name ]
if( op.__len > 2 ) {
Object.defineProperty( op, 'c', {
get() { return __c },
set(v) {
__c.set( v )
}
})
if( op.__len > 3 ) {
let __d = param_wrap( d, float_var_gen(4) )
Object.defineProperty( op, 'd', {
get() { return __d },
set(v) {
__d.set( v )
}
})
}
}
op.__setTexture = function(tex,props) {
if( typeof tex === 'string' ) {
this.texture = op.texture.bind( this )
this.__textureObj = this.tex = Marching.Texture( tex,props,this.texture )
this.__textureID = this.__textureObj.id
}else{
this.__textureObj = this.tex = Object.assign( tex, props )
this.__textureID = this.__textureObj.id
}
}
op.__setMaterial = function(mat) {
if( typeof mat === 'string' ) mat = Marching.Material[ mat ]
this.__material = this.mat = Marching.materials.addMaterial( mat )
}
op.__setBump = function(tex,props) {
//this.bump = p.bump.bind( this )
const b = this.bump = this.__bumpObj = Marching.Bump( this, tex, props )
this.bump.texture = this.bump.amount.value
this.__bumpID = this.__bumpObj.id
this.rotate = this.bump.rotate
this.translate = this.bump.translate
this.scale = this.bump.scale
Object.defineProperty( this.bump, 'strength', {
get() { return b.size },
set(v){ b.size = v }
})
}
Object.assign( op, {
renderingBump : false,
emittingDecl : false,
uploading : false,
updating : false
})
let repeat = null
Object.defineProperty( op, 'repeat', {
get() { return repeat },
set(v){
repeat = v
this.a.repeat = v
this.b.repeat = v
}
})
op.matId = MaterialID.alloc()
op.params = [{name:'c'},{ name:'d'}]
op.__desc = { parameters: op.params }
return op
}
DistanceOps[ name2 ] = function( ...args ) {
// accepts unlimited arguments, but the last one could be a blending coefficient
let blend = .25, coeff=4, u
if( typeof args[ args.length - 1 ] === 'number' ) {
blend = args.pop()
// if there are two non-sdf arguments to the function...
if( typeof args[ args.length - 1 ] === 'number' ) {
coeff = blend
blend = args.pop()
}
u = args.reduce( (state,next) => DistanceOps[ name ]( state, next, blend, coeff ) )
}else{
u = args.reduce( (state,next) => DistanceOps[ name ]( state, next ) )
}
return u
}
DistanceOps[ name ].prototype = SceneNode()
DistanceOps[ name ].prototype.texture = function( ...args ) {
this.__setTexture( ...args )
this.a.texture( this.__textureObj )
if( oneops.indexOf( name ) === -1 )
this.b.texture( this.__textureObj )
return this
}
DistanceOps[ name ].prototype.material = function( ...args ) {
this.__setMaterial( ...args )
this.a.material( this.__material )
if( oneops.indexOf( name ) === -1 )
this.b.material( this.__material )
return this
}
const pushString = function( name ) {
const glslobj = glslops[ name ]
// some definitions are a single string, and not split into
// separate float and opOut functions
if( typeof glslobj === 'string' ) {
if( DistanceOps.__glsl.indexOf( glslobj ) === -1 ) {
DistanceOps.__glsl.push( glslobj )
}
}else{
// some distance operations are dependent on other ones...
// if this one has dependencies add them.
// dependencies must be added before adding other functions
// so that they're above them in the final GLSL code.
if( glslobj.dependencies !== undefined ) {
for( let dname of glslobj.dependencies ) {
const d = glslops[ dname ]
if( DistanceOps.__glsl.indexOf( d.float ) === -1 ) {
DistanceOps.__glsl.push( d.float )
}
}
}
if( DistanceOps.__glsl.indexOf( glslobj.float ) === -1 ) {
DistanceOps.__glsl.push( glslobj.float )
}
if( DistanceOps.__glsl.indexOf( glslobj.vec2) === -1 ) {
DistanceOps.__glsl.push( glslobj.vec2 )
}
}
}
DistanceOps[ name ].prototype.emit = function ( pname='p', transform = null ){
const isNotOneop = oneops.indexOf( name ) === -1
if( this.__bumpObj !== undefined && this.renderingBump === false) {
this.renderingBump = true
return this.__bumpObj.emit( pname, transform )
}
pushString( name )
if( transform !== null ) this.transform.apply( transform, false )
//this.transform.internal()
// first two args are fixed, rest are variable
let emitters = []
const a = this.a.emit( pname, this.transform )
const b = isNotOneop ? this.b.emit( pname, this.transform ) : this.b.emit()
emitters[0] = a.out
emitters[1] = isNotOneop ? b.out : b
if( this.__len > 2 ) emitters.push( this.c.emit() )
if( this.__len > 3 ) emitters.push( this.d.emit() )
const body = `
vec2 do${this.id} = ${op( ...emitters )};
do${this.id}.x *= ${this.transform.emit()}_scale;
`
const output = {
out: 'do'+this.id,
preface: (a.preface || '') + ( isNotOneop ? b.preface || '' : '') + body
}
this.renderingBump = false
return output
}
DistanceOps[name].prototype.emit_decl = function () {
if( this.__bumpObj !== undefined && this.emittingDecl === false) {
this.emittingDecl = true
return this.__bumpObj.emit_decl()
}
let str = this.transform.emit_decl() + this.a.emit_decl() + this.b.emit_decl()
if( this.c !== undefined ) str += this.c.emit_decl()
if( this.d !== undefined ) str += this.d.emit_decl()
if( ops[ name ].code !== undefined ) {
//str += ops[ name ].code
if( Marching.requiredOps.indexOf( ops[ name ].code ) === - 1 ) {
Marching.requiredOps.push( ops[ name ].code )
}
}
this.emittingDecl = false
return str
};
DistanceOps[name].prototype.update_location = function(gl, program) {
if( this.__bumpObj !== undefined && this.updating === false) {
this.updating = true
return this.__bumpObj.update_location( gl, program )
}
this.a.update_location( gl, program )
this.transform.update_location( gl, program )
this.b.update_location( gl, program )
if( this.c !== undefined ) this.c.update_location( gl, program )
if( this.d !== undefined ) this.d.update_location( gl, program )
this.updating = false
}
DistanceOps[name].prototype.upload_data = function(gl) {
if( this.__bumpObj !== undefined && this.uploading === false ) {
this.uploading = true
return this.__bumpObj.upload_data( gl )
}
this.transform.internal()
this.transform.upload_data( gl )
this.a.transform.apply( this.transform )
if( oneops.indexOf( name ) === -1 )
this.b.transform.apply( this.transform )
this.a.upload_data( gl )
this.b.upload_data( gl )
if( this.c !== undefined ) this.c.upload_data( gl )
if( this.d !== undefined ) this.d.upload_data( gl )
this.uploading = false
}
}
module.exports = DistanceOps
},{"./distanceOperationsGLSL.js":7,"./sceneNode.js":23,"./transform.js":27,"./utils.js":28,"./var.js":29}],7:[function(require,module,exports){
module.exports = {
Union:{
float:`
float opU( float d1, float d2 ) {
return min(d1,d2);
}
`,
vec2:`
vec2 opU( vec2 d1, vec2 d2 ) {
vec2 o;
if( d1.x < d2.x ) {
o = d1;
}else{
o = d2;
}
return o;
}
`
},
Intersection:{
float:`
float opI( float d1, float d2 ) {
return max(d1,d2);
}
`,
vec2:`
vec2 opI( vec2 d1, vec2 d2 ) {
vec2 o;
if( d1.x > d2.x ) {
o = d1;
}else{
o = d2;
}
return o;
}
`
},
Difference:{
float:`
float opS( float d1, float d2 ) { return max(d1,-d2); }
`,
vec2:`
vec2 opS( vec2 d1, vec2 d2 ) {
vec2 o;
if( d1.x >= -d2.x ) {
o = d1;
}else{
d2.x *= -1.;
o = d2;
}
return o;
}
`
},
StairsUnion:{
float:`
float fOpUnionStairs(float a, float b, float r, float n) {
float s = r/n;
float u = b-r;
return min(min(a,b), 0.5 * (u + a + abs ((mod (u - a + s, 2. * s)) - s)));
}`,
vec2:`
vec2 fOpUnionStairs( vec2 d1, vec2 d2, float r, float n ) {
vec2 o = vec2( 0., d1.y );
if( d1.x <= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
o.x = fOpUnionStairs( d1.x, d2.x, r, n );
return o;
}
`
},
StairsIntersection:{
dependencies: ['StairsUnion'],
float:`
// We can just call Union since stairs are symmetric.
float fOpIntersectionStairs(float a, float b, float r, float n) {
return -fOpUnionStairs(-a, -b, r, n);
}
`,
vec2:`
vec2 fOpIntersectionStairs( vec2 d1, vec2 d2, float r, float n ) {
vec2 o = vec2( 0., d1.y );
o.x = -fOpUnionStairs( -d1.x, -d2.x, r, n );
if( -d1.x <= -d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
StairsDifference:{
dependencies: ['StairsUnion'],
float:`
float fOpSubstractionStairs(float a, float b, float r, float n) {
return -fOpUnionStairs(-a, b, r, n);
}`,
vec2:`
vec2 fOpSubstractionStairs( vec2 d1, vec2 d2, float r, float n ) {
vec2 o = vec2( 0., d1.y );
o.x = -fOpUnionStairs( -d1.x, d2.x, r, n );
if( -d1.x <= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
RoundUnion:{
float:`
float fOpUnionRound(float a, float b, float r) {
vec2 u = max(vec2(r - a,r - b), vec2(0));
return max(r, min (a, b)) - length(u);
}`,
vec2:`
vec2 fOpUnionRound( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpUnionRound( d1.x, d2.x, r );
if( d1.x <= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
RoundIntersection:{
float:`
float fOpIntersectionRound(float a, float b, float r) {
vec2 u = max(vec2(r + a,r + b), vec2(0));
return min(-r, max (a, b)) + length(u);
}`,
vec2:`
vec2 fOpIntersectionRound( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpIntersectionRound( d1.x, d2.x, r );
if( d1.x >= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
RoundDifference:{
dependencies: ['RoundIntersection'],
float:`
float fOpDifferenceRound (float a, float b, float r) {
return fOpIntersectionRound(a, -b, r);
}`,
vec2:`
vec2 fOpDifferenceRound( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpDifferenceRound( d1.x, d2.x, r );
if( d1.x >= -d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
ChamferUnion:{
float:`
float fOpUnionChamfer(float a, float b, float r) {
return min(min(a, b), (a - r + b)*sqrt(0.5));
}`,
vec2:`
vec2 fOpUnionChamfer( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpUnionChamfer( d1.x, d2.x, r );
if( d1.x <= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
ChamferIntersection:{
float:`
float fOpIntersectionChamfer(float a, float b, float r) {
return max(max(a, b), (a + r + b)*sqrt(0.5));
}`,
vec2:`
vec2 fOpIntersectionChamfer( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpIntersectionChamfer( d1.x, d2.x, r );
if( d1.x >= d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
ChamferDifference:{
dependencies:['ChamferIntersection'],
float:`
float fOpDifferenceChamfer (float a, float b, float r) {
return fOpIntersectionChamfer(a, -b, r);
}`,
vec2:`
vec2 fOpDifferenceChamfer( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpDifferenceChamfer( d1.x, d2.x, r );
if( d1.x >= -d2.x ) {
o.y = d1.y;
}else{
o.y = d2.y;
}
return o;
}
`
},
Pipe:`
float fOpPipe(float a, float b, float r) {
return length(vec2(a, b)) - r;
}
vec2 fOpPipe( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpPipe( d1.x, d2.x, r );
return o;
}
`,
Engrave:`
float fOpEngrave(float a, float b, float r) {
return max(a, (a + r - abs(b))*sqrt(0.5));
}
vec2 fOpEngrave( vec2 d1, vec2 d2, float r ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpEngrave( d1.x, d2.x, r );
return o;
}
`,
Groove:`
float fOpGroove(float a, float b, float ra, float rb) {
return max(a, min(a + ra, rb - abs(b)));
}
vec2 fOpGroove( vec2 d1, vec2 d2, float r, float n ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpGroove( d1.x, d2.x, r, n );
return o;
}
`,
Tongue:`
float fOpTongue(float a, float b, float ra, float rb) {
return min(a, max(a - ra, abs(b) - rb));
}
vec2 fOpTongue( vec2 d1, vec2 d2, float r, float n ) {
vec2 o = vec2( 0., d1.y );
o.x = fOpTongue( d1.x, d2.x, r, n );
return o;
}
`,
Onion:`
float opOnion( float sdf, float thickness ){
return abs(sdf)-thickness;
}
vec2 opOnion( vec2 sdf, float thickness ) {
float x = 0.;
sdf.x = opOnion( sdf.x, thickness );
return sdf;
}
`,
Halve:`
float opHalve( in float sdf, vec4 p, in int dir ){
float _out = 0.;
switch( dir ) {
case 0:
_out = max( sdf, p.y );
break;
case 1:
_out = max( sdf, -p.y );
break;
case 2:
_out = max( sdf, p.x );
break;
case 3:
_out = max( sdf, -p.x );
break;
}
return _out;
}
vec2 opHalve( vec2 sdf, vec4 p, int dir ) {
float x = 0.;
x = opHalve( sdf.x, p, dir );
sdf.x = x;
return sdf;
}
`,
Switch:`
vec2 opSwitch( vec2 a, vec2 b, float c ) {
if( c < .5 ) {
return a;
}else{
return b;
}
}
`
}
},{}],8:[function(require,module,exports){
const { Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen, int_var_gen, VarAlloc } = require( './var.js' )
const SceneNode = require( './sceneNode.js' )
const { param_wrap, MaterialID } = require( './utils.js' )
const { Vec2, Vec3, Vec4 } = require( './vec.js' )
const Transform = require( './transform.js' )
const descriptions = {
Elongation: {
parameters:[ { name:'active', type:'float', default:1. },{ name:'distance', type:'vec3', default:Vec3(0) } ],
func:`
vec4 opElongate( in vec3 p, in vec3 h ) {
//return vec4( p-clamp(p,-h,h), 0.0 ); // faster, but produces zero in the interior elongated box
vec3 q = abs(p)-h;
return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
}`,
emit( name='p' ) {
const pId = this.getID()
const pName = 'p' + pId
let preface =
` vec4 ${pName}_xyzw = opElongate( ${name}, ${this.distance.emit()} );\n
vec3 ${pName} = ${pName}_xyzw.xyz;\n`
const sdf = this.sdf.emit( pName )
if( typeof sdf.preface === 'string' ) preface += sdf.preface
return { out:`vec2(${pName}_xyzw.w + ${sdf.out}.x, ${sdf.out}.y)`, preface }
}
},
PolarRepetition: {
parameters:[
{ name:'count', type:'float', default:5 },
{ name:'distance', type:'vec3', default:Vec3(.25) },
{ name:'active', type:'float', default:1. }
],
emit( name='p', transform=null) {
const pId = VarAlloc.alloc()
const pName = 'p' + pId
if( transform !== null ) this.transform.apply( transform, false )
this.transform.invert()
const pointString = `( ${name} * ${this.transform.emit()} ).xyz`
let preface =`
vec4 ${pName} = vec4( polarRepeat( ${pointString}, ${this.__target.count.emit() } ) * ${this.transform.emit_scale()}, 1. );
${pName} -= vec4(${this.__target.distance.emit()}.x,0.,0.,0.);\n`
const sdf = this.sdf.emit( pName )
if( typeof sdf.preface === 'string' ) preface += sdf.preface
return { out:sdf.out, preface }
}
},
Mirror: {
parameters: [ { name:'distance', type:'vec3', default:Vec3(0) },{ name:'active', type:'float', default:1. } ],
extra:[{ name:'dims', type:'local', default:'xyz' }],
emit( name='p', transform=null, notused=null, scale=null ) {
const pId = VarAlloc.alloc()
const pName = 'p' + pId
if( transform !== null ) {
this.transform.apply( transform, false )
}
this.transform.invert()
const pointString = `( ${name} * ${this.transform.emit()} ).xyz`,
s = scale === null ? this.transform.emit_scale() : `${this.transform.emit_scale()} * ${scale}`
let preface =`
vec4 ${pName} = vec4( ( ${pointString} ) , 1.);\n
${pName}.${this.dims} = abs( ${pName}.${this.dims} );\n`
const sdf = this.sdf.emit( pName, null, null, s )
if( typeof sdf.preface === 'string' ) preface += sdf.preface
return { out:sdf.out, preface }
}
},
//let preface = ` vec3 ${pName} = ${name} / ${this.amount.emit()};\n`
//let sdf = this.sdf.emit( pName )
//let out = sdf.out
//sdf.preface += ` ${out}.x = ${out}.x * ${this.amount.emit()};\n`
//if( typeof sdf.preface === 'string' ) preface += sdf.preface
Repetition: {
parameters: [ { name:'distance', type:'vec3', default:Vec3(0) }, { name:'active', type:'float', default:1. }],
emit( name='p', transform=null ) {
const pId = VarAlloc.alloc()
const pName = 'p' + pId
if( transform !== null ) this.transform.apply( transform, false )
this.transform.invert()
const pointString = `( ${name} * ${this.transform.emit()} ).xyz`;
let preface =`
vec4 ${pName} = vec4( (mod( ${pointString}, ${this.__target.distance.emit()} ) - .5 * ${this.__target.distance.emit()}) * ${this.transform.emit_scale()}, 1.);\n`
const sdf = this.sdf.emit( pName )//, this.transform )//, 1, this.__target.distance )
if( typeof sdf.preface === 'string' ) preface += sdf.preface
return { out:sdf.out, preface }
}
},
// https://www.shadertoy.com/view/wlyBWm
// https://www.shadertoy.com/view/NdS3Dh
SmoothRepetition: {
parameters: [ { name:'distance', type:'vec3', default:Vec3(0) }, { name:'smoothness', type:'float', default:.5 }],
emit( name='p', transform=null ) {
const pId = VarAlloc.alloc()
const pName = 'p' + pId
if( transform !== null ) this.transform.apply( transform, false )
this.transform.invert()
const pointString = `( ${name} * ${this.transform.emit()} ).xyz`;
// vec2 smoothrepeat_asin_sin(vec2 p,float smooth_size,float size){
// p/=size;
// p=asin(sin(p)*(1.0-smooth_size));
// return p*size;
let preface =`
vec3 ${pName}Mod = ${pointString}/${this.__target.distance.emit()};
${pName}Mod = asin( sin( ${pName}Mod ) * (1.0 - ${this.__target.smoothness.emit()} ) );
vec4 ${pName} = vec4( ${pName}Mod * ${this.__target.distance.emit()} * ${this.transform.emit_scale()}, 1. );\n`
const sdf = this.sdf.emit( pName )//, this.transform )//, 1, this.__target.distance )
if( typeof sdf.preface === 'string' ) preface += sdf.preface
return { out:sdf.out, preface }
}
},
SmoothPolar: {
parameters:[
{ name:'count', type:'float', default:5 },
{ name:'distance', type:'vec3', default:Vec3(.25) },
{ name:'active', type:'float', default:1. }
],
emit( name='p', transform=null) {
const pId = VarAlloc.alloc()
const pName = 'p' + pId
if( transform !== null ) this.transform.apply( transform, false )
this.transform.invert()
const pointString = `( ${name} * ${this.transform.emit()} ).xyz`
//s repetitions
////m smoothness (0-1)
////c correction (0-1)
////d object displace from center
/*vec2 smoothRot(vec2 p,float s,float m,float c,float d){
s*=0.5;
float k=length(p);
float x=asin(sin(atan(p.x,p.y)*s)*(1.0-m))*k;
float ds=k*s;
float y=mix(ds,2.0*ds-sqrt(x*x+ds*ds),c);
return vec2(x/s,y/s-d);
}*/
let preface =`
vec4 ${pName} = vec4( polarRepeat( ${pointString}, ${this.__target.count.emit() } ) * ${this.transform.emit_scale()}, 1. );
${pName} -= vec4(${this.__target.distance.emit()}.x,0.,