UNPKG

marching

Version:

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

318 lines (261 loc) 9.89 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 createPrimitives = function( SDF ) { const gens = { int: int_var_gen, float: float_var_gen, vec2: vec2_var_gen, vec3: vec3_var_gen, vec4: vec4_var_gen, } const vars = { vec2: Vec2, vec3: Vec3, vec4: Vec4 } // load descriptions of all primtives const descriptions = require( './primitiveDescriptions.js' ) const Primitives = { descriptions, textureMemo: [], emit_geometries() { const head = Array.isArray( SDF.__scene.__prerender ) ? SDF.__scene.__prerender[0] : SDF.__scene.__prerender const geos = Primitives.crawlNode( head, [] ) geos.forEach( (geo,i) => { geo.__sdfID = i if( geo.__textureObj !== undefined ) { SDF.textures.addTexture( geo.__textureObj ) } }) const length = geos.length const materials = SDF.materials.materials let decl = `SDF sdfs[${length}] = SDF[${length}](\n` geos.forEach( (geo, i) => { const textureID = geo.__textureObj === undefined ? 50000 : geo.__textureObj.id const hasRepeat = geo.repeat !== null && geo.repeat !== undefined decl += ` SDF( ${materials.indexOf( geo.__material )}, ${geo.transform.varName}, ${textureID}, ${hasRepeat ? geo.repeat.distance.emit() : 'vec3(0.)'}, ${hasRepeat ? geo.repeat.transform.emit() : `mat4(1.)`} )` if( i < geos.length - 1 ) decl += ',' decl += '\n' }) decl += ` );\n` this.geometries = geos return decl }, crawlNode( node, arr ) { if( node.type === 'geometry' ) { arr.push( node ) }else{ if( node.a !== undefined ) Primitives.crawlNode( node.a, arr ) if( node.b !== undefined ) Primitives.crawlNode( node.b, arr ) if( node.sdf !== undefined ) Primitives.crawlNode( node.sdf, arr ) } return arr } } const createPrimitive = function( name, desc ) { const params = desc.parameters // create constructor Primitives[ name ] = function( ...args ) { const p = Object.create( Primitives[ name ].prototype ) p.params = params p.transform = Transform() p.transform.shouldInvert = true p.type = 'geometry' p.name = name p.repeat = null//Var( vars.vec3( 0 ), null, 'vec3' ) p.__material = null p.__textureID = 500000 let count = 0 // wrap each param in a Var object for codegen for( let param of params ) { if( param.name === 'color' ) { p.color = args[ count ] === undefined ? param.default : args[ count++ ] continue } if( param.type === 'obj' ) { let __value = args[ count++ ] p[ param.name ] = { get value() { return __value }, set value(v){ __value = v }, emit() { const output = p[ param.name ].value.emit() return output }, emit_decl() { return p[ param.name ].value.a.emit_decl() + p[param.name].value.b.emit_decl() } } continue } const defaultValues = param.default const isArray = Array.isArray( defaultValues ) if( isArray ) { let val = args[ count++ ], __var if( typeof val === 'number' ) { __var = Var( vars[ param.type ]( val ), null, 'vec3' ) }else{ __var = param_wrap( val, gens[ param.type ]( ...defaultValues ) ) } // for assigning entire new vectors to property Object.defineProperty( p, param.name, { configurable:true, 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 } } }) }else{ let __var = param_wrap( args[ count++ ], gens[ param.type ]( defaultValues ) ) //__var.set( defaultValues ) Object.defineProperty( p, param.name, { configurable:true, get() { return __var }, set(v) { __var.set( v ) } }) } } p.id = VarAlloc.alloc() p.__desc = desc p.__setMaterial = function(mat) { if( typeof mat === 'string' ) mat = SDF.Material[ mat ] this.__material = this.mat = SDF.materials.addMaterial( mat ) } p.__setTexture = function(tex,props) { if( typeof tex === 'string' ) { this.texture = p.texture.bind( this ) this.__textureObj = this.tex = SDF.Texture( tex,props,this.texture ) this.__textureID = this.__textureObj.id }else{ this.__textureObj = this.tex = Object.assign( tex, props ) this.__textureID = this.__textureObj.id } } p.__setBump = function(tex,props) { //this.bump = p.bump.bind( this ) const b = this.bump = this.__bumpObj = SDF.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( p, { renderingBump : false, emittingDecl : false, uploading : false, updating : false }) if( p.__material === null ) p.__setMaterial() SDF.geometries.push( p ) return p } // define prototype to use Primitives[ name ].prototype = SceneNode() Primitives[ name ].prototype.type = 'geometry' // create codegen string Primitives[ name ].prototype.emit = function ( __name, transform = null, bump=null, scale=null ) { if( SDF.memo[ this.id ] !== undefined ) return { preface:'', out:name+this.matId } if( this.__bumpObj !== undefined && this.renderingBump === false) { this.renderingBump = true return this.__bumpObj.emit( __name, transform ) } const shaderCode = desc.glslify.indexOf('#') > -1 ? desc.glslify.slice(18) : desc.glslify if( SDF.requiredGeometries.indexOf( shaderCode ) === - 1 ) { SDF.requiredGeometries.push( shaderCode ) } if( transform !== null ) this.transform.apply( transform, false ) //this.transform.invert( true ) this.transform.internal() const pname = typeof __name !== 'string' ? 'p' : __name, id = this.__sdfID, s = scale === null ? this.transform.emit_scale() : `${this.transform.emit_scale()} * ${scale}`, tstring = `( ${pname} * ${this.transform.emit()} ).xyz` const primitive = ` vec2 ${name}${this.id} = vec2( ${desc.primitiveString.call( this, tstring, bump )} * ${s}, ${id}.); ` SDF.memo[ this.id ] = name + this.id this.renderingBump = false return { preface:primitive, out:name+this.id } } // declare any uniform variables Primitives[ name ].prototype.emit_decl = function() { if( this.__bumpObj !== undefined && this.emittingDecl === false) { this.emittingDecl = true return this.__bumpObj.emit_decl() } let decl = '' decl += this.transform.emit_decl() //debugger if( this.__repeat !== undefined ) decl += this.__repeat.emit_decl( false ) if( this.__polarRepeat !== undefined ) decl += this.__polarRepeat.emit_decl( false ) for( let param of params ) { if( param.name !== 'material' ) decl += this[ param.name ].emit_decl( ) } this.emittingDecl = false return decl } Primitives[ name ].prototype.update_location = function( gl, program ) { if( this.__bumpObj !== undefined && this.updating === false) { this.updating = true return this.__bumpObj.update_location( gl, program ) } for( let param of params ) { if( param.type !== 'obj' ) { if( param.name !== 'material' ) this[ param.name ].update_location( gl,program ) } } if( this.__repeat !== undefined ) this.__repeat.update_location( gl, program, false ) if( this.__polarRepeat !== undefined ) this.__polarRepeat.update_location( gl, program, false ) this.transform.update_location( gl, program ) this.updating = false } Primitives[ name ].prototype.upload_data = function( gl ) { if( this.__bumpObj !== undefined && this.uploading === false ) { this.uploading = true return this.__bumpObj.upload_data( gl ) } for( let param of params ) { if( param.type !== 'obj' && param.name !== 'material' ) this[ param.name ].upload_data( gl ) } if( this.__polarRepeat !== undefined ) this.__polarRepeat.upload_data( gl, false ) this.transform.upload_data( gl ) this.uploading = false } return Primitives[ name ] } for( let name in descriptions ) { const desc = descriptions[ name ] createPrimitive( name, desc ) } Primitives.create = createPrimitive return Primitives } module.exports = createPrimitives