UNPKG

marching

Version:

Marching.js is a JavaScript library that compiles GLSL ray marchers.

247 lines (194 loc) 8 kB
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