UNPKG

marching

Version:

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

379 lines (304 loc) 12.3 kB
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.,0.,0.);\n` const sdf = this.sdf.emit( pName ) if( typeof sdf.preface === 'string' ) preface += sdf.preface return { out:sdf.out, preface } } }, } const getDomainOps = function( SDF ) { const ops = {} for( let key in descriptions ) { const opDesc = descriptions[ key ] ops[ key ] = function( sdf, ...args ) { const op = Object.create( ops[ key ].prototype ) op.sdf = sdf op.parameters = [] op.transform = Transform() op.name = key const target = op.__target = op // sdf.__target !== undefined ? sdf.__target : op let count = 0 for( let prop of opDesc.parameters ) { op.parameters.push( prop ) let arg = args[ count ] let __var switch( prop.type ) { case 'vec2': if( typeof arg === 'number' ) arg = Vec2( arg ) if( arg === undefined ) arg = prop.default.copy() __var = param_wrap( arg, vec2_var_gen( prop.default ) ) Object.defineProperty( target, prop.name, { get() { return __var }, set(v) { if( typeof v === 'object' ) { __var.set( v ) }else{ __var.value.x = v __var.value.y = v __var.dirty = true } } }) break; case 'vec3': if( typeof arg === 'number' ) arg = Vec3( arg ) if( arg === undefined ) arg = prop.default.copy() __var = param_wrap( arg, vec3_var_gen( prop.default ) ) Object.defineProperty( target, prop.name, { 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.dirty = true } } }) break; case 'vec4': if( typeof arg === 'number' ) arg = Vec4( arg ) __var = param_wrap( arg, vec4_var_gen( prop.default ) ) if( arg === undefined ) arg = prop.default.copy() Object.defineProperty( target, prop.name, { 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 } } }) break; default: // float __var = param_wrap( arg, float_var_gen( prop.default ) ) Object.defineProperty( target, prop.name, { get() { return __var }, set(v) { __var.set( v ) } }) break; } count++ } if( opDesc.extra !== undefined ) { for( let extra of opDesc.extra ) { op[ extra.name ] = args[ count - 1 ] || extra.default } } op.sdf.active = op.active 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.__desc = opDesc if( key !== 'Mirror' ) op.sdf.repeat = op return op } ops[ key ].prototype = SceneNode() ops[ key ].prototype.emit = opDesc.emit ops[ key ].prototype.texture = function( ...args ) { this.__setTexture( ...args ) this.sdf.texture( this.__textureObj ) return this } ops[ key ].prototype.material = function( ...args ) { this.__setMaterial( ...args ) this.sdf.material( this.__material ) return this } ops[ key ].prototype.emit_decl = function( shouldEmitSDF=true ) { let decl = '' decl += this.transform.emit_decl() for( let param of this.parameters ) { decl += this.__target[ param.name ].emit_decl() } if( shouldEmitSDF ) decl += this.sdf.emit_decl() // for rotation etc... any extra glsl function that needs to // be added to the shader if( opDesc.glsl !== undefined && SDF.memo[ key ] === undefined ) { decl += opDesc.glsl SDF.memo[ key ] = true } return decl } ops[ key ].prototype.update_location = function( gl, program, shouldUpdateSDF=true ) { for( let param of this.parameters ) this.__target[ param.name ].update_location( gl, program) if( shouldUpdateSDF ) this.sdf.update_location( gl, program ) this.transform.update_location( gl, program ) } ops[ key ].prototype.upload_data = function( gl, shouldUploadSDF=true ) { for( let param of this.parameters ) this.__target[ param.name ].upload_data( gl ) this.transform.upload_data( gl ) if( shouldUploadSDF ) this.sdf.upload_data( gl ) } } ops.Repeat = ops.Repetition ops.RepeatScale = ops.RepetitionShrink ops.PolarRepeat = ops.PolarRepetition return ops } module.exports = getDomainOps